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 }