Support single-underscore variable names partially

Currently only parameters of lambdas/function expressions
and destructuring entries are allowed

 #KT-3824 In Progress
 #KT-2783 In Progress
This commit is contained in:
Denis Zharkov
2016-10-13 11:55:37 +03:00
parent 82364ad3e5
commit dbca310d8c
19 changed files with 319 additions and 21 deletions
@@ -53,6 +53,7 @@ import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.getDispatchReceiverWithSmartCast
import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.hasThisOrNoDispatchReceiver
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject
import org.jetbrains.kotlin.resolve.calls.util.isSingleUnderscore
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils.*
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
@@ -572,7 +573,7 @@ class ControlFlowInformationProvider private constructor(
val element = instruction.variableDeclarationElement as? KtNamedDeclaration ?: return@traverse
element.nameIdentifier ?: return@traverse
if (!VariableUseState.isUsed(variableUseState)) {
if (KtPsiUtil.isRemovableVariableDeclaration(element)) {
if (!element.isSingleUnderscore && KtPsiUtil.isRemovableVariableDeclaration(element)) {
report(Errors.UNUSED_VARIABLE.on(element, variableDescriptor), ctxt)
}
else if (element is KtParameter) {
@@ -46,6 +46,7 @@ import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfoFactory;
import org.jetbrains.kotlin.resolve.calls.util.UnderscoreUtilKt;
import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil;
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyTypeAliasDescriptor;
import org.jetbrains.kotlin.resolve.scopes.*;
@@ -304,14 +305,23 @@ public class DescriptorResolver {
destructuringVariables = null;
}
Name parameterName;
if (destructuringDeclaration == null) {
parameterName = UnderscoreUtilKt.isSingleUnderscore(valueParameter)
? Name.special("<anonymous parameter " + index + ">")
: KtPsiUtil.safeName(valueParameter.getName());
}
else {
parameterName = Name.special("<name for destructuring parameter " + index + ">");
}
ValueParameterDescriptorImpl valueParameterDescriptor = ValueParameterDescriptorImpl.createWithDestructuringDeclarations(
owner,
null,
index,
valueParameterAnnotations,
destructuringVariables == null
? KtPsiUtil.safeName(valueParameter.getName())
: Name.special("<name for destructuring parameter " + index + ">"),
parameterName,
variableType,
valueParameter.hasDefaultValue(),
valueParameter.hasModifier(CROSSINLINE_KEYWORD),
@@ -23,12 +23,14 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
import org.jetbrains.kotlin.diagnostics.Errors.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.KtPsiUtil
import org.jetbrains.kotlin.psi.KtVariableDeclaration
import org.jetbrains.kotlin.resolve.calls.context.ContextDependency
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
import org.jetbrains.kotlin.resolve.calls.util.isSingleUnderscore
import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.resolve.source.toSourceElement
@@ -186,11 +188,16 @@ class LocalVariableResolver(
type: KotlinType?,
trace: BindingTrace
): LocalVariableDescriptor {
val hasDelegate = variable is KtProperty && variable.hasDelegate();
val hasDelegate = variable is KtProperty && variable.hasDelegate()
val variableDescriptor = LocalVariableDescriptor(
scope.ownerDescriptor,
annotationResolver.resolveAnnotationsWithArguments(scope, variable.modifierList, trace),
KtPsiUtil.safeName(variable.name),
// Note, that the same code works both for common local vars and for destructuring declarations,
// but since the first case is illegal error must be reported somewhere else
if (variable.isSingleUnderscore)
Name.special("<underscore local var>")
else
KtPsiUtil.safeName(variable.name),
type,
variable.isVar,
hasDelegate,
@@ -199,4 +206,4 @@ class LocalVariableResolver(
trace.record(BindingContext.VARIABLE, variable, variableDescriptor)
return variableDescriptor
}
}
}
@@ -206,7 +206,7 @@ public class ModifiersChecker {
for (KtDestructuringDeclarationEntry multiEntry: multiDeclaration.getEntries()) {
annotationChecker.check(multiEntry, trace, null);
ModifierCheckerCore.INSTANCE.check(multiEntry, trace, null, languageVersionSettings);
UnderscoreChecker.INSTANCE.checkNamed(multiEntry, trace);
UnderscoreChecker.INSTANCE.checkNamed(multiEntry, trace, /* allowSingleUnderscore = */ true);
}
}
@@ -0,0 +1,33 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.resolve.calls.util
import com.intellij.psi.StubBasedPsiElement
import org.jetbrains.kotlin.psi.KtNamedDeclaration
/**
* val lambda = fun(x: Int, _: String, `_`: Double) = 1
*
* This property is true only for second value parameter in the example above
*/
val KtNamedDeclaration.isSingleUnderscore: Boolean
get() {
// We don't want to call 'getNameIdentifier' on stubs to prevent text building
// But it's fine because one-underscore names are prohibited for non-local declarations (only lambda parameters, local vars are allowed)
if (this is StubBasedPsiElement<*> && this.stub != null) return false
return nameIdentifier?.text == "_"
}
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.resolve.checkers
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.descriptors.impl.FunctionExpressionDescriptor
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.psi.*
@@ -26,15 +27,17 @@ import org.jetbrains.kotlin.resolve.BindingContext
object UnderscoreChecker : SimpleDeclarationChecker {
fun checkIdentifier(identifier: PsiElement?, diagnosticHolder: DiagnosticSink) {
@JvmOverloads
fun checkIdentifier(identifier: PsiElement?, diagnosticHolder: DiagnosticSink, allowSingleUnderscore: Boolean = false) {
if (identifier == null || identifier.text.isEmpty()) return
if (identifier.text.all { it == '_' }) {
if (identifier.text.all { it == '_' } && (!allowSingleUnderscore || identifier.text.length != 1)) {
diagnosticHolder.report(Errors.UNDERSCORE_IS_RESERVED.on(identifier))
}
}
fun checkNamed(declaration: KtNamedDeclaration, diagnosticHolder: DiagnosticSink) {
checkIdentifier(declaration.nameIdentifier, diagnosticHolder)
@JvmOverloads
fun checkNamed(declaration: KtNamedDeclaration, diagnosticHolder: DiagnosticSink, allowSingleUnderscore: Boolean = false) {
checkIdentifier(declaration.nameIdentifier, diagnosticHolder, allowSingleUnderscore)
}
override fun check(
@@ -46,7 +49,7 @@ object UnderscoreChecker : SimpleDeclarationChecker {
if (declaration is KtProperty && descriptor !is VariableDescriptor) return
if (declaration is KtCallableDeclaration) {
for (parameter in declaration.valueParameters) {
checkNamed(parameter, diagnosticHolder)
checkNamed(parameter, diagnosticHolder, allowSingleUnderscore = descriptor is FunctionExpressionDescriptor)
}
}
if (declaration is KtTypeParameterListOwner) {
@@ -146,7 +146,7 @@ internal class FunctionsTypingVisitor(facade: ExpressionTypingInternals) : Expre
val functionDescriptor = createFunctionLiteralDescriptor(expression, context)
expression.valueParameters.forEach {
components.identifierChecker.checkDeclaration(it, context.trace)
UnderscoreChecker.checkNamed(it, context.trace)
UnderscoreChecker.checkNamed(it, context.trace, allowSingleUnderscore = true)
}
val safeReturnType = computeReturnType(expression, context, functionDescriptor, functionTypeExpected)
functionDescriptor.setReturnType(safeReturnType)
+12 -1
View File
@@ -8,13 +8,24 @@ class <!UNDERSCORE_IS_RESERVED!>_<!><<!UNDERSCORE_IS_RESERVED!>________<!>>
val <!UNDERSCORE_IS_RESERVED!>______<!> = _<Int>()
fun <!UNDERSCORE_IS_RESERVED!>__<!>(<!UNDERSCORE_IS_RESERVED!>___<!>: Int, y: _<Int>?): Int {
val (_, <!UNUSED_VARIABLE!>z<!>) = Pair(___ - 1, 42)
val (x, <!UNDERSCORE_IS_RESERVED!>__________<!>) = Pair(___ - 1, 42)
val <!UNDERSCORE_IS_RESERVED!>____<!> = x
// in backquotes: allowed
val `_` = __________
val q = fun(_: Int, <!UNDERSCORE_IS_RESERVED, UNUSED_PARAMETER!>__<!>: Int) {}
q(1, 2)
val <!UNDERSCORE_IS_RESERVED!>_<!> = 56
<!UNDERSCORE_IS_RESERVED!>__<!>@ return if (y != null) __(____, y) else __(`_`, ______)
}
// one underscore parameters for named function are still prohibited
fun oneUnderscore(<!UNUSED_PARAMETER, UNDERSCORE_IS_RESERVED!>_<!>: Int) {}
fun doIt(f: (Any?) -> Any?) = f(null)
val something = doIt { <!UNDERSCORE_IS_RESERVED!>__<!> -> __ }
val something = doIt { <!UNDERSCORE_IS_RESERVED!>__<!> -> __ }
val something2 = doIt { _ -> 1 }
+2
View File
@@ -2,8 +2,10 @@ package
public val ______: _<kotlin.Int>
public val something: kotlin.Any?
public val something2: kotlin.Any?
public fun __(/*0*/ ___: kotlin.Int, /*1*/ y: _<kotlin.Int>?): kotlin.Int
public fun doIt(/*0*/ f: (kotlin.Any?) -> kotlin.Any?): kotlin.Any?
public fun oneUnderscore(/*0*/ <anonymous parameter 0>: kotlin.Int): kotlin.Unit
@kotlin.Deprecated(message = "") public final data class Pair {
public constructor Pair(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int)
@@ -6,9 +6,9 @@ fun useDeclaredVariables() {
}
fun checkersShouldRun() {
for ((@A a, <!UNDERSCORE_IS_RESERVED, UNUSED_VARIABLE!>_<!>)<!SYNTAX!><!>) {
for ((@A a, _)<!SYNTAX!><!>) {
}
}
annotation class A
annotation class A
@@ -5,7 +5,7 @@ fun useDeclaredVariables() {
}
fun checkersShouldRun() {
val (@A a, <!UNDERSCORE_IS_RESERVED, UNUSED_VARIABLE!>_<!>) = <!UNRESOLVED_REFERENCE!>unresolved<!>
val (@A a, _) = <!UNRESOLVED_REFERENCE!>unresolved<!>
}
annotation class A
annotation class A
@@ -5,7 +5,7 @@ fun useDeclaredVariables() {
}
fun checkersShouldRun() {
<!INITIALIZER_REQUIRED_FOR_DESTRUCTURING_DECLARATION!>val (@A a, <!UNDERSCORE_IS_RESERVED, UNUSED_VARIABLE!>_<!>)<!>
<!INITIALIZER_REQUIRED_FOR_DESTRUCTURING_DECLARATION!>val (@A a, _)<!>
}
annotation class A
annotation class A
@@ -0,0 +1,48 @@
class A {
operator fun component1() = 1
operator fun component2() = ""
}
class C {
operator fun iterator(): Iterator<A> = null!!
}
fun test() {
for ((x, _) in C()) {
foo(x, <!UNRESOLVED_REFERENCE!>_<!>)
}
for ((_, y) in C()) {
foo(<!UNRESOLVED_REFERENCE!>_<!>, y)
}
for ((_, _) in C()) {
foo(<!UNRESOLVED_REFERENCE!>_<!>, <!UNRESOLVED_REFERENCE!>_<!>)
}
for ((_ : Int, _ : String) in C()) {
foo(<!UNRESOLVED_REFERENCE!>_<!>, <!UNRESOLVED_REFERENCE!>_<!>)
}
for ((_ : String, _ : Int) in <!COMPONENT_FUNCTION_RETURN_TYPE_MISMATCH, COMPONENT_FUNCTION_RETURN_TYPE_MISMATCH!>C()<!>) {
foo(<!UNRESOLVED_REFERENCE!>_<!>, <!UNRESOLVED_REFERENCE!>_<!>)
}
val (x, _) = A()
val (_, y) = A()
foo(x, y)
foo(x, <!UNRESOLVED_REFERENCE!>_<!>)
foo(<!UNRESOLVED_REFERENCE!>_<!>, y)
val (<!REDECLARATION!>`_`<!>, z) = A()
foo(_, z)
val (_, <!NAME_SHADOWING, REDECLARATION!>`_`<!>) = A()
foo(<!TYPE_MISMATCH!>_<!>, y)
}
fun foo(<!UNUSED_PARAMETER!>x<!>: Int, <!UNUSED_PARAMETER!>y<!>: String) {}
@@ -0,0 +1,21 @@
package
public fun foo(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.String): kotlin.Unit
public fun test(): kotlin.Unit
public final class A {
public constructor A()
public final operator fun component1(): kotlin.Int
public final operator fun component2(): kotlin.String
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class C {
public constructor C()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public final operator fun iterator(): kotlin.collections.Iterator<A>
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,62 @@
// !CHECK_TYPE
// !DIAGNOSTICS: -UNUSED_PARAMETER
data class A(val x: Int, val y: String)
data class B(val u: Double, val w: Short)
fun foo(block: (A) -> Unit) { }
fun bar() {
foo { (_, b) ->
<!UNRESOLVED_REFERENCE!>_<!>.<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>hashCode<!>()
b checkType { _<String>() }
}
foo { (a, _) ->
a checkType { _<Int>() }
<!UNRESOLVED_REFERENCE!>_<!>.<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>hashCode<!>()
}
foo { (_, _) ->
<!UNRESOLVED_REFERENCE!>_<!>.<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>hashCode<!>()
}
foo { (_: Int, b: String) ->
<!UNRESOLVED_REFERENCE!>_<!>.<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>hashCode<!>()
b checkType { _<String>() }
}
foo { (a: Int, _: String) ->
a checkType { _<Int>() }
<!UNRESOLVED_REFERENCE!>_<!>.<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>hashCode<!>()
}
foo { (_: Int, _: String) ->
<!UNRESOLVED_REFERENCE!>_<!>.<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>hashCode<!>()
}
foo { (_, _): A ->
<!UNRESOLVED_REFERENCE!>_<!>.<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>hashCode<!>()
}
foo { (`_`, _) ->
_ checkType { _<Int>() }
}
foo { (_, `_`) ->
_ checkType { _<String>() }
}
foo { (<!REDECLARATION!>`_`<!>, <!REDECLARATION!>`_`<!>) ->
_ checkType { _<String>() }
}
foo { (<!COMPONENT_FUNCTION_RETURN_TYPE_MISMATCH!>_: String<!>, b) ->
<!UNRESOLVED_REFERENCE!>_<!>.<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>hashCode<!>()
b checkType { _<String>() }
}
foo { <!EXPECTED_PARAMETER_TYPE_MISMATCH!>(_, b): B<!> ->
<!UNRESOLVED_REFERENCE!>_<!>.<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>hashCode<!>()
b checkType { _<Short>() }
}
}
@@ -0,0 +1,28 @@
package
public fun bar(): kotlin.Unit
public fun foo(/*0*/ block: (A) -> kotlin.Unit): kotlin.Unit
public final data class A {
public constructor A(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.String)
public final val x: kotlin.Int
public final val y: kotlin.String
public final operator /*synthesized*/ fun component1(): kotlin.Int
public final operator /*synthesized*/ fun component2(): kotlin.String
public final /*synthesized*/ fun copy(/*0*/ x: kotlin.Int = ..., /*1*/ y: kotlin.String = ...): A
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
public final data class B {
public constructor B(/*0*/ u: kotlin.Double, /*1*/ w: kotlin.Short)
public final val u: kotlin.Double
public final val w: kotlin.Short
public final operator /*synthesized*/ fun component1(): kotlin.Double
public final operator /*synthesized*/ fun component2(): kotlin.Short
public final /*synthesized*/ fun copy(/*0*/ u: kotlin.Double = ..., /*1*/ w: kotlin.Short = ...): B
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
@@ -0,0 +1,49 @@
// !CHECK_TYPE
// !DIAGNOSTICS: -UNUSED_PARAMETER
fun foo(block: (Int, String) -> Unit) { }
fun foobar(block: (Double) -> Unit) { }
fun bar() {
foo { _, b ->
<!UNRESOLVED_REFERENCE!>_<!>.<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>hashCode<!>()
b checkType { _<String>() }
}
foo { a, _ ->
a checkType { _<Int>() }
<!UNRESOLVED_REFERENCE!>_<!>.<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>hashCode<!>()
}
foo { _, _ ->
<!UNRESOLVED_REFERENCE!>_<!>.<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>hashCode<!>()
}
foo { _: Int, b: String ->
<!UNRESOLVED_REFERENCE!>_<!>.<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>hashCode<!>()
b checkType { _<String>() }
}
foo { a: Int, _: String ->
a checkType { _<Int>() }
<!UNRESOLVED_REFERENCE!>_<!>.<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>hashCode<!>()
}
foo { _: Int, _: String ->
<!UNRESOLVED_REFERENCE!>_<!>.<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>hashCode<!>()
}
foo { `_`, _ ->
_ checkType { _<Int>() }
}
foo { _, `_` ->
_ checkType { _<String>() }
}
foo { <!REDECLARATION, REDECLARATION!>`_`<!>, <!REDECLARATION, REDECLARATION!>`_`<!> ->
_ checkType { _<String>() }
}
foo(fun(x: Int, _: String) {})
}
@@ -0,0 +1,5 @@
package
public fun bar(): kotlin.Unit
public fun foo(/*0*/ block: (kotlin.Int, kotlin.String) -> kotlin.Unit): kotlin.Unit
public fun foobar(/*0*/ block: (kotlin.Double) -> kotlin.Unit): kotlin.Unit
@@ -5308,6 +5308,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/declarationChecks/destructuringDeclarations/SingleDeclForLoop.kt");
doTest(fileName);
}
@TestMetadata("underscore.kt")
public void testUnderscore() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/declarationChecks/destructuringDeclarations/underscore.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/diagnostics/tests/declarationChecks/finiteBoundRestriction")
@@ -7662,6 +7668,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("underscopeParameters.kt")
public void testUnderscopeParameters() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/functionLiterals/underscopeParameters.kt");
doTest(fileName);
}
@TestMetadata("unusedLiteral.kt")
public void testUnusedLiteral() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/functionLiterals/unusedLiteral.kt");
@@ -7724,6 +7736,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("underscore.kt")
public void testUnderscore() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/functionLiterals/destructuringInLambdas/underscore.kt");
doTest(fileName);
}
@TestMetadata("unsupportedFeature.kt")
public void testUnsupportedFeature() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/functionLiterals/destructuringInLambdas/unsupportedFeature.kt");