From 1b42298025e8dad028be71a2f956421dee322fdb Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Wed, 9 Nov 2022 18:44:57 +0200 Subject: [PATCH] [FIR] Implement IMPLICIT_NOTHING_*_TYPE diagnostics --- .../diagnostics/KtFirDataClassConverters.kt | 12 +++++++ .../api/fir/diagnostics/KtFirDiagnostics.kt | 8 +++++ .../fir/diagnostics/KtFirDiagnosticsImpl.kt | 10 ++++++ .../testData/resolveWithStdlib/problems.kt | 2 +- .../diagnostics/FirDiagnosticsList.kt | 3 ++ .../fir/analysis/diagnostics/FirErrors.kt | 2 ++ .../checkers/CommonDeclarationCheckers.kt | 1 + .../FirImplicitNothingReturnTypeChecker.kt | 34 +++++++++++++++++++ .../checkers/declaration/declarationUtils.kt | 6 ++++ .../diagnostics/FirErrorsDefaultMessages.kt | 5 +++ .../tests/FunctionReturnTypes.fir.kt | 2 +- .../ifToAnyDiscriminatingUsages.fir.kt | 4 +-- .../tests/controlStructures/kt10717.fir.kt | 4 +-- .../tests/controlStructures/kt799.fir.kt | 2 +- .../whenToAnyDiscriminatingUsages.fir.kt | 6 ++-- .../diagnostics/tests/implicitNothing.fir.kt | 20 +++++------ .../inferenceFromGetters/nullAsNothing.fir.kt | 2 +- .../return-expressions/p-4/neg/1.1.fir.kt | 4 +-- 18 files changed, 104 insertions(+), 23 deletions(-) create mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirImplicitNothingReturnTypeChecker.kt diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt index 26560357b02..26fe17949c8 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -1927,6 +1927,18 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.IMPLICIT_NOTHING_RETURN_TYPE) { firDiagnostic -> + ImplicitNothingReturnTypeImpl( + firDiagnostic as KtPsiDiagnostic, + token, + ) + } + add(FirErrors.IMPLICIT_NOTHING_PROPERTY_TYPE) { firDiagnostic -> + ImplicitNothingPropertyTypeImpl( + firDiagnostic as KtPsiDiagnostic, + token, + ) + } add(FirErrors.CYCLIC_GENERIC_UPPER_BOUND) { firDiagnostic -> CyclicGenericUpperBoundImpl( firDiagnostic as KtPsiDiagnostic, diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt index b5d721ff982..d991121749c 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt @@ -1371,6 +1371,14 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { abstract val isMismatchDueToNullability: Boolean } + abstract class ImplicitNothingReturnType : KtFirDiagnostic() { + override val diagnosticClass get() = ImplicitNothingReturnType::class + } + + abstract class ImplicitNothingPropertyType : KtFirDiagnostic() { + override val diagnosticClass get() = ImplicitNothingPropertyType::class + } + abstract class CyclicGenericUpperBound : KtFirDiagnostic() { override val diagnosticClass get() = CyclicGenericUpperBound::class } diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index b09fc0d7a11..26a594c2a6d 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -1648,6 +1648,16 @@ internal class ReturnTypeMismatchImpl( override val token: KtLifetimeToken, ) : KtFirDiagnostic.ReturnTypeMismatch(), KtAbstractFirDiagnostic +internal class ImplicitNothingReturnTypeImpl( + override val firDiagnostic: KtPsiDiagnostic, + override val token: KtLifetimeToken, +) : KtFirDiagnostic.ImplicitNothingReturnType(), KtAbstractFirDiagnostic + +internal class ImplicitNothingPropertyTypeImpl( + override val firDiagnostic: KtPsiDiagnostic, + override val token: KtLifetimeToken, +) : KtFirDiagnostic.ImplicitNothingPropertyType(), KtAbstractFirDiagnostic + internal class CyclicGenericUpperBoundImpl( override val firDiagnostic: KtPsiDiagnostic, override val token: KtLifetimeToken, diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/problems.kt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/problems.kt index 8956e799a0f..f342e552b35 100644 --- a/compiler/fir/analysis-tests/testData/resolveWithStdlib/problems.kt +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/problems.kt @@ -21,4 +21,4 @@ open class Base(val x: T) class Derived : Base(10) val xx = Derived().x + 1 -val t = throw AssertionError("") +val t = throw AssertionError("") 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 a77d2e3abcb..ad0e93a786d 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 @@ -653,6 +653,9 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { parameter("isMismatchDueToNullability") } + val IMPLICIT_NOTHING_RETURN_TYPE by error(PositioningStrategy.NAME_IDENTIFIER) + val IMPLICIT_NOTHING_PROPERTY_TYPE by error(PositioningStrategy.NAME_IDENTIFIER) + val CYCLIC_GENERIC_UPPER_BOUND by error() val DEPRECATED_TYPE_PARAMETER_SYNTAX by error(PositioningStrategy.TYPE_PARAMETERS_LIST) 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 756ed97df78..faa7cf40d3e 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 @@ -407,6 +407,8 @@ object FirErrors { val TYPE_PARAMETERS_NOT_ALLOWED by error0(SourceElementPositioningStrategies.TYPE_PARAMETERS_LIST) val TYPE_PARAMETER_OF_PROPERTY_NOT_USED_IN_RECEIVER by error0() val RETURN_TYPE_MISMATCH by error4(SourceElementPositioningStrategies.WHOLE_ELEMENT) + val IMPLICIT_NOTHING_RETURN_TYPE by error0(SourceElementPositioningStrategies.NAME_IDENTIFIER) + val IMPLICIT_NOTHING_PROPERTY_TYPE by error0(SourceElementPositioningStrategies.NAME_IDENTIFIER) val CYCLIC_GENERIC_UPPER_BOUND by error0() val DEPRECATED_TYPE_PARAMETER_SYNTAX by error0(SourceElementPositioningStrategies.TYPE_PARAMETERS_LIST) val MISPLACED_TYPE_PARAMETER_CONSTRAINTS by warning0() diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt index b12ccaf3549..4cbc6ab6e79 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt @@ -37,6 +37,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() { override val callableDeclarationCheckers: Set get() = setOf( FirKClassWithIncorrectTypeArgumentChecker, + FirImplicitNothingReturnTypeChecker, ) override val functionCheckers: Set diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirImplicitNothingReturnTypeChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirImplicitNothingReturnTypeChecker.kt new file mode 100644 index 00000000000..5580b21d929 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirImplicitNothingReturnTypeChecker.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.analysis.checkers.declaration + +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.reportOn +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors +import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration +import org.jetbrains.kotlin.fir.declarations.FirProperty +import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction +import org.jetbrains.kotlin.fir.declarations.utils.isOverride +import org.jetbrains.kotlin.fir.types.coneType +import org.jetbrains.kotlin.fir.types.isNothing + +object FirImplicitNothingReturnTypeChecker : FirCallableDeclarationChecker() { + override fun check(declaration: FirCallableDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { + if (declaration !is FirSimpleFunction && declaration !is FirProperty) return + if (declaration is FirProperty && declaration.isLocal) return + if (declaration.isOverride) return + if (declaration.symbol.hasExplicitReturnType) return + if (declaration.returnTypeRef.coneType.isNothing) { + val factory = when (declaration) { + is FirSimpleFunction -> FirErrors.IMPLICIT_NOTHING_RETURN_TYPE + is FirProperty -> FirErrors.IMPLICIT_NOTHING_PROPERTY_TYPE + else -> error("Should not be here") + } + reporter.reportOn(declaration.source, factory, context) + } + } +} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/declarationUtils.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/declarationUtils.kt index 8cf7979efb2..dfda9405292 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/declarationUtils.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/declarationUtils.kt @@ -19,6 +19,7 @@ import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.types.coneType +import org.jetbrains.kotlin.fir.types.impl.FirImplicitUnitTypeRef import org.jetbrains.kotlin.fir.types.isBoolean import org.jetbrains.kotlin.fir.types.isNothing import org.jetbrains.kotlin.fir.types.replaceArgumentsWithStarProjections @@ -130,3 +131,8 @@ fun FirSimpleFunction.isTypedEqualsInInlineClass(session: FirSession): Boolean = } } ?: false +val FirCallableSymbol<*>.hasExplicitReturnType: Boolean + get() { + val returnTypeRef = resolvedReturnTypeRef + return returnTypeRef.delegatedTypeRef != null || returnTypeRef is FirImplicitUnitTypeRef + } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt index d5c66bc07fa..a445fd2d100 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt @@ -236,6 +236,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ILLEGAL_SUSPEND_F import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ILLEGAL_SUSPEND_PROPERTY_ACCESS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ILLEGAL_UNDERSCORE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.IMPLEMENTATION_BY_DELEGATION_IN_EXPECT_CLASS +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.IMPLICIT_NOTHING_PROPERTY_TYPE +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.IMPLICIT_NOTHING_RETURN_TYPE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INAPPLICABLE_CANDIDATE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INAPPLICABLE_FILE_TARGET import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INAPPLICABLE_INFIX_MODIFIER @@ -1220,6 +1222,9 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() { NOT_RENDERED ) + map.put(IMPLICIT_NOTHING_RETURN_TYPE, "'Nothing' return type needs to be specified explicitly") + map.put(IMPLICIT_NOTHING_PROPERTY_TYPE, "'Nothing' property type needs to be specified explicitly"); + map.put(CYCLIC_GENERIC_UPPER_BOUND, "Type parameter has cyclic upper bounds") map.put(DEPRECATED_TYPE_PARAMETER_SYNTAX, "Type parameters must be placed before the name of the function") diff --git a/compiler/testData/diagnostics/tests/FunctionReturnTypes.fir.kt b/compiler/testData/diagnostics/tests/FunctionReturnTypes.fir.kt index 610aebd5baf..7e93bdab7e6 100644 --- a/compiler/testData/diagnostics/tests/FunctionReturnTypes.fir.kt +++ b/compiler/testData/diagnostics/tests/FunctionReturnTypes.fir.kt @@ -136,7 +136,7 @@ fun blockNoReturnIfUnitInOneBranch(): Int { fun nonBlockReturnIfEmptyIf(): Int = if (1 < 2) {} else {} fun nonBlockNoReturnIfUnitInOneBranch(): Int = if (1 < 2) {} else 2 -val a = return 1 +val a = return 1 class A() { } diff --git a/compiler/testData/diagnostics/tests/controlStructures/ifToAnyDiscriminatingUsages.fir.kt b/compiler/testData/diagnostics/tests/controlStructures/ifToAnyDiscriminatingUsages.fir.kt index 08cfe1922d8..e1ada18d66e 100644 --- a/compiler/testData/diagnostics/tests/controlStructures/ifToAnyDiscriminatingUsages.fir.kt +++ b/compiler/testData/diagnostics/tests/controlStructures/ifToAnyDiscriminatingUsages.fir.kt @@ -30,13 +30,13 @@ fun testReturnFromAnonFun() = return if (true) 42 else println() }) -fun testReturn1() = +fun testReturn1() = run { return if (true) 42 else println() } -fun testReturn2() = +fun testReturn2() = run { return if (true) 42 else if (true) 42 diff --git a/compiler/testData/diagnostics/tests/controlStructures/kt10717.fir.kt b/compiler/testData/diagnostics/tests/controlStructures/kt10717.fir.kt index fd092171295..1b01901d694 100644 --- a/compiler/testData/diagnostics/tests/controlStructures/kt10717.fir.kt +++ b/compiler/testData/diagnostics/tests/controlStructures/kt10717.fir.kt @@ -1,10 +1,10 @@ // !DIAGNOSTICS: -UNUSED_EXPRESSION -UNREACHABLE_CODE -UNUSED_PARAMETER -RETURN_NOT_ALLOWED -fun test1() = run { +fun test1() = run { return "OK" } -fun test2() = run { +fun test2() = run { fun local(): String { return "" } diff --git a/compiler/testData/diagnostics/tests/controlStructures/kt799.fir.kt b/compiler/testData/diagnostics/tests/controlStructures/kt799.fir.kt index 9035778dd09..735cec3ae46 100644 --- a/compiler/testData/diagnostics/tests/controlStructures/kt799.fir.kt +++ b/compiler/testData/diagnostics/tests/controlStructures/kt799.fir.kt @@ -14,7 +14,7 @@ fun test() { val a : Nothing = return 1 -val b = return 1 +val b = return 1 val c = doSmth(if (true) 3 else return) diff --git a/compiler/testData/diagnostics/tests/controlStructures/whenToAnyDiscriminatingUsages.fir.kt b/compiler/testData/diagnostics/tests/controlStructures/whenToAnyDiscriminatingUsages.fir.kt index 3f15aab9809..f8e7162fd5a 100644 --- a/compiler/testData/diagnostics/tests/controlStructures/whenToAnyDiscriminatingUsages.fir.kt +++ b/compiler/testData/diagnostics/tests/controlStructures/whenToAnyDiscriminatingUsages.fir.kt @@ -24,7 +24,7 @@ fun testResultOfLambda2() = } } -fun testReturn1() = +fun testReturn1() = run { return when { true -> 42 @@ -32,7 +32,7 @@ fun testReturn1() = } } -fun testReturn2() = +fun testReturn2() = run { return when { true -> 42 @@ -80,4 +80,4 @@ val testUsage5: Any get() = when { true -> 42 else -> println() - } \ No newline at end of file + } diff --git a/compiler/testData/diagnostics/tests/implicitNothing.fir.kt b/compiler/testData/diagnostics/tests/implicitNothing.fir.kt index 75018f55018..a6c2bd1fcb4 100644 --- a/compiler/testData/diagnostics/tests/implicitNothing.fir.kt +++ b/compiler/testData/diagnostics/tests/implicitNothing.fir.kt @@ -1,37 +1,37 @@ -fun foo() = throw Exception() +fun foo() = throw Exception() -fun bar() = null!! +fun bar() = null!! -fun baz() = bar() +fun baz() = bar() fun gav(): Any = null!! -val x = null!! +val x = null!! val y: Nothing = throw Exception() fun check() { // Error: KT-10449 - fun local() = bar() + fun local() = bar() // Unreachable / unused, but not implicit Nothing val x = null!! } -fun nonLocalReturn() = run { return } +fun nonLocalReturn() = run { return } class Klass { - fun bar() = null!! + fun bar() = null!! - val y = null!! + val y = null!! init { - fun local() = bar() + fun local() = bar() // Should be unreachable: see KT-5311 val z = null!! } fun foo() { - fun local() = bar() + fun local() = bar() val x = y } diff --git a/compiler/testData/diagnostics/tests/properties/inferenceFromGetters/nullAsNothing.fir.kt b/compiler/testData/diagnostics/tests/properties/inferenceFromGetters/nullAsNothing.fir.kt index d0efc02d6d9..75a77f33ceb 100644 --- a/compiler/testData/diagnostics/tests/properties/inferenceFromGetters/nullAsNothing.fir.kt +++ b/compiler/testData/diagnostics/tests/properties/inferenceFromGetters/nullAsNothing.fir.kt @@ -1,6 +1,6 @@ // !CHECK_TYPE val x get() = null -val y get() = null!! +val y get() = null!! fun foo() { x checkType { _() } diff --git a/compiler/tests-spec/testData/diagnostics/linked/expressions/jump-expressions/return-expressions/p-4/neg/1.1.fir.kt b/compiler/tests-spec/testData/diagnostics/linked/expressions/jump-expressions/return-expressions/p-4/neg/1.1.fir.kt index bbc290f75e9..beddc179020 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/expressions/jump-expressions/return-expressions/p-4/neg/1.1.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/expressions/jump-expressions/return-expressions/p-4/neg/1.1.fir.kt @@ -4,8 +4,8 @@ // TESTCASE NUMBER: 1 // UNEXPECTED BEHAVIOUR // ISSUES : KT-35545 -fun case1(a: Boolean) = run { println("d"); return true } +fun case1(a: Boolean) = run { println("d"); return true } // TESTCASE NUMBER: 2 -val case2 +val case2 get() = run { println("d"); return true }