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.", 100, 8000, 1000, 1.0f
039 );
040
041 private static final IntegerProperty MAX_VIOLATIONS_DESCRIPTOR = new IntegerProperty(
042 "maxViolations", "Maximum number of anomalies per class", 1, 2000, 100, 2.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 = (RuleContext) data;
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 = (RuleContext) data;
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 }
|