[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> {
|
||||
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")
|
||||
|
||||
+1
@@ -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)
|
||||
|
||||
+1
@@ -45,6 +45,7 @@ object CommonExpressionCheckers : ExpressionCheckers() {
|
||||
FirConventionFunctionCallChecker,
|
||||
FirDivisionByZeroChecker,
|
||||
FirConstructorCallChecker,
|
||||
FirSpreadOfNullableChecker
|
||||
)
|
||||
|
||||
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.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)
|
||||
|
||||
+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.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 =
|
||||
|
||||
+5
@@ -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> {
|
||||
|
||||
+9
-9
@@ -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<!>)
|
||||
}
|
||||
|
||||
-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
|
||||
|
||||
// FILE: Test.java
|
||||
|
||||
+6
@@ -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 ->
|
||||
|
||||
+4
@@ -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>
|
||||
|
||||
+7
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user