diff --git a/java-checks-test-sources/default/src/main/java/checks/BoxedBooleanExpressionsCheckSample.java b/java-checks-test-sources/default/src/main/java/checks/BoxedBooleanExpressionsCheckSample.java index 81b4fa95a28..0db3737b5ac 100644 --- a/java-checks-test-sources/default/src/main/java/checks/BoxedBooleanExpressionsCheckSample.java +++ b/java-checks-test-sources/default/src/main/java/checks/BoxedBooleanExpressionsCheckSample.java @@ -1,5 +1,7 @@ package checks; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; @@ -725,4 +727,52 @@ void testBoxedValue(Boolean value) { } } + // Allow suppressing warnings caused by generic parameters, + // see SONARJAVA-5184. + public void lambdaParameterAnnotations() { + List xs = new ArrayList<>(); + xs.add(true); + + xs.forEach(b -> { + if (b) { // Noncompliant + foo(); + } else { + bar(); + } + }); + + xs.forEach((Boolean b) -> { + if (b) { // Noncompliant + foo(); + } else { + bar(); + } + }); + + xs.forEach((@NonNull Boolean b) -> { + if (b) { + foo(); + } else { + bar(); + } + }); + + // Suppressing warnings on @NonNull Booleans works too and we like it. + + Boolean badBoolean = getSurprizeBoxedBoolean(); + + if(badBoolean) { // Noncompliant + foo(); + } else { + bar(); + } + + @NonNull Boolean goodBoolean = getSurprizeBoxedBoolean(); + + if(goodBoolean) { + foo(); + } else { + bar(); + } + } } diff --git a/java-checks/src/main/java/org/sonar/java/checks/BoxedBooleanExpressionsCheck.java b/java-checks/src/main/java/org/sonar/java/checks/BoxedBooleanExpressionsCheck.java index 684be03c97c..6afe8dcf725 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/BoxedBooleanExpressionsCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/BoxedBooleanExpressionsCheck.java @@ -217,7 +217,7 @@ private static Optional getParentConditionalBranch(Tree tree) { @CheckForNull private static ExpressionTree findBoxedBoolean(ExpressionTree tree) { - if (tree.symbolType().is(BOOLEAN) && !isValidMethodInvocation(tree)) { + if (tree.symbolType().is(BOOLEAN) && !isValidMethodInvocation(tree) && !isNonnullIdentifier(tree)) { return tree; } if (tree.is(Kind.LOGICAL_COMPLEMENT)) { @@ -245,7 +245,7 @@ private static boolean isNullCheck(ExpressionTree tree) { private static boolean isValidMethodInvocation(ExpressionTree tree) { if (tree.is(Kind.METHOD_INVOCATION)) { MethodInvocationTree mit = (MethodInvocationTree) tree; - return isOptionalInvocation(mit) || isAnnotatedNonnull(mit); + return isOptionalInvocation(mit) || isAnnotatedNonnull(mit.methodSymbol()); } return false; } @@ -254,8 +254,12 @@ private static boolean isOptionalInvocation(MethodInvocationTree mit) { return OPTIONAL_OR_ELSE.matches(mit) && !mit.arguments().get(0).is(Kind.NULL_LITERAL); } - private static boolean isAnnotatedNonnull(MethodInvocationTree mit) { - return mit.methodSymbol().metadata() + private static boolean isNonnullIdentifier(ExpressionTree tree) { + return (tree instanceof IdentifierTree it) && isAnnotatedNonnull(it.symbol()); + } + + private static boolean isAnnotatedNonnull(Symbol symbol) { + return symbol.metadata() .annotations() .stream() .map(SymbolMetadata.AnnotationInstance::symbol)