Java: fix incorrect "visibility conflict" detection when moving static method (IDEA-223533)

GitOrigin-RevId: 3137d113f9535fd7bd3595aa7bcfc67c763fae5d
This commit is contained in:
Bas Leijdekkers
2025-01-24 14:43:12 +01:00
committed by intellij-monorepo-bot
parent be5e75752f
commit 53ded717a4
12 changed files with 57 additions and 18 deletions

View File

@@ -15,6 +15,7 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.light.LightElement;
import com.intellij.psi.impl.source.resolve.JavaResolveUtil;
import com.intellij.psi.presentation.java.SymbolPresentationUtil;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiSearchScopeUtil;
@@ -133,9 +134,9 @@ public final class RefactoringConflictsUtilImpl implements RefactoringConflictsU
conflicts.putValue(targetClass, message);
}
// check for member accessibility
else if (!manager.getResolveHelper().isAccessible(member, modifierListCopy, ref, targetClass, null)) {
else if (!JavaResolveUtil.isAccessible(member, targetClass, modifierListCopy, ref, null, null)) {
String message = JavaRefactoringBundle.message("0.is.1.and.will.not.be.accessible.from.2.in.the.target.class",
RefactoringUIUtil.getDescription(member, true),
RefactoringUIUtil.getDescription(member, false),
VisibilityUtil.toPresentableText(
VisibilityUtil.getVisibilityModifier(modifierListCopy)),
RefactoringUIUtil.getDescription(ConflictsUtil.getContainer(ref), true));

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 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.intellij.psi.impl.source.resolve;
import com.intellij.openapi.project.Project;
@@ -112,7 +112,7 @@ public final class JavaResolveUtil {
JavaPsiFacade facade = JavaPsiFacade.getInstance(manager.getProject());
if (effectiveAccessLevel == PsiUtil.ACCESS_LEVEL_PROTECTED) {
if (facade.arePackagesTheSame(member, place)) {
if (facade.arePackagesTheSame((memberClass == null) ? member : memberClass, place)) {
return true;
}
if (memberClass == null) {
@@ -162,17 +162,14 @@ public final class JavaResolveUtil {
}
}
if (!facade.arePackagesTheSame(member, place)) return false;
//if (modifierList.hasModifierProperty(PsiModifier.STATIC)) return true;
if (!facade.arePackagesTheSame((memberClass == null) ? member : memberClass, place)) return false;
// maybe inheritance lead through package-private class in other package ?
final PsiClass placeClass = getContextClass(place);
if (memberClass == null || placeClass == null) return true;
// check only classes since interface members are public, and if placeClass is interface,
// then its members are static, and cannot refer to non-static members of memberClass
if (memberClass.isInterface() || placeClass.isInterface()) return true;
PsiClass clazz = accessObjectClass != null ?
accessObjectClass :
placeClass.getSuperClass(); //may start from super class
PsiClass clazz = accessObjectClass != null ? accessObjectClass : placeClass.getSuperClass(); //may start from super class
if (clazz != null && clazz.isInheritor(memberClass, true)) {
PsiClass superClass = clazz;
while (!manager.areElementsEquivalent(superClass, memberClass)) {

View File

@@ -0,0 +1,4 @@
package pack1;
public class A {
}

View File

@@ -0,0 +1,7 @@
package pack2;
public class B {
public static void foo() {
C.bar();
}
}

View File

@@ -0,0 +1,6 @@
package pack2;
public class C {
static void bar() {}
}

View File

@@ -0,0 +1,5 @@
package pack1;
public class A {
public static void bar() {}
}

View File

@@ -0,0 +1,9 @@
package pack2;
import pack1.A;
public class B {
public static void foo() {
A.bar();
}
}

View File

@@ -0,0 +1,5 @@
package pack2;
public class C {
}

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 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.intellij.java.refactoring;
import com.intellij.JavaTestUtil;
@@ -91,6 +91,10 @@ public class MoveMembersTest extends LightMultiFileTestCase {
doTest("pack1.A", "pack1.C", 0);
}
public void testPackagePrivateStaticMember() {
doTest("pack1.A", "pack2.C", true, PsiModifier.PACKAGE_LOCAL, 0);
}
public void testUntouchedVisibility() {
doTest("pack1.A", "pack1.C", 0, 1);
}

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 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.intellij.java.refactoring.convertToInstanceMethod;
import com.intellij.JavaTestUtil;
@@ -40,7 +40,8 @@ public class ConvertToInstanceMethodTest extends LightRefactoringTestCase {
fail("Conflict was not detected");
}
catch (BaseRefactoringProcessor.ConflictsInTestsException e) {
assertEquals("Method <b><code>Test.foo(Bar)</code></b> is private and will not be accessible from instance initializer of class <b><code>Test</code></b>.", e.getMessage());
assertEquals("Method <b><code>foo(Bar)</code></b> is private and will not be accessible from instance initializer of class " +
"<b><code>Test</code></b>.", e.getMessage());
}
}

View File

@@ -1,3 +1,3 @@
Field A.X is private and will not be accessible from file noImports.kt.
Field A.X is private and will not be accessible from file onDemandImport.kt.
Field A.X is private and will not be accessible from file specificImport.kt.
Field X is private and will not be accessible from file noImports.kt.
Field X is private and will not be accessible from file onDemandImport.kt.
Field X is private and will not be accessible from file specificImport.kt.

View File

@@ -1,3 +1,3 @@
Method A.foo(String) is private and will not be accessible from file noImports.kt.
Method A.foo(String) is private and will not be accessible from file onDemandImport.kt.
Method A.foo(String) is private and will not be accessible from file specificImport.kt.
Method foo(String) is private and will not be accessible from file noImports.kt.
Method foo(String) is private and will not be accessible from file onDemandImport.kt.
Method foo(String) is private and will not be accessible from file specificImport.kt.