diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/Declaration.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/Declaration.java index 8ad426e9437..5ef69590f47 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/Declaration.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/Declaration.java @@ -1,13 +1,19 @@ +/********************************************************************** + * Copyright (c) 2002,2003 Rational Software Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v0.5 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v05.html + * + * Contributors: + * Rational Software - Initial API and implementation +***********************************************************************/ + package org.eclipse.cdt.internal.core.parser; import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; import java.util.LinkedList; -import java.util.List; import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Set; /** * @author aniefer @@ -17,12 +23,13 @@ import java.util.Set; * To enable and disable the creation of type comments go to * Window>Preferences>Java>Code Generation. */ + public class Declaration { /** * Constructor for Declaration. */ - public Declaration() { + public Declaration(){ super(); } @@ -35,36 +42,139 @@ public class Declaration { _object = obj; } - //Type information, only what we need for now... - public static final int typeMask = 0x0001f; - public static final int isStatic = 0x00020; + public static final int typeMask = 0x001f; + public static final int isAuto = 0x0020; + public static final int isRegister = 0x0040; + public static final int isStatic = 0x0080; + public static final int isExtern = 0x0100; + public static final int isMutable = 0x0200; + public static final int isInline = 0x0400; + public static final int isVirtual = 0x0800; + public static final int isExplicit = 0x1000; + public static final int isTypedef = 0x2000; + public static final int isFriend = 0x4000; + public static final int isConst = 0x8000; + public static final int isVolatile = 0x10000; + public static final int isUnsigned = 0x20000; + public static final int isShort = 0x40000; + public static final int isLong = 0x80000; + + public void setAuto(boolean b) { setBit(b, isAuto); } + public boolean isAuto() { return checkBit(isAuto); } + + public void setRegister(boolean b) { setBit(b, isRegister); } + public boolean isRegister() { return checkBit(isRegister); } + + public void setStatic(boolean b) { setBit(b, isStatic); } + public boolean isStatic() { return checkBit(isStatic); } + + public void setExtern(boolean b) { setBit(b, isExtern); } + public boolean isExtern() { return checkBit(isExtern); } + + public void setMutable(boolean b) { setBit(b, isMutable); } + public boolean isMutable() { return checkBit(isMutable); } + + public void setInline(boolean b) { setBit(b, isInline); } + public boolean isInline() { return checkBit(isInline); } + + public void setVirtual(boolean b) { setBit(b, isVirtual); } + public boolean isVirtual() { return checkBit(isVirtual); } + + public void setExplicit(boolean b) { setBit(b, isExplicit); } + public boolean isExplicit() { return checkBit(isExplicit); } + + public void setTypedef(boolean b) { setBit(b, isTypedef); } + public boolean isTypedef() { return checkBit(isTypedef); } + + public void setFriend(boolean b) { setBit(b, isFriend); } + public boolean isFriend() { return checkBit(isFriend); } + + public void setConst(boolean b) { setBit(b, isConst); } + public boolean isConst() { return checkBit(isConst); } + + public void setVolatile(boolean b) { setBit(b, isVolatile); } + public boolean isVolatile() { return checkBit(isVolatile); } + public void setUnsigned(boolean b) { setBit(b, isUnsigned); } + public boolean isUnsigned() { return checkBit(isUnsigned); } + + public void setShort(boolean b) { setBit(b, isShort); } + public boolean isShort() { return checkBit(isShort); } + + public void setLong(boolean b) { setBit(b, isLong); } + public boolean isLong() { return checkBit(isLong); } + // Types - public static final int t_type = 0; // Type Specifier - public static final int t_class = 1; - public static final int t_struct = 2; - public static final int t_union = 3; - public static final int t_enum = 4; + // Note that these should be considered ordered and if you change + // the order, you should consider the ParserSymbolTable uses + public static final int t_type = 0; // Type Specifier + public static final int t_namespace = 1; + public static final int t_class = 2; + public static final int t_struct = 3; + public static final int t_union = 4; + public static final int t_enum = 5; + public static final int t_function = 6; + public static final int t_char = 7; + public static final int t_wchar_t = 8; + public static final int t_bool = 9; + public static final int t_int = 10; + public static final int t_float = 11; + public static final int t_double = 12; + public static final int t_void = 13; + public static final int t_enumerator = 14; + - public void setStatic( boolean b ) { setBit( b, isStatic ); } - public boolean isStatic() { return checkBit( isStatic ); } - - public void setType(int t) throws ParserSymbolTableException { - if( t > typeMask ) + public void setType(int t) throws ParserSymbolTableException{ + //sanity check, t must fit in its allocated 5 bits in _typeInfo + if( t > typeMask ){ throw new ParserSymbolTableException( ParserSymbolTableException.r_BadTypeInfo ); + } + _typeInfo = _typeInfo & ~typeMask | t; } + public int getType(){ return _typeInfo & typeMask; } - public boolean isType( int t ){ - return ( t == -1 || getType() == t ); + + public boolean isType( int type ){ + return isType( type, 0 ); + } + + /** + * + * @param type + * @param upperType + * @return boolean + * + * type checking, check that this declaration's type is between type and + * upperType (inclusive). upperType of 0 means no range and our type must + * be type. + */ + public boolean isType( int type, int upperType ){ + //type of -1 means we don't care + if( type == -1 ) + return true; + + //upperType of 0 means no range + if( upperType == 0 ){ + return ( getType() == type ); + } else { + return ( getType() >= type && getType() <= upperType ); + } } - public Declaration getTypeDeclaration() { return _typeDeclaration; } + public Declaration getTypeDeclaration(){ + return _typeDeclaration; + } + public void setTypeDeclaration( Declaration type ){ - try { setType( t_type ); } - catch (ParserSymbolTableException e) { /*will never happen*/ } + //setting our type to a declaration implies we are type t_type + try { + setType( t_type ); + } catch (ParserSymbolTableException e) { + /*will never happen*/ + } _typeDeclaration = type; } @@ -76,137 +186,64 @@ public class Declaration { public void setObject( Object obj ) { _object = obj; } public Declaration getContainingScope() { return _containingScope; } - protected void setContainingScope( Declaration scope ) { _containingScope = scope; } + protected void setContainingScope( Declaration scope ){ + _containingScope = scope; + _depth = scope._depth + 1; + } public void addParent( Declaration parent ){ addParent( parent, false ); } public void addParent( Declaration parent, boolean virtual ){ + if( _parentScopes == null ){ + _parentScopes = new LinkedList(); + } + _parentScopes.add( new ParentWrapper( parent, virtual ) ); } - - protected void addDeclaration( Declaration obj ){ - obj.setContainingScope( this ); - _containedDeclarations.put( obj.getName(), obj ); - } public Map getContainedDeclarations(){ return _containedDeclarations; } - /** - * Lookup the given name in this context. - * @param type: for elaborated lookups, only return declarations of this - * type - * @param name: Name of the object to lookup - * @return Declaration - * @throws ParserSymbolTableException - * @see ParserSymbolTable#Lookup - */ - protected Declaration Lookup( int type, String name ) throws ParserSymbolTableException{ + public Map createContained(){ + if( _containedDeclarations == null ) + _containedDeclarations = new HashMap(); - if( type != -1 && type < t_class && type > t_union ) - throw new ParserSymbolTableException( ParserSymbolTableException.r_BadTypeInfo ); - - Declaration decl = null; - - //if this name define in this scope? - decl = (Declaration) _containedDeclarations.get( name ); - - //if yes, it hides any others, we are done. - if( decl != null && decl.isType( type ) ){ - return decl; - } - - //if no, we next check any parents we have - decl = LookupInParents( type, name, new HashSet() ); - - //if still not found, check our containing scope. - if( decl == null && _containingScope != null ) - decl = _containingScope.Lookup( type, name ); - - return decl; + return _containedDeclarations; } - private Declaration LookupInParents( int type, String name, Set virtualsVisited ) throws ParserSymbolTableException{ - - Declaration decl = null, temp = null; - - Iterator iterator = _parentScopes.iterator(); - - ParentWrapper wrapper = null; - try{ - wrapper = (ParentWrapper) iterator.next(); - } - catch ( NoSuchElementException e ){ - wrapper = null; - } - - while( wrapper != null ) - { - if( !wrapper.isVirtual || !virtualsVisited.contains( wrapper.parent ) ){ - if( wrapper.isVirtual ) - virtualsVisited.add( wrapper.parent ); - - //is this name define in this scope? - temp = (Declaration) wrapper.parent._containedDeclarations.get( name ); - if( temp == null || !temp.isType( type ) ) - temp = wrapper.parent.LookupInParents( type, name, virtualsVisited ); - } - - if( temp != null && temp.isType( type ) ){ - if( decl == null ) - decl = temp; - else if ( temp != null ) - { - //it is not ambiguous if temp & decl are the same thing and it is static - //or an enum - if( decl == temp && ( temp.isStatic() || temp.getType() == t_enum) ) - temp = null; - else - throw( new ParserSymbolTableException( ParserSymbolTableException.r_AmbiguousName ) ); - - } - } - else - temp = null; - - try{ - wrapper = (ParentWrapper) iterator.next(); - } - catch (NoSuchElementException e){ - wrapper = null; - } - } - - return decl; + public LinkedList getParentScopes(){ + return _parentScopes; } - // Convenience methods - private void setBit(boolean b, int mask) { - if (b) _typeInfo = _typeInfo | mask; - else _typeInfo = _typeInfo & ~mask; + private void setBit(boolean b, int mask){ + if( b ){ + _typeInfo = _typeInfo | mask; + } else { + _typeInfo = _typeInfo & ~mask; + } } - private boolean checkBit(int mask) { + private boolean checkBit(int mask){ return (_typeInfo & mask) != 0; } + private int _typeInfo; //our type info + private String _name; //our name + private Object _object; //the object associated with us + private Declaration _typeDeclaration; //our type if _typeInfo says t_type - //Other scopes to check if the name is not in currRegion - //we might want another Vector to deal with namespaces & using... - private Declaration _containingScope = null; - private Declaration _type = null; - private Declaration _typeDeclaration = null; - private int _typeInfo = 0; - private Object _object = null; - private List _parentScopes = new LinkedList(); - private Map _containedDeclarations = new HashMap(); - private String _name; + protected Declaration _containingScope; //the scope that contains us + protected LinkedList _parentScopes; //inherited scopes (is base classes) + protected LinkedList _usingDirectives; //collection of nominated namespaces + protected Map _containedDeclarations; //declarations contained by us. + protected int _depth; //how far down the scope stack we are - private class ParentWrapper{ + protected class ParentWrapper + { public ParentWrapper( Declaration p, boolean v ){ parent = p; isVirtual = v; @@ -215,6 +252,5 @@ public class Declaration { public boolean isVirtual = false; public Declaration parent = null; } - - + } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/ParserSymbolTable.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/ParserSymbolTable.java index 3f4f5ab35f7..e0bb08333b9 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/ParserSymbolTable.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/ParserSymbolTable.java @@ -1,6 +1,26 @@ +/********************************************************************** + * Copyright (c) 2002,2003 Rational Software Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v0.5 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v05. html + * + * Contributors: + * Rational Software - Initial API and implementation + * +***********************************************************************/ + + package org.eclipse.cdt.internal.core.parser; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Map; +import java.util.Set; import java.util.Stack; + /** * @author aniefer * @@ -9,6 +29,7 @@ import java.util.Stack; * To enable and disable the creation of type comments go to * Window>Preferences>Java>Code Generation. */ + public class ParserSymbolTable { /** @@ -21,8 +42,10 @@ public class ParserSymbolTable { } public void push( Declaration obj ){ - if( _contextStack.empty() == false ) + if( _contextStack.empty() == false && obj.getContainingScope() == null ){ obj.setContainingScope( (Declaration) _contextStack.peek() ); + } + _contextStack.push( obj ); } @@ -34,22 +57,589 @@ public class ParserSymbolTable { return (Declaration) _contextStack.peek(); } + public Declaration getCompilationUnit(){ + return _compilationUnit; + } + public Declaration Lookup( String name ) throws ParserSymbolTableException { - return ( (Declaration) _contextStack.peek() ).Lookup( -1, name ); + LookupData data = new LookupData( name, -1 ); + return Lookup( data, (Declaration) _contextStack.peek() ); } public Declaration ElaboratedLookup( int type, String name ) throws ParserSymbolTableException{ - return ( (Declaration) _contextStack.peek() ).Lookup( type, name ); + LookupData data = new LookupData( name, type ); + return Lookup( data, (Declaration) _contextStack.peek() ); + } + + /** + * Method LookupNestedNameSpecifier. + * @param name + * @return Declaration + * The name of a class or namespace member can be referred to after the :: + * scope resolution operator applied to a nested-name-specifier that + * nominates its class or namespace. During the lookup for a name preceding + * the ::, object, function and enumerator names are ignored. If the name + * is not a class-name or namespace-name, the program is ill-formed + */ + + public Declaration LookupNestedNameSpecifier( String name ) throws ParserSymbolTableException { + return LookupNestedNameSpecifier( name, (Declaration) _contextStack.peek() ); + } + + private Declaration LookupNestedNameSpecifier(String name, Declaration inDeclaration ) throws ParserSymbolTableException + { + Declaration foundDeclaration = null; + + LookupData data = new LookupData( name, Declaration.t_namespace ); + data.upperType = Declaration.t_union; + + foundDeclaration = LookupInContained( data, inDeclaration ); + + if( foundDeclaration == null && inDeclaration._containingScope != null ){ + foundDeclaration = LookupNestedNameSpecifier( name, inDeclaration._containingScope ); + } + + return foundDeclaration; } - public void addDeclaration( Declaration obj ){ - ((Declaration) _contextStack.peek() ).addDeclaration( obj ); + /** + * + * @param name + * @return Declaration + * @throws ParserSymbolTableException + * + * During lookup for a name preceding the :: scope resolution operator, + * object, function, and enumerator names are ignored. + */ + public Declaration QualifiedLookup( String name ) throws ParserSymbolTableException + { + LookupData data = new LookupData( name, -1 ); + data.qualified = true; + return Lookup( data, (Declaration) _contextStack.peek() ); } - public Declaration getCompilationUnit(){ - return _compilationUnit; + public void addUsingDirective( Declaration namespace ) throws ParserSymbolTableException + { + if( namespace.getType() != Declaration.t_namespace ){ + throw new ParserSymbolTableException(); + } + + Declaration declaration = (Declaration) _contextStack.peek(); + + if( declaration._usingDirectives == null ){ + declaration._usingDirectives = new LinkedList(); + } + + declaration._usingDirectives.add( namespace ); + } + + public void addDeclaration( Declaration obj ) throws ParserSymbolTableException{ + Declaration containing = (Declaration) _contextStack.peek(); + Map declarations = containing.getContainedDeclarations(); + + Object origObj = null; + + obj.setContainingScope( containing ); + + if( declarations == null ){ + declarations = containing.createContained(); + } else { + //does this name exist already? + origObj = declarations.get( obj.getName() ); + } + + if( origObj != null ) + { + Declaration origDecl = null; + LinkedList origList = null; + + if( origObj.getClass() == Declaration.class ){ + origDecl = (Declaration)origObj; + } else if( origObj.getClass() == LinkedList.class ){ + origList = (LinkedList)origObj; + } else { + throw new ParserSymbolTableException(); + } + + if( (origList == null) ? isValidOverload( origDecl, obj ) : isValidOverload( origList, obj ) ){ + if( origList == null ){ + origList = new LinkedList(); + origList.add( origDecl ); + origList.add( obj ); + + declarations.remove( obj ); + declarations.put( obj.getName(), origList ); + } else { + origList.add( obj ); + //origList is already in _containedDeclarations + } + } else { + throw new ParserSymbolTableException(); + } + } else { + declarations.put( obj.getName(), obj ); + } + } + + /** + * Lookup the name from LookupData starting in the inDeclaration + * @param data + * @param inDeclaration + * @return Declaration + * @throws ParserSymbolTableException + */ + static private Declaration Lookup( LookupData data, Declaration inDeclaration ) throws ParserSymbolTableException + { + if( data.type != -1 && data.type < Declaration.t_class && data.upperType > Declaration.t_union ){ + throw new ParserSymbolTableException( ParserSymbolTableException.r_BadTypeInfo ); + } + + Declaration decl = null; //the return value + LinkedList tempList = null; + LinkedList foundNames = new LinkedList(); //list of names found + LinkedList transitives = new LinkedList(); //list of transitive using directives + + + //if this name define in this scope? + decl = LookupInContained( data, inDeclaration ); + if( decl != null ){ + foundNames.add( decl ); + } + + //check nominated namespaces + //the transitives list is populated in LookupInNominated, and then + //processed in ProcessDirectives + + data.visited.clear(); //each namesapce is searched at most once, so keep track + + tempList = LookupInNominated( data, inDeclaration, transitives ); + + if( tempList != null ){ + foundNames.addAll( tempList ); + } + + //if we are doing a qualified lookup, only process using directives if + //we haven't found the name yet. + if( !data.qualified || foundNames.size() == 0 ){ + ProcessDirectives( inDeclaration, data, transitives ); + + if( inDeclaration._usingDirectives != null ){ + ProcessDirectives( inDeclaration, data, inDeclaration._usingDirectives ); + } + + while( data.usingDirectives != null && data.usingDirectives.get( inDeclaration ) != null ){ + transitives.clear(); + + tempList = LookupInNominated( data, inDeclaration, transitives ); + + if( tempList != null ){ + foundNames.addAll( tempList ); + } + + if( !data.qualified || foundNames.size() == 0 ){ + ProcessDirectives( inDeclaration, data, transitives ); + } + } + } + + decl = ResolveAmbiguities( foundNames ); + if( decl != null ){ + return decl; + } + + //if we still havn't found it, check any parents we have + data.visited.clear(); //each virtual base class is searched at most once + decl = LookupInParents( data, inDeclaration ); + + //if still not found, check our containing scope. + if( decl == null && inDeclaration._containingScope != null ){ + decl = Lookup( data, inDeclaration._containingScope ); + } + + return decl; + } + + /** + * function LookupInNominated + * @param data + * @param transitiveDirectives + * @return List + * + * for qualified: + * 3.4.3.2-2 "let S be the set of all declarations of m in X + * and in the transitive closure of all namespaces nominated by using- + * directives in X and its used namespaces, except that using-directives are + * ignored in any namespace, including X, directly containing one or more + * declarations of m." + * + * for unqualified: + * 7.3.4-2 The using-directive is transitive: if a scope contains a using + * directive that nominates a second namespace that itself contains using- + * directives, the effect is as if the using-directives from the second + * namespace also appeared in the first. + */ + static private LinkedList LookupInNominated( LookupData data, Declaration declaration, LinkedList transitiveDirectives ) throws ParserSymbolTableException{ + //if the data.usingDirectives is empty, there is nothing to do. + if( data.usingDirectives == null ){ + return null; + } + + LinkedList found = null; //list of found names to return + + //local variables + LinkedList list = null; + Iterator iter = null; + Declaration decl = null; + Declaration temp = null; + int size = 0; + + list = (LinkedList) data.usingDirectives.remove( declaration ); + + if( list == null ){ + return null; + } + + iter = list.iterator(); + size = list.size(); + for( int i = size; i > 0; i-- ){ + decl = (Declaration) iter.next(); + + //namespaces are searched at most once + if( !data.visited.contains( decl ) ){ + data.visited.add( decl ); + + temp = LookupInContained( data, decl ); + + //if we found something, add it to the list of found names + if( temp != null ){ + if( found == null ){ + found = new LinkedList(); + } + found.add( temp ); + } + + //only consider the transitive using directives if we are an unqualified + //lookup, or we didn't find the name in decl + if( (!data.qualified || temp == null) && decl._usingDirectives != null ){ + //name wasn't found, add transitive using directives for later consideration + transitiveDirectives.addAll( decl._usingDirectives ); + } + } + } + + return found; + } + + /** + * function LookupInContained + * @param data + * @return List + * @throws ParserSymbolTableException + * + * Look for data.name in our collection _containedDeclarations + */ + private static Declaration LookupInContained( LookupData data, Declaration lookIn ) throws ParserSymbolTableException{ + LinkedList found = null; + Declaration temp = null; + Object obj = null; + + Map declarations = lookIn.getContainedDeclarations(); + if( declarations == null ) + return null; + + obj = declarations.get( data.name ); + + if( obj == null ){ + //not found + return null; + } + + //the contained declarations map either to a Declaration object, or to a list + //of declaration objects. + if( obj.getClass() == Declaration.class ){ + if( ((Declaration)obj).isType( data.type, data.upperType ) ){ + return (Declaration) obj; + } + } else { + found = new LinkedList(); + + LinkedList objList = (LinkedList)obj; + Iterator iter = objList.iterator(); + int size = objList.size(); + + for( int i = 0; i < size; i++ ){ + temp = (Declaration) iter.next(); + + if( temp.isType( data.type, data.upperType ) ){ + found.add(temp); + } + } + } + + //if none of the found items made it through the type filtering, just + //return null instead of an empty list. + if( found == null || found.size() == 0 ) + return null; + + return ResolveAmbiguities( found ); + } + + /** + * + * @param data + * @param lookIn + * @return Declaration + * @throws ParserSymbolTableException + */ + private static Declaration LookupInParents( LookupData data, Declaration lookIn ) throws ParserSymbolTableException{ + LinkedList scopes = lookIn.getParentScopes(); + Declaration decl = null; + Declaration temp = null; + Iterator iterator = null; + Declaration.ParentWrapper wrapper = null; + + if( scopes == null ) + return null; + + iterator = scopes.iterator(); + + int size = scopes.size(); + + for( int i = size; i > 0; i-- ) + { + wrapper = (Declaration.ParentWrapper) iterator.next(); + if( !wrapper.isVirtual || !data.visited.contains( wrapper.parent ) ){ + if( wrapper.isVirtual ){ + data.visited.add( wrapper.parent ); + } + + //is this name define in this scope? + temp = LookupInContained( data, wrapper.parent ); + + if( temp == null ){ + temp = LookupInParents( data, wrapper.parent ); + } + } + + if( temp != null && temp.isType( data.type ) ){ + if( decl == null ){ + decl = temp; + } else if ( temp != null ) { + //it is not ambiguous if temp & decl are the same thing and it is static + //or an enumerator + if( decl == temp && ( temp.isStatic() || temp.getType() == Declaration.t_enumerator) ){ + temp = null; + } else { + throw( new ParserSymbolTableException( ParserSymbolTableException.r_AmbiguousName ) ); + } + + } + } else { + temp = null; //reset temp for next iteration + } + } + + return decl; + } + + /** + * function isValidOverload + * @param origDecl + * @param newDecl + * @return boolean + * + * 3.3.7 "A class name or enumeration name can be hidden by the name of an + * object, function or enumerator declared in the same scope" + * + * 3.4-1 "Name lookup may associate more than one declaration with a name if + * it finds the name to be a function name" + */ + private static boolean isValidOverload( Declaration origDecl, Declaration newDecl ){ + int origType = origDecl.getType(); + int newType = newDecl.getType(); + + if( (origType >= Declaration.t_class && origType <= Declaration.t_enum) && //class name or enumeration ... + ( newType == Declaration.t_type || (newType >= Declaration.t_function && newType <= Declaration.typeMask) ) ){ + + return true; + } + //if the origtype is not a class-name or enumeration name, then the only other + //allowable thing is if they are both functions. + else if( origType == Declaration.t_function && newType == Declaration.t_function ){ + return true; + } + + return false; + } + + private static boolean isValidOverload( LinkedList origList, Declaration newDecl ){ + if( origList.size() == 1 ){ + return isValidOverload( (Declaration)origList.getFirst(), newDecl ); + } else if ( origList.size() > 1 ){ + + //the first thing can be a class-name or enumeration name, but the rest + //must be functions. So make sure the newDecl is a function before even + //considering the list + if( newDecl.getType() != Declaration.t_function ){ + return false; + } + + Iterator iter = origList.iterator(); + Declaration decl = (Declaration) iter.next(); + boolean valid = (( decl.getType() >= Declaration.t_class && decl.getType() <= Declaration.t_enum ) || + decl.getType() == Declaration.t_function ); + + while( valid && iter.hasNext() ){ + decl = (Declaration) iter.next(); + valid = ( decl.getType() == Declaration.t_function ); + } + + return valid; + } + + //empty list, return true + return true; + } + + static private Declaration ResolveAmbiguities( LinkedList items ) throws ParserSymbolTableException{ + Declaration decl = null; + + int size = items.size(); + + if( size == 0){ + return null; + } else if (size == 1) { + return (Declaration) items.getFirst(); + } else { + Declaration first = (Declaration)items.removeFirst(); + + //if first one is a class-name, the next ones hide it + if( first.getType() >= Declaration.t_class && first.getType() <= Declaration.t_enum ){ + return ResolveAmbiguities( items ); + } + + //else, if the first is an object (ie not a function), the rest must be the same + //declaration. otherwise (ie it is a function), the rest must be functions. + boolean needSame = ( first.getType() != Declaration.t_function ); + + Iterator iter = items.iterator(); + + for( int i = (size - 1); i > 0; i-- ){ + decl = (Declaration) iter.next(); + + if( needSame ){ + if( decl != first ){ + throw new ParserSymbolTableException(); + } + } else { + if( decl.getType() != Declaration.t_function ){ + throw new ParserSymbolTableException(); + } + } + } + + if( needSame ){ + return first; + } else { + items.addFirst( first ); + return ResolveFunction( items ); + } + } + } + + static private Declaration ResolveFunction( LinkedList functions ){ + //TBD + return null; + } + + /** + * function ProcessDirectives + * @param Declaration decl + * @param LookupData data + * @param LinkedList directives + * + * Go through the directives and for each nominated namespace find the + * closest enclosing declaration for that namespace and decl, then add the + * nominated namespace to the lookup data for consideration when we reach + * the enclosing declaration. + */ + static private void ProcessDirectives( Declaration decl, LookupData data, LinkedList directives ){ + Declaration enclosing = null; + Declaration temp = null; + + int size = directives.size(); + Iterator iter = directives.iterator(); + + for( int i = size; i > 0; i-- ){ + temp = (Declaration) iter.next(); + + //namespaces are searched at most once + if( !data.visited.contains( temp ) ){ + enclosing = getClosestEnclosingDeclaration( decl, temp ); + + //the data.usingDirectives is a map from enclosing declaration to + //a list of namespaces to consider when we reach that enclosing + //declaration + LinkedList list = (data.usingDirectives == null ) + ? null + : (LinkedList) data.usingDirectives.get( enclosing ); + if ( list == null ){ + list = new LinkedList(); + list.add( temp ); + if( data.usingDirectives == null ){ + data.usingDirectives = new HashMap(); + } + data.usingDirectives.put( enclosing, list ); + } else { + list.add( temp ); + } + } + } + } + + /** + * function getClosestEnclosingDeclaration + * @param decl1 + * @param decl2 + * @return Declaration + * + * 7.3.4-1 "During unqualified lookup, the names appear as if they were + * declared in the nearest enclosing namespace which contains both the + * using-directive and the nominated namespace" + * + * TBD: Consider rewriting this iteratively instead of recursively, for + * performance + */ + static private Declaration getClosestEnclosingDeclaration( Declaration decl1, Declaration decl2 ){ + if( decl1 == decl2 ){ + return decl1; + } + + if( decl1._depth == decl2._depth ){ + return getClosestEnclosingDeclaration( decl1._containingScope, decl2._containingScope ); + } else if( decl1._depth > decl2._depth ) { + return getClosestEnclosingDeclaration( decl1._containingScope, decl2 ); + } else { + return getClosestEnclosingDeclaration( decl1, decl2._containingScope ); + } } private Stack _contextStack = new Stack(); private Declaration _compilationUnit; + + private class LookupData + { + public String name; + public Map usingDirectives; + public Set visited = new HashSet(); //used to ensure we don't visit things more than once + + public int type = -1; + public int upperType = 0; + public boolean qualified = false; + + public LookupData( String n, int t ){ + name = n; + type = t; + } + } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/ParserSymbolTableException.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/ParserSymbolTableException.java index b031325bced..efe87c21833 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/ParserSymbolTableException.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/ParserSymbolTableException.java @@ -1,3 +1,14 @@ +/********************************************************************** + * Copyright (c) 2002,2003 Rational Software Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v0.5 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v05.html + * + * Contributors: + * Rational Software - Initial API and implementation +***********************************************************************/ + package org.eclipse.cdt.internal.core.parser; /** diff --git a/core/org.eclipse.cdt.ui.tests/parser/org/eclipse/cdt/core/parser/tests/ParserSymbolTableTest.java b/core/org.eclipse.cdt.ui.tests/parser/org/eclipse/cdt/core/parser/tests/ParserSymbolTableTest.java index 45c3aad32dd..91b71b978a6 100644 --- a/core/org.eclipse.cdt.ui.tests/parser/org/eclipse/cdt/core/parser/tests/ParserSymbolTableTest.java +++ b/core/org.eclipse.cdt.ui.tests/parser/org/eclipse/cdt/core/parser/tests/ParserSymbolTableTest.java @@ -1,3 +1,14 @@ +/********************************************************************** + * Copyright (c) 2002,2003 Rational Software Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v0.5 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v05.html + * + * Contributors: + * Rational Software - Initial API and implementation +***********************************************************************/ + package org.eclipse.cdt.core.parser.tests; import junit.framework.TestCase; @@ -339,7 +350,7 @@ public class ParserSymbolTableTest extends TestCase { table.addDeclaration( d ); Declaration enum = new Declaration("enum"); - enum.setType( Declaration.t_enum ); + enum.setType( Declaration.t_enumerator ); Declaration stat = new Declaration("static"); stat.setStatic(true); @@ -503,4 +514,519 @@ public class ParserSymbolTableTest extends TestCase { //on just the function name without the rest of the signature assertEquals( table.Lookup("foo"), null ); } -} \ No newline at end of file + + /** + * + * @throws Exception + * + * struct stat { + * //... + * } + * int stat( struct stat* ); + * void f() + * { + * struct stat *ps; + * stat(ps); + * } + */ + public void testFunctionHidesClass() throws Exception{ + newTable(); + + Declaration struct = new Declaration( "stat"); + struct.setType( Declaration.t_struct ); + table.addDeclaration( struct ); + + Declaration function = new Declaration( "stat" ); + function.setType( Declaration.t_function ); + table.addDeclaration( function ); + + Declaration f = new Declaration("f"); + f.setType( Declaration.t_function ); + table.addDeclaration( f ); + table.push( f ); + + Declaration look = table.ElaboratedLookup( Declaration.t_struct, "stat" ); + assertEquals( look, struct ); + + look = table.Lookup( "stat" ); + assertEquals( look, function ); + } + + /** + * + * @throws Exception + * + * namespace A { + * int i; + * namespace B { + * namespace C{ + * int i; + * } + * using namespace A::B::C; + * void f1() { + * i = 5; //OK, C::i visible and hides A::i + * } + * } + * namespace D{ + * using namespace B; + * using namespace C; + * void f2(){ + * i = 5; //ambiguous, B::C and A::i + * } + * } + * void f3() { + * i = 5; //uses A::i + * } + * } + * void f4(){ + * i = 5; //no i is visible here + * } + * + */ + public void testUsingDirectives_1() throws Exception{ + newTable(); + + Declaration nsA = new Declaration("A"); + nsA.setType( Declaration.t_namespace ); + table.addDeclaration( nsA ); + table.push( nsA ); + + Declaration nsA_i = new Declaration("i"); + table.addDeclaration( nsA_i ); + + Declaration nsB = new Declaration("B"); + nsB.setType( Declaration.t_namespace ); + table.addDeclaration( nsB ); + table.push( nsB ); + + Declaration nsC = new Declaration("C"); + nsC.setType( Declaration.t_namespace ); + table.addDeclaration( nsC ); + table.push( nsC ); + + Declaration nsC_i = new Declaration("i"); + table.addDeclaration( nsC_i ); + table.pop(); + + Declaration look = table.Lookup("C"); + table.addUsingDirective( look ); + + Declaration f1 = new Declaration("f"); + f1.setType( Declaration.t_function ); + table.push( f1 ); + + look = table.Lookup( "i" ); + assertEquals( look, nsC_i ); //C::i visible and hides A::i + + table.pop(); //end of f1 + table.pop(); //end of nsB + + assertEquals( table.peek(), nsA ); + + Declaration nsD = new Declaration("D"); + nsD.setType( Declaration.t_namespace ); + table.addDeclaration( nsD ); + table.push( nsD ); + + look = table.Lookup("B"); + assertEquals( look, nsB ); + table.addUsingDirective( look ); + + look = table.Lookup("C"); + assertEquals( look, nsC ); + table.addUsingDirective( look ); + + Declaration f2 = new Declaration( "f2" ); + f2.setType( Declaration.t_function ); + table.addDeclaration( f2 ); + table.push( f2 ); + + try + { + look = table.Lookup( "i" ); + assertTrue( false ); + } + catch ( ParserSymbolTableException e ) + { + assertTrue(true); //ambiguous B::C::i and A::i + } + table.pop(); //end f2 + table.pop(); //end nsD + + Declaration f3 = new Declaration ("f3"); + f3.setType( Declaration.t_function ); + table.addDeclaration( f3 ); + table.push( f3 ); + + look = table.Lookup("i"); + assertEquals( look, nsA_i ); //uses A::i + + table.pop(); + table.pop(); + + Declaration f4 = new Declaration ("f4"); + f4.setType( Declaration.t_function ); + table.addDeclaration( f4 ); + table.push( f4 ); + + look = table.Lookup("i"); + assertEquals( look, null );//neither i is visible here. + } + /** + * + * @throws Exception + * + * namespace M { + * int i; + * } + * namespace N { + * int i; + * using namespace M; + * } + * + * void f() { + * using namespace N; + * i = 7; //error, both M::i and N::i are visible + * N::i = 5; //ok, i directly declared in N, using M not + * considered (since this is a qualified lookup) + * } + * + */ + public void testTransitiveUsingDirective() throws Exception + { + newTable(); + + Declaration nsM = new Declaration( "M" ); + nsM.setType( Declaration.t_namespace ); + + table.addDeclaration( nsM ); + + table.push( nsM ); + Declaration nsM_i = new Declaration("i"); + table.addDeclaration( nsM_i ); + table.pop(); + + Declaration nsN = new Declaration( "N" ); + nsN.setType( Declaration.t_namespace ); + + table.addDeclaration( nsN ); + + table.push( nsN ); + Declaration nsN_i = new Declaration("i"); + table.addDeclaration( nsN_i ); + table.addUsingDirective( nsM ); + table.pop(); + + Declaration f = new Declaration("f"); + table.addDeclaration( f ); + table.push( f ); + + table.addUsingDirective( nsN ); + + Declaration look = null; + try + { + look = table.Lookup( "i" ); + assertTrue( false ); + } + catch ( ParserSymbolTableException e ) + { + assertTrue( true ); //ambiguous, both M::i and N::i are visible. + } + + look = table.LookupNestedNameSpecifier("N"); + table.push( look ); + look = table.QualifiedLookup("i"); //ok + assertEquals( look, nsN_i ); + } + + /** + * + * @throws Exception + * The same declaration found more than once is not an ambiguity + * namespace A{ + * int a; + * } + * namespace B{ + * using namespace A; + * } + * namespace C{ + * using namespace A; + * } + * + * namespace BC{ + * using namespace B; + * using namespace C; + * } + * + * void f(){ + * BC::a++; //ok + * } + */ + public void testUsing_SameDeclarationTwice() throws Exception + { + newTable(); + + Declaration nsA = new Declaration("A"); + nsA.setType( Declaration.t_namespace ); + table.addDeclaration( nsA ); + table.push( nsA ); + + Declaration a = new Declaration("a"); + table.addDeclaration( a ); + table.pop(); + + Declaration nsB = new Declaration("B"); + nsB.setType( Declaration.t_namespace ); + table.addDeclaration( nsB ); + table.push( nsB ); + table.addUsingDirective( nsA ); + table.pop(); + + Declaration nsC = new Declaration("C"); + nsC.setType( Declaration.t_namespace ); + table.addDeclaration( nsC ); + table.push( nsC ); + table.addUsingDirective( nsA ); + table.pop(); + + Declaration nsBC = new Declaration("BC"); + nsBC.setType( Declaration.t_namespace ); + table.addDeclaration( nsBC ); + table.push( nsBC ); + table.addUsingDirective( nsB ); + table.addUsingDirective( nsC ); + table.pop(); + + Declaration f = new Declaration("f"); + f.setType(Declaration.t_function); + table.addDeclaration( f ); + table.push(f); + + Declaration look = table.LookupNestedNameSpecifier("BC"); + assertEquals( look, nsBC ); + table.push(look); + look = table.QualifiedLookup("a"); + assertEquals( look, a ); + } + + /** + * + * @throws Exception + * + * namespace B { + * int b; + * } + * namespace A { + * using namespace B; + * int a; + * } + * namespace B { + * using namespace A; + * } + * + * void f(){ + * A::a++; //ok + * A::b++; //ok + * B::a++; //ok + * B::b++; //ok + * } + */ + public void testUsing_SearchedOnce() throws Exception + { + newTable(); + + Declaration nsB = new Declaration( "B" ); + nsB.setType( Declaration.t_namespace ); + table.addDeclaration( nsB ); + table.push( nsB ); + + Declaration b = new Declaration("b"); + table.addDeclaration( b ); + table.pop(); + + Declaration nsA = new Declaration( "A" ); + nsA.setType( Declaration.t_namespace ); + table.addDeclaration( nsA ); + table.push( nsA ); + + table.addUsingDirective( nsB ); + + Declaration a = new Declaration("a"); + table.addDeclaration( a ); + + table.pop(); + + table.push( nsB ); + table.addUsingDirective( nsA ); + table.pop(); + + Declaration f = new Declaration("f"); + table.addDeclaration(f); + table.push(f); + + Declaration look = table.LookupNestedNameSpecifier("A"); + table.push(look); + look = table.QualifiedLookup("a"); + assertEquals( look, a ); + + look = table.QualifiedLookup("b"); + assertEquals( look, b ); + table.pop(); + + look = table.LookupNestedNameSpecifier("B"); + table.push(look); + look = table.QualifiedLookup("a"); + assertEquals( look, a ); + + look = table.QualifiedLookup("b"); + assertEquals( look, b ); + table.pop(); + + } + + /** + * we pass if we don't go into an infinite loop. + * TBD: we need a mechanism to detect failure of this + * test instead of just looping forever. + * + * @throws Exception + * + * namespace A{ + * } + * namespace B{ + * using namespace A; + * } + * namespace A{ + * using namespace B; + * } + * void f(){ + * using namespace A; + * using namespace B; + * i = 1; //not declared anywhere. + * } + */ + public void testUsing_SearchedOnce_2() throws Exception + { + newTable(); + + Declaration nsA = new Declaration( "A" ); + nsA.setType( Declaration.t_namespace ); + table.addDeclaration( nsA ); + + Declaration nsB = new Declaration( "B" ); + nsB.setType( Declaration.t_namespace ); + table.addDeclaration( nsB ); + table.push( nsB ); + table.addUsingDirective( nsA ); + table.pop(); + + table.push( nsA ); + table.addUsingDirective( nsB ); + table.pop(); + + Declaration f = new Declaration("f"); + table.addDeclaration(f); + table.push(f); + table.addUsingDirective(nsA); + table.addUsingDirective(nsB); + + Declaration look = table.Lookup("i"); + assertEquals( look, null ); + + } + + /** + * During lookup of a qualified namespace member name, if the lookup finds + * more than one declaration of the member, non-type names hide class or + * enumeration names if and only if the declarations are from the same + * namespace + * @throws Exception + * + * namespace A { + * struct x { }; + * int x; + * int y; + * } + * namespace B { + * struct y { }; + * } + * + * namespace C { + * using namespace A; + * using namespace B; + * + * int i = C::x; //ok, finds A::x + * int j = C::y; //ambiguous, A::y or B::y + * } + */ + public void testNamespaceMemberHiding() throws Exception{ + newTable(); + + Declaration nsA = new Declaration("A"); + nsA.setType( Declaration.t_namespace ); + + table.addDeclaration( nsA ); + table.push( nsA ); + + Declaration structX = new Declaration("x"); + structX.setType( Declaration.t_struct ); + table.addDeclaration( structX ); + + Declaration intX = new Declaration("x"); + intX.setType( Declaration.t_int ); + table.addDeclaration( intX ); + + Declaration intY = new Declaration("y"); + intY.setType( Declaration.t_int ); + table.addDeclaration( intY ); + + table.pop(); + + Declaration nsB = new Declaration("B"); + nsB.setType( Declaration.t_namespace ); + + table.addDeclaration( nsB ); + table.push( nsB ); + + Declaration structY = new Declaration("y"); + structY.setType( Declaration.t_struct ); + table.addDeclaration( structY ); + + table.pop(); + + Declaration nsC = new Declaration("C"); + nsC.setType( Declaration.t_namespace); + table.addDeclaration( nsC ); + + table.push( nsC ); + + Declaration look = table.Lookup("A"); + assertEquals( look, nsA ); + table.addUsingDirective( look ); + + look = table.Lookup("B"); + assertEquals( look, nsB ); + table.addUsingDirective( look ); + + //lookup C::x + look = table.LookupNestedNameSpecifier("C"); + assertEquals( look, nsC ); + table.push(look); + look = table.QualifiedLookup( "x" ); + assertEquals( look, intX ); + table.pop(); + + //lookup C::y + look = table.LookupNestedNameSpecifier("C"); + assertEquals( look, nsC ); + table.push(look); + try{ + look = table.QualifiedLookup( "y" ); + assertTrue(false); + } catch ( Exception e ) { + assertTrue(true); + } + } + +}