From 546defb6f8da9bb7b7a84248720db2f3b11d1889 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Tue, 8 Jul 2025 23:12:47 +0200 Subject: [PATCH] Java: inner class accessing local variable or parameter can't be static (IDEA-375602) GitOrigin-RevId: 1b16d1388c09d51009b288ede89e73d156364619 --- .../ig/memory/InnerClassReferenceVisitor.java | 19 ++++----- .../classCanBeRecord/ConvertToRecordFix.java | 2 +- ...nymousInnerClassMayBeStaticInspection.java | 6 +-- .../InnerStaticsJDK16.after.java | 11 +++++ .../InnerStaticsJDK16.java | 11 +++++ .../ClassMayBeInterfaceInspectionTest.java | 40 ++++++++++++------- 6 files changed, 59 insertions(+), 30 deletions(-) diff --git a/java/java-analysis-impl/src/com/siyeh/ig/memory/InnerClassReferenceVisitor.java b/java/java-analysis-impl/src/com/siyeh/ig/memory/InnerClassReferenceVisitor.java index 948fb9c57e48..2a583b6407cc 100644 --- a/java/java-analysis-impl/src/com/siyeh/ig/memory/InnerClassReferenceVisitor.java +++ b/java/java-analysis-impl/src/com/siyeh/ig/memory/InnerClassReferenceVisitor.java @@ -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; } } diff --git a/java/java-impl-inspections/src/com/intellij/codeInspection/classCanBeRecord/ConvertToRecordFix.java b/java/java-impl-inspections/src/com/intellij/codeInspection/classCanBeRecord/ConvertToRecordFix.java index 2e31a8fbb5c5..99da927d89ef 100644 --- a/java/java-impl-inspections/src/com/intellij/codeInspection/classCanBeRecord/ConvertToRecordFix.java +++ b/java/java-impl-inspections/src/com/intellij/codeInspection/classCanBeRecord/ConvertToRecordFix.java @@ -131,7 +131,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(); } diff --git a/java/java-impl/src/com/siyeh/ig/memory/AnonymousInnerClassMayBeStaticInspection.java b/java/java-impl/src/com/siyeh/ig/memory/AnonymousInnerClassMayBeStaticInspection.java index d5fa89e06c77..0717cf228854 100644 --- a/java/java-impl/src/com/siyeh/ig/memory/AnonymousInnerClassMayBeStaticInspection.java +++ b/java/java-impl/src/com/siyeh/ig/memory/AnonymousInnerClassMayBeStaticInspection.java @@ -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; diff --git a/java/java-tests/testData/ig/com/siyeh/igtest/memory/inner_class_may_be_static/InnerStaticsJDK16.after.java b/java/java-tests/testData/ig/com/siyeh/igtest/memory/inner_class_may_be_static/InnerStaticsJDK16.after.java index 62da7822d7c2..5bc41b7bb8a7 100644 --- a/java/java-tests/testData/ig/com/siyeh/igtest/memory/inner_class_may_be_static/InnerStaticsJDK16.after.java +++ b/java/java-tests/testData/ig/com/siyeh/igtest/memory/inner_class_may_be_static/InnerStaticsJDK16.after.java @@ -26,4 +26,15 @@ class One { public static void main(String[] args) { One.Two.Three x; } +} +class C { + void x(int i) { + new Object() { + class Local { // can't be static + int get() { + return i; + } + } + }; + } } \ No newline at end of file diff --git a/java/java-tests/testData/ig/com/siyeh/igtest/memory/inner_class_may_be_static/InnerStaticsJDK16.java b/java/java-tests/testData/ig/com/siyeh/igtest/memory/inner_class_may_be_static/InnerStaticsJDK16.java index e44fea4916a8..0f6f4b4cc85d 100644 --- a/java/java-tests/testData/ig/com/siyeh/igtest/memory/inner_class_may_be_static/InnerStaticsJDK16.java +++ b/java/java-tests/testData/ig/com/siyeh/igtest/memory/inner_class_may_be_static/InnerStaticsJDK16.java @@ -26,4 +26,15 @@ class One { public static void main(String[] args) { One.Two.Three x; } +} +class C { + void x(int i) { + new Object() { + class Local { // can't be static + int get() { + return i; + } + } + }; + } } \ No newline at end of file diff --git a/java/java-tests/testSrc/com/siyeh/ig/classlayout/ClassMayBeInterfaceInspectionTest.java b/java/java-tests/testSrc/com/siyeh/ig/classlayout/ClassMayBeInterfaceInspectionTest.java index 9cbfe07d235c..2656f3b05ddf 100644 --- a/java/java-tests/testSrc/com/siyeh/ig/classlayout/ClassMayBeInterfaceInspectionTest.java +++ b/java/java-tests/testSrc/com/siyeh/ig/classlayout/ClassMayBeInterfaceInspectionTest.java @@ -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();