From 614489206a023bfb6223f098ca84f0b2589cffe7 Mon Sep 17 00:00:00 2001 From: tomasz-tylenda-sonarsource Date: Thu, 21 Nov 2024 13:56:22 +0100 Subject: [PATCH] SONARJAVA-5184 S5411 Suppress boxed Boolean warnings on @NonNull parameters. (#4931) Co-authored-by: leonardo-pilastri-sonarsource <115481625+leonardo-pilastri-sonarsource@users.noreply.github.com> --- .../BoxedBooleanExpressionsCheckSample.java | 50 +++++++++++++++++++ .../checks/BoxedBooleanExpressionsCheck.java | 12 +++-- 2 files changed, 58 insertions(+), 4 deletions(-) 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..e8dfa1a83e1 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)