diff --git a/rules/S6518/kotlin/metadata.json b/rules/S6518/kotlin/metadata.json new file mode 100644 index 0000000000..1e9072ab15 --- /dev/null +++ b/rules/S6518/kotlin/metadata.json @@ -0,0 +1,17 @@ +{ + "title": "Element access should use indexed access operators", + "type": "CODE_SMELL", + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "5min" + }, + "tags": [ + ], + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-6518", + "sqKey": "S6518", + "scope": "All", + "defaultQualityProfiles": ["Sonar way"], + "quickfix": "unknown" +} diff --git a/rules/S6518/kotlin/rule.adoc b/rules/S6518/kotlin/rule.adoc new file mode 100644 index 0000000000..b54c688d4a --- /dev/null +++ b/rules/S6518/kotlin/rule.adoc @@ -0,0 +1,70 @@ +== Why is this an issue? + +The primary purpose of classes that implement indexed access operators is that of element access, +like this is the case for arrays, lists, maps, or sets. +When a class implements indexed access operators, they should be used as operators instead of calling them as functions, +because this is the intention of the designer of the API. + +=== What is the potential impact? + +==== Readability and Understanding + +This change makes it easier to understand what a function does, +because the semantics of indexed access operators is evident to the reader, +while for a function call, the reader would need to know what the called function does. + +== How to fix it + +Replace `instance.get(index)` with `instance[index]`. + +Replace `instance.set(index, value)` with `instance[index] = value`. + +This also works with multi-index access operators. + +=== Code examples + +==== Noncompliant code example + +[source,kotlin] +---- +interface Grid { + operator fun get(row: Int, column: Int): Int + operator fun set(row: Int, column: Int, value: Int) +} +---- + +[source,kotlin,diff-id=1,diff-type=noncompliant] +---- +fun indexedAccess(list: MutableList, map: MutableMap, grid: Grid) { + list.get(1) // Noncompliant + list.set(1, 42) // Noncompliant + map.get("b") // Noncompliant + map.set("b", 42) // Noncompliant + grid.get(1, 2) // Noncompliant + grid.set(1, 2, 42) // Noncompliant +} +---- + +==== Compliant solution + +[source,kotlin,diff-id=1,diff-type=compliant] +---- +fun indexedAccess(list: MutableList, map: MutableMap, grid: Grid) { + list[1] // Compliant + list[1] = 42 // Compliant + map["b"] // Compliant + map["b"] = 42 // Compliant + grid[1, 2] // Compliant + grid[1, 2] = 42 // Compliant + list.getOrNull(2) // Compliant, because function is not an operator + list.getOrElse(3) {42} // Compliant, because function is not an operator + map.getValue("a") // Compliant, because function is not an operator + map.getOrElse("c") {42} // Compliant, because function is not an operator +} +---- + +== Resources + +=== Documentation + +* https://kotlinlang.org/docs/operator-overloading.html#indexed-access-operator[Kotlin Docs, Indexed access operator] diff --git a/rules/S6518/metadata.json b/rules/S6518/metadata.json new file mode 100644 index 0000000000..2c63c08510 --- /dev/null +++ b/rules/S6518/metadata.json @@ -0,0 +1,2 @@ +{ +}