diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index f0bb0c29ead..b4aa78328c0 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -106,6 +106,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { parameter("functionName") parameter("hasValueParameters") } + val ILLEGAL_SELECTOR by error() } val SUPER by object : DiagnosticGroup("Super") { diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index fc0df8bf2c9..b35273a1143 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -120,6 +120,7 @@ object FirErrors { // Call resolution val CREATING_AN_INSTANCE_OF_ABSTRACT_CLASS by error0() val FUNCTION_CALL_EXPECTED by error2(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED) + val ILLEGAL_SELECTOR by error0() // Super val SUPER_IS_NOT_AN_EXPRESSION by error0(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt index d45b54263fd..3cd13138b71 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt @@ -579,8 +579,9 @@ fun checkTypeMismatch( } internal fun checkCondition(condition: FirExpression, context: CheckerContext, reporter: DiagnosticReporter) { - val coneType = condition.typeRef.coneType.lowerBoundIfFlexible() - if (coneType !is ConeKotlinErrorType && + val coneType = condition.typeRef.coneTypeSafe()?.lowerBoundIfFlexible() + if (coneType != null && + coneType !is ConeKotlinErrorType && !coneType.isSubtypeOf(context.session.typeContext, context.session.builtinTypes.booleanType.type) ) { reporter.reportOn(condition.source, FirErrors.CONDITION_TYPE_MISMATCH, coneType, context) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt index 7f0bd390fcf..2e0c302afd6 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt @@ -163,6 +163,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.GETTER_VISIBILITY import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.HAS_NEXT_FUNCTION_AMBIGUITY import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ILLEGAL_CONST_EXPRESSION import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ILLEGAL_KOTLIN_VERSION_STRING_VALUE +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ILLEGAL_SELECTOR import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ILLEGAL_UNDERSCORE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INAPPLICABLE_CANDIDATE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INAPPLICABLE_FILE_TARGET @@ -446,6 +447,7 @@ class FirDefaultErrorMessages { ) 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(ILLEGAL_SELECTOR, "The expression cannot be a selector (occur after a dot)") // Supertypes map.put(ENUM_AS_SUPERTYPE, "Enum as supertype") 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 ae12aa52752..53b1ffe8dbb 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 @@ -360,6 +360,7 @@ private fun ConeSimpleDiagnostic.getFactory(source: FirSourceElement): FirDiagno DiagnosticKind.IllegalEscape -> FirErrors.ILLEGAL_ESCAPE DiagnosticKind.RecursiveTypealiasExpansion -> FirErrors.RECURSIVE_TYPEALIAS_EXPANSION DiagnosticKind.LoopInSupertype -> FirErrors.CYCLIC_INHERITANCE_HIERARCHY + DiagnosticKind.IllegalSelector -> FirErrors.ILLEGAL_SELECTOR DiagnosticKind.UnresolvedSupertype, DiagnosticKind.UnresolvedExpandedType, DiagnosticKind.Other -> FirErrors.OTHER_ERROR 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 9103b8b256f..4cd5826035b 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 @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.lightTree.converter import com.intellij.lang.LighterASTNode import com.intellij.psi.TokenType import com.intellij.util.diff.FlyweightCapableTreeStructure +import org.jetbrains.kotlin.KtNodeTypes import org.jetbrains.kotlin.KtNodeTypes.* import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Visibilities @@ -41,6 +42,7 @@ import org.jetbrains.kotlin.fir.types.FirTypeProjection import org.jetbrains.kotlin.fir.types.FirTypeRef import org.jetbrains.kotlin.lexer.KtTokens.* import org.jetbrains.kotlin.psi.stubs.elements.KtConstantExpressionElementType +import org.jetbrains.kotlin.psi.stubs.elements.KtNameReferenceExpressionElementType import org.jetbrains.kotlin.types.ConstantValueKind import org.jetbrains.kotlin.types.expressions.OperatorConventions import org.jetbrains.kotlin.util.OperatorNameConventions @@ -196,7 +198,7 @@ class ExpressionsConverter( buildSingleExpressionBlock(buildErrorExpression(null, ConeSimpleDiagnostic("Lambda has no body", DiagnosticKind.Syntax))) } context.firFunctionTargets.removeLast() - }.also { + }.also { target.bind(it) } return buildAnonymousFunctionExpression { @@ -306,7 +308,7 @@ class ExpressionsConverter( else -> if (it.isExpression()) leftArgAsFir = getAsFirExpression(it, "No left operand") } } - + return buildTypeOperatorCall { source = binaryExpression.toFirSourceElement() operation = operationTokenName.toFirOperation() @@ -478,7 +480,7 @@ class ExpressionsConverter( private fun convertQualifiedExpression(dotQualifiedExpression: LighterASTNode): FirExpression { var isSelector = false var isSafe = false - var firSelector: FirExpression = buildErrorExpression(null, ConeSimpleDiagnostic("Qualified expression without selector", DiagnosticKind.Syntax)) //after dot + var firSelector: FirExpression? = null var firReceiver: FirExpression? = null //before dot dotQualifiedExpression.forEachChildren { when (it.tokenType) { @@ -488,10 +490,26 @@ class ExpressionsConverter( isSelector = true } else -> { - if (isSelector && it.tokenType != TokenType.ERROR_ELEMENT) - firSelector = getAsFirExpression(it, "Incorrect selector expression") - else - firReceiver = getAsFirExpression(it, "Incorrect receiver expression") + val isEffectiveSelector = isSelector && it.tokenType != TokenType.ERROR_ELEMENT + val firExpression = + getAsFirExpression(it, "Incorrect ${if (isEffectiveSelector) "selector" else "receiver"} expression") + if (isEffectiveSelector) { + firSelector = + if (it.tokenType is KtNameReferenceExpressionElementType || it.tokenType == KtNodeTypes.CALL_EXPRESSION) { + firExpression + } else { + buildErrorExpression { + source = it.toFirSourceElement() + diagnostic = ConeSimpleDiagnostic( + "The expression cannot be a selector (occur after a dot)", + DiagnosticKind.IllegalSelector + ) + expression = firExpression + } + } + } else { + firReceiver = firExpression + } } } } @@ -511,7 +529,11 @@ class ExpressionsConverter( @OptIn(FirImplementationDetail::class) it.replaceSource(dotQualifiedExpression.toFirSourceElement()) } - return firSelector + + return firSelector ?: buildErrorExpression( + null, + ConeSimpleDiagnostic("Qualified expression without selector", DiagnosticKind.Syntax) + ) } /** @@ -687,7 +709,7 @@ class ExpressionsConverter( buildWhenBranch { source = entrySource condition = firCondition - result = branch + result = branch } } else { val firCondition = entry.toFirWhenConditionWithoutSubject() 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 1d244a5ea7b..2af2395939f 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 @@ -33,10 +33,7 @@ import org.jetbrains.kotlin.fir.scopes.FirScopeProvider import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.builder.* -import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl -import org.jetbrains.kotlin.fir.types.impl.FirQualifierPartImpl -import org.jetbrains.kotlin.fir.types.impl.FirTypeArgumentListImpl -import org.jetbrains.kotlin.fir.types.impl.FirTypePlaceholderProjection +import org.jetbrains.kotlin.fir.types.impl.* import org.jetbrains.kotlin.lexer.KtTokens.* import org.jetbrains.kotlin.name.CallableId import org.jetbrains.kotlin.name.Name @@ -201,11 +198,37 @@ open class RawFirBuilder( private fun KtExpression?.toFirExpression( errorReason: String, kind: DiagnosticKind = DiagnosticKind.ExpressionExpected, - ): FirExpression = - if (stubMode) buildExpressionStub() - else convertSafe() ?: buildErrorExpression( - this?.toFirSourceElement(), ConeSimpleDiagnostic(errorReason, kind), - ) + ): FirExpression { + if (stubMode) { + return buildExpressionStub() + } else { + val result = this.convertSafe() + if (result != null) { + if (this != null && + this !is KtNameReferenceExpression && + 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 + } + + return buildErrorExpression( + this?.toFirSourceElement(), ConeSimpleDiagnostic(errorReason, kind), + ) + } + } private inline fun KtExpression.toFirStatement(errorReasonLazy: () -> String): FirStatement = convertSafe() ?: buildErrorExpression(this.toFirSourceElement(), ConeSimpleDiagnostic(errorReasonLazy(), DiagnosticKind.Syntax)) 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 da4d407ff09..fddf4110465 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 @@ -22,6 +22,7 @@ enum class DiagnosticKind { UnresolvedLabel, NoThis, IllegalConstExpression, + IllegalSelector, IllegalUnderscore, DeserializationError, InferenceError, diff --git a/compiler/testData/diagnostics/tests/FunctionCalleeExpressions.fir.kt b/compiler/testData/diagnostics/tests/FunctionCalleeExpressions.fir.kt index c138a76b569..4d6b87cc625 100644 --- a/compiler/testData/diagnostics/tests/FunctionCalleeExpressions.fir.kt +++ b/compiler/testData/diagnostics/tests/FunctionCalleeExpressions.fir.kt @@ -70,9 +70,9 @@ fun main1() { 1."sdf"() - 1."sdf" - 1.{} - 1.if (true) {} + 1."sdf" + 1.{} + 1.if (true) {} } fun test() { diff --git a/compiler/testData/diagnostics/tests/functionLiterals/DeprecatedSyntax.fir.kt b/compiler/testData/diagnostics/tests/functionLiterals/DeprecatedSyntax.fir.kt index 83cdfd7f608..cc8a8b93bde 100644 --- a/compiler/testData/diagnostics/tests/functionLiterals/DeprecatedSyntax.fir.kt +++ b/compiler/testData/diagnostics/tests/functionLiterals/DeprecatedSyntax.fir.kt @@ -1,5 +1,5 @@ val receiver = { Int.() -> } -val receiverWithParameter = { Int.(a) -> } +val receiverWithParameter = { Int.(a) -> } val receiverAndReturnType = { Int.(): Int -> 5 } val receiverAndReturnTypeWithParameter = { Int.(a: Int): Int -> 5 } diff --git a/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/declarationAfterDotSelectorExpected.fir.kt b/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/declarationAfterDotSelectorExpected.fir.kt deleted file mode 100644 index c983234e53f..00000000000 --- a/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/declarationAfterDotSelectorExpected.fir.kt +++ /dev/null @@ -1,29 +0,0 @@ -// !DIAGNOSTICS: -UNUSED_VARIABLE - -fun foo(x: Any) { - x. - val foo = 1 - - x. - fun bar() = 2 - - x. - fun String.() = 3 - - var a = 24. - var b = 42.0 -} - -class A { - val z = "a". - val x = 4 - - val y = "b". - fun baz() = 5 - - val q = "c". - fun String.() = 6 - - var a = 24. - var b = 42.0 -} diff --git a/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/declarationAfterDotSelectorExpected.kt b/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/declarationAfterDotSelectorExpected.kt index 3d8902c35ac..976923cb658 100644 --- a/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/declarationAfterDotSelectorExpected.kt +++ b/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/declarationAfterDotSelectorExpected.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_VARIABLE fun foo(x: Any) { diff --git a/compiler/testData/diagnostics/tests/incompleteCode/illegalSelectorCallableReference.fir.kt b/compiler/testData/diagnostics/tests/incompleteCode/illegalSelectorCallableReference.fir.kt index fd1c9dba1ec..f16f4271f63 100644 --- a/compiler/testData/diagnostics/tests/incompleteCode/illegalSelectorCallableReference.fir.kt +++ b/compiler/testData/diagnostics/tests/incompleteCode/illegalSelectorCallableReference.fir.kt @@ -1,8 +1,8 @@ // !DIAGNOSTICS: -UNUSED_EXPRESSION fun test() { - "a"."b"::foo - "a"."b"::class - "a"."b"."c"::foo - "a"."b"."c"::class + "a"."b"::foo + "a"."b"::class + "a"."b"."c"::foo + "a"."b"."c"::class } diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt index 3318af34a0d..0a64c401e23 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -291,6 +291,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.ILLEGAL_SELECTOR) { firDiagnostic -> + IllegalSelectorImpl( + firDiagnostic as FirPsiDiagnostic, + token, + ) + } add(FirErrors.SUPER_IS_NOT_AN_EXPRESSION) { firDiagnostic -> SuperIsNotAnExpressionImpl( firDiagnostic as FirPsiDiagnostic, diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt index 77631895e13..954073776e9 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt @@ -226,6 +226,10 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { abstract val hasValueParameters: Boolean } + abstract class IllegalSelector : KtFirDiagnostic() { + override val diagnosticClass get() = IllegalSelector::class + } + abstract class SuperIsNotAnExpression : KtFirDiagnostic() { override val diagnosticClass get() = SuperIsNotAnExpression::class } diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 403ca129994..0608db6655c 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -338,6 +338,13 @@ internal class FunctionCallExpectedImpl( override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) } +internal class IllegalSelectorImpl( + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.IllegalSelector(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} + internal class SuperIsNotAnExpressionImpl( firDiagnostic: FirPsiDiagnostic, override val token: ValidityToken,