Skip to content

Commit 6d4d60d

Browse files
committed
UnnecessarilyFullyQualified
1 parent 5aae224 commit 6d4d60d

File tree

20 files changed

+510
-29
lines changed

20 files changed

+510
-29
lines changed

aws-resources/src/main/java/io/opentelemetry/contrib/aws/resource/EcsResource.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,7 @@ static void parseResponse(
193193
DockerImage parsedImage = DockerImage.parse(value);
194194
if (parsedImage != null) {
195195
attrBuilders.put(CONTAINER_IMAGE_NAME, parsedImage.getRepository());
196-
attrBuilders.put(
197-
io.opentelemetry.contrib.aws.resource.IncubatingAttributes.CONTAINER_IMAGE_TAGS,
198-
parsedImage.getTag());
196+
attrBuilders.put(IncubatingAttributes.CONTAINER_IMAGE_TAGS, parsedImage.getTag());
199197
}
200198
break;
201199
case "ImageID":

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

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ plugins {
1010
dependencies {
1111
errorprone("com.google.errorprone:error_prone_core")
1212
errorprone("com.uber.nullaway:nullaway")
13+
errorprone(project(":custom-checks"))
1314
}
1415

1516
val disableErrorProne = properties["disableErrorProne"]?.toString()?.toBoolean() ?: false
@@ -45,10 +46,6 @@ tasks {
4546
// Suggests using Guava types for fields but we don't use Guava
4647
disable("ImmutableMemberCollection")
4748

48-
// Fully qualified names may be necessary when deprecating a class to avoid
49-
// deprecation warning.
50-
disable("UnnecessarilyFullyQualified")
51-
5249
// TODO (trask) use animal sniffer
5350
disable("Java8ApiChecker")
5451

@@ -110,3 +107,14 @@ tasks {
110107
}
111108
}
112109
}
110+
111+
// Our conventions apply this project as a dependency in the errorprone configuration, which would cause
112+
// a circular dependency if trying to compile this project with that still there. So we filter this
113+
// project out.
114+
configurations {
115+
named("errorprone") {
116+
dependencies.removeIf {
117+
it is ProjectDependency && it.name == project.name
118+
}
119+
}
120+
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ tasks {
4848
"-Xlint:-processing",
4949
// We suppress the "options" warning because it prevents compilation on modern JDKs
5050
"-Xlint:-options",
51+
// We suppress the "deprecation" warning because --release 8 causes javac to warn on
52+
// importing deprecated classes (fixed in JDK 9+, see https://bugs.openjdk.org/browse/JDK-8032211).
53+
// We use a custom Error Prone check instead (OtelDeprecatedApiUsage).
54+
"-Xlint:-deprecation",
5155

5256
// Fail build on any warning
5357
"-Werror",

custom-checks/build.gradle.kts

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
plugins {
2+
id("otel.java-conventions")
3+
}
4+
5+
dependencies {
6+
compileOnly("com.google.errorprone:error_prone_core")
7+
8+
testImplementation("com.google.errorprone:error_prone_test_helpers")
9+
}
10+
11+
otelJava.moduleName.set("io.opentelemetry.contrib.customchecks")
12+
13+
// We cannot use "--release" javac option here because that will forbid exporting com.sun.tools package.
14+
// We also can't seem to use the toolchain without the "--release" option. So disable everything.
15+
16+
java {
17+
sourceCompatibility = JavaVersion.VERSION_21
18+
targetCompatibility = JavaVersion.VERSION_21
19+
toolchain {
20+
languageVersion.set(null as JavaLanguageVersion?)
21+
}
22+
}
23+
24+
tasks {
25+
withType<JavaCompile>().configureEach {
26+
with(options) {
27+
release.set(null as Int?)
28+
29+
compilerArgs.addAll(
30+
listOf(
31+
"--add-exports",
32+
"jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",
33+
"--add-exports",
34+
"jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
35+
"--add-exports",
36+
"jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED",
37+
"--add-exports",
38+
"jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
39+
"--add-exports",
40+
"jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
41+
),
42+
)
43+
}
44+
}
45+
46+
// only test on java 21+
47+
val testJavaVersion: String? by project
48+
if (testJavaVersion != null && Integer.valueOf(testJavaVersion) < 21) {
49+
test {
50+
enabled = false
51+
}
52+
}
53+
}
54+
55+
tasks.withType<Test>().configureEach {
56+
// required when accessing javac internals
57+
jvmArgs("--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED")
58+
jvmArgs("--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED")
59+
jvmArgs("--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED")
60+
jvmArgs("--add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED")
61+
jvmArgs("--add-opens=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED")
62+
jvmArgs("--add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED")
63+
jvmArgs("--add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED")
64+
jvmArgs("--add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED")
65+
jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
66+
}
67+
68+
tasks.withType<Javadoc>().configureEach {
69+
// using com.sun.tools.javac.api.JavacTrees breaks javadoc generation
70+
enabled = false
71+
}
72+
73+
// Our conventions apply this project as a dependency in the errorprone configuration, which would cause
74+
// a circular dependency if trying to compile this project with that still there. So we filter this
75+
// project out.
76+
configurations {
77+
named("errorprone") {
78+
dependencies.removeIf {
79+
it is ProjectDependency && it.name == project.name
80+
}
81+
}
82+
}
83+
84+
// Skip OWASP dependencyCheck task on test module
85+
dependencyCheck {
86+
skip = true
87+
}
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+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
io.opentelemetry.gradle.customchecks.OtelDeprecatedApiUsage

0 commit comments

Comments
 (0)