View Javadoc

    1   /**
    2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
    3    */
    4   package net.sourceforge.pmd.lang.java.symboltable;
    5
    6   import java.util.Stack;
    7
    8   import net.sourceforge.pmd.lang.ast.Node;
    9   import net.sourceforge.pmd.lang.java.ast.ASTAnnotationTypeDeclaration;
    10  import net.sourceforge.pmd.lang.java.ast.ASTBlock;
    11  import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement;
    12  import net.sourceforge.pmd.lang.java.ast.ASTCatchStatement;
    13  import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBodyDeclaration;
    14  import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
    15  import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
    16  import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration;
    17  import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration;
    18  import net.sourceforge.pmd.lang.java.ast.ASTFinallyStatement;
    19  import net.sourceforge.pmd.lang.java.ast.ASTForStatement;
    20  import net.sourceforge.pmd.lang.java.ast.ASTFormalParameters;
    21  import net.sourceforge.pmd.lang.java.ast.ASTIfStatement;
    22  import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
    23  import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator;
    24  import net.sourceforge.pmd.lang.java.ast.ASTPackageDeclaration;
    25  import net.sourceforge.pmd.lang.java.ast.ASTSwitchStatement;
    26  import net.sourceforge.pmd.lang.java.ast.ASTTryStatement;
    27  import net.sourceforge.pmd.lang.java.ast.ASTTypeParameters;
    28  import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
    29  import net.sourceforge.pmd.lang.java.ast.AbstractJavaNode;
    30  import net.sourceforge.pmd.lang.java.ast.JavaNode;
    31  import net.sourceforge.pmd.lang.java.ast.JavaParserVisitorAdapter;
    32
    33  /**
    34   * Visitor for scope creation.
    35   * Visits all nodes of an AST and creates scope objects for nodes representing
    36   * syntactic entities which may contain declarations. For example, a block
    37   * may contain variable definitions (which are declarations) and
    38   * therefore needs a scope object where these declarations can be associated,
    39   * whereas an expression can't contain declarations and therefore doesn't need
    40   * a scope object.
    41   * With the exception of global scopes, each scope object is linked to its
    42   * parent scope, which is the scope object of the next embedding syntactic
    43   * entity that has a scope.
    44   */
    45  public class ScopeAndDeclarationFinder extends JavaParserVisitorAdapter {
    46
    47      /**
    48       * A stack of scopes reflecting the scope hierarchy when a node is visited.
    49       * This is used to set the parents of the created scopes correctly.
    50       */
    51      private Stack<Scope> scopes = new Stack<Scope>();
    52
    53      /**
    54       * Sets the scope of a node and adjusts the scope stack accordingly.
    55       * The scope on top of the stack is set as the parent of the given scope,
    56       * which is then also stored on the scope stack.
    57       *
    58       * @param newScope the scope for the node.
    59       * @param node     the AST node for which the scope is to be set.
    60       * @throws java.util.EmptyStackException if the scope stack is empty.
    61       */
    62      private void addScope(Scope newScope, JavaNode node) {
    63  	newScope.setParent(scopes.peek());
    64  	scopes.push(newScope);
    65  	node.setScope(newScope);
    66      }
    67
    68      /**
    69       * Creates a new local scope for an AST node.
    70       * The scope on top of the stack is set as the parent of the new scope,
    71       * which is then also stored on the scope stack.
    72       *
    73       * @param node the AST node for which the scope has to be created.
    74       * @throws java.util.EmptyStackException if the scope stack is empty.
    75       */
    76      private void createLocalScope(JavaNode node) {
    77  	addScope(new LocalScope(), node);
    78      }
    79
    80      /**
    81       * Creates a new method scope for an AST node.
    82       * The scope on top of the stack is set as the parent of the new scope,
    83       * which is then also stored on the scope stack.
    84       *
    85       * @param node the AST node for which the scope has to be created.
    86       * @throws java.util.EmptyStackException if the scope stack is empty.
    87       */
    88      private void createMethodScope(JavaNode node) {
    89  	addScope(new MethodScope(node), node);
    90      }
    91
    92      /**
    93       * Creates a new class scope for an AST node.
    94       * The scope on top of the stack is set as the parent of the new scope,
    95       * which is then also stored on the scope stack.
    96       *
    97       * @param node the AST node for which the scope has to be created.
    98       * @throws java.util.EmptyStackException if the scope stack is empty.
    99       */
    100     private void createClassScope(JavaNode node) {
    101 	if (node instanceof ASTClassOrInterfaceBodyDeclaration) {
    102 	    addScope(new ClassScope(), node);
    103 	} else {
    104 	    addScope(new ClassScope(node.getImage()), node);
    105 	}
    106     }
    107
    108     /**
    109      * Creates a new global scope for an AST node.
    110      * The new scope is stored on the scope stack.
    111      *
    112      * @param node the AST node for which the scope has to be created.
    113      */
    114     private void createSourceFileScope(ASTCompilationUnit node) {
    115 	// When we do full symbol resolution, we'll need to add a truly top-level GlobalScope.
    116 	Scope scope;
    117 	ASTPackageDeclaration n = node.getPackageDeclaration();
    118 	if (n != null) {
    119 	    scope = new SourceFileScope(n.jjtGetChild(0).getImage());
    120 	} else {
    121 	    scope = new SourceFileScope();
    122 	}
    123 	scopes.push(scope);
    124 	node.setScope(scope);
    125     }
    126
    127     @Override
    128     public Object visit(ASTCompilationUnit node, Object data) {
    129 	createSourceFileScope(node);
    130 	cont(node);
    131 	return data;
    132     }
    133
    134     @Override
    135     public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
    136 	createClassScope(node);
    137 	Scope s = ((JavaNode)node.jjtGetParent()).getScope();
    138 	s.addDeclaration(new ClassNameDeclaration(node));
    139 	cont(node);
    140 	return data;
    141     }
    142
    143     @Override
    144     public Object visit(ASTEnumDeclaration node, Object data) {
    145 	createClassScope(node);
    146 	cont(node);
    147 	return data;
    148     }
    149
    150     @Override
    151     public Object visit(ASTAnnotationTypeDeclaration node, Object data) {
    152 	createClassScope(node);
    153 	cont(node);
    154 	return data;
    155     }
    156
    157     @Override
    158     public Object visit(ASTClassOrInterfaceBodyDeclaration node, Object data) {
    159 	if (node.isAnonymousInnerClass() || node.isEnumChild()) {
    160 	    createClassScope(node);
    161 	    cont(node);
    162 	} else {
    163 	    super.visit(node, data);
    164 	}
    165 	return data;
    166     }
    167
    168     @Override
    169     public Object visit(ASTBlock node, Object data) {
    170 	createLocalScope(node);
    171 	cont(node);
    172 	return data;
    173     }
    174
    175     @Override
    176     public Object visit(ASTCatchStatement node, Object data) {
    177 	createLocalScope(node);
    178 	cont(node);
    179 	return data;
    180     }
    181
    182     @Override
    183     public Object visit(ASTFinallyStatement node, Object data) {
    184 	createLocalScope(node);
    185 	cont(node);
    186 	return data;
    187     }
    188
    189     @Override
    190     public Object visit(ASTConstructorDeclaration node, Object data) {
    191 	/*
    192 	 * Local variables declared inside the constructor need to
    193 	 * be in a different scope so special handling is needed
    194 	 */
    195 	createMethodScope(node);
    196
    197 	Scope methodScope = node.getScope();
    198
    199 	Node formalParameters = node.jjtGetChild(0);
    200 	int i = 1;
    201 	int n = node.jjtGetNumChildren();
    202 	if (!(formalParameters instanceof ASTFormalParameters)) {
    203 	    visit((ASTTypeParameters) formalParameters, data);
    204 	    formalParameters = node.jjtGetChild(1);
    205 	    i++;
    206 	}
    207 	visit((ASTFormalParameters) formalParameters, data);
    208
    209 	Scope localScope = null;
    210 	for (; i < n; i++) {
    211 	    JavaNode b = (JavaNode) node.jjtGetChild(i);
    212 	    if (b instanceof ASTBlockStatement) {
    213 		if (localScope == null) {
    214 		    createLocalScope(node);
    215 		    localScope = node.getScope();
    216 		}
    217 		b.setScope(localScope);
    218 		visit(b, data);
    219 	    } else {
    220 		visit(b, data);
    221 	    }
    222 	}
    223 	if (localScope != null) {
    224 	    // pop the local scope
    225 	    scopes.pop();
    226
    227 	    // reset the correct scope for the constructor
    228 	    node.setScope(methodScope);
    229 	}
    230 	// pop the method scope
    231 	scopes.pop();
    232
    233 	return data;
    234     }
    235
    236     @Override
    237     public Object visit(ASTMethodDeclaration node, Object data) {
    238 	createMethodScope(node);
    239 	ASTMethodDeclarator md = node.getFirstChildOfType(ASTMethodDeclarator.class);
    240 	node.getScope().getEnclosingClassScope().addDeclaration(new MethodNameDeclaration(md));
    241 	cont(node);
    242 	return data;
    243     }
    244
    245     @Override
    246     public Object visit(ASTTryStatement node, Object data) {
    247 	createLocalScope(node);
    248 	cont(node);
    249 	return data;
    250     }
    251
    252     // TODO - what about while loops and do loops?
    253     @Override
    254     public Object visit(ASTForStatement node, Object data) {
    255 	createLocalScope(node);
    256 	cont(node);
    257 	return data;
    258     }
    259
    260     @Override
    261     public Object visit(ASTIfStatement node, Object data) {
    262 	createLocalScope(node);
    263 	cont(node);
    264 	return data;
    265     }
    266
    267     @Override
    268     public Object visit(ASTVariableDeclaratorId node, Object data) {
    269 	VariableNameDeclaration decl = new VariableNameDeclaration(node);
    270 	node.getScope().addDeclaration(decl);
    271 	node.setNameDeclaration(decl);
    272 	return super.visit(node, data);
    273     }
    274
    275     @Override
    276     public Object visit(ASTSwitchStatement node, Object data) {
    277 	createLocalScope(node);
    278 	cont(node);
    279 	return data;
    280     }
    281
    282     private void cont(AbstractJavaNode node) {
    283 	super.visit(node, null);
    284 	scopes.pop();
    285     }
    286 }