Java: inner class accessing local variable or parameter can't be static (IDEA-375602)

(cherry picked from commit 1b16d1388c09d51009b288ede89e73d156364619)

IJ-CR-168593

GitOrigin-RevId: 7ff7cfa95cdc674c5b1d32a572eec0b95bbfb58d
This commit is contained in:
Bas Leijdekkers
2025-07-08 23:12:47 +02:00
committed by intellij-monorepo-bot
parent 5df57ad536
commit 9a906a8753
6 changed files with 59 additions and 30 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2003-2017 Dave Griffith, Bas Leijdekkers
* Copyright 2003-2025 Dave Griffith, Bas Leijdekkers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,11 +24,11 @@ import org.jetbrains.annotations.NotNull;
public class InnerClassReferenceVisitor extends JavaRecursiveElementWalkingVisitor {
private final PsiClass innerClass;
private final boolean allowReferencesToLocalVariables;
private boolean referencesStaticallyAccessible = true;
private boolean allowReferencesToLocalVariables = true;
public InnerClassReferenceVisitor(@NotNull PsiClass innerClass) {
this.innerClass = innerClass;
this(innerClass, false);
}
public InnerClassReferenceVisitor(@NotNull PsiClass innerClass, boolean allowReferencesToLocalVariables) {
@@ -51,8 +51,8 @@ public class InnerClassReferenceVisitor extends JavaRecursiveElementWalkingVisit
if (PsiTreeUtil.isAncestor(innerClass, aClass, false) || aClass.hasModifierProperty(PsiModifier.STATIC)) {
return true;
}
if (aClass instanceof PsiTypeParameter) {
PsiTypeParameterListOwner owner = ((PsiTypeParameter)aClass).getOwner();
if (aClass instanceof PsiTypeParameter parameter) {
PsiTypeParameterListOwner owner = parameter.getOwner();
return owner != null && PsiTreeUtil.isAncestor(innerClass, owner, false);
}
final PsiClass containingClass = aClass.getContainingClass();
@@ -98,7 +98,7 @@ public class InnerClassReferenceVisitor extends JavaRecursiveElementWalkingVisit
}
final PsiElement target = expression.resolve();
if (target == null) {
referencesStaticallyAccessible = false; // TODO(bartekpacia): We probably should introduce the 3rd "unknown" state to signal this
referencesStaticallyAccessible = false;
return;
}
if (target instanceof PsiLocalVariable || target instanceof PsiParameter) {
@@ -128,7 +128,7 @@ public class InnerClassReferenceVisitor extends JavaRecursiveElementWalkingVisit
}
referencesStaticallyAccessible = false;
}
else if (target instanceof PsiClass && !isClassStaticallyAccessible((PsiClass)target)) {
else if (target instanceof PsiClass aClass && !isClassStaticallyAccessible(aClass)) {
referencesStaticallyAccessible = false;
}
}
@@ -144,10 +144,7 @@ public class InnerClassReferenceVisitor extends JavaRecursiveElementWalkingVisit
return;
}
final PsiElement target = classReference.resolve();
if (!(target instanceof PsiClass)) {
return;
}
if (!isClassStaticallyAccessible((PsiClass)target)) {
if (target instanceof PsiClass aClass && !isClassStaticallyAccessible(aClass)) {
referencesStaticallyAccessible = false;
}
}

View File

@@ -128,7 +128,7 @@ public final class ConvertToRecordFix implements LocalQuickFix {
* @see com.siyeh.ig.memory.InnerClassMayBeStaticInspection
*/
private static boolean containsOuterNonStaticReferences(PsiClass psiClass) {
InnerClassReferenceVisitor visitor = new InnerClassReferenceVisitor(psiClass, false);
InnerClassReferenceVisitor visitor = new InnerClassReferenceVisitor(psiClass);
psiClass.accept(visitor);
return !visitor.canInnerClassBeStatic();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2003-2023 Dave Griffith, Bas Leijdekkers
* Copyright 2003-2025 Dave Griffith, Bas Leijdekkers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -78,7 +78,7 @@ public final class AnonymousInnerClassMayBeStaticInspection extends BaseInspecti
// strictly speaking can be named static inner class but not when part of the current containing class
return;
}
final InnerClassReferenceVisitor visitor = new InnerClassReferenceVisitor(anonymousClass);
final InnerClassReferenceVisitor visitor = new InnerClassReferenceVisitor(anonymousClass, true);
anonymousClass.accept(visitor);
if (!visitor.canInnerClassBeStatic()) {
return;
@@ -106,7 +106,7 @@ public final class AnonymousInnerClassMayBeStaticInspection extends BaseInspecti
return;
}
final PsiElement target = reference.resolve();
if (!(target instanceof PsiClass) || !PsiUtil.isLocalClass((PsiClass)target)) {
if (!(target instanceof PsiClass aClass) || !PsiUtil.isLocalClass(aClass)) {
return;
}
referenceToLocalClass = true;

View File

@@ -26,4 +26,15 @@ class One<A> {
public static void main(String[] args) {
One.Two.Three<Void> x;
}
}
class C {
void x(int i) {
new Object() {
class Local { // can't be static
int get() {
return i;
}
}
};
}
}

View File

@@ -26,4 +26,15 @@ class One<A> {
public static void main(String[] args) {
One<Void>.Two<Void>.Three<Void> x;
}
}
class C {
void x(int i) {
new Object() {
class Local { // can't be static
int get() {
return i;
}
}
};
}
}

View File

@@ -1,18 +1,4 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the 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.siyeh.ig.classlayout;
import com.intellij.codeInspection.InspectionProfileEntry;
@@ -69,6 +55,30 @@ public class ClassMayBeInterfaceInspectionTest extends LightJavaInspectionTestCa
}""");
}
public void testLocalClassAccessingParameter() {
doTest("""
class Outer {
void x(int parameter) {
abstract class Example {
public static final int MY_CONST = 42;
public abstract void foo();
public void g() {
System.out.println(parameter);
}
}
class Inheritor extends Example {
@Override
public void foo() {
System.out.println(MY_CONST);
}
}
}
}
""");
}
@Override
protected InspectionProfileEntry getInspection() {
final ClassMayBeInterfaceInspection inspection = new ClassMayBeInterfaceInspection();