Compare commits

...

27 Commits

Author SHA1 Message Date
SimonBaars
9d37b2777f PSM-1474 Introduce Duplicate Expect Next Test 2022-12-30 11:59:33 +01:00
Pieter Dirk Soels
601fcf2648 Update website styling and add Google site verification (#408) 2022-12-30 08:46:27 +01:00
Picnic-Bot
27c6c48e68 Upgrade Mockito 4.10.0 -> 4.11.0 (#427)
See:
- https://github.com/mockito/mockito/releases/tag/v4.11.0
- https://github.com/mockito/mockito/compare/v4.10.0...v4.11.0
2022-12-29 08:02:16 +01:00
Picnic-Bot
b22078657a Upgrade AspectJ 1.9.9.1 -> 1.9.19 (#422)
See:
- https://github.com/eclipse/org.aspectj/releases/tag/V1_9_19
- https://github.com/eclipse/org.aspectj/compare/V1_9_9_1...V1_9_19
2022-12-27 08:51:16 +01:00
Picnic-Bot
165a003f6a Upgrade Spring Boot 2.7.6 -> 2.7.7 (#423)
See:
- https://github.com/spring-projects/spring-boot/releases/tag/v2.7.7
- https://github.com/spring-projects/spring-boot/compare/v2.7.6...v2.7.7
2022-12-27 08:34:41 +01:00
Picnic-Bot
ecb8820d80 Upgrade versions-maven-plugin 2.14.1 -> 2.14.2 (#424)
See:
- https://github.com/mojohaus/versions/releases/tag/2.14.2
- https://github.com/mojohaus/versions-maven-plugin/compare/2.14.1...2.14.2
2022-12-27 06:38:04 +01:00
Stephan Schroevers
6313bd56d8 Improve JUnitMethodDeclaration check (#406)
Implemented changes:
- Ignore method overrides even if not annotated with `@Override`.
- Don't rename methods to `true`, `false` or `null`.
- Don't rename methods to a name declared in a super type. This
  prevents e.g. renaming `testToString` to `toString`.
2022-12-22 08:34:11 +01:00
Stephan Schroevers
5665470fe4 Improve IdentityConversion check (#407)
If the result of an explicit boxing operation is immediately
dereferenced, then the explicit conversion operation is not redundant.
2022-12-21 09:44:30 +01:00
EvgheniiShipilov
d0a89da24d Have IdentityConversion flag com.google.errorprone.matchers.Matchers#{allOf,anyOf} (#420)
While there, sort and rename some (test) code.

Fixes #340.
2022-12-20 11:14:46 +01:00
Picnic-Bot
7c40fdc033 Upgrade Arcmutate 1.0.1 -> 1.0.2 (#418) 2022-12-19 21:02:39 +01:00
Picnic-Bot
8724701baf Upgrade Immutables Annotations 2.9.2 -> 2.9.3 (#413)
See:
- https://github.com/immutables/immutables/releases/tag/2.9.3
- https://github.com/immutables/immutables/compare/2.9.2...2.9.3
2022-12-19 14:12:40 +01:00
Picnic-Bot
e9ae238c2b Upgrade Mockito 4.9.0 -> 4.10.0 (#416)
See:
- https://github.com/mockito/mockito/releases/tag/v4.10.0
- https://github.com/mockito/mockito/compare/v4.9.0...v4.10.0
2022-12-19 13:52:49 +01:00
Picnic-Bot
ff2ed6f82c Upgrade JSpecify 0.2.0 -> 0.3.0 (#415)
See:
- https://github.com/jspecify/jspecify/releases/tag/v0.3.0-alpha-1
- https://github.com/jspecify/jspecify/releases/tag/v0.3.0-alpha-2
- https://github.com/jspecify/jspecify/releases/tag/v0.3.0-alpha-3
- https://github.com/jspecify/jspecify/releases/tag/v0.3.0
- https://github.com/jspecify/jspecify/compare/v0.2.0...v0.3.0
2022-12-19 13:14:50 +01:00
Picnic-Bot
17aeeb9ea9 Upgrade versions-maven-plugin 2.13.0 -> 2.14.1 (#414)
See:
- https://github.com/mojohaus/versions/releases/tag/2.14.0
- https://github.com/mojohaus/versions/releases/tag/2.14.1
- https://github.com/mojohaus/versions-maven-plugin/compare/2.13.0...2.14.1
2022-12-19 12:56:03 +01:00
Stephan Schroevers
fd2946a9c8 Disable failing JDK 20-ea build for now (#419)
The build fails due to
openjdk/jdk20@2cb64a7557; the upcoming
Error Prone release includes a workaround for this:
google/error-prone@df033f03cb.
2022-12-19 09:37:03 +01:00
Guillaume Toison
870d16a0b6 Prevent NestedOptionals from throwing an NPE (#412)
Previously, a `NullPointerException` was thrown if during compilation the
`java.util.Optional` class was not loaded at all.
2022-12-16 09:40:05 +01:00
Picnic-Bot
96114235c5 Upgrade Project Reactor 2022.0.0 -> 2022.0.1 (#411)
See:
- https://github.com/reactor/reactor/releases/tag/2022.0.1
- https://github.com/reactor/reactor/compare/2022.0.0...2022.0.1
2022-12-14 07:18:40 +01:00
Picnic-Bot
bfbf748d47 Upgrade SLF4J API 2.0.5 -> 2.0.6 (#409)
See:
- https://www.slf4j.org/news.html
- https://github.com/qos-ch/slf4j/compare/v_2.0.5...v_2.0.6
2022-12-13 17:11:06 +01:00
Stephan Schroevers
8d0f1d78e6 Upgrade Error Prone fork v2.16-picnic-1 -> v2.16-picnic-2 (#410)
See:
- https://github.com/PicnicSupermarket/error-prone/releases/tag/v2.16-picnic-2
- https://github.com/PicnicSupermarket/error-prone/compare/v2.16-picnic-1...v2.16-picnic-2
2022-12-13 12:51:50 +01:00
Stephan Schroevers
ec00a5522f [maven-release-plugin] prepare for next development iteration 2022-12-12 09:57:05 +01:00
Stephan Schroevers
465b16c471 [maven-release-plugin] prepare release v0.6.0 2022-12-12 09:57:03 +01:00
Paco van Beckhoven
2cbd48ec47 Introduce MonoIdentity and MonoThen Refaster rules (#405)
The `MonoIdentity` rule is a generalization of the existing
`MonoSwitchIfEmptyOfEmptyPublisher` rule.
2022-12-12 08:52:56 +01:00
jarmilakaiser
0153c1495f Show Christmas Cody in README and on website home page (#404) 2022-12-09 16:27:51 +01:00
Rick Ossendrijver
81450285be Fix suggestions emitted by the StringCaseLocaleUsage check (#400)
The suggested `Locale` arguments are now always located in the correct place.
2022-12-09 14:35:35 +01:00
Bastien Diederichs
096acfb14f Improve IsInstanceLambdaUsage check (#401)
Fixes #399.
2022-12-09 13:27:46 +01:00
Shang Xiang
17bcdb6faa Introduce Flux and Stream Refaster rules to suggest filtering before sorting (#393)
Fixes #386.
2022-12-09 13:07:31 +01:00
Rick Ossendrijver
3ee527fda2 Drop indentation in feature request issue template (#403)
While there, add "Improve performance" as a rewrite reason.
2022-12-09 08:55:12 +01:00
72 changed files with 1311 additions and 177 deletions

View File

@@ -17,23 +17,24 @@ you'd like to be solved through Error Prone Support. -->
- [ ] Support a stylistic preference.
- [ ] Avoid a common gotcha, or potential problem.
- [ ] Improve performance.
<!--
Here, provide a clear and concise description of the desired change.
If possible, provide a simple and minimal example using the following format:
I would like to rewrite the following code:
```java
// XXX: Write the code to match here.
```
to:
```java
// XXX: Write the desired code here.
```
-->
I would like to rewrite the following code:
```java
// XXX: Write the code to match here.
```
to:
```java
// XXX: Write the desired code here.
```
### Considerations
<!--

View File

@@ -22,10 +22,11 @@ jobs:
jdk: 17.0.4
distribution: temurin
experimental: false
- os: ubuntu-22.04
jdk: 20-ea
distribution: zulu
experimental: true
# XXX: Re-enable this build after upgrading to Error Prone 2.17.
#- os: ubuntu-22.04
# jdk: 20-ea
# distribution: zulu
# experimental: true
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.experimental }}
steps:

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>error-prone-support</artifactId>
<version>0.5.1-SNAPSHOT</version>
<version>0.6.1-SNAPSHOT</version>
</parent>
<artifactId>error-prone-contrib</artifactId>

View File

@@ -32,7 +32,7 @@ import com.sun.source.util.SimpleTreeVisitor;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.jspecify.nullness.Nullable;
import org.jspecify.annotations.Nullable;
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
/**

View File

@@ -23,6 +23,7 @@ import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.util.ASTHelpers;
import com.google.errorprone.util.ASTHelpers.TargetType;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.Types;
@@ -32,7 +33,7 @@ import tech.picnic.errorprone.bugpatterns.util.SourceCode;
/** A {@link BugChecker} that flags redundant identity conversions. */
// XXX: Consider detecting cases where a flagged expression is passed to a method, and where removal
// of the identify conversion would cause a different method overload to be selected. Depending on
// of the identity conversion would cause a different method overload to be selected. Depending on
// the target method such a modification may change the code's semantics or performance.
@AutoService(BugChecker.class)
@BugPattern(
@@ -45,6 +46,13 @@ public final class IdentityConversion extends BugChecker implements MethodInvoca
private static final long serialVersionUID = 1L;
private static final Matcher<ExpressionTree> IS_CONVERSION_METHOD =
anyOf(
staticMethod()
.onClassAny(
Primitives.allWrapperTypes().stream()
.map(Class::getName)
.collect(toImmutableSet()))
.named("valueOf"),
staticMethod().onClass(String.class.getName()).named("valueOf"),
staticMethod()
.onClassAny(
"com.google.common.collect.ImmutableBiMap",
@@ -60,12 +68,8 @@ public final class IdentityConversion extends BugChecker implements MethodInvoca
"com.google.common.collect.ImmutableTable")
.named("copyOf"),
staticMethod()
.onClassAny(
Primitives.allWrapperTypes().stream()
.map(Class::getName)
.collect(toImmutableSet()))
.named("valueOf"),
staticMethod().onClass(String.class.getName()).named("valueOf"),
.onClass("com.google.errorprone.matchers.Matchers")
.namedAnyOf("allOf", "anyOf"),
staticMethod().onClass("reactor.adapter.rxjava.RxJava2Adapter"),
staticMethod()
.onClass("reactor.core.publisher.Flux")
@@ -95,6 +99,15 @@ public final class IdentityConversion extends BugChecker implements MethodInvoca
return Description.NO_MATCH;
}
if (sourceType.isPrimitive()
&& state.getPath().getParentPath().getLeaf() instanceof MemberSelectTree) {
/*
* The result of the conversion method is dereferenced, while the source type is a primitive:
* dropping the conversion would yield uncompilable code.
*/
return Description.NO_MATCH;
}
return buildDescription(tree)
.setMessage(
"This method invocation appears redundant; remove it or suppress this warning and "

View File

@@ -6,15 +6,18 @@ import static com.google.errorprone.BugPattern.StandardTags.SIMPLIFICATION;
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
import com.google.auto.service.AutoService;
import com.google.common.collect.Iterables;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.LambdaExpressionTreeMatcher;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.InstanceOfTree;
import com.sun.source.tree.LambdaExpressionTree;
import com.sun.source.tree.Tree.Kind;
import com.sun.source.tree.VariableTree;
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
/**
@@ -39,15 +42,19 @@ public final class IsInstanceLambdaUsage extends BugChecker implements LambdaExp
@Override
public Description matchLambdaExpression(LambdaExpressionTree tree, VisitorState state) {
if (tree.getKind() != Kind.LAMBDA_EXPRESSION || tree.getBody().getKind() != Kind.INSTANCE_OF) {
if (tree.getParameters().size() != 1 || tree.getBody().getKind() != Kind.INSTANCE_OF) {
return Description.NO_MATCH;
}
VariableTree param = Iterables.getOnlyElement(tree.getParameters());
InstanceOfTree instanceOf = (InstanceOfTree) tree.getBody();
if (!ASTHelpers.getSymbol(param).equals(ASTHelpers.getSymbol(instanceOf.getExpression()))) {
return Description.NO_MATCH;
}
return describeMatch(
tree,
SuggestedFix.replace(
tree,
SourceCode.treeToString(((InstanceOfTree) tree.getBody()).getType(), state)
+ ".class::isInstance"));
tree, SourceCode.treeToString(instanceOf.getType(), state) + ".class::isInstance"));
}
}

View File

@@ -3,21 +3,19 @@ package tech.picnic.errorprone.bugpatterns;
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION;
import static com.google.errorprone.BugPattern.StandardTags.SIMPLIFICATION;
import static com.google.errorprone.matchers.ChildMultiMatcher.MatchType.AT_LEAST_ONE;
import static com.google.errorprone.matchers.Matchers.allOf;
import static com.google.errorprone.matchers.Matchers.annotations;
import static com.google.errorprone.matchers.Matchers.anyOf;
import static com.google.errorprone.matchers.Matchers.enclosingClass;
import static com.google.errorprone.matchers.Matchers.hasModifier;
import static com.google.errorprone.matchers.Matchers.isType;
import static com.google.errorprone.matchers.Matchers.not;
import static java.util.function.Predicate.not;
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
import static tech.picnic.errorprone.bugpatterns.util.JavaKeywords.isReservedKeyword;
import static tech.picnic.errorprone.bugpatterns.util.JavaKeywords.isValidIdentifier;
import static tech.picnic.errorprone.bugpatterns.util.MoreJUnitMatchers.SETUP_OR_TEARDOWN_METHOD;
import static tech.picnic.errorprone.bugpatterns.util.MoreJUnitMatchers.TEST_METHOD;
import com.google.auto.service.AutoService;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
@@ -26,14 +24,15 @@ import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.fixes.SuggestedFixes;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.matchers.Matchers;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.ImportTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symbol.MethodSymbol;
import com.sun.tools.javac.code.Type;
import java.util.Optional;
import javax.lang.model.element.Modifier;
import tech.picnic.errorprone.bugpatterns.util.MoreASTHelpers;
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
/** A {@link BugChecker} that flags non-canonical JUnit method declarations. */
@@ -53,21 +52,19 @@ public final class JUnitMethodDeclaration extends BugChecker implements MethodTr
private static final long serialVersionUID = 1L;
private static final String TEST_PREFIX = "test";
private static final ImmutableSet<Modifier> ILLEGAL_MODIFIERS =
ImmutableSet.of(Modifier.PRIVATE, Modifier.PROTECTED, Modifier.PUBLIC);
private static final Matcher<MethodTree> HAS_UNMODIFIABLE_SIGNATURE =
anyOf(
annotations(AT_LEAST_ONE, isType("java.lang.Override")),
allOf(
Matchers.not(hasModifier(Modifier.FINAL)),
Matchers.not(hasModifier(Modifier.PRIVATE)),
enclosingClass(hasModifier(Modifier.ABSTRACT))));
Sets.immutableEnumSet(Modifier.PRIVATE, Modifier.PROTECTED, Modifier.PUBLIC);
private static final Matcher<MethodTree> IS_LIKELY_OVERRIDDEN =
allOf(
not(hasModifier(Modifier.FINAL)),
not(hasModifier(Modifier.PRIVATE)),
enclosingClass(hasModifier(Modifier.ABSTRACT)));
/** Instantiates a new {@link JUnitMethodDeclaration} instance. */
public JUnitMethodDeclaration() {}
@Override
public Description matchMethod(MethodTree tree, VisitorState state) {
if (HAS_UNMODIFIABLE_SIGNATURE.matches(tree, state)) {
if (IS_LIKELY_OVERRIDDEN.matches(tree, state) || isOverride(tree, state)) {
return Description.NO_MATCH;
}
@@ -89,10 +86,11 @@ public final class JUnitMethodDeclaration extends BugChecker implements MethodTr
private void suggestTestMethodRenameIfApplicable(
MethodTree tree, SuggestedFix.Builder fixBuilder, VisitorState state) {
tryCanonicalizeMethodName(tree)
MethodSymbol symbol = ASTHelpers.getSymbol(tree);
tryCanonicalizeMethodName(symbol)
.ifPresent(
newName ->
findMethodRenameBlocker(newName, state)
findMethodRenameBlocker(symbol, newName, state)
.ifPresentOrElse(
blocker -> reportMethodRenameBlocker(tree, blocker, state),
() -> fixBuilder.merge(SuggestedFixes.renameMethod(tree, newName, state))));
@@ -124,23 +122,31 @@ public final class JUnitMethodDeclaration extends BugChecker implements MethodTr
* consideration cannot be referenced directly.)
* </ul>
*/
private static Optional<String> findMethodRenameBlocker(String methodName, VisitorState state) {
if (MoreASTHelpers.methodExistsInEnclosingClass(methodName, state)) {
private static Optional<String> findMethodRenameBlocker(
MethodSymbol method, String newName, VisitorState state) {
if (isExistingMethodName(method.owner.type, newName, state)) {
return Optional.of(
String.format("a method named `%s` already exists in this class", methodName));
String.format(
"a method named `%s` is already defined in this class or a supertype", newName));
}
if (isSimpleNameStaticallyImported(methodName, state)) {
return Optional.of(String.format("`%s` is already statically imported", methodName));
if (isSimpleNameStaticallyImported(newName, state)) {
return Optional.of(String.format("`%s` is already statically imported", newName));
}
if (isReservedKeyword(methodName)) {
return Optional.of(String.format("`%s` is a reserved keyword", methodName));
if (!isValidIdentifier(newName)) {
return Optional.of(String.format("`%s` is not a valid identifier", newName));
}
return Optional.empty();
}
private static boolean isExistingMethodName(Type clazz, String name, VisitorState state) {
return ASTHelpers.matchingMethods(state.getName(name), method -> true, clazz, state.getTypes())
.findAny()
.isPresent();
}
private static boolean isSimpleNameStaticallyImported(String simpleName, VisitorState state) {
return state.getPath().getCompilationUnit().getImports().stream()
.filter(ImportTree::isStatic)
@@ -154,12 +160,18 @@ public final class JUnitMethodDeclaration extends BugChecker implements MethodTr
return source.subSequence(source.lastIndexOf('.') + 1, source.length());
}
private static Optional<String> tryCanonicalizeMethodName(MethodTree tree) {
return Optional.of(ASTHelpers.getSymbol(tree).getQualifiedName().toString())
private static Optional<String> tryCanonicalizeMethodName(Symbol symbol) {
return Optional.of(symbol.getQualifiedName().toString())
.filter(name -> name.startsWith(TEST_PREFIX))
.map(name -> name.substring(TEST_PREFIX.length()))
.filter(not(String::isEmpty))
.map(name -> Character.toLowerCase(name.charAt(0)) + name.substring(1))
.filter(name -> !Character.isDigit(name.charAt(0)));
}
private static boolean isOverride(MethodTree tree, VisitorState state) {
return ASTHelpers.streamSuperMethods(ASTHelpers.getSymbol(tree), state.getTypes())
.findAny()
.isPresent();
}
}

View File

@@ -40,7 +40,7 @@ import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;
import org.jspecify.nullness.Nullable;
import org.jspecify.annotations.Nullable;
import tech.picnic.errorprone.bugpatterns.util.AnnotationAttributeMatcher;
import tech.picnic.errorprone.bugpatterns.util.SourceCode;

View File

@@ -27,7 +27,7 @@ import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.TypeAnnotations.AnnotationType;
import java.util.Comparator;
import java.util.List;
import org.jspecify.nullness.Nullable;
import org.jspecify.annotations.Nullable;
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
/**

View File

@@ -41,8 +41,11 @@ public final class NestedOptionals extends BugChecker implements MethodInvocatio
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
return state.getTypes().isSubtype(ASTHelpers.getType(tree), OPTIONAL_OF_OPTIONAL.get(state))
? describeMatch(tree)
: Description.NO_MATCH;
Type type = OPTIONAL_OF_OPTIONAL.get(state);
if (type == null || !state.getTypes().isSubtype(ASTHelpers.getType(tree), type)) {
return Description.NO_MATCH;
}
return describeMatch(tree);
}
}

View File

@@ -0,0 +1,90 @@
package tech.picnic.errorprone.bugpatterns;
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION;
import static com.google.errorprone.BugPattern.StandardTags.SIMPLIFICATION;
import static com.google.errorprone.matchers.method.MethodMatchers.instanceMethod;
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
import com.google.auto.service.AutoService;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.suppliers.Supplier;
import com.google.errorprone.suppliers.Suppliers;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.tools.javac.code.Type;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;
/**
* A {@link BugChecker} that flags duplicated usages of {@link StepVerifier.Step#expectNext} in
* favor of the overloaded variant.
*
* <p>{@link Flux#flatMap(Function)} and {@link Flux#flatMapSequential(Function)} eagerly perform up
* to {@link reactor.util.concurrent.Queues#SMALL_BUFFER_SIZE} subscriptions. Additionally, the
* former interleaves values as they are emitted, yielding nondeterministic results. In most cases
* {@link Flux#concatMap(Function)} should be preferred, as it produces consistent results and
* avoids potentially saturating the thread pool on which subscription happens. If {@code
* concatMap}'s sequential-subscription semantics are undesirable one should invoke a {@code
* flatMap} or {@code flatMapSequential} overload with an explicit concurrency level.
*
* <p>NB: The rarely-used overload {@link Flux#flatMap(Function, Function,
* java.util.function.Supplier)} is not flagged by this check because there is no clear alternative
* to point to.
*/
@AutoService(BugChecker.class)
@BugPattern(
summary =
"When chaining multiple `StepVerifier.Step#expectNext` calls, please use the varargs overload instead",
link = BUG_PATTERNS_BASE_URL + "StepVerifierDuplicateExpectNext",
linkType = CUSTOM,
severity = SUGGESTION,
tags = SIMPLIFICATION)
public final class StepVerifierDuplicateExpectNext extends BugChecker
implements MethodInvocationTreeMatcher {
private static final long serialVersionUID = 1L;
private static final Supplier<Type> STEP =
Suppliers.typeFromString("reactor.test.StepVerifier.Step");
private static final Matcher<ExpressionTree> STEP_EXPECTNEXT =
instanceMethod().onDescendantOf(STEP).named("expectNext");
/** Instantiates a new {@link StepVerifierDuplicateExpectNext} instance. */
public StepVerifierDuplicateExpectNext() {}
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
if (!STEP_EXPECTNEXT.matches(tree, state)) {
return Description.NO_MATCH;
}
MemberSelectTree ms = (MemberSelectTree) tree.getMethodSelect();
MethodInvocationTree parent = (MethodInvocationTree) (ms).getExpression();
boolean matches = STEP_EXPECTNEXT.matches(parent, state);
if (!matches) {
return Description.NO_MATCH;
}
List<? extends ExpressionTree> args = parent.getArguments();
String newArgument =
tree.getArguments().stream().map(Object::toString).collect(Collectors.joining(", "));
SuggestedFix.Builder argumentsFix =
SuggestedFix.builder().postfixWith(args.get(args.size() - 1), ", " + newArgument);
int startPosition = state.getEndPosition(parent);
int endPosition = state.getEndPosition(tree);
SuggestedFix removeDuplicateCall = SuggestedFix.replace(startPosition, endPosition, "");
Description.Builder description = buildDescription(tree);
description.addFix(argumentsFix.merge(removeDuplicateCall).build());
return description.build();
}
}

View File

@@ -4,9 +4,11 @@ import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import static com.google.errorprone.BugPattern.StandardTags.FRAGILE_CODE;
import static com.google.errorprone.matchers.method.MethodMatchers.instanceMethod;
import static com.sun.tools.javac.parser.Tokens.TokenKind.RPAREN;
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
import com.google.auto.service.AutoService;
import com.google.common.collect.Streams;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
@@ -15,8 +17,11 @@ import com.google.errorprone.fixes.Fix;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.util.ASTHelpers;
import com.google.errorprone.util.ErrorProneTokens;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.tools.javac.util.Position;
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
/**
@@ -51,20 +56,34 @@ public final class StringCaseLocaleUsage extends BugChecker implements MethodInv
return Description.NO_MATCH;
}
int closingParenPosition = getClosingParenPosition(tree, state);
if (closingParenPosition == Position.NOPOS) {
return describeMatch(tree);
}
return buildDescription(tree)
.addFix(suggestLocale(tree, "Locale.ROOT", state))
.addFix(suggestLocale(tree, "Locale.getDefault()", state))
.addFix(suggestLocale(closingParenPosition, "Locale.ROOT"))
.addFix(suggestLocale(closingParenPosition, "Locale.getDefault()"))
.build();
}
private static Fix suggestLocale(MethodInvocationTree tree, String locale, VisitorState state) {
// XXX: The logic that replaces the first parenthesis assumes that `tree` does not have a source
// code representation such as `str.toLowerCase/* Some comment with parens (). */()`. In such a
// case the comment, rather than the method invocation arguments, will be modified. Implement a
// generic solution for this.
private static Fix suggestLocale(int insertPosition, String locale) {
return SuggestedFix.builder()
.addImport("java.util.Locale")
.replace(tree, SourceCode.treeToString(tree, state).replaceFirst("\\(", '(' + locale))
.replace(insertPosition, insertPosition, locale)
.build();
}
private static int getClosingParenPosition(MethodInvocationTree tree, VisitorState state) {
int startPosition = ASTHelpers.getStartPosition(tree);
if (startPosition == Position.NOPOS) {
return Position.NOPOS;
}
return Streams.findLast(
ErrorProneTokens.getTokens(SourceCode.treeToString(tree, state), state.context).stream()
.filter(t -> t.kind() == RPAREN))
.map(token -> startPosition + token.pos())
.orElse(Position.NOPOS);
}
}

View File

@@ -27,7 +27,7 @@ import com.sun.tools.javac.util.Convert;
import java.util.Formattable;
import java.util.Iterator;
import java.util.List;
import org.jspecify.nullness.Nullable;
import org.jspecify.annotations.Nullable;
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
/**

View File

@@ -1,4 +1,4 @@
/** Picnic Error Prone Contrib checks. */
@com.google.errorprone.annotations.CheckReturnValue
@org.jspecify.nullness.NullMarked
@org.jspecify.annotations.NullMarked
package tech.picnic.errorprone.bugpatterns;

View File

@@ -4,7 +4,19 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
/** Utility class that can be used to identify reserved keywords of the Java language. */
// XXX: This class is no longer only about keywords. Consider changing its name and class-level
// documentation.
public final class JavaKeywords {
/**
* Enumeration of boolean and null literals.
*
* @see <a href="https://docs.oracle.com/javase/specs/jls/se17/html/jls-3.html#jls-3.10.3">JDK 17
* JLS section 3.10.3: Boolean Literals</a>
* @see <a href="https://docs.oracle.com/javase/specs/jls/se17/html/jls-3.html#jls-3.10.8">JDK 17
* JLS section 3.10.8: The Null Literal</a>
*/
private static final ImmutableSet<String> BOOLEAN_AND_NULL_LITERALS =
ImmutableSet.of("true", "false", "null");
/**
* List of all reserved keywords in the Java language.
*
@@ -64,7 +76,6 @@ public final class JavaKeywords {
"void",
"volatile",
"while");
/**
* List of all contextual keywords in the Java language.
*
@@ -89,13 +100,28 @@ public final class JavaKeywords {
"var",
"with",
"yield");
/** List of all keywords in the Java language. */
private static final ImmutableSet<String> ALL_KEYWORDS =
Sets.union(RESERVED_KEYWORDS, CONTEXTUAL_KEYWORDS).immutableCopy();
private JavaKeywords() {}
/**
* Tells whether the given string is a valid identifier in the Java language.
*
* @param str The string of interest.
* @return {@code true} if the given string is a valid identifier in the Java language.
* @see <a href="https://docs.oracle.com/javase/specs/jls/se17/html/jls-3.html#jls-3.8">JDK 17 JLS
* section 3.8: Identifiers</a>
*/
public static boolean isValidIdentifier(String str) {
return !str.isEmpty()
&& !isReservedKeyword(str)
&& !BOOLEAN_AND_NULL_LITERALS.contains(str)
&& Character.isJavaIdentifierStart(str.codePointAt(0))
&& str.codePoints().skip(1).allMatch(Character::isUnicodeIdentifierPart);
}
/**
* Tells whether the given string is a reserved keyword in the Java language.
*

View File

@@ -18,7 +18,7 @@ import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.NewArrayTree;
import org.jspecify.nullness.Nullable;
import org.jspecify.annotations.Nullable;
/**
* A collection of JUnit-specific helper methods and {@link Matcher}s.

View File

@@ -1,4 +1,4 @@
/** Auxiliary utilities for use by Error Prone checks. */
@com.google.errorprone.annotations.CheckReturnValue
@org.jspecify.nullness.NullMarked
@org.jspecify.annotations.NullMarked
package tech.picnic.errorprone.bugpatterns.util;

View File

@@ -24,7 +24,7 @@ import java.util.Iterator;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Stream;
import org.jspecify.nullness.Nullable;
import org.jspecify.annotations.Nullable;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
/**
@@ -103,7 +103,8 @@ final class AssortedRules {
}
@AfterTemplate
@Nullable T after(Iterator<T> iterator, T defaultValue) {
@Nullable
T after(Iterator<T> iterator, T defaultValue) {
return Iterators.getNext(iterator, defaultValue);
}
}

View File

@@ -141,6 +141,22 @@ final class DoubleStreamRules {
}
}
/**
* Apply {@link DoubleStream#filter(DoublePredicate)} before {@link DoubleStream#sorted()} to
* reduce the number of elements to sort.
*/
static final class DoubleStreamFilterSorted {
@BeforeTemplate
DoubleStream before(DoubleStream stream, DoublePredicate predicate) {
return stream.sorted().filter(predicate);
}
@AfterTemplate
DoubleStream after(DoubleStream stream, DoublePredicate predicate) {
return stream.filter(predicate).sorted();
}
}
/** In order to test whether a stream has any element, simply try to find one. */
static final class DoubleStreamIsEmpty {
@BeforeTemplate

View File

@@ -154,6 +154,22 @@ final class IntStreamRules {
}
}
/**
* Apply {@link IntStream#filter(IntPredicate)} before {@link IntStream#sorted()} to reduce the
* number of elements to sort.
*/
static final class IntStreamFilterSorted {
@BeforeTemplate
IntStream before(IntStream stream, IntPredicate predicate) {
return stream.sorted().filter(predicate);
}
@AfterTemplate
IntStream after(IntStream stream, IntPredicate predicate) {
return stream.filter(predicate).sorted();
}
}
/** In order to test whether a stream has any element, simply try to find one. */
static final class IntStreamIsEmpty {
@BeforeTemplate

View File

@@ -154,6 +154,22 @@ final class LongStreamRules {
}
}
/**
* Apply {@link LongStream#filter(LongPredicate)} before {@link LongStream#sorted()} to reduce the
* number of elements to sort.
*/
static final class LongStreamFilterSorted {
@BeforeTemplate
LongStream before(LongStream stream, LongPredicate predicate) {
return stream.sorted().filter(predicate);
}
@AfterTemplate
LongStream after(LongStream stream, LongPredicate predicate) {
return stream.filter(predicate).sorted();
}
}
/** In order to test whether a stream has any element, simply try to find one. */
static final class LongStreamIsEmpty {
@BeforeTemplate

View File

@@ -7,7 +7,7 @@ import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
import org.jspecify.nullness.Nullable;
import org.jspecify.annotations.Nullable;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
/** Refaster rules related to expressions dealing with {@link Map} instances. */
@@ -31,12 +31,14 @@ final class MapRules {
static final class MapGetOrNull<K, V, T> {
@BeforeTemplate
@Nullable V before(Map<K, V> map, T key) {
@Nullable
V before(Map<K, V> map, T key) {
return map.getOrDefault(key, null);
}
@AfterTemplate
@Nullable V after(Map<K, V> map, T key) {
@Nullable
V after(Map<K, V> map, T key) {
return map.get(key);
}
}

View File

@@ -7,7 +7,7 @@ import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import java.util.Collection;
import java.util.Set;
import org.jspecify.nullness.Nullable;
import org.jspecify.annotations.Nullable;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
/** Refaster rules related to expressions dealing with {@link Multimap}s. */
@@ -50,7 +50,8 @@ final class MultimapRules {
*/
static final class MultimapGet<K, V> {
@BeforeTemplate
@Nullable Collection<V> before(Multimap<K, V> multimap, K key) {
@Nullable
Collection<V> before(Multimap<K, V> multimap, K key) {
return Refaster.anyOf(multimap.asMap(), Multimaps.asMap(multimap)).get(key);
}

View File

@@ -9,7 +9,7 @@ import com.google.errorprone.refaster.annotation.BeforeTemplate;
import com.google.errorprone.refaster.annotation.UseImportPolicy;
import java.util.Objects;
import java.util.function.Predicate;
import org.jspecify.nullness.Nullable;
import org.jspecify.annotations.Nullable;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
/** Refaster rules related to expressions dealing with (possibly) null values. */

View File

@@ -16,7 +16,7 @@ import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.jspecify.nullness.Nullable;
import org.jspecify.annotations.Nullable;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
/** Refaster rules related to expressions dealing with {@link Optional}s. */

View File

@@ -17,6 +17,7 @@ import com.google.errorprone.refaster.annotation.NotMatches;
import com.google.errorprone.refaster.annotation.Placeholder;
import com.google.errorprone.refaster.annotation.UseImportPolicy;
import java.time.Duration;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Optional;
import java.util.concurrent.Callable;
@@ -26,7 +27,7 @@ import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import org.jspecify.nullness.Nullable;
import org.jspecify.annotations.Nullable;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -364,13 +365,21 @@ final class ReactorRules {
}
}
/** Don't unnecessarily pass an empty publisher to {@link Mono#switchIfEmpty(Mono)}. */
static final class MonoSwitchIfEmptyOfEmptyPublisher<T> {
/** Don't unnecessarily transform a {@link Mono} to an equivalent instance. */
static final class MonoIdentity<T> {
@BeforeTemplate
Mono<T> before(Mono<T> mono) {
return mono.switchIfEmpty(Mono.empty());
}
// XXX: Review the suppression once NullAway has better support for generics. Keep an eye on
// https://github.com/uber/NullAway/issues?q=is%3Aopen+generics.
@BeforeTemplate
@SuppressWarnings("NullAway" /* False positive. */)
Mono<@Nullable Void> before2(Mono<@Nullable Void> mono) {
return mono.then();
}
@AfterTemplate
Mono<T> after(Mono<T> mono) {
return mono;
@@ -674,6 +683,19 @@ final class ReactorRules {
}
}
/** Prefer direct invocation of {@link Mono#then()}} over more contrived alternatives. */
static final class MonoThen<T> {
@BeforeTemplate
Mono<@Nullable Void> before(Mono<T> mono) {
return mono.flux().then();
}
@AfterTemplate
Mono<@Nullable Void> after(Mono<T> mono) {
return mono.then();
}
}
/**
* Prefer a collection using {@link MoreCollectors#toOptional()} over more contrived alternatives.
*/
@@ -1062,6 +1084,38 @@ final class ReactorRules {
}
}
/**
* Apply {@link Flux#filter(Predicate)} before {@link Flux#sort()} to reduce the number of
* elements to sort.
*/
static final class FluxFilterSort<T> {
@BeforeTemplate
Flux<T> before(Flux<T> flux, Predicate<? super T> predicate) {
return flux.sort().filter(predicate);
}
@AfterTemplate
Flux<T> after(Flux<T> flux, Predicate<? super T> predicate) {
return flux.filter(predicate).sort();
}
}
/**
* Apply {@link Flux#filter(Predicate)} before {@link Flux#sort(Comparator)} to reduce the number
* of elements to sort.
*/
static final class FluxFilterSortWithComparator<T> {
@BeforeTemplate
Flux<T> before(Flux<T> flux, Predicate<? super T> predicate, Comparator<? super T> comparator) {
return flux.sort(comparator).filter(predicate);
}
@AfterTemplate
Flux<T> after(Flux<T> flux, Predicate<? super T> predicate, Comparator<? super T> comparator) {
return flux.filter(predicate).sort(comparator);
}
}
/** Prefer {@link reactor.util.context.Context#empty()}} over more verbose alternatives. */
// XXX: Consider introducing an `IsEmpty` matcher that identifies a wide range of guaranteed-empty
// `Collection` and `Map` expressions.

View File

@@ -9,7 +9,7 @@ import io.reactivex.Flowable;
import io.reactivex.Maybe;
import io.reactivex.Observable;
import io.reactivex.Single;
import org.jspecify.nullness.Nullable;
import org.jspecify.annotations.Nullable;
import reactor.adapter.rxjava.RxJava2Adapter;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

View File

@@ -167,6 +167,40 @@ final class StreamRules {
}
}
/**
* Apply {@link Stream#filter(Predicate)} before {@link Stream#sorted()} to reduce the number of
* elements to sort.
*/
static final class StreamFilterSorted<T> {
@BeforeTemplate
Stream<T> before(Stream<T> stream, Predicate<? super T> predicate) {
return stream.sorted().filter(predicate);
}
@AfterTemplate
Stream<T> after(Stream<T> stream, Predicate<? super T> predicate) {
return stream.filter(predicate).sorted();
}
}
/**
* Apply {@link Stream#filter(Predicate)} before {@link Stream#sorted(Comparator)} to reduce the
* number of elements to sort.
*/
static final class StreamFilterSortedWithComparator<T> {
@BeforeTemplate
Stream<T> before(
Stream<T> stream, Predicate<? super T> predicate, Comparator<? super T> comparator) {
return stream.sorted(comparator).filter(predicate);
}
@AfterTemplate
Stream<T> after(
Stream<T> stream, Predicate<? super T> predicate, Comparator<? super T> comparator) {
return stream.filter(predicate).sorted(comparator);
}
}
/**
* Where possible, clarify that a mapping operation will be applied only to a single stream
* element.

View File

@@ -16,7 +16,7 @@ import java.util.Collection;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import org.jspecify.nullness.Nullable;
import org.jspecify.annotations.Nullable;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
/** Refaster rules related to expressions dealing with {@link String}s. */

View File

@@ -1,4 +1,4 @@
/** Picnic Refaster rules. */
@com.google.errorprone.annotations.CheckReturnValue
@org.jspecify.nullness.NullMarked
@org.jspecify.annotations.NullMarked
package tech.picnic.errorprone.refasterrules;

View File

@@ -16,7 +16,10 @@ final class IdentityConversionTest {
void identification() {
compilationTestHelper
.addSourceLines(
"Foo.java",
"A.java",
"import static com.google.errorprone.matchers.Matchers.instanceMethod;",
"import static com.google.errorprone.matchers.Matchers.staticMethod;",
"",
"import com.google.common.collect.ImmutableBiMap;",
"import com.google.common.collect.ImmutableList;",
"import com.google.common.collect.ImmutableListMultimap;",
@@ -28,12 +31,14 @@ final class IdentityConversionTest {
"import com.google.common.collect.ImmutableSet;",
"import com.google.common.collect.ImmutableSetMultimap;",
"import com.google.common.collect.ImmutableTable;",
"import com.google.errorprone.matchers.Matcher;",
"import com.google.errorprone.matchers.Matchers;",
"import reactor.adapter.rxjava.RxJava2Adapter;",
"import reactor.core.publisher.Flux;",
"import reactor.core.publisher.Mono;",
"",
"public final class Foo {",
" public void foo() {",
"public final class A {",
" public void m() {",
" // BUG: Diagnostic contains:",
" Boolean b1 = Boolean.valueOf(Boolean.FALSE);",
" // BUG: Diagnostic contains:",
@@ -113,6 +118,13 @@ final class IdentityConversionTest {
" // BUG: Diagnostic contains:",
" short s4 = Short.valueOf(Short.MIN_VALUE);",
"",
" // BUG: Diagnostic contains:",
" String boolStr = Boolean.valueOf(Boolean.FALSE).toString();",
" int boolHash = Boolean.valueOf(false).hashCode();",
" // BUG: Diagnostic contains:",
" int byteHash = Byte.valueOf((Byte) Byte.MIN_VALUE).hashCode();",
" String byteStr = Byte.valueOf(Byte.MIN_VALUE).toString();",
"",
" String str1 = String.valueOf(0);",
" // BUG: Diagnostic contains:",
" String str2 = String.valueOf(\"1\");",
@@ -143,7 +155,15 @@ final class IdentityConversionTest {
" ImmutableTable<Object, Object, Object> o11 = ImmutableTable.copyOf(ImmutableTable.of());",
"",
" // BUG: Diagnostic contains:",
" Matcher allOf1 = Matchers.allOf(instanceMethod());",
" Matcher allOf2 = Matchers.allOf(instanceMethod(), staticMethod());",
" // BUG: Diagnostic contains:",
" Matcher anyOf1 = Matchers.anyOf(staticMethod());",
" Matcher anyOf2 = Matchers.anyOf(instanceMethod(), staticMethod());",
"",
" // BUG: Diagnostic contains:",
" Flux<Integer> flux1 = Flux.just(1).flatMap(e -> RxJava2Adapter.fluxToFlowable(Flux.just(2)));",
"",
" // BUG: Diagnostic contains:",
" Flux<Integer> flux2 = Flux.concat(Flux.just(1));",
" // BUG: Diagnostic contains:",
@@ -154,9 +174,9 @@ final class IdentityConversionTest {
" Flux<Integer> flux5 = Flux.merge(Flux.just(1));",
"",
" // BUG: Diagnostic contains:",
" Mono<Integer> m1 = Mono.from(Mono.just(1));",
" Mono<Integer> mono1 = Mono.from(Mono.just(1));",
" // BUG: Diagnostic contains:",
" Mono<Integer> m2 = Mono.fromDirect(Mono.just(1));",
" Mono<Integer> mono2 = Mono.fromDirect(Mono.just(1));",
" }",
"}")
.doTest();
@@ -167,12 +187,15 @@ final class IdentityConversionTest {
refactoringTestHelper
.setFixChooser(FixChoosers.FIRST)
.addInputLines(
"Foo.java",
"A.java",
"import static com.google.errorprone.matchers.Matchers.staticMethod;",
"import static org.mockito.Mockito.when;",
"",
"import com.google.common.collect.ImmutableCollection;",
"import com.google.common.collect.ImmutableList;",
"import com.google.common.collect.ImmutableSet;",
"import com.google.errorprone.matchers.Matcher;",
"import com.google.errorprone.matchers.Matchers;",
"import java.util.ArrayList;",
"import java.util.Collection;",
"import org.reactivestreams.Publisher;",
@@ -180,8 +203,8 @@ final class IdentityConversionTest {
"import reactor.core.publisher.Flux;",
"import reactor.core.publisher.Mono;",
"",
"public final class Foo {",
" public void foo() {",
"public final class A {",
" public void m() {",
" ImmutableSet<Object> set1 = ImmutableSet.copyOf(ImmutableSet.of());",
" ImmutableSet<Object> set2 = ImmutableSet.copyOf(ImmutableList.of());",
"",
@@ -206,18 +229,23 @@ final class IdentityConversionTest {
" Object o1 = ImmutableSet.copyOf(ImmutableList.of());",
" Object o2 = ImmutableSet.copyOf(ImmutableSet.of());",
"",
" Matcher matcher = Matchers.allOf(staticMethod());",
"",
" when(\"foo\".contains(\"f\")).thenAnswer(inv -> ImmutableSet.copyOf(ImmutableList.of(1)));",
" }",
"",
" void bar(Publisher<Integer> publisher) {}",
"}")
.addOutputLines(
"Foo.java",
"A.java",
"import static com.google.errorprone.matchers.Matchers.staticMethod;",
"import static org.mockito.Mockito.when;",
"",
"import com.google.common.collect.ImmutableCollection;",
"import com.google.common.collect.ImmutableList;",
"import com.google.common.collect.ImmutableSet;",
"import com.google.errorprone.matchers.Matcher;",
"import com.google.errorprone.matchers.Matchers;",
"import java.util.ArrayList;",
"import java.util.Collection;",
"import org.reactivestreams.Publisher;",
@@ -225,8 +253,8 @@ final class IdentityConversionTest {
"import reactor.core.publisher.Flux;",
"import reactor.core.publisher.Mono;",
"",
"public final class Foo {",
" public void foo() {",
"public final class A {",
" public void m() {",
" ImmutableSet<Object> set1 = ImmutableSet.of();",
" ImmutableSet<Object> set2 = ImmutableSet.copyOf(ImmutableList.of());",
"",
@@ -251,6 +279,8 @@ final class IdentityConversionTest {
" Object o1 = ImmutableSet.copyOf(ImmutableList.of());",
" Object o2 = ImmutableSet.of();",
"",
" Matcher matcher = staticMethod();",
"",
" when(\"foo\".contains(\"f\")).thenAnswer(inv -> ImmutableSet.copyOf(ImmutableList.of(1)));",
" }",
"",
@@ -264,14 +294,14 @@ final class IdentityConversionTest {
refactoringTestHelper
.setFixChooser(FixChoosers.SECOND)
.addInputLines(
"Foo.java",
"A.java",
"import com.google.common.collect.ImmutableCollection;",
"import com.google.common.collect.ImmutableList;",
"import com.google.common.collect.ImmutableSet;",
"import java.util.ArrayList;",
"",
"public final class Foo {",
" public void foo() {",
"public final class A {",
" public void m() {",
" ImmutableSet<Object> set1 = ImmutableSet.copyOf(ImmutableSet.of());",
" ImmutableSet<Object> set2 = ImmutableSet.copyOf(ImmutableList.of());",
"",
@@ -280,14 +310,14 @@ final class IdentityConversionTest {
" }",
"}")
.addOutputLines(
"Foo.java",
"A.java",
"import com.google.common.collect.ImmutableCollection;",
"import com.google.common.collect.ImmutableList;",
"import com.google.common.collect.ImmutableSet;",
"import java.util.ArrayList;",
"",
"public final class Foo {",
" public void foo() {",
"public final class A {",
" public void m() {",
" @SuppressWarnings(\"IdentityConversion\")",
" ImmutableSet<Object> set1 = ImmutableSet.copyOf(ImmutableSet.of());",
" ImmutableSet<Object> set2 = ImmutableSet.copyOf(ImmutableList.of());",

View File

@@ -17,12 +17,28 @@ final class IsInstanceLambdaUsageTest {
.addSourceLines(
"A.java",
"import java.util.stream.Stream;",
"import reactor.core.publisher.Flux;",
"",
"class A {",
" void m() {",
" Integer localVariable = 0;",
"",
" Stream.of(0).map(i -> i + 1);",
" Stream.of(1).filter(Integer.class::isInstance);",
" Stream.of(2).filter(i -> i.getClass() instanceof Class);",
" Stream.of(3).filter(i -> localVariable instanceof Integer);",
" // XXX: Ideally this case is also flagged. Pick this up in the context of merging the",
" // `IsInstanceLambdaUsage` and `MethodReferenceUsage` checks, or introduce a separate check that",
" // simplifies unnecessary block lambda expressions.",
" Stream.of(4)",
" .filter(",
" i -> {",
" return localVariable instanceof Integer;",
" });",
" Flux.just(5, \"foo\").distinctUntilChanged(v -> v, (a, b) -> a instanceof Integer);",
"",
" // BUG: Diagnostic contains:",
" Stream.of(1).filter(i -> i instanceof Integer);",
" Stream.of(2).filter(Integer.class::isInstance);",
" Stream.of(6).filter(i -> i instanceof Integer);",
" }",
"}")
.doTest();

View File

@@ -91,6 +91,9 @@ final class JUnitMethodDeclarationTest {
" private void tearDown8() {}",
"",
" @Test",
" void test() {}",
"",
" @Test",
" void method1() {}",
"",
" @Test",
@@ -144,8 +147,13 @@ final class JUnitMethodDeclarationTest {
" void test5() {}",
"",
" @Test",
" // BUG: Diagnostic contains: (but note that a method named `overload` already exists in this",
" // class)",
" // BUG: Diagnostic contains: (but note that a method named `toString` is already defined in this",
" // class or a supertype)",
" void testToString() {}",
"",
" @Test",
" // BUG: Diagnostic contains: (but note that a method named `overload` is already defined in this",
" // class or a supertype)",
" void testOverload() {}",
"",
" void overload() {}",
@@ -155,8 +163,20 @@ final class JUnitMethodDeclarationTest {
" void testArguments() {}",
"",
" @Test",
" // BUG: Diagnostic contains: (but note that `public` is a reserved keyword)",
" // BUG: Diagnostic contains: (but note that `public` is not a valid identifier)",
" void testPublic() {}",
"",
" @Test",
" // BUG: Diagnostic contains: (but note that `null` is not a valid identifier)",
" void testNull() {}",
"",
" @Test",
" // BUG: Diagnostic contains:",
" void testRecord() {}",
"",
" @Test",
" // BUG: Diagnostic contains:",
" void testMethodThatIsOverriddenWithoutOverrideAnnotation() {}",
"}")
.addSourceLines(
"B.java",
@@ -218,6 +238,10 @@ final class JUnitMethodDeclarationTest {
"",
" @Override",
" @Test",
" void test() {}",
"",
" @Override",
" @Test",
" void method1() {}",
"",
" @Override",
@@ -267,6 +291,10 @@ final class JUnitMethodDeclarationTest {
"",
" @Override",
" @Test",
" void testToString() {}",
"",
" @Override",
" @Test",
" void testOverload() {}",
"",
" @Override",
@@ -279,6 +307,17 @@ final class JUnitMethodDeclarationTest {
" @Override",
" @Test",
" void testPublic() {}",
"",
" @Override",
" @Test",
" void testNull() {}",
"",
" @Override",
" @Test",
" void testRecord() {}",
"",
" @Test",
" void testMethodThatIsOverriddenWithoutOverrideAnnotation() {}",
"}")
.addSourceLines(
"C.java",
@@ -352,6 +391,9 @@ final class JUnitMethodDeclarationTest {
" protected void quux() {}",
"",
" @Test",
" public void testToString() {}",
"",
" @Test",
" public void testOverload() {}",
"",
" void overload() {}",
@@ -361,6 +403,9 @@ final class JUnitMethodDeclarationTest {
"",
" @Test",
" private void testClass() {}",
"",
" @Test",
" private void testTrue() {}",
"}")
.addOutputLines(
"A.java",
@@ -407,6 +452,9 @@ final class JUnitMethodDeclarationTest {
" void quux() {}",
"",
" @Test",
" void testToString() {}",
"",
" @Test",
" void testOverload() {}",
"",
" void overload() {}",
@@ -416,6 +464,9 @@ final class JUnitMethodDeclarationTest {
"",
" @Test",
" void testClass() {}",
"",
" @Test",
" void testTrue() {}",
"}")
.doTest(TestMode.TEXT_MATCH);
}

View File

@@ -39,4 +39,19 @@ final class NestedOptionalsTest {
"}")
.doTest();
}
@Test
void identificationOptionalTypeNotLoaded() {
compilationTestHelper
.addSourceLines(
"A.java",
"import java.time.Duration;",
"",
"class A {",
" void m() {",
" Duration.ofSeconds(1);",
" }",
"}")
.doTest();
}
}

View File

@@ -19,7 +19,7 @@ final class RequestParamTypeTest {
"import java.util.List;",
"import java.util.Map;",
"import java.util.Set;",
"import org.jspecify.nullness.Nullable;",
"import org.jspecify.annotations.Nullable;",
"import org.springframework.web.bind.annotation.DeleteMapping;",
"import org.springframework.web.bind.annotation.GetMapping;",
"import org.springframework.web.bind.annotation.PostMapping;",

View File

@@ -0,0 +1,68 @@
package tech.picnic.errorprone.bugpatterns;
import static com.google.errorprone.BugCheckerRefactoringTestHelper.newInstance;
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
public class StepVerifierDuplicateExpectNextTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(StepVerifierDuplicateExpectNext.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
newInstance(StepVerifierDuplicateExpectNext.class, getClass());
@Test
void replacementFirstSuggestedFix() {
refactoringTestHelper
.addInputLines(
"A.java",
"import reactor.core.publisher.Flux;",
"import reactor.test.StepVerifier;",
"",
"class A {",
" void m() {",
" // BUG Diagnotics contains:",
" Flux.just(0, 1).as(StepVerifier::create).expectNext(0).expectNext(1);",
" }",
"}")
.addOutputLines(
"A.java",
"import reactor.core.publisher.Flux;",
"import reactor.test.StepVerifier;",
"",
"class A {",
" void m() {",
" Flux.just(0, 1).as(StepVerifier::create).expectNext(0, 1);",
" }",
"}")
.doTest(TestMode.TEXT_MATCH);
}
@Test
void replacement2() {
refactoringTestHelper
.addInputLines(
"A.java",
"import reactor.core.publisher.Flux;",
"import reactor.test.StepVerifier;",
"",
"class A {",
" void m() {",
" Flux.just(0, 1).as(StepVerifier::create).expectNext(0).expectNext(1).verifyComplete();",
" }",
"}")
.addOutputLines(
"A.java",
"import reactor.core.publisher.Flux;",
"import reactor.test.StepVerifier;",
"",
"class A {",
" void m() {",
" Flux.just(0, 1).as(StepVerifier::create).expectNext(0, 1).verifyComplete();",
" }",
"}")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -62,6 +62,13 @@ final class StringCaseLocaleUsageTest {
" void m() {",
" \"a\".toLowerCase(/* Comment with parens: (). */ );",
" \"b\".toUpperCase();",
" \"c\".toLowerCase().toString();",
"",
" toString().toLowerCase();",
" toString().toUpperCase /* Comment with parens: (). */();",
"",
" this.toString().toLowerCase() /* Comment with parens: (). */;",
" this.toString().toUpperCase();",
" }",
"}")
.addOutputLines(
@@ -70,8 +77,15 @@ final class StringCaseLocaleUsageTest {
"",
"class A {",
" void m() {",
" \"a\".toLowerCase(Locale.ROOT /* Comment with parens: (). */);",
" \"a\".toLowerCase(/* Comment with parens: (). */ Locale.ROOT);",
" \"b\".toUpperCase(Locale.ROOT);",
" \"c\".toLowerCase(Locale.ROOT).toString();",
"",
" toString().toLowerCase(Locale.ROOT);",
" toString().toUpperCase /* Comment with parens: (). */(Locale.ROOT);",
"",
" this.toString().toLowerCase(Locale.ROOT) /* Comment with parens: (). */;",
" this.toString().toUpperCase(Locale.ROOT);",
" }",
"}")
.doTest(TestMode.TEXT_MATCH);
@@ -87,6 +101,13 @@ final class StringCaseLocaleUsageTest {
" void m() {",
" \"a\".toLowerCase();",
" \"b\".toUpperCase(/* Comment with parens: (). */ );",
" \"c\".toLowerCase().toString();",
"",
" toString().toLowerCase();",
" toString().toUpperCase /* Comment with parens: (). */();",
"",
" this.toString().toLowerCase() /* Comment with parens: (). */;",
" this.toString().toUpperCase();",
" }",
"}")
.addOutputLines(
@@ -96,7 +117,14 @@ final class StringCaseLocaleUsageTest {
"class A {",
" void m() {",
" \"a\".toLowerCase(Locale.getDefault());",
" \"b\".toUpperCase(Locale.getDefault() /* Comment with parens: (). */);",
" \"b\".toUpperCase(/* Comment with parens: (). */ Locale.getDefault());",
" \"c\".toLowerCase(Locale.getDefault()).toString();",
"",
" toString().toLowerCase(Locale.getDefault());",
" toString().toUpperCase /* Comment with parens: (). */(Locale.getDefault());",
"",
" this.toString().toLowerCase(Locale.getDefault()) /* Comment with parens: (). */;",
" this.toString().toUpperCase(Locale.getDefault());",
" }",
"}")
.doTest(TestMode.TEXT_MATCH);

View File

@@ -0,0 +1,34 @@
package tech.picnic.errorprone.bugpatterns.util;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.params.provider.Arguments.arguments;
import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
final class JavaKeywordsTest {
private static Stream<Arguments> isValidIdentifierTestCases() {
/* { str, expected } */
return Stream.of(
arguments("", false),
arguments("public", false),
arguments("true", false),
arguments("false", false),
arguments("null", false),
arguments("0", false),
arguments("\0", false),
arguments("a%\0", false),
arguments("a", true),
arguments("a0", true),
arguments("_a0", true),
arguments("test", true));
}
@MethodSource("isValidIdentifierTestCases")
@ParameterizedTest
void isValidIdentifier(String str, boolean expected) {
assertThat(JavaKeywords.isValidIdentifier(str)).isEqualTo(expected);
}
}

View File

@@ -46,6 +46,10 @@ final class DoubleStreamRulesTest implements RefasterRuleCollectionTestCase {
return Stream.of(1).flatMapToDouble(v -> DoubleStream.of(v * v).flatMap(DoubleStream::of));
}
DoubleStream testDoubleStreamFilterSorted() {
return DoubleStream.of(1, 4, 3, 2).sorted().filter(d -> d % 2 == 0);
}
ImmutableSet<Boolean> testDoubleStreamIsEmpty() {
return ImmutableSet.of(
DoubleStream.of(1).count() == 0,

View File

@@ -46,6 +46,10 @@ final class DoubleStreamRulesTest implements RefasterRuleCollectionTestCase {
return Stream.of(1).flatMapToDouble(v -> DoubleStream.of(v * v)).flatMap(DoubleStream::of);
}
DoubleStream testDoubleStreamFilterSorted() {
return DoubleStream.of(1, 4, 3, 2).filter(d -> d % 2 == 0).sorted();
}
ImmutableSet<Boolean> testDoubleStreamIsEmpty() {
return ImmutableSet.of(
DoubleStream.of(1).findAny().isEmpty(),

View File

@@ -50,6 +50,10 @@ final class IntStreamRulesTest implements RefasterRuleCollectionTestCase {
return Stream.of(1).flatMapToInt(v -> IntStream.of(v * v).flatMap(IntStream::of));
}
IntStream testIntStreamFilterSorted() {
return IntStream.of(1, 4, 3, 2).sorted().filter(i -> i % 2 == 0);
}
ImmutableSet<Boolean> testIntStreamIsEmpty() {
return ImmutableSet.of(
IntStream.of(1).count() == 0,

View File

@@ -50,6 +50,10 @@ final class IntStreamRulesTest implements RefasterRuleCollectionTestCase {
return Stream.of(1).flatMapToInt(v -> IntStream.of(v * v)).flatMap(IntStream::of);
}
IntStream testIntStreamFilterSorted() {
return IntStream.of(1, 4, 3, 2).filter(i -> i % 2 == 0).sorted();
}
ImmutableSet<Boolean> testIntStreamIsEmpty() {
return ImmutableSet.of(
IntStream.of(1).findAny().isEmpty(),

View File

@@ -50,6 +50,10 @@ final class LongStreamRulesTest implements RefasterRuleCollectionTestCase {
return Stream.of(1).flatMapToLong(v -> LongStream.of(v * v).flatMap(LongStream::of));
}
LongStream testLongStreamFilterSorted() {
return LongStream.of(1, 4, 3, 2).sorted().filter(l -> l % 2 == 0);
}
ImmutableSet<Boolean> testLongStreamIsEmpty() {
return ImmutableSet.of(
LongStream.of(1).count() == 0,

View File

@@ -50,6 +50,10 @@ final class LongStreamRulesTest implements RefasterRuleCollectionTestCase {
return Stream.of(1).flatMapToLong(v -> LongStream.of(v * v)).flatMap(LongStream::of);
}
LongStream testLongStreamFilterSorted() {
return LongStream.of(1, 4, 3, 2).filter(l -> l % 2 == 0).sorted();
}
ImmutableSet<Boolean> testLongStreamIsEmpty() {
return ImmutableSet.of(
LongStream.of(1).findAny().isEmpty(),

View File

@@ -1,5 +1,6 @@
package tech.picnic.errorprone.refasterrules;
import static java.util.Comparator.reverseOrder;
import static java.util.function.Function.identity;
import static org.assertj.core.api.Assertions.assertThat;
@@ -114,8 +115,8 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
Flux.just("baz").switchIfEmpty(Flux.just("qux")));
}
Mono<Integer> testMonoSwitchIfEmptyOfEmptyPublisher() {
return Mono.just(1).switchIfEmpty(Mono.empty());
ImmutableSet<Mono<?>> testMonoIdentity() {
return ImmutableSet.of(Mono.just(1).switchIfEmpty(Mono.empty()), Mono.<Void>empty().then());
}
ImmutableSet<Flux<Integer>> testFluxSwitchIfEmptyOfEmptyPublisher() {
@@ -220,6 +221,10 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
Flux.concat(Mono.just("baz")));
}
Mono<Void> testMonoThen() {
return Mono.just("foo").flux().then();
}
Mono<Optional<String>> testMonoCollectToOptional() {
return Mono.just("foo").map(Optional::of).defaultIfEmpty(Optional.empty());
}
@@ -327,6 +332,14 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
return Flux.just(1).onErrorReturn(IllegalArgumentException.class::isInstance, 2);
}
Flux<Integer> testFluxFilterSort() {
return Flux.just(1, 4, 3, 2).sort().filter(i -> i % 2 == 0);
}
Flux<Integer> testFluxFilterSortWithComparator() {
return Flux.just(1, 4, 3, 2).sort(reverseOrder()).filter(i -> i % 2 == 0);
}
ImmutableSet<Context> testContextEmpty() {
return ImmutableSet.of(Context.of(new HashMap<>()), Context.of(ImmutableMap.of()));
}

View File

@@ -1,6 +1,7 @@
package tech.picnic.errorprone.refasterrules;
import static com.google.common.collect.MoreCollectors.toOptional;
import static java.util.Comparator.reverseOrder;
import static java.util.function.Function.identity;
import static org.assertj.core.api.Assertions.assertThat;
import static reactor.function.TupleUtils.function;
@@ -119,8 +120,8 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
Flux.just("foo").defaultIfEmpty("bar"), Flux.just("baz").defaultIfEmpty("qux"));
}
Mono<Integer> testMonoSwitchIfEmptyOfEmptyPublisher() {
return Mono.just(1);
ImmutableSet<Mono<?>> testMonoIdentity() {
return ImmutableSet.of(Mono.just(1), Mono.<Void>empty());
}
ImmutableSet<Flux<Integer>> testFluxSwitchIfEmptyOfEmptyPublisher() {
@@ -219,6 +220,10 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
Mono.just("foo").flux(), Mono.just("bar").flux(), Mono.just("baz").flux());
}
Mono<Void> testMonoThen() {
return Mono.just("foo").then();
}
Mono<Optional<String>> testMonoCollectToOptional() {
return Mono.just("foo").flux().collect(toOptional());
}
@@ -322,6 +327,14 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
return Flux.just(1).onErrorReturn(IllegalArgumentException.class, 2);
}
Flux<Integer> testFluxFilterSort() {
return Flux.just(1, 4, 3, 2).filter(i -> i % 2 == 0).sort();
}
Flux<Integer> testFluxFilterSortWithComparator() {
return Flux.just(1, 4, 3, 2).filter(i -> i % 2 == 0).sort(reverseOrder());
}
ImmutableSet<Context> testContextEmpty() {
return ImmutableSet.of(Context.empty(), Context.empty());
}

View File

@@ -16,7 +16,7 @@ import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class StreamRulesTest implements RefasterRuleCollectionTestCase {
@Override
public ImmutableSet<?> elidedTypesAndStaticImports() {
return ImmutableSet.of(Objects.class, Streams.class, not(null), reverseOrder());
return ImmutableSet.of(Objects.class, Streams.class, not(null));
}
String testJoining() {
@@ -56,6 +56,14 @@ final class StreamRulesTest implements RefasterRuleCollectionTestCase {
return Stream.of("foo").flatMap(v -> Stream.of(v.length()).flatMap(Stream::of));
}
Stream<Integer> testStreamFilterSorted() {
return Stream.of(1, 4, 3, 2).sorted().filter(i -> i % 2 == 0);
}
Stream<Integer> testStreamFilterSortedWithComparator() {
return Stream.of(1, 4, 3, 2).sorted(reverseOrder()).filter(i -> i % 2 == 0);
}
ImmutableSet<Optional<Integer>> testStreamMapFirst() {
return ImmutableSet.of(
Stream.of("foo").map(s -> s.length()).findFirst(),

View File

@@ -18,7 +18,7 @@ import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class StreamRulesTest implements RefasterRuleCollectionTestCase {
@Override
public ImmutableSet<?> elidedTypesAndStaticImports() {
return ImmutableSet.of(Objects.class, Streams.class, not(null), reverseOrder());
return ImmutableSet.of(Objects.class, Streams.class, not(null));
}
String testJoining() {
@@ -57,6 +57,14 @@ final class StreamRulesTest implements RefasterRuleCollectionTestCase {
return Stream.of("foo").flatMap(v -> Stream.of(v.length())).flatMap(Stream::of);
}
Stream<Integer> testStreamFilterSorted() {
return Stream.of(1, 4, 3, 2).filter(i -> i % 2 == 0).sorted();
}
Stream<Integer> testStreamFilterSortedWithComparator() {
return Stream.of(1, 4, 3, 2).filter(i -> i % 2 == 0).sorted(reverseOrder());
}
ImmutableSet<Optional<Integer>> testStreamMapFirst() {
return ImmutableSet.of(
Stream.of("foo").findFirst().map(s -> s.length()),

32
pom.xml
View File

@@ -4,7 +4,7 @@
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>error-prone-support</artifactId>
<version>0.5.1-SNAPSHOT</version>
<version>0.6.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Picnic :: Error Prone Support</name>
@@ -148,13 +148,13 @@
<version.auto-service>1.0.1</version.auto-service>
<version.auto-value>1.10.1</version.auto-value>
<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-fork>v${version.error-prone-orig}-picnic-2</version.error-prone-fork>
<version.error-prone-orig>2.16</version.error-prone-orig>
<version.error-prone-slf4j>0.1.16</version.error-prone-slf4j>
<version.guava-beta-checker>1.0</version.guava-beta-checker>
<version.jdk>11</version.jdk>
<version.maven>3.8.6</version.maven>
<version.mockito>4.9.0</version.mockito>
<version.mockito>4.11.0</version.mockito>
<version.nopen-checker>1.0.1</version.nopen-checker>
<version.nullaway>0.10.5</version.nullaway>
<version.pitest-git>1.0.3</version.pitest-git>
@@ -281,7 +281,7 @@
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-bom</artifactId>
<version>2022.0.0</version>
<version>2022.0.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -333,7 +333,7 @@
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.9.1</version>
<version>1.9.19</version>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
@@ -353,12 +353,12 @@
<dependency>
<groupId>org.immutables</groupId>
<artifactId>value-annotations</artifactId>
<version>2.9.2</version>
<version>2.9.3</version>
</dependency>
<dependency>
<groupId>org.jspecify</groupId>
<artifactId>jspecify</artifactId>
<version>0.2.0</version>
<version>0.3.0</version>
</dependency>
<dependency>
<groupId>org.junit</groupId>
@@ -377,7 +377,7 @@
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.5</version>
<version>2.0.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
@@ -389,7 +389,7 @@
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<version>2.7.6</version>
<version>2.7.7</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
@@ -611,15 +611,15 @@
</property>
<property name="illegalClasses" value="com.mongodb.lang.Nullable">
<!-- Instead, please use
`org.jspecify.nullness.Nullable`. -->
`org.jspecify.annotations.Nullable`. -->
</property>
<property name="illegalClasses" value="io.micrometer.core.lang.Nullable">
<!-- Instead, please use
`org.jspecify.nullness.Nullable`. -->
`org.jspecify.annotations.Nullable`. -->
</property>
<property name="illegalClasses" value="javax.annotation.Nullable">
<!-- Instead, please use
`org.jspecify.nullness.Nullable`. -->
`org.jspecify.annotations.Nullable`. -->
</property>
<property name="illegalClasses" value="javax.annotation.concurrent.Immutable">
<!-- Instead, please use
@@ -635,7 +635,7 @@
</property>
<property name="illegalClasses" value="org.springframework.lang.Nullable">
<!-- Instead, please use
`org.jspecify.nullness.Nullable`. -->
`org.jspecify.annotations.Nullable`. -->
</property>
<property name="illegalPkgs" value="com.amazonaws.annotation" />
<property name="illegalPkgs" value="com.beust.jcommander.internal" />
@@ -852,6 +852,7 @@
<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>
<arg>--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg>
<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>-Xmaxerrs</arg>
@@ -997,6 +998,7 @@
<additionalJOption>--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</additionalJOption>
<additionalJOption>--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</additionalJOption>
<additionalJOption>--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</additionalJOption>
<additionalJOption>--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED</additionalJOption>
<additionalJOption>--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</additionalJOption>
<additionalJOption>--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</additionalJOption>
</additionalJOptions>
@@ -1195,7 +1197,7 @@
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>versions-maven-plugin</artifactId>
<version>2.13.0</version>
<version>2.14.2</version>
<configuration>
<updateBuildOutputTimestampPolicy>never</updateBuildOutputTimestampPolicy>
</configuration>
@@ -1268,7 +1270,7 @@
<dependency>
<groupId>com.groupcdg.arcmutate</groupId>
<artifactId>base</artifactId>
<version>1.0.1</version>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>com.groupcdg.pitest</groupId>

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>error-prone-support</artifactId>
<version>0.5.1-SNAPSHOT</version>
<version>0.6.1-SNAPSHOT</version>
</parent>
<artifactId>refaster-compiler</artifactId>

View File

@@ -31,7 +31,7 @@ import java.util.Map;
import javax.tools.FileObject;
import javax.tools.JavaFileManager;
import javax.tools.StandardLocation;
import org.jspecify.nullness.Nullable;
import org.jspecify.annotations.Nullable;
import tech.picnic.errorprone.refaster.AnnotatedCompositeCodeTransformer;
/**

View File

@@ -3,5 +3,5 @@
* files on the classpath.
*/
@com.google.errorprone.annotations.CheckReturnValue
@org.jspecify.nullness.NullMarked
@org.jspecify.annotations.NullMarked
package tech.picnic.errorprone.refaster.plugin;

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>error-prone-support</artifactId>
<version>0.5.1-SNAPSHOT</version>
<version>0.6.1-SNAPSHOT</version>
</parent>
<artifactId>refaster-runner</artifactId>

View File

@@ -1,4 +1,4 @@
/** Exposes Refaster rules found on the classpath through a regular Error Prone check. */
@com.google.errorprone.annotations.CheckReturnValue
@org.jspecify.nullness.NullMarked
@org.jspecify.annotations.NullMarked
package tech.picnic.errorprone.refaster.runner;

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>error-prone-support</artifactId>
<version>0.5.1-SNAPSHOT</version>
<version>0.6.1-SNAPSHOT</version>
</parent>
<artifactId>refaster-support</artifactId>

View File

@@ -4,5 +4,5 @@
* non-patch mode.
*/
@com.google.errorprone.annotations.CheckReturnValue
@org.jspecify.nullness.NullMarked
@org.jspecify.annotations.NullMarked
package tech.picnic.errorprone.refaster.annotation;

View File

@@ -5,5 +5,5 @@
* com.google.errorprone.refaster.annotation.NotMatches @NotMatches} annotations.
*/
@com.google.errorprone.annotations.CheckReturnValue
@org.jspecify.nullness.NullMarked
@org.jspecify.annotations.NullMarked
package tech.picnic.errorprone.refaster.matchers;

View File

@@ -1,4 +1,4 @@
/** Assorted classes that aid the compilation or evaluation of Refaster rules. */
@com.google.errorprone.annotations.CheckReturnValue
@org.jspecify.nullness.NullMarked
@org.jspecify.annotations.NullMarked
package tech.picnic.errorprone.refaster;

View File

@@ -11,7 +11,7 @@ import com.sun.source.tree.ImportTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree;
import com.sun.source.util.TreeScanner;
import org.jspecify.nullness.Nullable;
import org.jspecify.annotations.Nullable;
/**
* An abstract {@link BugChecker} that reports a match for each expression matched by the given

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>error-prone-support</artifactId>
<version>0.5.1-SNAPSHOT</version>
<version>0.6.1-SNAPSHOT</version>
</parent>
<artifactId>refaster-test-support</artifactId>

View File

@@ -44,7 +44,7 @@ import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;
import org.jspecify.nullness.Nullable;
import org.jspecify.annotations.Nullable;
import tech.picnic.errorprone.refaster.runner.CodeTransformers;
import tech.picnic.errorprone.refaster.runner.Refaster;

View File

@@ -1,4 +1,4 @@
/** Opinionated utilities for the testing of Refaster rules. */
@com.google.errorprone.annotations.CheckReturnValue
@org.jspecify.nullness.NullMarked
@org.jspecify.annotations.NullMarked
package tech.picnic.errorprone.refaster.test;

View File

@@ -8,7 +8,7 @@ import com.google.errorprone.refaster.annotation.BeforeTemplate;
import com.google.errorprone.refaster.annotation.Placeholder;
import com.google.errorprone.refaster.annotation.UseImportPolicy;
import java.util.Set;
import org.jspecify.nullness.Nullable;
import org.jspecify.annotations.Nullable;
/** Refaster rule collection to validate that having no violations works as expected. */
final class ValidRules {

View File

@@ -24,6 +24,9 @@ If you are not familiar with Jekyll, be sure to check out its
[documentation][jekyll-docs]. It is recommended to follow the provided
step-by-step tutorial.
We use the [Just the Docs][just-the-docs] Jekyll theme, which also includes
several configuration options.
###### Switch Ruby versions
The required Ruby version is set in `.ruby-version`. To switch, you can use
@@ -56,5 +59,6 @@ Actions workflow any time a change is merged to `master`.
[jekyll]: https://jekyllrb.com
[jekyll-docs]: https://jekyllrb.com/docs
[jekyll-docs-installation]: https://jekyllrb.com/docs/installation
[just-the-docs]: https://just-the-docs.github.io/just-the-docs/
[localhost-port-4000]: http://127.0.0.1:4000
[rvm]: https://rvm.io

View File

@@ -28,6 +28,12 @@ nav_external_links:
url: https://github.com/PicnicSupermarket/error-prone-support
hide_icon: false
callouts:
summary:
color: blue
note:
color: grey-dk
# SEO configuration.
# See https://jekyll.github.io/jekyll-seo-tag/usage.
social:

View File

@@ -1,5 +1,5 @@
<img src="/assets/images/picnic-logo@2x.png" alt="Picnic Logo" id="logo" />
<img src="/assets/images/picnic-logo@2x.png" alt="Picnic Logo" id="logo"/>
<p align="center">
<p>
Copyright &copy; 2017-2022 Picnic Technologies BV
</p>

View File

@@ -4,15 +4,15 @@
<link rel="icon" type="image/png" sizes="16x16" href="/assets/images/favicon-16x16.png">
<link rel="manifest" href="/assets/images/site.webmanifest">
<link rel="mask-icon" href="/assets/images/safari-pinned-tab.svg" color="#5bbad5">
<link rel="shortcut icon" href="/favicon.ico">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="msapplication-config" content="/assets/images/browserconfig.xml">
<meta name="theme-color" content="#ffffff">
<!-- XXX: The theme does not natively support both light and dark mode. Drop
this section once https://github.com/just-the-docs/just-the-docs/issues/234 is
resolved. -->
<!-- Support light and dark mode, as it's not natively supported. See
https://github.com/just-the-docs/just-the-docs/issues/234. -->
<link rel="stylesheet" href="{{ '/assets/css/just-the-docs-eps-light.css' | relative_url }}"
media="(prefers-color-scheme: light)">
media="(prefers-color-scheme: light)">
<link rel="stylesheet" href="{{ '/assets/css/just-the-docs-eps-dark.css' | relative_url }}"
media="(prefers-color-scheme: dark)">
media="(prefers-color-scheme: dark)">
<meta name="google-site-verification" content="2GBzy2ufS8Rfqffu8T6iqng6dbDw9EKuykMisUZU3IQ"/>

View File

@@ -1,5 +1,20 @@
footer > img#logo {
// Add support for external anchor icons.
.external > svg {
width: 1rem;
vertical-align: text-bottom;
}
.label {
// Reduce spacing between labels and align with surrounding elements.
margin-left: 0 !important;
}
footer {
text-align: center;
img#logo {
width: 2rem;
margin: 0 auto;
display: block;
}
}

View File

@@ -1 +1,5 @@
// Overrides for Just the Docs. See
// https://github.com/just-the-docs/just-the-docs/blob/main/_sass/support/_variables.scss.
// Grid system.
$nav-width: 400px;

View File

@@ -1,3 +1,16 @@
@import "./color_schemes/dark";
@import "_variables";
@import "_common";
// Swap `$blue-000` and `$blue-300`, mainly for callouts. This is done by
// default for red, but not for other colors.
$blue-000: #183385;
$blue-300: #2c84fa;
// Use light-theme greys in dark theme so that summary callouts stand out more.
// (Note that the former has four shades, while the latter has five.)
$grey-dk-000: $grey-lt-000;
$grey-dk-100: $grey-lt-100;
$grey-dk-200: $grey-lt-200;
$grey-dk-250: $grey-lt-200;
$grey-dk-300: $grey-lt-300;

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 26 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 26 KiB