diff --git a/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/OverrideOnlyInspection.kt b/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/OverrideOnlyInspection.kt
index 34308179d769..624c1971eb8a 100644
--- a/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/OverrideOnlyInspection.kt
+++ b/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/OverrideOnlyInspection.kt
@@ -1,15 +1,14 @@
-// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInspection
import com.intellij.analysis.JvmAnalysisBundle
import com.intellij.codeInsight.daemon.impl.analysis.HighlightMessageUtil
import com.intellij.codeInspection.apiUsage.ApiUsageProcessor
import com.intellij.codeInspection.apiUsage.ApiUsageUastVisitor
+import com.intellij.lang.jvm.JvmModifier
import com.intellij.openapi.roots.ProjectFileIndex
-import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.PsiMethod
-import com.intellij.psi.PsiModifier
import com.intellij.psi.PsiModifierListOwner
import com.intellij.psi.util.MethodSignatureUtil
import com.intellij.psi.util.PsiUtilCore
@@ -28,7 +27,6 @@ private inline val ANNOTATION_NAME get() = ApiStatus.OverrideOnly::class.java.ca
*/
@VisibleForTesting
class OverrideOnlyInspection : LocalInspectionTool() {
-
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor =
if (AnnotatedApiUsageUtil.canAnnotationBeUsedInFile(ANNOTATION_NAME, holder.file)) {
ApiUsageUastVisitor.createPsiElementVisitor(OverrideOnlyProcessor(holder))
@@ -38,19 +36,21 @@ class OverrideOnlyInspection : LocalInspectionTool() {
}
private class OverrideOnlyProcessor(private val problemsHolder: ProblemsHolder) : ApiUsageProcessor {
-
- private fun isLibraryElement(element: PsiElement): Boolean {
- val containingVirtualFile = PsiUtilCore.getVirtualFile(element)
- return containingVirtualFile != null && ProjectFileIndex.getInstance(element.project).isInLibraryClasses(containingVirtualFile)
+ private fun PsiMethod.isOverridable(): Boolean {
+ return !hasModifier(JvmModifier.PRIVATE) && !hasModifier(JvmModifier.FINAL) && !hasModifier(JvmModifier.STATIC)
}
- private fun isOverrideOnlyMethod(method: PsiMethod) =
- method.hasAnnotation(ANNOTATION_NAME) ||
- !method.hasModifierProperty(PsiModifier.STATIC) && method.containingClass?.hasAnnotation(ANNOTATION_NAME) == true
+ private fun isLibraryElement(method: PsiMethod): Boolean {
+ val containingVirtualFile = PsiUtilCore.getVirtualFile(method)
+ return containingVirtualFile != null && ProjectFileIndex.getInstance(method.project).isInLibraryClasses(containingVirtualFile)
+ }
+
+ private fun isOverrideOnlyMethod(method: PsiMethod): Boolean {
+ return method.hasAnnotation(ANNOTATION_NAME) || method.containingClass?.hasAnnotation(ANNOTATION_NAME) == true
+ }
private fun isInsideOverridenOnlyMethod(sourceNode: UElement, target: PsiMethod): Boolean = sourceNode.getContainingUMethod()?.let {
- val psiMethod = it.javaPsi as? PsiMethod ?: return false
- MethodSignatureUtil.areSignaturesEqual(psiMethod, target)
+ MethodSignatureUtil.areSignaturesEqual(it.javaPsi, target)
} ?: false
private fun isSuperCall(qualifier: UExpression?) = qualifier is USuperExpression
@@ -67,7 +67,7 @@ class OverrideOnlyInspection : LocalInspectionTool() {
}
override fun processReference(sourceNode: UElement, target: PsiModifierListOwner, qualifier: UExpression?) {
- if (target is PsiMethod && isOverrideOnlyMethod(target) && isLibraryElement(target)
+ if (target is PsiMethod && target.isOverridable() && isLibraryElement(target) && isOverrideOnlyMethod(target)
&& !isSuperOrDelegateCall(sourceNode, target, qualifier)) {
val elementToHighlight = sourceNode.sourcePsi ?: return
val methodName = HighlightMessageUtil.getSymbolName(target) ?: return
diff --git a/jvm/jvm-analysis-kotlin-tests-shared/testData/codeInspection/overrideOnly/library/JavaClassOverrideOnly.class b/jvm/jvm-analysis-kotlin-tests-shared/testData/codeInspection/overrideOnly/library/JavaClassOverrideOnly.class
index 174e7095abaf..6d1c3afc0272 100644
Binary files a/jvm/jvm-analysis-kotlin-tests-shared/testData/codeInspection/overrideOnly/library/JavaClassOverrideOnly.class and b/jvm/jvm-analysis-kotlin-tests-shared/testData/codeInspection/overrideOnly/library/JavaClassOverrideOnly.class differ
diff --git a/jvm/jvm-analysis-kotlin-tests-shared/testData/codeInspection/overrideOnly/library/JavaMembersOverrideOnly.class b/jvm/jvm-analysis-kotlin-tests-shared/testData/codeInspection/overrideOnly/library/JavaMembersOverrideOnly.class
new file mode 100644
index 000000000000..12582f528ea1
Binary files /dev/null and b/jvm/jvm-analysis-kotlin-tests-shared/testData/codeInspection/overrideOnly/library/JavaMembersOverrideOnly.class differ
diff --git a/jvm/jvm-analysis-kotlin-tests-shared/testData/codeInspection/overrideOnly/plugin/JavaCode.java b/jvm/jvm-analysis-kotlin-tests-shared/testData/codeInspection/overrideOnly/plugin/JavaCode.java
index 19ea325b8da7..6b9b508a6fe0 100644
--- a/jvm/jvm-analysis-kotlin-tests-shared/testData/codeInspection/overrideOnly/plugin/JavaCode.java
+++ b/jvm/jvm-analysis-kotlin-tests-shared/testData/codeInspection/overrideOnly/plugin/JavaCode.java
@@ -8,6 +8,7 @@ import library.KotlinInterface;
import library.JavaClassOverrideOnly;
import library.JavaInterfaceOverrideOnly;
+import library.JavaMembersOverrideOnly;
import library.KotlinClassOverrideOnly;
import library.KotlinInterfaceOverrideOnly;
@@ -20,6 +21,7 @@ class Invoker {
JavaClassOverrideOnly javaClassOverrideOnly,
JavaInterfaceOverrideOnly javaInterfaceOverrideOnly,
+ JavaMembersOverrideOnly javeMembersOverrideOnly,
KotlinClassOverrideOnly kotlinClassOverrideOnly,
KotlinInterfaceOverrideOnly kotlinInterfaceOverrideOnly
) {
@@ -29,13 +31,17 @@ class Invoker {
kotlinInterface.implementOnlyMethod();
javaClassOverrideOnly.overrideOnlyMethod();
+ javaClassOverrideOnly.finalMethod(); // no warning because it's a final method
javaInterfaceOverrideOnly.implementOnlyMethod();
+ javeMembersOverrideOnly.implementOnlyMethod();
+ javeMembersOverrideOnly.finalMethod(); // no warning because it's a final method
kotlinClassOverrideOnly.overrideOnlyMethod();
kotlinInterfaceOverrideOnly.implementOnlyMethod();
//No warning
JavaClassOverrideOnly.staticMethod();
JavaInterfaceOverrideOnly.staticMethod();
+ JavaMembersOverrideOnly.staticMethod();
KotlinClassOverrideOnly.staticMethod();
KotlinInterfaceOverrideOnly.staticMethod();
}
@@ -47,15 +53,19 @@ class Invoker {
Consumer d = KotlinInterface::implementOnlyMethod;
Consumer a1 = JavaClassOverrideOnly::overrideOnlyMethod;
+ Consumer a2 = JavaClassOverrideOnly::finalMethod; // no warning because it's a final method
Consumer b1 = JavaInterfaceOverrideOnly::implementOnlyMethod;
- Consumer c1 = KotlinClassOverrideOnly::overrideOnlyMethod;
- Consumer d1 = KotlinInterfaceOverrideOnly::implementOnlyMethod;
+ Consumer c1 = JavaMembersOverrideOnly::implementOnlyMethod;
+ Consumer c2 = JavaMembersOverrideOnly::finalMethod; // no warning because it's a final method
+ Consumer d1 = KotlinClassOverrideOnly::overrideOnlyMethod;
+ Consumer e1 = KotlinInterfaceOverrideOnly::implementOnlyMethod;
//No warning
- Runnable a2 = JavaClassOverrideOnly::staticMethod;
+ Runnable a3 = JavaClassOverrideOnly::staticMethod;
Runnable b2 = JavaInterfaceOverrideOnly::staticMethod;
- Runnable c2 = KotlinClassOverrideOnly::staticMethod;
- Runnable d2 = KotlinInterfaceOverrideOnly::staticMethod;
+ Runnable c3 = JavaMembersOverrideOnly::staticMethod;
+ Runnable d2 = KotlinClassOverrideOnly::staticMethod;
+ Runnable e2 = KotlinInterfaceOverrideOnly::staticMethod;
}
}
diff --git a/jvm/jvm-analysis-kotlin-tests-shared/testData/codeInspection/overrideOnly/src/library/JavaClassOverrideOnly.java b/jvm/jvm-analysis-kotlin-tests-shared/testData/codeInspection/overrideOnly/src/library/JavaClassOverrideOnly.java
index 29715f4bb0fc..112cc648ddde 100644
--- a/jvm/jvm-analysis-kotlin-tests-shared/testData/codeInspection/overrideOnly/src/library/JavaClassOverrideOnly.java
+++ b/jvm/jvm-analysis-kotlin-tests-shared/testData/codeInspection/overrideOnly/src/library/JavaClassOverrideOnly.java
@@ -7,5 +7,7 @@ public abstract class JavaClassOverrideOnly {
public abstract void overrideOnlyMethod();
- public static void staticMethod() {}
+ public static void staticMethod() { }
+
+ public final void finalMethod() { }
}
diff --git a/jvm/jvm-analysis-kotlin-tests-shared/testData/codeInspection/overrideOnly/src/library/JavaMembersOverrideOnly.java b/jvm/jvm-analysis-kotlin-tests-shared/testData/codeInspection/overrideOnly/src/library/JavaMembersOverrideOnly.java
new file mode 100644
index 000000000000..b7110993ccaf
--- /dev/null
+++ b/jvm/jvm-analysis-kotlin-tests-shared/testData/codeInspection/overrideOnly/src/library/JavaMembersOverrideOnly.java
@@ -0,0 +1,14 @@
+package library;
+
+import org.jetbrains.annotations.ApiStatus.OverrideOnly;
+
+public abstract class JavaMembersOverrideOnly {
+ @OverrideOnly
+ public abstract void implementOnlyMethod();
+
+ @OverrideOnly
+ public static void staticMethod() { }
+
+ @OverrideOnly
+ public final void finalMethod() { }
+}