DataflowAnomalyAnalysisRule.java
001 /**
002  * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
003  */
004 package net.sourceforge.pmd.lang.java.rule.controversial;
005 
006 import java.text.MessageFormat;
007 import java.util.ArrayList;
008 import java.util.HashMap;
009 import java.util.Iterator;
010 import java.util.List;
011 import java.util.Map;
012 
013 import net.sourceforge.pmd.RuleContext;
014 import net.sourceforge.pmd.lang.ast.Node;
015 import net.sourceforge.pmd.lang.dfa.DataFlowNode;
016 import net.sourceforge.pmd.lang.dfa.VariableAccess;
017 import net.sourceforge.pmd.lang.dfa.pathfinder.CurrentPath;
018 import net.sourceforge.pmd.lang.dfa.pathfinder.DAAPathFinder;
019 import net.sourceforge.pmd.lang.dfa.pathfinder.Executable;
020 import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
021 import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
022 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
023 import net.sourceforge.pmd.lang.rule.properties.IntegerProperty;
024 
025 /**
026  * Starts path search for each method and runs code if found.
027  *
028  @author raik
029  @author Sven Jacob
030  */
031 public class DataflowAnomalyAnalysisRule extends AbstractJavaRule implements Executable {
032     private RuleContext rc;
033     private List<DaaRuleViolation> daaRuleViolations;
034     private int maxRuleViolations;
035     private int currentRuleViolationCount;
036 
037     private static final IntegerProperty MAX_PATH_DESCRIPTOR = new IntegerProperty(
038             "maxPaths""Maximum number of checked paths per method. A lower value will increase the performance of the rule but may decrease anomalies found."100800010001.0f
039             );
040 
041     private static final IntegerProperty MAX_VIOLATIONS_DESCRIPTOR = new IntegerProperty(
042             "maxViolations""Maximum number of anomalies per class"120001002.0f
043             );
044 
045     private static class Usage {
046         public int accessType;
047         public DataFlowNode node;
048 
049         public Usage(int accessType, DataFlowNode node) {
050             this.accessType = accessType;
051             this.node = node;
052         }
053 
054         public String toString() {
055             return "accessType = " + accessType + ", line = " + node.getLine();
056         }
057     }
058     
059     public DataflowAnomalyAnalysisRule() {
060   definePropertyDescriptor(MAX_PATH_DESCRIPTOR);
061   definePropertyDescriptor(MAX_VIOLATIONS_DESCRIPTOR);
062     }
063 
064     public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
065         this.maxRuleViolations = getProperty(MAX_VIOLATIONS_DESCRIPTOR);
066         this.currentRuleViolationCount = 0;
067         return super.visit(node, data);
068     }
069 
070     public Object visit(ASTMethodDeclaration methodDeclaration, Object data) {
071         this.rc = (RuleContextdata;
072         this.daaRuleViolations = new ArrayList<DaaRuleViolation>();
073 
074         final DataFlowNode node = methodDeclaration.getDataFlowNode().getFlow().get(0);
075 
076         final DAAPathFinder pathFinder = new DAAPathFinder(node, this, getProperty(MAX_PATH_DESCRIPTOR));
077         pathFinder.run();
078 
079         super.visit(methodDeclaration, data);
080         return data;
081     }
082 
083     public void execute(CurrentPath path) {
084         if (maxNumberOfViolationsReached()) {
085             // dont execute this path if the limit is already reached
086             return;
087         }
088 
089         final Map<String, Usage> hash = new HashMap<String, Usage>();
090 
091         final Iterator<DataFlowNode> pathIterator = path.iterator();
092         while (pathIterator.hasNext()) {
093             // iterate all nodes in this path
094             DataFlowNode inode = pathIterator.next();
095             if (inode.getVariableAccess() != null) {
096                 // iterate all variables of this node
097                 for (int g = 0; g < inode.getVariableAccess().size(); g++) {
098                     final VariableAccess va = inode.getVariableAccess().get(g);
099 
100                     // get the last usage of the current variable
101                     final Usage lastUsage = hash.get(va.getVariableName());
102                     if (lastUsage != null) {
103                         // there was a usage to this variable before
104                         checkVariableAccess(inode, va, lastUsage);
105                     }
106 
107                     final Usage newUsage = new Usage(va.getAccessType(), inode);
108                     // put the new usage for the variable
109                     hash.put(va.getVariableName(), newUsage);
110                 }
111             }
112         }
113     }
114 
115     private void checkVariableAccess(DataFlowNode inode, VariableAccess va, final Usage u) {
116         // get the start and end line
117         final int startLine = u.node.getLine();
118         final int endLine = inode.getLine();
119 
120         final Node lastNode = inode.getNode();
121         final Node firstNode = u.node.getNode();
122 
123         if (va.accessTypeMatches(u.accessType&& va.isDefinition() ) { // DD
124             addDaaViolation(rc, lastNode, "DD", va.getVariableName(), startLine, endLine);
125         else if (u.accessType == VariableAccess.UNDEFINITION && va.isReference()) { // UR
126             addDaaViolation(rc, lastNode, "UR", va.getVariableName(), startLine, endLine);
127         else if (u.accessType == VariableAccess.DEFINITION && va.isUndefinition()) { // DU
128             addDaaViolation(rc, firstNode, "DU", va.getVariableName(), startLine, endLine);
129         }
130     }
131 
132     /**
133      * Adds a daa violation to the report.
134      */
135     private final void addDaaViolation(Object data, Node node, String type, String var, int startLine, int endLine) {
136         if (!maxNumberOfViolationsReached()
137                 && !violationAlreadyExists(type, var, startLine, endLine)
138                 && node != null) {
139             final RuleContext ctx = (RuleContextdata;
140             String msg = type;
141             if (getMessage() != null) {
142                 msg = MessageFormat.format(getMessage(), type, var, startLine, endLine);
143             }
144             final DaaRuleViolation violation = new DaaRuleViolation(this, ctx, node, type, msg, var, startLine, endLine);
145             ctx.getReport().addRuleViolation(violation);
146             this.daaRuleViolations.add(violation);
147             this.currentRuleViolationCount++;
148       }
149     }
150 
151     /**
152      * Maximum number of violations was already reached?
153      @return <code>true</code> if the maximum number of violations was reached,
154      <code>false</code> otherwise.
155      */
156     private boolean maxNumberOfViolationsReached() {
157         return this.currentRuleViolationCount >= this.maxRuleViolations;
158     }
159 
160     /**
161      * Checks if a violation already exists.
162      * This is needed because on the different paths same anomalies can occur.
163      @param type
164      @param var
165      @param startLine
166      @param endLine
167      @return true if the violation already was added to the report
168      */
169     private boolean violationAlreadyExists(String type, String var, int startLine, int endLine) {
170         for(DaaRuleViolation violation: this.daaRuleViolations) {
171             if ((violation.getBeginLine() == startLine)
172                     && (violation.getEndLine() == endLine)
173                     && violation.getType().equals(type)
174                     && violation.getVariableName().equals(var)) {
175                 return true;
176             }
177         }
178         return false;
179     }
180 }