Skip to content

Commit 16d775f

Browse files
sandwwraithnikitabobko
authored andcommitted
Add KEEPs for Unused return value checker and Underscore for unused local variables
closes #413
1 parent 3d7fb57 commit 16d775f

File tree

3 files changed

+421
-0
lines changed

3 files changed

+421
-0
lines changed

check-uniq-keep-ids.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ val duplicateIds = java.io.File("proposals").walkTopDown().filter { it.isFile }
66
.eachCount()
77
.filter { it.value > 1 }
88
.keys
9+
.filter { it != "KEEP-0412-" } // Two related proposals were submitted under a single KEEP number:
10+
// - KEEP-0412-underscores-for-local-variables.md
11+
// - KEEP-0412-unused-return-value-checker.md
912
if (duplicateIds.isNotEmpty()) {
1013
println("!!! Duplicated KEEP IDs found !!!")
1114
duplicateIds.forEach(::println)
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# Underscore syntax for unused local variables
2+
3+
* **Type**: Design proposal
4+
* **Authors**: Leonid Startsev, Mikhail Zarechenskiy
5+
* **Contributors**: Alejandro Serrano Mena, Denis Zharkov, Marat Akhin, Nikita Bobko, Pavel Kunyavskiy, Vsevolod Tolstopyatov
6+
* **Status**: Experimental in Kotlin 2.2
7+
* **Discussion**: [KEEP-412](https://github.com/Kotlin/KEEP/issues/412)
8+
9+
## Synopsis
10+
11+
The gist of this proposal is allowing `_` (one underscore) as a local variable name.
12+
The intention is for the compiler and other static analysis tools to consider that value
13+
as ignored or unused (depending on the particular scenario):
14+
15+
```kotlin
16+
fun writeTo(file: File): Boolean {
17+
val result = runCatching { file.writeText("Hello World!") }
18+
return result.isSuccess
19+
}
20+
21+
fun foo(file: File) {
22+
val _ = writeTo(file) // We are not interested in whether the write operation was successful
23+
}
24+
```
25+
26+
Underscore can be used only in *local variable declaration*: `val _ = ...` and cannot be referenced or resolved otherwise.
27+
28+
## Motivation
29+
30+
1. Explicit expression of intent
31+
32+
When reading and reviewing code you are unfamiliar with, it may be hard to say whether unused function return value is an intention or a subtle bug.
33+
Explicit syntax would help quickly differentiate between the two.
34+
35+
2. Readiness for static analysis
36+
37+
In the scope of [Unused return value checker](unused-return-value-checker.md) proposal, a checker for more complex unused expressions will be added to the Kotlin compiler.
38+
This syntax will make its adoption much easier since there would be no need to `@Suppress` checker warnings in cases where the value has to be explicitly ignored.
39+
Other static analysis tools will also report less false positives with this feature.
40+
41+
3. Uniformity in existing Kotlin features
42+
43+
Kotlin already supports underscores for unused things in several different positions:
44+
45+
* Underscore for unused lambda/anonymous function parameters ([KEEP](underscore-for-unused-parameters.md), [link](https://kotlinlang.org/docs/lambdas.html#underscore-for-unused-variables)).
46+
* Underscore for unused components in positional-based destructuring ([link](https://kotlinlang.org/docs/destructuring-declarations.html)).
47+
* Underscore for unused type arguments ([link](https://kotlinlang.org/docs/generics.html#underscore-operator-for-type-arguments))
48+
* Underscore for unused exception instances ([link](https://youtrack.jetbrains.com/issue/KT-31567))
49+
50+
Adding an underscore as a name for unused local variables is a logical extension of this trend.
51+
52+
## Proposed syntax
53+
54+
It is allowed to declare a variable as `val _`. Such a variable is called **unnamed**.
55+
Unnamed variable declarations should adhere to the following restrictions:
56+
57+
* Only local variables (not class or top-level properties) can be declared unnamed.
58+
* Only the `val` keyword can be used; `var` is not allowed.
59+
* You can have multiple unnamed variables in a single scope.
60+
* Unnamed variables cannot be delegated.
61+
* Referencing an unnamed variable is not possible. Because of that, it is required to have an initializer.
62+
* Unnamed variables can have their type specified explicitly. If type is not specified, it is inferred using the same rules as regular variables.
63+
64+
### Discarded alternatives
65+
66+
Besides using `val _ = ...` syntax, a shorter `_ = ...` statement was considered as an alternative.
67+
However, we've decided not to proceed with it for the following reasons:
68+
69+
1. `val` syntax is better aligned with already existing Kotlin features.
70+
71+
Kotlin already has positional-based destructuring with an option to use an underscore to omit certain components: `val (first, _) = getSomePair()`. Since positional-based destructuring declares new variables, it uses `val` syntax.
72+
73+
On the other hand, Kotlin has the 'underscore for unused lambda parameter' feature. Using underscore without `val` here may imply some connection with the lambda parameter, while in reality, there is no such connection:
74+
75+
```kotlin
76+
listOf(1, 2, 3).mapIndexed { idx, _ ->
77+
_ = unusedCall(idx) // Is _ a new unnamed variable or an existing lambda parameter?
78+
}
79+
```
80+
81+
2. `val` syntax implies the creation of a new unnamed variable rather than referencing an existing one.
82+
83+
Normally, without the `val/var` keywords, you can encounter only existing declarations on the left-hand side of an assignment.
84+
Using `_ = ...` syntax may create an impression that `_` is some existing global variable, while in reality, any expression that uses `_` — such as `println(_)` — would produce 'unresolved reference' compiler error.
85+
86+
## Further extensions
87+
88+
Besides this proposal and already existing underscore usages, one of the most requested places for underscore support is an unused function parameter:
89+
90+
```kotlin
91+
fun foo(_: String) {
92+
println("Parameter is unused")
93+
}
94+
```
95+
96+
However, this use case is explicitly out of the scope of this proposal.
97+
Its destiny will be decided later.

0 commit comments

Comments
 (0)