diff --git a/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/UsePropertyAccessSyntaxInspection.kt b/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/UsePropertyAccessSyntaxInspection.kt index 1dc3815ba482..d65f456a321e 100644 --- a/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/UsePropertyAccessSyntaxInspection.kt +++ b/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/UsePropertyAccessSyntaxInspection.kt @@ -405,9 +405,7 @@ class UsePropertyAccessSyntaxInspection : LocalInspectionTool(), CleanupLocalIns propertyName: String ): Boolean { val allOverriddenSymbols = symbol.allOverriddenSymbolsWithSelf.toList() - // allOverriddenSymbolsWithSelf is a sequence, and if the last element in the list it forms is not a Java symbol, - // then it means it's not for our inspection. This list can't be empty because it contains the `symbol` itself - if (!allOverriddenSymbols.last().origin.isJavaSourceOrLibrary()) return false + if (!functionOriginateFromJava(allOverriddenSymbols)) return false if (functionOrItsAncestorIsInNotPropertiesList(allOverriddenSymbols, callExpression)) return false // Check that the receiver or its ancestors don't have public fields with the same name as the probable synthetic property @@ -573,6 +571,20 @@ class UsePropertyAccessSyntaxInspection : LocalInspectionTool(), CleanupLocalIns return false } + context(KaSession) + private fun functionOriginateFromJava(allOverriddenSymbols: List): Boolean { + // Calling `.reversed()` – small optimization because the last Java symbol in the list more probable doesn't have overrides + val javaSymbols = allOverriddenSymbols.filter { it.origin.isJavaSourceOrLibrary() }.reversed() + if (javaSymbols.isEmpty()) return false + for (javaSymbol in javaSymbols) { + if (javaSymbol.allOverriddenSymbols.toList().isEmpty()) { + // Nothing overrides it, true Java origin + return true + } + } + return false + } + /** * Check if this set() is used after `return` * Covered with the test "dontReplaceSetterIfItGoesAfterReturn" diff --git a/plugins/kotlin/code-insight/inspections-k2/tests/test/org/jetbrains/kotlin/idea/k2/inspections/tests/K2LocalInspectionTestGenerated.java b/plugins/kotlin/code-insight/inspections-k2/tests/test/org/jetbrains/kotlin/idea/k2/inspections/tests/K2LocalInspectionTestGenerated.java index 0e771dd8804f..1c96761f77ac 100644 --- a/plugins/kotlin/code-insight/inspections-k2/tests/test/org/jetbrains/kotlin/idea/k2/inspections/tests/K2LocalInspectionTestGenerated.java +++ b/plugins/kotlin/code-insight/inspections-k2/tests/test/org/jetbrains/kotlin/idea/k2/inspections/tests/K2LocalInspectionTestGenerated.java @@ -6555,6 +6555,11 @@ public abstract class K2LocalInspectionTestGenerated extends AbstractK2LocalInsp runTest("../../../idea/tests/testData/inspectionsLocal/usePropertyAccessSyntax/dotQualifiedExpressions/replaceSetterToIsSetter.kt"); } + @TestMetadata("replaceWhenMultipleInheritance.kt") + public void testReplaceWhenMultipleInheritance() throws Exception { + runTest("../../../idea/tests/testData/inspectionsLocal/usePropertyAccessSyntax/dotQualifiedExpressions/replaceWhenMultipleInheritance.kt"); + } + @TestMetadata("replaceWithBackticks.kt") public void testReplaceWithBackticks() throws Exception { runTest("../../../idea/tests/testData/inspectionsLocal/usePropertyAccessSyntax/dotQualifiedExpressions/replaceWithBackticks.kt"); diff --git a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index 935acb92cd53..d7afc6c5157f 100644 --- a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -19477,6 +19477,11 @@ public abstract class LocalInspectionTestGenerated extends AbstractLocalInspecti runTest("testData/inspectionsLocal/usePropertyAccessSyntax/dotQualifiedExpressions/replaceSetterToIsSetter.kt"); } + @TestMetadata("replaceWhenMultipleInheritance.kt") + public void testReplaceWhenMultipleInheritance() throws Exception { + runTest("testData/inspectionsLocal/usePropertyAccessSyntax/dotQualifiedExpressions/replaceWhenMultipleInheritance.kt"); + } + @TestMetadata("replaceWithBackticks.kt") public void testReplaceWithBackticks() throws Exception { runTest("testData/inspectionsLocal/usePropertyAccessSyntax/dotQualifiedExpressions/replaceWithBackticks.kt"); diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/usePropertyAccessSyntax/dotQualifiedExpressions/dontReplaceImplicitOverrideOfKotlinMethod.1.java b/plugins/kotlin/idea/tests/testData/inspectionsLocal/usePropertyAccessSyntax/dotQualifiedExpressions/dontReplaceImplicitOverrideOfKotlinMethod.1.java index 44c5218838e1..be3c9231e307 100644 --- a/plugins/kotlin/idea/tests/testData/inspectionsLocal/usePropertyAccessSyntax/dotQualifiedExpressions/dontReplaceImplicitOverrideOfKotlinMethod.1.java +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/usePropertyAccessSyntax/dotQualifiedExpressions/dontReplaceImplicitOverrideOfKotlinMethod.1.java @@ -1,4 +1,5 @@ class JavaChild implements KotlinBase { + // missing @Override is intentional public String getName() { return ""; } diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/usePropertyAccessSyntax/dotQualifiedExpressions/replaceWhenMultipleInheritance.1.java b/plugins/kotlin/idea/tests/testData/inspectionsLocal/usePropertyAccessSyntax/dotQualifiedExpressions/replaceWhenMultipleInheritance.1.java new file mode 100644 index 000000000000..c6aa7c957ace --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/usePropertyAccessSyntax/dotQualifiedExpressions/replaceWhenMultipleInheritance.1.java @@ -0,0 +1,3 @@ +public interface JavaName { + String getName(); +} diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/usePropertyAccessSyntax/dotQualifiedExpressions/replaceWhenMultipleInheritance.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/usePropertyAccessSyntax/dotQualifiedExpressions/replaceWhenMultipleInheritance.kt new file mode 100644 index 000000000000..0ebd4f173fa1 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/usePropertyAccessSyntax/dotQualifiedExpressions/replaceWhenMultipleInheritance.kt @@ -0,0 +1,16 @@ +// FIX: Use property access syntax +fun main() { + val w = ClassInheritingBoth() + w.getName() +} + +class ClassInheritingBoth: JavaName, KotlinName { + + override fun getName(): String { + return "String" + } +} + +interface KotlinName { + fun getName(): String +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/usePropertyAccessSyntax/dotQualifiedExpressions/replaceWhenMultipleInheritance.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/usePropertyAccessSyntax/dotQualifiedExpressions/replaceWhenMultipleInheritance.kt.after new file mode 100644 index 000000000000..1456e483666c --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/usePropertyAccessSyntax/dotQualifiedExpressions/replaceWhenMultipleInheritance.kt.after @@ -0,0 +1,16 @@ +// FIX: Use property access syntax +fun main() { + val w = ClassInheritingBoth() + w.name +} + +class ClassInheritingBoth: JavaName, KotlinName { + + override fun getName(): String { + return "String" + } +} + +interface KotlinName { + fun getName(): String +} \ No newline at end of file