Skip to content

Commit 728b736

Browse files
committed
Create rule S7125
1 parent 0445ff0 commit 728b736

File tree

2 files changed

+99
-26
lines changed

2 files changed

+99
-26
lines changed

rules/S7125/java/metadata.json

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"title": "FIXME",
2+
"title": "Null values should not be used in non-nullable input positions",
33
"type": "CODE_SMELL",
44
"status": "ready",
55
"remediation": {
@@ -11,14 +11,12 @@
1111
"defaultSeverity": "Major",
1212
"ruleSpecification": "RSPEC-7125",
1313
"sqKey": "S7125",
14-
"scope": "All",
14+
"scope": "Main",
1515
"defaultQualityProfiles": ["Sonar way"],
1616
"quickfix": "unknown",
1717
"code": {
1818
"impacts": {
19-
"MAINTAINABILITY": "HIGH",
20-
"RELIABILITY": "MEDIUM",
21-
"SECURITY": "LOW"
19+
"RELIABILITY": "HIGH"
2220
},
2321
"attribute": "CONVENTIONAL"
2422
}

rules/S7125/java/rule.adoc

Lines changed: 96 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,119 @@
1-
FIXME: add a description
2-
3-
// If you want to factorize the description uncomment the following line and create the file.
4-
//include::../description.adoc[]
5-
61
== Why is this an issue?
72

8-
FIXME: remove the unused optional headers (that are commented out)
3+
Using `null` in a non-nullable input position (e.g., as the right-hand side of an assignment, a function call argument, or a return statement argument) can lead to a NullPointerException (NPE) at runtime. This occurs because the receiving code typically assumes the value is non-null and omits null checks.
94

10-
//=== What is the potential impact?
5+
Formally, non-nullable and nullable versions of a type are distinct, with different domains.
6+
The domain of a non-nullable type is _D_, while the domain of a nullable type is _D ∪ null_, a superset of _D_.
7+
Thus, a non-null value can be used wherever a nullable type is expected, but not vice versa.
8+
The only reason it's allowed by the compiler is that null-safety is not a built-in Java language feature, and it's therefore handled via nullability annotations by external tools bypassing the regular typing system.
119

1210
== How to fix it
13-
//== How to fix it in FRAMEWORK NAME
1411

15-
=== Code examples
12+
Depending on the use-case, there are different strategies to fix this problem:
13+
14+
1. **Change the input position type from non-nullable to nullable:** This resolves the issue at the reported location but may propagate it elsewhere. Note: you should avoid declaring everything nullable; only do so where it aligns with your data and state models. Otherwise, consider the other approaches.
1615

1716
==== Noncompliant code example
1817

1918
[source,java,diff-id=1,diff-type=noncompliant]
2019
----
21-
FIXME
20+
@NonNull String title = null;
2221
----
2322

2423
==== Compliant solution
2524

2625
[source,java,diff-id=1,diff-type=compliant]
2726
----
28-
FIXME
27+
String title = null;
28+
----
29+
30+
==== Noncompliant code example
31+
32+
[source,java,diff-id=2,diff-type=noncompliant]
33+
----
34+
@NullMarked
35+
class Collector {
36+
void collectData(List<Entity> entities) {
37+
// ...
38+
}
39+
}
40+
41+
void process() {
42+
collector.collectData(null);
43+
}
44+
----
45+
46+
==== Compliant solution
47+
48+
[source,java,diff-id=2,diff-type=compliant]
49+
----
50+
class Collector {
51+
void collectData(List<Entity> entities) {
52+
// ...
53+
}
54+
}
55+
56+
void process() {
57+
collector.collectData(null);
58+
}
59+
----
60+
61+
2. **Replace `null` with a Guard Element:** This is particularly effective for array and collection types, where `null` can easily be replaced with an empty array or collection instance.
62+
63+
[source,java,diff-id=3,diff-type=noncompliant]
64+
----
65+
@NullMarked
66+
class Collector {
67+
void collectData(List<Entity> entities) {
68+
// ...
69+
}
70+
}
71+
72+
void process() {
73+
collector.collectData(null);
74+
}
75+
----
76+
77+
==== Compliant solution
78+
79+
[source,java,diff-id=3,diff-type=compliant]
2980
----
81+
@NullMarked
82+
class Collector {
83+
void collectData(List<Entity> entities) {
84+
// ...
85+
}
86+
}
3087
31-
//=== How does this work?
88+
void process() {
89+
collector.collectData(List.of());
90+
}
91+
----
92+
93+
3. **Throw an Exception:** For unexpected or uninitialized values or unspecified behavior, throw an exception instead of returning `null`. This reports the issue at its origin, not somewhere else in the source code where the unexpected `null` value suddenly becomes a problem. This makes debugging easier.
3294

33-
//=== Pitfalls
95+
==== Noncompliant code example
3496

35-
//=== Going the extra mile
97+
[source,java,diff-id=4,diff-type=noncompliant]
98+
----
99+
@NonNull State getNextState() {
100+
return switch (state) {
101+
case State.PENDING -> State.PROCESSING;
102+
case State.PROCESSING -> State.PENDING;
103+
default -> null;
104+
};
105+
}
106+
----
36107

108+
==== Compliant solution
37109

38-
//== Resources
39-
//=== Documentation
40-
//=== Articles & blog posts
41-
//=== Conference presentations
42-
//=== Standards
43-
//=== External coding guidelines
44-
//=== Benchmarks
110+
[source,java,diff-id=4,diff-type=compliant]
111+
----
112+
@NonNull State getNextState() {
113+
return switch (state) {
114+
case State.PENDING -> State.PROCESSING;
115+
case State.PROCESSING -> State.PENDING;
116+
default -> throw new IllegalStateException();
117+
};
118+
}
119+
----

0 commit comments

Comments
 (0)