Report warnings on safe call + nullable extension operator
^KT-41034 In Progress
This commit is contained in:
@@ -901,6 +901,8 @@ public interface Errors {
|
||||
DiagnosticFactory1<PsiElement, CallableDescriptor> DSL_SCOPE_VIOLATION = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory1<PsiElement, CallableDescriptor> DSL_SCOPE_VIOLATION_WARNING = DiagnosticFactory1.create(WARNING);
|
||||
|
||||
DiagnosticFactory0<PsiElement> NULLABLE_EXTENSION_OPERATOR_WITH_SAFE_CALL_RECEIVER = DiagnosticFactory0.create(WARNING);
|
||||
|
||||
// Labels
|
||||
|
||||
DiagnosticFactory0<KtSimpleNameExpression> LABEL_NAME_CLASH = DiagnosticFactory0.create(WARNING);
|
||||
|
||||
+3
@@ -528,6 +528,9 @@ public class DefaultErrorMessages {
|
||||
MAP.put(DSL_SCOPE_VIOLATION_WARNING, "''{0}'' shouldn't be called in this context by implicit receiver, it will become an error soon. " +
|
||||
"Use the explicit one if necessary", COMPACT);
|
||||
|
||||
MAP.put(NULLABLE_EXTENSION_OPERATOR_WITH_SAFE_CALL_RECEIVER, "Semantics of such combination of safe call and operator will change in next compiler version. " +
|
||||
"Namely, the right part of the safe call will not be evaluated if receiver is null");
|
||||
|
||||
MAP.put(RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY,
|
||||
"Returns are not allowed for functions with expression body. Use block body in '{...}'");
|
||||
MAP.put(NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY, "A 'return' expression required in a function with a block body ('{...}')");
|
||||
|
||||
@@ -51,7 +51,7 @@ private val DEFAULT_CALL_CHECKERS = listOf(
|
||||
UselessElvisCallChecker(), ResultTypeWithNullableOperatorsChecker(), NullableVarargArgumentCallChecker,
|
||||
NamedFunAsExpressionChecker, ContractNotAllowedCallChecker, ReifiedTypeParameterSubstitutionChecker(),
|
||||
MissingDependencySupertypeChecker.ForCalls, AbstractClassInstantiationChecker, SuspendConversionCallChecker,
|
||||
UnitConversionCallChecker, FunInterfaceConstructorReferenceChecker
|
||||
UnitConversionCallChecker, FunInterfaceConstructorReferenceChecker, NullableExtensionOperatorWithSafeCallChecker
|
||||
)
|
||||
private val DEFAULT_TYPE_CHECKERS = emptyList<AdditionalTypeChecker>()
|
||||
private val DEFAULT_CLASSIFIER_USAGE_CHECKERS = listOf(
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.resolve.checkers
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtCallExpression
|
||||
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
|
||||
import org.jetbrains.kotlin.psi.KtParenthesizedExpression
|
||||
import org.jetbrains.kotlin.psi.KtSafeQualifiedExpression
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.isReallySuccess
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.kotlin.types.isFlexible
|
||||
import org.jetbrains.kotlin.types.isNullable
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
object NullableExtensionOperatorWithSafeCallChecker : CallChecker {
|
||||
private val RELEVANT_OPERATORS = mutableSetOf<Name>().apply {
|
||||
addAll(OperatorNameConventions.ASSIGNMENT_OPERATIONS)
|
||||
add(OperatorNameConventions.INC)
|
||||
add(OperatorNameConventions.DEC)
|
||||
add(OperatorNameConventions.GET)
|
||||
add(OperatorNameConventions.SET)
|
||||
}
|
||||
|
||||
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
|
||||
if (!resolvedCall.isReallySuccess()) return
|
||||
val name = resolvedCall.resultingDescriptor.name
|
||||
if (name !in RELEVANT_OPERATORS) return
|
||||
|
||||
if (!isNullableSafeCallReceiver(resolvedCall.extensionReceiver)) return
|
||||
|
||||
val callElement = resolvedCall.call.callElement
|
||||
|
||||
// It's an operator call, not a regular one
|
||||
if (callElement is KtCallExpression && name.identifier == (callElement.calleeExpression as? KtNameReferenceExpression)?.getReferencedName()) return
|
||||
|
||||
context.trace.report(Errors.NULLABLE_EXTENSION_OPERATOR_WITH_SAFE_CALL_RECEIVER.on(reportOn))
|
||||
}
|
||||
|
||||
private fun isNullableSafeCallReceiver(receiverValue: ReceiverValue?): Boolean {
|
||||
if (receiverValue !is ExpressionReceiver) return false
|
||||
if (!receiverValue.type.isNullable() || receiverValue.type.isFlexible()) return false
|
||||
|
||||
val expression = receiverValue.expression
|
||||
|
||||
if (expression !is KtSafeQualifiedExpression) return false
|
||||
if (expression.parent is KtParenthesizedExpression) return false
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user