Repository navigation
Expand file tree
/
Copy pathNullAssignmentRule.java
More file actions
114 lines (96 loc) · 4.85 KB
/
Copy pathNullAssignmentRule.java
File metadata and controls
114 lines (96 loc) · 4.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.rule.errorprone;
import java.util.Optional;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr;
import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.ASTNamedReferenceExpr;
import net.sourceforge.pmd.lang.java.ast.ASTAssignmentExpression;
import net.sourceforge.pmd.lang.java.ast.ASTConditionalExpression;
import net.sourceforge.pmd.lang.java.ast.ASTNullLiteral;
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator;
import net.sourceforge.pmd.lang.java.ast.ASTVariableId;
import net.sourceforge.pmd.lang.java.ast.JavaNode;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule;
import net.sourceforge.pmd.lang.java.rule.internal.DataflowPass;
import net.sourceforge.pmd.lang.java.rule.internal.DataflowPass.ReachingDefinitionSet;
import net.sourceforge.pmd.lang.java.symbols.JVariableSymbol;
public class NullAssignmentRule extends AbstractJavaRulechainRule {
public NullAssignmentRule() {
super(ASTNullLiteral.class);
}
@Override
public Object visit(ASTNullLiteral node, Object data) {
if (node.getParent() instanceof ASTAssignmentExpression) {
ASTAssignmentExpression assignment = (ASTAssignmentExpression) node.getParent();
if (isAssignmentToFinal(assignment) || isFirstAssignmentToBlankLocal(assignment)) {
return data;
}
if (assignment.getRightOperand() == node) {
asCtx(data).addViolation(node);
}
} else if (node.getParent() instanceof ASTConditionalExpression) {
if (isBadTernary((ASTConditionalExpression) node.getParent(), node)) {
asCtx(data).addViolation(node);
}
}
return data;
}
private boolean isAssignmentToFinal(ASTAssignmentExpression n) {
@Nullable
JVariableSymbol symbol = tryGetLeftOperandSymbol(n);
return symbol != null && symbol.isFinal();
}
private boolean isBadTernary(ASTConditionalExpression ternary, ASTNullLiteral nullLiteral) {
boolean isInitializer;
ASTVariableDeclarator variableDeclarator = ternary.ancestors(ASTVariableDeclarator.class).first();
isInitializer = variableDeclarator != null && variableDeclarator.getInitializer() == ternary;
boolean isThenOrElse = ternary.getThenBranch() == nullLiteral || ternary.getElseBranch() == nullLiteral;
// check for nested ternaries...
ASTConditionalExpression currentTernary = ternary;
while (currentTernary.getParent() instanceof ASTConditionalExpression) {
ASTConditionalExpression parentTernary = (ASTConditionalExpression) currentTernary.getParent();
isThenOrElse &= parentTernary.getThenBranch() == currentTernary || parentTernary.getElseBranch() == currentTernary;
currentTernary = parentTernary;
}
final JavaNode parent = currentTernary.getParent();
boolean isAssignment = parent instanceof ASTAssignmentExpression;
if (isAssignment && isFinalField((ASTAssignmentExpression) parent)) {
isInitializer = true;
}
return isThenOrElse
&& isAssignment
&& !isInitializer;
}
private boolean isFirstAssignmentToBlankLocal(ASTAssignmentExpression assignment) {
boolean isBlankLocalVarAssignment = Optional.ofNullable(tryGetLeftOperandSymbol(assignment))
.map(JVariableSymbol::tryGetNode)
.filter(varId -> varId.getInitializer() == null)
.map(ASTVariableId::isLocalVariable)
.orElse(false);
if (!isBlankLocalVarAssignment) {
return false;
}
ReachingDefinitionSet reaching = DataflowPass.getDataflowResult(assignment.getRoot())
.getReachingDefinitions((ASTNamedReferenceExpr) assignment.getLeftOperand());
// nothing was assigned yet on any path, so this assignment is the first one
return !reaching.isNotFullyKnown() && reaching.getReaching().isEmpty();
}
private @Nullable JVariableSymbol tryGetLeftOperandSymbol(ASTAssignmentExpression expression) {
@NonNull
ASTAssignableExpr leftOperand = expression.getLeftOperand();
if (leftOperand instanceof ASTNamedReferenceExpr) {
@Nullable
JVariableSymbol symbol = ((ASTNamedReferenceExpr) leftOperand).getReferencedSym();
return symbol;
}
return null;
}
private boolean isFinalField(ASTAssignmentExpression expression) {
@Nullable
JVariableSymbol symbol = tryGetLeftOperandSymbol(expression);
return symbol != null && symbol.isField() && symbol.isFinal();
}
}