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