Compare commits

...

1 Commits

Author SHA1 Message Date
Stephan Schroevers
196edf9118 WIP: Introduce Checker Framework
To be determined: move to a separate profile?
2023-12-26 16:41:00 +01:00
23 changed files with 214 additions and 40 deletions

View File

@@ -2,6 +2,7 @@ package tech.picnic.errorprone.documentation;
import static com.google.errorprone.matchers.Matchers.instanceMethod;
import static com.google.errorprone.matchers.method.MethodMatchers.staticMethod;
import static java.util.Objects.requireNonNull;
import static java.util.function.Predicate.not;
import com.google.auto.service.AutoService;
@@ -17,7 +18,9 @@ import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.util.TreeScanner;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
import tech.picnic.errorprone.documentation.BugPatternTestExtractor.TestCases;
@@ -157,7 +160,7 @@ public final class BugPatternTestExtractor implements Extractor<TestCases> {
* is safe, because this code is guarded by an earlier call to `#getClassUnderTest(..)`,
* which ensures that `tree` is part of a longer method invocation chain.
*/
MethodInvocationTree inputTree = (MethodInvocationTree) ASTHelpers.getReceiver(tree);
MethodInvocationTree inputTree = (MethodInvocationTree) requireNonNull(ASTHelpers.getReceiver(tree));
String path = ASTHelpers.constValue(inputTree.getArguments().get(0), String.class);
Optional<String> inputCode = getSourceCode(inputTree);

View File

@@ -1,6 +1,7 @@
package tech.picnic.errorprone.documentation;
import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;
import com.google.auto.service.AutoService;
import com.google.common.annotations.VisibleForTesting;
@@ -46,7 +47,7 @@ public final class DocumentationGenerator implements Plugin {
checkArgument(
matcher.matches(), "'%s' must be of the form '%s=<value>'", pathArg, OUTPUT_DIRECTORY_FLAG);
String path = matcher.group(1);
String path = requireNonNull(matcher.group(1), "Path must be present");
try {
return Path.of(path);
} catch (InvalidPathException e) {

View File

@@ -1,6 +1,7 @@
package tech.picnic.errorprone.documentation;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.PropertyAccessor;
@@ -93,7 +94,7 @@ final class DocumentationGeneratorTaskListener implements TaskListener {
}
}
private <T> void writeToFile(String identifier, String className, T data) {
private <T extends Object> void writeToFile(String identifier, String className, T data) {
File file = docsPath.resolve(String.format("%s-%s.json", identifier, className)).toFile();
try (FileWriter fileWriter = new FileWriter(file, UTF_8)) {
@@ -104,6 +105,8 @@ final class DocumentationGeneratorTaskListener implements TaskListener {
}
private static String getSimpleClassName(URI path) {
return Paths.get(path).getFileName().toString().replace(".java", "");
return requireNonNull(Paths.get(path).getFileName(), "Path lacks filename")
.toString()
.replace(".java", "");
}
}

View File

@@ -8,6 +8,8 @@ import com.google.errorprone.FileObjects;
import com.sun.tools.javac.api.JavacTaskImpl;
import com.sun.tools.javac.api.JavacTool;
import com.sun.tools.javac.file.JavacFileManager;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
@@ -53,22 +55,25 @@ public final class Compilation {
}
private static void compile(ImmutableList<String> options, JavaFileObject javaFileObject) {
JavacFileManager javacFileManager = FileManagers.testFileManager();
JavaCompiler compiler = JavacTool.create();
try (JavacFileManager javacFileManager = FileManagers.testFileManager()) {
JavaCompiler compiler = JavacTool.create();
List<Diagnostic<?>> diagnostics = new ArrayList<>();
JavacTaskImpl task =
(JavacTaskImpl)
compiler.getTask(
null,
javacFileManager,
diagnostics::add,
options,
ImmutableList.of(),
ImmutableList.of(javaFileObject));
List<Diagnostic<?>> diagnostics = new ArrayList<>();
JavacTaskImpl task =
(JavacTaskImpl)
compiler.getTask(
null,
javacFileManager,
diagnostics::add,
options,
ImmutableList.of(),
ImmutableList.of(javaFileObject));
Boolean result = task.call();
assertThat(diagnostics).isEmpty();
assertThat(result).isTrue();
Boolean result = task.call();
assertThat(diagnostics).isEmpty();
assertThat(result).isTrue();
} catch (IOException e) {
throw new UncheckedIOException("Failed to close `JavaCompiler`", e);
}
}
}

View File

@@ -146,6 +146,10 @@
<artifactId>assertj-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>checker-util</artifactId>
</dependency>
<dependency>
<groupId>org.immutables</groupId>
<artifactId>value-annotations</artifactId>

View File

@@ -31,6 +31,7 @@ import com.sun.source.tree.ParenthesizedTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.Tree.Kind;
import com.sun.source.util.SimpleTreeVisitor;
import com.sun.tools.javac.code.Type;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.List;
@@ -208,7 +209,9 @@ public final class FormatStringConcatenation extends BugChecker
}
private static boolean isStringTyped(ExpressionTree tree, VisitorState state) {
return ASTHelpers.isSameType(ASTHelpers.getType(tree), state.getSymtab().stringType, state);
// XXX: Open Error Prone PR to improve the `@Nullable` annotations on `ASTHelpers`.
Type type = ASTHelpers.getType(tree);
return type != null && ASTHelpers.isSameType(type, state.getSymtab().stringType, state);
}
private static class ReplacementArgumentsConstructor

View File

@@ -171,6 +171,9 @@ public final class JUnitValueSource extends BugChecker implements MethodTreeMatc
return findMatchingSibling(tree, m -> m.getName().contentEquals(methodName), state);
}
// XXX: File ticket for the `@SuppressWarnings`: the checker incorrectly claims that the predicate
// only accepts `@InternedDistinct @SignatureBottom MethodTree` values (IIUC).
@SuppressWarnings({"interning:argument", "signature:argument"})
private static Optional<MethodTree> findMatchingSibling(
MethodTree tree, Predicate<? super MethodTree> predicate, VisitorState state) {
return state.findEnclosing(ClassTree.class).getMembers().stream()

View File

@@ -34,6 +34,7 @@ import com.sun.tools.javac.code.Type;
import java.util.List;
import java.util.Optional;
import javax.lang.model.element.Name;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
* A {@link BugChecker} that flags lambda expressions that can be replaced with method references.
@@ -189,7 +190,12 @@ public final class MethodReferenceUsage extends BugChecker implements LambdaExpr
}
}
return Optional.of(diff == 0 ? Optional.empty() : Optional.of(expectedArguments.get(0)));
// XXX: These type hints shouldn't be necessary; see
// https://github.com/typetools/checker-framework/issues/4007.
return Optional.of(
diff == 0
? Optional.<@NonNull Name>empty()
: Optional.<@NonNull Name>of(expectedArguments.get(0)));
}
private static ImmutableList<Name> getVariables(LambdaExpressionTree tree) {

View File

@@ -45,6 +45,9 @@ public final class MethodMatcherFactory {
// XXX: It seems parse errors are silently swallowed. Double-check; if true, file a ticket.
// XXX: This (probably) doesn't work for methods with array type arguments; if true, implement a
// fix.
// XXX: The `nullness` warning suppression shouldn't be necessary. See
// https://github.com/typetools/checker-framework/issues/4006.
@SuppressWarnings("nullness:argument")
private static Matcher<ExpressionTree> createMethodMatcher(CharSequence signature) {
java.util.regex.Matcher m = METHOD_SIGNATURE.matcher(signature);
checkArgument(m.matches(), "Not a valid method signature: %s", signature);

View File

@@ -13,6 +13,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import org.assertj.core.api.AbstractAssert;
import org.assertj.core.api.AbstractStringAssert;
import org.checkerframework.checker.regex.qual.Regex;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
@OnlineDocumentation
@@ -71,26 +72,26 @@ final class AssertJStringRules {
static final class AssertThatMatches {
@BeforeTemplate
AbstractAssert<?, ?> before(String string, String regex) {
AbstractAssert<?, ?> before(String string, @Regex String regex) {
return assertThat(string.matches(regex)).isTrue();
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
AbstractAssert<?, ?> after(String string, String regex) {
AbstractAssert<?, ?> after(String string, @Regex String regex) {
return assertThat(string).matches(regex);
}
}
static final class AssertThatDoesNotMatch {
@BeforeTemplate
AbstractAssert<?, ?> before(String string, String regex) {
AbstractAssert<?, ?> before(String string, @Regex String regex) {
return assertThat(string.matches(regex)).isFalse();
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
AbstractAssert<?, ?> after(String string, String regex) {
AbstractAssert<?, ?> after(String string, @Regex String regex) {
return assertThat(string).doesNotMatch(regex);
}
}

View File

@@ -16,6 +16,7 @@ import java.io.IOException;
import org.assertj.core.api.AbstractObjectAssert;
import org.assertj.core.api.AbstractThrowableAssert;
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
import org.checkerframework.checker.formatter.qual.FormatMethod;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
/**
@@ -591,6 +592,7 @@ final class AssertJThrowingCallableRules {
// arguments to a wide range of format methods.
static final class AbstractThrowableAssertHasMessage {
@BeforeTemplate
@FormatMethod
AbstractThrowableAssert<?, ? extends Throwable> before(
AbstractThrowableAssert<?, ? extends Throwable> abstractThrowableAssert,
String message,
@@ -599,6 +601,7 @@ final class AssertJThrowingCallableRules {
}
@AfterTemplate
@FormatMethod
AbstractThrowableAssert<?, ? extends Throwable> after(
AbstractThrowableAssert<?, ? extends Throwable> abstractThrowableAssert,
String message,
@@ -611,6 +614,7 @@ final class AssertJThrowingCallableRules {
// arguments to a wide range of format methods.
static final class AbstractThrowableAssertWithFailMessage {
@BeforeTemplate
@FormatMethod
AbstractThrowableAssert<?, ? extends Throwable> before(
AbstractThrowableAssert<?, ? extends Throwable> abstractThrowableAssert,
String message,
@@ -619,6 +623,7 @@ final class AssertJThrowingCallableRules {
}
@AfterTemplate
@FormatMethod
AbstractThrowableAssert<?, ? extends Throwable> after(
AbstractThrowableAssert<?, ? extends Throwable> abstractThrowableAssert,
String message,

View File

@@ -12,6 +12,7 @@ import com.google.errorprone.refaster.annotation.Placeholder;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Predicate;
import org.checkerframework.checker.interning.qual.Interned;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
/** Refaster rules related to expressions dealing with (in)equalities. */
@@ -21,7 +22,7 @@ final class EqualityRules {
/** Prefer reference-based quality for enums. */
// Primitive value comparisons are not listed, because Error Prone flags those out of the box.
static final class PrimitiveOrReferenceEquality<T extends Enum<T>> {
static final class PrimitiveOrReferenceEquality<T extends @Interned Enum<T>> {
/**
* Enums can be compared by reference. It is safe to do so even in the face of refactorings,
* because if the type is ever converted to a non-enum, then Error-Prone will complain about any
@@ -30,6 +31,7 @@ final class EqualityRules {
// XXX: This Refaster rule is the topic of https://github.com/google/error-prone/issues/559. We
// work around the issue by selecting the "largest replacements". See the `Refaster` check.
@BeforeTemplate
@SuppressWarnings("interning:unnecessary.equals" /* This violation will be rewritten. */)
boolean before(T a, T b) {
return Refaster.anyOf(a.equals(b), Objects.equals(a, b));
}
@@ -96,7 +98,7 @@ final class EqualityRules {
}
@BeforeTemplate
boolean before(Object a, Object b) {
boolean before(@Interned Object a, @Interned Object b) {
return !(a == b);
}
@@ -127,7 +129,7 @@ final class EqualityRules {
}
@BeforeTemplate
boolean before(Object a, Object b) {
boolean before(@Interned Object a, @Interned Object b) {
return !(a != b);
}

View File

@@ -7,6 +7,7 @@ import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import org.checkerframework.checker.regex.qual.Regex;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
/** Refaster rules related to code dealing with regular expressions. */
@@ -34,12 +35,12 @@ final class PatternRules {
/** Prefer {@link Pattern#asPredicate()} over non-JDK alternatives. */
static final class PatternCompileAsPredicate {
@BeforeTemplate
Predicate<CharSequence> before(String pattern) {
Predicate<CharSequence> before(@Regex String pattern) {
return containsPattern(pattern);
}
@AfterTemplate
Predicate<String> after(String pattern) {
Predicate<String> after(@Regex String pattern) {
return Pattern.compile(pattern).asPredicate();
}
}

View File

@@ -452,7 +452,11 @@ final class StreamRules {
static final class StreamMapToIntSum<T> {
@BeforeTemplate
@SuppressWarnings("java:S4266" /* This violation will be rewritten. */)
@SuppressWarnings({
"interning:return" /* Handle inference failure. */,
"java:S4266" /* This violation will be rewritten. */,
"key-to-resolve-AnnotationUseStyle-and-TrailingComment-check-conflict"
})
long before(Stream<T> stream, ToIntFunction<T> mapper) {
return stream.collect(summingInt(mapper));
}
@@ -472,7 +476,11 @@ final class StreamRules {
static final class StreamMapToDoubleSum<T> {
@BeforeTemplate
@SuppressWarnings("java:S4266" /* This violation will be rewritten. */)
@SuppressWarnings({
"interning:return" /* Handle inference failure. */,
"java:S4266" /* This violation will be rewritten. */,
"key-to-resolve-AnnotationUseStyle-and-TrailingComment-check-conflict"
})
double before(Stream<T> stream, ToDoubleFunction<T> mapper) {
return stream.collect(summingDouble(mapper));
}
@@ -492,7 +500,11 @@ final class StreamRules {
static final class StreamMapToLongSum<T> {
@BeforeTemplate
@SuppressWarnings("java:S4266" /* This violation will be rewritten. */)
@SuppressWarnings({
"interning:return" /* Handle inference failure. */,
"java:S4266" /* This violation will be rewritten. */,
"key-to-resolve-AnnotationUseStyle-and-TrailingComment-check-conflict"
})
long before(Stream<T> stream, ToLongFunction<T> mapper) {
return stream.collect(summingLong(mapper));
}
@@ -548,7 +560,11 @@ final class StreamRules {
static final class StreamCount<T> {
@BeforeTemplate
@SuppressWarnings("java:S4266" /* This violation will be rewritten. */)
@SuppressWarnings({
"interning:return" /* Handle inference failure. */,
"java:S4266" /* This violation will be rewritten. */,
"key-to-resolve-AnnotationUseStyle-and-TrailingComment-check-conflict"
})
long before(Stream<T> stream) {
return stream.collect(counting());
}

102
pom.xml
View File

@@ -203,6 +203,7 @@
that need to be referenced only once should *not* be listed here. -->
<version.auto-service>1.1.1</version.auto-service>
<version.auto-value>1.10.4</version.auto-value>
<version.checker-framework>3.42.0</version.checker-framework>
<version.error-prone>${version.error-prone-orig}</version.error-prone>
<version.error-prone-fork>v${version.error-prone-orig}-picnic-1</version.error-prone-fork>
<version.error-prone-orig>2.24.0</version.error-prone-orig>
@@ -411,7 +412,17 @@
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>checker-qual</artifactId>
<version>3.42.0</version>
<version>${version.checker-framework}</version>
</dependency>
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>checker-util</artifactId>
<version>${version.checker-framework}</version>
</dependency>
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>dataflow-shaded</artifactId>
<version>${version.checker-framework}</version>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
@@ -481,6 +492,22 @@
</dependency>
</dependencies>
</dependencyManagement>
<!-- These two dependencies are implicitly used by all submodules because
the Checker Framework enriches the compiled code with inferred annotations. -->
<!-- XXX: Move these if we decide to move the Checker Framework code to a
separate profile. -->
<dependencies>
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>checker-qual</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>dataflow-shaded</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
@@ -930,12 +957,54 @@
<artifactId>errorprone-slf4j</artifactId>
<version>${version.error-prone-slf4j}</version>
</path>
<path>
<groupId>org.checkerframework</groupId>
<artifactId>checker</artifactId>
<version>${version.checker-framework}</version>
</path>
<!-- This dependency is listed explicitly because
Auto Service depends on an older incompatible
version of this artifact. -->
<path>
<groupId>org.checkerframework</groupId>
<artifactId>checker-qual</artifactId>
<version>${version.checker-framework}</version>
</path>
<path>
<groupId>org.mockito</groupId>
<artifactId>mockito-errorprone</artifactId>
<version>${version.mockito}</version>
</path>
</annotationProcessorPaths>
<annotationProcessors>
<annotationProcessor>com.google.auto.service.processor.AutoServiceProcessor</annotationProcessor>
<annotationProcessor>com.google.auto.value.processor.AutoAnnotationProcessor</annotationProcessor>
<annotationProcessor>com.google.auto.value.processor.AutoValueProcessor</annotationProcessor>
<annotationProcessor>org.checkerframework.checker.calledmethods.CalledMethodsChecker</annotationProcessor>
<!-- XXX: This check seems to trigger some false
positives.
<annotationProcessor>org.checkerframework.checker.lock.LockChecker</annotationProcessor> -->
<annotationProcessor>org.checkerframework.checker.formatter.FormatterChecker</annotationProcessor>
<!-- XXX: This check triggers false positives, at
least in part because it doesn't respect invariants
guarded by `check{Argument,State}`.
<annotationProcessor>org.checkerframework.checker.index.IndexChecker</annotationProcessor> -->
<annotationProcessor>org.checkerframework.checker.interning.InterningChecker</annotationProcessor>
<!-- XXX: Flag some good stuff, but also a bunch of
false positives.
<annotationProcessor>org.checkerframework.checker.nullness.NullnessChecker</annotationProcessor> -->
<!-- XXX: This checker also flags `Optional` fields
and method method parameters. Consider introducing
a derivative checker which _does_ allow this.
<annotationProcessor>org.checkerframework.checker.optional.OptionalChecker</annotationProcessor> -->
<annotationProcessor>org.checkerframework.checker.regex.RegexChecker</annotationProcessor>
<!-- XXX: This check seems to trigger some false
positives, with suppressions being reported as
redundant.
<annotationProcessor>org.checkerframework.checker.resourceleak.ResourceLeakChecker</annotationProcessor> -->
<annotationProcessor>org.checkerframework.checker.signature.SignatureChecker</annotationProcessor>
<annotationProcessor>org.checkerframework.common.initializedfields.InitializedFieldsChecker</annotationProcessor>
</annotationProcessors>
<compilerArgs>
<arg>--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</arg>
<arg>--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
@@ -944,10 +1013,41 @@
<arg>--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED</arg>
<arg>--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</arg>
<arg>--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
<arg>-AskipDefs=\.Auto(Annotation|Value)_</arg>
<!-- XXX: This prevents the nullness checker from
flagging nullable arguments to `requireNonull`.
Review this approach. -->
<arg>-AskipUses=^java\.util\.Objects$</arg>
<!-- XXX: This flag triggers a crash.
<arg>-AcheckCastElementType</arg>-->
<arg>-AcheckPurityAnnotations</arg>
<arg>-AconcurrentSemantics</arg>
<!-- XXX: This flag triggers a crash.
<arg>-AconservativeUninferredTypeArguments</arg>-->
<arg>-AignoreRawTypeArguments=false</arg>
<!-- XXX: Odd impact on arrays; review.
<arg>-AinvariantArrays</arg>-->
<arg>-AresolveReflection</arg>
<arg>-AshowSuppressWarningsStrings</arg>
<arg>-AwarnRedundantAnnotations</arg>
<arg>-AwarnUnneededSuppressions</arg>
<arg>-ArequirePrefixInWarningSuppressions</arg>
<arg>-AshowPrefixInWarningMessages</arg>
<!-- XXX: Ponder.
<arg>-AsuggestPureMethods</arg> -->
<!-- XXX: Seemingly redundant null checks may be
necessary when interacting with an unannotated,
partially annotated or incorrectly annotated API.
So ideally we specify
`-Alint=all,-redundantNullComparison` or
`-Alint=all -ANullnessChecker_lint=-redundantNullComparison`,
but neither of these options has the desired
effect. -->
<arg>-Xmaxerrs</arg>
<arg>10000</arg>
<arg>-Xmaxwarns</arg>
<arg>10000</arg>
<!-- XXX: Move <arg>-Awarn</arg>-->
</compilerArgs>
<parameters>true</parameters>
<source>${version.jdk}</source>

View File

@@ -25,6 +25,7 @@ import com.sun.tools.javac.util.Name;
import java.io.IOException;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.lang.annotation.Annotation;
import java.util.Map;
@@ -145,7 +146,8 @@ final class RefasterRuleCompilerTaskListener implements TaskListener {
private static void outputCodeTransformer(CodeTransformer codeTransformer, FileObject target)
throws IOException {
try (ObjectOutput output = new ObjectOutputStream(target.openOutputStream())) {
try (OutputStream stream = target.openOutputStream();
ObjectOutput output = new ObjectOutputStream(stream)) {
output.writeObject(codeTransformer);
}
}

View File

@@ -67,6 +67,10 @@
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>checker-util</artifactId>
</dependency>
<dependency>
<groupId>org.jspecify</groupId>
<artifactId>jspecify</artifactId>

View File

@@ -42,6 +42,7 @@ import java.util.Optional;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import javax.inject.Inject;
import org.checkerframework.checker.regex.util.RegexUtil;
/**
* A {@link BugChecker} that flags code that can be simplified using Refaster rules located on the
@@ -210,7 +211,7 @@ public final class Refaster extends BugChecker implements CompilationUnitTreeMat
return CompositeCodeTransformer.compose(
flags
.get(INCLUDED_RULES_PATTERN_FLAG)
.map(Pattern::compile)
.map(pattern -> Pattern.compile(RegexUtil.asRegex(pattern)))
.<ImmutableCollection<CodeTransformer>>map(
nameFilter -> filterCodeTransformers(allTransformers, nameFilter))
.orElseGet(allTransformers::values));

View File

@@ -178,6 +178,7 @@ final class RefasterTest {
}
}
@SuppressWarnings({"regex:argument", "regex:group.count"})
private static ImmutableList<SeverityLevel> extractRefasterSeverities(
String fileName, String message) {
return Pattern.compile(

View File

@@ -56,6 +56,7 @@ import java.util.stream.Stream;
// XXX: Also recognize empty builders and `emptyBuilder.build()` invocations.
public final class IsEmpty implements Matcher<ExpressionTree> {
private static final long serialVersionUID = 1L;
private static final Integer ZERO = 0;
private static final Pattern EMPTY_INSTANCE_FACTORY_METHOD_PATTERN = Pattern.compile("empty.*");
private static final Matcher<Tree> EMPTY_COLLECTION_CONSTRUCTOR_ARGUMENT =
anyOf(isPrimitiveType(), isSubtypeOf(Comparator.class));
@@ -145,7 +146,7 @@ public final class IsEmpty implements Matcher<ExpressionTree> {
NewArrayTree newArray = (NewArrayTree) tree;
return (!newArray.getDimensions().isEmpty()
&& ASTHelpers.constValue(newArray.getDimensions().get(0), Integer.class) == 0)
&& ZERO.equals(ASTHelpers.constValue(newArray.getDimensions().get(0), Integer.class)))
|| (newArray.getInitializers() != null && newArray.getInitializers().isEmpty());
}
}

View File

@@ -30,6 +30,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
import org.checkerframework.checker.mustcall.qual.MustCall;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
@@ -149,7 +150,7 @@ final class AnnotatedCompositeCodeTransformerTest {
ImmutableSet<? extends Annotation> annotations,
Context expectedContext,
Description returnedDescription) {
CodeTransformer codeTransformer = mock();
@MustCall CodeTransformer codeTransformer = mock();
when(codeTransformer.annotations()).thenReturn(indexAnnotations(annotations));
doAnswer(
@@ -183,7 +184,7 @@ final class AnnotatedCompositeCodeTransformerTest {
private static Context context() {
// XXX: Use `ErrorProneOptions#processArgs` to test the
// `AnnotatedCompositeCodeTransformer#overrideSeverity` logic.
Context context = mock();
@MustCall Context context = mock();
when(context.get(ErrorProneOptions.class)).thenReturn(ErrorProneOptions.empty());
return context;
}

View File

@@ -35,7 +35,9 @@ abstract class AbstractMatcherTestChecker extends BugChecker implements Compilat
@Override
public Description matchCompilationUnit(CompilationUnitTree compilationUnit, VisitorState state) {
new TreePathScanner<@Nullable Void, @Nullable Void>() {
// XXX: Update `TreePathScanner#scan` stub to declare its first parameter `@Nullable`.
@Override
@SuppressWarnings("nullness:argument")
public @Nullable Void scan(@Nullable Tree tree, @Nullable Void unused) {
if (tree instanceof ExpressionTree) {
TreePath path = new TreePath(getCurrentPath(), tree);

View File

@@ -55,6 +55,12 @@ final class IsEmptyTest {
" return new int[][] {{0}};",
" }",
"",
" // XXX: Renumber the methods below.",
" int[] negative5X() {",
" int i = hashCode();",
" return new int[i];",
" }",
"",
" Random negative5() {",
" return new Random();",
" }",