[java] Ignore static and final methods in OverrideOnlyInspection

#IDEA-367314 Fixed

GitOrigin-RevId: 418750107648323fe36a75c492d05a9e499268b2
This commit is contained in:
Bart van Helvert
2025-02-10 23:14:05 +01:00
committed by intellij-monorepo-bot
parent 6c7da7b25a
commit 69ddce69ae
6 changed files with 46 additions and 20 deletions

View File

@@ -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

View File

@@ -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.<warning descr="Method 'implementOnlyMethod()' can only be overridden">implementOnlyMethod</warning>();
javaClassOverrideOnly.<warning descr="Method 'overrideOnlyMethod()' can only be overridden">overrideOnlyMethod</warning>();
javaClassOverrideOnly.finalMethod(); // no warning because it's a final method
javaInterfaceOverrideOnly.<warning descr="Method 'implementOnlyMethod()' can only be overridden">implementOnlyMethod</warning>();
javeMembersOverrideOnly.<warning descr="Method 'implementOnlyMethod()' can only be overridden">implementOnlyMethod</warning>();
javeMembersOverrideOnly.finalMethod(); // no warning because it's a final method
kotlinClassOverrideOnly.<warning descr="Method 'overrideOnlyMethod()' can only be overridden">overrideOnlyMethod</warning>();
kotlinInterfaceOverrideOnly.<warning descr="Method 'implementOnlyMethod()' can only be overridden">implementOnlyMethod</warning>();
//No warning
JavaClassOverrideOnly.staticMethod();
JavaInterfaceOverrideOnly.staticMethod();
JavaMembersOverrideOnly.staticMethod();
KotlinClassOverrideOnly.staticMethod();
KotlinInterfaceOverrideOnly.staticMethod();
}
@@ -47,15 +53,19 @@ class Invoker {
Consumer<KotlinInterface> d = KotlinInterface::<warning descr="Method 'implementOnlyMethod()' can only be overridden">implementOnlyMethod</warning>;
Consumer<JavaClassOverrideOnly> a1 = JavaClassOverrideOnly::<warning descr="Method 'overrideOnlyMethod()' can only be overridden">overrideOnlyMethod</warning>;
Consumer<JavaClassOverrideOnly> a2 = JavaClassOverrideOnly::finalMethod; // no warning because it's a final method
Consumer<JavaInterfaceOverrideOnly> b1 = JavaInterfaceOverrideOnly::<warning descr="Method 'implementOnlyMethod()' can only be overridden">implementOnlyMethod</warning>;
Consumer<KotlinClassOverrideOnly> c1 = KotlinClassOverrideOnly::<warning descr="Method 'overrideOnlyMethod()' can only be overridden">overrideOnlyMethod</warning>;
Consumer<KotlinInterfaceOverrideOnly> d1 = KotlinInterfaceOverrideOnly::<warning descr="Method 'implementOnlyMethod()' can only be overridden">implementOnlyMethod</warning>;
Consumer<JavaMembersOverrideOnly> c1 = JavaMembersOverrideOnly::<warning descr="Method 'implementOnlyMethod()' can only be overridden">implementOnlyMethod</warning>;
Consumer<JavaMembersOverrideOnly> c2 = JavaMembersOverrideOnly::finalMethod; // no warning because it's a final method
Consumer<KotlinClassOverrideOnly> d1 = KotlinClassOverrideOnly::<warning descr="Method 'overrideOnlyMethod()' can only be overridden">overrideOnlyMethod</warning>;
Consumer<KotlinInterfaceOverrideOnly> e1 = KotlinInterfaceOverrideOnly::<warning descr="Method 'implementOnlyMethod()' can only be overridden">implementOnlyMethod</warning>;
//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;
}
}

View File

@@ -8,4 +8,6 @@ public abstract class JavaClassOverrideOnly {
public abstract void overrideOnlyMethod();
public static void staticMethod() { }
public final void finalMethod() { }
}

View File

@@ -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() { }
}