From 0d0a17cb17f7e1580bb7b24116d33983c70caaf2 Mon Sep 17 00:00:00 2001 From: Romain Brenguier Date: Mon, 12 Oct 2026 17:23:57 +0200 Subject: [PATCH 1/7] Add rule metadata --- .../org/sonar/l10n/java/rules/java/S9432.html | 57 +++++++++++++++++++ .../org/sonar/l10n/java/rules/java/S9432.json | 25 ++++++++ .../main/resources/profiles/Sonar_way/S9432 | 0 3 files changed, 82 insertions(+) create mode 100644 sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9432.html create mode 100644 sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9432.json create mode 100644 sonar-java-plugin/src/main/resources/profiles/Sonar_way/S9432 diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9432.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9432.html new file mode 100644 index 00000000000..7175fdf8a8b --- /dev/null +++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9432.html @@ -0,0 +1,57 @@ +

This rule raises an issue when code calls a method that removes leading and trailing whitespace from a string and then checks its length or +emptiness to determine if the string contains only whitespace.

+

In Java, this specifically refers to the trim() and strip() methods.

+

Why is this an issue?

+

Invoking operations that create new string objects with whitespace removed incurs unnecessary memory allocation and management overhead. When the +goal is simply to check if a string contains only whitespace, creating this intermediate string object is wasteful.

+

The following patterns are inefficient:

+ +

These checks create a temporary string object just to examine it, which is discarded immediately after the check completes.

+

In performance-sensitive code or when processing many strings, this inefficiency can accumulate, increasing memory pressure and CPU usage for +memory management operations.

+

In Java specifically, this applies to patterns like string.trim().length() > 0, string.trim().isEmpty(), +string.strip().length() > 0, string.strip().isEmpty(), or string.strip().isBlank().

+

What is the potential impact?

+

The performance impact of this issue depends on how frequently the code executes:

+ +

The issue also affects code readability. Using the appropriate whitespace-checking method makes the intent clearer than checking the length of a +trimmed string.

+

In Java, the appropriate method is isBlank().

+

How to fix it

+

For Java 11 and later, replace trim/strip-based checks with the isBlank() method, which efficiently checks if a string contains only +whitespace without creating intermediate objects.

+

Code examples

+

Noncompliant code example

+
+public void validate(String input) {
+    if (input != null && input.trim().length() > 0) { // Noncompliant
+        process(input);
+    }
+}
+
+

Compliant solution

+
+public void validate(String input) {
+    if (input != null && !input.isBlank()) {
+        process(input);
+    }
+}
+
+

Resources

+

Documentation

+ + diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9432.json b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9432.json new file mode 100644 index 00000000000..dc6fe1f0be3 --- /dev/null +++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9432.json @@ -0,0 +1,25 @@ +{ + "title": "Strings should not be trimmed or stripped just to check if they are blank", + "type": "CODE_SMELL", + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "5min" + }, + "tags": [ + "performance", + "optimization", + "pitfall" + ], + "defaultSeverity": "Minor", + "ruleSpecification": "RSPEC-9432", + "sqKey": "S9432", + "scope": "Main", + "quickfix": "unknown", + "code": { + "impacts": { + "MAINTAINABILITY": "LOW" + }, + "attribute": "EFFICIENT" + } +} diff --git a/sonar-java-plugin/src/main/resources/profiles/Sonar_way/S9432 b/sonar-java-plugin/src/main/resources/profiles/Sonar_way/S9432 new file mode 100644 index 00000000000..e69de29bb2d From 68e2c1533a52b7a7efb24c10d6695825e493785c Mon Sep 17 00:00:00 2001 From: Romain Brenguier Date: Mon, 12 Oct 2026 17:26:48 +0200 Subject: [PATCH 2/7] Add failing reproducer --- .../main/java/checks/S9432CheckSample.java | 65 +++++++++++++++++++ .../org/sonar/java/checks/S9432Check.java | 45 +++++++++++++ .../org/sonar/java/checks/S9432CheckTest.java | 52 +++++++++++++++ 3 files changed, 162 insertions(+) create mode 100644 java-checks-test-sources/default/src/main/java/checks/S9432CheckSample.java create mode 100644 java-checks/src/main/java/org/sonar/java/checks/S9432Check.java create mode 100644 java-checks/src/test/java/org/sonar/java/checks/S9432CheckTest.java diff --git a/java-checks-test-sources/default/src/main/java/checks/S9432CheckSample.java b/java-checks-test-sources/default/src/main/java/checks/S9432CheckSample.java new file mode 100644 index 00000000000..a686a14dddc --- /dev/null +++ b/java-checks-test-sources/default/src/main/java/checks/S9432CheckSample.java @@ -0,0 +1,65 @@ +package checks; + +public class S9432CheckSample { + + boolean hasContent(String input) { + boolean result; + result = input.trim().length() == 0; // Noncompliant {{Use "isBlank()" on the original string instead.}} + result = input.trim().length() <= 0; // Noncompliant {{Use "isBlank()" on the original string instead.}} + result = input.trim().length() < 1; // Noncompliant {{Use "isBlank()" on the original string instead.}} + result = input.strip().length() != 0; // Noncompliant {{Use "isBlank()" on the original string instead.}} + result = input.strip().length() > 0; // Noncompliant {{Use "isBlank()" on the original string instead.}} + result = input.strip().length() >= 1; // Noncompliant {{Use "isBlank()" on the original string instead.}} + result = 0 == input.trim().length(); // Noncompliant {{Use "isBlank()" on the original string instead.}} + result = 0 >= input.trim().length(); // Noncompliant {{Use "isBlank()" on the original string instead.}} + result = 1 > input.trim().length(); // Noncompliant {{Use "isBlank()" on the original string instead.}} + result = 0 != input.strip().length(); // Noncompliant {{Use "isBlank()" on the original string instead.}} + result = 0 < input.strip().length(); // Noncompliant {{Use "isBlank()" on the original string instead.}} + result = 1 <= input.strip().length(); // Noncompliant {{Use "isBlank()" on the original string instead.}} + result = input.trim().isEmpty(); // Noncompliant {{Use "isBlank()" on the original string instead.}} + result = !input.strip().isEmpty(); // Noncompliant {{Use "isBlank()" on the original string instead.}} + result = input.trim().isBlank(); // Noncompliant {{Use "isBlank()" on the original string instead.}} + result = input.strip().isBlank(); // Noncompliant {{Use "isBlank()" on the original string instead.}} + return result; + } + + void validUses(String input) { + boolean blank = input.isBlank(); + boolean present = !input.isBlank(); + boolean longEnough = input.trim().length() > 1; + boolean exactLength = input.strip().length() == 5; + boolean matches = input.trim().equals("value"); + String normalized = input.trim(); + audit(input.strip()); + audit(normalized + blank + present + longEnough + exactLength + matches); + } + + void audit(String input) { + } + + static class TextBuffer { + TextBuffer trim() { + return this; + } + + TextBuffer strip() { + return this; + } + + int length() { + return 0; + } + + boolean isEmpty() { + return true; + } + + boolean isBlank() { + return true; + } + } + + boolean customText(TextBuffer input) { + return input.trim().length() == 0 || input.strip().isEmpty() || input.trim().isBlank(); + } +} diff --git a/java-checks/src/main/java/org/sonar/java/checks/S9432Check.java b/java-checks/src/main/java/org/sonar/java/checks/S9432Check.java new file mode 100644 index 00000000000..8c0a453ed63 --- /dev/null +++ b/java-checks/src/main/java/org/sonar/java/checks/S9432Check.java @@ -0,0 +1,45 @@ +/* + * SonarQube Java + * Copyright (C) SonarSource Sàrl + * mailto:info AT sonarsource DOT com + * + * You can redistribute and/or modify this program under the terms of + * the Sonar Source-Available License Version 1, as published by SonarSource Sàrl. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Sonar Source-Available License for more details. + * + * You should have received a copy of the Sonar Source-Available License + * along with this program; if not, see https://sonarsource.com/license/ssal/ + */ +package org.sonar.java.checks; + +import java.util.List; +import org.sonar.check.Rule; +import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; +import org.sonar.plugins.java.api.JavaVersion; +import org.sonar.plugins.java.api.JavaVersionAwareVisitor; +import org.sonar.plugins.java.api.tree.Tree; + +@Rule(key = "S9432") +public class S9432Check extends IssuableSubscriptionVisitor implements JavaVersionAwareVisitor { + + @Override + public boolean isCompatibleWithJavaVersion(JavaVersion version) { + return version.asInt() >= 11; + } + + @Override + public List nodesToVisit() { + return List.of(Tree.Kind.METHOD_INVOCATION); + } + + @Override + public void visitNode(Tree tree) { + if (!tree.is(Tree.Kind.METHOD_INVOCATION)) { + throw new IllegalArgumentException(); + } + } +} diff --git a/java-checks/src/test/java/org/sonar/java/checks/S9432CheckTest.java b/java-checks/src/test/java/org/sonar/java/checks/S9432CheckTest.java new file mode 100644 index 00000000000..4fbdb06e86b --- /dev/null +++ b/java-checks/src/test/java/org/sonar/java/checks/S9432CheckTest.java @@ -0,0 +1,52 @@ +/* + * SonarQube Java + * Copyright (C) SonarSource Sàrl + * mailto:info AT sonarsource DOT com + * + * You can redistribute and/or modify this program under the terms of + * the Sonar Source-Available License Version 1, as published by SonarSource Sàrl. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Sonar Source-Available License for more details. + * + * You should have received a copy of the Sonar Source-Available License + * along with this program; if not, see https://sonarsource.com/license/ssal/ + */ +package org.sonar.java.checks; + +import org.junit.jupiter.api.Test; +import org.sonar.java.checks.verifier.CheckVerifier; + +import static org.sonar.java.checks.verifier.TestUtils.mainCodeSourcesPath; + +class S9432CheckTest { + + @Test + void detects_blankness_checks_after_whitespace_removal() { + CheckVerifier.newVerifier() + .onFile(mainCodeSourcesPath("checks/S9432CheckSample.java")) + .withCheck(new S9432Check()) + .withJavaVersion(11) + .verifyIssues(); + } + + @Test + void ignores_pre_java_11_code() { + CheckVerifier.newVerifier() + .onFile(mainCodeSourcesPath("checks/S9432CheckSample.java")) + .withCheck(new S9432Check()) + .withJavaVersion(8) + .verifyNoIssues(); + } + + @Test + void ignores_code_without_semantic_information() { + CheckVerifier.newVerifier() + .onFile(mainCodeSourcesPath("checks/S9432CheckSample.java")) + .withCheck(new S9432Check()) + .withoutSemantic() + .verifyNoIssues(); + } +} From 6089bfaf45a9df093a563e49010658df8c34718d Mon Sep 17 00:00:00 2001 From: Romain Brenguier Date: Mon, 12 Oct 2026 17:27:41 +0200 Subject: [PATCH 3/7] Implement rule --- .../org/sonar/java/checks/S9432Check.java | 88 ++++++++++++++++++- 1 file changed, 86 insertions(+), 2 deletions(-) diff --git a/java-checks/src/main/java/org/sonar/java/checks/S9432Check.java b/java-checks/src/main/java/org/sonar/java/checks/S9432Check.java index 8c0a453ed63..c96bb547ef0 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/S9432Check.java +++ b/java-checks/src/main/java/org/sonar/java/checks/S9432Check.java @@ -17,15 +17,44 @@ package org.sonar.java.checks; import java.util.List; +import javax.annotation.Nullable; import org.sonar.check.Rule; +import org.sonar.java.model.ExpressionUtils; +import org.sonar.java.model.LiteralUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.JavaVersion; import org.sonar.plugins.java.api.JavaVersionAwareVisitor; +import org.sonar.plugins.java.api.semantic.MethodMatchers; +import org.sonar.plugins.java.api.tree.BinaryExpressionTree; +import org.sonar.plugins.java.api.tree.ExpressionTree; +import org.sonar.plugins.java.api.tree.MemberSelectExpressionTree; +import org.sonar.plugins.java.api.tree.MethodInvocationTree; import org.sonar.plugins.java.api.tree.Tree; @Rule(key = "S9432") public class S9432Check extends IssuableSubscriptionVisitor implements JavaVersionAwareVisitor { + private static final String MESSAGE = "Use \"isBlank()\" on the original string instead."; + private static final String STRING_TYPE = "java.lang.String"; + + private static final MethodMatchers WHITESPACE_REMOVAL_METHODS = MethodMatchers.create() + .ofTypes(STRING_TYPE) + .names("trim", "strip") + .addWithoutParametersMatcher() + .build(); + + private static final MethodMatchers BLANKNESS_METHODS = MethodMatchers.create() + .ofTypes(STRING_TYPE) + .names("isEmpty", "isBlank") + .addWithoutParametersMatcher() + .build(); + + private static final MethodMatchers LENGTH_METHOD = MethodMatchers.create() + .ofTypes(STRING_TYPE) + .names("length") + .addWithoutParametersMatcher() + .build(); + @Override public boolean isCompatibleWithJavaVersion(JavaVersion version) { return version.asInt() >= 11; @@ -38,8 +67,63 @@ public List nodesToVisit() { @Override public void visitNode(Tree tree) { - if (!tree.is(Tree.Kind.METHOD_INVOCATION)) { - throw new IllegalArgumentException(); + MethodInvocationTree invocation = (MethodInvocationTree) tree; + MethodInvocationTree removalInvocation = whitespaceRemovalInvocation(invocation); + if (removalInvocation == null) { + return; + } + if (BLANKNESS_METHODS.matches(invocation) || (LENGTH_METHOD.matches(invocation) && isEmptinessCheck(invocation))) { + reportIssue(removalInvocation, MESSAGE); + } + } + + @Nullable + private static MethodInvocationTree whitespaceRemovalInvocation(MethodInvocationTree invocation) { + ExpressionTree receiver = receiver(invocation); + if (receiver instanceof MethodInvocationTree receiverInvocation && WHITESPACE_REMOVAL_METHODS.matches(receiverInvocation)) { + return receiverInvocation; + } + return null; + } + + @Nullable + private static ExpressionTree receiver(MethodInvocationTree invocation) { + if (invocation.methodSelect() instanceof MemberSelectExpressionTree memberSelect) { + return ExpressionUtils.skipParentheses(memberSelect.expression()); } + return null; + } + + private static boolean isEmptinessCheck(MethodInvocationTree lengthInvocation) { + Tree parent = lengthInvocation.parent(); + while (parent.is(Tree.Kind.PARENTHESIZED_EXPRESSION)) { + parent = parent.parent(); + } + if (!(parent instanceof BinaryExpressionTree binaryExpression)) { + return false; + } + ExpressionTree left = ExpressionUtils.skipParentheses(binaryExpression.leftOperand()); + ExpressionTree right = ExpressionUtils.skipParentheses(binaryExpression.rightOperand()); + if (left == lengthInvocation) { + return comparisonWithLiteralOnRight(binaryExpression, right); + } + if (right == lengthInvocation) { + return comparisonWithLiteralOnLeft(binaryExpression, left); + } + return false; + } + + private static boolean comparisonWithLiteralOnRight(BinaryExpressionTree binaryExpression, ExpressionTree literal) { + return (binaryExpression.is(Tree.Kind.EQUAL_TO, Tree.Kind.LESS_THAN_OR_EQUAL_TO) && LiteralUtils.isZero(literal)) + || (binaryExpression.is(Tree.Kind.NOT_EQUAL_TO, Tree.Kind.GREATER_THAN) && LiteralUtils.isZero(literal)) + || (binaryExpression.is(Tree.Kind.LESS_THAN) && LiteralUtils.isOne(literal)) + || (binaryExpression.is(Tree.Kind.GREATER_THAN_OR_EQUAL_TO) && LiteralUtils.isOne(literal)); + } + + private static boolean comparisonWithLiteralOnLeft(BinaryExpressionTree binaryExpression, ExpressionTree literal) { + return (LiteralUtils.isZero(literal) && binaryExpression.is(Tree.Kind.EQUAL_TO, Tree.Kind.GREATER_THAN_OR_EQUAL_TO)) + || (LiteralUtils.isZero(literal) && binaryExpression.is(Tree.Kind.NOT_EQUAL_TO, Tree.Kind.LESS_THAN)) + || (LiteralUtils.isOne(literal) && binaryExpression.is(Tree.Kind.GREATER_THAN)) + || (LiteralUtils.isOne(literal) && binaryExpression.is(Tree.Kind.LESS_THAN_OR_EQUAL_TO)); } } From 987a0da952b9c09ec08a248f181cf82141acd751 Mon Sep 17 00:00:00 2001 From: Romain Brenguier Date: Tue, 13 Oct 2026 16:06:52 +0200 Subject: [PATCH 4/7] Qualify S9432 message for trim()-based checks trim() removes characters <= U+0020 while isBlank() relies on Character.isWhitespace, so replacing a trim()-based check by isBlank() is only equivalent if Unicode whitespace should count as blank and control characters as non-blank. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../src/main/java/checks/S9432CheckSample.java | 16 ++++++++-------- .../java/org/sonar/java/checks/S9432Check.java | 5 ++++- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/java-checks-test-sources/default/src/main/java/checks/S9432CheckSample.java b/java-checks-test-sources/default/src/main/java/checks/S9432CheckSample.java index a686a14dddc..8346f6d9246 100644 --- a/java-checks-test-sources/default/src/main/java/checks/S9432CheckSample.java +++ b/java-checks-test-sources/default/src/main/java/checks/S9432CheckSample.java @@ -4,21 +4,21 @@ public class S9432CheckSample { boolean hasContent(String input) { boolean result; - result = input.trim().length() == 0; // Noncompliant {{Use "isBlank()" on the original string instead.}} - result = input.trim().length() <= 0; // Noncompliant {{Use "isBlank()" on the original string instead.}} - result = input.trim().length() < 1; // Noncompliant {{Use "isBlank()" on the original string instead.}} + result = input.trim().length() == 0; // Noncompliant {{Use "isBlank()" on the original string instead, if Unicode whitespace should be treated as blank and control characters as non-blank.}} + result = input.trim().length() <= 0; // Noncompliant {{Use "isBlank()" on the original string instead, if Unicode whitespace should be treated as blank and control characters as non-blank.}} + result = input.trim().length() < 1; // Noncompliant {{Use "isBlank()" on the original string instead, if Unicode whitespace should be treated as blank and control characters as non-blank.}} result = input.strip().length() != 0; // Noncompliant {{Use "isBlank()" on the original string instead.}} result = input.strip().length() > 0; // Noncompliant {{Use "isBlank()" on the original string instead.}} result = input.strip().length() >= 1; // Noncompliant {{Use "isBlank()" on the original string instead.}} - result = 0 == input.trim().length(); // Noncompliant {{Use "isBlank()" on the original string instead.}} - result = 0 >= input.trim().length(); // Noncompliant {{Use "isBlank()" on the original string instead.}} - result = 1 > input.trim().length(); // Noncompliant {{Use "isBlank()" on the original string instead.}} + result = 0 == input.trim().length(); // Noncompliant {{Use "isBlank()" on the original string instead, if Unicode whitespace should be treated as blank and control characters as non-blank.}} + result = 0 >= input.trim().length(); // Noncompliant {{Use "isBlank()" on the original string instead, if Unicode whitespace should be treated as blank and control characters as non-blank.}} + result = 1 > input.trim().length(); // Noncompliant {{Use "isBlank()" on the original string instead, if Unicode whitespace should be treated as blank and control characters as non-blank.}} result = 0 != input.strip().length(); // Noncompliant {{Use "isBlank()" on the original string instead.}} result = 0 < input.strip().length(); // Noncompliant {{Use "isBlank()" on the original string instead.}} result = 1 <= input.strip().length(); // Noncompliant {{Use "isBlank()" on the original string instead.}} - result = input.trim().isEmpty(); // Noncompliant {{Use "isBlank()" on the original string instead.}} + result = input.trim().isEmpty(); // Noncompliant {{Use "isBlank()" on the original string instead, if Unicode whitespace should be treated as blank and control characters as non-blank.}} result = !input.strip().isEmpty(); // Noncompliant {{Use "isBlank()" on the original string instead.}} - result = input.trim().isBlank(); // Noncompliant {{Use "isBlank()" on the original string instead.}} + result = input.trim().isBlank(); // Noncompliant {{Use "isBlank()" on the original string instead, if Unicode whitespace should be treated as blank and control characters as non-blank.}} result = input.strip().isBlank(); // Noncompliant {{Use "isBlank()" on the original string instead.}} return result; } diff --git a/java-checks/src/main/java/org/sonar/java/checks/S9432Check.java b/java-checks/src/main/java/org/sonar/java/checks/S9432Check.java index c96bb547ef0..7cc76873174 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/S9432Check.java +++ b/java-checks/src/main/java/org/sonar/java/checks/S9432Check.java @@ -35,6 +35,8 @@ public class S9432Check extends IssuableSubscriptionVisitor implements JavaVersionAwareVisitor { private static final String MESSAGE = "Use \"isBlank()\" on the original string instead."; + private static final String TRIM_MESSAGE = "Use \"isBlank()\" on the original string instead, " + + "if Unicode whitespace should be treated as blank and control characters as non-blank."; private static final String STRING_TYPE = "java.lang.String"; private static final MethodMatchers WHITESPACE_REMOVAL_METHODS = MethodMatchers.create() @@ -73,7 +75,8 @@ public void visitNode(Tree tree) { return; } if (BLANKNESS_METHODS.matches(invocation) || (LENGTH_METHOD.matches(invocation) && isEmptinessCheck(invocation))) { - reportIssue(removalInvocation, MESSAGE); + String message = "trim".equals(removalInvocation.methodSymbol().name()) ? TRIM_MESSAGE : MESSAGE; + reportIssue(removalInvocation, message); } } From a0eaa726edae6ab9cd1e356cca2aea6f410a901f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 14 Oct 2026 09:40:40 +0200 Subject: [PATCH 5/7] Update ruling results for PR #6332 (#6334) Co-authored-by: github-actions[bot] --- .../expected/eclipse-jetty/java-S9432.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 its/ruling/src/test/resources/expected/eclipse-jetty/java-S9432.json diff --git a/its/ruling/src/test/resources/expected/eclipse-jetty/java-S9432.json b/its/ruling/src/test/resources/expected/eclipse-jetty/java-S9432.json new file mode 100644 index 00000000000..e84fc39f2c0 --- /dev/null +++ b/its/ruling/src/test/resources/expected/eclipse-jetty/java-S9432.json @@ -0,0 +1,13 @@ +{ +"eclipse-jetty:jetty-server/src/main/java/org/eclipse/jetty/server/MultiPartFormInputStream.java": [ +143 +], +"eclipse-jetty:jetty-util/src/main/java/org/eclipse/jetty/util/HttpCookieStore.java": [ +84 +], +"eclipse-jetty:jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java": [ +1324, +1333, +1485 +] +} From a21986e37ec3c89bb1cc57d8ab08797f87ff0e24 Mon Sep 17 00:00:00 2001 From: Romain Brenguier Date: Wed, 14 Oct 2026 09:46:34 +0200 Subject: [PATCH 6/7] Increase S9432 coverage and remove unreachable branch Co-Authored-By: Claude Opus 5.5 (1M context) --- .../default/src/main/java/checks/S9432CheckSample.java | 10 +++++++++- .../main/java/org/sonar/java/checks/S9432Check.java | 7 ++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/java-checks-test-sources/default/src/main/java/checks/S9432CheckSample.java b/java-checks-test-sources/default/src/main/java/checks/S9432CheckSample.java index 8346f6d9246..5579fcc8962 100644 --- a/java-checks-test-sources/default/src/main/java/checks/S9432CheckSample.java +++ b/java-checks-test-sources/default/src/main/java/checks/S9432CheckSample.java @@ -20,6 +20,9 @@ boolean hasContent(String input) { result = !input.strip().isEmpty(); // Noncompliant {{Use "isBlank()" on the original string instead.}} result = input.trim().isBlank(); // Noncompliant {{Use "isBlank()" on the original string instead, if Unicode whitespace should be treated as blank and control characters as non-blank.}} result = input.strip().isBlank(); // Noncompliant {{Use "isBlank()" on the original string instead.}} + result = (input.trim().length()) == 0; // Noncompliant {{Use "isBlank()" on the original string instead, if Unicode whitespace should be treated as blank and control characters as non-blank.}} + result = 0 < ((input.strip().length())); // Noncompliant {{Use "isBlank()" on the original string instead.}} + result = (input.trim()).isEmpty(); // Noncompliant {{Use "isBlank()" on the original string instead, if Unicode whitespace should be treated as blank and control characters as non-blank.}} return result; } @@ -28,10 +31,15 @@ void validUses(String input) { boolean present = !input.isBlank(); boolean longEnough = input.trim().length() > 1; boolean exactLength = input.strip().length() == 5; + boolean otherComparisons = input.trim().length() != 2 || input.trim().length() <= 1 || input.trim().length() < 2 || input.trim().length() >= 2 + || 2 != input.trim().length() || 1 >= input.trim().length() || 2 > input.trim().length() || 2 <= input.trim().length() || 5 == input.trim().length() || 0 > input.trim().length(); + int trimmedLength = input.trim().length(); + boolean sumIsZero = input.trim().length() + trimmedLength == 0; + boolean parenthesizedSum = (input.strip().length()) + 1 > 0; boolean matches = input.trim().equals("value"); String normalized = input.trim(); audit(input.strip()); - audit(normalized + blank + present + longEnough + exactLength + matches); + audit(normalized + blank + present + longEnough + exactLength + matches + sumIsZero + parenthesizedSum + otherComparisons); } void audit(String input) { diff --git a/java-checks/src/main/java/org/sonar/java/checks/S9432Check.java b/java-checks/src/main/java/org/sonar/java/checks/S9432Check.java index 7cc76873174..528bec58a66 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/S9432Check.java +++ b/java-checks/src/main/java/org/sonar/java/checks/S9432Check.java @@ -106,14 +106,11 @@ private static boolean isEmptinessCheck(MethodInvocationTree lengthInvocation) { return false; } ExpressionTree left = ExpressionUtils.skipParentheses(binaryExpression.leftOperand()); - ExpressionTree right = ExpressionUtils.skipParentheses(binaryExpression.rightOperand()); if (left == lengthInvocation) { + ExpressionTree right = ExpressionUtils.skipParentheses(binaryExpression.rightOperand()); return comparisonWithLiteralOnRight(binaryExpression, right); } - if (right == lengthInvocation) { - return comparisonWithLiteralOnLeft(binaryExpression, left); - } - return false; + return comparisonWithLiteralOnLeft(binaryExpression, left); } private static boolean comparisonWithLiteralOnRight(BinaryExpressionTree binaryExpression, ExpressionTree literal) { From 6a682bc20e220972b730aab00d3aca060780968e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 14 Oct 2026 15:30:04 +0200 Subject: [PATCH 7/7] Update ruling results for PR #6332 (#6345) Co-authored-by: github-actions[bot] --- .../expected/eclipse-jetty-similar-to-main/java-S9432.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 its/ruling/src/test/resources/expected/eclipse-jetty-similar-to-main/java-S9432.json diff --git a/its/ruling/src/test/resources/expected/eclipse-jetty-similar-to-main/java-S9432.json b/its/ruling/src/test/resources/expected/eclipse-jetty-similar-to-main/java-S9432.json new file mode 100644 index 00000000000..64489596bfd --- /dev/null +++ b/its/ruling/src/test/resources/expected/eclipse-jetty-similar-to-main/java-S9432.json @@ -0,0 +1,5 @@ +{ +"eclipse-jetty:jetty-server/src/main/java/org/eclipse/jetty/server/MultiPartFormInputStream.java": [ +143 +] +}