提交 9b1f01ab 编写于 作者: J Jinseong Jeon 提交者: Dmitriy Novozhilov

FIR checker: differentiate unsafe infix/operator calls from UNSAFE_CALL

上级 1729eff3
......@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.fir.PrivateForInline
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.WhenMissingCase
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
......@@ -62,7 +63,7 @@ val DIAGNOSTICS_LIST = DiagnosticListBuilder.buildDiagnosticList {
val SUPER_IS_NOT_AN_EXPRESSION by error<FirSourceElement, PsiElement>()
val SUPER_NOT_AVAILABLE by error<FirSourceElement, PsiElement>()
val ABSTRACT_SUPER_CALL by error<FirSourceElement, PsiElement>()
val INSTANCE_ACCESS_BEFORE_SUPER_CALL by error<FirSourceElement, PsiElement>() {
val INSTANCE_ACCESS_BEFORE_SUPER_CALL by error<FirSourceElement, PsiElement> {
parameter<String>("target")
}
}
......@@ -73,7 +74,7 @@ val DIAGNOSTICS_LIST = DiagnosticListBuilder.buildDiagnosticList {
val RECURSION_IN_SUPERTYPES by error<FirSourceElement, PsiElement>()
val NOT_A_SUPERTYPE by error<FirSourceElement, PsiElement>()
val SUPERCLASS_NOT_ACCESSIBLE_FROM_INTERFACE by error<FirSourceElement, PsiElement>()
val QUALIFIED_SUPERTYPE_EXTENDED_BY_OTHER_SUPERTYPE by error<FirSourceElement, PsiElement>() {
val QUALIFIED_SUPERTYPE_EXTENDED_BY_OTHER_SUPERTYPE by error<FirSourceElement, PsiElement> {
parameter<FirClass<*>>("otherSuperType")
}
val SUPERTYPE_INITIALIZED_IN_INTERFACE by error<FirSourceElement, PsiElement>()
......@@ -155,7 +156,7 @@ val DIAGNOSTICS_LIST = DiagnosticListBuilder.buildDiagnosticList {
val INAPPLICABLE_CANDIDATE by error<FirSourceElement, PsiElement> {
parameter<AbstractFirBasedSymbol<*>>("candidate")
}
val INAPPLICABLE_LATEINIT_MODIFIER by error<FirSourceElement, PsiElement>() {
val INAPPLICABLE_LATEINIT_MODIFIER by error<FirSourceElement, PsiElement> {
parameter<String>("reason")
}
}
......@@ -331,7 +332,7 @@ val DIAGNOSTICS_LIST = DiagnosticListBuilder.buildDiagnosticList {
parameter<Collection<AbstractFirBasedSymbol<*>>>("candidates")
}
val COMPONENT_FUNCTION_ON_NULLABLE by error<FirSourceElement, KtExpression>() {
val COMPONENT_FUNCTION_ON_NULLABLE by error<FirSourceElement, KtExpression> {
parameter<Name>("componentFunctionName")
}
......@@ -357,11 +358,19 @@ val DIAGNOSTICS_LIST = DiagnosticListBuilder.buildDiagnosticList {
val UNSAFE_CALL by error<FirSourceElement, PsiElement>(PositioningStrategy.DOT_BY_SELECTOR) {
parameter<ConeKotlinType>("receiverType")
}
val UNSAFE_IMPLICIT_INVOKE_CALL by error<FirSourceElement, PsiElement>() {
val UNSAFE_IMPLICIT_INVOKE_CALL by error<FirSourceElement, PsiElement> {
parameter<ConeKotlinType>("receiverType")
}
// TODO: val UNSAFE_INFIX_CALL by ...
// TODO: val UNSAFE_OPERATOR_CALL by ...
val UNSAFE_INFIX_CALL by error<FirSourceElement, KtExpression> {
parameter<FirExpression>("lhs")
parameter<String>("operator")
parameter<FirExpression>("rhs")
}
val UNSAFE_OPERATOR_CALL by error<FirSourceElement, KtExpression> {
parameter<FirExpression>("lhs")
parameter<String>("operator")
parameter<FirExpression>("rhs")
}
// TODO: val UNEXPECTED_SAFE_CALL by ...
}
......
......@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.SourceElementPositioningStr
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.WhenMissingCase
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
......@@ -231,6 +232,8 @@ object FirErrors {
// Nullability
val UNSAFE_CALL by error1<FirSourceElement, PsiElement, ConeKotlinType>(SourceElementPositioningStrategies.DOT_BY_SELECTOR)
val UNSAFE_IMPLICIT_INVOKE_CALL by error1<FirSourceElement, PsiElement, ConeKotlinType>()
val UNSAFE_INFIX_CALL by error3<FirSourceElement, KtExpression, FirExpression, String, FirExpression>()
val UNSAFE_OPERATOR_CALL by error3<FirSourceElement, KtExpression, FirExpression, String, FirExpression>()
// When expressions
val NO_ELSE_IN_WHEN by error1<FirSourceElement, KtWhenExpression, List<WhenMissingCase>>(SourceElementPositioningStrategies.WHEN_EXPRESSION)
......
......@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticFactoryToRendererMap
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.AMBIGUOUS_CALLS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.DECLARATION_NAME
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.FIR
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.FQ_NAMES_IN_TYPES
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.NAME
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.NULLABLE_STRING
......@@ -158,6 +159,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNRESOLVED_LABEL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNRESOLVED_REFERENCE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNSAFE_CALL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNSAFE_IMPLICIT_INVOKE_CALL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNSAFE_INFIX_CALL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNSAFE_OPERATOR_CALL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNUSED_VARIABLE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UPPER_BOUND_VIOLATED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USELESS_VARARG_ON_PARAMETER
......@@ -512,6 +515,21 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
"Reference has a nullable type ''{0}'', use explicit \"?.invoke\" to make a function-like call instead.",
RENDER_TYPE
)
map.put(
UNSAFE_INFIX_CALL,
"Infix call corresponds to a dot-qualified call ''{0}.{1}({2})'' which is not allowed on a nullable receiver ''{0}''. " +
"Use ''?.''-qualified call instead",
FIR,
TO_STRING,
FIR
)
map.put(
UNSAFE_OPERATOR_CALL,
"Operator call corresponds to a dot-qualified call ''{0}.{1}({2})'' which is not allowed on a nullable receiver ''{0}''. ",
FIR,
TO_STRING,
FIR
)
// When expressions
map.put(NO_ELSE_IN_WHEN, "''when'' expression must be exhaustive, add necessary {0}", WHEN_MISSING_CASES)
......
......@@ -5,12 +5,18 @@
package org.jetbrains.kotlin.fir.analysis.diagnostics
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.analysis.getChild
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.declarations.isInfix
import org.jetbrains.kotlin.fir.declarations.isOperator
import org.jetbrains.kotlin.fir.diagnostics.*
import org.jetbrains.kotlin.fir.resolve.calls.InapplicableWrongReceiver
import org.jetbrains.kotlin.fir.resolve.diagnostics.*
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
......@@ -58,7 +64,6 @@ private fun mapInapplicableCandidateError(
source: FirSourceElement,
): FirDiagnostic<*> {
// TODO: Need to distinguish SMARTCAST_IMPOSSIBLE
// TODO: handle other UNSAFE_* variants: infix, operator
val rootCause = diagnostic.candidate.diagnostics.find { it.applicability == diagnostic.applicability }
if (rootCause != null &&
rootCause is InapplicableWrongReceiver &&
......@@ -68,6 +73,24 @@ private fun mapInapplicableCandidateError(
if (diagnostic.candidate.callInfo.isImplicitInvoke) {
return FirErrors.UNSAFE_IMPLICIT_INVOKE_CALL.on(source, rootCause.actualType!!)
}
val candidateFunction = diagnostic.candidate.symbol.fir as? FirSimpleFunction
val candidateFunctionName = candidateFunction?.name
val left = diagnostic.candidate.callInfo.explicitReceiver
val right = diagnostic.candidate.callInfo.argumentList.arguments.singleOrNull()
if (left != null && right != null &&
source.elementType == KtNodeTypes.OPERATION_REFERENCE &&
(candidateFunction?.isOperator == true || candidateFunction?.isInfix == true)
) {
val operationToken = source.getChild(KtTokens.IDENTIFIER)
if (candidateFunction.isInfix && operationToken?.elementType == KtTokens.IDENTIFIER) {
return FirErrors.UNSAFE_INFIX_CALL.on(source, left, candidateFunctionName!!.asString(), right)
}
if (candidateFunction.isOperator && operationToken == null) {
return FirErrors.UNSAFE_OPERATOR_CALL.on(source, left, candidateFunctionName!!.asString(), right)
}
}
return FirErrors.UNSAFE_CALL.on(source, rootCause.actualType!!)
}
return FirErrors.INAPPLICABLE_CANDIDATE.on(source, diagnostic.candidate.symbol)
......
......@@ -18,15 +18,15 @@ fun test(x : Int?, a : A?) {
a<!UNSAFE_CALL!>.<!>plus(1)
a?.plus(1)
a <!UNSAFE_CALL!>plus<!> 1
a <!UNSAFE_CALL!>+<!> 1
a <!UNSAFE_INFIX_CALL!>plus<!> 1
a <!UNSAFE_OPERATOR_CALL!>+<!> 1
<!UNSAFE_CALL!>-<!>a
a<!UNSAFE_CALL!>.<!>unaryMinus()
a?.unaryMinus()
a<!UNSAFE_CALL!>.<!>div(1)
a <!UNSAFE_CALL!>/<!> 1
a <!UNSAFE_CALL!>div<!> 1
a <!UNSAFE_OPERATOR_CALL!>/<!> 1
a <!UNSAFE_INFIX_CALL!>div<!> 1
a?.div(1)
a.times(1)
......@@ -34,8 +34,8 @@ fun test(x : Int?, a : A?) {
a times 1
a?.times(1)
1 <!UNSAFE_CALL!>in<!> a
a <!UNSAFE_CALL!>contains<!> 1
1 <!UNSAFE_OPERATOR_CALL!>in<!> a
a <!UNSAFE_INFIX_CALL!>contains<!> 1
a<!UNSAFE_CALL!>.<!>contains(1)
a?.contains(1)
}
......@@ -14,6 +14,6 @@ fun f(): Unit {
val i : Int? = null
i <!NONE_APPLICABLE!>+<!> 1
set + 1
1 <!UNSAFE_CALL!>in<!> set
1 <!UNSAFE_OPERATOR_CALL!>in<!> set
1 in 2
}
......@@ -7,5 +7,5 @@ infix fun Int.bar(i: Int) = i
fun test() {
val p = A()
// For open value properties, smart casts should not work
if (p.foo is Int) p.foo <!UNSAFE_CALL!>bar<!> 11
if (p.foo is Int) p.foo <!UNSAFE_INFIX_CALL!>bar<!> 11
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册