diff --git a/README.md b/README.md index 1a9c7c29d..423329318 100644 --- a/README.md +++ b/README.md @@ -186,6 +186,7 @@ Safe Logging can be found at [github.com/palantir/safe-logging](https://github.c - `VisibleForTestingPackagePrivate`: `@VisibleForTesting` members should be package-private. - `OptionalFlatMapOfNullable`: Optional.map functions may return null to safely produce an empty result. - `ExtendsErrorOrThrowable`: Avoid extending Error (or subclasses of it) or Throwable directly. +- `ImmutablesStyleCollision`: Prevent unintentionally voiding immutables Style meta-annotations through the introduction of inline style annotations. ### Programmatic Application diff --git a/baseline-error-prone/build.gradle b/baseline-error-prone/build.gradle index 75e8cb4fe..510823853 100644 --- a/baseline-error-prone/build.gradle +++ b/baseline-error-prone/build.gradle @@ -8,6 +8,7 @@ apply from: "${rootDir}/gradle/publish-jar.gradle" dependencies { compile 'com.google.errorprone:error_prone_core' compile 'org.mockito:mockito-errorprone' + compile 'org.immutables:value::annotations' testCompile gradleApi() testCompile 'com.palantir.tokens:auth-tokens' @@ -21,10 +22,12 @@ dependencies { testCompile 'org.assertj:assertj-core' testCompile 'org.jooq:jooq' testCompile 'com.palantir.tritium:tritium-registry' + testCompileOnly 'org.immutables:value::annotations' testImplementation 'org.junit.jupiter:junit-jupiter' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-migrationsupport' annotationProcessor 'com.google.auto.service:auto-service' + annotationProcessor 'org.immutables:value' compileOnly 'com.google.auto.service:auto-service' } diff --git a/baseline-error-prone/src/main/java/com/palantir/baseline/errorprone/ImmutablesStyleCollision.java b/baseline-error-prone/src/main/java/com/palantir/baseline/errorprone/ImmutablesStyleCollision.java new file mode 100644 index 000000000..6d3161704 --- /dev/null +++ b/baseline-error-prone/src/main/java/com/palantir/baseline/errorprone/ImmutablesStyleCollision.java @@ -0,0 +1,53 @@ +/* + * (c) Copyright 2020 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.palantir.baseline.errorprone; + +import com.google.auto.service.AutoService; +import com.google.errorprone.BugPattern; +import com.google.errorprone.BugPattern.LinkType; +import com.google.errorprone.VisitorState; +import com.google.errorprone.bugpatterns.BugChecker; +import com.google.errorprone.matchers.ChildMultiMatcher; +import com.google.errorprone.matchers.Description; +import com.google.errorprone.matchers.Matcher; +import com.google.errorprone.matchers.Matchers; +import com.sun.source.tree.ClassTree; +import org.immutables.value.Value; + +@AutoService(BugChecker.class) +@BugPattern( + name = "ImmutablesStyleCollision", + linkType = LinkType.CUSTOM, + link = "https://github.com/palantir/gradle-baseline#baseline-error-prone-checks", + severity = BugPattern.SeverityLevel.ERROR, + summary = "Immutables @Value.Style inline annotation should not be present alongside a Style " + + "meta-annotation, as there is no Style merging. You should either modify the " + + "meta-annotation, or add the meta-annotation's fields to your inline @Value.Style " + + "declaration.") +public final class ImmutablesStyleCollision extends BugChecker implements BugChecker.ClassTreeMatcher { + private static final Matcher INLINE_STYLE_ANNOTATION = Matchers.hasAnnotation(Value.Style.class); + private static final Matcher STYLE_META_ANNOTATION = + Matchers.annotations(ChildMultiMatcher.MatchType.AT_LEAST_ONE, Matchers.hasAnnotation(Value.Style.class)); + private static final Matcher MATCHER = Matchers.allOf(INLINE_STYLE_ANNOTATION, STYLE_META_ANNOTATION); + + @Override + public Description matchClass(ClassTree tree, VisitorState state) { + if (MATCHER.matches(tree, state)) { + return describeMatch(tree); + } + return Description.NO_MATCH; + } +} diff --git a/baseline-error-prone/src/test/java/com/palantir/baseline/errorprone/ImmutablesStyleCollisionTest.java b/baseline-error-prone/src/test/java/com/palantir/baseline/errorprone/ImmutablesStyleCollisionTest.java new file mode 100644 index 000000000..3550a4f6b --- /dev/null +++ b/baseline-error-prone/src/test/java/com/palantir/baseline/errorprone/ImmutablesStyleCollisionTest.java @@ -0,0 +1,63 @@ +/* + * (c) Copyright 2020 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.palantir.baseline.errorprone; + +import com.google.errorprone.CompilationTestHelper; +import org.junit.Test; + +public class ImmutablesStyleCollisionTest { + + @Test + public void testPass() { + helper().addSourceLines( + "Person.java", + "import org.immutables.value.Value;", + "public interface Person {", + " String name();", + "}") + .doTest(); + } + + @Test + public void testFail() { + helper().addSourceLines( + "MyMetaAnnotation.java", + "import org.immutables.value.Value;", + "import java.lang.annotation.ElementType;", + "import java.lang.annotation.Retention;", + "import java.lang.annotation.RetentionPolicy;", + "import java.lang.annotation.Target;", + "import org.immutables.value.Value;", + "@Target({ElementType.PACKAGE, ElementType.TYPE})\n", + "@Retention(RetentionPolicy.CLASS)\n", + "@Value.Style(visibility = Value.Style.ImplementationVisibility.PUBLIC)\n", + "public @interface MyMetaAnnotation {}") + .addSourceLines( + "Person.java", + "import org.immutables.value.Value;", + "@MyMetaAnnotation", + "@Value.Style(with = \"with\")", + "// BUG: Diagnostic contains: Immutables @Value.Style inline annotation should not be present", + "public interface Person {", + " String name();", + "}") + .doTest(); + } + + private CompilationTestHelper helper() { + return CompilationTestHelper.newInstance(ImmutablesStyleCollision.class, getClass()); + } +} diff --git a/changelog/@unreleased/pr-1396.v2.yml b/changelog/@unreleased/pr-1396.v2.yml new file mode 100644 index 000000000..80418e3b3 --- /dev/null +++ b/changelog/@unreleased/pr-1396.v2.yml @@ -0,0 +1,6 @@ +type: improvement +improvement: + description: Add errorprone check to avoid accidentally undoing immutables Style + meta-annotations. + links: + - https://github.com/palantir/gradle-baseline/pull/1396