Java: anonymous and local classes are not runnable, don't show run line marker (IDEA-310635)

GitOrigin-RevId: f1ff2c762a9c2936004aa9f56e4721ccf60e1591
This commit is contained in:
Bas Leijdekkers
2023-01-20 13:23:07 +01:00
committed by intellij-monorepo-bot
parent c5e1e1bff9
commit 638d02690b
4 changed files with 61 additions and 24 deletions

View File

@@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.execution.application;
import com.intellij.codeInsight.daemon.impl.analysis.JavaHighlightUtil;
@@ -7,8 +7,12 @@ import com.intellij.execution.lineMarker.RunLineMarkerContributor;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.psi.*;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiIdentifier;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.util.PsiMethodUtil;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -18,28 +22,33 @@ import java.util.stream.Collectors;
public class ApplicationRunLineMarkerProvider extends RunLineMarkerContributor {
@Override
public final @Nullable Info getInfo(@NotNull final PsiElement e) {
if (Registry.is("ide.jvm.run.marker") || !isIdentifier(e)) {
public final @Nullable Info getInfo(@NotNull final PsiElement element) {
if (Registry.is("ide.jvm.run.marker") || !isIdentifier(element)) {
return null;
}
PsiElement element = e.getParent();
PsiFile containingFile = element.getContainingFile();
if (element instanceof PsiClass && PsiMethodUtil.findMainInClass((PsiClass)element) != null ||
element instanceof PsiMethod && "main".equals(((PsiMethod)element).getName()) && PsiMethodUtil.isMainMethod((PsiMethod)element)) {
if (JavaHighlightUtil.isJavaHashBangScript(containingFile)) {
return null;
}
AnAction[] actions = ExecutorAction.getActions(Integer.MAX_VALUE);
return new Info(AllIcons.RunConfigurations.TestState.Run, actions, element1 -> {
return Arrays.stream(actions)
.map(action -> getText(action, element1))
.filter(Objects::nonNull)
.collect(Collectors.joining("\n"));
});
PsiElement parent = element.getParent();
if (parent instanceof PsiClass aClass) {
if (PsiMethodUtil.findMainInClass(aClass) == null) return null;
}
return null;
else if (parent instanceof PsiMethod method) {
if (!"main".equals(method.getName()) || !PsiMethodUtil.isMainMethod(method)) return null;
PsiClass containingClass = method.getContainingClass();
if (containingClass == null || PsiUtil.isLocalOrAnonymousClass(containingClass)) return null;
}
else {
return null;
}
if (JavaHighlightUtil.isJavaHashBangScript(element.getContainingFile())) {
return null;
}
AnAction[] actions = ExecutorAction.getActions(Integer.MAX_VALUE);
return new Info(AllIcons.RunConfigurations.TestState.Run, actions,
e -> Arrays.stream(actions)
.map(action -> getText(action, e))
.filter(Objects::nonNull)
.collect(Collectors.joining("\n")));
}
protected boolean isIdentifier(@NotNull PsiElement e) {

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.lang.jvm.util;
import com.intellij.lang.jvm.*;
@@ -30,7 +30,7 @@ public final class JvmMainMethodUtil {
}
private static boolean canBeMainClass(@NotNull JvmClass clazz) {
if (clazz.getName() == null) return false; // anonymous classes
if (clazz.getQualifiedName() == null) return false; // anonymous and local classes
final JvmClassKind kind = clazz.getClassKind();
if (kind == JvmClassKind.ANNOTATION) return false;
return clazz.getContainingClass() == null || clazz.hasModifier(JvmModifier.STATIC);

View File

@@ -9,7 +9,7 @@ import org.jetbrains.annotations.Nullable;
public final class PsiMethodUtil {
public static final Condition<PsiClass> MAIN_CLASS = psiClass -> {
if (psiClass instanceof PsiAnonymousClass) return false;
if (PsiUtil.isLocalOrAnonymousClass(psiClass)) return false;
if (psiClass.isAnnotationType()) return false;
if (psiClass.isInterface() && !PsiUtil.isLanguageLevel8OrHigher(psiClass)) return false;
return psiClass.getContainingClass() == null || psiClass.hasModifierProperty(PsiModifier.STATIC);

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.java.codeInsight.navigation;
import com.intellij.application.options.editor.GutterIconsConfigurable;
@@ -92,8 +92,36 @@ public class RunLineMarkerTest extends LightJavaCodeInsightFixtureTestCase {
assertEquals(ThreeState.YES, RunLineMarkerProvider.hadAnythingRunnable(myFixture.getFile().getVirtualFile()));
}
public void testNoRunLineMarkerAnonymous() {
myFixture.configureByText("X.java", """
public class X {
void foo() {
new Object() {
public static void <caret>main(String[] args) {}
};
}
}""");
doTestNoRunLineMarkers();
}
public void testNoRunLineMarkerLocal() {
myFixture.configureByText("X.java", """
public class X {
void foo() {
class Local {
public static void <caret>main(String[] args) {}
};
}
}""");
doTestNoRunLineMarkers();
}
public void testNoRunLineMarker() {
myFixture.configureByText("MainTest.java", "public class MainTest {}");
doTestNoRunLineMarkers();
}
private void doTestNoRunLineMarkers() {
assertEquals(ThreeState.UNSURE, RunLineMarkerProvider.hadAnythingRunnable(myFixture.getFile().getVirtualFile()));
assertEmpty(myFixture.findAllGutters());
assertEquals(ThreeState.NO, RunLineMarkerProvider.hadAnythingRunnable(myFixture.getFile().getVirtualFile()));