Compare commits

...

2 Commits

Author SHA1 Message Date
Marco Kaufmann
d66e6407d5 Create rule S7125 2024-10-11 18:11:17 +02:00
kaufco
0445ff0a80 Create rule S7125 2024-10-10 16:20:35 +00:00
3 changed files with 148 additions and 0 deletions

View File

@ -0,0 +1,23 @@
{
"title": "Null values should not be used in non-nullable input positions",
"type": "CODE_SMELL",
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-7125",
"sqKey": "S7125",
"scope": "Main",
"defaultQualityProfiles": ["Sonar way"],
"quickfix": "unknown",
"code": {
"impacts": {
"RELIABILITY": "HIGH"
},
"attribute": "CONVENTIONAL"
}
}

123
rules/S7125/java/rule.adoc Normal file
View File

@ -0,0 +1,123 @@
== Why is this an issue?
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.
Formally, non-nullable and nullable versions of a type are distinct, with different domains.
The domain of a non-nullable type is _D_, while the domain of a nullable type is _D ∪ null_, a superset of _D_.
Thus, a non-null value can be used wherever a nullable type is expected, but not vice versa.
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.
== How to fix it
Depending on the use-case, there are different strategies to fix this problem
=== Code examples
**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.
==== Noncompliant code example
[source,java,diff-id=1,diff-type=noncompliant]
----
@NonNull String title = null;
----
==== Compliant solution
[source,java,diff-id=1,diff-type=compliant]
----
String title = null;
----
==== Noncompliant code example
[source,java,diff-id=2,diff-type=noncompliant]
----
@NullMarked
class Collector {
void collectData(List<Entity> entities) {
// ...
}
}
void process() {
collector.collectData(null);
}
----
==== Compliant solution
[source,java,diff-id=2,diff-type=compliant]
----
class Collector {
void collectData(List<Entity> entities) {
// ...
}
}
void process() {
collector.collectData(null);
}
----
**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.
==== Noncompliant code example
[source,java,diff-id=3,diff-type=noncompliant]
----
@NullMarked
class Collector {
void collectData(List<Entity> entities) {
// ...
}
}
void process() {
collector.collectData(null);
}
----
==== Compliant solution
[source,java,diff-id=3,diff-type=compliant]
----
@NullMarked
class Collector {
void collectData(List<Entity> entities) {
// ...
}
}
void process() {
collector.collectData(List.of());
}
----
**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.
==== Noncompliant code example
[source,java,diff-id=4,diff-type=noncompliant]
----
@NonNull State getNextState() {
return switch (state) {
case State.PENDING -> State.PROCESSING;
case State.PROCESSING -> State.PENDING;
default -> null;
};
}
----
==== Compliant solution
[source,java,diff-id=4,diff-type=compliant]
----
@NonNull State getNextState() {
return switch (state) {
case State.PENDING -> State.PROCESSING;
case State.PROCESSING -> State.PENDING;
default -> throw new IllegalStateException();
};
}
----

View File

@ -0,0 +1,2 @@
{
}