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((ASTTypeParameters) formalParameters, data);
204 formalParameters = node.jjtGetChild(1);
205 i++;
206 }
207 visit((ASTFormalParameters) formalParameters, data);
208
209 Scope localScope = null;
210 for (; i < n; i++) {
211 JavaNode b = (JavaNode) node.jjtGetChild(i);
212 if (b 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 }
|