View Javadoc

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