-
Notifications
You must be signed in to change notification settings - Fork 724
Expand file tree
/
Copy pathAbstractCallToDeprecatedCodeChecker.java
More file actions
135 lines (118 loc) · 5.13 KB
/
Copy pathAbstractCallToDeprecatedCodeChecker.java
File metadata and controls
135 lines (118 loc) · 5.13 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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/*
* SonarQube Java
* Copyright (C) SonarSource Sàrl
* mailto:info AT sonarsource DOT com
*
* You can redistribute and/or modify this program under the terms of
* the Sonar Source-Available License Version 1, as published by SonarSource Sàrl.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the Sonar Source-Available License for more details.
*
* You should have received a copy of the Sonar Source-Available License
* along with this program; if not, see https://sonarsource.com/license/ssal/
*/
package org.sonar.java.checks;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import org.sonar.plugins.java.api.IssuableSubscriptionVisitor;
import org.sonar.plugins.java.api.JavaFileScannerContext;
import org.sonar.plugins.java.api.semantic.Symbol;
import org.sonar.plugins.java.api.semantic.SymbolMetadata.AnnotationValue;
import org.sonar.plugins.java.api.tree.ClassTree;
import org.sonar.plugins.java.api.tree.IdentifierTree;
import org.sonar.plugins.java.api.tree.MethodTree;
import org.sonar.plugins.java.api.tree.Tree;
import org.sonar.plugins.java.api.tree.VariableTree;
import org.sonarsource.analyzer.commons.collections.ListUtils;
public abstract class AbstractCallToDeprecatedCodeChecker extends IssuableSubscriptionVisitor {
private int nestedDeprecationLevel = 0;
@Override
public final void leaveFile(JavaFileScannerContext context) {
nestedDeprecationLevel = 0;
}
@Override
public final List<Tree.Kind> nodesToVisit() {
return ListUtils.concat(Tree.Kind.CLASS_KINDS, List.of(Tree.Kind.IDENTIFIER, Tree.Kind.METHOD, Tree.Kind.CONSTRUCTOR));
}
@Override
public final void visitNode(Tree tree) {
if (nestedDeprecationLevel == 0) {
if (tree.is(Tree.Kind.IDENTIFIER)) {
IdentifierTree identifierTree = (IdentifierTree) tree;
if (isSimpleNameOfVariableTreeOrVariableIsDeprecated(identifierTree)) {
return;
}
tryGetDeprecatedSymbol(identifierTree).ifPresent(deprecatedSymbol -> checkDeprecatedIdentifier(identifierTree, deprecatedSymbol));
} else if (tree.is(Tree.Kind.METHOD)) {
MethodTree methodTree = (MethodTree) tree;
List<Symbol.MethodSymbol> deprectatedMethods = deprecatedMethodSymbols(methodTree);
if (!deprectatedMethods.isEmpty()) {
checkOverridingMethod(methodTree, deprectatedMethods);
}
}
}
if (isDeprecatedMethod(tree) || isDeprecatedClassTree(tree)) {
nestedDeprecationLevel++;
}
}
@Override
public final void leaveNode(Tree tree) {
if (isDeprecatedMethod(tree) || isDeprecatedClassTree(tree)) {
nestedDeprecationLevel--;
}
}
private static Optional<Symbol> tryGetDeprecatedSymbol(IdentifierTree identifierTree) {
Symbol symbol = identifierTree.symbol();
if (symbol.isDeprecated()) {
return Optional.of(symbol);
}
if (isConstructor(symbol) && symbol.owner().isDeprecated()) {
return Optional.of(symbol.owner());
}
if (isDeprecatedEnumConstant(symbol)) {
return Optional.of(symbol.type().symbol());
}
return Optional.empty();
}
public static boolean isConstructor(Symbol symbol) {
return symbol.isMethodSymbol() && "<init>".equals(symbol.name());
}
private static boolean isDeprecatedEnumConstant(Symbol symbol) {
return symbol.isVariableSymbol() && symbol.isEnum() && symbol.type().symbol().isDeprecated();
}
abstract void checkDeprecatedIdentifier(IdentifierTree identifierTree, Symbol deprecatedSymbol);
private static boolean isSimpleNameOfVariableTreeOrVariableIsDeprecated(IdentifierTree identifierTree) {
Tree parent = identifierTree.parent();
return parent.is(Tree.Kind.VARIABLE) && (identifierTree.equals(((VariableTree) parent).simpleName()) || ((VariableTree) parent).symbol().isDeprecated());
}
private static List<Symbol.MethodSymbol> deprecatedMethodSymbols(MethodTree methodTree) {
Symbol.MethodSymbol methodSymbol = methodTree.symbol();
if (methodSymbol.isDeprecated()) {
return Collections.emptyList();
}
return methodSymbol.overriddenSymbols()
.stream()
.filter(Symbol.MethodSymbol::isDeprecated)
.toList();
}
abstract void checkOverridingMethod(MethodTree methodTree, List<Symbol.MethodSymbol> deprecatedSymbol);
private static boolean isDeprecatedMethod(Tree tree) {
return tree.is(Tree.Kind.METHOD, Tree.Kind.CONSTRUCTOR) && ((MethodTree) tree).symbol().isDeprecated();
}
private static boolean isDeprecatedClassTree(Tree tree) {
return Tree.Kind.CLASS_KINDS.contains(tree.kind()) && ((ClassTree) tree).symbol().isDeprecated();
}
boolean isFlaggedForRemoval(Symbol deprecatedSymbol) {
List<AnnotationValue> valuesForAnnotation = deprecatedSymbol.metadata().valuesForAnnotation("java.lang.Deprecated");
if (valuesForAnnotation == null) {
return false;
}
return valuesForAnnotation.stream()
.filter(annotationValue -> "forRemoval".equals(annotationValue.name()))
.anyMatch(annotationValue -> Boolean.TRUE.equals(annotationValue.value()));
}
}