[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 package com.intellij.codeInspection
import com.intellij.analysis.JvmAnalysisBundle import com.intellij.analysis.JvmAnalysisBundle
import com.intellij.codeInsight.daemon.impl.analysis.HighlightMessageUtil import com.intellij.codeInsight.daemon.impl.analysis.HighlightMessageUtil
import com.intellij.codeInspection.apiUsage.ApiUsageProcessor import com.intellij.codeInspection.apiUsage.ApiUsageProcessor
import com.intellij.codeInspection.apiUsage.ApiUsageUastVisitor import com.intellij.codeInspection.apiUsage.ApiUsageUastVisitor
import com.intellij.lang.jvm.JvmModifier
import com.intellij.openapi.roots.ProjectFileIndex import com.intellij.openapi.roots.ProjectFileIndex
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementVisitor import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.PsiMethod import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiModifier
import com.intellij.psi.PsiModifierListOwner import com.intellij.psi.PsiModifierListOwner
import com.intellij.psi.util.MethodSignatureUtil import com.intellij.psi.util.MethodSignatureUtil
import com.intellij.psi.util.PsiUtilCore import com.intellij.psi.util.PsiUtilCore
@@ -28,7 +27,6 @@ private inline val ANNOTATION_NAME get() = ApiStatus.OverrideOnly::class.java.ca
*/ */
@VisibleForTesting @VisibleForTesting
class OverrideOnlyInspection : LocalInspectionTool() { class OverrideOnlyInspection : LocalInspectionTool() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor = override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor =
if (AnnotatedApiUsageUtil.canAnnotationBeUsedInFile(ANNOTATION_NAME, holder.file)) { if (AnnotatedApiUsageUtil.canAnnotationBeUsedInFile(ANNOTATION_NAME, holder.file)) {
ApiUsageUastVisitor.createPsiElementVisitor(OverrideOnlyProcessor(holder)) ApiUsageUastVisitor.createPsiElementVisitor(OverrideOnlyProcessor(holder))
@@ -38,19 +36,21 @@ class OverrideOnlyInspection : LocalInspectionTool() {
} }
private class OverrideOnlyProcessor(private val problemsHolder: ProblemsHolder) : ApiUsageProcessor { private class OverrideOnlyProcessor(private val problemsHolder: ProblemsHolder) : ApiUsageProcessor {
private fun PsiMethod.isOverridable(): Boolean {
private fun isLibraryElement(element: PsiElement): Boolean { return !hasModifier(JvmModifier.PRIVATE) && !hasModifier(JvmModifier.FINAL) && !hasModifier(JvmModifier.STATIC)
val containingVirtualFile = PsiUtilCore.getVirtualFile(element)
return containingVirtualFile != null && ProjectFileIndex.getInstance(element.project).isInLibraryClasses(containingVirtualFile)
} }
private fun isOverrideOnlyMethod(method: PsiMethod) = private fun isLibraryElement(method: PsiMethod): Boolean {
method.hasAnnotation(ANNOTATION_NAME) || val containingVirtualFile = PsiUtilCore.getVirtualFile(method)
!method.hasModifierProperty(PsiModifier.STATIC) && method.containingClass?.hasAnnotation(ANNOTATION_NAME) == true 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 { private fun isInsideOverridenOnlyMethod(sourceNode: UElement, target: PsiMethod): Boolean = sourceNode.getContainingUMethod()?.let {
val psiMethod = it.javaPsi as? PsiMethod ?: return false MethodSignatureUtil.areSignaturesEqual(it.javaPsi, target)
MethodSignatureUtil.areSignaturesEqual(psiMethod, target)
} ?: false } ?: false
private fun isSuperCall(qualifier: UExpression?) = qualifier is USuperExpression private fun isSuperCall(qualifier: UExpression?) = qualifier is USuperExpression
@@ -67,7 +67,7 @@ class OverrideOnlyInspection : LocalInspectionTool() {
} }
override fun processReference(sourceNode: UElement, target: PsiModifierListOwner, qualifier: UExpression?) { 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)) { && !isSuperOrDelegateCall(sourceNode, target, qualifier)) {
val elementToHighlight = sourceNode.sourcePsi ?: return val elementToHighlight = sourceNode.sourcePsi ?: return
val methodName = HighlightMessageUtil.getSymbolName(target) ?: return val methodName = HighlightMessageUtil.getSymbolName(target) ?: return

View File

@@ -8,6 +8,7 @@ import library.KotlinInterface;
import library.JavaClassOverrideOnly; import library.JavaClassOverrideOnly;
import library.JavaInterfaceOverrideOnly; import library.JavaInterfaceOverrideOnly;
import library.JavaMembersOverrideOnly;
import library.KotlinClassOverrideOnly; import library.KotlinClassOverrideOnly;
import library.KotlinInterfaceOverrideOnly; import library.KotlinInterfaceOverrideOnly;
@@ -20,6 +21,7 @@ class Invoker {
JavaClassOverrideOnly javaClassOverrideOnly, JavaClassOverrideOnly javaClassOverrideOnly,
JavaInterfaceOverrideOnly javaInterfaceOverrideOnly, JavaInterfaceOverrideOnly javaInterfaceOverrideOnly,
JavaMembersOverrideOnly javeMembersOverrideOnly,
KotlinClassOverrideOnly kotlinClassOverrideOnly, KotlinClassOverrideOnly kotlinClassOverrideOnly,
KotlinInterfaceOverrideOnly kotlinInterfaceOverrideOnly KotlinInterfaceOverrideOnly kotlinInterfaceOverrideOnly
) { ) {
@@ -29,13 +31,17 @@ class Invoker {
kotlinInterface.<warning descr="Method 'implementOnlyMethod()' can only be overridden">implementOnlyMethod</warning>(); kotlinInterface.<warning descr="Method 'implementOnlyMethod()' can only be overridden">implementOnlyMethod</warning>();
javaClassOverrideOnly.<warning descr="Method 'overrideOnlyMethod()' can only be overridden">overrideOnlyMethod</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>(); 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>(); kotlinClassOverrideOnly.<warning descr="Method 'overrideOnlyMethod()' can only be overridden">overrideOnlyMethod</warning>();
kotlinInterfaceOverrideOnly.<warning descr="Method 'implementOnlyMethod()' can only be overridden">implementOnlyMethod</warning>(); kotlinInterfaceOverrideOnly.<warning descr="Method 'implementOnlyMethod()' can only be overridden">implementOnlyMethod</warning>();
//No warning //No warning
JavaClassOverrideOnly.staticMethod(); JavaClassOverrideOnly.staticMethod();
JavaInterfaceOverrideOnly.staticMethod(); JavaInterfaceOverrideOnly.staticMethod();
JavaMembersOverrideOnly.staticMethod();
KotlinClassOverrideOnly.staticMethod(); KotlinClassOverrideOnly.staticMethod();
KotlinInterfaceOverrideOnly.staticMethod(); KotlinInterfaceOverrideOnly.staticMethod();
} }
@@ -47,15 +53,19 @@ class Invoker {
Consumer<KotlinInterface> d = KotlinInterface::<warning descr="Method 'implementOnlyMethod()' can only be overridden">implementOnlyMethod</warning>; 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> 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<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<JavaMembersOverrideOnly> c1 = JavaMembersOverrideOnly::<warning descr="Method 'implementOnlyMethod()' can only be overridden">implementOnlyMethod</warning>;
Consumer<KotlinInterfaceOverrideOnly> d1 = KotlinInterfaceOverrideOnly::<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 //No warning
Runnable a2 = JavaClassOverrideOnly::staticMethod; Runnable a3 = JavaClassOverrideOnly::staticMethod;
Runnable b2 = JavaInterfaceOverrideOnly::staticMethod; Runnable b2 = JavaInterfaceOverrideOnly::staticMethod;
Runnable c2 = KotlinClassOverrideOnly::staticMethod; Runnable c3 = JavaMembersOverrideOnly::staticMethod;
Runnable d2 = KotlinInterfaceOverrideOnly::staticMethod; Runnable d2 = KotlinClassOverrideOnly::staticMethod;
Runnable e2 = KotlinInterfaceOverrideOnly::staticMethod;
} }
} }

View File

@@ -7,5 +7,7 @@ public abstract class JavaClassOverrideOnly {
public abstract void overrideOnlyMethod(); public abstract void overrideOnlyMethod();
public static void staticMethod() {} 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() { }
}