From a5789263fcfe72e0c6193cc9edcca4527f781ea0 Mon Sep 17 00:00:00 2001
From: asya-vorobeva This rule also examines the credential value itself: a value that is recognizable as something other than a real credential is not reported, even
+when the variable name or the connection string parameter matches one of the configured words. Placeholders, references to an environment variable or
+to a secrets manager, encrypted values, and obvious example or test values are therefore not flagged. Credentials should be stored in a configuration file that is not committed to the code repository, in a database, or managed by your cloud
provider’s secrets management service. If a password is exposed in the source code, it must be changed immediately. This rule is deprecated; use {rule:java:S6813} instead. Spring AssertJ contains many assertions methods specific to common types. Both versions will test the same things, but the dedicated one will provide a
-better error message, simplifying the debugging process. This rule reports an issue when an assertion can be simplified to a dedicated one. The array below gives a non-exhaustive list of assertion reported by the rule. Code behaving similarly, or with a negation will also be
-reported. A dedicated AssertJ assertion receives the value under test, so its failure message can show that value. For example, when
+ The following catalog is not exhaustive. Similar assertions and negated forms may also be reported. Choose a matcher that expresses the intended
+test, and check the AssertJ version used by the project. The object-identity mapping for A method annotated with Spring’s This is because Spring generates a proxy class with wrapper code to manage the method’s asynchronicity ( With Spring’s proxy-based configuration, calling an Replace calls to The example assumes asynchronous method execution is enabled with Put these services in separate source files. The annotations are The same call boundary matters for caching and transactions: Enable the relevant Spring feature and use a method that the configured proxy can intercept. Creating the collaborator with Avoid using self-injection as the default solution. It introduces a circular dependency and depends on configuration that may reject circular
+references. Separating the operation into a collaborating bean makes the intercepted call explicit. This rule is deprecated; use {rule:java:S9352} instead. Naming conventions play a crucial role in maintaining code clarity and readability. The uniqueness of bean names in Spring configurations is vital
to the clarity and readability of the code. When two beans share the same name within a configuration, it is not obvious to the reader which bean is
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S6862.json b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S6862.json
index a6e9129323f..01461fc1ab2 100644
--- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S6862.json
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S6862.json
@@ -1,14 +1,12 @@
{
"title": "Beans in \"@Configuration\" class should have different names",
"type": "BUG",
- "status": "ready",
+ "status": "deprecated",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
- "tags": [
- "spring"
- ],
+ "tags": [],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-6862",
"sqKey": "S6862",
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S7158.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S7158.html
index cc3db143ea2..4d8900d7075 100644
--- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S7158.html
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S7158.html
@@ -2,6 +2,14 @@ Calling A It is also cheaper. Invoking operations that create new string objects with white space removed incurs unnecessary memory allocation and management
+overhead: whenever there is white space to remove, Documentation
Resources
Standards
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S1989.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S1989.html
index 8cb24a12f92..a3139351224 100644
--- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S1989.html
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S1989.html
@@ -43,7 +43,7 @@ Compliant solution
Resources
Articles & blog posts
-
Standards
+Why is this an issue?
How to fix it
Code Examples
Noncompliant code example
String username = "steve";
-String password = "blue";
+String password = "Xk9mP2vL8qR4";
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/test?" +
"user=" + username + "&password=" + password); // Noncompliant
@@ -30,7 +33,7 @@ Resources
Articles & blog posts
Standards
Standards
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S2119.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S2119.html
index 8a5b50de79a..064ac70d5ee 100644
--- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S2119.html
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S2119.html
@@ -37,7 +37,7 @@ Documentation
Articles & blog posts
-
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S2245.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S2245.html
index fda245ce119..6feba441fb5 100644
--- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S2245.html
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S2245.html
@@ -38,7 +38,7 @@ Documentation
Standards
Documentation
Standards
Documentation
Standards
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S2612.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S2612.html
index cebcd0f6617..e7872eeb606 100644
--- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S2612.html
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S2612.html
@@ -74,8 +74,8 @@ Standards
Development: V-222430 - The application must execute without excessive account permissionsDocumentation
Standards
Noncompliant code example
Resources
-
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S3329.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S3329.html
index 3e4e334beb6..f7c1bc69ffe 100644
--- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S3329.html
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S3329.html
@@ -112,9 +112,9 @@ Resources
Standards
Documentation
Standards
Compliant solution
Resources
-
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S3752.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S3752.html
index 707a8fa3e86..f5f8c24a2be 100644
--- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S3752.html
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S3752.html
@@ -42,7 +42,7 @@ Standards
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4036.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4036.html
index 45265ad706b..35156d77755 100644
--- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4036.html
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4036.html
@@ -38,7 +38,7 @@ Standards
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4288.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4288.html
index d00bb15943d..a118100da81 100644
--- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4288.html
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4288.html
@@ -1,3 +1,4 @@
+Why is this an issue?
@Controller, @Service, and @Repository classes are singletons by default, meaning only one instance
of the class is ever instantiated in the application. Typically such a class might have a few static members, such as a logger, but all
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4288.json b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4288.json
index c70a6818f60..ee419381154 100644
--- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4288.json
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4288.json
@@ -7,16 +7,13 @@
},
"attribute": "CONVENTIONAL"
},
- "status": "ready",
+ "status": "deprecated",
"remediation": {
"func": "Linear",
"linearDesc": "2min by injected field",
"linearFactor": "2min"
},
- "tags": [
- "spring",
- "design"
- ],
+ "tags": [],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-4288",
"sqKey": "S4288",
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4347.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4347.html
index 0cfd315392c..ca4954a4539 100644
--- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4347.html
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4347.html
@@ -64,7 +64,7 @@ Documentation
Standards
Standards
Articles & blog posts
Standards
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4502.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4502.html
index 26de1555c7e..9efeb3181f4 100644
--- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4502.html
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4502.html
@@ -54,7 +54,7 @@ Standards
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4434.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4434.html
index 96795934208..80928b912c3 100644
--- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4434.html
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4434.html
@@ -41,7 +41,7 @@ Standards
Standards
Resources
Standards
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4544.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4544.html
index a07e7557db4..12b675dd8ae 100644
--- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4544.html
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4544.html
@@ -46,7 +46,7 @@ Standards
Standards
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4601.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4601.html
index 95ae62319aa..2ace98cf0df 100644
--- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4601.html
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4601.html
@@ -61,7 +61,7 @@ Documentation
Standards
Standards
Documentation
Standards
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4830.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4830.html
index a8ecd3304d5..65164ffec1e 100644
--- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4830.html
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S4830.html
@@ -61,9 +61,9 @@ Standards
Standards
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S5301.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S5301.html
index 9d9045a5889..4db160aa69a 100644
--- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S5301.html
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S5301.html
@@ -60,7 +60,7 @@ Standards
Standards
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S5332.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S5332.html
index 6bcb93b6666..beb8f5308ab 100644
--- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S5332.html
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S5332.html
@@ -105,7 +105,7 @@ Articles & blog posts
Standards
-
Standards
Articles & blog posts
Standards
Standards
Articles & blog posts
Standards
Resources
Standards
Resources
Standards
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S5679.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S5679.html
index b81931a70dc..4360d34355b 100644
--- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S5679.html
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S5679.html
@@ -88,7 +88,7 @@ Standards
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S5689.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S5689.html
index dc188af34fb..8c6a06c75df 100644
--- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S5689.html
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S5689.html
@@ -40,7 +40,7 @@ Documentation
Standards
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S5693.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S5693.html
index 89609b7738c..16215b2378c 100644
--- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S5693.html
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S5693.html
@@ -51,7 +51,7 @@ Articles & blog posts
Standards
Documentation
Standards
Resources
Standards
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S5838.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S5838.html
index 85152013790..ac6c4331021 100644
--- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S5838.html
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S5838.html
@@ -1,9 +1,22 @@
Why is this an issue?
-assertThat(names).contains("Ada") fails, it shows the collection and the missing element.
+assertThat(names.contains("Ada")).isTrue() only reports that false was not true.Noncompliant code example
+
+assertThat(names.contains("Ada")).isTrue(); // Noncompliant
+
+Compliant solution
+
+assertThat(names).contains("Ada");
+
+Dedicated assertions by type
+x == y applies to reference values. Check null and edge-case behavior when replacing a derived
+expression: for example, map.get(key) can return null for a missing key, whereas containsEntry(key, null)
+requires the key to exist. For floating-point values, check NaN and signed-zero behavior rather than assuming a relational-expression rewrite is
+equivalent.
-Why is this an issue?
assertThat(getString()).matches(expected)
-
-
- assertThat(getString().trim()).isEmpty()
-
- assertThat(getString()).isBlank()
assertThat(getString().length()).isEqualTo(length)Why is this an issue?
-
assertThat(getString().length()).hasSize(expected.length())assertThat(getString().length()).isEqualTo(expected.length())
assertThat(getString()).hasSameSizeAs(expected)Why is this an issue?
-
assertThat(getPath().getParent()).isEqualTo(name)assertThat(getPath().getParent()).isEqualTo(path)
-
assertThat(getPath()).hasParentRaw(name)assertThat(getPath()).hasParentRaw(path)
@@ -514,7 +519,7 @@
Why is this an issue?
-
assertThat(getMap().size()).isEqualTo(otherMap().size()assertThat(getMap().size()).isEqualTo(otherMap().size())
assertThat(getMap()).hasSameSizeAs(otherMap())Why is this an issue?
-
assertThat(getMap().keySet()).containsOnlyKey(key)assertThat(getMap().keySet()).containsOnly(key)
-
assertThat(getMap()).containsOnlyKey(key)assertThat(getMap()).containsOnlyKeys(key)
@@ -600,22 +605,4 @@
Why is this an issue?
Noncompliant code example
-
-assertThat(getObject()).isEqualTo(null); // Noncompliant
-assertThat(getObject()).isNotEqualTo(null); // Noncompliant - not listed above but also supported
-
-assertThat(getString().trim()).isEmpty();
-assertThat(getFile().canRead()).isTrue();
-assertThat(getPath().getParent()).isNull();
-
-Compliant solution
-
-assertThat(getObject()).isNull();
-assertThat(getObject()).isNotNull();
-
-assertThat(getString()).isBlank();
-assertThat(getFile()).canRead();
-assertThat(getPath()).hasNoParentRaw();
-
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S5876.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S5876.html
index bc4ecf48b1f..3319bec3777 100644
--- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S5876.html
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S5876.html
@@ -78,7 +78,7 @@ Standards
Documentation
Standards
-
Resources
Standards
Documentation
Standards
Resources
Standards
Standards
Why is this an issue?
-@Async, @Cacheable or @Transactional annotations will not work as expected
-if invoked directly from within its class.@Async), to cache methods
-invocations (@Cacheable), or to handle the transaction (@Transactional). However, when called using this, the
-proxy instance is bypassed, and the method is invoked directly without the required wrapper code.@Async, @Cacheable, or @Transactional method from its
+own class bypasses the proxy that applies the annotation’s behavior. For example, an @Async method runs on the calling thread instead of
+being scheduled asynchronously. Move the annotated operation to a separate Spring-managed bean and call it through an injected reference.How to fix it
-@Async, @Cacheable or @Transactional methods via this with calls on an
-instance that was injected by Spring (@Autowired, @Resource or @Inject). The injected instance is a proxy on
-which the methods can be invoked safely.@EnableAsync and both services are discovered by component scanning.
+Constructor injection gives the calling service a reference to the managed bean, allowing Spring’s proxy to intercept the call.Code examples
Noncompliant code example
@Service
-public class AsyncNotificationProcessor implements NotificationProcessor {
-
- @Override
- public void process(Notification notification) {
- processAsync(notification); // Noncompliant, call bypasses proxy
- retrieveNotification(notification.id); // Noncompliant, call bypasses proxy and will not be cached
+public class NotificationProcessor {
+ public void process(String message) {
+ sendAsync(message); // Noncompliant: call bypasses the proxy
}
@Async
- public processAsync(Notification notification) {
- // ...
- }
-
- @Cacheable
- public Notification retrieveNotification(Long id) {
- // ...
+ public void sendAsync(String message) {
+ System.out.println(message);
}
-
}
Compliant solution
+
@Service
-public class AsyncNotificationProcessor implements NotificationProcessor {
-
- @Resource
- private AsyncNotificationProcessor asyncNotificationProcessor;
+public class NotificationProcessor {
+ private final NotificationSender sender;
- @Override
- public void process(Notification notification) {
- asyncNotificationProcessor.processAsync(notification); // Compliant, call via injected proxy
- asyncNotificationProcessor.retrieveNotification(notification.id); // Compliant, the call will be cached
+ public NotificationProcessor(NotificationSender sender) {
+ this.sender = sender;
}
- @Async
- public processAsync(Notification notification) {
- // ...
+ public void process(String message) {
+ sender.sendAsync(message);
}
+}
- @Cacheable
- public Notification retrieveNotification(Long id) {
- // ...
+@Service
+public class NotificationSender {
+ @Async
+ public void sendAsync(String message) {
+ System.out.println(message);
}
}
+org.springframework.stereotype.Service, org.springframework.scheduling.annotation.Async, and
+org.springframework.scheduling.annotation.EnableAsync.Other annotations and configuration
+
+
+@Cacheable: self-invocation bypasses the cache lookup and population that would wrap the call.@Transactional: self-invocation does not apply the called method’s transaction settings. The method may still run in a transaction
+ already started by its caller.new
+bypasses Spring management and does not fix the problem. This explanation concerns proxy mode; AspectJ weaving has different self-invocation
+behavior.Resources
Documentation
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S6862.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S6862.html
index a101d30ac22..ecf0825b538 100644
--- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S6862.html
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S6862.html
@@ -1,3 +1,4 @@
+
Why is this an issue?
Why is this an issue?
String.isEmpty() clearly communicates the code’s intention, which is to test if the string is empty. Using
String.length() == 0 is less direct and makes the code less readable. This preference for isEmpty() extends to all
CharSequence objects, including StringBuilder and StringBuffer.String.strip().length() == 0 or String.trim().length() == 0 test is a blankness check, and
+String.isBlank() states that intent directly.strip() and trim() copy the remaining characters into a new string, which
+is then discarded as soon as its length has been read. isBlank() inspects the characters in place and allocates nothing.Exceptions
+String.isBlank() is only available from Java 11 on. This rule does not raise an issue on strip().length() or
+trim().length() comparisons when the project targets an earlier version.How to fix it
Code examples
Noncompliant code example
@@ -15,6 +23,11 @@ Noncompliant code example
...
if (sb.length() == 0) { /* … */ } // Noncompliant
+
+if ("string".strip().length() == 0) { /* … */ } // Noncompliant
+
+if ("string".trim().length() > 0) { /* … */ } // Noncompliant
+
Compliant solution
if ("string".isEmpty()){ /* … */ }
@@ -26,10 +39,27 @@
+Compliant solution
...
if (sb.isEmpty()) { /* … */ }
+if ("string".isBlank()) { /* … */ }
+
+if (!"string".isBlank()) { /* … */ }
+
+Pitfalls
+strip() removes exactly the characters isBlank() treats as white space, both relying on
+Character.isWhitespace, so strip().length() == 0 and isBlank() always agree.trim() does not. It removes every character up to and including U+0020, which covers control characters such as
+U+0000 that Character.isWhitespace rejects, and it leaves Unicode white space above U+0020, such as the em
+space U+2003, in place. Replacing trim().length() == 0 with isBlank() therefore makes strings of Unicode white
+space count as blank and strings of control characters count as non-blank. The issue message and the quick fix say so explicitly; apply the fix only
+when that is the intended behavior, and keep trim().isEmpty() otherwise.Resources
Documentation
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S7158.json b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S7158.json
index 9640bf2cf6d..479d2e0e053 100644
--- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S7158.json
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S7158.json
@@ -1,5 +1,5 @@
{
- "title": "\"String.isEmpty()\" should be used to test for emptiness",
+ "title": "\"String.isEmpty()\" and \"String.isBlank()\" should be used to test for emptiness and blankness",
"type": "CODE_SMELL",
"status": "ready",
"remediation": {
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9352.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9352.html
index 7e0cdfaf864..1ecbe3dd442 100644
--- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9352.html
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9352.html
@@ -2,18 +2,21 @@
type exist in the application’s dependency injection container without explicit disambiguation.
Spring resolves autowiring by type: when exactly one bean matches the required type, it gets injected without further input. If two or more beans
-of the same type exist in the application context, Spring cannot decide which one to use and expects the ambiguity to be resolved explicitly, either
-with @Qualifier or by marking one bean @Primary.
Without this disambiguation, Spring throws a NoUniqueBeanDefinitionException while building the application context.
@Qualifier, by marking one bean @Primary, or by ensuring only one candidate is registered.
The application will fail to start with an error indicating ambiguous dependency resolution during the dependency injection framework’s -initialization phase. This prevents the application from running and requires immediate attention to resolve the configuration error.
+Spring will throw a NoUniqueBeanDefinitionException exception at runtime, indicating ambiguous dependency resolution during the
+dependency injection framework’s initialization phase. This prevents the application from running and requires immediate attention to resolve the
+configuration error.
The ambiguity can be resolved in several ways, for example with @Qualifier, @Primary, or by restricting which beans are
+registered with @Conditional and its variants. The two most common options are shown below.
Option 1: select the bean with @Qualifier
Use the @Qualifier annotation at the injection point to explicitly specify which bean to inject. The qualifier value must match the
bean name (typically the method name for @Bean methods).
This approach is useful when different parts of your application need different beans of the same type. Each injection point can specify exactly which bean it requires.
-@Configuration @@ -57,6 +60,7 @@+Compliant solution
private DataSource dataSource; // Compliant; @Qualifier explicitly specifies that primaryDataSource needs to be used }
Option 2: mark a default bean with @Primary
Use the @Primary annotation on one of the bean definitions to mark it as the default choice. When Spring encounters multiple beans of
the same type, it will automatically select the primary bean if no explicit qualifier is specified.
This approach is useful when you have a clear default bean that should be used in most cases, with alternative beans available for specific @@ -119,5 +123,10 @@
Many APIs provide built-in formatting capabilities. When you wrap generic string formatting functions around these APIs, you’re adding an unnecessary layer that hurts both performance and readability.
-Generic string formatting functions are typically eagerly evaluated. This means the formatted string is created immediately, even if it’s never used. This is particularly problematic in logging scenarios:
@@ -21,7 +21,7 @@Performance concerns
LOG.debug("Processing {} items", items.size()); // Only formats if DEBUG is enabled-Readability and idioms
+Readability and idioms
Each API has its own idiomatic way of formatting. Using generic string formatting makes the code less recognizable to developers familiar with these frameworks:
In Java, this specifically refers to redundant String.format calls.
SLF4J and similar logging frameworks (Log4j2, Logback) support parameterized logging with {} placeholders. This approach defers string
-construction until the log level is enabled.
Arguments implementing java.util.Formattable are an exception: String.format formats them with
-formatTo instead of toString, so replacing it with {} placeholders could change the message.
Pass the format string and its arguments directly to the API instead of pre-formatting them with String.format. How to do this depends
+on the API:
{} placeholders. This
+ approach defers string construction until the log level is enabled. Arguments implementing java.util.Formattable are an exception:
+ String.format formats them with formatTo instead of toString, so replacing it with {}
+ placeholders could change the message.System.out/System.err): use printf instead of println with
+ String.format. The printf method supports the same format specifiers as String.format.PrintStream and PrintWriter both provide printf and format methods. Use
+ these instead of wrapping String.format in print or println.Note that println appends a line separator, so use %n at the end of the format string when switching to
+printf.
In the examples below, activateUser illustrates the SLF4J case, displayResult the console output case, and
+writeHeader the PrintWriter case.
+import java.io.PrintWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-class UserService {
- private static final Logger LOG = LoggerFactory.getLogger(UserService.class);
+class Example {
+ private static final Logger LOG = LoggerFactory.getLogger(Example.class);
void activateUser(String username, String role) {
LOG.info(String.format("Activated user '%s' with role '%s'", username, role)); // Noncompliant
}
+
+ void displayResult(int a, int b, int sum) {
+ System.out.println(String.format("Result: %d + %d = %d", a, b, sum)); // Noncompliant
+ }
+
+ void writeHeader(PrintWriter writer, String title, int pageNum) {
+ writer.println(String.format("Page %d: %s", pageNum, title)); // Noncompliant
+ }
}
+import java.io.PrintWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-class UserService {
- private static final Logger LOG = LoggerFactory.getLogger(UserService.class);
+class Example {
+ private static final Logger LOG = LoggerFactory.getLogger(Example.class);
void activateUser(String username, String role) {
LOG.info("Activated user '{}' with role '{}'", username, role);
}
-}
-
-For console output, use printf instead of println with String.format. The printf method
-supports the same format specifiers as String.format.
-class Calculator {
- void displayResult(int a, int b, int sum) {
- System.out.println(String.format("Result: %d + %d = %d", a, b, sum)); // Noncompliant
- }
-}
-
-
-class Calculator {
+
void displayResult(int a, int b, int sum) {
System.out.printf("Result: %d + %d = %d%n", a, b, sum);
}
-}
-
-Both PrintStream and PrintWriter provide printf and format methods. Use these instead of
-wrapping String.format in print or println.
-import java.io.PrintWriter;
-
-class ReportGenerator {
- void writeHeader(PrintWriter writer, String title, int pageNum) {
- writer.println(String.format("Page %d: %s", pageNum, title)); // Noncompliant
- }
-}
-
-
-import java.io.PrintWriter;
-class ReportGenerator {
void writeHeader(PrintWriter writer, String title, int pageNum) {
writer.printf("Page %d: %s%n", pageNum, title);
}
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9428.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9428.html
index 7856aaa0221..07471a8874d 100644
--- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9428.html
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9428.html
@@ -1,6 +1,5 @@
-This rule raises an issue when a cryptographic digest/hash object is declared as a class-level variable (either instance-level or shared across all
-instances).
-In Java, this specifically refers to MessageDigest instances.
+This rule raises an issue when a MessageDigest object is declared as a class-level variable (either instance-level or shared across
+all instances).
Why is this an issue?
Cryptographic hash computation objects are stateful components that calculate hash values by maintaining internal state as data is incrementally
processed through their methods.
@@ -81,3 +80,4 @@ Standards
An issue is raised when a literal value (numeric, boolean, or other primitive) is converted to a string by concatenating it with an empty string -rather than using explicit conversion methods.
+An issue is raised when a primitive literal is converted to a string by concatenating it with an empty string rather than writing the resulting +string literal directly or using an explicit conversion method.
+This includes compile-time constant contexts when the concatenation can be replaced with an equivalent string literal. Annotation arguments,
+annotation element defaults, constant case labels, and constant field initializers are not exempt.
When the targeted empty-string/primitive-literal pattern is part of a larger compile-time constant concatenation, simplify and report the enclosing
+constant concatenation. For example, report the whole initializer "a" + ("" + 1) and recommend "a1". This rule does not
+target arbitrary constant concatenations such as "a" + "b" unless they contain the targeted pattern.
Converting literals to strings by concatenating them with empty strings is an inefficient and unclear approach.
-When you write an expression that concatenates an empty string with a non-string value, the language runtime must:
-This creates unnecessary intermediate objects and performs extra operations. More importantly, the intent is unclear - a developer reading an empty -string concatenated with a value must infer that this is a type conversion, rather than seeing an explicit conversion call.
-Most languages provide explicit, efficient methods for converting primitives to strings:
+Converting literals to strings by concatenating them with empty strings obscures the intended string value.
+When concatenation is evaluated at runtime, it may involve intermediate objects or additional operations to build the resulting string. The exact +behavior depends on the language, compiler, and runtime. These costs do not apply when the concatenation is evaluated at compile time.
+The readability concern remains in either case: a developer reading an empty string concatenated with a literal must infer the resulting string, +rather than seeing it directly.
+Writing the resulting string literal directly makes the value clear. When an explicit conversion is appropriate, most languages also provide +methods for converting primitives to strings:
These methods directly convert the value to a string without unnecessary intermediate steps, making both the intent and the operation clearer.
-In Java, these explicit methods include Integer.toString(), Boolean.toString(), and other type-specific methods, or the
-generic String.valueOf() method.
These methods make the conversion intent explicit, but do not necessarily preserve compile-time constant semantics.
+In Java, concatenations that are constant expressions are evaluated at compile time. For example, "" + 42 and "a" + ("" +
+1) do not incur runtime string-building overhead. The reason to simplify these expressions is readability, not runtime performance.
The performance impact of using string operations as an indirect method for type conversion is typically small in individual cases, but can -accumulate in large applications or performance-sensitive code paths. More significantly, this pattern reduces code readability and maintainability by -obscuring the developer’s intent.
+This pattern reduces code readability and maintainability by obscuring the intended string value. Runtime concatenation may also have a performance +cost, but concatenations evaluated at compile time do not incur runtime string-building overhead.
Replace empty string concatenation with explicit type conversion methods. For primitive types, use the wrapper class’s static
-toString() method (e.g., Integer.toString(), Long.toString(), Boolean.toString()). Alternatively,
-use String.valueOf() which works for all types.
For compile-time constant concatenations, replace the enclosing constant concatenation with a string literal containing its evaluated value. +Preserve the exact string value, including escaping where necessary.
+Use this replacement in annotation arguments and annotation element defaults (including array elements), constant case labels in
+switch statements or expressions, and constant field initializers. This also applies to implicitly static final fields in
+interfaces and annotation types.
Method calls such as Integer.toString(1) and String.valueOf(1) are not constant expressions. They are unsuitable
+replacements in annotation values, annotation element defaults, and constant case labels because the code would no longer compile. In a
+constant field initializer, they would turn the field into a non-constant, potentially breaking uses in constant contexts and changing constant
+inlining behavior. An equivalent string literal preserves these semantics.
Outside contexts where compile-time constant semantics must be preserved, an explicit conversion is also an option. For primitive types, use the
+wrapper class’s static toString() method (e.g., Integer.toString(), Long.toString(),
+Boolean.toString()) or String.valueOf().
+@interface Label {
+ String value() default "" + 1; // Noncompliant
+ String[] aliases() default {"" + 2}; // Noncompliant
+}
+
+@Label(value = "" + 3, aliases = {"" + 4}) // Noncompliant
+class Example {
+ static final String SIMPLE = "" + 42; // Noncompliant
+ static final String NESTED = "a" + ("" + 1); // Noncompliant: replace the whole initializer with "a1"
+
+ boolean matches(String value) {
+ switch (value) {
+ case "" + 5: // Noncompliant
+ return true;
+ default:
+ return false;
+ }
+ }
+}
+
+
+@interface Label {
+ String value() default "1";
+ String[] aliases() default {"2"};
+}
+
+@Label(value = "3", aliases = {"4"})
+class Example {
+ static final String SIMPLE = "42";
+ static final String NESTED = "a1";
+
+ boolean matches(String value) {
+ switch (value) {
+ case "5":
+ return true;
+ default:
+ return false;
+ }
+ }
+}
+
+When constant semantics do not need to be preserved, explicit conversion methods are another solution:
String s = "" + 123; // Noncompliant String b = "" + true; // Noncompliant @@ -43,6 +97,9 @@Compliant solution
Resources
Documentation