Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions rules/S8326/groovy/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"title": "Range methods should be used appropriately to avoid unexpected behavior",
"type": "CODE_SMELL",
"status": "ready",
"remediation": {
"func": "Constant/Issue",
"constantCost": "5 min"
},
"tags": [
"confusing"
],
"defaultSeverity": "Blocker",
"ruleSpecification": "RSPEC-8326",
"sqKey": "S8326",
"scope": "All",
"defaultQualityProfiles": [
"Sonar way"
],
"quickfix": "unknown",
"code": {
"impacts": {
"RELIABILITY": "MEDIUM",
"MAINTAINABILITY": "BLOCKER"
},
"attribute": "CLEAR"
}
}
47 changes: 47 additions & 0 deletions rules/S8326/groovy/rule.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
This is an issue when using `containsWithinBounds()` instead of `contains()` for membership testing, or when using incorrect step directions for range iteration.

== Why is this an issue?

Groovy Range objects have subtle but important differences in their methods that can lead to unexpected behavior if misused.

The `containsWithinBounds()` method only checks if a value falls between the range boundaries, while `contains()` checks if the value is actually part of the discrete items in the range. For example, with a range `1.5..3`, the value `2` falls within the bounds but is not actually in the range since the range contains `[1.5, 2.5]` (incrementing by 1 from 1.5). Using `containsWithinBounds(2)` returns `true`, but `contains(2)` correctly returns `false`.

Similarly, when iterating through ranges with the `step()` method, the step direction must match the range direction. A reverse range like `5..1` needs a negative step to iterate properly. Using a positive step on a reverse range or a negative step on a forward range will not produce the expected iteration behavior.

These misunderstandings can lead to logic errors where conditions evaluate incorrectly or loops don't execute as expected.

=== What is the potential impact?

Using the wrong Range method can cause logic errors in conditional statements, leading to incorrect program behavior. Mismatched step directions can result in unexpected iteration patterns or empty iterations, potentially causing data processing errors or infinite loops.

== How to fix it

Use `contains()` instead of `containsWithinBounds()` when you need to check if a value is actually part of the range's discrete items.

=== Code examples

==== Noncompliant code example

[source,groovy,diff-id=1,diff-type=noncompliant]
----
def range = 1.5..3
if (range.containsWithinBounds(2)) { // Noncompliant
println "Found 2 in range"
}
----

==== Compliant solution

[source,groovy,diff-id=1,diff-type=compliant]
----
def range = 1.5..3
if (range.contains(2)) {
println "Found 2 in range"
}
----

== Resources

=== Documentation

* Groovy Range Documentation - http://docs.groovy-lang.org/latest/html/api/groovy/lang/Range.html[Official Groovy documentation for Range interface methods and behavior]
2 changes: 2 additions & 0 deletions rules/S8326/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{
}