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