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 280b6f124fc..e42699b4888 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 @@ -42,6 +42,7 @@ enum class PositioningStrategy(private val strategy: String) { IF_EXPRESSION("IF_EXPRESSION"), VARIANCE_MODIFIER("VARIANCE_MODIFIER"), LATEINIT_MODIFIER("LATEINIT_MODIFIER"), + SELECTOR_BY_QUALIFIED("SELECTOR_BY_QUALIFIED"), ; 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 8cb30a29927..7a2ea3c0abf 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 @@ -388,7 +388,7 @@ object DIAGNOSTICS_LIST : DiagnosticList() { } val FUNCTION_CONTRACTS by object : DiagnosticGroup("Function contracts") { - val ERROR_IN_CONTRACT_DESCRIPTION by error { + val ERROR_IN_CONTRACT_DESCRIPTION by error(PositioningStrategy.SELECTOR_BY_QUALIFIED) { parameter("reason") } } @@ -401,7 +401,7 @@ object DIAGNOSTICS_LIST : DiagnosticList() { val REDUNDANT_SINGLE_EXPRESSION_STRING_TEMPLATE by warning() val CAN_BE_VAL by warning(PositioningStrategy.VAL_OR_VAR_NODE) val CAN_BE_REPLACED_WITH_OPERATOR_ASSIGNMENT by warning(PositioningStrategy.OPERATOR) - val REDUNDANT_CALL_OF_CONVERSION_METHOD by warning() + val REDUNDANT_CALL_OF_CONVERSION_METHOD by warning(PositioningStrategy.SELECTOR_BY_QUALIFIED) val ARRAY_EQUALITY_OPERATOR_CAN_BE_REPLACED_WITH_EQUALS by warning(PositioningStrategy.OPERATOR) val EMPTY_RANGE by warning() val REDUNDANT_SETTER_PARAMETER_TYPE by warning() @@ -409,7 +409,7 @@ object DIAGNOSTICS_LIST : DiagnosticList() { val ASSIGNED_VALUE_IS_NEVER_READ by warning() val VARIABLE_INITIALIZER_IS_REDUNDANT by warning() val VARIABLE_NEVER_READ by warning(PositioningStrategy.DECLARATION_NAME) - val USELESS_CALL_ON_NOT_NULL by warning() + val USELESS_CALL_ON_NOT_NULL by warning(PositioningStrategy.SELECTOR_BY_QUALIFIED) } } 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 0133cf8126a..e0fd06ce1bc 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 @@ -244,7 +244,7 @@ object FirErrors { val INVALID_IF_AS_EXPRESSION by error0(SourceElementPositioningStrategies.IF_EXPRESSION) // Function contracts - val ERROR_IN_CONTRACT_DESCRIPTION by error1() + val ERROR_IN_CONTRACT_DESCRIPTION by error1(SourceElementPositioningStrategies.SELECTOR_BY_QUALIFIED) // Extended checkers val REDUNDANT_VISIBILITY_MODIFIER by warning0(SourceElementPositioningStrategies.VISIBILITY_MODIFIER) @@ -254,7 +254,7 @@ object FirErrors { val REDUNDANT_SINGLE_EXPRESSION_STRING_TEMPLATE by warning0() val CAN_BE_VAL by warning0(SourceElementPositioningStrategies.VAL_OR_VAR_NODE) val CAN_BE_REPLACED_WITH_OPERATOR_ASSIGNMENT by warning0(SourceElementPositioningStrategies.OPERATOR) - val REDUNDANT_CALL_OF_CONVERSION_METHOD by warning0() + val REDUNDANT_CALL_OF_CONVERSION_METHOD by warning0(SourceElementPositioningStrategies.SELECTOR_BY_QUALIFIED) val ARRAY_EQUALITY_OPERATOR_CAN_BE_REPLACED_WITH_EQUALS by warning0(SourceElementPositioningStrategies.OPERATOR) val EMPTY_RANGE by warning0() val REDUNDANT_SETTER_PARAMETER_TYPE by warning0() @@ -262,6 +262,6 @@ object FirErrors { val ASSIGNED_VALUE_IS_NEVER_READ by warning0() val VARIABLE_INITIALIZER_IS_REDUNDANT by warning0() val VARIABLE_NEVER_READ by warning0(SourceElementPositioningStrategies.DECLARATION_NAME) - val USELESS_CALL_ON_NOT_NULL by warning0() + val USELESS_CALL_ON_NOT_NULL by warning0(SourceElementPositioningStrategies.SELECTOR_BY_QUALIFIED) } 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 f4b21c77cbb..da3031de9b1 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 @@ -335,6 +335,24 @@ object LightTreePositioningStrategies { } } + val SELECTOR_BY_QUALIFIED: LightTreePositioningStrategy = object : LightTreePositioningStrategy() { + override fun mark( + node: LighterASTNode, + startOffset: Int, + endOffset: Int, + tree: FlyweightCapableTreeStructure + ): List { + if (node.tokenType != KtNodeTypes.DOT_QUALIFIED_EXPRESSION && node.tokenType != KtNodeTypes.SAFE_ACCESS_EXPRESSION) { + return super.mark(node, startOffset, endOffset, tree) + } + val selector = tree.selector(node) + if (selector != null) { + return markElement(selector, startOffset, endOffset, tree, node) + } + return super.mark(node, startOffset, endOffset, tree) + } + } + val WHEN_EXPRESSION = object : LightTreePositioningStrategy() { override fun mark( node: LighterASTNode, @@ -448,6 +466,25 @@ private fun FlyweightCapableTreeStructure.defaultValue(node: Lig return null } +private fun FlyweightCapableTreeStructure.selector(node: LighterASTNode): LighterASTNode? { + val childrenRef = Ref>() + getChildren(node, childrenRef) + val children = childrenRef.get() ?: return null + var dotFound = false + for (child in children) { + if (child == null) continue + if (child.tokenType == KtTokens.DOT) { + dotFound = true + continue + } + if (dotFound && (child.tokenType == KtNodeTypes.CALL_EXPRESSION || child.tokenType == KtNodeTypes.REFERENCE_EXPRESSION)) { + return child + } + } + return null + +} + fun FlyweightCapableTreeStructure.findChildByType(node: LighterASTNode, type: IElementType): LighterASTNode? { val childrenRef = Ref>() getChildren(node, childrenRef) 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 4d4e326d73f..804e1626282 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 @@ -103,6 +103,11 @@ object SourceElementPositioningStrategies { PositioningStrategies.DOT_BY_SELECTOR ) + val SELECTOR_BY_QUALIFIED = SourceElementPositioningStrategy( + LightTreePositioningStrategies.SELECTOR_BY_QUALIFIED, + PositioningStrategies.SELECTOR_BY_QUALIFIED + ) + val WHEN_EXPRESSION = SourceElementPositioningStrategy( LightTreePositioningStrategies.WHEN_EXPRESSION, PositioningStrategies.WHEN_EXPRESSION diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt index 65684cefd04..ee35b392ce8 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt @@ -725,4 +725,15 @@ object PositioningStrategies { return super.mark(element) } } + + val SELECTOR_BY_QUALIFIED: PositioningStrategy = object : PositioningStrategy() { + override fun mark(element: PsiElement): List { + if (element is KtQualifiedExpression) { + when (val selectorExpression = element.selectorExpression) { + is KtCallExpression, is KtReferenceExpression -> return mark(selectorExpression) + } + } + return super.mark(element) + } + } }