From b307358f695a3b9aa16d06a20c967123fb4bd3a0 Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Wed, 21 Jul 2021 20:01:31 +0300 Subject: [PATCH] [FIR] buildErrorExpression for labels with underscored name Add ConeDiagnosticWithSource, ConeUnderscoreIsReserved, ConeUnderscoreUsageWithoutBackticks diagnostics --- .../coneDiagnosticToFirDiagnostic.kt | 4 +- .../fir/lightTree/converter/ConverterUtil.kt | 3 +- .../converter/ExpressionsConverter.kt | 19 ++++--- .../kotlin/fir/builder/RawFirBuilder.kt | 25 +++++----- .../kotlin/fir/builder/BaseFirBuilder.kt | 49 +++++++++++++++++++ .../kotlin/fir/builder/ConversionUtils.kt | 3 ++ .../fir/resolve/diagnostics/FirDiagnostics.kt | 5 +- .../providers/impl/FirTypeResolverImpl.kt | 15 +++--- .../fir/diagnostics/ConeSimpleDiagnostic.kt | 12 +++++ 9 files changed, 106 insertions(+), 29 deletions(-) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt index ee8d4e9e294..12dd675af5c 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt @@ -72,7 +72,7 @@ private fun ConeDiagnostic.toFirDiagnostic( is ConeUnexpectedTypeArgumentsError -> FirErrors.TYPE_ARGUMENTS_NOT_ALLOWED.createOn(this.source ?: source) is ConeIllegalAnnotationError -> FirErrors.NOT_AN_ANNOTATION_CLASS.createOn(source, this.name.asString()) is ConeWrongNumberOfTypeArgumentsError -> - FirErrors.WRONG_NUMBER_OF_TYPE_ARGUMENTS.createOn(this.source ?: qualifiedAccessSource ?: source, this.desiredCount, this.symbol) + FirErrors.WRONG_NUMBER_OF_TYPE_ARGUMENTS.createOn(this.source, this.desiredCount, this.symbol) is ConeOuterClassArgumentsRequired -> FirErrors.OUTER_CLASS_ARGUMENTS_REQUIRED.createOn(qualifiedAccessSource ?: source, this.symbol) is ConeNoTypeArgumentsOnRhsError -> @@ -92,6 +92,8 @@ private fun ConeDiagnostic.toFirDiagnostic( is ConeUnsupportedDynamicType -> FirErrors.UNSUPPORTED.createOn(source, this.reason) is ConeLocalVariableNoTypeOrInitializer -> runIf(variable.isLocalMember) { FirErrors.VARIABLE_WITH_NO_TYPE_NO_INITIALIZER.createOn(source) } + is ConeUnderscoreIsReserved -> FirErrors.UNDERSCORE_IS_RESERVED.createOn(this.source) + is ConeUnderscoreUsageWithoutBackticks -> FirErrors.UNDERSCORE_USAGE_WITHOUT_BACKTICKS.createOn(this.source) else -> throw IllegalArgumentException("Unsupported diagnostic type: ${this.javaClass}") } diff --git a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/ConverterUtil.kt b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/ConverterUtil.kt index 89fb54357f9..535e87dd6e9 100644 --- a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/ConverterUtil.kt +++ b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/ConverterUtil.kt @@ -29,6 +29,7 @@ import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.parsing.KotlinExpressionParsing +import org.jetbrains.kotlin.psi.KtPsiUtil import org.jetbrains.kotlin.psi.stubs.elements.KtConstantExpressionElementType import org.jetbrains.kotlin.psi.stubs.elements.KtStringTemplateExpressionElementType @@ -41,7 +42,7 @@ private val expressionSet = listOf( fun String?.nameAsSafeName(defaultName: String = ""): Name { return when { - this != null -> Name.identifier(this.replace("`", "")) + this != null -> Name.identifier(KtPsiUtil.unquoteIdentifier(this)) defaultName.isNotEmpty() -> Name.identifier(defaultName) else -> SpecialNames.NO_NAME_PROVIDED } diff --git a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/ExpressionsConverter.kt b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/ExpressionsConverter.kt index 5ef1b51bfb4..e70936cddb0 100644 --- a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/ExpressionsConverter.kt +++ b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/ExpressionsConverter.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.fir.declarations.builder.buildProperty import org.jetbrains.kotlin.fir.declarations.builder.buildValueParameter import org.jetbrains.kotlin.fir.declarations.impl.FirDeclarationStatusImpl import org.jetbrains.kotlin.fir.diagnostics.ConeNotAnnotationContainer +import org.jetbrains.kotlin.fir.diagnostics.ConeUnderscoreIsReserved import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind import org.jetbrains.kotlin.fir.expressions.* @@ -331,13 +332,17 @@ class ExpressionsConverter( * @see org.jetbrains.kotlin.fir.builder.RawFirBuilder.Visitor.visitLabeledExpression */ private fun convertLabeledExpression(labeledExpression: LighterASTNode): FirElement { - val size = context.firLabels.size var firExpression: FirElement? = null + val previousLabelsSize = context.firLabels.size + var errorLabelSource: FirSourceElement? = null + labeledExpression.forEachChildren { when (it.tokenType) { - LABEL_QUALIFIER -> context.firLabels += buildLabel { - source = labeledExpression.toFirLightSourceElement(tree) - name = it.toString().replace("@", "") + LABEL_QUALIFIER -> { + val rawName = it.toString() + val pair = buildLabelAndErrorSource(rawName.substring(0, rawName.length - 1), it.toFirSourceElement()) + context.firLabels += pair.first + errorLabelSource = pair.second } BLOCK -> firExpression = declarationsConverter.convertBlock(it) PROPERTY -> firExpression = declarationsConverter.convertPropertyDeclaration(it) @@ -345,11 +350,11 @@ class ExpressionsConverter( } } - if (size != context.firLabels.size) { + if (context.firLabels.size != previousLabelsSize) { context.firLabels.removeLast() - //println("Unused label: ${labeledExpression.getAsString()}") } - return firExpression ?: buildErrorExpression(null, ConeSimpleDiagnostic("Empty label", DiagnosticKind.Syntax)) + + return buildExpressionWithErrorLabel(firExpression, errorLabelSource, labeledExpression.toFirSourceElement()) } /** diff --git a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt index fdb7d45add0..c18eb0a2ce2 100644 --- a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt +++ b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt @@ -22,9 +22,7 @@ import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyGetter import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertySetter import org.jetbrains.kotlin.fir.declarations.utils.* -import org.jetbrains.kotlin.fir.diagnostics.ConeNotAnnotationContainer -import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic -import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind +import org.jetbrains.kotlin.fir.diagnostics.* import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.builder.* import org.jetbrains.kotlin.fir.expressions.impl.FirSingleExpressionBlock @@ -2219,21 +2217,24 @@ open class RawFirBuilder( } override fun visitLabeledExpression(expression: KtLabeledExpression, data: Unit): FirElement { - val sourceElement = expression.toFirSourceElement() val label = expression.getTargetLabel() - val size = context.firLabels.size + val previousLabelsSize = context.firLabels.size + var errorLabelSource: FirSourceElement? = null + if (label != null) { - context.firLabels += buildLabel { - source = label.toFirPsiSourceElement() - name = label.getReferencedName() - } + val rawName = label.getReferencedNameElement().node!!.text + val labelAndErrorSource = buildLabelAndErrorSource(rawName, label.toFirPsiSourceElement()) + context.firLabels += labelAndErrorSource.first + errorLabelSource = labelAndErrorSource.second } + val result = expression.baseExpression?.accept(this, data) - ?: buildErrorExpression(sourceElement, ConeSimpleDiagnostic("Empty label", DiagnosticKind.Syntax)) - if (size != context.firLabels.size) { + + if (context.firLabels.size != previousLabelsSize) { context.firLabels.removeLast() } - return result + + return buildExpressionWithErrorLabel(result, errorLabelSource, expression.toFirSourceElement()) } override fun visitAnnotatedExpression(expression: KtAnnotatedExpression, data: Unit): FirElement { diff --git a/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/BaseFirBuilder.kt b/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/BaseFirBuilder.kt index df2f8209607..b102fd41bbe 100644 --- a/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/BaseFirBuilder.kt +++ b/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/BaseFirBuilder.kt @@ -16,6 +16,7 @@ import org.jetbrains.kotlin.fir.declarations.impl.FirDeclarationStatusImpl import org.jetbrains.kotlin.fir.declarations.utils.addDeclaration import org.jetbrains.kotlin.fir.declarations.utils.isLocal import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic +import org.jetbrains.kotlin.fir.diagnostics.ConeUnderscoreIsReserved import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.builder.* @@ -35,6 +36,7 @@ import org.jetbrains.kotlin.name.CallableId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.parsing.* +import org.jetbrains.kotlin.psi.KtPsiUtil import org.jetbrains.kotlin.types.ConstantValueKind import org.jetbrains.kotlin.util.OperatorNameConventions @@ -1195,6 +1197,53 @@ abstract class BaseFirBuilder(val baseSession: FirSession, val context: Conte } } + protected fun buildLabelAndErrorSource(rawName: String, source: FirSourceElement): Pair { + val firLabel = buildLabel { + name = KtPsiUtil.unquoteIdentifier(rawName) + this.source = source + } + + return Pair(firLabel, if (rawName.isUnderscore) firLabel.source else null) + } + + protected fun buildExpressionWithErrorLabel( + element: FirElement?, + errorLabelSource: FirSourceElement?, + elementSource: FirSourceElement + ): FirElement { + return if (element != null) { + if (errorLabelSource != null) { + buildErrorExpression { + this.source = element.source + this.expression = element as? FirExpression + diagnostic = ConeUnderscoreIsReserved(errorLabelSource) + } + } else { + element + } + } else { + buildErrorExpression(elementSource, ConeSimpleDiagnostic("Empty label", DiagnosticKind.Syntax)) + } + } + + protected fun convertValueParameterName( + safeName: Name, + rawName: String?, + valueParameterDeclaration: ValueParameterDeclaration + ): Name { + return if (valueParameterDeclaration == ValueParameterDeclaration.LAMBDA && rawName == "_" + || + valueParameterDeclaration == ValueParameterDeclaration.CATCH && + baseSession.sessionProvider != null && + baseSession.languageVersionSettings.supportsFeature(LanguageFeature.ForbidReferencingToUnderscoreNamedParameterOfCatchBlock) && + safeName.asString() == "_" + ) { + SpecialNames.UNDERSCORE_FOR_UNUSED_VAR + } else { + safeName + } + } + /**** Common utils ****/ companion object { val ANONYMOUS_OBJECT_NAME = Name.special("") diff --git a/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/ConversionUtils.kt b/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/ConversionUtils.kt index 38040d496b9..67faabb5f1f 100644 --- a/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/ConversionUtils.kt +++ b/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/ConversionUtils.kt @@ -542,6 +542,9 @@ fun FirQualifiedAccess.wrapWithSafeCall(receiver: FirExpression, source: FirSour } } +val CharSequence.isUnderscore: Boolean + get() = all { it == '_' } + data class CalleeAndReceiver( val reference: FirNamedReference, val receiverExpression: FirExpression? = null, diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/diagnostics/FirDiagnostics.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/diagnostics/FirDiagnostics.kt index 69efd9e6706..d46bbc88722 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/diagnostics/FirDiagnostics.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/diagnostics/FirDiagnostics.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.fir.declarations.FirRegularClass import org.jetbrains.kotlin.fir.declarations.FirVariable import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic import org.jetbrains.kotlin.fir.expressions.FirExpression +import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnosticWithSource import org.jetbrains.kotlin.fir.render import org.jetbrains.kotlin.fir.resolve.calls.Candidate import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol @@ -112,8 +113,8 @@ abstract class ConeUnmatchedTypeArgumentsError(val desiredCount: Int, val type: class ConeWrongNumberOfTypeArgumentsError( val desiredCount: Int, val symbol: FirRegularClassSymbol, - val source: FirSourceElement? -) : ConeDiagnostic() { + source: FirSourceElement +) : ConeDiagnosticWithSource(source) { override val reason: String get() = "Wrong number of type arguments" } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/impl/FirTypeResolverImpl.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/impl/FirTypeResolverImpl.kt index d5e715c2f34..9852ef0a413 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/impl/FirTypeResolverImpl.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/impl/FirTypeResolverImpl.kt @@ -301,13 +301,16 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() { (parameterClass != null && parameterClass.classId.relativeClassName.parent().isRoot) || outerClass != null ) { - return ConeClassErrorType( - ConeWrongNumberOfTypeArgumentsError( - actualTypeParametersCount, - parameterClass?.symbol ?: symbol, - getTypeArgumentsOrNameSource(userTypeRef, qualifierPartIndex) + val source = getTypeArgumentsOrNameSource(userTypeRef, qualifierPartIndex) + if (source != null) { + return ConeClassErrorType( + ConeWrongNumberOfTypeArgumentsError( + actualTypeParametersCount, + parameterClass?.symbol ?: symbol, + source + ) ) - ) + } } } diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/diagnostics/ConeSimpleDiagnostic.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/diagnostics/ConeSimpleDiagnostic.kt index 59bdd1e2b3c..37831c7d857 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/diagnostics/ConeSimpleDiagnostic.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/diagnostics/ConeSimpleDiagnostic.kt @@ -5,12 +5,24 @@ package org.jetbrains.kotlin.fir.diagnostics +import org.jetbrains.kotlin.fir.FirSourceElement + class ConeSimpleDiagnostic(override val reason: String, val kind: DiagnosticKind = DiagnosticKind.Other) : ConeDiagnostic() class ConeNotAnnotationContainer(val text: String) : ConeDiagnostic() { override val reason: String get() = "Strange annotated expression: $text" } +abstract class ConeDiagnosticWithSource(val source: FirSourceElement) : ConeDiagnostic() + +class ConeUnderscoreIsReserved(source: FirSourceElement) : ConeDiagnosticWithSource(source) { + override val reason: String get() = "Names _, __, ___, ..., are reserved in Kotlin" +} + +class ConeUnderscoreUsageWithoutBackticks(source: FirSourceElement) : ConeDiagnosticWithSource(source) { + override val reason: String get() = "Names _, __, ___, ... can be used only in back-ticks (`_`, `__`, `___`, ...)" +} + enum class DiagnosticKind { Syntax, ExpressionExpected,