diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefClassImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefClassImpl.java index 2d67e8459a50..fd5b751fb7b0 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefClassImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefClassImpl.java @@ -635,4 +635,13 @@ public final class RefClassImpl extends RefJavaElementImpl implements RefClass { } return false; } + + @Override + public boolean isSuppressed(String @NotNull ... toolIds) { + if (super.isSuppressed(toolIds)) { + return true; + } + RefElement fileRef = getRefManager().getReference(getContainingFile()); + return fileRef instanceof RefJavaFileImpl file && file.isSuppressed(toolIds); + } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaManagerImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaManagerImpl.java index adcb011c7819..dbfbee7ff9c5 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaManagerImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaManagerImpl.java @@ -29,6 +29,7 @@ import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.uast.*; +import org.jetbrains.uast.expressions.UInjectionHost; import org.jetbrains.uast.visitor.AbstractUastNonRecursiveVisitor; import java.util.*; @@ -504,7 +505,6 @@ public final class RefJavaManagerImpl extends RefJavaManager { if (Comparing.strEqual(qualifiedName, BatchSuppressManager.SUPPRESS_INSPECTIONS_ANNOTATION_NAME) || Comparing.strEqual(qualifiedName, "kotlin.Suppress")) { UAnnotated annotated = UastUtils.getParentOfType(annotation, UAnnotated.class); - // TODO support kotlin suppressions if (annotated == null) { UElement parent = annotation.getUastParent(); if (parent == null) { @@ -528,17 +528,17 @@ public final class RefJavaManagerImpl extends RefJavaManager { final List nameValuePairs = annotation.getAttributeValues(); for (UNamedExpression nameValuePair : nameValuePairs) { UExpression value = nameValuePair.getExpression(); - if (value instanceof ULiteralExpression) { - Object val = ((ULiteralExpression)value).getValue(); - if (val instanceof String) { - buf.append(",").append(String.valueOf(val).replaceAll("[{}\"]", "")); + if (value instanceof UInjectionHost host) { + String val = host.evaluateToString(); + if (val != null) { + buf.append(",").append(val.replaceAll("[{}\"]", "")); } } - else if (value instanceof UCallExpression && ((UCallExpression)value).getKind() == UastCallKind.NESTED_ARRAY_INITIALIZER) { - for (UExpression argument : ((UCallExpression)value).getValueArguments()) { - if (argument instanceof ULiteralExpression) { - Object val = ((ULiteralExpression)argument).getValue(); - if (val instanceof String) { + else if (value instanceof UCallExpression call && call.getKind() == UastCallKind.NESTED_ARRAY_INITIALIZER) { + for (UExpression argument : call.getValueArguments()) { + if (argument instanceof UInjectionHost host) { + String val = host.evaluateToString(); + if (val != null) { buf.append(",").append(val); } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefMethodImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefMethodImpl.java index eefd261a1354..85dc8898ee9b 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefMethodImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefMethodImpl.java @@ -219,7 +219,7 @@ public class RefMethodImpl extends RefJavaElementImpl implements RefMethod { public boolean hasBody() { if (!isAbstract()) { RefClass ownerClass = getOwnerClass(); - if (ownerClass != null && !ownerClass.isInterface()) { + if (ownerClass == null || !ownerClass.isInterface()) { return true; } } diff --git a/java/java-impl/src/META-INF/JavaPlugin.xml b/java/java-impl/src/META-INF/JavaPlugin.xml index 9747bf2287cc..e46f7ee37017 100644 --- a/java/java-impl/src/META-INF/JavaPlugin.xml +++ b/java/java-impl/src/META-INF/JavaPlugin.xml @@ -1996,9 +1996,6 @@ groupKey="group.names.performance.issues" groupBundle="messages.InspectionsBundle" enabledByDefault="false" level="WARNING" implementationClass="com.intellij.codeInspection.BulkFileAttributesReadInspection"/> - diff --git a/java/openapi/resources/messages/JavaBundle.properties b/java/openapi/resources/messages/JavaBundle.properties index bfe26ca7d913..a936247bc17e 100644 --- a/java/openapi/resources/messages/JavaBundle.properties +++ b/java/openapi/resources/messages/JavaBundle.properties @@ -76,7 +76,6 @@ checkbox.collapse.multiline.comments=Multiline comments checkbox.collapse.one.line.methods=One-line methods checkbox.collapse.simple.property.accessors=Simple property accessors checkbox.collapse.suppress.warnings=@SuppressWarnings -checkbox.comments.and.javadoc.count.as.content=Comments and javadoc count as content checkbox.deprecated.members=Deprecated members checkbox.do.not.indent.top.level.class.members=Do not indent top level class members checkbox.do.not.wrap.after.single.annotation=Do not wrap after single annotation @@ -426,12 +425,6 @@ inspection.duplicate.expressions.replace.other.occurrences.fix.family.name=Repla inspection.duplicate.expressions.replace.other.occurrences.fix.name=Replace with ''{0}'' other occurrences of ''{1}'' inspection.duplicate.expressions.reuse.variable.fix.family.name=Reuse variable inspection.duplicate.expressions.reuse.variable.fix.name=Reuse variable ''{0}'' for ''{1}'' -inspection.empty.method.delete.quickfix=Delete unnecessary method(s) -inspection.empty.method.problem.descriptor=Method only calls its super -inspection.empty.method.problem.descriptor1=Empty method overrides empty method -inspection.empty.method.problem.descriptor2=The method is empty -inspection.empty.method.problem.descriptor3=The method and all its derivables are empty -inspection.empty.method.problem.descriptor4=All implementations of this method are empty inspection.endless.stream.description=Non-short-circuit operation consumes infinite stream inspection.equals.hashcode.display.name='equals()' and 'hashCode()' not paired inspection.equals.hashcode.generate.equals.quickfix=Generate 'equals()' @@ -1367,7 +1360,6 @@ live.template.context.else='else' position live.template.context.expression=Expression live.template.context.declaration=Declaration inspection.unused.display.name=Unused declaration -inspection.empty.method.display.name=Empty method inspection.unused.assignment.display.name=Unused assignment inspection.unchecked.warning.display.name=Unchecked warning inspection.convert.2.streamapi.display.name=Loop can be collapsed with Stream API diff --git a/jvm/jvm-analysis-impl/resources/META-INF/JvmAnalysisPlugin.xml b/jvm/jvm-analysis-impl/resources/META-INF/JvmAnalysisPlugin.xml index 62b9d9e1929a..01ea2e2d6bb9 100644 --- a/jvm/jvm-analysis-impl/resources/META-INF/JvmAnalysisPlugin.xml +++ b/jvm/jvm-analysis-impl/resources/META-INF/JvmAnalysisPlugin.xml @@ -154,6 +154,10 @@ groupBundle="messages.JvmAnalysisBundle" bundle="messages.JvmAnalysisBundle" groupKey="jvm.inspections.group.name" key="inspection.suppression.annotation.display.name" implementationClass="com.intellij.codeInspection.SuppressionAnnotationInspection"/> + diff --git a/jvm/jvm-analysis-impl/resources/messages/JvmAnalysisBundle.properties b/jvm/jvm-analysis-impl/resources/messages/JvmAnalysisBundle.properties index e760a66bc8dc..2f46a1071481 100644 --- a/jvm/jvm-analysis-impl/resources/messages/JvmAnalysisBundle.properties +++ b/jvm/jvm-analysis-impl/resources/messages/JvmAnalysisBundle.properties @@ -226,3 +226,12 @@ allow.suppressions.fix.text=Allow these suppressions group.advanced.settings.jvm=JVM languages advanced.setting.process.console.output.to.find.class.names=Process terminal output to find class names and highlight them + +inspection.empty.method.display.name=Empty method +inspection.empty.method.delete.quickfix=Delete unnecessary method(s) +inspection.empty.method.problem.descriptor=Method only calls its super +inspection.empty.method.problem.descriptor1=Empty method overrides empty method +inspection.empty.method.problem.descriptor2=The method is empty +inspection.empty.method.problem.descriptor3=The method and all its derivables are empty +inspection.empty.method.problem.descriptor4=All implementations of this method are empty +checkbox.comments.and.javadoc.count.as.content=Comments and javadoc count as content diff --git a/java/java-impl-inspections/src/com/intellij/codeInspection/emptyMethod/EmptyMethodInspection.java b/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/emptyMethod/EmptyMethodInspection.java similarity index 91% rename from java/java-impl-inspections/src/com/intellij/codeInspection/emptyMethod/EmptyMethodInspection.java rename to jvm/jvm-analysis-impl/src/com/intellij/codeInspection/emptyMethod/EmptyMethodInspection.java index 1bc60044151b..05559d5fe661 100644 --- a/java/java-impl-inspections/src/com/intellij/codeInspection/emptyMethod/EmptyMethodInspection.java +++ b/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/emptyMethod/EmptyMethodInspection.java @@ -1,7 +1,8 @@ -// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.codeInspection.emptyMethod; import com.intellij.analysis.AnalysisScope; +import com.intellij.analysis.JvmAnalysisBundle; import com.intellij.codeInsight.AnnotationUtil; import com.intellij.codeInsight.daemon.QuickFixBundle; import com.intellij.codeInsight.options.JavaClassValidator; @@ -90,28 +91,28 @@ public final class EmptyMethodInspection extends GlobalJavaBatchInspectionTool { } } if (refSuper == null || refUtil.compareAccess(refMethod.getAccessModifier(), refSuper.getAccessModifier()) <= 0) { - message = JavaBundle.message("inspection.empty.method.problem.descriptor"); + message = JvmAnalysisBundle.message("inspection.empty.method.problem.descriptor"); } } else if (refMethod.hasBody() && hasEmptySuperImplementation(refMethod)) { - message = JavaBundle.message("inspection.empty.method.problem.descriptor1"); + message = JvmAnalysisBundle.message("inspection.empty.method.problem.descriptor1"); } else if (refSuper == null && areAllImplementationsEmpty(refMethod)) { if (refMethod.hasBody()) { if (refMethod.getDerivedMethods().isEmpty()) { if (refMethod.getSuperMethods().isEmpty()) { - message = JavaBundle.message("inspection.empty.method.problem.descriptor2"); + message = JvmAnalysisBundle.message("inspection.empty.method.problem.descriptor2"); } } else { needToDeleteHierarchy = true; - message = JavaBundle.message("inspection.empty.method.problem.descriptor3"); + message = JvmAnalysisBundle.message("inspection.empty.method.problem.descriptor3"); } } else { if (!refMethod.getDerivedReferences().isEmpty()) { needToDeleteHierarchy = true; - message = JavaBundle.message("inspection.empty.method.problem.descriptor4"); + message = JvmAnalysisBundle.message("inspection.empty.method.problem.descriptor4"); } } } @@ -182,11 +183,11 @@ public final class EmptyMethodInspection extends GlobalJavaBatchInspectionTool { } private boolean areAllImplementationsEmpty(@NotNull RefOverridable reference) { - if (reference instanceof RefMethod) { - if (((RefMethod)reference).hasBody() && !isBodyEmpty((RefMethod)reference)) return false; + if (reference instanceof RefMethod refMethod) { + if (refMethod.hasBody() && !isBodyEmpty(refMethod)) return false; } - else if (reference instanceof RefFunctionalExpression) { - if (!((RefFunctionalExpression)reference).hasEmptyBody()) return false; + else if (reference instanceof RefFunctionalExpression refFunctionalExpression) { + if (!refFunctionalExpression.hasEmptyBody()) return false; } for (RefOverridable derivedReference : reference.getDerivedReferences()) { @@ -251,21 +252,21 @@ public final class EmptyMethodInspection extends GlobalJavaBatchInspectionTool { @Override public String getHint(final @NotNull QuickFix fix) { - if (fix instanceof DeleteMethodQuickFix) { - return String.valueOf(((DeleteMethodQuickFix)fix).myNeedToDeleteHierarchy); + if (fix instanceof DeleteMethodQuickFix deleteMethodQuickFix) { + return String.valueOf(deleteMethodQuickFix.myNeedToDeleteHierarchy); } return null; } @Override - public @Nullable LocalQuickFix getQuickFix(final String hint) { + public @NotNull LocalQuickFix getQuickFix(final String hint) { return new DeleteMethodIntention(hint); } @Override public @NotNull OptPane getOptionsPane() { return pane( - checkbox("commentsAreContent", JavaBundle.message("checkbox.comments.and.javadoc.count.as.content")), + checkbox("commentsAreContent", JvmAnalysisBundle.message("checkbox.comments.and.javadoc.count.as.content")), stringList("EXCLUDE_ANNOS", JavaBundle.message("special.annotations.annotations.list"), new JavaClassValidator().annotationsOnly())); } @@ -360,6 +361,6 @@ public final class EmptyMethodInspection extends GlobalJavaBatchInspectionTool { } private static @IntentionFamilyName String getQuickFixName() { - return JavaBundle.message("inspection.empty.method.delete.quickfix"); + return JvmAnalysisBundle.message("inspection.empty.method.delete.quickfix"); } } diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/EmptyMethodInspectionTest.java b/jvm/jvm-analysis-java-tests/testSrc/com/intellij/codeInspection/tests/java/EmptyMethodInspectionTest.java similarity index 96% rename from java/java-tests/testSrc/com/intellij/java/codeInspection/EmptyMethodInspectionTest.java rename to jvm/jvm-analysis-java-tests/testSrc/com/intellij/codeInspection/tests/java/EmptyMethodInspectionTest.java index 0886d7ef8980..3d145c460d4c 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/EmptyMethodInspectionTest.java +++ b/jvm/jvm-analysis-java-tests/testSrc/com/intellij/codeInspection/tests/java/EmptyMethodInspectionTest.java @@ -1,5 +1,5 @@ // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -package com.intellij.java.codeInspection; +package com.intellij.codeInspection.tests.java; import com.intellij.JavaTestUtil; import com.intellij.codeInspection.emptyMethod.EmptyMethodInspection; diff --git a/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/emptyMethod/basic/expected.xml b/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/emptyMethod/basic/expected.xml new file mode 100644 index 000000000000..d5e4a55af106 --- /dev/null +++ b/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/emptyMethod/basic/expected.xml @@ -0,0 +1,20 @@ + + + TestBasic.kt + 2 + Empty method + The method is empty + + + TestBasic.kt + 4 + Empty method + The method is empty + + + TestBasic.kt + 18 + Empty method + The method is empty + + \ No newline at end of file diff --git a/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/emptyMethod/basic/src/TestBasic.kt b/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/emptyMethod/basic/src/TestBasic.kt new file mode 100644 index 000000000000..1b2de9c7f0f1 --- /dev/null +++ b/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/emptyMethod/basic/src/TestBasic.kt @@ -0,0 +1,20 @@ +class TestBasic { + fun empty() {} + + fun comment() { + // comment + } + + fun nonEmpty() { + empty() + } + + @Suppress("EmptyMethod") + fun emptySuppressed() { + + } +} + +fun topLevel() { + +} \ No newline at end of file diff --git a/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/emptyMethod/basic/src/TestSuppressFile.kt b/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/emptyMethod/basic/src/TestSuppressFile.kt new file mode 100644 index 000000000000..5dfe90427545 --- /dev/null +++ b/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/emptyMethod/basic/src/TestSuppressFile.kt @@ -0,0 +1,7 @@ +@file:Suppress("EmptyMethod") + +class TestSuppressFile { + fun empty() { + + } +} \ No newline at end of file diff --git a/jvm/jvm-analysis-kotlin-tests/testSrc/com/intellij/codeInspection/tests/kotlin/KotlinEmptyMethodInspectionTest.kt b/jvm/jvm-analysis-kotlin-tests/testSrc/com/intellij/codeInspection/tests/kotlin/KotlinEmptyMethodInspectionTest.kt new file mode 100644 index 000000000000..b86f55b630f7 --- /dev/null +++ b/jvm/jvm-analysis-kotlin-tests/testSrc/com/intellij/codeInspection/tests/kotlin/KotlinEmptyMethodInspectionTest.kt @@ -0,0 +1,20 @@ +package com.intellij.codeInspection.tests.kotlin + +import com.intellij.codeInspection.emptyMethod.EmptyMethodInspection +import com.intellij.codeInspection.ex.GlobalInspectionToolWrapper +import com.intellij.jvm.analysis.KotlinJvmAnalysisTestUtil +import com.intellij.jvm.analysis.internal.testFramework.JvmSdkInspectionTestBase +import com.intellij.testFramework.TestDataPath + +private const val inspectionPath = "/codeInspection/emptyMethod" + +@TestDataPath("\$CONTENT_ROOT/testData$inspectionPath") +class KotlinEmptyMethodInspectionTest : JvmSdkInspectionTestBase() { + override var inspection = EmptyMethodInspection() + + override fun getBasePath() = KotlinJvmAnalysisTestUtil.TEST_DATA_PROJECT_RELATIVE_BASE_PATH + inspectionPath + + fun `test basic`() { + myFixture.testInspection("basic", GlobalInspectionToolWrapper(inspection)) + } +} \ No newline at end of file