ScopeAndDeclarationFinder.java
001 /**
002  * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
003  */
004 package net.sourceforge.pmd.lang.java.symboltable;
005 
006 import java.util.Stack;
007 
008 import net.sourceforge.pmd.lang.ast.Node;
009 import net.sourceforge.pmd.lang.java.ast.ASTAnnotationTypeDeclaration;
010 import net.sourceforge.pmd.lang.java.ast.ASTBlock;
011 import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement;
012 import net.sourceforge.pmd.lang.java.ast.ASTCatchStatement;
013 import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBodyDeclaration;
014 import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
015 import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
016 import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration;
017 import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration;
018 import net.sourceforge.pmd.lang.java.ast.ASTFinallyStatement;
019 import net.sourceforge.pmd.lang.java.ast.ASTForStatement;
020 import net.sourceforge.pmd.lang.java.ast.ASTFormalParameters;
021 import net.sourceforge.pmd.lang.java.ast.ASTIfStatement;
022 import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
023 import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator;
024 import net.sourceforge.pmd.lang.java.ast.ASTPackageDeclaration;
025 import net.sourceforge.pmd.lang.java.ast.ASTSwitchStatement;
026 import net.sourceforge.pmd.lang.java.ast.ASTTryStatement;
027 import net.sourceforge.pmd.lang.java.ast.ASTTypeParameters;
028 import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
029 import net.sourceforge.pmd.lang.java.ast.AbstractJavaNode;
030 import net.sourceforge.pmd.lang.java.ast.JavaNode;
031 import net.sourceforge.pmd.lang.java.ast.JavaParserVisitorAdapter;
032 
033 /**
034  * Visitor for scope creation.
035  * Visits all nodes of an AST and creates scope objects for nodes representing
036  * syntactic entities which may contain declarations. For example, a block
037  * may contain variable definitions (which are declarations) and
038  * therefore needs a scope object where these declarations can be associated,
039  * whereas an expression can't contain declarations and therefore doesn't need
040  * a scope object.
041  * With the exception of global scopes, each scope object is linked to its
042  * parent scope, which is the scope object of the next embedding syntactic
043  * entity that has a scope.
044  */
045 public class ScopeAndDeclarationFinder extends JavaParserVisitorAdapter {
046 
047     /**
048      * A stack of scopes reflecting the scope hierarchy when a node is visited.
049      * This is used to set the parents of the created scopes correctly.
050      */
051     private Stack<Scope> scopes = new Stack<Scope>();
052 
053     /**
054      * Sets the scope of a node and adjusts the scope stack accordingly.
055      * The scope on top of the stack is set as the parent of the given scope,
056      * which is then also stored on the scope stack.
057      *
058      @param newScope the scope for the node.
059      @param node     the AST node for which the scope is to be set.
060      @throws java.util.EmptyStackException if the scope stack is empty.
061      */
062     private void addScope(Scope newScope, JavaNode node) {
063   newScope.setParent(scopes.peek());
064   scopes.push(newScope);
065   node.setScope(newScope);
066     }
067 
068     /**
069      * Creates a new local scope for an AST node.
070      * The scope on top of the stack is set as the parent of the new scope,
071      * which is then also stored on the scope stack.
072      *
073      @param node the AST node for which the scope has to be created.
074      @throws java.util.EmptyStackException if the scope stack is empty.
075      */
076     private void createLocalScope(JavaNode node) {
077   addScope(new LocalScope(), node);
078     }
079 
080     /**
081      * Creates a new method scope for an AST node.
082      * The scope on top of the stack is set as the parent of the new scope,
083      * which is then also stored on the scope stack.
084      *
085      @param node the AST node for which the scope has to be created.
086      @throws java.util.EmptyStackException if the scope stack is empty.
087      */
088     private void createMethodScope(JavaNode node) {
089   addScope(new MethodScope(node), node);
090     }
091 
092     /**
093      * Creates a new class scope for an AST node.
094      * The scope on top of the stack is set as the parent of the new scope,
095      * which is then also stored on the scope stack.
096      *
097      @param node the AST node for which the scope has to be created.
098      @throws java.util.EmptyStackException if the scope stack is empty.
099      */
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((ASTTypeParametersformalParameters, data);
204       formalParameters = node.jjtGetChild(1);
205       i++;
206   }
207   visit((ASTFormalParametersformalParameters, data);
208 
209   Scope localScope = null;
210   for (; i < n; i++) {
211       JavaNode b = (JavaNodenode.jjtGetChild(i);
212       if (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 }