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", "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
92 DataFlowNode inode = pathIterator.next();
93 if (inode.getVariableAccess() != null) {
94
95 for (VariableAccess va : inode.getVariableAccess()) {
96
97
98 Usage lastUsage = usagesByVarName.get(va.getVariableName());
99 if (lastUsage != null) {
100
101 checkVariableAccess(inode, va, lastUsage);
102 }
103
104 Usage newUsage = new Usage(va.getAccessType(), inode);
105
106 usagesByVarName.put(va.getVariableName(), newUsage);
107 }
108 }
109 }
110 }
111
112 private void checkVariableAccess(DataFlowNode inode, VariableAccess va, final Usage u) {
113
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() ) {
121 addDaaViolation(rc, lastNode, "DD", va.getVariableName(), startLine, endLine);
122 } else if (u.accessType == VariableAccess.UNDEFINITION && va.isReference()) {
123 addDaaViolation(rc, lastNode, "UR", va.getVariableName(), startLine, endLine);
124 } else if (u.accessType == VariableAccess.DEFINITION && va.isUndefinition()) {
125 addDaaViolation(rc, firstNode, "DU", va.getVariableName(), startLine, endLine);
126 }
127 }
128
129
130
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
150
151
152
153 private boolean maxNumberOfViolationsReached() {
154 return currentRuleViolationCount >= maxRuleViolations;
155 }
156
157
158
159
160
161
162
163
164
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 }