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
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
163
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
195 scopes.pop();
196
197
198 node.setScope(methodScope);
199 }
200
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
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 }