From 69ddce69ae72059643da76e9615bd2c310fdc599 Mon Sep 17 00:00:00 2001 From: Bart van Helvert Date: Mon, 10 Feb 2025 23:14:05 +0100 Subject: [PATCH] [java] Ignore static and final methods in OverrideOnlyInspection #IDEA-367314 Fixed GitOrigin-RevId: 418750107648323fe36a75c492d05a9e499268b2 --- .../codeInspection/OverrideOnlyInspection.kt | 28 +++++++++--------- .../library/JavaClassOverrideOnly.class | Bin 623 -> 694 bytes .../library/JavaMembersOverrideOnly.class | Bin 0 -> 725 bytes .../overrideOnly/plugin/JavaCode.java | 20 +++++++++---- .../src/library/JavaClassOverrideOnly.java | 4 ++- .../src/library/JavaMembersOverrideOnly.java | 14 +++++++++ 6 files changed, 46 insertions(+), 20 deletions(-) create mode 100644 jvm/jvm-analysis-kotlin-tests-shared/testData/codeInspection/overrideOnly/library/JavaMembersOverrideOnly.class create mode 100644 jvm/jvm-analysis-kotlin-tests-shared/testData/codeInspection/overrideOnly/src/library/JavaMembersOverrideOnly.java 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 174e7095abaf756a63958673430b4f80ef3de5e5..6d1c3afc0272227fbd9b0af7e8a93c44449bce82 100644 GIT binary patch delta 353 zcmaFQvW->c)W2Q(7#J8#7^Jxvm>ERb8N}Eb#2FdbY%=pQOY9gKm^C!RCdSmVai?YG zCFV@L=%>KWAj!zU<6o9qRFs*L>YtZW3FLa_<)s!m=Oh*vr%sk*G-KsqU}0dLXrm-9 zkd;`LsGpOVm#*)hl$DxX!p8JZ8CZB2SQ*$D83eKt%M$f-67$ma{gbj%lS>#G z*laTMGE3|j8JIOR!`K-(CI>JoO;oF%xZaJ0k-^Ak@*YOz$+nDqqKphYo_TqxMb0^i z#l@+`>t{i42&Q*07*_D zPajA#0%=yQ?F@_?fea=FE+7e#29exAk_VyyNb@o<0Nug|6lVZx=7$?13FLzS*Z@Y5 m0eVmqc!1Uk0A-jM1Q~>YG{{_GAT0vKTtK!cgE)hv8Yci+-Y8%I 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 0000000000000000000000000000000000000000..12582f528ea10dc60fe4a1a8ff1644ea0e256e10 GIT binary patch literal 725 zcmb7BT}vB56g_v{WX)!6;zwIQ3Pmd72e?K0W)UPHDA9n5?~`Pz6E?FlGYR;!d=mQL z5Aa7x@5IEe4`MqH_v3KSIrlP;Prn}k{J=_#DSV9ZDZ*z$`KQv#?GvUpHZKWbBkxK= zt*Nv;9rWAMT!?nY$Xqk;i0o1r<@+%hx@%<#tIbTcjWD1nn#l*Xv8Z0YQF zk1((LH<|29?L2TI-F4n2teg$BQ+;`)hsr7zKF~UM!l_)_2up;dD8ucM*3vXGVXb82 z-DYmC(w=neuCz_XUv_$Mqt01rVAqNgyh&ocL1(f;^_S8jn{@GcJ}@15sQk@e-Z*Ec z$7d!8Q6hYO=X65dPp6sCS81!=lN}dfnehF8Hz$1i2THh#fWOzj5d57eaV&F}awg8D zt$WZN7Xc!UWG|-r|O(5m}M*&2`+K(-$;xKVlfLn3wz2e=3Xe< ietbeH`p*>niCx(Ll}LbjMglCL#yMt@87$(%dgTw|VWzimplementOnlyMethod(); 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() { } +}