From 255814949ba9b9b22c46c0be002f6263e5f4a8a5 Mon Sep 17 00:00:00 2001 From: Romain Brenguier Date: Thu, 24 Sep 2026 11:30:38 +0200 Subject: [PATCH 01/11] SONARJAVA-7054 Reduce S9397 scope to short format strings without brackets or quotes Limit the rule to format strings shorter than 15 characters and exclude cases where %s markers appear inside unclosed brackets {}()[] or between single quotes. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../java/checks/StringFormatCheckSample.java | 38 ++++++++++++++++++- .../sonar/java/checks/StringFormatCheck.java | 36 +++++++++++++++++- 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/java-checks-test-sources/default/src/main/java/checks/StringFormatCheckSample.java b/java-checks-test-sources/default/src/main/java/checks/StringFormatCheckSample.java index 56afb5d4e2c..359a004238f 100644 --- a/java-checks-test-sources/default/src/main/java/checks/StringFormatCheckSample.java +++ b/java-checks-test-sources/default/src/main/java/checks/StringFormatCheckSample.java @@ -18,7 +18,7 @@ String label(String key, Object value) { } String wrapped(String value) { - return String.format("prefix-%s-suffix", value); // Noncompliant {{Use string concatenation instead of String.format().}} + return String.format("prefix-%s-suffix", value); // compliant: format string is too long (>= 15 chars) } String localized(Object value) { @@ -152,4 +152,40 @@ String nonLocalizedFormattableConcat(String prefix, Formattable value) { String escapeSequenceInFormat(int value) { return String.format("\045s", value); // compliant } + + String longFormatString(String a, String b) { + return String.format("long prefix %s/%s", a, b); // compliant: format string is too long (>= 15 chars) + } + + String inBraces(String value) { + return String.format("{%s}", value); // compliant: %s is inside brackets + } + + String inParentheses(String value) { + return String.format("(%s)", value); // compliant: %s is inside brackets + } + + String inSquareBrackets(String value) { + return String.format("[%s]", value); // compliant: %s is inside brackets + } + + String inQuotes(String value) { + return String.format("'%s'", value); // Noncompliant {{Use string concatenation instead of String.format().}} + } + + String inBackticks(String value) { + return String.format("`%s`", value); // compliant: %s is between backticks + } + + String escapedPercentInBrackets(String value) { + return String.format("[%%s] %s", value); // Noncompliant {{Use string concatenation instead of String.format().}} + } + + String escapedPercentInQuotes(String value) { + return String.format("'%%s' %s", value); // Noncompliant {{Use string concatenation instead of String.format().}} + } + + String apostropheInFormat(String owner, String item) { + return String.format("%s's %s", owner, item); // Noncompliant {{Use string concatenation instead of String.format().}} + } } diff --git a/java-checks/src/main/java/org/sonar/java/checks/StringFormatCheck.java b/java-checks/src/main/java/org/sonar/java/checks/StringFormatCheck.java index 14b923f7ba9..a7a82cf51e5 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/StringFormatCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/StringFormatCheck.java @@ -60,11 +60,11 @@ protected void onMethodInvocationFound(MethodInvocationTree invocation) { } LiteralTree literal = (LiteralTree) formatArgument; String rawValue = LiteralUtils.trimQuotes(literal.value()); - if (rawValue.contains("\\")) { + if (rawValue.length() >= 15 || rawValue.contains("\\")) { return; } int placeholders = countSimplePlaceholders(rawValue); - if (placeholders <= 0 || invocation.arguments().size() != formatIndex + placeholders + 1) { + if (placeholders <= 0 || hasPlaceholderInBracketsOrQuotes(rawValue) || invocation.arguments().size() != formatIndex + placeholders + 1) { return; } List valueArguments = invocation.arguments().subList(formatIndex + 1, invocation.arguments().size()); @@ -86,6 +86,38 @@ private static boolean isJustPlaceholder(String rawValue) { return "%s".equals(rawValue); } + private static boolean hasPlaceholderInBracketsOrQuotes(String value) { + int depth = 0; + boolean inBackticks = false; + for (int i = 0; i < value.length(); i++) { + char c = value.charAt(i); + if (c == '`') { + inBackticks = !inBackticks; + continue; + } + if (c == '%' && i + 1 < value.length()) { + char next = value.charAt(i + 1); + if (next == '%') { + i++; + continue; + } + if (next == 's' && (inBackticks || depth > 0)) { + return true; + } + continue; + } + if (inBackticks) { + continue; + } + if (c == '{' || c == '(' || c == '[') { + depth++; + } else if (c == '}' || c == ')' || c == ']') { + depth = Math.max(0, depth - 1); + } + } + return false; + } + private static int countSimplePlaceholders(String value) { int placeholders = 0; int index = 0; From 82e3da645a96fcca4f2d8f4cafe418b636981be1 Mon Sep 17 00:00:00 2001 From: Romain Brenguier Date: Thu, 24 Sep 2026 12:07:30 +0200 Subject: [PATCH 02/11] SONARJAVA-7054 Refactor hasPlaceholderInBracketsOrQuotes and add test coverage Rewrite the method to use a while-loop with if/else-if chains instead of a for-loop with multiple continue statements. Extract helper methods to reduce cognitive complexity. Add test cases for edge cases (unmatched closing brackets, escaped percents in backticks, nested brackets, backtick sections without placeholders). Co-Authored-By: Claude Opus 4.6 (1M context) --- .../java/checks/StringFormatCheckSample.java | 16 ++++++++ .../sonar/java/checks/StringFormatCheck.java | 40 ++++++++++--------- 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/java-checks-test-sources/default/src/main/java/checks/StringFormatCheckSample.java b/java-checks-test-sources/default/src/main/java/checks/StringFormatCheckSample.java index 359a004238f..09ed4ee4845 100644 --- a/java-checks-test-sources/default/src/main/java/checks/StringFormatCheckSample.java +++ b/java-checks-test-sources/default/src/main/java/checks/StringFormatCheckSample.java @@ -188,4 +188,20 @@ String escapedPercentInQuotes(String value) { String apostropheInFormat(String owner, String item) { return String.format("%s's %s", owner, item); // Noncompliant {{Use string concatenation instead of String.format().}} } + + String unmatchedClosingBracket(String value) { + return String.format(")%s", value); // Noncompliant {{Use string concatenation instead of String.format().}} + } + + String escapedPercentInBackticks(String value) { + return String.format("`%%s` %s", value); // Noncompliant {{Use string concatenation instead of String.format().}} + } + + String nestedBrackets(String value) { + return String.format("({%s})", value); // compliant: %s is inside brackets + } + + String backtickNoPlaceholder(String value) { + return String.format("`x` %s", value); // Noncompliant {{Use string concatenation instead of String.format().}} + } } diff --git a/java-checks/src/main/java/org/sonar/java/checks/StringFormatCheck.java b/java-checks/src/main/java/org/sonar/java/checks/StringFormatCheck.java index a7a82cf51e5..2063a62794f 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/StringFormatCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/StringFormatCheck.java @@ -89,35 +89,37 @@ private static boolean isJustPlaceholder(String rawValue) { private static boolean hasPlaceholderInBracketsOrQuotes(String value) { int depth = 0; boolean inBackticks = false; - for (int i = 0; i < value.length(); i++) { + int i = 0; + while (i < value.length()) { char c = value.charAt(i); if (c == '`') { inBackticks = !inBackticks; - continue; - } - if (c == '%' && i + 1 < value.length()) { - char next = value.charAt(i + 1); - if (next == '%') { - i++; - continue; - } - if (next == 's' && (inBackticks || depth > 0)) { + } else if (c == '%' && i + 1 < value.length()) { + if (isEnclosedPlaceholder(value.charAt(i + 1), inBackticks, depth)) { return true; } - continue; - } - if (inBackticks) { - continue; - } - if (c == '{' || c == '(' || c == '[') { - depth++; - } else if (c == '}' || c == ')' || c == ']') { - depth = Math.max(0, depth - 1); + i++; + } else if (!inBackticks) { + depth = updateBracketDepth(c, depth); } + i++; } return false; } + private static boolean isEnclosedPlaceholder(char next, boolean inBackticks, int depth) { + return next == 's' && (inBackticks || depth > 0); + } + + private static int updateBracketDepth(char c, int depth) { + if (c == '{' || c == '(' || c == '[') { + return depth + 1; + } else if (c == '}' || c == ')' || c == ']') { + return Math.max(0, depth - 1); + } + return depth; + } + private static int countSimplePlaceholders(String value) { int placeholders = 0; int index = 0; From 55124b1df23f5bc0066a903d2894aafd61b95d99 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 24 Sep 2026 10:09:11 +0000 Subject: [PATCH 03/11] Update ruling results MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with GitHub Actions --- .../expected/eclipse-jetty/java-S9397.json | 68 +- .../expected/sonar-server/java-S9397.json | 722 +----------------- 2 files changed, 2 insertions(+), 788 deletions(-) diff --git a/its/ruling/src/test/resources/expected/eclipse-jetty/java-S9397.json b/its/ruling/src/test/resources/expected/eclipse-jetty/java-S9397.json index 0f2be8af36e..29a7b06873a 100644 --- a/its/ruling/src/test/resources/expected/eclipse-jetty/java-S9397.json +++ b/its/ruling/src/test/resources/expected/eclipse-jetty/java-S9397.json @@ -2,24 +2,12 @@ "org.eclipse.jetty:jetty-project:jetty-http/src/main/java/org/eclipse/jetty/http/HttpCompliance.java": [ 249 ], -"org.eclipse.jetty:jetty-project:jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java": [ -1972, -1974 -], -"org.eclipse.jetty:jetty-project:jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/AbstractPathSpec.java": [ -64 -], -"org.eclipse.jetty:jetty-project:jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/MappedResource.java": [ -99 -], "org.eclipse.jetty:jetty-project:jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java": [ 462 ], "org.eclipse.jetty:jetty-project:jetty-io/src/main/java/org/eclipse/jetty/io/ConnectionStatistics.java": [ 217, -218, -219, -220 +218 ], "org.eclipse.jetty:jetty-project:jetty-io/src/main/java/org/eclipse/jetty/io/SelectorManager.java": [ 368 @@ -34,69 +22,18 @@ "org.eclipse.jetty:jetty-project:jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java": [ 139 ], -"org.eclipse.jetty:jetty-project:jetty-jmx/src/main/java/org/eclipse/jetty/jmx/MetaData.java": [ -249, -257 -], "org.eclipse.jetty:jetty-project:jetty-server/src/main/java/org/eclipse/jetty/server/DebugListener.java": [ 308 ], -"org.eclipse.jetty:jetty-project:jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelOverHttp.java": [ -339 -], -"org.eclipse.jetty:jetty-project:jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java": [ -931 -], -"org.eclipse.jetty:jetty-project:jetty-server/src/main/java/org/eclipse/jetty/server/MultiPartParser.java": [ -667, -712, -714 -], "org.eclipse.jetty:jetty-project:jetty-server/src/main/java/org/eclipse/jetty/server/Server.java": [ 480 ], -"org.eclipse.jetty:jetty-project:jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java": [ -610 -], -"org.eclipse.jetty:jetty-project:jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandlerCollection.java": [ -374 -], -"org.eclipse.jetty:jetty-project:jetty-server/src/main/java/org/eclipse/jetty/server/session/DatabaseAdaptor.java": [ -311 -], -"org.eclipse.jetty:jetty-project:jetty-server/src/main/java/org/eclipse/jetty/server/session/DefaultSessionIdManager.java": [ -488 -], -"org.eclipse.jetty:jetty-project:jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionDataStore.java": [ -599 -], "org.eclipse.jetty:jetty-project:jetty-slf4j-impl/src/main/java/org/eclipse/jetty/logging/JettyLogger.java": [ 685 ], "org.eclipse.jetty:jetty-project:jetty-util/src/main/java/org/eclipse/jetty/util/Fields.java": [ 339 ], -"org.eclipse.jetty:jetty-project:jetty-util/src/main/java/org/eclipse/jetty/util/IteratingCallback.java": [ -289, -295, -309, -411, -495 -], -"org.eclipse.jetty:jetty-project:jetty-util/src/main/java/org/eclipse/jetty/util/SharedBlockingCallback.java": [ -160 -], -"org.eclipse.jetty:jetty-project:jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java": [ -793 -], -"org.eclipse.jetty:jetty-project:jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslContextFactory.java": [ -1647, -1671, -1696, -1723, -1750, -1773 -], "org.eclipse.jetty:jetty-project:jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslSelectionDump.java": [ 171 ], @@ -107,8 +44,5 @@ ], "org.eclipse.jetty:jetty-project:jetty-util/src/test/java/org/eclipse/jetty/util/component/LifeCycleListenerNestedTest.java": [ 116 -], -"org.eclipse.jetty:jetty-project:jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java": [ -396 ] } diff --git a/its/ruling/src/test/resources/expected/sonar-server/java-S9397.json b/its/ruling/src/test/resources/expected/sonar-server/java-S9397.json index b424aee6428..5cb09055f16 100644 --- a/its/ruling/src/test/resources/expected/sonar-server/java-S9397.json +++ b/its/ruling/src/test/resources/expected/sonar-server/java-S9397.json @@ -1,790 +1,70 @@ { -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/ce/http/CeHttpClient.java": [ -112 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/app/ProcessCommandWrapperImpl.java": [ -89, -93 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/app/TomcatConnectors.java": [ -51 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/app/TomcatContexts.java": [ -75, -85 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/authentication/AuthenticationError.java": [ -69, -81 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/authentication/AuthenticationFilter.java": [ -58 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/authentication/IdentityProviderRepository.java": [ -54 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/authentication/InitFilter.java": [ -82, -88 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/authentication/OAuth2CallbackFilter.java": [ -77, -83 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/authentication/OAuth2ContextFactory.java": [ -81, -106 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/authentication/OAuthCsrfVerifier.java": [ -53 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/authentication/UserIdentityAuthenticator.java": [ -94, -95, -104, -105 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/batch/IssuesAction.java": [ -130 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/component/ComponentFinder.java": [ -124 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/component/ws/FilterParser.java": [ -69, -198 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/component/ws/ProjectMeasuresQueryFactory.java": [ -129, -151 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/component/ws/SearchProjectsAction.java": [ -114, -118 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/component/ws/SuggestionCategory.java": [ -51 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/queue/ReportSubmitter.java": [ -98 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/api/measurecomputer/MeasureComputerContextImpl.java": [ -177 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/api/measurecomputer/MeasureImpl.java": [ -80 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/api/posttask/ConditionToCondition.java": [ -72, -89 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/api/posttask/PostProjectAnalysisTasksExecutor.java": [ -162 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/component/ComponentRootBuilder.java": [ -105 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/component/PathAwareCrawler.java": [ -117, -138, -177 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/component/PathAwareVisitorWrapper.java": [ -116 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/component/VisitorsCrawler.java": [ -141 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/formula/AverageFormula.java": [ -157 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ComponentIssuesRepositoryImpl.java": [ -49 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/issue/EffortAggregator.java": [ -122 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IntegrateIssuesVisitor.java": [ -83 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/issue/NewEffortAggregator.java": [ -150 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ScmAccountToUserLoader.java": [ -59 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/issue/commonrule/CommonRule.java": [ -86, -90 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/issue/filter/IssueFilter.java": [ -116, -118 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/measure/MapBasedRawMeasureRepository.java": [ -79, -95, -106 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/measure/Measure.java": [ -308 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/metric/MetricRepositoryImpl.java": [ -72, -83 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/qualitygate/Condition.java": [ -72 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/qualitygate/ConditionEvaluator.java": [ -62, -90, -108, -138 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/NewMaintainabilityMeasuresVisitor.java": [ -167 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/RatingGrid.java": [ -77 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/RatingSettings.java": [ -85 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoImpl.java": [ -66 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/source/HighlightingLineReader.java": [ -81, -128 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/source/SymbolsLineReader.java": [ -73 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/step/AbstractComputationSteps.java": [ -45 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/step/ComputeMeasureVariationsStep.java": [ -146 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/step/CustomMeasuresCopyStep.java": [ -106 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/step/LoadQualityGateStep.java": [ -94 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStep.java": [ -116, -125, -130, -142, -147 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistComponentsStep.java": [ -128 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistEventsStep.java": [ -137 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistFileSourcesStep.java": [ -124 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStep.java": [ -114, -145 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/step/QualityGateEventsStep.java": [ -95, -103, -124 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/step/QualityGateMeasuresStep.java": [ -135, -150 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/step/QualityProfileEventsStep.java": [ -159 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/step/UnitTestMeasuresStep.java": [ -97 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/step/ValidateProjectStep.java": [ -130 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookCallerImpl.java": [ -107, -113 -], "org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookPostTask.java": [ 73, 81, 82 ], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/debt/DebtRulesXMLImporter.java": [ -159, -185, -189 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/es/BaseDoc.java": [ -57 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/es/BulkIndexer.java": [ -273 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/es/IndexCreator.java": [ -61, -77, -92 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/es/IndexDefinition.java": [ -35 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/es/IndexerStartupTask.java": [ -131 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/es/NewIndex.java": [ -83 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/es/request/ProxyBulkRequestBuilder.java": [ -50, -164 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/es/request/ProxyClearCacheRequestBuilder.java": [ -44, -72, -76 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/es/request/ProxyClusterHealthRequestBuilder.java": [ -44, -72 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/es/request/ProxyClusterStateRequestBuilder.java": [ -44, -72 -], "org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/es/request/ProxyClusterStatsRequestBuilder.java": [ -44, -72 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/es/request/ProxyCreateIndexRequestBuilder.java": [ -46, -71 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/es/request/ProxyDeleteRequestBuilder.java": [ -43 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/es/request/ProxyFlushRequestBuilder.java": [ -44, 72 ], "org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/es/request/ProxyGetRequestBuilder.java": [ -43, 69, 70, 71 ], "org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/es/request/ProxyIndexRequestBuilder.java": [ -43, 69, 70, 71 ], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/es/request/ProxyIndicesExistsRequestBuilder.java": [ -44, -72 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/es/request/ProxyIndicesStatsRequestBuilder.java": [ -44, -72 -], "org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/es/request/ProxyMultiGetRequestBuilder.java": [ -44, -72, 73, 76 ], "org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/es/request/ProxyNodesStatsRequestBuilder.java": [ -44, 72 ], "org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/es/request/ProxyPutMappingRequestBuilder.java": [ -44, -72, -76, -80 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/es/request/ProxyRefreshRequestBuilder.java": [ -44, -72 +76 ], "org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/es/request/ProxySearchRequestBuilder.java": [ -48, -74, -76, 79 ], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/es/request/ProxySearchScrollRequestBuilder.java": [ -43, -68 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/issue/IssueFinder.java": [ -43 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/issue/IssueQueryFactory.java": [ -147 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/issue/index/IssueIndex.java": [ -665 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/issue/notification/AbstractNewIssuesEmailTemplate.java": [ -97, -149, -178 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/issue/notification/MyNewIssuesEmailTemplate.java": [ -51, -63 -], "org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/issue/workflow/Transition.java": [ 118 ], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/issue/ws/AssignAction.java": [ -149, -151, -153 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/issue/ws/BulkChangeAction.java": [ -223 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/issue/ws/DeleteCommentAction.java": [ -113 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/issue/ws/EditCommentAction.java": [ -74, -136 -], "org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/issue/ws/SearchAction.java": [ 187 ], "org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/measure/custom/ws/CustomMeasureValueDescription.java": [ 35 ], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/measure/ws/ComponentAction.java": [ -195 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java": [ -140, -164, -169 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/measure/ws/ComponentTreeDataLoader.java": [ -170 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/measure/ws/ComponentTreeSort.java": [ -148 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/measure/ws/MeasureValueFormatter.java": [ -84 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/measure/ws/SearchHistoryAction.java": [ -178 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/notification/NotificationService.java": [ -70 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/organization/OrganizationCreationImpl.java": [ -95 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/organization/ws/UpdateAction.java": [ -120 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/permission/PermissionTemplateService.java": [ -212, -223 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/permission/ws/AddUserAction.java": [ -99 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/permission/ws/PermissionRequestValidator.java": [ -52, -58, -63, -72, -83, -94 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/permission/ws/PermissionWsSupport.java": [ -110 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/permission/ws/template/AddGroupToTemplateAction.java": [ -78 -], "org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/platform/Platform.java": [ 229, 232 ], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/platform/db/EmbeddedDatabase.java": [ -114 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/platform/web/MasterServletFilter.java": [ -71 -], "org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/platform/web/RedirectFilter.java": [ -67, 80, -118, 134, 153 ], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/platform/web/RootFilter.java": [ -64 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/plugins/PluginDownloader.java": [ -132 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/plugins/ServerExtensionInstaller.java": [ -88, -101 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/plugins/ServerPluginJarExploder.java": [ -62 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/plugins/ServerPluginRepository.java": [ -177, -181, -206, -252, -309, -363 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/plugins/StaticResourcesServlet.java": [ -72, -74, -95 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/plugins/privileged/PrivilegedPluginsBootstraper.java": [ -49 -], "org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/plugins/privileged/PrivilegedPluginsStopper.java": [ 54 ], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/plugins/ws/InstallAction.java": [ -95 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/plugins/ws/PluginUpdateAggregator.java": [ -90 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/plugins/ws/UninstallAction.java": [ -72 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/plugins/ws/UpdateAction.java": [ -98 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/project/ws/UpdateVisibilityAction.java": [ -103 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/projectanalysis/ws/CreateEventAction.java": [ -133, -197, -201 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/projectanalysis/ws/DeleteAction.java": [ -102 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/projectanalysis/ws/DeleteEventAction.java": [ -92, -98 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/projectanalysis/ws/UpdateEventAction.java": [ -127, -142, -151 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/qualitygate/QualityGateConditionsUpdater.java": [ -99, -107, -176, -177, -185, -188, -203 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/qualitygate/QualityGateFinder.java": [ -72 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/qualitygate/ws/ProjectStatusAction.java": [ -62 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatter.java": [ -174, -184 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileRepositoryImpl.java": [ -99, -105 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/qualityprofile/BuiltInQualityProfilesNotification.java": [ -97 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/qualityprofile/BuiltInQualityProfilesNotificationTemplate.java": [ -99 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/qualityprofile/QProfileCopier.java": [ -82 -], "org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/qualityprofile/QProfileName.java": [ 69 ], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/qualityprofile/ws/ActivateRuleAction.java": [ -81, -85 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/qualityprofile/ws/BackupAction.java": [ -73 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/qualityprofile/ws/ChangeParentAction.java": [ -94 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/qualityprofile/ws/CreateAction.java": [ -108 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/qualityprofile/ws/DeleteAction.java": [ -106 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/qualityprofile/ws/ProjectsAction.java": [ -129 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/qualityprofile/ws/RenameAction.java": [ -106 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/qualityprofile/ws/SearchAction.java": [ -94, -178 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/qualityprofile/ws/SetDefaultAction.java": [ -72 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/root/ws/SetRootAction.java": [ -70 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/root/ws/UnsetRootAction.java": [ -71 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/rule/DefaultRuleFinder.java": [ -81 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/rule/DeprecatedRulesDefinitionLoader.java": [ -136 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/rule/RegisterRules.java": [ -206, -340, -371, -439, -510 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/rule/RuleCreator.java": [ -76, -78, -206, -213 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/rule/RuleUpdater.java": [ -219 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/rule/index/RuleQuery.java": [ -224 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/rule/ws/CreateAction.java": [ -167 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/rule/ws/RuleMapper.java": [ -250 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/rule/ws/RuleQueryFactory.java": [ -129, -143 -], "org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/rule/ws/SearchAction.java": [ 139, 152 ], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/rule/ws/ShowAction.java": [ -109 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/rule/ws/UpdateAction.java": [ -230, -275 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/setting/ws/SettingValidations.java": [ -136 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/setting/ws/ValuesAction.java": [ -173 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/source/ws/HashAction.java": [ -104 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/startup/RegisterMetrics.java": [ -123, -127 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/startup/RegisterPermissionTemplates.java": [ -105, -107 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/test/index/TestIndex.java": [ -87 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/test/index/TestResultSetIterator.java": [ -92 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/user/SecurityRealmFactory.java": [ -51, -58 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/user/UserUpdater.java": [ -349 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/user/ws/DeactivateAction.java": [ -142 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/usergroups/DefaultGroupFinder.java": [ -40, -41 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/usergroups/ws/AddUserAction.java": [ -102 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/util/AbstractStoppableExecutorService.java": [ -57, -61 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/util/FloatTypeValidation.java": [ -41 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/util/IntegerTypeValidation.java": [ -41 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/util/LongTypeValidation.java": [ -40 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/util/MetricKeyValidator.java": [ -55 -], "org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/util/OkHttpClientProvider.java": [ 64 ], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/view/index/ViewIndexer.java": [ -128 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/ws/RemovedWebServiceHandler.java": [ -40 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/ws/RequestVerifier.java": [ -42 -], -"org.sonarsource.sonarqube:sonar-server:src/test/java/org/sonar/server/batch/ProjectDataLoaderTest.java": [ -123 -], -"org.sonarsource.sonarqube:sonar-server:src/test/java/org/sonar/server/computation/task/projectanalysis/component/ViewsPathAwareVisitorTest.java": [ -206 -], -"org.sonarsource.sonarqube:sonar-server:src/test/java/org/sonar/server/computation/task/projectanalysis/issue/ComponentIssuesRepositoryRule.java": [ -61, -80, -82 -], -"org.sonarsource.sonarqube:sonar-server:src/test/java/org/sonar/server/computation/task/projectanalysis/issue/FillComponentIssuesVisitorRule.java": [ -75 -], -"org.sonarsource.sonarqube:sonar-server:src/test/java/org/sonar/server/computation/task/projectanalysis/measure/MapBasedRawMeasureRepositoryTest.java": [ -193 -], -"org.sonarsource.sonarqube:sonar-server:src/test/java/org/sonar/server/computation/task/projectanalysis/measure/MeasureRepositoryImplTest.java": [ -250 -], -"org.sonarsource.sonarqube:sonar-server:src/test/java/org/sonar/server/computation/task/projectanalysis/measure/MeasureRepositoryRule.java": [ -102, -113, -153, -175, -226, -238 -], -"org.sonarsource.sonarqube:sonar-server:src/test/java/org/sonar/server/computation/task/projectanalysis/metric/MetricRepositoryImplTest.java": [ -60, -69, -97, -106 -], -"org.sonarsource.sonarqube:sonar-server:src/test/java/org/sonar/server/computation/task/projectanalysis/metric/MetricRepositoryRule.java": [ -77, -78, -95, -102 -], -"org.sonarsource.sonarqube:sonar-server:src/test/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesRepositoryRule.java": [ -47, -50 -], -"org.sonarsource.sonarqube:sonar-server:src/test/java/org/sonar/server/computation/task/projectanalysis/step/QualityGateLoadingStepTest.java": [ -83 -], "org.sonarsource.sonarqube:sonar-server:src/test/java/org/sonar/server/es/EsClientProviderTest.java": [ -80, -101, -104, 125 ], -"org.sonarsource.sonarqube:sonar-server:src/test/java/org/sonar/server/organization/ws/AddMemberActionTest.java": [ -176 -], -"org.sonarsource.sonarqube:sonar-server:src/test/java/org/sonar/server/organization/ws/EnableSupportActionTest.java": [ -242 -], -"org.sonarsource.sonarqube:sonar-server:src/test/java/org/sonar/server/permission/ws/template/AddGroupToTemplateActionTest.java": [ -123 -], "org.sonarsource.sonarqube:sonar-server:src/test/java/org/sonar/server/plugins/TestProjectUtils.java": [ 33 -], -"org.sonarsource.sonarqube:sonar-server:src/test/java/org/sonar/server/project/ws/UpdateVisibilityActionTest.java": [ -159, -269 -], -"org.sonarsource.sonarqube:sonar-server:src/test/java/org/sonar/server/qualityprofile/ws/ExportActionTest.java": [ -295, -308 -], -"org.sonarsource.sonarqube:sonar-server:src/test/java/org/sonar/server/qualityprofile/ws/QProfileWsSupportTest.java": [ -101 -], -"org.sonarsource.sonarqube:sonar-server:src/test/java/org/sonar/server/qualityprofile/ws/SearchActionTest.java": [ -337, -349 -], -"org.sonarsource.sonarqube:sonar-server:src/test/java/org/sonar/server/rule/RegisterRulesMediumTest.java": [ -94 -], -"org.sonarsource.sonarqube:sonar-server:src/test/java/org/sonar/server/test/index/TestIndexerTest.java": [ -185 -], -"org.sonarsource.sonarqube:sonar-server:src/test/java/org/sonar/server/user/ws/GroupsActionTest.java": [ -228 -], -"org.sonarsource.sonarqube:sonar-server:src/test/java/org/sonar/server/usergroups/DefaultGroupCreatorImplTest.java": [ -65 -], -"org.sonarsource.sonarqube:sonar-server:src/test/java/org/sonar/server/usergroups/DefaultGroupFinderTest.java": [ -60, -72 -], -"org.sonarsource.sonarqube:sonar-server:src/test/java/org/sonar/server/usergroups/ws/SearchActionTest.java": [ -196 -], -"org.sonarsource.sonarqube:sonar-server:src/test/java/org/sonar/server/ws/WsTester.java": [ -334, -339 ] } From ae5601dc49b52c52555ca805dfd5ba4387d86bed Mon Sep 17 00:00:00 2001 From: Romain Brenguier Date: Thu, 24 Sep 2026 12:44:59 +0200 Subject: [PATCH 04/11] SONARJAVA-7054 Simplify hasPlaceholderInBracketsOrQuotes loop structure Replace loop counter manipulation with an afterPercent flag to avoid modifying the loop variable inside the body (S127). Inline the isEnclosedPlaceholder helper to reduce method count and cognitive complexity (S3776). Co-Authored-By: Claude Opus 4.6 (1M context) --- .../sonar/java/checks/StringFormatCheck.java | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/java-checks/src/main/java/org/sonar/java/checks/StringFormatCheck.java b/java-checks/src/main/java/org/sonar/java/checks/StringFormatCheck.java index 2063a62794f..4368443359a 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/StringFormatCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/StringFormatCheck.java @@ -89,28 +89,25 @@ private static boolean isJustPlaceholder(String rawValue) { private static boolean hasPlaceholderInBracketsOrQuotes(String value) { int depth = 0; boolean inBackticks = false; - int i = 0; - while (i < value.length()) { + boolean afterPercent = false; + for (int i = 0; i < value.length(); i++) { char c = value.charAt(i); - if (c == '`') { - inBackticks = !inBackticks; - } else if (c == '%' && i + 1 < value.length()) { - if (isEnclosedPlaceholder(value.charAt(i + 1), inBackticks, depth)) { + if (afterPercent) { + afterPercent = false; + if (c == 's' && (inBackticks || depth > 0)) { return true; } - i++; + } else if (c == '`') { + inBackticks = !inBackticks; + } else if (c == '%') { + afterPercent = true; } else if (!inBackticks) { depth = updateBracketDepth(c, depth); } - i++; } return false; } - private static boolean isEnclosedPlaceholder(char next, boolean inBackticks, int depth) { - return next == 's' && (inBackticks || depth > 0); - } - private static int updateBracketDepth(char c, int depth) { if (c == '{' || c == '(' || c == '[') { return depth + 1; From 1c9a2e4f6c6af7454e50b217387a4bbd0bca8558 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 24 Sep 2026 13:06:00 +0200 Subject: [PATCH 05/11] Update ruling results for PR #6227 (#6230) Co-authored-by: github-actions[bot] --- .../java-S9397.json | 44 +------------------ 1 file changed, 1 insertion(+), 43 deletions(-) diff --git a/its/ruling/src/test/resources/expected/eclipse-jetty-similar-to-main/java-S9397.json b/its/ruling/src/test/resources/expected/eclipse-jetty-similar-to-main/java-S9397.json index a133ec0f8ff..4f2cee9285e 100644 --- a/its/ruling/src/test/resources/expected/eclipse-jetty-similar-to-main/java-S9397.json +++ b/its/ruling/src/test/resources/expected/eclipse-jetty-similar-to-main/java-S9397.json @@ -2,24 +2,12 @@ "org.eclipse.jetty:jetty-project:jetty-http/src/main/java/org/eclipse/jetty/http/HttpCompliance.java": [ 249 ], -"org.eclipse.jetty:jetty-project:jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java": [ -1972, -1974 -], -"org.eclipse.jetty:jetty-project:jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/AbstractPathSpec.java": [ -64 -], -"org.eclipse.jetty:jetty-project:jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/MappedResource.java": [ -99 -], "org.eclipse.jetty:jetty-project:jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java": [ 462 ], "org.eclipse.jetty:jetty-project:jetty-io/src/main/java/org/eclipse/jetty/io/ConnectionStatistics.java": [ 217, -218, -219, -220 +218 ], "org.eclipse.jetty:jetty-project:jetty-io/src/main/java/org/eclipse/jetty/io/SelectorManager.java": [ 368 @@ -34,40 +22,10 @@ "org.eclipse.jetty:jetty-project:jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java": [ 139 ], -"org.eclipse.jetty:jetty-project:jetty-jmx/src/main/java/org/eclipse/jetty/jmx/MetaData.java": [ -249, -257 -], "org.eclipse.jetty:jetty-project:jetty-server/src/main/java/org/eclipse/jetty/server/DebugListener.java": [ 308 ], -"org.eclipse.jetty:jetty-project:jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelOverHttp.java": [ -339 -], -"org.eclipse.jetty:jetty-project:jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java": [ -931 -], -"org.eclipse.jetty:jetty-project:jetty-server/src/main/java/org/eclipse/jetty/server/MultiPartParser.java": [ -667, -712, -714 -], "org.eclipse.jetty:jetty-project:jetty-server/src/main/java/org/eclipse/jetty/server/Server.java": [ 480 -], -"org.eclipse.jetty:jetty-project:jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java": [ -610 -], -"org.eclipse.jetty:jetty-project:jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandlerCollection.java": [ -374 -], -"org.eclipse.jetty:jetty-project:jetty-server/src/main/java/org/eclipse/jetty/server/session/DatabaseAdaptor.java": [ -311 -], -"org.eclipse.jetty:jetty-project:jetty-server/src/main/java/org/eclipse/jetty/server/session/DefaultSessionIdManager.java": [ -488 -], -"org.eclipse.jetty:jetty-project:jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionDataStore.java": [ -599 ] } From 00bc3a3a2d180130022b8158110ad230e33b05f1 Mon Sep 17 00:00:00 2001 From: Romain Brenguier Date: Thu, 24 Sep 2026 13:42:23 +0200 Subject: [PATCH 06/11] SONARJAVA-7054 Recognize single and double quotes in S9397 Extend hasPlaceholderInBracketsOrQuotes to treat single quotes and double quotes the same as backticks, so that format strings like '%s' are considered compliant. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../src/main/java/checks/StringFormatCheckSample.java | 10 +++++++--- .../java/org/sonar/java/checks/StringFormatCheck.java | 10 +++++----- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/java-checks-test-sources/default/src/main/java/checks/StringFormatCheckSample.java b/java-checks-test-sources/default/src/main/java/checks/StringFormatCheckSample.java index 09ed4ee4845..7568aacec77 100644 --- a/java-checks-test-sources/default/src/main/java/checks/StringFormatCheckSample.java +++ b/java-checks-test-sources/default/src/main/java/checks/StringFormatCheckSample.java @@ -169,8 +169,12 @@ String inSquareBrackets(String value) { return String.format("[%s]", value); // compliant: %s is inside brackets } - String inQuotes(String value) { - return String.format("'%s'", value); // Noncompliant {{Use string concatenation instead of String.format().}} + String inSingleQuotes(String value) { + return String.format("'%s'", value); // compliant: %s is between single quotes + } + + String inDoubleQuotes(String value) { + return String.format("\"%s\"", value); // compliant: contains escape sequences } String inBackticks(String value) { @@ -186,7 +190,7 @@ String escapedPercentInQuotes(String value) { } String apostropheInFormat(String owner, String item) { - return String.format("%s's %s", owner, item); // Noncompliant {{Use string concatenation instead of String.format().}} + return String.format("%s's %s", owner, item); // compliant: %s is between single quotes } String unmatchedClosingBracket(String value) { diff --git a/java-checks/src/main/java/org/sonar/java/checks/StringFormatCheck.java b/java-checks/src/main/java/org/sonar/java/checks/StringFormatCheck.java index 4368443359a..10fe68a4d32 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/StringFormatCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/StringFormatCheck.java @@ -88,20 +88,20 @@ private static boolean isJustPlaceholder(String rawValue) { private static boolean hasPlaceholderInBracketsOrQuotes(String value) { int depth = 0; - boolean inBackticks = false; + char inQuotes = 0; boolean afterPercent = false; for (int i = 0; i < value.length(); i++) { char c = value.charAt(i); if (afterPercent) { afterPercent = false; - if (c == 's' && (inBackticks || depth > 0)) { + if (c == 's' && (inQuotes != 0 || depth > 0)) { return true; } - } else if (c == '`') { - inBackticks = !inBackticks; + } else if (c == '`' || c == '\'' || c == '"') { + inQuotes = (inQuotes == c) ? 0 : (inQuotes == 0 ? c : inQuotes); } else if (c == '%') { afterPercent = true; - } else if (!inBackticks) { + } else if (inQuotes == 0) { depth = updateBracketDepth(c, depth); } } From 9b3dbb1c74ed830a77429cd2423543c3f602f176 Mon Sep 17 00:00:00 2001 From: Romain Brenguier Date: Thu, 24 Sep 2026 14:05:20 +0200 Subject: [PATCH 07/11] SONARJAVA-7054 Fix quote handling to require matching delimiters Refactor hasPlaceholderInBracketsOrQuotes to properly distinguish apostrophes from quoted ranges: only suppress when %s lies between matching quote delimiters. Also fixes %%s handling and removes unreachable double-quote branch to resolve SonarQube issues. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../java/checks/StringFormatCheckSample.java | 6 ++- .../sonar/java/checks/StringFormatCheck.java | 51 ++++++++++++------- 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/java-checks-test-sources/default/src/main/java/checks/StringFormatCheckSample.java b/java-checks-test-sources/default/src/main/java/checks/StringFormatCheckSample.java index 7568aacec77..a84a96e563f 100644 --- a/java-checks-test-sources/default/src/main/java/checks/StringFormatCheckSample.java +++ b/java-checks-test-sources/default/src/main/java/checks/StringFormatCheckSample.java @@ -190,7 +190,11 @@ String escapedPercentInQuotes(String value) { } String apostropheInFormat(String owner, String item) { - return String.format("%s's %s", owner, item); // compliant: %s is between single quotes + return String.format("%s's %s", owner, item); // Noncompliant {{Use string concatenation instead of String.format().}} + } + + String itsFormat(String value) { + return String.format("it's %s", value); // Noncompliant {{Use string concatenation instead of String.format().}} } String unmatchedClosingBracket(String value) { diff --git a/java-checks/src/main/java/org/sonar/java/checks/StringFormatCheck.java b/java-checks/src/main/java/org/sonar/java/checks/StringFormatCheck.java index 10fe68a4d32..51e61f9f4fd 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/StringFormatCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/StringFormatCheck.java @@ -87,34 +87,51 @@ private static boolean isJustPlaceholder(String rawValue) { } private static boolean hasPlaceholderInBracketsOrQuotes(String value) { + return hasPlaceholderInBrackets(value) || hasPlaceholderInMatchingQuotes(value); + } + + private static boolean hasPlaceholderInBrackets(String value) { int depth = 0; - char inQuotes = 0; - boolean afterPercent = false; + for (int i = 0; i < value.length() - 1; i++) { + char c = value.charAt(i); + if (c == '%') { + char next = value.charAt(i + 1); + if (next == 's' && depth > 0) { + return true; + } + i++; + } else if (c == '{' || c == '(' || c == '[') { + depth++; + } else if ((c == '}' || c == ')' || c == ']') && depth > 0) { + depth--; + } + } + return false; + } + + private static boolean hasPlaceholderInMatchingQuotes(String value) { for (int i = 0; i < value.length(); i++) { char c = value.charAt(i); - if (afterPercent) { - afterPercent = false; - if (c == 's' && (inQuotes != 0 || depth > 0)) { + if (c == '\'' || c == '`') { + int close = value.indexOf(c, i + 1); + if (close != -1 && containsPlaceholder(value, i + 1, close)) { return true; } - } else if (c == '`' || c == '\'' || c == '"') { - inQuotes = (inQuotes == c) ? 0 : (inQuotes == 0 ? c : inQuotes); - } else if (c == '%') { - afterPercent = true; - } else if (inQuotes == 0) { - depth = updateBracketDepth(c, depth); } } return false; } - private static int updateBracketDepth(char c, int depth) { - if (c == '{' || c == '(' || c == '[') { - return depth + 1; - } else if (c == '}' || c == ')' || c == ']') { - return Math.max(0, depth - 1); + private static boolean containsPlaceholder(String value, int from, int to) { + for (int i = from; i < to - 1; i++) { + if (value.charAt(i) == '%') { + if (value.charAt(i + 1) == 's') { + return true; + } + i++; + } } - return depth; + return false; } private static int countSimplePlaceholders(String value) { From 1b9324871a84eae0dffcca1d863d73a89c4d5198 Mon Sep 17 00:00:00 2001 From: Romain Brenguier Date: Thu, 24 Sep 2026 14:13:15 +0200 Subject: [PATCH 08/11] SONARJAVA-7054 Fix closing quote reused as opening quote in span pairing Co-Authored-By: Claude Opus 4.6 (1M context) --- .../src/main/java/checks/StringFormatCheckSample.java | 8 ++++++++ .../java/org/sonar/java/checks/StringFormatCheck.java | 6 +++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/java-checks-test-sources/default/src/main/java/checks/StringFormatCheckSample.java b/java-checks-test-sources/default/src/main/java/checks/StringFormatCheckSample.java index a84a96e563f..f4c7fca3c51 100644 --- a/java-checks-test-sources/default/src/main/java/checks/StringFormatCheckSample.java +++ b/java-checks-test-sources/default/src/main/java/checks/StringFormatCheckSample.java @@ -212,4 +212,12 @@ String nestedBrackets(String value) { String backtickNoPlaceholder(String value) { return String.format("`x` %s", value); // Noncompliant {{Use string concatenation instead of String.format().}} } + + String betweenQuotedSpans(String value) { + return String.format("'x' %s 'y'", value); // Noncompliant {{Use string concatenation instead of String.format().}} + } + + String betweenBacktickSpans(String value) { + return String.format("`x` %s `y`", value); // Noncompliant {{Use string concatenation instead of String.format().}} + } } diff --git a/java-checks/src/main/java/org/sonar/java/checks/StringFormatCheck.java b/java-checks/src/main/java/org/sonar/java/checks/StringFormatCheck.java index 51e61f9f4fd..3cbd2ed21f9 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/StringFormatCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/StringFormatCheck.java @@ -114,9 +114,13 @@ private static boolean hasPlaceholderInMatchingQuotes(String value) { char c = value.charAt(i); if (c == '\'' || c == '`') { int close = value.indexOf(c, i + 1); - if (close != -1 && containsPlaceholder(value, i + 1, close)) { + if (close == -1) { + continue; + } + if (containsPlaceholder(value, i + 1, close)) { return true; } + i = close; } } return false; From ee2fbd439e34fa07c930e051b18a11e6f570b86b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 24 Sep 2026 14:16:32 +0200 Subject: [PATCH 09/11] Update ruling results for PR #6227 (#6231) Co-authored-by: github-actions[bot] --- .../expected/sonar-server/java-S9397.json | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/its/ruling/src/test/resources/expected/sonar-server/java-S9397.json b/its/ruling/src/test/resources/expected/sonar-server/java-S9397.json index 5cb09055f16..8d555463dd0 100644 --- a/its/ruling/src/test/resources/expected/sonar-server/java-S9397.json +++ b/its/ruling/src/test/resources/expected/sonar-server/java-S9397.json @@ -4,32 +4,6 @@ 81, 82 ], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/es/request/ProxyClusterStatsRequestBuilder.java": [ -72 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/es/request/ProxyGetRequestBuilder.java": [ -69, -70, -71 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/es/request/ProxyIndexRequestBuilder.java": [ -69, -70, -71 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/es/request/ProxyMultiGetRequestBuilder.java": [ -73, -76 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/es/request/ProxyNodesStatsRequestBuilder.java": [ -72 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/es/request/ProxyPutMappingRequestBuilder.java": [ -76 -], -"org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/es/request/ProxySearchRequestBuilder.java": [ -79 -], "org.sonarsource.sonarqube:sonar-server:src/main/java/org/sonar/server/issue/workflow/Transition.java": [ 118 ], From 7f373e4d9ca8748c8d7ae0260fa976e7e0ac7e25 Mon Sep 17 00:00:00 2001 From: Romain Brenguier Date: Thu, 24 Sep 2026 14:38:10 +0200 Subject: [PATCH 10/11] SONARJAVA-7054 Use while-loops to fix SonarQube quality gate issues Convert for-loops with internal counter mutations to while-loops in hasPlaceholderInBrackets, hasPlaceholderInMatchingQuotes, and containsPlaceholder to resolve S127 (loop counter assigned from within loop body) violations. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../sonar/java/checks/StringFormatCheck.java | 40 ++++++++++++------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/java-checks/src/main/java/org/sonar/java/checks/StringFormatCheck.java b/java-checks/src/main/java/org/sonar/java/checks/StringFormatCheck.java index 3cbd2ed21f9..e41bcf96b57 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/StringFormatCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/StringFormatCheck.java @@ -92,46 +92,56 @@ private static boolean hasPlaceholderInBracketsOrQuotes(String value) { private static boolean hasPlaceholderInBrackets(String value) { int depth = 0; - for (int i = 0; i < value.length() - 1; i++) { + int i = 0; + while (i < value.length() - 1) { char c = value.charAt(i); if (c == '%') { - char next = value.charAt(i + 1); - if (next == 's' && depth > 0) { + if (value.charAt(i + 1) == 's' && depth > 0) { return true; } + i += 2; + } else { + if (c == '{' || c == '(' || c == '[') { + depth++; + } else if ((c == '}' || c == ')' || c == ']') && depth > 0) { + depth--; + } i++; - } else if (c == '{' || c == '(' || c == '[') { - depth++; - } else if ((c == '}' || c == ')' || c == ']') && depth > 0) { - depth--; } } return false; } private static boolean hasPlaceholderInMatchingQuotes(String value) { - for (int i = 0; i < value.length(); i++) { + int i = 0; + while (i < value.length()) { char c = value.charAt(i); if (c == '\'' || c == '`') { int close = value.indexOf(c, i + 1); - if (close == -1) { - continue; - } - if (containsPlaceholder(value, i + 1, close)) { - return true; + if (close != -1) { + if (containsPlaceholder(value, i + 1, close)) { + return true; + } + i = close + 1; + } else { + i++; } - i = close; + } else { + i++; } } return false; } private static boolean containsPlaceholder(String value, int from, int to) { - for (int i = from; i < to - 1; i++) { + int i = from; + while (i < to - 1) { if (value.charAt(i) == '%') { if (value.charAt(i + 1) == 's') { return true; } + i += 2; + } else { i++; } } From 5602e4426226d4ba9ed92a31d66ac3d92441274b Mon Sep 17 00:00:00 2001 From: Romain Brenguier Date: Thu, 24 Sep 2026 15:08:53 +0200 Subject: [PATCH 11/11] SONARJAVA-7054 Update ruling results for guava and eclipse-jetty-similar-to-main-small Co-Authored-By: Claude Opus 4.6 (1M context) --- .../eclipse-jetty-similar-to-main-small/java-S9397.json | 3 --- .../src/test/resources/expected/guava/java-S9397.json | 9 --------- 2 files changed, 12 deletions(-) diff --git a/its/ruling/src/test/resources/expected/eclipse-jetty-similar-to-main-small/java-S9397.json b/its/ruling/src/test/resources/expected/eclipse-jetty-similar-to-main-small/java-S9397.json index 30002e79bc8..2c63c085104 100644 --- a/its/ruling/src/test/resources/expected/eclipse-jetty-similar-to-main-small/java-S9397.json +++ b/its/ruling/src/test/resources/expected/eclipse-jetty-similar-to-main-small/java-S9397.json @@ -1,5 +1,2 @@ { -"org.eclipse.jetty:jetty-project:jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/AbstractPathSpec.java": [ -64 -] } diff --git a/its/ruling/src/test/resources/expected/guava/java-S9397.json b/its/ruling/src/test/resources/expected/guava/java-S9397.json index 34f81cc8ea8..2c63c085104 100644 --- a/its/ruling/src/test/resources/expected/guava/java-S9397.json +++ b/its/ruling/src/test/resources/expected/guava/java-S9397.json @@ -1,11 +1,2 @@ { -"com.google.guava:guava:src/com/google/common/eventbus/EventBus.java": [ -177 -], -"com.google.guava:guava:src/com/google/common/io/ByteStreams.java": [ -394 -], -"com.google.guava:guava:src/com/google/common/util/concurrent/UncaughtExceptionHandlers.java": [ -67 -] }