[FIR] Implement SPREAD_OF_NULLABLE

This commit is contained in:
Ivan Kochurkin
2021-07-11 23:49:58 +03:00
committed by TeamCityServer
parent 83c5c41dd4
commit 66e052b9b3
15 changed files with 99 additions and 25 deletions
@@ -409,6 +409,8 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER by error<KtElement> {
parameter<String>("name")
}
val SPREAD_OF_NULLABLE by error<PsiElement>(PositioningStrategy.SPREAD_OPERATOR)
}
val AMBIGUITY by object : DiagnosticGroup("Ambiguity") {
@@ -570,7 +572,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val NOTHING_TO_OVERRIDE by error<KtModifierListOwner>(PositioningStrategy.OVERRIDE_MODIFIER) {
parameter<FirMemberDeclaration>("declaration")
}
val CANNOT_OVERRIDE_INVISIBLE_MEMBER by error<KtNamedDeclaration>(PositioningStrategy.OVERRIDE_MODIFIER) {
parameter<FirCallableDeclaration>("overridingMember")
parameter<FirCallableDeclaration>("baseMember")
@@ -75,6 +75,7 @@ enum class PositioningStrategy(private val strategy: String? = null) {
ASSIGNMENT_LHS,
IMPORT_LAST_NAME,
DATA_MODIFIER,
SPREAD_OPERATOR,
;
@@ -281,6 +281,7 @@ object FirErrors {
val RESULT_TYPE_MISMATCH by error2<KtExpression, ConeKotlinType, ConeKotlinType>()
val MANY_LAMBDA_EXPRESSION_ARGUMENTS by error0<KtValueArgument>()
val NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER by error1<KtElement, String>()
val SPREAD_OF_NULLABLE by error0<PsiElement>(SourceElementPositioningStrategies.SPREAD_OPERATOR)
// Ambiguity
val OVERLOAD_RESOLUTION_AMBIGUITY by error1<PsiElement, Collection<FirBasedSymbol<*>>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
@@ -45,6 +45,7 @@ object CommonExpressionCheckers : ExpressionCheckers() {
FirConventionFunctionCallChecker,
FirDivisionByZeroChecker,
FirConstructorCallChecker,
FirSpreadOfNullableChecker
)
override val tryExpressionCheckers: Set<FirTryExpressionChecker>
@@ -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)
}
}
}
}
@@ -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)
@@ -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<LighterASTNode>
): List<TextRange> {
return super.mark(node, startOffset, startOffset + 1, tree)
}
}
}
fun FirSourceElement.hasValOrVar(): Boolean =
@@ -262,4 +262,9 @@ object SourceElementPositioningStrategies {
LightTreePositioningStrategies.IMPORT_LAST_NAME,
PositioningStrategies.IMPORT_LAST_NAME
)
val SPREAD_OPERATOR = SourceElementPositioningStrategy(
LightTreePositioningStrategies.SPREAD_OPERATOR,
PositioningStrategies.SPREAD_OPERATOR
)
}
@@ -849,6 +849,12 @@ object PositioningStrategies {
}
}
val SPREAD_OPERATOR: PositioningStrategy<PsiElement> = object : PositioningStrategy<PsiElement>() {
override fun mark(element: PsiElement): List<TextRange> {
return super.mark((element as? KtValueArgument)?.getSpreadElement()?.node?.psi ?: element)
}
}
@JvmField
val FUN_INTERFACE: PositioningStrategy<KtDeclaration> = object : PositioningStrategy<KtDeclaration>() {
override fun mark(element: KtDeclaration): List<TextRange> {
@@ -29,9 +29,9 @@ fun baz(s: String) {}
fun getArr(): Array<String>? = null
fun f() {
A().foo(1, *args)
bar(2, *<!ARGUMENT_TYPE_MISMATCH!>args<!>)
baz(<!NON_VARARG_SPREAD!>*<!><!ARGUMENT_TYPE_MISMATCH!>args<!>)
A().foo(1, <!SPREAD_OF_NULLABLE!>*<!>args)
bar(2, <!SPREAD_OF_NULLABLE!>*<!><!ARGUMENT_TYPE_MISMATCH!>args<!>)
baz(<!NON_VARARG_SPREAD, SPREAD_OF_NULLABLE!>*<!><!ARGUMENT_TYPE_MISMATCH!>args<!>)
}
fun g(args: Array<String>?) {
@@ -48,18 +48,18 @@ class B {
fun h(b: B) {
if (b.args != null) {
A().foo(1, *b.args)
A().foo(1, <!SPREAD_OF_NULLABLE!>*<!>b.args)
}
}
fun k() {
A().foo(1, *getArr())
bar(2, *<!ARGUMENT_TYPE_MISMATCH!>getArr()<!>)
baz(<!NON_VARARG_SPREAD!>*<!><!ARGUMENT_TYPE_MISMATCH!>getArr()<!>)
A().foo(1, <!SPREAD_OF_NULLABLE!>*<!>getArr())
bar(2, <!SPREAD_OF_NULLABLE!>*<!><!ARGUMENT_TYPE_MISMATCH!>getArr()<!>)
baz(<!NON_VARARG_SPREAD, SPREAD_OF_NULLABLE!>*<!><!ARGUMENT_TYPE_MISMATCH!>getArr()<!>)
}
fun invokeTest(goodArgs: Array<String>) {
J.staticFun(*goodArgs)
J.staticFun(*args)
J.staticFun(*args <!USELESS_ELVIS_RIGHT_IS_NULL!>?: null<!>)
J.staticFun(<!SPREAD_OF_NULLABLE!>*<!>args)
J.staticFun(<!SPREAD_OF_NULLABLE!>*<!>args <!USELESS_ELVIS_RIGHT_IS_NULL!>?: null<!>)
}
@@ -1,15 +0,0 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
// FILE: Test.java
public class Test {
static public void foo(String ... x) {
}
}
// FILE: test.kt
fun <T> select(vararg x: T) = x[1]
fun main(x: Array<String>?) {
Test.foo(*(select(arrayOf(""), null))) // no compilation errors before the fix, NPE
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_PARAMETER
// FILE: Test.java
@@ -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 ->
@@ -888,6 +888,10 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
abstract val name: String
}
abstract class SpreadOfNullable : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = SpreadOfNullable::class
}
abstract class OverloadResolutionAmbiguity : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = OverloadResolutionAmbiguity::class
abstract val candidates: List<KtSymbol>
@@ -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<PsiElement> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class OverloadResolutionAmbiguityImpl(
override val candidates: List<KtSymbol>,
firDiagnostic: FirPsiDiagnostic,