View Javadoc

    1   /**
    2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
    3    */
    4   package net.sourceforge.pmd.lang.java.rule.controversial;
    5
    6   import java.text.MessageFormat;
    7   import java.util.ArrayList;
    8   import java.util.HashMap;
    9   import java.util.Iterator;
    10  import java.util.List;
    11  import java.util.Map;
    12
    13  import net.sourceforge.pmd.RuleContext;
    14  import net.sourceforge.pmd.lang.ast.Node;
    15  import net.sourceforge.pmd.lang.dfa.DataFlowNode;
    16  import net.sourceforge.pmd.lang.dfa.VariableAccess;
    17  import net.sourceforge.pmd.lang.dfa.pathfinder.CurrentPath;
    18  import net.sourceforge.pmd.lang.dfa.pathfinder.DAAPathFinder;
    19  import net.sourceforge.pmd.lang.dfa.pathfinder.Executable;
    20  import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
    21  import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
    22  import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
    23  import net.sourceforge.pmd.lang.rule.properties.IntegerProperty;
    24
    25  /**
    26   * Starts path search for each method and runs code if found.
    27   *
    28   * @author raik
    29   * @author Sven Jacob
    30   */
    31  public class DataflowAnomalyAnalysisRule extends AbstractJavaRule implements Executable {
    32      private RuleContext rc;
    33      private List<DaaRuleViolation> daaRuleViolations;
    34      private int maxRuleViolations;
    35      private int currentRuleViolationCount;
    36
    37      private static final IntegerProperty MAX_PATH_DESCRIPTOR = new IntegerProperty(
    38              "maxPaths", "Maximum number of checked paths per method. A lower value will increase the performance of the rule but may decrease anomalies found.", 100, 8000, 1000, 1.0f
    39              );
    40
    41      private static final IntegerProperty MAX_VIOLATIONS_DESCRIPTOR = new IntegerProperty(
    42              "maxViolations", "Maximum number of anomalies per class", 1, 2000, 100, 2.0f
    43              );
    44
    45      private static class Usage {
    46          public int accessType;
    47          public DataFlowNode node;
    48
    49          public Usage(int accessType, DataFlowNode node) {
    50              this.accessType = accessType;
    51              this.node = node;
    52          }
    53
    54          public String toString() {
    55              return "accessType = " + accessType + ", line = " + node.getLine();
    56          }
    57      }
    58
    59      public DataflowAnomalyAnalysisRule() {
    60  	definePropertyDescriptor(MAX_PATH_DESCRIPTOR);
    61  	definePropertyDescriptor(MAX_VIOLATIONS_DESCRIPTOR);
    62      }
    63
    64      public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
    65          maxRuleViolations = getProperty(MAX_VIOLATIONS_DESCRIPTOR);
    66          currentRuleViolationCount = 0;
    67          return super.visit(node, data);
    68      }
    69
    70      public Object visit(ASTMethodDeclaration methodDeclaration, Object data) {
    71          rc = (RuleContext) data;
    72          daaRuleViolations = new ArrayList<DaaRuleViolation>();
    73
    74          final DataFlowNode node = methodDeclaration.getDataFlowNode().getFlow().get(0);
    75
    76          final DAAPathFinder pathFinder = new DAAPathFinder(node, this, getProperty(MAX_PATH_DESCRIPTOR));
    77          pathFinder.run();
    78
    79          super.visit(methodDeclaration, data);
    80          return data;
    81      }
    82
    83      public void execute(CurrentPath path) {
    84
    85          if (maxNumberOfViolationsReached()) return;
    86
    87          Map<String, Usage> usagesByVarName = new HashMap<String, Usage>();
    88
    89          Iterator<DataFlowNode> pathIterator = path.iterator();
    90          while (pathIterator.hasNext()) {
    91              // iterate all nodes in this path
    92              DataFlowNode inode = pathIterator.next();
    93              if (inode.getVariableAccess() != null) {
    94                  // iterate all variables of this node
    95                  for (VariableAccess va : inode.getVariableAccess()) {
    96
    97                      // get the last usage of the current variable
    98                      Usage lastUsage = usagesByVarName.get(va.getVariableName());
    99                      if (lastUsage != null) {
    100                         // there was a usage to this variable before
    101                         checkVariableAccess(inode, va, lastUsage);
    102                     }
    103
    104                     Usage newUsage = new Usage(va.getAccessType(), inode);
    105                     // put the new usage for the variable
    106                     usagesByVarName.put(va.getVariableName(), newUsage);
    107                 }
    108             }
    109         }
    110     }
    111
    112     private void checkVariableAccess(DataFlowNode inode, VariableAccess va, final Usage u) {
    113         // get the start and end line
    114         int startLine = u.node.getLine();
    115         int endLine = inode.getLine();
    116
    117         Node lastNode = inode.getNode();
    118         Node firstNode = u.node.getNode();
    119
    120         if (va.accessTypeMatches(u.accessType) && va.isDefinition() ) { // DD
    121             addDaaViolation(rc, lastNode, "DD", va.getVariableName(), startLine, endLine);
    122         } else if (u.accessType == VariableAccess.UNDEFINITION && va.isReference()) { // UR
    123             addDaaViolation(rc, lastNode, "UR", va.getVariableName(), startLine, endLine);
    124         } else if (u.accessType == VariableAccess.DEFINITION && va.isUndefinition()) { // DU
    125             addDaaViolation(rc, firstNode, "DU", va.getVariableName(), startLine, endLine);
    126         }
    127     }
    128
    129     /**
    130      * Adds a daa violation to the report.
    131      */
    132     private final void addDaaViolation(Object data, Node node, String type, String var, int startLine, int endLine) {
    133         if (!maxNumberOfViolationsReached()
    134                 && !violationAlreadyExists(type, var, startLine, endLine)
    135                 && node != null) {
    136             RuleContext ctx = (RuleContext) data;
    137             String msg = type;
    138             if (getMessage() != null) {
    139                 msg = MessageFormat.format(getMessage(), type, var, startLine, endLine);
    140             }
    141             DaaRuleViolation violation = new DaaRuleViolation(this, ctx, node, type, msg, var, startLine, endLine);
    142             ctx.getReport().addRuleViolation(violation);
    143             daaRuleViolations.add(violation);
    144             currentRuleViolationCount++;
    145       }
    146     }
    147
    148     /**
    149      * Maximum number of violations was already reached?
    150      * @return <code>true</code> if the maximum number of violations was reached,
    151      * <code>false</code> otherwise.
    152      */
    153     private boolean maxNumberOfViolationsReached() {
    154         return currentRuleViolationCount >= maxRuleViolations;
    155     }
    156
    157     /**
    158      * Checks if a violation already exists.
    159      * This is needed because on the different paths same anomalies can occur.
    160      * @param type
    161      * @param var
    162      * @param startLine
    163      * @param endLine
    164      * @return true if the violation already was added to the report
    165      */
    166     private boolean violationAlreadyExists(String type, String var, int startLine, int endLine) {
    167         for(DaaRuleViolation violation: daaRuleViolations) {
    168             if ((violation.getBeginLine() == startLine)
    169                     && (violation.getEndLine() == endLine)
    170                     && violation.getType().equals(type)
    171                     && violation.getVariableName().equals(var)) {
    172                 return true;
    173             }
    174         }
    175         return false;
    176     }
    177 }