001: /**
002: * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
003: */package net.sourceforge.pmd.symboltable;
004:
005: import net.sourceforge.pmd.ast.*;
006:
007: import java.util.List;
008: import java.util.Stack;
009:
010: /**
011: * Visitor for scope creation.
012: * Visits all nodes of an AST and creates scope objects for nodes representing
013: * syntactic entities which may contain declarations. For example, a block
014: * may contain variable definitions (which are declarations) and
015: * therefore needs a scope object where these declarations can be associated,
016: * whereas an expression can't contain declarations and therefore doesn't need
017: * a scope object.
018: * With the exception of global scopes, each scope object is linked to its
019: * parent scope, which is the scope object of the next embedding syntactic
020: * entity that has a scope.
021: */
022: public class ScopeAndDeclarationFinder extends JavaParserVisitorAdapter {
023:
024: /**
025: * A stack of scopes reflecting the scope hierarchy when a node is visited.
026: * This is used to set the parents of the created scopes correctly.
027: */
028: private Stack<Scope> scopes = new Stack<Scope>();
029:
030: /**
031: * Sets the scope of a node and adjustes the scope stack accordingly.
032: * The scope on top of the stack is set as the parent of the given scope,
033: * which is then also stored on the scope stack.
034: *
035: * @param newScope the scope for the node.
036: * @param node the AST node for which the scope is to be set.
037: * @throws java.util.EmptyStackException if the scope stack is empty.
038: */
039: private void addScope(Scope newScope, SimpleNode node) {
040: newScope.setParent(scopes.peek());
041: scopes.push(newScope);
042: node.setScope(newScope);
043: }
044:
045: /**
046: * Creates a new local scope for an AST node.
047: * The scope on top of the stack is set as the parent of the new scope,
048: * which is then also stored on the scope stack.
049: *
050: * @param node the AST node for which the scope has to be created.
051: * @throws java.util.EmptyStackException if the scope stack is empty.
052: */
053: private void createLocalScope(SimpleNode node) {
054: addScope(new LocalScope(), node);
055: }
056:
057: /**
058: * Creates a new method scope for an AST node.
059: * The scope on top of the stack is set as the parent of the new scope,
060: * which is then also stored on the scope stack.
061: *
062: * @param node the AST node for which the scope has to be created.
063: * @throws java.util.EmptyStackException if the scope stack is empty.
064: */
065: private void createMethodScope(SimpleNode node) {
066: addScope(new MethodScope(node), node);
067: }
068:
069: /**
070: * Creates a new class scope for an AST node.
071: * The scope on top of the stack is set as the parent of the new scope,
072: * which is then also stored on the scope stack.
073: *
074: * @param node the AST node for which the scope has to be created.
075: * @throws java.util.EmptyStackException if the scope stack is empty.
076: */
077: private void createClassScope(SimpleNode node) {
078: if (node instanceof ASTClassOrInterfaceBodyDeclaration) {
079: addScope(new ClassScope(), node);
080: } else {
081: addScope(new ClassScope(node.getImage()), node);
082: }
083: }
084:
085: /**
086: * Creates a new global scope for an AST node.
087: * The new scope is stored on the scope stack.
088: *
089: * @param node the AST node for which the scope has to be created.
090: */
091: private void createSourceFileScope(SimpleNode node) {
092: // When we do full symbol resolution, we'll need to add a truly top-level GlobalScope.
093: Scope scope;
094: List packages = node
095: .findChildrenOfType(ASTPackageDeclaration.class);
096: if (!packages.isEmpty()) {
097: Node n = (Node) packages.get(0);
098: scope = new SourceFileScope(((SimpleNode) n.jjtGetChild(0))
099: .getImage());
100: } else {
101: scope = new SourceFileScope();
102: }
103: scopes.push(scope);
104: node.setScope(scope);
105: }
106:
107: public Object visit(ASTCompilationUnit node, Object data) {
108: createSourceFileScope(node);
109: cont(node);
110: return data;
111: }
112:
113: public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
114: createClassScope(node);
115: Scope s = ((SimpleNode) node.jjtGetParent()).getScope();
116: s.addDeclaration(new ClassNameDeclaration(node));
117: cont(node);
118: return data;
119: }
120:
121: public Object visit(ASTEnumDeclaration node, Object data) {
122: createClassScope(node);
123: cont(node);
124: return data;
125: }
126:
127: public Object visit(ASTAnnotationTypeDeclaration node, Object data) {
128: createClassScope(node);
129: cont(node);
130: return data;
131: }
132:
133: public Object visit(ASTClassOrInterfaceBodyDeclaration node,
134: Object data) {
135: if (node.isAnonymousInnerClass() || node.isEnumChild()) {
136: createClassScope(node);
137: cont(node);
138: } else {
139: super .visit(node, data);
140: }
141: return data;
142: }
143:
144: public Object visit(ASTBlock node, Object data) {
145: createLocalScope(node);
146: cont(node);
147: return data;
148: }
149:
150: public Object visit(ASTCatchStatement node, Object data) {
151: createLocalScope(node);
152: cont(node);
153: return data;
154: }
155:
156: public Object visit(ASTFinallyStatement node, Object data) {
157: createLocalScope(node);
158: cont(node);
159: return data;
160: }
161:
162: public Object visit(ASTConstructorDeclaration node, Object data) {
163: /*
164: * Local variables declared inside the constructor need to
165: * be in a different scope so special handling is needed
166: */
167: createMethodScope(node);
168:
169: Scope methodScope = node.getScope();
170:
171: Node formalParameters = node.jjtGetChild(0);
172: int i = 1;
173: int n = node.jjtGetNumChildren();
174: if (!(formalParameters instanceof ASTFormalParameters)) {
175: visit((ASTTypeParameters) formalParameters, data);
176: formalParameters = node.jjtGetChild(1);
177: i++;
178: }
179: visit((ASTFormalParameters) formalParameters, data);
180:
181: Scope localScope = null;
182: for (; i < n; i++) {
183: SimpleJavaNode b = (SimpleJavaNode) node.jjtGetChild(i);
184: if (b instanceof ASTBlockStatement) {
185: if (localScope == null) {
186: createLocalScope(node);
187: localScope = node.getScope();
188: }
189: b.setScope(localScope);
190: visit(b, data);
191: } else {
192: visit(b, data);
193: }
194: }
195: if (localScope != null) {
196: // pop the local scope
197: scopes.pop();
198:
199: // reset the correct scope for the constructor
200: node.setScope(methodScope);
201: }
202: // pop the method scope
203: scopes.pop();
204:
205: return data;
206: }
207:
208: public Object visit(ASTMethodDeclaration node, Object data) {
209: createMethodScope(node);
210: ASTMethodDeclarator md = node
211: .getFirstChildOfType(ASTMethodDeclarator.class);
212: node.getScope().getEnclosingClassScope().addDeclaration(
213: new MethodNameDeclaration(md));
214: cont(node);
215: return data;
216: }
217:
218: public Object visit(ASTTryStatement node, Object data) {
219: createLocalScope(node);
220: cont(node);
221: return data;
222: }
223:
224: // TODO - what about while loops and do loops?
225: public Object visit(ASTForStatement node, Object data) {
226: createLocalScope(node);
227: cont(node);
228: return data;
229: }
230:
231: public Object visit(ASTIfStatement node, Object data) {
232: createLocalScope(node);
233: cont(node);
234: return data;
235: }
236:
237: public Object visit(ASTVariableDeclaratorId node, Object data) {
238: VariableNameDeclaration decl = new VariableNameDeclaration(node);
239: node.getScope().addDeclaration(decl);
240: node.setNameDeclaration(decl);
241: return super .visit(node, data);
242: }
243:
244: public Object visit(ASTSwitchStatement node, Object data) {
245: createLocalScope(node);
246: cont(node);
247: return data;
248: }
249:
250: private void cont(SimpleJavaNode node) {
251: super.visit(node, null);
252: scopes.pop();
253: }
254: }
|