From 5a0b75bd8989809f49395e9ace81e708b8d0ebf8 Mon Sep 17 00:00:00 2001 From: Jinseong Jeon Date: Thu, 1 Apr 2021 11:55:05 -0700 Subject: [PATCH] FIR checker: warn unnecessary safe calls --- .../resolve/arguments/lambdaInLambda.kt | 2 +- .../boundSmartcasts/boundSmartcasts.kt | 4 +- .../generator/diagnostics/DiagnosticData.kt | 1 + .../diagnostics/FirDiagnosticsList.kt | 11 ++-- .../fir/analysis/diagnostics/FirErrors.kt | 2 + .../FirSafeCallExpressionChecker.kt | 32 +++++++++++ .../ExpressionCheckersDiagnosticComponent.kt | 4 ++ .../diagnostics/FirDefaultErrorMessages.kt | 4 ++ .../LightTreePositioningStrategies.kt | 14 +++++ .../SourceElementPositioningStrategies.kt | 5 ++ .../fir/checkers/CommonExpressionCheckers.kt | 1 + .../converter/ExpressionsConverter.kt | 5 +- .../kotlin/fir/builder/RawFirBuilder.kt | 5 +- .../kotlin/fir/builder/ConversionUtils.kt | 6 +-- .../diagnostics/PositioningStrategies.kt | 7 +++ .../tests/ExtensionCallInvoke.fir.kt | 2 +- .../tests/FunctionCalleeExpressions.fir.kt | 2 +- .../diagnostics/tests/SafeCallInvoke.fir.kt | 4 +- .../tests/SafeCallNonNullReceiver.fir.kt | 6 --- .../tests/SafeCallNonNullReceiver.kt | 1 + .../SafeCallNonNullReceiverReturnNull.fir.kt | 2 +- .../tests/SafeCallOnFakePackage.fir.kt | 6 +-- .../deadCode/deadCallInReceiver.fir.kt | 4 +- .../controlFlowAnalysis/throwInLambda.fir.kt | 4 +- .../extensions/ExtensionFunctions.fir.kt | 4 +- .../diagnostics/tests/generics/kt9985.fir.kt | 8 +-- .../generics/nullability/smartCasts.fir.kt | 8 +-- .../nullability/smartCastsOnThis.fir.kt | 6 +-- .../tests/inference/findViewById.fir.kt | 2 +- .../nestedCalls/makeNullableIfSafeCall.fir.kt | 4 +- ...CastInLambdaReturnAfterIntersection.fir.kt | 2 +- .../classGenericsInParamsIndexMismatch.fir.kt | 34 ------------ .../classGenericsInParamsIndexMismatch.kt | 1 + ...sVsFunctionGenericsInParamsMismatch.fir.kt | 2 +- .../differentNumberOfParams.fir.kt | 4 +- .../simpleWithInheritance.fir.kt | 38 ------------- .../duplicateMethod/simpleWithInheritance.kt | 1 + .../substitutedGenericInParams.fir.kt | 34 ------------ .../substitutedGenericInParams.kt | 1 + .../ReceiverNullability.fir.kt | 44 --------------- .../ReceiverNullability.kt | 1 + .../definitelyNotNullWithNullableBound.fir.kt | 2 +- ...afeCallOnTypeWithNullableUpperBound.fir.kt | 4 +- .../nullableTypes/safeCallOperators.fir.kt | 54 +++++++++---------- .../noInheritanceReturnType.fir.kt | 23 -------- .../noInheritanceReturnType.kt | 1 + .../onTypeProjection.fir.kt | 19 ------- .../notNullTypeParameter/onTypeProjection.kt | 1 + ...ullTypeMarkedWithNullableAnnotation.fir.kt | 2 +- .../nullabilityWarnings/safeCall.fir.kt | 43 --------------- .../nullabilityWarnings/safeCall.kt | 1 + .../tests/regressions/Jet72.fir.kt | 4 +- .../regressions/SpecififcityByReceiver.fir.kt | 2 +- .../tests/resolve/invoke/kt30695_2.fir.kt | 2 +- .../nullableReceiverWithExclExcl.fir.kt | 6 +-- .../testsWithStdLib/assert/cast.fir.kt | 2 +- .../testsWithStdLib/assert/safeCall.fir.kt | 4 +- .../resolve/javaStaticMembers.fir.kt | 4 +- .../analysis/smartcasts/pos/2.fir.kt | 22 ++++---- .../analysis/smartcasts/pos/3.fir.kt | 10 ++-- .../diagnostics/notLinked/dfa/neg/2.fir.kt | 4 +- .../diagnostics/notLinked/dfa/pos/30.fir.kt | 6 +-- .../diagnostics/KtFirDataClassConverters.kt | 13 +++++ .../api/fir/diagnostics/KtFirDiagnostics.kt | 9 ++++ .../fir/diagnostics/KtFirDiagnosticsImpl.kt | 15 ++++++ 65 files changed, 232 insertions(+), 349 deletions(-) create mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirSafeCallExpressionChecker.kt delete mode 100644 compiler/testData/diagnostics/tests/SafeCallNonNullReceiver.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/multimodule/duplicateMethod/classGenericsInParamsIndexMismatch.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/multimodule/duplicateMethod/simpleWithInheritance.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/multimodule/duplicateMethod/substitutedGenericInParams.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/ReceiverNullability.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/platformTypes/notNullTypeParameter/noInheritanceReturnType.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/platformTypes/notNullTypeParameter/onTypeProjection.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/safeCall.fir.kt diff --git a/compiler/fir/analysis-tests/testData/resolve/arguments/lambdaInLambda.kt b/compiler/fir/analysis-tests/testData/resolve/arguments/lambdaInLambda.kt index 4c565c751eb..4f09f96166a 100644 --- a/compiler/fir/analysis-tests/testData/resolve/arguments/lambdaInLambda.kt +++ b/compiler/fir/analysis-tests/testData/resolve/arguments/lambdaInLambda.kt @@ -16,7 +16,7 @@ fun test(ordinal: Int) { buildString { insert(KDocTemplate()) { definition { - ordinal?.let {} + ordinal?.let {} } } } diff --git a/compiler/fir/analysis-tests/testData/resolve/smartcasts/boundSmartcasts/boundSmartcasts.kt b/compiler/fir/analysis-tests/testData/resolve/smartcasts/boundSmartcasts/boundSmartcasts.kt index e7be4c1de50..148f8af2c9b 100644 --- a/compiler/fir/analysis-tests/testData/resolve/smartcasts/boundSmartcasts/boundSmartcasts.kt +++ b/compiler/fir/analysis-tests/testData/resolve/smartcasts/boundSmartcasts/boundSmartcasts.kt @@ -69,8 +69,8 @@ fun test_6(d1: D) { } fun test_7(d1: D, d2: D) { - val a = d1?.any - val b = d2?.any + val a = d1?.any + val b = d2?.any a as A a.foo() // should be OK b as B diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/DiagnosticData.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/DiagnosticData.kt index 0ab4cd5db9b..0d68a52a1c0 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/DiagnosticData.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/DiagnosticData.kt @@ -51,6 +51,7 @@ enum class PositioningStrategy(private val strategy: String? = null) { COMPANION_OBJECT, CONST_MODIFIER, ARRAY_ACCESS, + SAFE_ACCESS, NAME_OF_NAMED_ARGUMENT, VALUE_ARGUMENTS, SUPERTYPES_LIST, diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index 194440b0088..9bfc89c691d 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -299,11 +299,11 @@ object DIAGNOSTICS_LIST : DiagnosticList() { } val UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE by error() - + val BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER by error() - + val ONLY_ONE_CLASS_BOUND_ALLOWED by error() - + val REPEATED_BOUND by error() val CONFLICTING_UPPER_BOUNDS by error { @@ -559,7 +559,10 @@ object DIAGNOSTICS_LIST : DiagnosticList() { parameter("operator") parameter("rhs") } - // TODO: val UNEXPECTED_SAFE_CALL by ... + val UNNECESSARY_SAFE_CALL by warning(PositioningStrategy.SAFE_ACCESS) { + parameter("receiverType") + } + val UNEXPECTED_SAFE_CALL by error(PositioningStrategy.SAFE_ACCESS) val UNNECESSARY_NOT_NULL_ASSERTION by warning(PositioningStrategy.OPERATOR) { parameter("receiverType") } diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index 81e9e4e5281..c631fae1e36 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -331,6 +331,8 @@ object FirErrors { val UNSAFE_IMPLICIT_INVOKE_CALL by error1(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) val UNSAFE_INFIX_CALL by error3(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) val UNSAFE_OPERATOR_CALL by error3(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) + val UNNECESSARY_SAFE_CALL by warning1(SourceElementPositioningStrategies.SAFE_ACCESS) + val UNEXPECTED_SAFE_CALL by error0(SourceElementPositioningStrategies.SAFE_ACCESS) val UNNECESSARY_NOT_NULL_ASSERTION by warning1(SourceElementPositioningStrategies.OPERATOR) val NOT_NULL_ASSERTION_ON_LAMBDA_EXPRESSION by warning0(SourceElementPositioningStrategies.OPERATOR) val NOT_NULL_ASSERTION_ON_CALLABLE_REFERENCE by warning0(SourceElementPositioningStrategies.OPERATOR) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirSafeCallExpressionChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirSafeCallExpressionChecker.kt new file mode 100644 index 00000000000..35ad6db06a5 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirSafeCallExpressionChecker.kt @@ -0,0 +1,32 @@ +/* + * 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.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.FirSafeCallExpression +import org.jetbrains.kotlin.fir.expressions.FirStatement +import org.jetbrains.kotlin.fir.resolve.fullyExpandedType +import org.jetbrains.kotlin.fir.types.canBeNull +import org.jetbrains.kotlin.fir.types.coneType +import org.jetbrains.kotlin.fir.types.isUnit + +object FirSafeCallExpressionChecker : FirBasicExpressionChecker() { + override fun check(expression: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) { + if (expression !is FirSafeCallExpression) return + + val receiverType = expression.receiver.typeRef.coneType.fullyExpandedType(context.session) + if (receiverType.isUnit) { + reporter.reportOn(expression.source, FirErrors.UNEXPECTED_SAFE_CALL, context) + return + } + if (!receiverType.canBeNull) { + reporter.reportOn(expression.source, FirErrors.UNNECESSARY_SAFE_CALL, receiverType, context) + } + } +} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ExpressionCheckersDiagnosticComponent.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ExpressionCheckersDiagnosticComponent.kt index bccba9c37de..1c2e9dd43f5 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ExpressionCheckersDiagnosticComponent.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ExpressionCheckersDiagnosticComponent.kt @@ -77,6 +77,10 @@ class ExpressionCheckersDiagnosticComponent( checkers.allBasicExpressionCheckers.check(checkNotNullCall, data, reporter) } + override fun visitSafeCallExpression(safeCallExpression: FirSafeCallExpression, data: CheckerContext) { + checkers.basicExpressionCheckers.check(safeCallExpression, data, reporter) + } + override fun visitTryExpression(tryExpression: FirTryExpression, data: CheckerContext) { checkers.allTryExpressionCheckers.check(tryExpression, data, reporter) } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt index 3c7a86f6384..5d34a2b52e1 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt @@ -217,9 +217,11 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_PARAMETER_AS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_PARAMETER_IN_CATCH_CLAUSE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_PARAMETER_IS_NOT_AN_EXPRESSION import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_PARAMETER_ON_LHS_OF_DOT +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNEXPECTED_SAFE_CALL import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNINITIALIZED_VARIABLE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNNECESSARY_LATEINIT import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNNECESSARY_NOT_NULL_ASSERTION +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNNECESSARY_SAFE_CALL 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 @@ -751,6 +753,8 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension { map.put(UNNECESSARY_NOT_NULL_ASSERTION, "Unnecessary non-null assertion (!!) on a non-null receiver of type {0}", RENDER_TYPE) map.put(NOT_NULL_ASSERTION_ON_LAMBDA_EXPRESSION, "Non-null assertion (!!) is called on a lambda expression") map.put(NOT_NULL_ASSERTION_ON_CALLABLE_REFERENCE, "Non-null assertion (!!) is called on a callable reference expression") + map.put(UNNECESSARY_SAFE_CALL, "Unnecessary safe call on a non-null receiver of type {0}", RENDER_TYPE) + map.put(UNEXPECTED_SAFE_CALL, "Safe-call is not allowed here") // When expressions map.put(NO_ELSE_IN_WHEN, "''when'' expression must be exhaustive, add necessary {0}", WHEN_MISSING_CASES) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/LightTreePositioningStrategies.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/LightTreePositioningStrategies.kt index 6f554beab6f..872b4658990 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/LightTreePositioningStrategies.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/LightTreePositioningStrategies.kt @@ -497,6 +497,17 @@ object LightTreePositioningStrategies { } } + val SAFE_ACCESS = object : LightTreePositioningStrategy() { + override fun mark( + node: LighterASTNode, + startOffset: Int, + endOffset: Int, + tree: FlyweightCapableTreeStructure + ): List { + return markElement(tree.safeAccess(node) ?: node, startOffset, endOffset, tree, node) + } + } + val RETURN_WITH_LABEL = object : LightTreePositioningStrategy() { override fun mark( node: LighterASTNode, @@ -531,6 +542,9 @@ private fun FlyweightCapableTreeStructure.constructorKeyword(nod private fun FlyweightCapableTreeStructure.dotOperator(node: LighterASTNode): LighterASTNode? = findChildByType(node, KtTokens.DOT) +private fun FlyweightCapableTreeStructure.safeAccess(node: LighterASTNode): LighterASTNode? = + findChildByType(node, KtTokens.SAFE_ACCESS) + private fun FlyweightCapableTreeStructure.initKeyword(node: LighterASTNode): LighterASTNode? = findChildByType(node, KtTokens.INIT_KEYWORD) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategies.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategies.kt index fbb8dda6919..4cafdf92ea0 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategies.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategies.kt @@ -168,6 +168,11 @@ object SourceElementPositioningStrategies { PositioningStrategies.ARRAY_ACCESS ) + val SAFE_ACCESS = SourceElementPositioningStrategy( + LightTreePositioningStrategies.SAFE_ACCESS, + PositioningStrategies.SAFE_ACCESS + ) + val RETURN_WITH_LABEL = SourceElementPositioningStrategy( LightTreePositioningStrategies.RETURN_WITH_LABEL, PositioningStrategies.RETURN_WITH_LABEL diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonExpressionCheckers.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonExpressionCheckers.kt index 567e3888526..7f5bba89e8b 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonExpressionCheckers.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonExpressionCheckers.kt @@ -12,6 +12,7 @@ object CommonExpressionCheckers : ExpressionCheckers() { FirAnonymousFunctionChecker, FirCheckNotNullCallChecker, FirGetClassCallChecker, + FirSafeCallExpressionChecker, ) override val qualifiedAccessCheckers: Set = setOf( diff --git a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/ExpressionsConverter.kt b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/ExpressionsConverter.kt index dd0e65f123f..cbf8aedfb8b 100644 --- a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/ExpressionsConverter.kt +++ b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/ExpressionsConverter.kt @@ -490,7 +490,10 @@ class ExpressionsConverter( (firSelector as? FirQualifiedAccess)?.let { if (isSafe) { - return it.wrapWithSafeCall(firReceiver!!) + return it.wrapWithSafeCall( + firReceiver!!, + dotQualifiedExpression.toFirSourceElement(FirFakeSourceElementKind.DesugaredSafeCallExpression) + ) } it.replaceExplicitReceiver(firReceiver) diff --git a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt index c6eb37c17d2..3b292b79c95 100644 --- a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt +++ b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt @@ -1988,7 +1988,10 @@ open class RawFirBuilder( val receiver = expression.receiverExpression.toFirExpression("Incorrect receiver expression") if (expression is KtSafeQualifiedExpression) { - return firSelector.wrapWithSafeCall(receiver) + return firSelector.wrapWithSafeCall( + receiver, + expression.toFirSourceElement(FirFakeSourceElementKind.DesugaredSafeCallExpression) + ) } firSelector.replaceExplicitReceiver(receiver) diff --git a/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/ConversionUtils.kt b/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/ConversionUtils.kt index 2ba840ff299..9844bb625fc 100644 --- a/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/ConversionUtils.kt +++ b/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/ConversionUtils.kt @@ -37,6 +37,7 @@ import org.jetbrains.kotlin.fir.types.builder.* import org.jetbrains.kotlin.fir.types.impl.* import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.KtQualifiedExpression import org.jetbrains.kotlin.types.ConstantValueKind import org.jetbrains.kotlin.types.expressions.OperatorConventions import org.jetbrains.kotlin.util.OperatorNameConventions @@ -513,8 +514,7 @@ private fun FirExpression.checkReceiver(name: String?): Boolean { return receiverName == name } - -fun FirQualifiedAccess.wrapWithSafeCall(receiver: FirExpression): FirSafeCallExpression { +fun FirQualifiedAccess.wrapWithSafeCall(receiver: FirExpression, source: FirSourceElement): FirSafeCallExpression { val checkedSafeCallSubject = buildCheckedSafeCallSubject { @OptIn(FirContractViolation::class) this.originalReceiverRef = FirExpressionRef().apply { @@ -531,7 +531,7 @@ fun FirQualifiedAccess.wrapWithSafeCall(receiver: FirExpression): FirSafeCallExp bind(checkedSafeCallSubject) } this.regularQualifiedAccess = this@wrapWithSafeCall - this.source = this@wrapWithSafeCall.source?.fakeElement(FirFakeSourceElementKind.DesugaredSafeCallExpression) + this.source = source } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt index 59a0eeff0d2..820088df4f5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt @@ -421,6 +421,13 @@ object PositioningStrategies { } } + @JvmField + val SAFE_ACCESS: PositioningStrategy = object : PositioningStrategy() { + override fun mark(element: PsiElement): List { + return markElement(element.node.findChildByType(KtTokens.SAFE_ACCESS)?.psi ?: element) + } + } + private class ModifierSetBasedPositioningStrategy(private val modifierSet: TokenSet) : PositioningStrategy() { override fun mark(element: KtModifierListOwner): List { val modifierList = element.modifierList diff --git a/compiler/testData/diagnostics/tests/ExtensionCallInvoke.fir.kt b/compiler/testData/diagnostics/tests/ExtensionCallInvoke.fir.kt index 9e365c301f0..197f060bd67 100644 --- a/compiler/testData/diagnostics/tests/ExtensionCallInvoke.fir.kt +++ b/compiler/testData/diagnostics/tests/ExtensionCallInvoke.fir.kt @@ -1,6 +1,6 @@ fun bar(doIt: Int.() -> Int) { 1.doIt() - 1?.doIt() + 1?.doIt() val i: Int? = 1 i.doIt() i?.doIt() diff --git a/compiler/testData/diagnostics/tests/FunctionCalleeExpressions.fir.kt b/compiler/testData/diagnostics/tests/FunctionCalleeExpressions.fir.kt index e1b90e09b4f..7d5eaa6b3aa 100644 --- a/compiler/testData/diagnostics/tests/FunctionCalleeExpressions.fir.kt +++ b/compiler/testData/diagnostics/tests/FunctionCalleeExpressions.fir.kt @@ -82,6 +82,6 @@ fun test() { val i : Int? = null i.(fun Int.() = 1)(); {}() - 1?.(fun Int.() = 1)() + 1?.(fun Int.() = 1)() 1.{}() } diff --git a/compiler/testData/diagnostics/tests/SafeCallInvoke.fir.kt b/compiler/testData/diagnostics/tests/SafeCallInvoke.fir.kt index bc4d474faf8..6c2e3c5ee59 100644 --- a/compiler/testData/diagnostics/tests/SafeCallInvoke.fir.kt +++ b/compiler/testData/diagnostics/tests/SafeCallInvoke.fir.kt @@ -18,6 +18,6 @@ fun foo() { // these both also ok (with smart cast / unnecessary safe call) if (rule != null) { rule.apply() - rule?.apply() + rule?.apply() } -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/tests/SafeCallNonNullReceiver.fir.kt b/compiler/testData/diagnostics/tests/SafeCallNonNullReceiver.fir.kt deleted file mode 100644 index 4e4765234c8..00000000000 --- a/compiler/testData/diagnostics/tests/SafeCallNonNullReceiver.fir.kt +++ /dev/null @@ -1,6 +0,0 @@ -// http://youtrack.jetbrains.net/issue/KT-418 - -fun ff() { - val i: Int = 1 - val a: Int = i?.plus(2) -} diff --git a/compiler/testData/diagnostics/tests/SafeCallNonNullReceiver.kt b/compiler/testData/diagnostics/tests/SafeCallNonNullReceiver.kt index 5157eb39277..90aa053604b 100644 --- a/compiler/testData/diagnostics/tests/SafeCallNonNullReceiver.kt +++ b/compiler/testData/diagnostics/tests/SafeCallNonNullReceiver.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // http://youtrack.jetbrains.net/issue/KT-418 fun ff() { diff --git a/compiler/testData/diagnostics/tests/SafeCallNonNullReceiverReturnNull.fir.kt b/compiler/testData/diagnostics/tests/SafeCallNonNullReceiverReturnNull.fir.kt index ccdd7aa33cf..d48f7c25ee5 100644 --- a/compiler/testData/diagnostics/tests/SafeCallNonNullReceiverReturnNull.fir.kt +++ b/compiler/testData/diagnostics/tests/SafeCallNonNullReceiverReturnNull.fir.kt @@ -3,5 +3,5 @@ fun Int.gg() = null fun ff() { val a: Int = 1 - val b: Int = a?.gg() + val b: Int = a?.gg() } diff --git a/compiler/testData/diagnostics/tests/SafeCallOnFakePackage.fir.kt b/compiler/testData/diagnostics/tests/SafeCallOnFakePackage.fir.kt index 0567c846721..666e78bbe27 100644 --- a/compiler/testData/diagnostics/tests/SafeCallOnFakePackage.fir.kt +++ b/compiler/testData/diagnostics/tests/SafeCallOnFakePackage.fir.kt @@ -10,7 +10,7 @@ val s: String = "test" // FILE: test.kt fun ff() { - val a = Test?.FOO - val b = foo?.s - System?.out.println(a + b) + val a = Test?.FOO + val b = foo?.s + System?.out.println(a + b) } diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCallInReceiver.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCallInReceiver.fir.kt index 4056042ba8b..4ed01f752af 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCallInReceiver.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCallInReceiver.fir.kt @@ -5,7 +5,7 @@ fun test11() { fun test12() { fun Any.bar(i: Int) {} - todo()?.bar(1) + todo()?.bar(1) } -fun todo(): Nothing = throw Exception() \ No newline at end of file +fun todo(): Nothing = throw Exception() diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/throwInLambda.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/throwInLambda.fir.kt index 3a71250e7bf..f9bcee73563 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/throwInLambda.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/throwInLambda.fir.kt @@ -7,6 +7,6 @@ fun foo(): String { } fun bar(): String { val x = fn() ?: return "" - val y = x?.let { throw Exception() } ?: "unreachable" + val y = x?.let { throw Exception() } ?: "unreachable" return y -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/tests/extensions/ExtensionFunctions.fir.kt b/compiler/testData/diagnostics/tests/extensions/ExtensionFunctions.fir.kt index 0233c9fc64c..cdd860a98bf 100644 --- a/compiler/testData/diagnostics/tests/extensions/ExtensionFunctions.fir.kt +++ b/compiler/testData/diagnostics/tests/extensions/ExtensionFunctions.fir.kt @@ -10,7 +10,7 @@ fun T.foo(x : E, y : A) : T { y plus 1 y + 1.0 - this?.minus(this) + this?.minus(this) return this } @@ -70,7 +70,7 @@ import outer.* command?.equals1(null) val c = Command() - c?.equals2(null) + c?.equals2(null) if (command == null) 1 } diff --git a/compiler/testData/diagnostics/tests/generics/kt9985.fir.kt b/compiler/testData/diagnostics/tests/generics/kt9985.fir.kt index 7be3088b3d8..b26fd22ed88 100644 --- a/compiler/testData/diagnostics/tests/generics/kt9985.fir.kt +++ b/compiler/testData/diagnostics/tests/generics/kt9985.fir.kt @@ -27,15 +27,15 @@ fun foo(l: A?) { foo(l?.bar()) checkType { _() } foo(l?.gav()) checkType { _() } if (l != null) { - foo(l?.bar()) checkType { _() } - foo(l?.gav()) checkType { _() } + foo(l?.bar()) checkType { _() } + foo(l?.gav()) checkType { _() } } } fun fooNotNull(l: A) { // No errors should be here - foo(l?.bar()) checkType { _() } - foo(l?.gav()) checkType { _() } + foo(l?.bar()) checkType { _() } + foo(l?.gav()) checkType { _() } } fun bar() { diff --git a/compiler/testData/diagnostics/tests/generics/nullability/smartCasts.fir.kt b/compiler/testData/diagnostics/tests/generics/nullability/smartCasts.fir.kt index 1a3b014fcf5..e31f09c7d92 100644 --- a/compiler/testData/diagnostics/tests/generics/nullability/smartCasts.fir.kt +++ b/compiler/testData/diagnostics/tests/generics/nullability/smartCasts.fir.kt @@ -13,7 +13,7 @@ fun foo(x: T) { if (x != null) {} x.length - x?.length + x?.length x.bar1() x.bar2() @@ -21,14 +21,14 @@ fun foo(x: T) { x.bar4() - x?.bar1() + x?.bar1() } x.length if (x is String) { x.length - x?.length + x?.length x.bar1() x.bar2() @@ -37,7 +37,7 @@ fun foo(x: T) { if (x is CharSequence) { x.length - x?.length + x?.length x.bar1() x.bar2() diff --git a/compiler/testData/diagnostics/tests/generics/nullability/smartCastsOnThis.fir.kt b/compiler/testData/diagnostics/tests/generics/nullability/smartCastsOnThis.fir.kt index 49bf2c04035..b60da381b9f 100644 --- a/compiler/testData/diagnostics/tests/generics/nullability/smartCastsOnThis.fir.kt +++ b/compiler/testData/diagnostics/tests/generics/nullability/smartCastsOnThis.fir.kt @@ -12,7 +12,7 @@ fun T.foo() { if (this != null) {} length - this?.length + this?.length bar1() bar2() @@ -20,14 +20,14 @@ fun T.foo() { bar4() - this?.bar1() + this?.bar1() } length if (this is String) { length - this?.length + this?.length bar1() bar2() diff --git a/compiler/testData/diagnostics/tests/inference/findViewById.fir.kt b/compiler/testData/diagnostics/tests/inference/findViewById.fir.kt index 37c59b36098..dd62ae8ab67 100644 --- a/compiler/testData/diagnostics/tests/inference/findViewById.fir.kt +++ b/compiler/testData/diagnostics/tests/inference/findViewById.fir.kt @@ -55,5 +55,5 @@ fun test2(t: Test?) { val xSafeCallSafeCastExplicitType = t?.findViewById(0) as? X val xSafeCallCast = t?.findViewById(0) as X - val xSafeCallCastExplicitType = t?.findViewById(0) as X + val xSafeCallCastExplicitType = t?.findViewById(0) as X } diff --git a/compiler/testData/diagnostics/tests/inference/nestedCalls/makeNullableIfSafeCall.fir.kt b/compiler/testData/diagnostics/tests/inference/nestedCalls/makeNullableIfSafeCall.fir.kt index bb1c0fe22f1..145ff09c7ec 100644 --- a/compiler/testData/diagnostics/tests/inference/nestedCalls/makeNullableIfSafeCall.fir.kt +++ b/compiler/testData/diagnostics/tests/inference/nestedCalls/makeNullableIfSafeCall.fir.kt @@ -11,7 +11,7 @@ interface B { fun test(u: A?, x: A?, y: A?, z: A?, w: A, v: A?) { u?.b?.foo()!! // was UNNECESSARY_SAFE_CALL everywhere, because result type (of 'foo()') wasn't made nullable - u!!.b?.foo()!! + u!!.b?.foo()!! x?.b!!.foo()!! // x?.b is not null x!!.b!!.foo()!! @@ -22,7 +22,7 @@ fun test(u: A?, x: A?, y: A?, z: A?, w: A, v: A?) { // z?.nb is not null z!!.nb!!.foo()!! - w.b?.foo()!! + w.b?.foo()!! w.b!!.foo()!! w.nb?.foo()!! w.nb!!.foo()!! diff --git a/compiler/testData/diagnostics/tests/inference/publicApproximation/smartCastInLambdaReturnAfterIntersection.fir.kt b/compiler/testData/diagnostics/tests/inference/publicApproximation/smartCastInLambdaReturnAfterIntersection.fir.kt index 87713bd85f0..6e0f6a00d4b 100644 --- a/compiler/testData/diagnostics/tests/inference/publicApproximation/smartCastInLambdaReturnAfterIntersection.fir.kt +++ b/compiler/testData/diagnostics/tests/inference/publicApproximation/smartCastInLambdaReturnAfterIntersection.fir.kt @@ -18,5 +18,5 @@ fun smartCastAfterIntersection(a: One, b: Two) = run { } fun test(one: One, two: Two) { - smartCastAfterIntersection(one, two)?.base() + smartCastAfterIntersection(one, two)?.base() } diff --git a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/classGenericsInParamsIndexMismatch.fir.kt b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/classGenericsInParamsIndexMismatch.fir.kt deleted file mode 100644 index af66c73c557..00000000000 --- a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/classGenericsInParamsIndexMismatch.fir.kt +++ /dev/null @@ -1,34 +0,0 @@ -// MODULE: m1 -// FILE: a.kt -package p - -public interface B { - public fun foo(a: T?) -} - -// MODULE: m2(m1) -// FILE: b.kt -package p - -public interface C : B { - override fun foo(a: X?) - -} - -// MODULE: m3 -// FILE: b.kt -package p - -public interface B { - public fun foo(a: T?) -} - -// MODULE: m4(m3, m2) -// FILE: c.kt -import p.* - -fun test(b: B?) { - if (b is C) { - b?.foo(null) - } -} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/classGenericsInParamsIndexMismatch.kt b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/classGenericsInParamsIndexMismatch.kt index eaf8e3034e8..b8d15210917 100644 --- a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/classGenericsInParamsIndexMismatch.kt +++ b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/classGenericsInParamsIndexMismatch.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // MODULE: m1 // FILE: a.kt package p diff --git a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/classVsFunctionGenericsInParamsMismatch.fir.kt b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/classVsFunctionGenericsInParamsMismatch.fir.kt index 548dc513914..ef0f3eb4599 100644 --- a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/classVsFunctionGenericsInParamsMismatch.fir.kt +++ b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/classVsFunctionGenericsInParamsMismatch.fir.kt @@ -32,6 +32,6 @@ fun test(b: B?, c: C) { b?.foo(1, 1) c.foo(1, 1) if (b is C) { - b?.foo(1, 1) + b?.foo(1, 1) } } diff --git a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/differentNumberOfParams.fir.kt b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/differentNumberOfParams.fir.kt index 59575852fbf..3c5cd34f0cf 100644 --- a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/differentNumberOfParams.fir.kt +++ b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/differentNumberOfParams.fir.kt @@ -29,6 +29,6 @@ import p.* fun test(b: B?) { if (b is C) { - b?.foo(1, "") + b?.foo(1, "") } -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/simpleWithInheritance.fir.kt b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/simpleWithInheritance.fir.kt deleted file mode 100644 index ce16a4e7867..00000000000 --- a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/simpleWithInheritance.fir.kt +++ /dev/null @@ -1,38 +0,0 @@ -// MODULE: m1 -// FILE: a.kt -package p - -public interface B { - public fun getParent(): B? -} - -// MODULE: m2(m1) -// FILE: b.kt -package p - -public interface C : B { - override fun getParent(): B? - -} - -// MODULE: m3 -// FILE: b.kt -package p - -public interface B { - public fun getParent(): B? -} - -public interface D : B { - override fun getParent(): B? -} - -// MODULE: m4(m3, m2) -// FILE: c.kt -import p.* - -fun test(b: B?) { - if (b is C && b is D) { - b?.getParent() - } -} diff --git a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/simpleWithInheritance.kt b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/simpleWithInheritance.kt index 8dadc86fd5b..6cd0d5b6079 100644 --- a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/simpleWithInheritance.kt +++ b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/simpleWithInheritance.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // MODULE: m1 // FILE: a.kt package p diff --git a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/substitutedGenericInParams.fir.kt b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/substitutedGenericInParams.fir.kt deleted file mode 100644 index e46d3003573..00000000000 --- a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/substitutedGenericInParams.fir.kt +++ /dev/null @@ -1,34 +0,0 @@ -// MODULE: m1 -// FILE: a.kt -package p - -public interface B { - public fun foo(a: T) -} - -// MODULE: m2(m1) -// FILE: b.kt -package p - -public interface C : B { - override fun foo(a: String) - -} - -// MODULE: m3 -// FILE: b.kt -package p - -public interface B { - public fun foo(a: String) -} - -// MODULE: m4(m3, m2) -// FILE: c.kt -import p.* - -fun test(b: B?) { - if (b is C) { - b?.foo("") - } -} diff --git a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/substitutedGenericInParams.kt b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/substitutedGenericInParams.kt index 6266f2e7e4e..d022450957c 100644 --- a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/substitutedGenericInParams.kt +++ b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/substitutedGenericInParams.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // MODULE: m1 // FILE: a.kt package p diff --git a/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/ReceiverNullability.fir.kt b/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/ReceiverNullability.fir.kt deleted file mode 100644 index 37770900ab6..00000000000 --- a/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/ReceiverNullability.fir.kt +++ /dev/null @@ -1,44 +0,0 @@ -class A { - fun foo() {} -} - -fun A.bar() {} -fun A?.buzz() {} - -fun test(a : A?) { - a.foo() // error - a.bar() // error - a.buzz() - - a?.foo() - a?.bar() - a?.buzz() -} - -fun A.test2() { - foo() - bar() - buzz() - - this.foo() - this.bar() - this.buzz() - - this?.foo() // warning - this?.bar() // warning - this?.buzz() // warning -} - -fun A?.test3() { - foo() // error - bar() // error - buzz() - - this.foo() // error - this.bar() // error - this.buzz() - - this?.foo() - this?.bar() - this?.buzz() -} diff --git a/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/ReceiverNullability.kt b/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/ReceiverNullability.kt index 611bc9a395f..a28dba0f939 100644 --- a/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/ReceiverNullability.kt +++ b/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/ReceiverNullability.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL class A { fun foo() {} } diff --git a/compiler/testData/diagnostics/tests/nullableTypes/definitelyNotNullWithNullableBound.fir.kt b/compiler/testData/diagnostics/tests/nullableTypes/definitelyNotNullWithNullableBound.fir.kt index be145dfbf1e..f7f45ccabda 100644 --- a/compiler/testData/diagnostics/tests/nullableTypes/definitelyNotNullWithNullableBound.fir.kt +++ b/compiler/testData/diagnostics/tests/nullableTypes/definitelyNotNullWithNullableBound.fir.kt @@ -8,7 +8,7 @@ fun test(arg: N) { makeDefinitelyNotNull(arg)!! - makeDefinitelyNotNull(arg)?.toInt() + makeDefinitelyNotNull(arg)?.toInt() val nullImposible = when (val dnn = makeDefinitelyNotNull(arg)) { null -> false diff --git a/compiler/testData/diagnostics/tests/nullableTypes/safeCallOnTypeWithNullableUpperBound.fir.kt b/compiler/testData/diagnostics/tests/nullableTypes/safeCallOnTypeWithNullableUpperBound.fir.kt index 99e341d1bfa..75356611a22 100644 --- a/compiler/testData/diagnostics/tests/nullableTypes/safeCallOnTypeWithNullableUpperBound.fir.kt +++ b/compiler/testData/diagnostics/tests/nullableTypes/safeCallOnTypeWithNullableUpperBound.fir.kt @@ -1,14 +1,14 @@ // !WITH_NEW_INFERENCE fun test(t: T): String? { if (t != null) { - return t?.toString() + return t?.toString() } return t?.toString() } fun T.testThis(): String? { if (this != null) { - return this?.toString() + return this?.toString() } return this?.toString() } diff --git a/compiler/testData/diagnostics/tests/nullableTypes/safeCallOperators.fir.kt b/compiler/testData/diagnostics/tests/nullableTypes/safeCallOperators.fir.kt index d437896e0d8..edc29b3f97b 100644 --- a/compiler/testData/diagnostics/tests/nullableTypes/safeCallOperators.fir.kt +++ b/compiler/testData/diagnostics/tests/nullableTypes/safeCallOperators.fir.kt @@ -52,40 +52,40 @@ fun foo(a: A?) { a?.w.inc() if (a != null) { - a?.l += 1 - a?.l[0] - a?.l[0]++ - a?.l[0] = 1 + a?.l += 1 + a?.l[0] + a?.l[0]++ + a?.l[0] = 1 - a?.ll[0][0] - a?.ll[0][0]++ - a?.ll[0][0] = 1 + a?.ll[0][0] + a?.ll[0][0]++ + a?.ll[0][0] = 1 // No warning is reported because // 1. All kinds of green code with safe+call + invoke we identified fails with CCE if `a != null`, anyway // 2. In case of null value, the behavior is intended (no call performed) - a?.q() - a?.w++ + a?.q() + a?.w++ - (a?.l) += 1 - (a?.l)[0] - (a?.l)[0]++ - (a?.l)[0] = 1 + (a?.l) += 1 + (a?.l)[0] + (a?.l)[0]++ + (a?.l)[0] = 1 - (a?.ll)[0][0] - (a?.ll)[0][0]++ - (a?.ll)[0][0] = 1 - (a?.q)() - (a?.w)++ + (a?.ll)[0][0] + (a?.ll)[0][0]++ + (a?.ll)[0][0] = 1 + (a?.q)() + (a?.w)++ - a?.l.plusAssign(1) - a?.l.get(0) - a?.l.get(0).inc() - a?.l.set(0, 1) + a?.l.plusAssign(1) + a?.l.get(0) + a?.l.get(0).inc() + a?.l.set(0, 1) - a?.ll.get(0).get(0) - a?.ll.get(0).get(0).inc() - a?.ll.get(0).set(0, 1) - a?.q.invoke() - a?.w.inc() + a?.ll.get(0).get(0) + a?.ll.get(0).get(0).inc() + a?.ll.get(0).set(0, 1) + a?.q.invoke() + a?.w.inc() } } diff --git a/compiler/testData/diagnostics/tests/platformTypes/notNullTypeParameter/noInheritanceReturnType.fir.kt b/compiler/testData/diagnostics/tests/platformTypes/notNullTypeParameter/noInheritanceReturnType.fir.kt deleted file mode 100644 index 9e558217c08..00000000000 --- a/compiler/testData/diagnostics/tests/platformTypes/notNullTypeParameter/noInheritanceReturnType.fir.kt +++ /dev/null @@ -1,23 +0,0 @@ -// FILE: A.java - -import java.util.*; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class A { - public static A create() { - return null; - } - - @NotNull - public T bar() { - } -} - -// FILE: k.kt - -fun test() { - A.create().bar()?.length - A().bar()?.length -} diff --git a/compiler/testData/diagnostics/tests/platformTypes/notNullTypeParameter/noInheritanceReturnType.kt b/compiler/testData/diagnostics/tests/platformTypes/notNullTypeParameter/noInheritanceReturnType.kt index 36063274c75..c0654a53dee 100644 --- a/compiler/testData/diagnostics/tests/platformTypes/notNullTypeParameter/noInheritanceReturnType.kt +++ b/compiler/testData/diagnostics/tests/platformTypes/notNullTypeParameter/noInheritanceReturnType.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // FILE: A.java import java.util.*; diff --git a/compiler/testData/diagnostics/tests/platformTypes/notNullTypeParameter/onTypeProjection.fir.kt b/compiler/testData/diagnostics/tests/platformTypes/notNullTypeParameter/onTypeProjection.fir.kt deleted file mode 100644 index e0269c09835..00000000000 --- a/compiler/testData/diagnostics/tests/platformTypes/notNullTypeParameter/onTypeProjection.fir.kt +++ /dev/null @@ -1,19 +0,0 @@ -// FILE: A.java - -import java.util.*; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class A { - @NotNull - public T bar() { - } -} - -// FILE: k.kt - -fun test(a: A) { - a.bar()?.length - a.bar()?.length -} diff --git a/compiler/testData/diagnostics/tests/platformTypes/notNullTypeParameter/onTypeProjection.kt b/compiler/testData/diagnostics/tests/platformTypes/notNullTypeParameter/onTypeProjection.kt index 929afa676e9..88acff1c928 100644 --- a/compiler/testData/diagnostics/tests/platformTypes/notNullTypeParameter/onTypeProjection.kt +++ b/compiler/testData/diagnostics/tests/platformTypes/notNullTypeParameter/onTypeProjection.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // FILE: A.java import java.util.*; diff --git a/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/notNullTypeMarkedWithNullableAnnotation.fir.kt b/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/notNullTypeMarkedWithNullableAnnotation.fir.kt index 9e75d7e9557..bd05e0fb7f7 100644 --- a/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/notNullTypeMarkedWithNullableAnnotation.fir.kt +++ b/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/notNullTypeMarkedWithNullableAnnotation.fir.kt @@ -13,7 +13,7 @@ public class J { fun list(j: J): Any { val a = j.n()!! - a?.get(0) + a?.get(0) if (a == null) {} a!! diff --git a/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/safeCall.fir.kt b/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/safeCall.fir.kt deleted file mode 100644 index 6704268cb6d..00000000000 --- a/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/safeCall.fir.kt +++ /dev/null @@ -1,43 +0,0 @@ -// !DIAGNOSTICS: -SENSELESS_COMPARISON - -// FILE: J.java - -import org.jetbrains.annotations.*; - -public class J { - @NotNull - public static J staticNN; - @Nullable - public static J staticN; - public static J staticJ; - - public void foo() {} -} - -// FILE: k.kt - -fun test() { - // @NotNull platform type - val platformNN = J.staticNN - // @Nullable platform type - val platformN = J.staticN - // platform type with no annotation - val platformJ = J.staticJ - - platformNN?.foo() - platformN?.foo() - platformJ?.foo() - - if (platformNN != null) { - platformNN?.foo() - } - - if (platformN != null) { - platformN?.foo() - } - - if (platformJ != null) { - platformJ?.foo() - } -} - diff --git a/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/safeCall.kt b/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/safeCall.kt index 0e3cb21c2fe..33936d30be9 100644 --- a/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/safeCall.kt +++ b/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/safeCall.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -SENSELESS_COMPARISON // FILE: J.java diff --git a/compiler/testData/diagnostics/tests/regressions/Jet72.fir.kt b/compiler/testData/diagnostics/tests/regressions/Jet72.fir.kt index accd324d8b6..aec263fca6c 100644 --- a/compiler/testData/diagnostics/tests/regressions/Jet72.fir.kt +++ b/compiler/testData/diagnostics/tests/regressions/Jet72.fir.kt @@ -12,7 +12,7 @@ fun test(room : Object) { for(item: Item? in items) { if (item?.room === room) { // item?.room is not null - System.out.println("You see " + item?.name) + System.out.println("You see " + item?.name) } } -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/tests/regressions/SpecififcityByReceiver.fir.kt b/compiler/testData/diagnostics/tests/regressions/SpecififcityByReceiver.fir.kt index 4133e61a048..4c822ec5d50 100644 --- a/compiler/testData/diagnostics/tests/regressions/SpecififcityByReceiver.fir.kt +++ b/compiler/testData/diagnostics/tests/regressions/SpecififcityByReceiver.fir.kt @@ -4,6 +4,6 @@ fun main() { val command : Any = 1 - command?.equals(null) + command?.equals(null) command.equals(null) } diff --git a/compiler/testData/diagnostics/tests/resolve/invoke/kt30695_2.fir.kt b/compiler/testData/diagnostics/tests/resolve/invoke/kt30695_2.fir.kt index 1bdeabc1273..9e377fda6eb 100644 --- a/compiler/testData/diagnostics/tests/resolve/invoke/kt30695_2.fir.kt +++ b/compiler/testData/diagnostics/tests/resolve/invoke/kt30695_2.fir.kt @@ -31,7 +31,7 @@ fun testNullableReceiver(nullable: Cls?) { fun testNotNullableReceiver(notNullable: Cls) { notNullable.nullableExtensionProperty() - notNullable?.extensionProperty() + notNullable?.extensionProperty() } fun testFlexibleReceiver() { diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/nullableReceiverWithExclExcl.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/nullableReceiverWithExclExcl.fir.kt index a3800971019..483d02dedc5 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/safecalls/nullableReceiverWithExclExcl.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/nullableReceiverWithExclExcl.fir.kt @@ -5,7 +5,7 @@ fun test(foo: Foo?) { // Correct foo.bar?.length // Unnecessary - foo?.bar?.length + foo?.bar?.length } - foo.bar?.length -} \ No newline at end of file + foo.bar?.length +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/assert/cast.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/assert/cast.fir.kt index 11505fccf9a..b7bddc0ecd4 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/assert/cast.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/assert/cast.fir.kt @@ -17,5 +17,5 @@ fun test1(a: A) { fun test2() { val a: A? = null; assert((a as B).bool()) - a?.bool() + a?.bool() } diff --git a/compiler/testData/diagnostics/testsWithStdLib/assert/safeCall.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/assert/safeCall.fir.kt index d5abe132680..5049110bfc8 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/assert/safeCall.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/assert/safeCall.fir.kt @@ -5,7 +5,7 @@ fun test1(s: String?) { assert(s!!.isEmpty()) - s?.length + s?.length } fun test2(s: String?) { @@ -21,7 +21,7 @@ fun test3(s: String?) { fun test4() { val s: String? = null; assert(s!!.isEmpty()) - s?.length + s?.length } fun test5() { diff --git a/compiler/testData/diagnostics/testsWithStdLib/resolve/javaStaticMembers.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/resolve/javaStaticMembers.fir.kt index b6bfd0b64fa..36ab797b176 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/resolve/javaStaticMembers.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/resolve/javaStaticMembers.fir.kt @@ -6,7 +6,7 @@ public class Test { // FILE: test.kt fun ff() { val a = Test.FOO - val b = Test?.FOO + val b = Test?.FOO System.out.println(a + b) - System?.out.println(a + b) + System?.out.println(a + b) } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/smartcasts/pos/2.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/smartcasts/pos/2.fir.kt index 3afea34242b..a8b1adcedaa 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/smartcasts/pos/2.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/smartcasts/pos/2.fir.kt @@ -27,7 +27,7 @@ fun case_3(value_1: Any?, value_2: Any?) { fun case_4(value_1: Any?, value_2: Number?) { funWithReturns(value_1 is Float? && value_1 != null && value_2 != null) println(value_1.dec()) - println(value_2?.toByte()) + println(value_2?.toByte()) } // TESTCASE NUMBER: 5 @@ -38,7 +38,7 @@ class case_5_class { val o = case_5_class() funWithReturns(value_1 is Float? && value_1 != null && value_2 != null && o.prop_1 != null) println(value_1.dec()) - println(value_2?.toByte()) + println(value_2?.toByte()) println(o.prop_1.plus(3)) } } @@ -107,19 +107,19 @@ fun case_8(value_1: Any?, value_2: Any?) { fun case_9(value_1: Any?, value_2: Number?) { if (funWithReturnsTrue(value_1 is Float? && value_1 != null && value_2 != null)) { println(value_1.dec()) - println(value_2?.toByte()) + println(value_2?.toByte()) } if (!funWithReturnsFalse(value_1 is Float? && value_1 != null && value_2 != null)) { println(value_1.dec()) - println(value_2?.toByte()) + println(value_2?.toByte()) } if (funWithReturnsNotNull(value_1 is Float? && value_1 != null && value_2 != null) != null) { println(value_1.dec()) - println(value_2?.toByte()) + println(value_2?.toByte()) } if (funWithReturnsNull(value_1 is Float? && value_1 != null && value_2 != null) == null) { println(value_1.dec()) - println(value_2?.toByte()) + println(value_2?.toByte()) } } @@ -131,22 +131,22 @@ class case_10_class { val o = case_10_class() if (funWithReturnsTrue(value_1 is Float? && value_1 != null && value_2 != null && o.prop_1 != null && this.prop_1 != null)) { println(value_1.dec()) - println(value_2?.toByte()) + println(value_2?.toByte()) println(o.prop_1.plus(3)) } if (!funWithReturnsFalse(value_1 is Float? && value_1 != null && value_2 != null && o.prop_1 != null && this.prop_1 != null)) { println(value_1.dec()) - println(value_2?.toByte()) + println(value_2?.toByte()) println(o.prop_1.plus(3)) } if (funWithReturnsNotNull(value_1 is Float? && value_1 != null && value_2 != null && o.prop_1 != null && this.prop_1 != null) != null) { println(value_1.dec()) - println(value_2?.toByte()) + println(value_2?.toByte()) println(o.prop_1.plus(3)) } if (funWithReturnsNull(value_1 is Float? && value_1 != null && value_2 != null && o.prop_1 != null && this.prop_1 != null) == null) { println(value_1.dec()) - println(value_2?.toByte()) + println(value_2?.toByte()) println(o.prop_1.plus(3)) } } @@ -160,5 +160,5 @@ class case_10_class { fun case_11(value_1: Any?, value_2: Any?, value_3: Any?) { funWithReturnsAndInvertCondition(value_1 !is String || value_2 !is Number || value_3 !is Any?) println(value_1!!.length) - println(value_2?.toByte()) + println(value_2?.toByte()) } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/smartcasts/pos/3.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/smartcasts/pos/3.fir.kt index 18a9970141c..c0bfd6feafe 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/smartcasts/pos/3.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/smartcasts/pos/3.fir.kt @@ -103,7 +103,7 @@ class case_3_class { val o = case_3_class() contracts.case_3(value_1, value_2, o.prop_1, this.prop_1) println(value_1.dec()) - println(value_2?.toByte()) + println(value_2?.toByte()) println(o.prop_1.plus(3)) } } @@ -155,22 +155,22 @@ class case_6_class { val o = case_6_class() if (contracts.case_6_1(value_1, value_2, o.prop_1, this.prop_1)) { println(value_1.dec()) - println(value_2?.toByte()) + println(value_2?.toByte()) println(o.prop_1.plus(3)) } if (!contracts.case_6_2(value_1, value_2, o.prop_1, this.prop_1)) { println(value_1.dec()) - println(value_2?.toByte()) + println(value_2?.toByte()) println(o.prop_1.plus(3)) } if (contracts.case_6_3(value_1, value_2, o.prop_1, this.prop_1) != null) { println(value_1.dec()) - println(value_2?.toByte()) + println(value_2?.toByte()) println(o.prop_1.plus(3)) } if (contracts.case_6_4(value_1, value_2, o.prop_1, this.prop_1) == null) { println(value_1.dec()) - println(value_2?.toByte()) + println(value_2?.toByte()) println(o.prop_1.plus(3)) } } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/2.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/2.fir.kt index f1f736785a8..e03dfe54da5 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/2.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/2.fir.kt @@ -38,7 +38,7 @@ fun case_4(x: Any) { fun case_5(x: Any?) { if (!(x !is Nothing?)) { x - x?.inv() + x?.inv() } } @@ -70,7 +70,7 @@ fun case_8(x: Any?) { fun case_9(x: Any?) { if (!!(x !is Nothing?)) else { x - x?.inv() + x?.inv() } } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/30.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/30.fir.kt index d19375bc06d..2af4f1c76a9 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/30.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/30.fir.kt @@ -53,7 +53,7 @@ fun case_3(x: Class?) { * ISSUES: KT-30376 */ fun case_4(x: Class?) { - if (x!!?.prop_8?.prop_8?.prop_8?.prop_8 == null == true) else { + if (x!!?.prop_8?.prop_8?.prop_8?.prop_8 == null == true) else { x x.prop_8 x.prop_8.prop_8 @@ -68,7 +68,7 @@ fun case_4(x: Class?) { * ISSUES: KT-30376 */ fun case_5(x: Class?) { - if (x?.prop_8!!?.prop_8?.prop_8?.prop_8 == null == true) else { + if (x?.prop_8!!?.prop_8?.prop_8?.prop_8 == null == true) else { x x.prop_8 x.prop_8.prop_8 @@ -83,7 +83,7 @@ fun case_5(x: Class?) { * ISSUES: KT-30376 */ fun case_6(x: Class?) { - if (x?.prop_8?.prop_8?.prop_8!!?.prop_8 == null == true) else { + if (x?.prop_8?.prop_8?.prop_8!!?.prop_8 == null == true) else { x x.prop_8 x.prop_8.prop_8 diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt index 14701db72d5..89c964d3546 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -1535,6 +1535,19 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.UNNECESSARY_SAFE_CALL) { firDiagnostic -> + UnnecessarySafeCallImpl( + firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a), + firDiagnostic as FirPsiDiagnostic<*>, + token, + ) + } + add(FirErrors.UNEXPECTED_SAFE_CALL) { firDiagnostic -> + UnexpectedSafeCallImpl( + firDiagnostic as FirPsiDiagnostic<*>, + token, + ) + } add(FirErrors.UNNECESSARY_NOT_NULL_ASSERTION) { firDiagnostic -> UnnecessaryNotNullAssertionImpl( firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a), diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt index 44210adea3e..86db3e71b3a 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt @@ -1087,6 +1087,15 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { abstract val rhs: KtExpression } + abstract class UnnecessarySafeCall : KtFirDiagnostic() { + override val diagnosticClass get() = UnnecessarySafeCall::class + abstract val receiverType: KtType + } + + abstract class UnexpectedSafeCall : KtFirDiagnostic() { + override val diagnosticClass get() = UnexpectedSafeCall::class + } + abstract class UnnecessaryNotNullAssertion : KtFirDiagnostic() { override val diagnosticClass get() = UnnecessaryNotNullAssertion::class abstract val receiverType: KtType diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index ba6f91d193b..bd40bccbdf2 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -1754,6 +1754,21 @@ internal class UnsafeOperatorCallImpl( override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) } +internal class UnnecessarySafeCallImpl( + override val receiverType: KtType, + firDiagnostic: FirPsiDiagnostic<*>, + override val token: ValidityToken, +) : KtFirDiagnostic.UnnecessarySafeCall(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) +} + +internal class UnexpectedSafeCallImpl( + firDiagnostic: FirPsiDiagnostic<*>, + override val token: ValidityToken, +) : KtFirDiagnostic.UnexpectedSafeCall(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) +} + internal class UnnecessaryNotNullAssertionImpl( override val receiverType: KtType, firDiagnostic: FirPsiDiagnostic<*>,