-
-
Notifications
You must be signed in to change notification settings - Fork 4
/
IllegalPassedClass.java
140 lines (126 loc) · 5.06 KB
/
IllegalPassedClass.java
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package jp.skypencil.errorprone.slf4j;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import com.google.auto.service.AutoService;
import com.google.errorprone.BugPattern;
import com.google.errorprone.ErrorProneVersion;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Description.Builder;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.matchers.method.MethodMatchers;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.VariableTree;
import com.sun.source.util.TreeScanner;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.code.Symbol.TypeSymbol;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.Type.ClassType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import javax.lang.model.element.Modifier;
@BugPattern(
name = "Slf4jIllegalPassedClass",
summary = "LoggerFactory.getLogger(Class) should get the class that defines variable",
tags = {"SLF4J"},
severity = WARNING)
@AutoService(BugChecker.class)
public class IllegalPassedClass extends BugChecker implements MethodInvocationTreeMatcher {
private static final long serialVersionUID = 8309704818374164342L;
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
TypeSymbol type = tree.accept(new LoggerInitializerVisitor(), state);
if (type == null) {
return Description.NO_MATCH;
}
List<ClassSymbol> enclosingClasses = listEnclosingClasses(state);
String message =
String.format(
"LoggerFactory.getLogger(Class) should get one of [%s] but it gets %s",
enclosingClasses.stream().map(ClassSymbol::className).collect(Collectors.joining(",")),
type.getSimpleName());
Builder builder =
Description.builder(
tree,
"Slf4jIllegalPassedClass",
"https://github.com/KengoTODA/findbugs-slf4j#slf4j_illegal_passed_class",
WARNING,
message);
for (ClassSymbol enclosingSymbol : enclosingClasses) {
if (ASTHelpers.isSameType(type.type, enclosingSymbol.type, state)) {
return Description.NO_MATCH;
}
}
VariableTree variableTree = state.findEnclosing(VariableTree.class);
if (variableTree != null && !variableTree.getModifiers().getFlags().contains(Modifier.STATIC)) {
builder.addFix(
SuggestedFix.builder().replace(tree.getArguments().get(0), "getClass()").build());
}
for (ClassSymbol enclosingSymbol : enclosingClasses) {
builder.addFix(
SuggestedFix.builder()
.replace(tree.getArguments().get(0), enclosingSymbol.getSimpleName() + ".class")
.build());
}
return builder.build();
}
private List<ClassSymbol> listEnclosingClasses(VisitorState state) {
ClassTree enclosing = state.findEnclosing(ClassTree.class);
if (enclosing == null) {
return Collections.emptyList();
}
List<ClassSymbol> result = new ArrayList<>();
ClassSymbol enclosingSymbol = ASTHelpers.getSymbol(enclosing);
while (enclosingSymbol != null) {
result.add(enclosingSymbol);
enclosingSymbol = ASTHelpers.enclosingClass(enclosingSymbol);
}
return result;
}
private static final class LoggerInitializerVisitor
extends TreeScanner<TypeSymbol, VisitorState> {
@Override
public TypeSymbol visitMethodInvocation(MethodInvocationTree node, VisitorState state) {
if (!MatherHolder.isGetLogger.matches(node, state)) {
return null;
}
ExpressionTree arg = node.getArguments().get(0);
ClassType type = (ClassType) ASTHelpers.getType(arg);
Type typeParameter = type.getTypeArguments().get(0);
return typeParameter.asElement();
}
}
static final class MatherHolder {
static {
boolean supported =
ErrorProneVersion.loadVersionFromPom()
.transform(MatherHolder::checkSupportedVersion)
.or(true);
if (!supported) {
throw new IllegalStateException("Run this rule with Errorprone 2.11.0 or later.");
}
}
static boolean checkSupportedVersion(String version) {
String[] split = version.split("\\.", 3);
int major = Integer.parseInt(split[0], 10);
if (major > 2) {
// assuming this version uses new API definition
return true;
}
int minor = Integer.parseInt(split[1], 10);
return minor >= 11;
}
static Matcher<ExpressionTree> isGetLogger =
MethodMatchers.staticMethod()
.onClass("org.slf4j.LoggerFactory")
.named("getLogger")
.withParameters("java.lang.Class");
}
}