[FIR] Implement NO_RECEIVER_ALLOWED

This commit is contained in:
Ivan Kochurkin
2021-06-29 16:20:57 +03:00
parent 45a384bb77
commit 263e40e753
11 changed files with 52 additions and 21 deletions
@@ -119,6 +119,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
parameter<Boolean>("hasValueParameters") parameter<Boolean>("hasValueParameters")
} }
val ILLEGAL_SELECTOR by error<PsiElement>() val ILLEGAL_SELECTOR by error<PsiElement>()
val NO_RECEIVER_ALLOWED by error<PsiElement>()
} }
val SUPER by object : DiagnosticGroup("Super") { val SUPER by object : DiagnosticGroup("Super") {
@@ -132,6 +132,7 @@ object FirErrors {
val CREATING_AN_INSTANCE_OF_ABSTRACT_CLASS by error0<KtExpression>() val CREATING_AN_INSTANCE_OF_ABSTRACT_CLASS by error0<KtExpression>()
val FUNCTION_CALL_EXPECTED by error2<PsiElement, String, Boolean>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED) val FUNCTION_CALL_EXPECTED by error2<PsiElement, String, Boolean>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
val ILLEGAL_SELECTOR by error0<PsiElement>() val ILLEGAL_SELECTOR by error0<PsiElement>()
val NO_RECEIVER_ALLOWED by error0<PsiElement>()
// Super // Super
val SUPER_IS_NOT_AN_EXPRESSION by error0<PsiElement>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED) val SUPER_IS_NOT_AN_EXPRESSION by error0<PsiElement>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
@@ -272,6 +272,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_ACTUAL_FOR_EXP
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_COMPANION_OBJECT import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_COMPANION_OBJECT
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_ELSE_IN_WHEN import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_ELSE_IN_WHEN
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_GET_METHOD import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_GET_METHOD
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_RECEIVER_ALLOWED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_SET_METHOD import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_SET_METHOD
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_THIS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_THIS
@@ -496,6 +497,7 @@ class FirDefaultErrorMessages {
map.put(CREATING_AN_INSTANCE_OF_ABSTRACT_CLASS, "Cannot create an instance of an abstract class") map.put(CREATING_AN_INSTANCE_OF_ABSTRACT_CLASS, "Cannot create an instance of an abstract class")
map.put(FUNCTION_CALL_EXPECTED, "Function invocation ''{0}({1})'' expected", TO_STRING, FUNCTION_PARAMETERS) map.put(FUNCTION_CALL_EXPECTED, "Function invocation ''{0}({1})'' expected", TO_STRING, FUNCTION_PARAMETERS)
map.put(ILLEGAL_SELECTOR, "The expression cannot be a selector (occur after a dot)") map.put(ILLEGAL_SELECTOR, "The expression cannot be a selector (occur after a dot)")
map.put(NO_RECEIVER_ALLOWED, "No receiver can be passed to this function or property")
// Supertypes // Supertypes
map.put(NOT_A_SUPERTYPE, "Not an immediate supertype") map.put(NOT_A_SUPERTYPE, "Not an immediate supertype")
@@ -363,6 +363,7 @@ private fun ConeSimpleDiagnostic.getFactory(source: FirSourceElement): FirDiagno
DiagnosticKind.RecursiveTypealiasExpansion -> FirErrors.RECURSIVE_TYPEALIAS_EXPANSION DiagnosticKind.RecursiveTypealiasExpansion -> FirErrors.RECURSIVE_TYPEALIAS_EXPANSION
DiagnosticKind.LoopInSupertype -> FirErrors.CYCLIC_INHERITANCE_HIERARCHY DiagnosticKind.LoopInSupertype -> FirErrors.CYCLIC_INHERITANCE_HIERARCHY
DiagnosticKind.IllegalSelector -> FirErrors.ILLEGAL_SELECTOR DiagnosticKind.IllegalSelector -> FirErrors.ILLEGAL_SELECTOR
DiagnosticKind.NoReceiverAllowed -> FirErrors.NO_RECEIVER_ALLOWED
DiagnosticKind.IsEnumEntry -> FirErrors.IS_ENUM_ENTRY DiagnosticKind.IsEnumEntry -> FirErrors.IS_ENUM_ENTRY
DiagnosticKind.EnumEntryAsType -> FirErrors.ENUM_ENTRY_AS_TYPE DiagnosticKind.EnumEntryAsType -> FirErrors.ENUM_ENTRY_AS_TYPE
DiagnosticKind.UnresolvedSupertype, DiagnosticKind.UnresolvedSupertype,
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.fir.lightTree.converter
import com.intellij.lang.LighterASTNode import com.intellij.lang.LighterASTNode
import com.intellij.psi.TokenType import com.intellij.psi.TokenType
import com.intellij.util.diff.FlyweightCapableTreeStructure import com.intellij.util.diff.FlyweightCapableTreeStructure
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.KtNodeTypes.* import org.jetbrains.kotlin.KtNodeTypes.*
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
@@ -504,15 +503,18 @@ class ExpressionsConverter(
val firExpression = val firExpression =
getAsFirExpression<FirExpression>(it, "Incorrect ${if (isEffectiveSelector) "selector" else "receiver"} expression") getAsFirExpression<FirExpression>(it, "Incorrect ${if (isEffectiveSelector) "selector" else "receiver"} expression")
if (isEffectiveSelector) { if (isEffectiveSelector) {
val callExpressionCallee = if (it.tokenType == CALL_EXPRESSION) it.getFirstChildExpressionUnwrapped() else null
firSelector = firSelector =
if (it.tokenType is KtNameReferenceExpressionElementType || it.tokenType == KtNodeTypes.CALL_EXPRESSION) { if (it.tokenType is KtNameReferenceExpressionElementType ||
(it.tokenType == CALL_EXPRESSION && callExpressionCallee?.tokenType != LAMBDA_EXPRESSION)
) {
firExpression firExpression
} else { } else {
buildErrorExpression { buildErrorExpression {
source = it.toFirSourceElement() source = callExpressionCallee?.toFirSourceElement() ?: it.toFirSourceElement()
diagnostic = ConeSimpleDiagnostic( diagnostic = ConeSimpleDiagnostic(
"The expression cannot be a selector (occur after a dot)", "The expression cannot be a selector (occur after a dot)",
DiagnosticKind.IllegalSelector if (callExpressionCallee == null) DiagnosticKind.IllegalSelector else DiagnosticKind.NoReceiverAllowed
) )
expression = firExpression expression = firExpression
} }
@@ -211,25 +211,31 @@ open class RawFirBuilder(
return buildExpressionStub() return buildExpressionStub()
} else { } else {
val result = this.convertSafe<FirExpression>() val result = this.convertSafe<FirExpression>()
if (result != null) { if (result != null) {
if (this != null && if (this == null) {
this !is KtNameReferenceExpression && return result
this !is KtCallExpression &&
this !is KtConstantExpression &&
getQualifiedExpressionForSelector() != null
) {
return buildErrorExpression {
source = toFirSourceElement()
diagnostic =
ConeSimpleDiagnostic(
"The expression cannot be a selector (occur after a dot)",
DiagnosticKind.IllegalSelector
)
expression = result
}
} }
return result val callExpressionCallee = (this as? KtCallExpression)?.calleeExpression?.unwrapParenthesesLabelsAndAnnotations()
if (this is KtNameReferenceExpression ||
this is KtConstantExpression ||
(this is KtCallExpression && callExpressionCallee !is KtLambdaExpression) ||
getQualifiedExpressionForSelector() == null
) {
return result
}
return buildErrorExpression {
source = callExpressionCallee?.toFirSourceElement() ?: toFirSourceElement()
diagnostic =
ConeSimpleDiagnostic(
"The expression cannot be a selector (occur after a dot)",
if (callExpressionCallee == null) DiagnosticKind.IllegalSelector else DiagnosticKind.NoReceiverAllowed
)
expression = result
}
} }
return buildErrorExpression( return buildErrorExpression(
@@ -23,6 +23,7 @@ enum class DiagnosticKind {
NoThis, NoThis,
IllegalConstExpression, IllegalConstExpression,
IllegalSelector, IllegalSelector,
NoReceiverAllowed,
IllegalUnderscore, IllegalUnderscore,
DeserializationError, DeserializationError,
InferenceError, InferenceError,
@@ -83,5 +83,5 @@ fun test() {
i.(<!UNRESOLVED_REFERENCE!>fun Int.() = 1<!>)(); i.(<!UNRESOLVED_REFERENCE!>fun Int.() = 1<!>)();
<!INAPPLICABLE_CANDIDATE!>{}<!><Int>() <!INAPPLICABLE_CANDIDATE!>{}<!><Int>()
1<!UNNECESSARY_SAFE_CALL!>?.<!>(<!UNRESOLVED_REFERENCE!>fun Int.() = 1<!>)() 1<!UNNECESSARY_SAFE_CALL!>?.<!>(<!UNRESOLVED_REFERENCE!>fun Int.() = 1<!>)()
1.<!UNRESOLVED_REFERENCE!>{}<!>() 1.<!NO_RECEIVER_ALLOWED!>{}<!>()
} }
@@ -321,6 +321,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token, token,
) )
} }
add(FirErrors.NO_RECEIVER_ALLOWED) { firDiagnostic ->
NoReceiverAllowedImpl(
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.SUPER_IS_NOT_AN_EXPRESSION) { firDiagnostic -> add(FirErrors.SUPER_IS_NOT_AN_EXPRESSION) { firDiagnostic ->
SuperIsNotAnExpressionImpl( SuperIsNotAnExpressionImpl(
firDiagnostic as FirPsiDiagnostic, firDiagnostic as FirPsiDiagnostic,
@@ -253,6 +253,10 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = IllegalSelector::class override val diagnosticClass get() = IllegalSelector::class
} }
abstract class NoReceiverAllowed : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = NoReceiverAllowed::class
}
abstract class SuperIsNotAnExpression : KtFirDiagnostic<PsiElement>() { abstract class SuperIsNotAnExpression : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = SuperIsNotAnExpression::class override val diagnosticClass get() = SuperIsNotAnExpression::class
} }
@@ -374,6 +374,13 @@ internal class IllegalSelectorImpl(
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
} }
internal class NoReceiverAllowedImpl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.NoReceiverAllowed(), KtAbstractFirDiagnostic<PsiElement> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class SuperIsNotAnExpressionImpl( internal class SuperIsNotAnExpressionImpl(
firDiagnostic: FirPsiDiagnostic, firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken, override val token: ValidityToken,