[FIR] buildErrorExpression for labels with underscored name

Add ConeDiagnosticWithSource, ConeUnderscoreIsReserved, ConeUnderscoreUsageWithoutBackticks diagnostics
This commit is contained in:
Ivan Kochurkin
2021-07-21 20:01:31 +03:00
committed by Space
parent f5d8535dc5
commit b307358f69
9 changed files with 106 additions and 29 deletions
@@ -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}")
}
@@ -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
}
@@ -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())
}
/**
@@ -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 {
@@ -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<T>(val baseSession: FirSession, val context: Conte
}
}
protected fun buildLabelAndErrorSource(rawName: String, source: FirSourceElement): Pair<FirLabel, FirSourceElement?> {
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("<anonymous>")
@@ -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,
@@ -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"
}
@@ -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
)
)
)
}
}
}
@@ -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,