View Javadoc

    1   /*
    2    * Created on 14.07.2004
    3    */
    4   package net.sourceforge.pmd.dfa.variableaccess;
    5
    6   import net.sourceforge.pmd.ast.ASTClassOrInterfaceBodyDeclaration;
    7   import net.sourceforge.pmd.ast.ASTConstructorDeclaration;
    8   import net.sourceforge.pmd.ast.ASTFormalParameter;
    9   import net.sourceforge.pmd.ast.ASTMethodDeclaration;
    10  import net.sourceforge.pmd.ast.ASTVariableInitializer;
    11  import net.sourceforge.pmd.ast.JavaParserVisitorAdapter;
    12  import net.sourceforge.pmd.ast.SimpleNode;
    13  import net.sourceforge.pmd.dfa.IDataFlowNode;
    14  import net.sourceforge.pmd.dfa.StartOrEndDataFlowNode;
    15  import net.sourceforge.pmd.symboltable.NameOccurrence;
    16  import net.sourceforge.pmd.symboltable.VariableNameDeclaration;
    17
    18  import java.util.ArrayList;
    19  import java.util.HashSet;
    20  import java.util.List;
    21  import java.util.Map;
    22  import java.util.Set;
    23
    24  /**
    25   * @author raik, Sven Jacob
    26   *         <p/>
    27   *         Searches for special nodes and computes based on the sequence, the type of
    28   *         access of a variable.
    29   */
    30  public class VariableAccessVisitor extends JavaParserVisitorAdapter {
    31
    32      public void compute(ASTMethodDeclaration node) {
    33          if (node.jjtGetParent() instanceof ASTClassOrInterfaceBodyDeclaration) {
    34              this.computeNow(node);
    35          }
    36      }
    37
    38      public void compute(ASTConstructorDeclaration node) {
    39          this.computeNow(node);
    40      }
    41
    42      private void computeNow(SimpleNode node) {
    43          IDataFlowNode inode = node.getDataFlowNode();
    44
    45          List<VariableAccess> undefinitions = markUsages(inode);
    46
    47          // all variables are first in state undefinition
    48          IDataFlowNode firstINode = inode.getFlow().get(0);
    49          firstINode.setVariableAccess(undefinitions);
    50
    51          // all variables are getting undefined when leaving scope
    52          IDataFlowNode lastINode = inode.getFlow().get(inode.getFlow().size() - 1);
    53          lastINode.setVariableAccess(undefinitions);
    54      }
    55
    56      private List<VariableAccess> markUsages(IDataFlowNode inode) {
    57          // undefinitions was once a field... seems like it works fine as a local
    58          List<VariableAccess> undefinitions = new ArrayList<VariableAccess>();
    59          Set<Map<VariableNameDeclaration, List<NameOccurrence>>> variableDeclarations = collectDeclarations(inode);
    60          for (Map<VariableNameDeclaration, List<NameOccurrence>> declarations: variableDeclarations) {
    61              for (Map.Entry<VariableNameDeclaration, List<NameOccurrence>> entry: declarations.entrySet()) {
    62                  VariableNameDeclaration vnd = entry.getKey();
    63
    64                  if (vnd.getAccessNodeParent() instanceof ASTFormalParameter) {
    65                      // no definition/undefinition/references for parameters
    66                      continue;
    67                  } else if (vnd.getAccessNodeParent().getFirstChildOfType(ASTVariableInitializer.class) != null) {
    68                      // add definition for initialized variables
    69                      addVariableAccess(
    70                              vnd.getNode(),
    71                              new VariableAccess(VariableAccess.DEFINITION, vnd.getImage()),
    72                              inode.getFlow());
    73                  }
    74                  undefinitions.add(new VariableAccess(VariableAccess.UNDEFINITION, vnd.getImage()));
    75
    76                  for (NameOccurrence occurrence: entry.getValue()) {
    77                      addAccess(occurrence, inode);
    78                  }
    79              }
    80          }
    81          return undefinitions;
    82      }
    83
    84      private Set<Map<VariableNameDeclaration, List<NameOccurrence>>> collectDeclarations(IDataFlowNode inode) {
    85          Set<Map<VariableNameDeclaration, List<NameOccurrence>>> decls = new HashSet<Map<VariableNameDeclaration, List<NameOccurrence>>>();
    86          Map<VariableNameDeclaration, List<NameOccurrence>> varDecls;
    87          for (int i = 0; i < inode.getFlow().size(); i++) {
    88              IDataFlowNode n = inode.getFlow().get(i);
    89              if (n instanceof StartOrEndDataFlowNode) {
    90                  continue;
    91              }
    92              varDecls = n.getSimpleNode().getScope().getVariableDeclarations();
    93              if (!decls.contains(varDecls)) {
    94                  decls.add(varDecls);
    95              }
    96          }
    97          return decls;
    98      }
    99
    100     private void addAccess(NameOccurrence occurrence, IDataFlowNode inode) {
    101         if (occurrence.isOnLeftHandSide()) {
    102             this.addVariableAccess(occurrence.getLocation(), new VariableAccess(VariableAccess.DEFINITION, occurrence.getImage()), inode.getFlow());
    103         } else if (occurrence.isOnRightHandSide() || (!occurrence.isOnLeftHandSide() && !occurrence.isOnRightHandSide())) {
    104             this.addVariableAccess(occurrence.getLocation(), new VariableAccess(VariableAccess.REFERENCING, occurrence.getImage()), inode.getFlow());
    105         }
    106     }
    107
    108     /**
    109      * Adds a VariableAccess to a dataflow node.
    110      * @param node location of the access of a variable
    111      * @param va variable access to add
    112      * @param flow dataflownodes that can contain the node.
    113      */
    114     private void addVariableAccess(SimpleNode node, VariableAccess va, List flow) {
    115         // backwards to find the right inode (not a method declaration)
    116         for (int i = flow.size()-1; i > 0; i--) {
    117             IDataFlowNode inode = (IDataFlowNode) flow.get(i);
    118             if (inode.getSimpleNode() == null) {
    119                 continue;
    120             }
    121
    122             List<? extends SimpleNode> children = inode.getSimpleNode().findChildrenOfType(node.getClass());
    123             for (SimpleNode n: children) {
    124                 if (node.equals(n)) {
    125                     List<VariableAccess> v = new ArrayList<VariableAccess>();
    126                     v.add(va);
    127                     inode.setVariableAccess(v);
    128                     return;
    129                 }
    130             }
    131         }
    132     }
    133
    134 }