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 }