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