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 }