mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Java: report local class instantiation from a different static context (IDEA-372971)
GitOrigin-RevId: 6bc936ee3dd849db2db187b03edac58f4c9b2c73
This commit is contained in:
committed by
intellij-monorepo-bot
parent
7ca512a863
commit
109ff355e9
@@ -123,6 +123,7 @@ class.inherits.type.parameter=Class cannot inherit from its type parameter
|
||||
class.anonymous.extends.sealed=Anonymous classes must not extend sealed classes
|
||||
class.already.imported=''{0}'' is already defined in this compilation unit
|
||||
class.not.enclosing=''{0}'' is not an enclosing class
|
||||
local.class.cannot.be.instantiated.from.different.static.context=Local class ''{0}'' cannot be instantiated from a different static context
|
||||
class.cannot.be.referenced.from.static.context=''{0}'' cannot be referenced from a static context
|
||||
class.inheritance.different.type.arguments=''{0}'' cannot be inherited with different type arguments: ''{1}'' and ''{2}''
|
||||
class.inheritance.raw.and.generic=''{0}'' cannot be inherited as a raw type and with generic type arguments ''{1}''
|
||||
|
||||
+18
-2
@@ -95,9 +95,25 @@ final class ExpressionChecker {
|
||||
void checkCreateInnerClassFromStaticContext(@NotNull PsiElement element,
|
||||
@NotNull PsiElement placeToSearchEnclosingFrom,
|
||||
@NotNull PsiClass aClass) {
|
||||
if (!PsiUtil.isInnerClass(aClass)) return;
|
||||
if (aClass.hasModifierProperty(PsiModifier.STATIC)) return;
|
||||
PsiClass outerClass = aClass.getContainingClass();
|
||||
if (outerClass == null) return;
|
||||
if (outerClass == null) {
|
||||
if (!(aClass.getParent() instanceof PsiDeclarationStatement)) return;
|
||||
PsiMember scope = PsiTreeUtil.getParentOfType(aClass, PsiMember.class); // local class
|
||||
if (scope == null) return;
|
||||
if (scope.hasModifierProperty(PsiModifier.STATIC)) {
|
||||
PsiModifierListOwner enclosingStaticElement = PsiUtil.getEnclosingStaticElement(element, null);
|
||||
assert enclosingStaticElement != null;
|
||||
if (enclosingStaticElement != scope) {
|
||||
JavaErrorKinds.LocalClassInstantiationErrorContext context =
|
||||
new JavaErrorKinds.LocalClassInstantiationErrorContext(aClass, enclosingStaticElement);
|
||||
myVisitor.report(JavaErrorKinds.LOCAL_CLASS_INSTANTIATED_FROM_DIFFERENT_STATIC_CONTEXT.create(element, context));
|
||||
}
|
||||
return;
|
||||
}
|
||||
outerClass = scope.getContainingClass();
|
||||
if (outerClass == null) return;
|
||||
}
|
||||
|
||||
if (outerClass instanceof PsiSyntheticClass ||
|
||||
InheritanceUtil.hasEnclosingInstanceInScope(outerClass, placeToSearchEnclosingFrom, true, false)) {
|
||||
|
||||
+7
-2
@@ -438,6 +438,9 @@ public final class JavaErrorKinds {
|
||||
error(PsiClass.class, "class.sealed.permits.on.non.sealed")
|
||||
.withAnchor(cls -> requireNonNull(cls.getPermitsList()).getFirstChild())
|
||||
.withDescription(cls -> message("class.sealed.permits.on.non.sealed", cls.getName()));
|
||||
public static final Parameterized<PsiElement, LocalClassInstantiationErrorContext> LOCAL_CLASS_INSTANTIATED_FROM_DIFFERENT_STATIC_CONTEXT =
|
||||
parameterized(PsiElement.class, LocalClassInstantiationErrorContext.class, "local.class.cannot.be.instantiated.from.different.static.context")
|
||||
.withDescription((psi, ctx) -> message("local.class.cannot.be.instantiated.from.different.static.context", ctx.localClass().getName()));
|
||||
public static final Parameterized<PsiElement, ClassStaticReferenceErrorContext> CLASS_NOT_ENCLOSING =
|
||||
parameterized(PsiElement.class, ClassStaticReferenceErrorContext.class, "class.not.enclosing")
|
||||
.withDescription((psi, ctx) -> message("class.not.enclosing", formatClass(ctx.outerClass())));
|
||||
@@ -1688,13 +1691,15 @@ public final class JavaErrorKinds {
|
||||
}
|
||||
|
||||
public record ClassStaticReferenceErrorContext(@NotNull PsiClass outerClass,
|
||||
@Nullable PsiClass innerClass,
|
||||
@Nullable PsiClass innerClass,
|
||||
@NotNull PsiElement place) {
|
||||
public @Nullable PsiModifierListOwner enclosingStaticElement() {
|
||||
return PsiUtil.getEnclosingStaticElement(place, outerClass);
|
||||
}
|
||||
}
|
||||
|
||||
public record LocalClassInstantiationErrorContext(@NotNull PsiClass localClass, @NotNull PsiModifierListOwner enclosingStaticElement) {}
|
||||
|
||||
/**
|
||||
* A context for {@link #CONSTRUCTOR_AMBIGUOUS_IMPLICIT_CALL} error kind
|
||||
* @param psiClass a class where an ambiguous call is performed
|
||||
@@ -1703,7 +1708,7 @@ public final class JavaErrorKinds {
|
||||
*/
|
||||
public record AmbiguousImplicitConstructorCallContext(@NotNull PsiClass psiClass,
|
||||
@NotNull PsiMethod candidate1,
|
||||
@NotNull PsiMethod candidate2) {
|
||||
@NotNull PsiMethod candidate2) {
|
||||
@Nls String description() {
|
||||
String m1 = PsiFormatUtil.formatMethod(candidate1, PsiSubstitutor.EMPTY,
|
||||
PsiFormatUtilBase.SHOW_CONTAINING_CLASS |
|
||||
|
||||
+2
@@ -967,6 +967,8 @@ public final class DefaultJavaErrorFixProvider extends AbstractJavaErrorFixProvi
|
||||
fix(CLASS_CANNOT_BE_REFERENCED_FROM_STATIC_CONTEXT, makeInnerStatic);
|
||||
fix(CLASS_CANNOT_BE_REFERENCED_FROM_STATIC_CONTEXT,
|
||||
error -> removeModifierFix(requireNonNull(error.context().enclosingStaticElement()), PsiModifier.STATIC));
|
||||
fix(LOCAL_CLASS_INSTANTIATED_FROM_DIFFERENT_STATIC_CONTEXT,
|
||||
error -> removeModifierFix(requireNonNull(error.context().enclosingStaticElement()), PsiModifier.STATIC));
|
||||
fix(CLASS_GENERIC_EXTENDS_EXCEPTION, error -> {
|
||||
PsiJavaCodeReferenceElement ref = error.psi();
|
||||
PsiMember owner = PsiTreeUtil.getParentOfType(ref, PsiClass.class, PsiMethod.class);
|
||||
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
class LocalInstantiation {
|
||||
|
||||
// local class in method
|
||||
static void foo(Object there) {
|
||||
class Local {
|
||||
{
|
||||
there.hashCode();
|
||||
}
|
||||
static {
|
||||
//can only be instantiated from its own static context
|
||||
//cannot be instantiated here
|
||||
//not allowed to be instantiated heree
|
||||
//cannot be instantiated outside its static context
|
||||
<error descr="Local class 'Local' cannot be instantiated from a different static context">new Local()</error>; // should be highlighted as an error
|
||||
}
|
||||
static Runnable r = () -> {
|
||||
<error descr="Local class 'Local' cannot be instantiated from a different static context">new Local()</error>; // should be highlighted as an error
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// local class in lambda
|
||||
static Runnable foo = () -> {
|
||||
Object there = "";
|
||||
class Local {
|
||||
{
|
||||
there.hashCode();
|
||||
}
|
||||
static {
|
||||
<error descr="Local class 'Local' cannot be instantiated from a different static context">new Local()</error>; // should be highlighted as an error
|
||||
}
|
||||
static Runnable r = () -> {
|
||||
<error descr="Local class 'Local' cannot be instantiated from a different static context">new Local()</error>; // should be highlighted as an error
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// local class in switch
|
||||
static Object bar = switch (foo) {
|
||||
case Runnable r -> {
|
||||
Object there = "";
|
||||
class Local {
|
||||
{
|
||||
there.hashCode();
|
||||
}
|
||||
|
||||
static {
|
||||
<error descr="Local class 'Local' cannot be instantiated from a different static context">new Local()</error>; // should be highlighted as an error
|
||||
}
|
||||
|
||||
static Runnable r = () -> {
|
||||
<error descr="Local class 'Local' cannot be instantiated from a different static context">new Local()</error>; // should be highlighted as an error
|
||||
};
|
||||
}
|
||||
yield r;
|
||||
}
|
||||
};
|
||||
|
||||
// local class in instance init
|
||||
{
|
||||
Object there = "";
|
||||
class Local {
|
||||
{
|
||||
there.hashCode();
|
||||
}
|
||||
|
||||
static {
|
||||
<error descr="'LocalInstantiation.this' cannot be referenced from a static context">new Local()</error>; // should be highlighted as an error
|
||||
}
|
||||
|
||||
static Runnable r = () -> {
|
||||
<error descr="'LocalInstantiation.this' cannot be referenced from a static context">new Local()</error>; // should be highlighted as an error
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// local class in static init
|
||||
static {
|
||||
Object there = "";
|
||||
class Local {
|
||||
{
|
||||
there.hashCode();
|
||||
}
|
||||
|
||||
static {
|
||||
<error descr="Local class 'Local' cannot be instantiated from a different static context">new Local()</error>; // should be highlighted as an error
|
||||
}
|
||||
|
||||
static Runnable r = () -> {
|
||||
<error descr="Local class 'Local' cannot be instantiated from a different static context">new Local()</error>; // should be highlighted as an error
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
@@ -409,6 +409,7 @@ public class LightAdvHighlightingTest extends LightDaemonAnalyzerTestCase {
|
||||
public void testStatementWithExpression() { doTest(false); }
|
||||
public void testReturnFromConstructor() { doTest(false); }
|
||||
public void testInheritFromFinalLocalClass() { doTest(false); }
|
||||
public void testLocalClassInstantiation() { IdeaTestUtil.withLevel(getModule(), LanguageLevel.JDK_24, () -> doTest(false)); }
|
||||
|
||||
public void testStaticMethodCalls() {
|
||||
doTestFile(BASE_PATH + "/" + getTestName(false) + ".java").checkSymbolNames().test();
|
||||
|
||||
Reference in New Issue
Block a user