diff --git a/baseline-error-prone/src/main/java/com/palantir/baseline/errorprone/ClassInitializationDeadlock.java b/baseline-error-prone/src/main/java/com/palantir/baseline/errorprone/ClassInitializationDeadlock.java index b473902de..2b6308315 100644 --- a/baseline-error-prone/src/main/java/com/palantir/baseline/errorprone/ClassInitializationDeadlock.java +++ b/baseline-error-prone/src/main/java/com/palantir/baseline/errorprone/ClassInitializationDeadlock.java @@ -190,7 +190,10 @@ public Void visitMemberSelect(MemberSelectTree node, VisitorState state) { if (symbol != null && symbol.isStatic() && symbol instanceof VarSymbol) { VarSymbol varSymbol = (VarSymbol) symbol; // Constant values may be accessed without forcing initialization - if (varSymbol.getConstValue() == null && isSubtype(varSymbol.owner.type, baseType, state)) { + if (varSymbol.getConstValue() == null + && isSubtype(varSymbol.owner.type, baseType, state) + // .class access does not force class initialization + && !varSymbol.name.contentEquals("class")) { state.reportMatch(describeMatch(node)); return null; } diff --git a/baseline-error-prone/src/test/java/com/palantir/baseline/errorprone/ClassInitializationDeadlockTest.java b/baseline-error-prone/src/test/java/com/palantir/baseline/errorprone/ClassInitializationDeadlockTest.java index dda66f9f2..d28649021 100644 --- a/baseline-error-prone/src/test/java/com/palantir/baseline/errorprone/ClassInitializationDeadlockTest.java +++ b/baseline-error-prone/src/test/java/com/palantir/baseline/errorprone/ClassInitializationDeadlockTest.java @@ -16,6 +16,8 @@ package com.palantir.baseline.errorprone; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.errorprone.CompilationTestHelper; import org.junit.jupiter.api.Test; @@ -280,6 +282,39 @@ void testStaticFieldAnonymousSubtypeAllowed() { .doTest(); } + @Test + @SuppressWarnings("ObjectToString") + void testClassAccessIsAllowed() { + assertThat(initializationTestClassInitialized) + .as("InitializationTestClass has already been initialized") + .isFalse(); + assertThat(InitializationTestClass.class).isNotNull(); + assertThat(initializationTestClassInitialized) + .as(".class access does not force initialization") + .isFalse(); + new InitializationTestClass(); + assertThat(initializationTestClassInitialized) + .as("Instantiation initializes classes") + .isTrue(); + + helper().addSourceLines( + "Base.java", + "import java.util.Optional;", + "class Base {", + " private static final Object VALUE = Sub.class;", + " static class Sub extends Base {}", + "}") + .doTest(); + } + + private static volatile boolean initializationTestClassInitialized; + + public static final class InitializationTestClass { + static { + initializationTestClassInitialized = true; + } + } + private CompilationTestHelper helper() { return CompilationTestHelper.newInstance(ClassInitializationDeadlock.class, getClass()); } diff --git a/changelog/@unreleased/pr-1654.v2.yml b/changelog/@unreleased/pr-1654.v2.yml new file mode 100644 index 000000000..c9dcbfe6e --- /dev/null +++ b/changelog/@unreleased/pr-1654.v2.yml @@ -0,0 +1,5 @@ +type: fix +fix: + description: Fix false positive ClassInitializationDeadlock on `.class` access + links: + - https://github.com/palantir/gradle-baseline/pull/1654