From 66e052b9b358ef0058cceb794f95d13765a43197 Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Sun, 11 Jul 2021 23:49:58 +0300 Subject: [PATCH] [FIR] Implement SPREAD_OF_NULLABLE --- .../diagnostics/FirDiagnosticsList.kt | 4 +- .../diagnostics/model/DiagnosticData.kt | 1 + .../fir/analysis/diagnostics/FirErrors.kt | 1 + .../checkers/CommonExpressionCheckers.kt | 1 + .../expression/FirSpreadOfNullableChecker.kt | 40 +++++++++++++++++++ .../diagnostics/FirDefaultErrorMessages.kt | 2 + .../LightTreePositioningStrategies.kt | 13 ++++++ .../SourceElementPositioningStrategies.kt | 5 +++ .../diagnostics/PositioningStrategies.kt | 6 +++ .../NullableTypeForVarargArgument.fir.kt | 18 ++++----- .../inferredNullableArrayAsVararg.fir.kt | 15 ------- .../varargs/inferredNullableArrayAsVararg.kt | 1 + .../diagnostics/KtFirDataClassConverters.kt | 6 +++ .../api/fir/diagnostics/KtFirDiagnostics.kt | 4 ++ .../fir/diagnostics/KtFirDiagnosticsImpl.kt | 7 ++++ 15 files changed, 99 insertions(+), 25 deletions(-) create mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirSpreadOfNullableChecker.kt delete mode 100644 compiler/testData/diagnostics/tests/varargs/inferredNullableArrayAsVararg.fir.kt 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 072c20ed823..555e4190843 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 @@ -409,6 +409,8 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER by error { parameter("name") } + + val SPREAD_OF_NULLABLE by error(PositioningStrategy.SPREAD_OPERATOR) } val AMBIGUITY by object : DiagnosticGroup("Ambiguity") { @@ -570,7 +572,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val NOTHING_TO_OVERRIDE by error(PositioningStrategy.OVERRIDE_MODIFIER) { parameter("declaration") } - + val CANNOT_OVERRIDE_INVISIBLE_MEMBER by error(PositioningStrategy.OVERRIDE_MODIFIER) { parameter("overridingMember") parameter("baseMember") diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/DiagnosticData.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/DiagnosticData.kt index 31e6060b984..0fd2b85350b 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/DiagnosticData.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/DiagnosticData.kt @@ -75,6 +75,7 @@ enum class PositioningStrategy(private val strategy: String? = null) { ASSIGNMENT_LHS, IMPORT_LAST_NAME, DATA_MODIFIER, + SPREAD_OPERATOR, ; 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 9255419cf8e..72d7968b16e 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 @@ -281,6 +281,7 @@ object FirErrors { val RESULT_TYPE_MISMATCH by error2() val MANY_LAMBDA_EXPRESSION_ARGUMENTS by error0() val NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER by error1() + val SPREAD_OF_NULLABLE by error0(SourceElementPositioningStrategies.SPREAD_OPERATOR) // Ambiguity val OVERLOAD_RESOLUTION_AMBIGUITY by error1>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt index c0d83911642..a0f666bc91d 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt @@ -45,6 +45,7 @@ object CommonExpressionCheckers : ExpressionCheckers() { FirConventionFunctionCallChecker, FirDivisionByZeroChecker, FirConstructorCallChecker, + FirSpreadOfNullableChecker ) override val tryExpressionCheckers: Set diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirSpreadOfNullableChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirSpreadOfNullableChecker.kt new file mode 100644 index 00000000000..a0ab7f37895 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirSpreadOfNullableChecker.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2010-2021 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.expression + +import org.jetbrains.kotlin.fir.FirSourceElement +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors +import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn +import org.jetbrains.kotlin.fir.expressions.FirExpression +import org.jetbrains.kotlin.fir.expressions.FirFunctionCall +import org.jetbrains.kotlin.fir.expressions.FirSpreadArgumentExpression +import org.jetbrains.kotlin.fir.expressions.FirVarargArgumentsExpression +import org.jetbrains.kotlin.fir.types.ConeFlexibleType +import org.jetbrains.kotlin.fir.types.canBeNull +import org.jetbrains.kotlin.fir.types.coneType + +object FirSpreadOfNullableChecker : FirFunctionCallChecker() { + override fun check(expression: FirFunctionCall, context: CheckerContext, reporter: DiagnosticReporter) { + fun checkAndReport(argument: FirExpression, source: FirSourceElement?) { + val argumentTypeRef = argument.typeRef + if (argument is FirSpreadArgumentExpression && argumentTypeRef.coneType !is ConeFlexibleType && argumentTypeRef.canBeNull) { + reporter.reportOn(source, FirErrors.SPREAD_OF_NULLABLE, context) + } + } + + for (argument in expression.argumentList.arguments) { + if (argument is FirVarargArgumentsExpression) { + for (subArgument in argument.arguments) { + checkAndReport(subArgument, argument.source) + } + } else { + checkAndReport(argument, argument.source) + } + } + } +} \ No newline at end of file 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 7bc3639b243..b2356365f4f 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 @@ -316,6 +316,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SETTER_PROJECTED_ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SETTER_VISIBILITY_INCONSISTENT_WITH_PROPERTY_VISIBILITY import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SINGLETON_IN_SUPERTYPE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SMARTCAST_IMPOSSIBLE +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SPREAD_OF_NULLABLE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERCLASS_NOT_ACCESSIBLE_FROM_INTERFACE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERTYPES_FOR_ANNOTATION_CLASS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERTYPE_APPEARS_TWICE @@ -672,6 +673,7 @@ class FirDefaultErrorMessages { map.put(NO_VALUE_FOR_PARAMETER, "No value passed for parameter ''{0}''", NAME) map.put(NAMED_PARAMETER_NOT_FOUND, "Cannot find a parameter with this name: {0}", TO_STRING) map.put(MANY_LAMBDA_EXPRESSION_ARGUMENTS, "Only one lambda expression is allowed outside a parenthesized argument list") + map.put(SPREAD_OF_NULLABLE, "The spread operator (*foo) may not be applied to an argument of nullable type") map.put(TYPE_MISMATCH, "Type mismatch: inferred type is {1} but {0} was expected", TO_STRING, TO_STRING) map.put(THROWABLE_TYPE_MISMATCH, "Throwable type mismatch: actual type is {0}", TO_STRING) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/LightTreePositioningStrategies.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/LightTreePositioningStrategies.kt index 4a8e1b1f17c..4e85ecb47b5 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/LightTreePositioningStrategies.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/LightTreePositioningStrategies.kt @@ -20,6 +20,8 @@ import org.jetbrains.kotlin.lexer.KtTokens.VISIBILITY_MODIFIERS import org.jetbrains.kotlin.psi.KtParameter.VAL_VAR_TOKEN_SET import org.jetbrains.kotlin.psi.stubs.elements.KtConstantExpressionElementType import org.jetbrains.kotlin.psi.stubs.elements.KtStringTemplateExpressionElementType +import org.jetbrains.kotlin.psi.stubs.elements.KtValueArgumentElementType +import org.jetbrains.kotlin.psi.stubs.elements.KtValueArgumentListElementType object LightTreePositioningStrategies { val DEFAULT = object : LightTreePositioningStrategy() { @@ -780,6 +782,17 @@ object LightTreePositioningStrategies { return markElement(nodeToMark, startOffset, endOffset, tree, node) } } + + val SPREAD_OPERATOR: LightTreePositioningStrategy = object : LightTreePositioningStrategy() { + override fun mark( + node: LighterASTNode, + startOffset: Int, + endOffset: Int, + tree: FlyweightCapableTreeStructure + ): List { + return super.mark(node, startOffset, startOffset + 1, tree) + } + } } fun FirSourceElement.hasValOrVar(): Boolean = diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategies.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategies.kt index 329aba4783f..7ba595b2d93 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategies.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategies.kt @@ -262,4 +262,9 @@ object SourceElementPositioningStrategies { LightTreePositioningStrategies.IMPORT_LAST_NAME, PositioningStrategies.IMPORT_LAST_NAME ) + + val SPREAD_OPERATOR = SourceElementPositioningStrategy( + LightTreePositioningStrategies.SPREAD_OPERATOR, + PositioningStrategies.SPREAD_OPERATOR + ) } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt index eaea5fa9b83..b4511f73c14 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt @@ -849,6 +849,12 @@ object PositioningStrategies { } } + val SPREAD_OPERATOR: PositioningStrategy = object : PositioningStrategy() { + override fun mark(element: PsiElement): List { + return super.mark((element as? KtValueArgument)?.getSpreadElement()?.node?.psi ?: element) + } + } + @JvmField val FUN_INTERFACE: PositioningStrategy = object : PositioningStrategy() { override fun mark(element: KtDeclaration): List { diff --git a/compiler/testData/diagnostics/tests/varargs/NullableTypeForVarargArgument.fir.kt b/compiler/testData/diagnostics/tests/varargs/NullableTypeForVarargArgument.fir.kt index c482750f53f..292197681d6 100644 --- a/compiler/testData/diagnostics/tests/varargs/NullableTypeForVarargArgument.fir.kt +++ b/compiler/testData/diagnostics/tests/varargs/NullableTypeForVarargArgument.fir.kt @@ -29,9 +29,9 @@ fun baz(s: String) {} fun getArr(): Array? = null fun f() { - A().foo(1, *args) - bar(2, *args) - baz(*args) + A().foo(1, *args) + bar(2, *args) + baz(*args) } fun g(args: Array?) { @@ -48,18 +48,18 @@ class B { fun h(b: B) { if (b.args != null) { - A().foo(1, *b.args) + A().foo(1, *b.args) } } fun k() { - A().foo(1, *getArr()) - bar(2, *getArr()) - baz(*getArr()) + A().foo(1, *getArr()) + bar(2, *getArr()) + baz(*getArr()) } fun invokeTest(goodArgs: Array) { J.staticFun(*goodArgs) - J.staticFun(*args) - J.staticFun(*args ?: null) + J.staticFun(*args) + J.staticFun(*args ?: null) } diff --git a/compiler/testData/diagnostics/tests/varargs/inferredNullableArrayAsVararg.fir.kt b/compiler/testData/diagnostics/tests/varargs/inferredNullableArrayAsVararg.fir.kt deleted file mode 100644 index 52a587bd6c1..00000000000 --- a/compiler/testData/diagnostics/tests/varargs/inferredNullableArrayAsVararg.fir.kt +++ /dev/null @@ -1,15 +0,0 @@ -// !DIAGNOSTICS: -UNUSED_PARAMETER - -// FILE: Test.java -public class Test { - static public void foo(String ... x) { - - } -} - -// FILE: test.kt -fun select(vararg x: T) = x[1] - -fun main(x: Array?) { - Test.foo(*(select(arrayOf(""), null))) // no compilation errors before the fix, NPE -} diff --git a/compiler/testData/diagnostics/tests/varargs/inferredNullableArrayAsVararg.kt b/compiler/testData/diagnostics/tests/varargs/inferredNullableArrayAsVararg.kt index 7bdcd493823..5f1226913c3 100644 --- a/compiler/testData/diagnostics/tests/varargs/inferredNullableArrayAsVararg.kt +++ b/compiler/testData/diagnostics/tests/varargs/inferredNullableArrayAsVararg.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_PARAMETER // FILE: Test.java 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 1cdebdd264c..f2767c226a5 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 @@ -1243,6 +1243,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.SPREAD_OF_NULLABLE) { firDiagnostic -> + SpreadOfNullableImpl( + firDiagnostic as FirPsiDiagnostic, + token, + ) + } add(FirErrors.OVERLOAD_RESOLUTION_AMBIGUITY) { firDiagnostic -> OverloadResolutionAmbiguityImpl( firDiagnostic.a.map { firBasedSymbol -> 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 55f4dfc38e6..ba26dbf4e13 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 @@ -888,6 +888,10 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { abstract val name: String } + abstract class SpreadOfNullable : KtFirDiagnostic() { + override val diagnosticClass get() = SpreadOfNullable::class + } + abstract class OverloadResolutionAmbiguity : KtFirDiagnostic() { override val diagnosticClass get() = OverloadResolutionAmbiguity::class abstract val candidates: List 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 9680992c726..7a82c7c6f42 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 @@ -1426,6 +1426,13 @@ internal class NewInferenceNoInformationForParameterImpl( override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) } +internal class SpreadOfNullableImpl( + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.SpreadOfNullable(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} + internal class OverloadResolutionAmbiguityImpl( override val candidates: List, firDiagnostic: FirPsiDiagnostic,