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 6687967b286..25500a4de10 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 @@ -938,6 +938,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { parameter("lambda") } val WRONG_IMPLIES_CONDITION by warning() + val VARIABLE_WITH_NO_TYPE_NO_INITIALIZER by error(PositioningStrategy.DECLARATION_NAME) } val NULLABILITY by object : DiagnosticGroup("Nullability") { 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 bdfc23ad659..f0f6cca1f7d 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 @@ -69,6 +69,7 @@ import org.jetbrains.kotlin.psi.KtTypeParameter import org.jetbrains.kotlin.psi.KtTypeProjection import org.jetbrains.kotlin.psi.KtTypeReference import org.jetbrains.kotlin.psi.KtValueArgument +import org.jetbrains.kotlin.psi.KtVariableDeclaration import org.jetbrains.kotlin.psi.KtWhenCondition import org.jetbrains.kotlin.psi.KtWhenEntry import org.jetbrains.kotlin.psi.KtWhenExpression @@ -496,6 +497,7 @@ object FirErrors { val WRONG_INVOCATION_KIND by warning3, EventOccurrencesRange, EventOccurrencesRange>() val LEAKED_IN_PLACE_LAMBDA by error1>() val WRONG_IMPLIES_CONDITION by warning0() + val VARIABLE_WITH_NO_TYPE_NO_INITIALIZER by error0(SourceElementPositioningStrategies.DECLARATION_NAME) // Nullability val UNSAFE_CALL by error2(SourceElementPositioningStrategies.DOT_BY_QUALIFIED) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirDeclarationCheckerUtils.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirDeclarationCheckerUtils.kt index 1bfcaf995ad..7e130495752 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirDeclarationCheckerUtils.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirDeclarationCheckerUtils.kt @@ -21,8 +21,10 @@ import org.jetbrains.kotlin.fir.containingClassAttr import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor import org.jetbrains.kotlin.fir.declarations.utils.* +import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeLocalVariableNoTypeOrInitializer import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.impl.* +import org.jetbrains.kotlin.fir.types.FirErrorTypeRef import org.jetbrains.kotlin.fir.types.FirImplicitTypeRef import org.jetbrains.kotlin.lexer.KtTokens @@ -106,7 +108,12 @@ internal fun checkPropertyInitializer( val hasAbstractModifier = KtTokens.ABSTRACT_KEYWORD in modifierList val isAbstract = property.isAbstract || hasAbstractModifier if (isAbstract) { - if (property.initializer == null && property.delegate == null && property.returnTypeRef is FirImplicitTypeRef) { + val returnTypeRef = property.returnTypeRef + if (property.initializer == null && + property.delegate == null && + (returnTypeRef is FirImplicitTypeRef || + (returnTypeRef is FirErrorTypeRef && returnTypeRef.diagnostic is ConeLocalVariableNoTypeOrInitializer)) + ) { property.source?.let { reporter.reportOn(it, FirErrors.PROPERTY_WITH_NO_TYPE_NO_INITIALIZER, 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 022ab0e9321..9d8fd9996b6 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 @@ -416,6 +416,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VARARG_OUTSIDE_PA import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VARIABLE_EXPECTED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VARIABLE_INITIALIZER_IS_REDUNDANT import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VARIABLE_NEVER_READ +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VARIABLE_WITH_NO_TYPE_NO_INITIALIZER import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VARIANCE_ON_TYPE_PARAMETER_NOT_ALLOWED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VAR_ANNOTATION_PARAMETER import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VAR_OVERRIDDEN_BY_VAL @@ -1128,6 +1129,8 @@ class FirDefaultErrorMessages { "This property must either have a type annotation, be initialized or be delegated" ) + map.put(VARIABLE_WITH_NO_TYPE_NO_INITIALIZER, "This variable must either have a type annotation or be initialized") + map.put(MUST_BE_INITIALIZED, "Property must be initialized") map.put(MUST_BE_INITIALIZED_OR_BE_ABSTRACT, "Property must be initialized or be abstract") map.put(EXTENSION_PROPERTY_MUST_HAVE_ACCESSORS_OR_BE_ABSTRACT, "Extension property must have accessors or be abstract") 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 39be7eeddfa..3ca9dd403af 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 @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.analysis.diagnostics import org.jetbrains.kotlin.KtNodeTypes import org.jetbrains.kotlin.fir.FirFakeSourceElementKind import org.jetbrains.kotlin.fir.FirSourceElement +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.isLocalMember import org.jetbrains.kotlin.fir.analysis.getChild import org.jetbrains.kotlin.fir.declarations.utils.isInfix import org.jetbrains.kotlin.fir.declarations.utils.isOperator @@ -29,6 +30,7 @@ import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability import org.jetbrains.kotlin.resolve.calls.tower.isSuccess import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance +import org.jetbrains.kotlin.utils.addToStdlib.runIf private fun ConeDiagnostic.toFirDiagnostic( source: FirSourceElement, @@ -86,6 +88,8 @@ private fun ConeDiagnostic.toFirDiagnostic( is ConeNotAnnotationContainer -> null is ConeImportFromSingleton -> FirErrors.CANNOT_ALL_UNDER_IMPORT_FROM_SINGLETON.createOn(source, this.name) is ConeUnsupportedDynamicType -> FirErrors.UNSUPPORTED.createOn(source, this.reason) + is ConeLocalVariableNoTypeOrInitializer -> + runIf(variable.isLocalMember) { FirErrors.VARIABLE_WITH_NO_TYPE_NO_INITIALIZER.createOn(source) } else -> throw IllegalArgumentException("Unsupported diagnostic type: ${this.javaClass}") } 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 82deed64986..1b9f5ba35bb 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 @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.fir.FirSourceElement import org.jetbrains.kotlin.fir.declarations.Deprecation import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration 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.render import org.jetbrains.kotlin.fir.resolve.calls.Candidate @@ -152,6 +153,10 @@ class ConeDeprecated(val source: FirSourceElement?, val symbol: FirBasedSymbol<* override val reason: String get() = "Deprecated: ${deprecation.message}" } +class ConeLocalVariableNoTypeOrInitializer(val variable: FirVariable) : ConeDiagnostic() { + override val reason: String get() = "Cannot infer variable type without initializer / getter / delegate" +} + private fun describeSymbol(symbol: FirBasedSymbol<*>): String { return when (symbol) { is FirClassLikeSymbol<*> -> symbol.classId.asString() diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt index 78a432d2735..7a7bb6ad876 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate import org.jetbrains.kotlin.fir.resolve.constructFunctionalTypeRef import org.jetbrains.kotlin.fir.resolve.dfa.FirControlFlowGraphReferenceImpl import org.jetbrains.kotlin.fir.resolve.dfa.unwrapSmartcastExpression +import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeLocalVariableNoTypeOrInitializer import org.jetbrains.kotlin.fir.resolve.inference.extractLambdaInfoFromFunctionalType import org.jetbrains.kotlin.fir.resolve.inference.isSuspendFunctionType import org.jetbrains.kotlin.fir.resolve.mode @@ -841,10 +842,8 @@ open class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransfor transformer, withExpectedType( buildErrorTypeRef { - diagnostic = ConeSimpleDiagnostic( - "Cannot infer variable type without initializer / getter / delegate", - DiagnosticKind.InferenceError, - ) + diagnostic = ConeLocalVariableNoTypeOrInitializer(variable) + source = variable.source }, ) ) diff --git a/compiler/testData/diagnostics/tests/declarationChecks/LocalVariableWithNoTypeInformation.fir.kt b/compiler/testData/diagnostics/tests/declarationChecks/LocalVariableWithNoTypeInformation.fir.kt deleted file mode 100644 index 12509208784..00000000000 --- a/compiler/testData/diagnostics/tests/declarationChecks/LocalVariableWithNoTypeInformation.fir.kt +++ /dev/null @@ -1,3 +0,0 @@ -fun test() { - val a -} diff --git a/compiler/testData/diagnostics/tests/declarationChecks/LocalVariableWithNoTypeInformation.kt b/compiler/testData/diagnostics/tests/declarationChecks/LocalVariableWithNoTypeInformation.kt index 809877f2071..6ff61caac7e 100644 --- a/compiler/testData/diagnostics/tests/declarationChecks/LocalVariableWithNoTypeInformation.kt +++ b/compiler/testData/diagnostics/tests/declarationChecks/LocalVariableWithNoTypeInformation.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL fun test() { val a } diff --git a/compiler/testData/diagnostics/tests/declarationChecks/kt2096.fir.kt b/compiler/testData/diagnostics/tests/declarationChecks/kt2096.fir.kt deleted file mode 100644 index 94875fba26f..00000000000 --- a/compiler/testData/diagnostics/tests/declarationChecks/kt2096.fir.kt +++ /dev/null @@ -1,7 +0,0 @@ -//KT-2096 Abstract property with no type specified causes compiler to crash - -package c - -abstract class Foo{ - protected abstract val prop -} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/declarationChecks/kt2096.kt b/compiler/testData/diagnostics/tests/declarationChecks/kt2096.kt index f51c275607f..c6900bfe897 100644 --- a/compiler/testData/diagnostics/tests/declarationChecks/kt2096.kt +++ b/compiler/testData/diagnostics/tests/declarationChecks/kt2096.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL //KT-2096 Abstract property with no type specified causes compiler to crash package c diff --git a/compiler/testData/diagnostics/tests/declarationChecks/localDeclarationModifiers.fir.kt b/compiler/testData/diagnostics/tests/declarationChecks/localDeclarationModifiers.fir.kt index 6b7a7bc265a..aaeada4539b 100644 --- a/compiler/testData/diagnostics/tests/declarationChecks/localDeclarationModifiers.fir.kt +++ b/compiler/testData/diagnostics/tests/declarationChecks/localDeclarationModifiers.fir.kt @@ -6,7 +6,7 @@ class T { fun foo() { public val i = 11 - abstract val j + abstract val j override fun T.baz() = 2 private fun bar() = 2 -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/tests/when/withSubjectVariable/unsupportedVariableDeclarationsInWhenSubject.fir.kt b/compiler/testData/diagnostics/tests/when/withSubjectVariable/unsupportedVariableDeclarationsInWhenSubject.fir.kt index 803b4e0922c..a729ab6b429 100644 --- a/compiler/testData/diagnostics/tests/when/withSubjectVariable/unsupportedVariableDeclarationsInWhenSubject.fir.kt +++ b/compiler/testData/diagnostics/tests/when/withSubjectVariable/unsupportedVariableDeclarationsInWhenSubject.fir.kt @@ -23,6 +23,6 @@ fun testVarInWhenSubject() { } fun testDelegatedValInWhenSubject() { - when (val y by lazy { 42 }) { + when (val y by lazy { 42 }) { } } 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 e2cba0e4f6d..924ca3204b1 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 @@ -54,6 +54,7 @@ import org.jetbrains.kotlin.psi.KtTypeParameter import org.jetbrains.kotlin.psi.KtTypeProjection import org.jetbrains.kotlin.psi.KtTypeReference import org.jetbrains.kotlin.psi.KtValueArgument +import org.jetbrains.kotlin.psi.KtVariableDeclaration import org.jetbrains.kotlin.psi.KtWhenCondition import org.jetbrains.kotlin.psi.KtWhenEntry import org.jetbrains.kotlin.psi.KtWhenExpression @@ -2547,6 +2548,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.VARIABLE_WITH_NO_TYPE_NO_INITIALIZER) { firDiagnostic -> + VariableWithNoTypeNoInitializerImpl( + firDiagnostic as FirPsiDiagnostic, + token, + ) + } add(FirErrors.UNSAFE_CALL) { firDiagnostic -> UnsafeCallImpl( firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a), 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 da7494f19bd..f06434b5320 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 @@ -63,6 +63,7 @@ import org.jetbrains.kotlin.psi.KtTypeParameter import org.jetbrains.kotlin.psi.KtTypeProjection import org.jetbrains.kotlin.psi.KtTypeReference import org.jetbrains.kotlin.psi.KtValueArgument +import org.jetbrains.kotlin.psi.KtVariableDeclaration import org.jetbrains.kotlin.psi.KtWhenCondition import org.jetbrains.kotlin.psi.KtWhenEntry import org.jetbrains.kotlin.psi.KtWhenExpression @@ -1785,6 +1786,10 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = WrongImpliesCondition::class } + abstract class VariableWithNoTypeNoInitializer : KtFirDiagnostic() { + override val diagnosticClass get() = VariableWithNoTypeNoInitializer::class + } + abstract class UnsafeCall : KtFirDiagnostic() { override val diagnosticClass get() = UnsafeCall::class abstract val receiverType: KtType 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 7ce3f381783..6386a68d78a 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 @@ -65,6 +65,7 @@ import org.jetbrains.kotlin.psi.KtTypeParameter import org.jetbrains.kotlin.psi.KtTypeProjection import org.jetbrains.kotlin.psi.KtTypeReference import org.jetbrains.kotlin.psi.KtValueArgument +import org.jetbrains.kotlin.psi.KtVariableDeclaration import org.jetbrains.kotlin.psi.KtWhenCondition import org.jetbrains.kotlin.psi.KtWhenEntry import org.jetbrains.kotlin.psi.KtWhenExpression @@ -2875,6 +2876,13 @@ internal class WrongImpliesConditionImpl( override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) } +internal class VariableWithNoTypeNoInitializerImpl( + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.VariableWithNoTypeNoInitializer(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} + internal class UnsafeCallImpl( override val receiverType: KtType, override val receiverExpression: KtExpression?,