Skip to content

Commit 0734ebc

Browse files
authored
Fix change method signature to recognize name collision (#1767)
* Fix change method signature to recognize name collision - modify ChangeSignatureProcessor to check for the case where the new name is already a method accessible to a location that is calling the old name - make method in Checks class public to convert ICompilationUnit to CompilationUnit - add new test to ChangeSignatureTests - fixes #1751
1 parent 324bdfd commit 0734ebc

File tree

6 files changed

+164
-4
lines changed

6 files changed

+164
-4
lines changed

org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/Checks.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -915,7 +915,7 @@ public static boolean isDeclaredIn(VariableDeclaration tempDeclaration, Class<?
915915
return true;
916916
}
917917

918-
private static CompilationUnit convertICUtoCU(ICompilationUnit compilationUnit) {
918+
public static CompilationUnit convertICUtoCU(ICompilationUnit compilationUnit) {
919919
ASTParser parser= ASTParser.newParser(IASTSharedValues.SHARED_AST_LEVEL);
920920
parser.setKind(ASTParser.K_COMPILATION_UNIT);
921921
parser.setSource(compilationUnit);

org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/RefactoringCoreMessages.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ public final class RefactoringCoreMessages extends NLS {
105105

106106
public static String ChangeSignatureRefactoring_method_deleted;
107107

108+
public static String ChangeSignatureRefactoring_method_name_will_shadow;
109+
108110
public static String ChangeSignatureRefactoring_method_name_not_empty;
109111

110112
public static String ChangeSignatureRefactoring_modify_Parameters;

org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/refactoring.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,7 @@ ChangeSignatureRefactoring_method_deleted=The selected method has been deleted f
808808
ChangeSignatureRefactoring_native=Method ''{0}'' declared in type ''{1}'' is native. Reordering parameters will cause UnsatisfiedLinkError on runtime if you do not update your native libraries.
809809
ChangeSignatureRefactoring_duplicate_name=Duplicate parameter name: ''{0}''.
810810
ChangeSignatureRefactoring_return_type_contains_type_variable=The return type ''{0}'' contains the type variable ''{1}'', which may not be available in related methods.
811+
ChangeSignatureRefactoring_method_name_will_shadow=Renaming method ''{0}'' to ''{1}'' causes potential logic change due to an existing ''{0}'' method accessible at a call location.
811812
ChangeSignatureRefactoring_method_name_not_empty=The method name cannot be empty.
812813
ChangeSignatureRefactoring_default_value=Enter the default value for parameter ''{0}''.
813814
ChangeSignatureRefactoring_default_visibility=(package)

org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/structure/ChangeSignatureProcessor.java

Lines changed: 114 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2018 IBM Corporation and others.
2+
* Copyright (c) 2000, 2024 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -13,6 +13,7 @@
1313
*******************************************************************************/
1414
package org.eclipse.jdt.internal.corext.refactoring.structure;
1515

16+
import java.lang.ref.Cleaner;
1617
import java.util.ArrayList;
1718
import java.util.HashMap;
1819
import java.util.HashSet;
@@ -48,16 +49,21 @@
4849
import org.eclipse.jdt.core.ICompilationUnit;
4950
import org.eclipse.jdt.core.IJavaElement;
5051
import org.eclipse.jdt.core.IJavaProject;
52+
import org.eclipse.jdt.core.IMember;
5153
import org.eclipse.jdt.core.IMethod;
5254
import org.eclipse.jdt.core.IType;
5355
import org.eclipse.jdt.core.ITypeHierarchy;
56+
import org.eclipse.jdt.core.JavaCore;
5457
import org.eclipse.jdt.core.JavaModelException;
5558
import org.eclipse.jdt.core.Signature;
59+
import org.eclipse.jdt.core.SourceRange;
60+
import org.eclipse.jdt.core.WorkingCopyOwner;
5661
import org.eclipse.jdt.core.compiler.IProblem;
5762
import org.eclipse.jdt.core.dom.AST;
5863
import org.eclipse.jdt.core.dom.ASTNode;
5964
import org.eclipse.jdt.core.dom.ASTParser;
6065
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
66+
import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
6167
import org.eclipse.jdt.core.dom.Block;
6268
import org.eclipse.jdt.core.dom.BodyDeclaration;
6369
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
@@ -137,6 +143,7 @@
137143
import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
138144
import org.eclipse.jdt.internal.corext.refactoring.RefactoringScopeFactory;
139145
import org.eclipse.jdt.internal.corext.refactoring.RefactoringSearchEngine;
146+
import org.eclipse.jdt.internal.corext.refactoring.RefactoringSearchEngine2;
140147
import org.eclipse.jdt.internal.corext.refactoring.ReturnTypeInfo;
141148
import org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup;
142149
import org.eclipse.jdt.internal.corext.refactoring.StubTypeContext;
@@ -183,6 +190,8 @@ public class ChangeSignatureProcessor extends RefactoringProcessor implements ID
183190
private List<ExceptionInfo> fExceptionInfos;
184191
private TextChangeManager fChangeManager;
185192

193+
private Cleaner fCleaner= Cleaner.create();
194+
private CleanableWorkingCopyOwner fOwner= new CleanableWorkingCopyOwner();
186195
private IMethod fMethod;
187196
private IMethod fTopMethod;
188197
private IMethod[] fRippleMethods;
@@ -206,6 +215,7 @@ public class ChangeSignatureProcessor extends RefactoringProcessor implements ID
206215

207216
public ChangeSignatureProcessor(JavaRefactoringArguments arguments, RefactoringStatus status) throws JavaModelException {
208217
this((IMethod) null);
218+
fCleaner.register(this, fOwner);
209219
status.merge(initialize(arguments));
210220
}
211221

@@ -228,6 +238,26 @@ public ChangeSignatureProcessor(IMethod method) throws JavaModelException {
228238
}
229239
}
230240

241+
static class CleanableWorkingCopyOwner extends WorkingCopyOwner implements Runnable {
242+
@Override
243+
public void run() {
244+
resetWorkingCopies(this);
245+
}
246+
}
247+
248+
/**
249+
* Resets the working copies.
250+
*/
251+
private static void resetWorkingCopies(WorkingCopyOwner owner) {
252+
for (ICompilationUnit unit : JavaCore.getWorkingCopies(owner)) {
253+
try {
254+
unit.discardWorkingCopy();
255+
} catch (Exception exception) {
256+
// Do nothing
257+
}
258+
}
259+
}
260+
231261
private static List<ParameterInfo> createParameterInfoList(IMethod method) {
232262
try {
233263
String[] typeNames= method.getParameterTypes();
@@ -395,7 +425,9 @@ private RefactoringStatus checkSignature(boolean resolveBindings) {
395425
checkMethodName(result);
396426
if (result.hasFatalError())
397427
return result;
398-
428+
checkShadowing(result);
429+
if (result.hasFatalError())
430+
return result;
399431
checkParameterNamesAndValues(result);
400432
if (result.hasFatalError())
401433
return result;
@@ -424,6 +456,86 @@ private RefactoringStatus checkSignature(boolean resolveBindings) {
424456
return result;
425457
}
426458

459+
private void checkShadowing(RefactoringStatus result) {
460+
if (!fMethodName.equals(fMethod.getElementName())) {
461+
try {
462+
SearchResultGroup[] matches= findReferences(fMethod, new NullProgressMonitor());
463+
for (SearchResultGroup match : matches) {
464+
ICompilationUnit cu= match.getCompilationUnit();
465+
466+
for (SearchMatch matchResult : match.getSearchResults()) {
467+
if (matchResult instanceof MethodReferenceMatch methodMatch && methodMatch.getElement() instanceof IMethod method) {
468+
final MethodDeclaration methodDecl= ASTNodeSearchUtil.getMethodDeclarationNode(method, Checks.convertICUtoCU(cu));
469+
ASTNode typeParent= ASTNodes.getFirstAncestorOrNull(methodDecl, AbstractTypeDeclaration.class, AnonymousClassDeclaration.class);
470+
ITypeBinding typeBinding= null;
471+
if (typeParent instanceof AbstractTypeDeclaration atd) {
472+
typeBinding= atd.resolveBinding();
473+
} else if (typeParent instanceof AnonymousClassDeclaration acd) {
474+
typeBinding= acd.resolveBinding();
475+
}
476+
if (typeBinding != null) {
477+
if (recursiveShadowCheck(typeBinding, typeBinding.getPackage().getName(), true)) {
478+
RefactoringStatusContext context= JavaStatusContext.create(method.getTypeRoot(), new SourceRange(methodMatch.getOffset(), methodMatch.getLength()));
479+
String msg= Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_method_name_will_shadow, new Object[] {fMethod.getElementName(), fMethodName});
480+
if (method.getParameterNames().length == 0 && fMethodName.equals(methodDecl.getName().getFullyQualifiedName())) {
481+
result.addFatalError(msg, context);
482+
return;
483+
} else {
484+
result.addError(msg, context);
485+
}
486+
}
487+
}
488+
}
489+
}
490+
}
491+
} catch (JavaModelException e) {
492+
// ignore and exit
493+
}
494+
}
495+
}
496+
497+
private boolean recursiveShadowCheck(ITypeBinding typeBinding, String origPackage, boolean allowPrivate) {
498+
if (typeBinding == null) {
499+
return false;
500+
}
501+
IMethodBinding[] methods= typeBinding.getDeclaredMethods();
502+
for (IMethodBinding method : methods) {
503+
if (method.getName().equals(fMethodName) && method.getParameterNames().length == fParameterInfos.size() &&
504+
(allowPrivate || Modifier.isPublic(method.getModifiers()) || Modifier.isProtected(method.getModifiers())
505+
|| !Modifier.isPrivate(method.getModifiers()) && typeBinding.getPackage().getName().equals(origPackage))) {
506+
return true;
507+
}
508+
}
509+
if (recursiveShadowCheck(typeBinding.getSuperclass(), origPackage, false)) {
510+
return true;
511+
}
512+
ITypeBinding[] interfaces= typeBinding.getInterfaces();
513+
for (ITypeBinding implemented : interfaces) {
514+
if (recursiveShadowCheck(implemented, origPackage, false)) {
515+
return true;
516+
}
517+
}
518+
if (typeBinding.isMember()) {
519+
if (recursiveShadowCheck(typeBinding.getDeclaringClass(), origPackage, true)) {
520+
return true;
521+
}
522+
}
523+
return false;
524+
}
525+
526+
private SearchResultGroup[] findReferences(final IMember member, final IProgressMonitor monitor) throws JavaModelException {
527+
SearchPattern pattern= SearchPattern.createPattern(member, IJavaSearchConstants.REFERENCES, SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE);
528+
if (pattern == null) {
529+
return new SearchResultGroup[0];
530+
}
531+
final RefactoringSearchEngine2 engine= new RefactoringSearchEngine2(pattern);
532+
engine.setOwner(fOwner);
533+
engine.setFiltering(true, true);
534+
engine.setScope(RefactoringScopeFactory.create(member));
535+
engine.searchPattern(monitor);
536+
return (SearchResultGroup[]) engine.getResults();
537+
}
538+
427539
public boolean isSignatureSameAsInitial() throws JavaModelException {
428540
if (! isVisibilitySameAsInitial())
429541
return false;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package p;
2+
3+
class A_testFailIssue1751 {
4+
// change method signature 'm' to 'k'
5+
public void m() {
6+
}
7+
8+
class B {
9+
void k() {
10+
m();
11+
}
12+
}
13+
}

org.eclipse.jdt.ui.tests.refactoring/test cases/org/eclipse/jdt/ui/tests/refactoring/ChangeSignatureTests.java

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2020 IBM Corporation and others.
2+
* Copyright (c) 2000, 2024 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -197,6 +197,32 @@ private void helperRenameMethod(String[] signature, String newMethodName, boolea
197197
assertParticipant(classType);
198198
}
199199

200+
/*
201+
* Rename method 'm(signature)' to 'newMethodName(signature)'
202+
*/
203+
private void helperRenameMethodFail(String[] signature, String newMethodName, int expectedSeverity, boolean createDelegate, boolean markAsDeprecated, String typeName) throws Exception {
204+
ICompilationUnit cu= createCUfromTestFile(getPackageP(), false, true);
205+
IType classType= getType(cu, typeName);
206+
IMethod method = classType.getMethod("m", signature);
207+
assertTrue("method m does not exist in " +typeName, method.exists());
208+
assertTrue("refactoring not available", RefactoringAvailabilityTester.isChangeSignatureAvailable(method));
209+
210+
ChangeSignatureProcessor processor= new ChangeSignatureProcessor(method);
211+
Refactoring ref= new ProcessorBasedRefactoring(processor);
212+
213+
processor.setNewMethodName(newMethodName);
214+
processor.setDelegateUpdating(createDelegate);
215+
processor.setDeprecateDelegates(markAsDeprecated);
216+
FussyProgressMonitor testMonitor= new FussyProgressMonitor();
217+
ref.checkInitialConditions(testMonitor);
218+
testMonitor.assertUsedUp();
219+
220+
JavaRefactoringDescriptor descriptor= processor.createDescriptor();
221+
RefactoringStatus result= performRefactoring(descriptor);
222+
assertNotNull("precondition was supposed to fail", result);
223+
assertEquals("Severity:", expectedSeverity, result.getSeverity());
224+
}
225+
200226
private void helperDoAll(String typeName,
201227
String methodName,
202228
String[] signature,
@@ -581,6 +607,12 @@ public void testFail1() throws Exception{
581607
helperFail(new String[0], new String[0], RefactoringStatus.FATAL);
582608
}
583609

610+
@Test
611+
public void testFailIssue1751() throws Exception {
612+
String[] signature= {};
613+
helperRenameMethodFail(signature, "k", RefactoringStatus.FATAL, false, true, "A_testFailIssue1751");
614+
}
615+
584616
@Test
585617
public void testFailAdd2() throws Exception{
586618
String[] signature= {"I"};

0 commit comments

Comments
 (0)