1
2
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
27
28
29
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",
39 "Maximum number of checked paths per method. A lower value will increase the performance of the rule but may decrease anomalies found.",
40 100, 8000, 1000, 1.0f);
41
42 private static final IntegerProperty MAX_VIOLATIONS_DESCRIPTOR = new IntegerProperty("maxViolations",
43 "Maximum number of anomalies per class", 1, 2000, 100, 2.0f);
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()) {
86 return;
87 }
88
89 Map<String, Usage> usagesByVarName = new HashMap<String, Usage>();
90
91 Iterator<DataFlowNode> pathIterator = path.iterator();
92 while (pathIterator.hasNext()) {
93
94 DataFlowNode inode = pathIterator.next();
95 if (inode.getVariableAccess() != null) {
96
97 for (VariableAccess va : inode.getVariableAccess()) {
98
99
100 Usage lastUsage = usagesByVarName.get(va.getVariableName());
101 if (lastUsage != null) {
102
103 checkVariableAccess(inode, va, lastUsage);
104 }
105
106 Usage newUsage = new Usage(va.getAccessType(), inode);
107
108 usagesByVarName.put(va.getVariableName(), newUsage);
109 }
110 }
111 }
112 }
113
114 private void checkVariableAccess(DataFlowNode inode, VariableAccess va, final Usage u) {
115
116 int startLine = u.node.getLine();
117 int endLine = inode.getLine();
118
119 Node lastNode = inode.getNode();
120 Node firstNode = u.node.getNode();
121
122 if (va.accessTypeMatches(u.accessType) && va.isDefinition()) {
123 addDaaViolation(rc, lastNode, "DD", va.getVariableName(), startLine, endLine);
124 } else if (u.accessType == VariableAccess.UNDEFINITION && va.isReference()) {
125 addDaaViolation(rc, lastNode, "UR", va.getVariableName(), startLine, endLine);
126 } else if (u.accessType == VariableAccess.DEFINITION && va.isUndefinition()) {
127 addDaaViolation(rc, firstNode, "DU", va.getVariableName(), startLine, endLine);
128 }
129 }
130
131
132
133
134 private final void addDaaViolation(Object data, Node node, String type, String var, int startLine, int endLine) {
135 if (!maxNumberOfViolationsReached() && !violationAlreadyExists(type, var, startLine, endLine) && 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
150
151
152
153
154 private boolean maxNumberOfViolationsReached() {
155 return currentRuleViolationCount >= maxRuleViolations;
156 }
157
158
159
160
161
162
163
164
165
166
167
168 private boolean violationAlreadyExists(String type, String var, int startLine, int endLine) {
169 for (DaaRuleViolation violation : daaRuleViolations) {
170 if (violation.getBeginLine() == startLine && violation.getEndLine() == endLine
171 && violation.getType().equals(type) && violation.getVariableName().equals(var)) {
172 return true;
173 }
174 }
175 return false;
176 }
177 }