Skip to content

Commit 8a1d9e0

Browse files
authored
Replace Xlint deprecation check with custom errorprone check (#8061)
1 parent bfda970 commit 8a1d9e0

24 files changed

Lines changed: 462 additions & 89 deletions

File tree

api/incubator/src/main/java/io/opentelemetry/api/incubator/common/ArrayBackedExtendedAttributes.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ private Value<?> getAsValue(String keyName) {
119119
return null;
120120
}
121121

122-
@SuppressWarnings("unchecked")
122+
@SuppressWarnings({"unchecked", "deprecation"}) // deprecation: EXTENDED_ATTRIBUTES
123123
@Nullable
124124
private static Value<?> asValue(ExtendedAttributeType type, Object value) {
125125
switch (type) {

api/incubator/src/main/java/io/opentelemetry/api/incubator/common/ArrayBackedExtendedAttributesBuilder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public <T> ExtendedAttributesBuilder put(ExtendedAttributeKey<T> key, T value) {
5656
return this;
5757
}
5858

59-
@SuppressWarnings("unchecked")
59+
@SuppressWarnings({"unchecked", "deprecation"}) // deprecation: EXTENDED_ATTRIBUTES
6060
private void putValue(ExtendedAttributeKey<?> key, Value<?> valueObj) {
6161
// Convert VALUE type to narrower type when possible
6262
String keyName = key.getKey();

buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ tasks {
9191
"-Xlint:-options",
9292
"-Xlint:-serial",
9393
"-Xlint:-this-escape",
94+
// We suppress the "deprecation" warning because --release 8 causes javac to warn on
95+
// importing deprecated classes (fixed in JDK 9+, see https://bugs.openjdk.org/browse/JDK-8032211).
96+
// We use a custom Error Prone check instead (OtelDeprecatedApiUsage).
97+
"-Xlint:-deprecation",
9498
// Fail build on any warning
9599
"-Werror",
96100
),
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.gradle.customchecks;
7+
8+
import static com.google.errorprone.BugPattern.LinkType.NONE;
9+
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
10+
11+
import com.google.errorprone.BugPattern;
12+
import com.google.errorprone.VisitorState;
13+
import com.google.errorprone.bugpatterns.BugChecker;
14+
import com.google.errorprone.matchers.Description;
15+
import com.google.errorprone.util.ASTHelpers;
16+
import com.sun.source.tree.IdentifierTree;
17+
import com.sun.source.tree.ImportTree;
18+
import com.sun.source.tree.MemberReferenceTree;
19+
import com.sun.source.tree.MemberSelectTree;
20+
import com.sun.source.tree.MethodInvocationTree;
21+
import com.sun.source.tree.NewClassTree;
22+
import com.sun.source.tree.Tree;
23+
import com.sun.tools.javac.code.Symbol;
24+
import javax.annotation.Nullable;
25+
26+
/**
27+
* Error Prone check that detects usage of deprecated APIs.
28+
*
29+
* <p>This is similar to javac's -Xlint:deprecation but properly honors {@code @SuppressWarnings}
30+
* (including on import statements, which javac doesn't support with --release 8 due to <a
31+
* href="https://bugs.openjdk.org/browse/JDK-8032211">JDK-8032211</a>).
32+
*
33+
* <p>Can be suppressed with {@code @SuppressWarnings("deprecation")}.
34+
*/
35+
@BugPattern(
36+
summary = "Use of deprecated API",
37+
severity = ERROR,
38+
linkType = NONE,
39+
altNames = "deprecation", // so it can be suppressed with @SuppressWarnings("deprecation")
40+
suppressionAnnotations = SuppressWarnings.class)
41+
public class OtelDeprecatedApiUsage extends BugChecker
42+
implements BugChecker.MethodInvocationTreeMatcher,
43+
BugChecker.NewClassTreeMatcher,
44+
BugChecker.MemberSelectTreeMatcher,
45+
BugChecker.MemberReferenceTreeMatcher,
46+
BugChecker.IdentifierTreeMatcher {
47+
48+
private static final long serialVersionUID = 1L;
49+
50+
@Override
51+
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
52+
Symbol sym = ASTHelpers.getSymbol(tree);
53+
return checkDeprecated(sym, tree, state);
54+
}
55+
56+
@Override
57+
public Description matchNewClass(NewClassTree tree, VisitorState state) {
58+
Symbol sym = ASTHelpers.getSymbol(tree);
59+
return checkDeprecated(sym, tree, state);
60+
}
61+
62+
@Override
63+
public Description matchMemberSelect(MemberSelectTree tree, VisitorState state) {
64+
Symbol sym = ASTHelpers.getSymbol(tree);
65+
return checkDeprecated(sym, tree, state);
66+
}
67+
68+
@Override
69+
public Description matchMemberReference(MemberReferenceTree tree, VisitorState state) {
70+
Symbol sym = ASTHelpers.getSymbol(tree);
71+
return checkDeprecated(sym, tree, state);
72+
}
73+
74+
@Override
75+
public Description matchIdentifier(IdentifierTree tree, VisitorState state) {
76+
Symbol sym = ASTHelpers.getSymbol(tree);
77+
return checkDeprecated(sym, tree, state);
78+
}
79+
80+
private Description checkDeprecated(Symbol sym, Tree tree, VisitorState state) {
81+
if (sym == null) {
82+
return Description.NO_MATCH;
83+
}
84+
85+
// Don't warn on import statements
86+
if (isInsideImport(state)) {
87+
return Description.NO_MATCH;
88+
}
89+
90+
// Check if the symbol itself is deprecated
91+
if (!isDeprecated(sym, state)) {
92+
return Description.NO_MATCH;
93+
}
94+
95+
// Don't warn if we're inside a deprecated context (class, method, etc.)
96+
if (isInsideDeprecatedCode(state)) {
97+
return Description.NO_MATCH;
98+
}
99+
100+
// Don't warn if the deprecated symbol is in the same top-level class (matches javac behavior)
101+
if (isInSameTopLevelClass(sym, state)) {
102+
return Description.NO_MATCH;
103+
}
104+
105+
return buildDescription(tree).setMessage("Use of deprecated API: " + sym).build();
106+
}
107+
108+
private static boolean isInsideImport(VisitorState state) {
109+
for (Tree tree : state.getPath()) {
110+
if (tree instanceof ImportTree) {
111+
return true;
112+
}
113+
}
114+
return false;
115+
}
116+
117+
private static boolean isDeprecated(Symbol sym, VisitorState state) {
118+
// First try the symbol's isDeprecated() method
119+
if (sym.isDeprecated()) {
120+
return true;
121+
}
122+
// Also check for @Deprecated annotation (some symbols may not have flag set)
123+
return ASTHelpers.hasAnnotation(sym, "java.lang.Deprecated", state);
124+
}
125+
126+
private static boolean isInsideDeprecatedCode(VisitorState state) {
127+
// Check enclosing elements (method, class) for @Deprecated
128+
// Skip the first element which is the current node being checked
129+
boolean first = true;
130+
for (Tree tree : state.getPath()) {
131+
if (first) {
132+
first = false;
133+
continue;
134+
}
135+
Symbol sym = ASTHelpers.getSymbol(tree);
136+
if (sym != null && isDeprecated(sym, state)) {
137+
return true;
138+
}
139+
}
140+
return false;
141+
}
142+
143+
private static boolean isInSameTopLevelClass(Symbol sym, VisitorState state) {
144+
// Get the top-level class containing the deprecated symbol
145+
Symbol.ClassSymbol deprecatedTopLevel = getTopLevelClass(sym);
146+
if (deprecatedTopLevel == null) {
147+
return false;
148+
}
149+
150+
// Get the top-level class containing the current usage by walking the tree path
151+
// Skip the first element (the node being checked) to find the enclosing class
152+
Symbol.ClassSymbol usageTopLevel = null;
153+
boolean first = true;
154+
for (Tree tree : state.getPath()) {
155+
if (first) {
156+
first = false;
157+
continue;
158+
}
159+
Symbol treeSym = ASTHelpers.getSymbol(tree);
160+
if (treeSym instanceof Symbol.ClassSymbol classSymbol) {
161+
usageTopLevel = getTopLevelClass(classSymbol);
162+
if (usageTopLevel != null) {
163+
break;
164+
}
165+
}
166+
}
167+
if (usageTopLevel == null) {
168+
return false;
169+
}
170+
171+
return deprecatedTopLevel.equals(usageTopLevel);
172+
}
173+
174+
@Nullable
175+
private static Symbol.ClassSymbol getTopLevelClass(Symbol sym) {
176+
Symbol current = sym;
177+
while (current != null) {
178+
if (current instanceof Symbol.ClassSymbol classSymbol) {
179+
Symbol owner = classSymbol.owner;
180+
// Top-level class is owned by a package
181+
if (owner instanceof Symbol.PackageSymbol) {
182+
return classSymbol;
183+
}
184+
}
185+
current = current.owner;
186+
}
187+
return null;
188+
}
189+
}

custom-checks/src/main/java/io/opentelemetry/gradle/customchecks/OtelInternalJavadoc.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
package io.opentelemetry.gradle.customchecks;
77

8+
import static com.google.errorprone.BugPattern.LinkType.NONE;
89
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
910

1011
import com.google.errorprone.BugPattern;
@@ -26,7 +27,8 @@
2627
+ "\", or \""
2728
+ OtelInternalJavadoc.EXPECTED_INTERNAL_COMMENT_V2
2829
+ "\"",
29-
severity = WARNING)
30+
severity = WARNING,
31+
linkType = NONE)
3032
public class OtelInternalJavadoc extends BugChecker implements BugChecker.ClassTreeMatcher {
3133

3234
private static final long serialVersionUID = 1L;

custom-checks/src/main/java/io/opentelemetry/gradle/customchecks/OtelPrivateConstructorForUtilityClass.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
package io.opentelemetry.gradle.customchecks;
77

8+
import static com.google.errorprone.BugPattern.LinkType.NONE;
89
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
910
import static com.google.errorprone.matchers.Description.NO_MATCH;
1011

@@ -18,7 +19,8 @@
1819
@BugPattern(
1920
summary =
2021
"Classes which are not intended to be instantiated should be made non-instantiable with a private constructor. This includes utility classes (classes with only static members), and the main class.",
21-
severity = WARNING)
22+
severity = WARNING,
23+
linkType = NONE)
2224
public class OtelPrivateConstructorForUtilityClass extends BugChecker
2325
implements BugChecker.ClassTreeMatcher {
2426

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
io.opentelemetry.gradle.customchecks.OtelDeprecatedApiUsage
12
io.opentelemetry.gradle.customchecks.OtelInternalJavadoc
23
io.opentelemetry.gradle.customchecks.OtelPrivateConstructorForUtilityClass

0 commit comments

Comments
 (0)