From 57a90020baa059ffa5ced02f54f8968cea9f7044 Mon Sep 17 00:00:00 2001 From: Romain Brenguier Date: Mon, 12 Oct 2026 13:41:43 +0200 Subject: [PATCH 1/6] SONARJAVA-7152 S1695: Update issue message to suggest Objects.requireNonNull() Co-Authored-By: Claude Opus 5.5 (1M context) --- .../default/src/main/java/checks/NPEThrowCheckSample.java | 6 +++--- .../src/main/java/org/sonar/java/checks/NPEThrowCheck.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/java-checks-test-sources/default/src/main/java/checks/NPEThrowCheckSample.java b/java-checks-test-sources/default/src/main/java/checks/NPEThrowCheckSample.java index 44e52d6da24..a11f58c3138 100644 --- a/java-checks-test-sources/default/src/main/java/checks/NPEThrowCheckSample.java +++ b/java-checks-test-sources/default/src/main/java/checks/NPEThrowCheckSample.java @@ -1,16 +1,16 @@ package checks; class NPEThrowCheckSample { - void foo() throws NullPointerException { // Noncompliant {{Throw some other exception here, such as "IllegalArgumentException".}} + void foo() throws NullPointerException { // Noncompliant {{Use Objects.requireNonNull() instead of explicitly throwing NullPointerException.}} // ^^^^^^^^^^^^^^^^^^^^ } void bar() { throw new - NullPointerException // Noncompliant {{Throw some other exception here, such as "IllegalArgumentException".}} + NullPointerException // Noncompliant {{Use Objects.requireNonNull() instead of explicitly throwing NullPointerException.}} (); } void baz() { - throw new java.lang.NullPointerException(); // Noncompliant {{Throw some other exception here, such as "IllegalArgumentException".}} + throw new java.lang.NullPointerException(); // Noncompliant {{Use Objects.requireNonNull() instead of explicitly throwing NullPointerException.}} // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ } void qix() throws IllegalArgumentException { diff --git a/java-checks/src/main/java/org/sonar/java/checks/NPEThrowCheck.java b/java-checks/src/main/java/org/sonar/java/checks/NPEThrowCheck.java index 07cc5da291e..dea76493a6d 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/NPEThrowCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/NPEThrowCheck.java @@ -50,7 +50,7 @@ public void visitNode(Tree tree) { private void raiseIssueOnNpe(Tree tree, Type type) { if (type.is("java.lang.NullPointerException")) { - reportIssue(treeAtFault(tree), "Throw some other exception here, such as \"IllegalArgumentException\"."); + reportIssue(treeAtFault(tree), "Use Objects.requireNonNull() instead of explicitly throwing NullPointerException."); } } From b756e856c3eb31ad0da6d854c3d232d9c587d2e9 Mon Sep 17 00:00:00 2001 From: Romain Brenguier Date: Tue, 13 Oct 2026 09:26:39 +0200 Subject: [PATCH 2/6] SONARJAVA-7152 S1695: Use dedicated message for throws clauses and align rule description Co-Authored-By: Claude Opus 5.5 (1M context) --- .../main/java/checks/NPEThrowCheckSample.java | 2 +- .../org/sonar/java/checks/NPEThrowCheck.java | 8 ++++---- .../org/sonar/l10n/java/rules/java/S1695.html | 17 ++++++++++------- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/java-checks-test-sources/default/src/main/java/checks/NPEThrowCheckSample.java b/java-checks-test-sources/default/src/main/java/checks/NPEThrowCheckSample.java index a11f58c3138..b935f98c637 100644 --- a/java-checks-test-sources/default/src/main/java/checks/NPEThrowCheckSample.java +++ b/java-checks-test-sources/default/src/main/java/checks/NPEThrowCheckSample.java @@ -1,7 +1,7 @@ package checks; class NPEThrowCheckSample { - void foo() throws NullPointerException { // Noncompliant {{Use Objects.requireNonNull() instead of explicitly throwing NullPointerException.}} + void foo() throws NullPointerException { // Noncompliant {{Remove NullPointerException from the throws clause.}} // ^^^^^^^^^^^^^^^^^^^^ } void bar() { diff --git a/java-checks/src/main/java/org/sonar/java/checks/NPEThrowCheck.java b/java-checks/src/main/java/org/sonar/java/checks/NPEThrowCheck.java index dea76493a6d..46892888181 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/NPEThrowCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/NPEThrowCheck.java @@ -40,17 +40,17 @@ public List nodesToVisit() { public void visitNode(Tree tree) { if (tree.is(Tree.Kind.THROW_STATEMENT)) { ExpressionTree expressionTree = ((ThrowStatementTree) tree).expression(); - raiseIssueOnNpe(expressionTree, expressionTree.symbolType()); + raiseIssueOnNpe(expressionTree, expressionTree.symbolType(), "Use Objects.requireNonNull() instead of explicitly throwing NullPointerException."); } else { for (TypeTree throwClause : ((MethodTree) tree).throwsClauses()) { - raiseIssueOnNpe(throwClause, throwClause.symbolType()); + raiseIssueOnNpe(throwClause, throwClause.symbolType(), "Remove NullPointerException from the throws clause."); } } } - private void raiseIssueOnNpe(Tree tree, Type type) { + private void raiseIssueOnNpe(Tree tree, Type type, String message) { if (type.is("java.lang.NullPointerException")) { - reportIssue(treeAtFault(tree), "Use Objects.requireNonNull() instead of explicitly throwing NullPointerException."); + reportIssue(treeAtFault(tree), message); } } diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S1695.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S1695.html index 2d6d90b6b8d..afd385fc30b 100644 --- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S1695.html +++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S1695.html @@ -1,20 +1,23 @@

Why is this an issue?

A NullPointerException should indicate that a null value was unexpectedly encountered. Good programming practice dictates that code is structured to avoid NPE’s.

-

Explicitly throwing NullPointerException forces a method’s callers to explicitly catch it, rather than coding to avoid it. Further, it -makes it difficult to distinguish between the unexpectedly-encountered null value and the condition which causes the method to purposely -throw an NPE.

-

If an NPE is being thrown to indicate that a parameter to the method should not have been null, use the @NotNull annotation -instead.

+

Explicitly throwing NullPointerException or declaring it in a throws clause suggests that callers should catch it, +rather than coding to avoid it. Since NullPointerException is an unchecked exception, declaring it in a throws clause is +unnecessary.

+

If an NPE is being thrown to indicate that a parameter to the method should not have been null, use Objects.requireNonNull() instead. +It makes the intent explicit and produces a consistent, descriptive error. Additionally, the parameter can be annotated with @NotNull to +document the contract and allow tools to detect violations.

Noncompliant code example

 public void doSomething (String aString) throws NullPointerException {
-     throw new NullPointerException();
+  if (aString == null) {
+    throw new NullPointerException();
+  }
 }
 

Compliant solution

 public void doSomething (@NotNull String aString) {
+  Objects.requireNonNull(aString, "aString must not be null");
 }
 
- From 08d8b559aafc0815cb27d4fd1c6d0e5cd75f6b09 Mon Sep 17 00:00:00 2001 From: Romain Brenguier Date: Tue, 13 Oct 2026 09:29:53 +0200 Subject: [PATCH 3/6] SONARJAVA-7152 S1695: Revert rule description changes (handled in RSPEC PR #8333) Co-Authored-By: Claude Opus 5.5 (1M context) --- .../org/sonar/l10n/java/rules/java/S1695.html | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S1695.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S1695.html index afd385fc30b..2d6d90b6b8d 100644 --- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S1695.html +++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S1695.html @@ -1,23 +1,20 @@

Why is this an issue?

A NullPointerException should indicate that a null value was unexpectedly encountered. Good programming practice dictates that code is structured to avoid NPE’s.

-

Explicitly throwing NullPointerException or declaring it in a throws clause suggests that callers should catch it, -rather than coding to avoid it. Since NullPointerException is an unchecked exception, declaring it in a throws clause is -unnecessary.

-

If an NPE is being thrown to indicate that a parameter to the method should not have been null, use Objects.requireNonNull() instead. -It makes the intent explicit and produces a consistent, descriptive error. Additionally, the parameter can be annotated with @NotNull to -document the contract and allow tools to detect violations.

+

Explicitly throwing NullPointerException forces a method’s callers to explicitly catch it, rather than coding to avoid it. Further, it +makes it difficult to distinguish between the unexpectedly-encountered null value and the condition which causes the method to purposely +throw an NPE.

+

If an NPE is being thrown to indicate that a parameter to the method should not have been null, use the @NotNull annotation +instead.

Noncompliant code example

 public void doSomething (String aString) throws NullPointerException {
-  if (aString == null) {
-    throw new NullPointerException();
-  }
+     throw new NullPointerException();
 }
 

Compliant solution

 public void doSomething (@NotNull String aString) {
-  Objects.requireNonNull(aString, "aString must not be null");
 }
 
+ From 2a1ce36c059a8bd3174c2fc1161a89ed57e76b45 Mon Sep 17 00:00:00 2001 From: Romain Brenguier Date: Tue, 13 Oct 2026 17:04:00 +0200 Subject: [PATCH 4/6] SONARJAVA-7152 S1695: Do not raise when thrown NullPointerException has a message argument Co-Authored-By: Claude Opus 5.5 (1M context) --- .../default/src/main/java/checks/NPEThrowCheckSample.java | 6 ++++++ .../src/main/java/org/sonar/java/checks/NPEThrowCheck.java | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/java-checks-test-sources/default/src/main/java/checks/NPEThrowCheckSample.java b/java-checks-test-sources/default/src/main/java/checks/NPEThrowCheckSample.java index b935f98c637..4aaf03c8d4a 100644 --- a/java-checks-test-sources/default/src/main/java/checks/NPEThrowCheckSample.java +++ b/java-checks-test-sources/default/src/main/java/checks/NPEThrowCheckSample.java @@ -13,6 +13,12 @@ void baz() { throw new java.lang.NullPointerException(); // Noncompliant {{Use Objects.requireNonNull() instead of explicitly throwing NullPointerException.}} // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ } + void withMessage(Object o) { + throw new NullPointerException("o must not be null"); // Compliant, a message is provided + } + void withComputedMessage(String name) { + throw new java.lang.NullPointerException(name + " must not be null"); // Compliant + } void qix() throws IllegalArgumentException { throw new IllegalArgumentException(); } diff --git a/java-checks/src/main/java/org/sonar/java/checks/NPEThrowCheck.java b/java-checks/src/main/java/org/sonar/java/checks/NPEThrowCheck.java index 46892888181..8896c28c5ac 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/NPEThrowCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/NPEThrowCheck.java @@ -40,6 +40,9 @@ public List nodesToVisit() { public void visitNode(Tree tree) { if (tree.is(Tree.Kind.THROW_STATEMENT)) { ExpressionTree expressionTree = ((ThrowStatementTree) tree).expression(); + if (hasArguments(expressionTree)) { + return; + } raiseIssueOnNpe(expressionTree, expressionTree.symbolType(), "Use Objects.requireNonNull() instead of explicitly throwing NullPointerException."); } else { for (TypeTree throwClause : ((MethodTree) tree).throwsClauses()) { @@ -54,6 +57,10 @@ private void raiseIssueOnNpe(Tree tree, Type type, String message) { } } + private static boolean hasArguments(ExpressionTree expressionTree) { + return expressionTree.is(Tree.Kind.NEW_CLASS) && !((NewClassTree) expressionTree).arguments().isEmpty(); + } + private static Tree treeAtFault(Tree tree) { return tree.is(Tree.Kind.NEW_CLASS) ? ((NewClassTree) tree).identifier() : tree; } From 5da5fd6d506f3102770b427151a1bc3944ed419c Mon Sep 17 00:00:00 2001 From: Romain Brenguier Date: Fri, 16 Oct 2026 10:03:54 +0200 Subject: [PATCH 5/6] SONARJAVA-7152 S1695: Update rule metadata and add to Sonar way Co-Authored-By: Claude Opus 5.5 (1M context) --- .../org/sonar/l10n/java/rules/java/S1695.html | 78 +++++++++++++++---- .../org/sonar/l10n/java/rules/java/S1695.json | 11 ++- .../main/resources/profiles/Sonar_way/S1695 | 0 3 files changed, 73 insertions(+), 16 deletions(-) create mode 100644 sonar-java-plugin/src/main/resources/profiles/Sonar_way/S1695 diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S1695.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S1695.html index 2d6d90b6b8d..7eb0d1fbec5 100644 --- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S1695.html +++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S1695.html @@ -1,20 +1,70 @@ +

An issue is raised when code explicitly throws a NullPointerException using a throw statement.

Why is this an issue?

-

A NullPointerException should indicate that a null value was unexpectedly encountered. Good programming practice dictates -that code is structured to avoid NPE’s.

-

Explicitly throwing NullPointerException forces a method’s callers to explicitly catch it, rather than coding to avoid it. Further, it -makes it difficult to distinguish between the unexpectedly-encountered null value and the condition which causes the method to purposely -throw an NPE.

-

If an NPE is being thrown to indicate that a parameter to the method should not have been null, use the @NotNull annotation -instead.

-

Noncompliant code example

-
-public void doSomething (String aString) throws NullPointerException {
-     throw new NullPointerException();
+

Explicitly throwing a NullPointerException is almost always done to validate that a value is not null, typically with an +if statement followed by throw new NullPointerException(…​). This is a verbose, hand-rolled way to perform a +null check that the Java standard library already provides. The rule therefore raises an issue on every explicit throw of a +NullPointerException, whether or not it is wrapped in a null check.

+

According to Effective Java, 3rd Edition, Item 72, while IllegalArgumentException might seem appropriate for null parameter +validation, Java conventions actually dictate that NullPointerException should be used for null values. Rather than throwing +it manually, you should use the standard validation utility that throws it for you: Objects.requireNonNull(), which has been available +since Java 7. This method:

+
    +
  • Is a standard idiom that developers instantly recognize across codebases
  • +
  • Replaces a multi-line if/throw block with a single, concise statement
  • +
  • Allows custom error messages for better diagnostics
  • +
  • Returns the validated object, enabling inline validation and assignment
  • +
+

What is the potential impact?

+

Hand-written null checks reduce code maintainability and readability:

+
    +
  • Boilerplate if/throw blocks add noise and make the actual logic of a method harder to follow
  • +
  • Each manual check is an opportunity for mistakes, such as a wrong condition or a missing message
  • +
  • The codebase becomes inconsistent if some methods use manual throws while others use standard validation approaches
  • +
+

How to fix it

+

Replace manual NullPointerException throws with Objects.requireNonNull(). This method validates that an object is not +null and throws a NullPointerException if it is, using a concise and widely recognized idiom that follows Java +conventions.

+

Code examples

+

Noncompliant code example

+
+public class UserService {
+    private String username;
+
+    public void setUsername(String username) {
+        if (username == null) {
+            throw new NullPointerException("username must not be null"); // Noncompliant
+        }
+        this.username = username;
+    }
 }
 
-

Compliant solution

-
-public void doSomething (@NotNull String aString) {
+

Compliant solution

+
+public class UserService {
+    private String username;
+
+    public void setUsername(String username) {
+        this.username = Objects.requireNonNull(username, "username must not be null");
+    }
 }
 
+

Resources

+

Documentation

+ +

Articles & blog posts

+ +

Standards

+ diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S1695.json b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S1695.json index 37567d20426..9c0de7354a8 100644 --- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S1695.json +++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S1695.json @@ -10,9 +10,11 @@ "status": "ready", "remediation": { "func": "Constant\/Issue", - "constantCost": "10min" + "constantCost": "5min" }, "tags": [ + "cert", + "convention", "error-handling", "pitfall" ], @@ -20,5 +22,10 @@ "ruleSpecification": "RSPEC-1695", "sqKey": "S1695", "scope": "Main", - "quickfix": "unknown" + "quickfix": "unknown", + "securityStandards": { + "CERT": [ + "ERR08-J." + ] + } } diff --git a/sonar-java-plugin/src/main/resources/profiles/Sonar_way/S1695 b/sonar-java-plugin/src/main/resources/profiles/Sonar_way/S1695 new file mode 100644 index 00000000000..e69de29bb2d From 05e1012fb26df8babcb714cebcad661b70b749b7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 16 Oct 2026 14:50:25 +0200 Subject: [PATCH 6/6] Update ruling results for PR #6324 (#6368) Co-authored-by: github-actions[bot] --- .../expected/commons-beanutils/java-S1695.json | 12 ------------ .../expected/eclipse-jetty/java-S1695.json | 3 --- .../resources/expected/guava/java-S1695.json | 17 +---------------- 3 files changed, 1 insertion(+), 31 deletions(-) diff --git a/its/ruling/src/test/resources/expected/commons-beanutils/java-S1695.json b/its/ruling/src/test/resources/expected/commons-beanutils/java-S1695.json index 4cb0bc96213..c8b28e7aea3 100644 --- a/its/ruling/src/test/resources/expected/commons-beanutils/java-S1695.json +++ b/its/ruling/src/test/resources/expected/commons-beanutils/java-S1695.json @@ -1,16 +1,4 @@ { -"commons-beanutils:src/main/java/org/apache/commons/beanutils2/BasicDynaBean.java": [ -121, -202, -234, -276, -307, -343, -385 -], -"commons-beanutils:src/main/java/org/apache/commons/beanutils2/LazyDynaBean.java": [ -501 -], "commons-beanutils:src/main/java/org/apache/commons/beanutils2/ResultSetDynaClass.java": [ 163 ], diff --git a/its/ruling/src/test/resources/expected/eclipse-jetty/java-S1695.json b/its/ruling/src/test/resources/expected/eclipse-jetty/java-S1695.json index b793951c56d..a9b2f345882 100644 --- a/its/ruling/src/test/resources/expected/eclipse-jetty/java-S1695.json +++ b/its/ruling/src/test/resources/expected/eclipse-jetty/java-S1695.json @@ -1,7 +1,4 @@ { -"eclipse-jetty:jetty-http/src/main/java/org/eclipse/jetty/http/PrecompressedHttpContent.java": [ -43 -], "eclipse-jetty:jetty-util/src/main/java/org/eclipse/jetty/util/BlockingArrayQueue.java": [ 468 ], diff --git a/its/ruling/src/test/resources/expected/guava/java-S1695.json b/its/ruling/src/test/resources/expected/guava/java-S1695.json index 3ff4ae6c89e..c30895bf790 100644 --- a/its/ruling/src/test/resources/expected/guava/java-S1695.json +++ b/its/ruling/src/test/resources/expected/guava/java-S1695.json @@ -1,20 +1,5 @@ { "guava:src/com/google/common/base/Preconditions.java": [ -212, -228, -251 -], -"guava:src/com/google/common/collect/CollectPreconditions.java": [ -31, -33 -], -"guava:src/com/google/common/collect/ImmutableMultimap.java": [ -216 -], -"guava:src/com/google/common/collect/MapMaker.java": [ -890 -], -"guava:src/com/google/common/collect/ObjectArrays.java": [ -240 +212 ] }