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)