[FIR] Implement SPREAD_OF_NULLABLE
This commit is contained in:
committed by
TeamCityServer
parent
83c5c41dd4
commit
66e052b9b3
+3
-1
@@ -409,6 +409,8 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
|||||||
val NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER by error<KtElement> {
|
val NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER by error<KtElement> {
|
||||||
parameter<String>("name")
|
parameter<String>("name")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val SPREAD_OF_NULLABLE by error<PsiElement>(PositioningStrategy.SPREAD_OPERATOR)
|
||||||
}
|
}
|
||||||
|
|
||||||
val AMBIGUITY by object : DiagnosticGroup("Ambiguity") {
|
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) {
|
val NOTHING_TO_OVERRIDE by error<KtModifierListOwner>(PositioningStrategy.OVERRIDE_MODIFIER) {
|
||||||
parameter<FirMemberDeclaration>("declaration")
|
parameter<FirMemberDeclaration>("declaration")
|
||||||
}
|
}
|
||||||
|
|
||||||
val CANNOT_OVERRIDE_INVISIBLE_MEMBER by error<KtNamedDeclaration>(PositioningStrategy.OVERRIDE_MODIFIER) {
|
val CANNOT_OVERRIDE_INVISIBLE_MEMBER by error<KtNamedDeclaration>(PositioningStrategy.OVERRIDE_MODIFIER) {
|
||||||
parameter<FirCallableDeclaration>("overridingMember")
|
parameter<FirCallableDeclaration>("overridingMember")
|
||||||
parameter<FirCallableDeclaration>("baseMember")
|
parameter<FirCallableDeclaration>("baseMember")
|
||||||
|
|||||||
+1
@@ -75,6 +75,7 @@ enum class PositioningStrategy(private val strategy: String? = null) {
|
|||||||
ASSIGNMENT_LHS,
|
ASSIGNMENT_LHS,
|
||||||
IMPORT_LAST_NAME,
|
IMPORT_LAST_NAME,
|
||||||
DATA_MODIFIER,
|
DATA_MODIFIER,
|
||||||
|
SPREAD_OPERATOR,
|
||||||
|
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|||||||
@@ -281,6 +281,7 @@ object FirErrors {
|
|||||||
val RESULT_TYPE_MISMATCH by error2<KtExpression, ConeKotlinType, ConeKotlinType>()
|
val RESULT_TYPE_MISMATCH by error2<KtExpression, ConeKotlinType, ConeKotlinType>()
|
||||||
val MANY_LAMBDA_EXPRESSION_ARGUMENTS by error0<KtValueArgument>()
|
val MANY_LAMBDA_EXPRESSION_ARGUMENTS by error0<KtValueArgument>()
|
||||||
val NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER by error1<KtElement, String>()
|
val NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER by error1<KtElement, String>()
|
||||||
|
val SPREAD_OF_NULLABLE by error0<PsiElement>(SourceElementPositioningStrategies.SPREAD_OPERATOR)
|
||||||
|
|
||||||
// Ambiguity
|
// Ambiguity
|
||||||
val OVERLOAD_RESOLUTION_AMBIGUITY by error1<PsiElement, Collection<FirBasedSymbol<*>>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
val OVERLOAD_RESOLUTION_AMBIGUITY by error1<PsiElement, Collection<FirBasedSymbol<*>>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
||||||
|
|||||||
+1
@@ -45,6 +45,7 @@ object CommonExpressionCheckers : ExpressionCheckers() {
|
|||||||
FirConventionFunctionCallChecker,
|
FirConventionFunctionCallChecker,
|
||||||
FirDivisionByZeroChecker,
|
FirDivisionByZeroChecker,
|
||||||
FirConstructorCallChecker,
|
FirConstructorCallChecker,
|
||||||
|
FirSpreadOfNullableChecker
|
||||||
)
|
)
|
||||||
|
|
||||||
override val tryExpressionCheckers: Set<FirTryExpressionChecker>
|
override val tryExpressionCheckers: Set<FirTryExpressionChecker>
|
||||||
|
|||||||
+40
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
@@ -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.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.SINGLETON_IN_SUPERTYPE
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SMARTCAST_IMPOSSIBLE
|
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.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.SUPERTYPES_FOR_ANNOTATION_CLASS
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERTYPE_APPEARS_TWICE
|
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(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(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(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(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)
|
map.put(THROWABLE_TYPE_MISMATCH, "Throwable type mismatch: actual type is {0}", TO_STRING)
|
||||||
|
|||||||
+13
@@ -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.KtParameter.VAL_VAR_TOKEN_SET
|
||||||
import org.jetbrains.kotlin.psi.stubs.elements.KtConstantExpressionElementType
|
import org.jetbrains.kotlin.psi.stubs.elements.KtConstantExpressionElementType
|
||||||
import org.jetbrains.kotlin.psi.stubs.elements.KtStringTemplateExpressionElementType
|
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 {
|
object LightTreePositioningStrategies {
|
||||||
val DEFAULT = object : LightTreePositioningStrategy() {
|
val DEFAULT = object : LightTreePositioningStrategy() {
|
||||||
@@ -780,6 +782,17 @@ object LightTreePositioningStrategies {
|
|||||||
return markElement(nodeToMark, startOffset, endOffset, tree, node)
|
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 =
|
fun FirSourceElement.hasValOrVar(): Boolean =
|
||||||
|
|||||||
+5
@@ -262,4 +262,9 @@ object SourceElementPositioningStrategies {
|
|||||||
LightTreePositioningStrategies.IMPORT_LAST_NAME,
|
LightTreePositioningStrategies.IMPORT_LAST_NAME,
|
||||||
PositioningStrategies.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
|
@JvmField
|
||||||
val FUN_INTERFACE: PositioningStrategy<KtDeclaration> = object : PositioningStrategy<KtDeclaration>() {
|
val FUN_INTERFACE: PositioningStrategy<KtDeclaration> = object : PositioningStrategy<KtDeclaration>() {
|
||||||
override fun mark(element: KtDeclaration): List<TextRange> {
|
override fun mark(element: KtDeclaration): List<TextRange> {
|
||||||
|
|||||||
+9
-9
@@ -29,9 +29,9 @@ fun baz(s: String) {}
|
|||||||
fun getArr(): Array<String>? = null
|
fun getArr(): Array<String>? = null
|
||||||
|
|
||||||
fun f() {
|
fun f() {
|
||||||
A().foo(1, *args)
|
A().foo(1, <!SPREAD_OF_NULLABLE!>*<!>args)
|
||||||
bar(2, *<!ARGUMENT_TYPE_MISMATCH!>args<!>)
|
bar(2, <!SPREAD_OF_NULLABLE!>*<!><!ARGUMENT_TYPE_MISMATCH!>args<!>)
|
||||||
baz(<!NON_VARARG_SPREAD!>*<!><!ARGUMENT_TYPE_MISMATCH!>args<!>)
|
baz(<!NON_VARARG_SPREAD, SPREAD_OF_NULLABLE!>*<!><!ARGUMENT_TYPE_MISMATCH!>args<!>)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun g(args: Array<String>?) {
|
fun g(args: Array<String>?) {
|
||||||
@@ -48,18 +48,18 @@ class B {
|
|||||||
|
|
||||||
fun h(b: B) {
|
fun h(b: B) {
|
||||||
if (b.args != null) {
|
if (b.args != null) {
|
||||||
A().foo(1, *b.args)
|
A().foo(1, <!SPREAD_OF_NULLABLE!>*<!>b.args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun k() {
|
fun k() {
|
||||||
A().foo(1, *getArr())
|
A().foo(1, <!SPREAD_OF_NULLABLE!>*<!>getArr())
|
||||||
bar(2, *<!ARGUMENT_TYPE_MISMATCH!>getArr()<!>)
|
bar(2, <!SPREAD_OF_NULLABLE!>*<!><!ARGUMENT_TYPE_MISMATCH!>getArr()<!>)
|
||||||
baz(<!NON_VARARG_SPREAD!>*<!><!ARGUMENT_TYPE_MISMATCH!>getArr()<!>)
|
baz(<!NON_VARARG_SPREAD, SPREAD_OF_NULLABLE!>*<!><!ARGUMENT_TYPE_MISMATCH!>getArr()<!>)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun invokeTest(goodArgs: Array<String>) {
|
fun invokeTest(goodArgs: Array<String>) {
|
||||||
J.staticFun(*goodArgs)
|
J.staticFun(*goodArgs)
|
||||||
J.staticFun(*args)
|
J.staticFun(<!SPREAD_OF_NULLABLE!>*<!>args)
|
||||||
J.staticFun(*args <!USELESS_ELVIS_RIGHT_IS_NULL!>?: null<!>)
|
J.staticFun(<!SPREAD_OF_NULLABLE!>*<!>args <!USELESS_ELVIS_RIGHT_IS_NULL!>?: null<!>)
|
||||||
}
|
}
|
||||||
|
|||||||
-15
@@ -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
|
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||||
|
|
||||||
// FILE: Test.java
|
// FILE: Test.java
|
||||||
|
|||||||
+6
@@ -1243,6 +1243,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
|||||||
token,
|
token,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
add(FirErrors.SPREAD_OF_NULLABLE) { firDiagnostic ->
|
||||||
|
SpreadOfNullableImpl(
|
||||||
|
firDiagnostic as FirPsiDiagnostic,
|
||||||
|
token,
|
||||||
|
)
|
||||||
|
}
|
||||||
add(FirErrors.OVERLOAD_RESOLUTION_AMBIGUITY) { firDiagnostic ->
|
add(FirErrors.OVERLOAD_RESOLUTION_AMBIGUITY) { firDiagnostic ->
|
||||||
OverloadResolutionAmbiguityImpl(
|
OverloadResolutionAmbiguityImpl(
|
||||||
firDiagnostic.a.map { firBasedSymbol ->
|
firDiagnostic.a.map { firBasedSymbol ->
|
||||||
|
|||||||
+4
@@ -888,6 +888,10 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
|
|||||||
abstract val name: String
|
abstract val name: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract class SpreadOfNullable : KtFirDiagnostic<PsiElement>() {
|
||||||
|
override val diagnosticClass get() = SpreadOfNullable::class
|
||||||
|
}
|
||||||
|
|
||||||
abstract class OverloadResolutionAmbiguity : KtFirDiagnostic<PsiElement>() {
|
abstract class OverloadResolutionAmbiguity : KtFirDiagnostic<PsiElement>() {
|
||||||
override val diagnosticClass get() = OverloadResolutionAmbiguity::class
|
override val diagnosticClass get() = OverloadResolutionAmbiguity::class
|
||||||
abstract val candidates: List<KtSymbol>
|
abstract val candidates: List<KtSymbol>
|
||||||
|
|||||||
+7
@@ -1426,6 +1426,13 @@ internal class NewInferenceNoInformationForParameterImpl(
|
|||||||
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
|
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(
|
internal class OverloadResolutionAmbiguityImpl(
|
||||||
override val candidates: List<KtSymbol>,
|
override val candidates: List<KtSymbol>,
|
||||||
firDiagnostic: FirPsiDiagnostic,
|
firDiagnostic: FirPsiDiagnostic,
|
||||||
|
|||||||
Reference in New Issue
Block a user