diff --git a/java/java-analysis-impl/resources/messages/InspectionGadgetsBundle.properties b/java/java-analysis-impl/resources/messages/InspectionGadgetsBundle.properties index 2562b02cf423..adeea2043250 100644 --- a/java/java-analysis-impl/resources/messages/InspectionGadgetsBundle.properties +++ b/java/java-analysis-impl/resources/messages/InspectionGadgetsBundle.properties @@ -1271,6 +1271,7 @@ unnecessary.interface.inner.class.modifier.problem.descriptor=Modifier #re unnecessary.interface.field.modifier.problem.descriptor=Modifier #ref is redundant for interface fields #loc unnecessary.transient.modifier.problem.descriptor=Modifier #ref is redundant for a 'static' field unnecessary.strictfp.modifier.problem.descriptor=Modifier #ref is redundant on Java 17 and later +unnecessary.main.modifier.problem.descriptor=Modifier #ref is redundant for ''main'' method on Java {0} #loc smth.unnecessary.remove.quickfix=Remove unnecessary ''{0}'' unqualified.static.usage.problem.descriptor=Unqualified static method call #ref() #loc unqualified.static.usage.problem.descriptor1=Unqualified static field access #ref #loc diff --git a/java/java-analysis-impl/src/com/siyeh/ig/style/UnnecessaryModifierInspection.java b/java/java-analysis-impl/src/com/siyeh/ig/style/UnnecessaryModifierInspection.java index 28352bac4f28..0e47d0da29fa 100644 --- a/java/java-analysis-impl/src/com/siyeh/ig/style/UnnecessaryModifierInspection.java +++ b/java/java-analysis-impl/src/com/siyeh/ig/style/UnnecessaryModifierInspection.java @@ -4,10 +4,15 @@ package com.siyeh.ig.style; import com.intellij.codeInspection.CleanupLocalInspectionTool; import com.intellij.codeInspection.LocalQuickFix; import com.intellij.pom.java.JavaFeature; +import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; +import com.intellij.psi.search.PsiSearchHelper; +import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.PsiMethodUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; +import com.siyeh.HardcodedMethodConstants; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; @@ -146,6 +151,54 @@ public final class UnnecessaryModifierInspection extends BaseInspection implemen } } } + + processMainMethod(method); + } + + private void processMainMethod(@NotNull PsiMethod method) { + if (PsiUtil.isAvailable(JavaFeature.IMPLICIT_CLASSES, method) && + HardcodedMethodConstants.MAIN.equals(method.getName()) && + PsiMethodUtil.isMainMethod(method)) { + boolean isImplicitClass = method.getParent() instanceof PsiImplicitClass; + final PsiModifierList modifierList = method.getModifierList(); + final List modifiers = PsiTreeUtil.getChildrenOfTypeAsList(modifierList, PsiKeyword.class); + LanguageLevel level = PsiUtil.getLanguageLevel(method); + for (PsiKeyword modifier : modifiers) { + if (modifier.getTokenType() == JavaTokenType.STATIC_KEYWORD && isImplicitClass) { + //static for implicit class + registerError(modifier, InspectionGadgetsBundle.message("unnecessary.main.modifier.problem.descriptor", level.getShortText()), + modifier.getText()); + continue; + } + if (modifier.getTokenType() == JavaTokenType.PUBLIC_KEYWORD || + modifier.getTokenType() == JavaTokenType.PROTECTED_KEYWORD) { + if (isImplicitClass) { + //public and protected for implicit class + registerError(modifier, InspectionGadgetsBundle.message("unnecessary.main.modifier.problem.descriptor", level.getShortText()), + modifier.getText()); + continue; + } + if (isOnTheFly()) { + final PsiSearchHelper searchHelper = PsiSearchHelper.getInstance(method.getProject()); + PsiClass containingClass = method.getContainingClass(); + if (containingClass == null || containingClass.getName() == null) return; + final PsiSearchHelper.SearchCostResult cost = + searchHelper.isCheapEnoughToSearch(containingClass.getName(), containingClass.getResolveScope(), null); + if (cost == PsiSearchHelper.SearchCostResult.TOO_MANY_OCCURRENCES) { + continue; + } + } + + PsiReference first = ReferencesSearch.search(method, method.getResolveScope()).findFirst(); + if (first != null) { + continue; + } + //public and protected for normal class + registerError(modifier, InspectionGadgetsBundle.message("unnecessary.main.modifier.problem.descriptor", level.getShortText()), + modifier.getText()); + } + } + } } @Override diff --git a/java/java-tests/testData/ig/com/siyeh/igtest/style/unnecessary_psvm_modifier/ImplicitClassWithModifiers.java b/java/java-tests/testData/ig/com/siyeh/igtest/style/unnecessary_psvm_modifier/ImplicitClassWithModifiers.java new file mode 100644 index 000000000000..a25dee2e3cc9 --- /dev/null +++ b/java/java-tests/testData/ig/com/siyeh/igtest/style/unnecessary_psvm_modifier/ImplicitClassWithModifiers.java @@ -0,0 +1,3 @@ +public static void main(String[] args) { + System.out.println("Hello World!"); +} diff --git a/java/java-tests/testData/ig/com/siyeh/igtest/style/unnecessary_psvm_modifier/NormalClassWithModifiers.java b/java/java-tests/testData/ig/com/siyeh/igtest/style/unnecessary_psvm_modifier/NormalClassWithModifiers.java new file mode 100644 index 000000000000..58a6e9b3c207 --- /dev/null +++ b/java/java-tests/testData/ig/com/siyeh/igtest/style/unnecessary_psvm_modifier/NormalClassWithModifiers.java @@ -0,0 +1,13 @@ +package test; + +public class NormalClassWithModifiers { + public static void main(String[] args) { + System.out.println("Hello World!"); + } + + + public static int main() { + System.out.println("Hello World!"); + return 1; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/ig/com/siyeh/igtest/style/unnecessary_psvm_modifier/NormalClassWithModifiersClassIsUsed.java b/java/java-tests/testData/ig/com/siyeh/igtest/style/unnecessary_psvm_modifier/NormalClassWithModifiersClassIsUsed.java new file mode 100644 index 000000000000..0749f4bd3605 --- /dev/null +++ b/java/java-tests/testData/ig/com/siyeh/igtest/style/unnecessary_psvm_modifier/NormalClassWithModifiersClassIsUsed.java @@ -0,0 +1,7 @@ +package test; + +class NormalClassWithModifiersClassIsUsed { + public static void main(String[] args) { + System.out.println("Hello World!"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/ig/com/siyeh/igtest/style/unnecessary_psvm_modifier/OldJavaWithModifiers.java b/java/java-tests/testData/ig/com/siyeh/igtest/style/unnecessary_psvm_modifier/OldJavaWithModifiers.java new file mode 100644 index 000000000000..4a6d563e9e62 --- /dev/null +++ b/java/java-tests/testData/ig/com/siyeh/igtest/style/unnecessary_psvm_modifier/OldJavaWithModifiers.java @@ -0,0 +1,7 @@ +package test; + +public class OldJavaWithModifiers{ + public static void main(String[] args) { + System.out.println("Hello World!"); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/siyeh/ig/style/UnnecessaryPsvmModifierInspectionTest.java b/java/java-tests/testSrc/com/siyeh/ig/style/UnnecessaryPsvmModifierInspectionTest.java new file mode 100644 index 000000000000..6942035788d0 --- /dev/null +++ b/java/java-tests/testSrc/com/siyeh/ig/style/UnnecessaryPsvmModifierInspectionTest.java @@ -0,0 +1,48 @@ +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.siyeh.ig.style; + +import com.intellij.pom.java.JavaFeature; +import com.intellij.pom.java.LanguageLevel; +import com.intellij.testFramework.IdeaTestUtil; +import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase; +import com.siyeh.ig.LightJavaInspectionTestCase; + +public class UnnecessaryPsvmModifierInspectionTest extends LightJavaCodeInsightFixtureTestCase { + @Override + protected String getBasePath() { + return LightJavaInspectionTestCase.INSPECTION_GADGETS_TEST_DATA_PATH + "com/siyeh/igtest/style/unnecessary_psvm_modifier"; + } + + + + private void doTest() { + myFixture.enableInspections(new UnnecessaryModifierInspection()); + myFixture.testHighlighting(getTestName(false) + ".java"); + } + + public void testOldJavaWithModifiers() { + IdeaTestUtil.withLevel(getModule(), LanguageLevel.JDK_1_8, () -> doTest()); + } + + public void testImplicitClassWithModifiers() { + IdeaTestUtil.withLevel(getModule(), JavaFeature.IMPLICIT_IMPORT_IN_IMPLICIT_CLASSES.getMinimumLevel(), () -> doTest()); + } + + public void testNormalClassWithModifiers() { + IdeaTestUtil.withLevel(getModule(), JavaFeature.IMPLICIT_IMPORT_IN_IMPLICIT_CLASSES.getMinimumLevel(), () -> doTest()); + } + + public void testNormalClassWithModifiersClassIsUsed() { + IdeaTestUtil.withLevel(getModule(), JavaFeature.IMPLICIT_IMPORT_IN_IMPLICIT_CLASSES.getMinimumLevel(), () -> { + myFixture.addClass(""" + import test.NormalClassWithModifiersClassIsUsed; + class Foo { + public static void main(String[] args){ + NormalClassWithModifiersClassIsUsed.main(args); + } + } + """); + doTest(); + }); + } +}