Bug 99622 Rename method misses ambiguously overridden method

RippleMethodFinder2 now has a MultiMap for the fTypeToMethod field
because with Generics it is possible to implement more than one method
in a super interface

Change-Id: I187127fae2dc2d6439a795bef915109875ca1eca
Signed-off-by: Nikolay Metchev <nikolaymetchev@gmail.com>
Signed-off-by: Jeff Johnston <jjohnstn@redhat.com>
Reviewed-on: https://git.eclipse.org/r/c/jdt/eclipse.jdt.ui/+/70037
Tested-by: JDT Bot <jdt-bot@eclipse.org>
diff --git a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/util/MethodOverrideTester.java b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/util/MethodOverrideTester.java
index 00a5374..04ef60c 100644
--- a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/util/MethodOverrideTester.java
+++ b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/util/MethodOverrideTester.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2016 IBM Corporation and others.
+ * Copyright (c) 2000, 2022 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -10,12 +10,15 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Nikolay Metchev <nikolaymetchev@gmail.com> - [rename] https://bugs.eclipse.org/99622
  *******************************************************************************/
 
 package org.eclipse.jdt.internal.corext.util;
 
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 
 import org.eclipse.core.runtime.Assert;
 
@@ -180,6 +183,28 @@
 		}
 		return method;
 	}
+	
+	/**
+	 * Finds all overridden methods in a type and its super types. First the super class is examined and then the implemented interfaces.
+	 * With generics it is possible that 2 methods in the same type are overidden at the same time. In that case all overrides are returned
+	 * @param type The type to find methods in
+	 * @param overriding The overriding method
+	 * @return The overridden methods or an empty set if no method is overridden
+	 * @throws JavaModelException if a problem occurs
+	 */
+	public Set<IMethod> findAllOverriddenMethodsInHierarchy(IType type, IMethod overriding) throws JavaModelException {
+		Set<IMethod> ans = findAllOverriddenMethodsInType(type, overriding);
+		IType superClass= fHierarchy.getSuperclass(type);
+		if (superClass != null) {
+			ans.addAll(findAllOverriddenMethodsInHierarchy(superClass, overriding));
+		}
+		IType[] superInterfaces= fHierarchy.getSuperInterfaces(type);
+		for (int i= 0; i < superInterfaces.length; i++) {
+			ans.addAll(findAllOverriddenMethodsInHierarchy(superInterfaces[i], overriding));
+		}
+		return ans;
+	}
+
 
 	/**
 	 * Finds an overridden method in a type. With generics it is possible that 2 methods in the same type are overridden at the same time.
@@ -203,6 +228,33 @@
 		}
 		return null;
 	}
+	
+	/**
+	 * Finds all overridden methods in a type. With generics it is possible that 2 methods in the same type are overridden at the same time.
+	 * In that case all overridden methods found are returned.
+	 * @param overriddenType The type to find methods in
+	 * @param overriding The overriding method
+	 * @return All overridden methods or an empty set if no method is overridden
+	 * @throws JavaModelException if a problem occurs
+	 */
+	public Set<IMethod> findAllOverriddenMethodsInType(IType overriddenType, IMethod overriding) throws JavaModelException {
+		Set<IMethod> ans = new HashSet<>();
+		int flags= overriding.getFlags();
+		if (Flags.isPrivate(flags) || Flags.isStatic(flags) || overriding.isConstructor())
+			return ans;
+		IMethod[] overriddenMethods= overriddenType.getMethods();
+		for (int i= 0; i < overriddenMethods.length; i++) {
+			IMethod overridden= overriddenMethods[i];
+			flags= overridden.getFlags();
+			if (Flags.isPrivate(flags) || Flags.isStatic(flags) || overridden.isConstructor())
+				continue;
+			if (isSubsignature(overriding, overridden)) {
+				ans.add(overridden);
+			}
+		}
+		return ans;
+	}
+
 
 	/**
 	 * Finds an overriding method in a type.
@@ -549,4 +601,14 @@
 		}
 	}
 
+	/**
+	 * Finds all overrides for the passed in method.
+	 * With generics it is possible that 2 methods in the same type are overridden at the same time. In that case all overrides are returned
+	 * @param overriding The overriding method
+	 * @return The overridden methods or an empty set if no method is overridden
+	 * @throws JavaModelException if a problem occurs
+	 */
+	public Set<IMethod> findAllOverridenMethods(IMethod overriding) throws JavaModelException {
+		return findAllOverriddenMethodsInHierarchy(overriding.getDeclaringType(), overriding);
+	}
 }
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test41/in/A.java b/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test41/in/A.java
new file mode 100644
index 0000000..ab751ae
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test41/in/A.java
@@ -0,0 +1,16 @@
+//can rename A.m to k
+package p;
+interface I {
+	
+}
+class B<T, K extends I> {
+	void m(T t) {
+	}
+	void m(K t) {
+	}
+}
+class A extends B<I, I> {
+	@Override
+	void m(I i) {
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test41/out/A.java b/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test41/out/A.java
new file mode 100644
index 0000000..192c543
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test41/out/A.java
@@ -0,0 +1,16 @@
+//can rename A.m to k
+package p;
+interface I {
+	
+}
+class B<T, K extends I> {
+	void k(T t) {
+	}
+	void k(K t) {
+	}
+}
+class A extends B<I, I> {
+	@Override
+	void k(I i) {
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test42/in/A.java b/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test42/in/A.java
new file mode 100644
index 0000000..3f7c770
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test42/in/A.java
@@ -0,0 +1,16 @@
+//can rename A.m(T) to k
+package p;
+interface I {
+	
+}
+class A<T, K extends I> {
+	void m(T t) {
+	}
+	void m(K t) {
+	}
+}
+class B extends A<I, I> {
+	@Override
+	void m(I i) {
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test42/out/A.java b/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test42/out/A.java
new file mode 100644
index 0000000..fa2814b
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test42/out/A.java
@@ -0,0 +1,16 @@
+//can rename A.m(T) to k
+package p;
+interface I {
+	
+}
+class A<T, K extends I> {
+	void k(T t) {
+	}
+	void k(K t) {
+	}
+}
+class B extends A<I, I> {
+	@Override
+	void k(I i) {
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test43/in/A.java b/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test43/in/A.java
new file mode 100644
index 0000000..0272802
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test43/in/A.java
@@ -0,0 +1,20 @@
+//can rename A.m(Object) to k
+package p;
+
+class A1 extends C3 {
+	class A extends C<Object> implements I1<Object> {
+		public void m(Object o) {
+		}
+	}
+}
+
+class C3 {
+	class C<E> {
+		public void m(E e) {
+		}
+	}
+}
+
+interface I1<T> {
+	void m(T t);
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test43/out/A.java b/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test43/out/A.java
new file mode 100644
index 0000000..ffa882a
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test43/out/A.java
@@ -0,0 +1,20 @@
+//can rename A.m(Object) to k
+package p;
+
+class A1 extends C3 {
+	class A extends C<Object> implements I1<Object> {
+		public void k(Object o) {
+		}
+	}
+}
+
+class C3 {
+	class C<E> {
+		public void k(E e) {
+		}
+	}
+}
+
+interface I1<T> {
+	void k(T t);
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test44/in/A.java b/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test44/in/A.java
new file mode 100644
index 0000000..2ecc25f
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test44/in/A.java
@@ -0,0 +1,20 @@
+//can rename A.m(E) to k
+package p;
+
+class A1 extends C3 {
+	class A2 extends A<Object> implements I1<Object> {
+		public void m(Object o) {
+		}
+	}
+}
+
+class C3 {
+	class A<E> {
+		public void m(E e) {
+		}
+	}
+}
+
+interface I1<T> {
+	void m(T t);
+}
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test44/out/A.java b/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test44/out/A.java
new file mode 100644
index 0000000..8005006
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test44/out/A.java
@@ -0,0 +1,20 @@
+//can rename A.m(E) to k
+package p;
+
+class A1 extends C3 {
+	class A2 extends A<Object> implements I1<Object> {
+		public void k(Object o) {
+		}
+	}
+}
+
+class C3 {
+	class A<E> {
+		public void k(E e) {
+		}
+	}
+}
+
+interface I1<T> {
+	void k(T t);
+}
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test45/in/A.java b/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test45/in/A.java
new file mode 100644
index 0000000..a2bb91a
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test45/in/A.java
@@ -0,0 +1,20 @@
+//can rename A.m(T) to k
+package p;
+
+class A1 extends C3 {
+	class A2 extends C<Object> implements A<Object> {
+		public void m(Object o) {
+		}
+	}
+}
+
+class C3 {
+	class C<E> {
+		public void m(E e) {
+		}
+	}
+}
+
+interface A<T> {
+	void m(T t);
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test45/out/A.java b/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test45/out/A.java
new file mode 100644
index 0000000..646443e
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test45/out/A.java
@@ -0,0 +1,20 @@
+//can rename A.m(T) to k
+package p;
+
+class A1 extends C3 {
+	class A2 extends C<Object> implements A<Object> {
+		public void k(Object o) {
+		}
+	}
+}
+
+class C3 {
+	class C<E> {
+		public void k(E e) {
+		}
+	}
+}
+
+interface A<T> {
+	void k(T t);
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test46/in/A.java b/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test46/in/A.java
new file mode 100644
index 0000000..a9876bb
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test46/in/A.java
@@ -0,0 +1,10 @@
+//can rename A.m(E[]) to k
+package p;
+
+class A<E> {
+    void m(E[] e) {}
+    void m(String[] t) {};
+}
+class Sub extends A<String> {
+    void m(String... s) {}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test46/out/A.java b/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test46/out/A.java
new file mode 100644
index 0000000..40e6eb0
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test46/out/A.java
@@ -0,0 +1,10 @@
+//can rename A.m(E[]) to k
+package p;
+
+class A<E> {
+    void k(E[] e) {}
+    void k(String[] t) {};
+}
+class Sub extends A<String> {
+    void k(String... s) {}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test47/in/A.java b/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test47/in/A.java
new file mode 100644
index 0000000..bad38ad
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test47/in/A.java
@@ -0,0 +1,10 @@
+//can rename A.m(String[]) to k
+package p;
+
+class A<E> {
+    void m(E[] e) {}
+    void m(String[] t) {};
+}
+class Sub extends A<String> {
+    void m(String... s) {}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test47/out/A.java b/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test47/out/A.java
new file mode 100644
index 0000000..6f7da09
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test47/out/A.java
@@ -0,0 +1,10 @@
+//can rename A.m(String[]) to k
+package p;
+
+class A<E> {
+    void k(E[] e) {}
+    void k(String[] t) {};
+}
+class Sub extends A<String> {
+    void k(String... s) {}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test48/in/A.java b/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test48/in/A.java
new file mode 100644
index 0000000..4ebe646
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test48/in/A.java
@@ -0,0 +1,10 @@
+//can rename A.m(String...) to k
+package p;
+
+class Sup<E> {
+    void m(E[] e) {}
+    void m(String[] t) {};
+}
+class A extends Sup<String> {
+    void m(String... s) {}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test48/out/A.java b/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test48/out/A.java
new file mode 100644
index 0000000..56ae7cf
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/RenameVirtualMethodInClass/test48/out/A.java
@@ -0,0 +1,10 @@
+//can rename A.m(String...) to k
+package p;
+
+class Sup<E> {
+    void k(E[] e) {}
+    void k(String[] t) {};
+}
+class A extends Sup<String> {
+    void k(String... s) {}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.ui.tests.refactoring/test cases/org/eclipse/jdt/ui/tests/refactoring/RenameVirtualMethodInClassTests.java b/org.eclipse.jdt.ui.tests.refactoring/test cases/org/eclipse/jdt/ui/tests/refactoring/RenameVirtualMethodInClassTests.java
index 4d6c8c1..9dd1b70 100644
--- a/org.eclipse.jdt.ui.tests.refactoring/test cases/org/eclipse/jdt/ui/tests/refactoring/RenameVirtualMethodInClassTests.java
+++ b/org.eclipse.jdt.ui.tests.refactoring/test cases/org/eclipse/jdt/ui/tests/refactoring/RenameVirtualMethodInClassTests.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2020 IBM Corporation and others.
+ * Copyright (c) 2000, 2022 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -10,6 +10,7 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Nikolay Metchev <nikolaymetchev@gmail.com> - [rename] https://bugs.eclipse.org/99622
  *******************************************************************************/
 package org.eclipse.jdt.ui.tests.refactoring;
 
@@ -495,6 +496,38 @@
 		helper2();
 	}
 
+	public void test41() throws Exception {
+		helper2_0("m", "k", new String[] { "QI;" });
+	}
+
+	public void test42() throws Exception {
+		helper2_0("m", "k", new String[] { "QT;" });
+	}
+
+	public void test43() throws Exception {
+		helper2_0("m", "k", new String[] { "QObject;" });
+	}
+
+	public void test44() throws Exception {
+		helper2_0("m", "k", new String[] { "QE;" });
+	}
+
+	public void test45() throws Exception {
+		helper2_0("m", "k", new String[] { "QT;" });
+	}
+
+	public void test46() throws Exception {
+		helper2_0("m", "k", new String[] { "[QE;" });
+	}
+
+	public void test47() throws Exception {
+		helper2_0("m", "k", new String[] { "[QString;" });
+	}
+
+	public void test48() throws Exception {
+		helper2_0("m", "k", new String[] { "[QString;" });
+	}
+
 	//anonymous inner class
 	@Test
 	public void test23() throws Exception{
diff --git a/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/rename/RenameVirtualMethodProcessor.java b/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/rename/RenameVirtualMethodProcessor.java
index dba9de0..8c52643 100644
--- a/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/rename/RenameVirtualMethodProcessor.java
+++ b/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/rename/RenameVirtualMethodProcessor.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2020 IBM Corporation and others.
+ * Copyright (c) 2000, 2022 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -10,6 +10,7 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Nikolay Metchev <nikolaymetchev@gmail.com> - [rename] https://bugs.eclipse.org/99622
  *******************************************************************************/
 package org.eclipse.jdt.internal.corext.refactoring.rename;
 
@@ -46,7 +47,6 @@
 
 public class RenameVirtualMethodProcessor extends RenameMethodProcessor {
 
-	private IMethod fOriginalMethod;
 	private boolean fActivationChecked;
 	private ITypeHierarchy fCachedHierarchy= null;
 
@@ -57,7 +57,6 @@
 	 */
 	public RenameVirtualMethodProcessor(IMethod method) {
 		super(method);
-		fOriginalMethod= getMethod();
 	}
 
 	/**
@@ -71,7 +70,6 @@
 		this(method);
 		RefactoringStatus initializeStatus= initialize(arguments);
 		status.merge(initializeStatus);
-		fOriginalMethod= getMethod();
 	}
 
 	/*
@@ -84,16 +82,11 @@
 	 */
 	RenameVirtualMethodProcessor(IMethod topLevel, IMethod[] ripples, TextChangeManager changeManager, ITypeHierarchy hierarchy, GroupCategorySet categorySet) {
 		super(topLevel, changeManager, categorySet);
-		fOriginalMethod= getMethod();
 		fActivationChecked= true; // is top level
 		fCachedHierarchy= hierarchy; // may be null
 		setMethodsToRename(ripples);
 	}
 
-	public IMethod getOriginalMethod() {
-		return fOriginalMethod;
-	}
-
 	private ITypeHierarchy getCachedHierarchy(IType declaring, IProgressMonitor monitor) throws JavaModelException {
 		if (fCachedHierarchy != null && declaring.equals(fCachedHierarchy.getType()))
 			return fCachedHierarchy;
@@ -117,19 +110,7 @@
 			monitor.beginTask("", 3); //$NON-NLS-1$
 			if (!fActivationChecked) {
 				// the following code may change the method to be changed.
-				IMethod method= getMethod();
-				fOriginalMethod= method;
-
-				ITypeHierarchy hierarchy= null;
-				IType declaringType= method.getDeclaringType();
-				if (!declaringType.isInterface())
-					hierarchy= getCachedHierarchy(declaringType, new SubProgressMonitor(monitor, 1));
-
-				IMethod topmost= getMethod();
-				if (MethodChecks.isVirtual(topmost))
-					topmost= MethodChecks.getTopmostMethod(getMethod(), hierarchy, monitor);
-				if (topmost != null)
-					initialize(topmost);
+				initialize(getMethod());
 				fActivationChecked= true;
 			}
 		} finally{
diff --git a/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/rename/RippleMethodFinder2.java b/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/rename/RippleMethodFinder2.java
index 7ab3b4d..c571c16 100644
--- a/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/rename/RippleMethodFinder2.java
+++ b/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/rename/RippleMethodFinder2.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * Copyright (c) 2000, 2022 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -10,6 +10,7 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Nikolay Metchev <nikolaymetchev@gmail.com> - [rename] https://bugs.eclipse.org/99622
  *******************************************************************************/
 package org.eclipse.jdt.internal.corext.refactoring.rename;
 
@@ -28,10 +29,10 @@
 import org.eclipse.core.runtime.Assert;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
 import org.eclipse.core.runtime.OperationCanceledException;
 import org.eclipse.core.runtime.SubProgressMonitor;
 
-import org.eclipse.jdt.core.IMember;
 import org.eclipse.jdt.core.IMethod;
 import org.eclipse.jdt.core.IRegion;
 import org.eclipse.jdt.core.IType;
@@ -50,14 +51,15 @@
 import org.eclipse.jdt.internal.corext.refactoring.RefactoringScopeFactory;
 import org.eclipse.jdt.internal.corext.refactoring.base.ReferencesInBinaryContext;
 import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
+import org.eclipse.jdt.internal.corext.util.MethodOverrideTester;
 import org.eclipse.jdt.internal.corext.util.SearchUtils;
 
 public class RippleMethodFinder2 {
 
 	private final IMethod fMethod;
-	private List<IMethod> fDeclarations;
+	private Set<IMethod> fDeclarations;
 	private ITypeHierarchy fHierarchy;
-	private Map<IType, IMethod> fTypeToMethod;
+	private MultiMap<IType, IMethod> fTypeToMethod;
 	private Set<IType> fRootTypes;
 	private MultiMap<IType, IType> fRootReps;
 	private Map<IType, ITypeHierarchy> fRootHierarchies;
@@ -66,6 +68,7 @@
 	private final boolean fExcludeBinaries;
 	private final ReferencesInBinaryContext fBinaryRefs;
 	private Map<IMethod, SearchMatch> fDeclarationToMatch;
+	private boolean fSearchOnlyInCompilationUnit = false;
 
 	private static class MultiMap<K, V> {
 		HashMap<K, Collection<V>> fImplementation= new HashMap<>();
@@ -131,9 +134,10 @@
 	}
 
 
-	private RippleMethodFinder2(IMethod method, boolean excludeBinaries){
+	private RippleMethodFinder2(IMethod method, boolean excludeBinaries, boolean searchOnlyInCompilationUnit){
 		fMethod= method;
 		fExcludeBinaries= excludeBinaries;
+		fSearchOnlyInCompilationUnit= searchOnlyInCompilationUnit;
 		fBinaryRefs= null;
 	}
 
@@ -149,7 +153,17 @@
 			if (! MethodChecks.isVirtual(method))
 				return new IMethod[]{ method };
 
-			return new RippleMethodFinder2(method, excludeBinaries).getAllRippleMethods(pm, owner);
+			return new RippleMethodFinder2(method, excludeBinaries, false).getAllRippleMethods(pm, owner);
+		} finally{
+			pm.done();
+		}
+	}
+	public static IMethod[] getRelatedMethodsInCompilationUnit(IMethod method, NullProgressMonitor pm, WorkingCopyOwner owner) throws CoreException {
+		try{
+			if (! MethodChecks.isVirtual(method))
+				return new IMethod[]{ method };
+
+			return new RippleMethodFinder2(method, true, true).getAllRippleMethods(pm, owner);
 		} finally{
 			pm.done();
 		}
@@ -198,6 +212,7 @@
 			Assert.isTrue(false, "Search for method declaration did not find original element: " + fMethod.toString()); //$NON-NLS-1$
 
 		createHierarchyOfDeclarations(new SubProgressMonitor(pm, 1), owner);
+		addMissedSuperTypes();
 		createTypeToMethod();
 		createUnionFind();
 		checkCanceled(pm);
@@ -206,7 +221,7 @@
 		fRootTypes= null;
 
 		Map<IType, List<IType>> partitioning= new HashMap<>();
-		for (IType type : fTypeToMethod.keySet()) {
+		for (IType type : fTypeToMethod.fImplementation.keySet()) {
 			IType rep= fUnionFind.find(type);
 			List<IType> types= partitioning.get(rep);
 			if (types == null)
@@ -225,7 +240,7 @@
 		boolean hasRelatedInterfaces= false;
 		List<IMethod> relatedMethods= new ArrayList<>();
 		for (IType relatedType : relatedTypes) {
-			relatedMethods.add(fTypeToMethod.get(relatedType));
+			relatedMethods.addAll(fTypeToMethod.get(relatedType));
 			if (relatedType.isInterface())
 				hasRelatedInterfaces= true;
 		}
@@ -283,15 +298,17 @@
 			HashSet<IType> marriedAlienTypeReps= new HashSet<>();
 			for (IType alienType : alienTypes) {
 				checkCanceled(pm);
-				IMethod alienMethod= fTypeToMethod.get(alienType);
-				ITypeHierarchy hierarchy= hierarchy(pm, owner, alienType);
+				Collection<IMethod> alienMethods= fTypeToMethod.get(alienType);
+				for (IMethod alienMethod : alienMethods) {
+					ITypeHierarchy hierarchy= hierarchy(pm, owner, alienType);
 
-				for (IType subtype : hierarchy.getAllSubtypes(alienType)) {
-					if (relatedSubTypes.contains(subtype)) {
-						if (JavaModelUtil.isVisibleInHierarchy(alienMethod, subtype.getPackageFragment())) {
-							marriedAlienTypeReps.add(fUnionFind.find(alienType));
-						} else {
-							// not overridden
+					for (IType subtype : hierarchy.getAllSubtypes(alienType)) {
+						if (relatedSubTypes.contains(subtype)) {
+							if (JavaModelUtil.isVisibleInHierarchy(alienMethod, subtype.getPackageFragment())) {
+								marriedAlienTypeReps.add(fUnionFind.find(alienType));
+							} else {
+								// not overridden
+							}
 						}
 					}
 				}
@@ -303,7 +320,7 @@
 			for (IType marriedAlienTypeRep : marriedAlienTypeReps) {
 				List<IType> marriedAlienTypes= partitioning.get(marriedAlienTypeRep);
 				for (IType marriedAlienInterfaceType : marriedAlienTypes) {
-					relatedMethods.add(fTypeToMethod.get(marriedAlienInterfaceType));
+					relatedMethods.addAll(fTypeToMethod.get(marriedAlienInterfaceType));
 				}
 				alienTypes.removeAll(marriedAlienTypes); //not alien any more
 				relatedTypesToProcess.addAll(marriedAlienTypes); //process freshly married types again
@@ -385,6 +402,16 @@
 		return hierarchy;
 	}
 
+	private void addMissedSuperTypes() throws JavaModelException {
+		Set<IMethod> newDeclarations = new HashSet<>();
+		for (IMethod method : fDeclarations) {
+			MethodOverrideTester methodOverrideTester= new MethodOverrideTester(method.getDeclaringType(), fHierarchy);
+			newDeclarations.addAll(methodOverrideTester.findAllOverridenMethods(method));
+		}
+		fDeclarations.addAll(newDeclarations);
+
+	}
+
 	private ITypeHierarchy getCachedHierarchy(IType type, WorkingCopyOwner owner, IProgressMonitor monitor) throws JavaModelException {
 		IType rep= fUnionFind.find(type);
 		if (rep != null) {
@@ -402,7 +429,7 @@
 	}
 
 	private void findAllDeclarations(IProgressMonitor monitor, WorkingCopyOwner owner) throws CoreException {
-		fDeclarations= new ArrayList<>();
+		fDeclarations= new HashSet<>();
 
 		class MethodRequestor extends SearchRequestor {
 			@Override
@@ -427,7 +454,12 @@
 		int matchRule= SearchPattern.R_ERASURE_MATCH | SearchPattern.R_CASE_SENSITIVE;
 		SearchPattern pattern= SearchPattern.createPattern(fMethod, limitTo, matchRule);
 		SearchParticipant[] participants= SearchUtils.getDefaultSearchParticipants();
-		IJavaSearchScope scope= RefactoringScopeFactory.createRelatedProjectsScope(fMethod.getJavaProject(), IJavaSearchScope.SOURCES | IJavaSearchScope.APPLICATION_LIBRARIES | IJavaSearchScope.SYSTEM_LIBRARIES);
+		IJavaSearchScope scope;
+		if (fSearchOnlyInCompilationUnit) {
+			scope= RefactoringScopeFactory.create(fMethod.getCompilationUnit());
+		} else {
+			scope= RefactoringScopeFactory.createRelatedProjectsScope(fMethod.getJavaProject(), IJavaSearchScope.SOURCES | IJavaSearchScope.APPLICATION_LIBRARIES | IJavaSearchScope.SYSTEM_LIBRARIES);
+		}
 		MethodRequestor requestor= new MethodRequestor();
 		SearchEngine searchEngine= owner != null ? new SearchEngine(owner) : new SearchEngine();
 
@@ -449,19 +481,19 @@
 	}
 
 	private void createTypeToMethod() {
-		fTypeToMethod= new HashMap<>();
+		fTypeToMethod= new MultiMap<>();
 		for (IMethod declaration : fDeclarations) {
 			fTypeToMethod.put(declaration.getDeclaringType(), declaration);
 		}
 	}
 
 	private void createUnionFind() throws JavaModelException {
-		fRootTypes= new HashSet<>(fTypeToMethod.keySet());
+		fRootTypes= new HashSet<>(fTypeToMethod.fImplementation.keySet());
 		fUnionFind= new UnionFind();
-		for (IType type : fTypeToMethod.keySet()) {
+		for (IType type : fTypeToMethod.fImplementation.keySet()) {
 			fUnionFind.init(type);
 		}
-		for (IType type : fTypeToMethod.keySet()) {
+		for (IType type : fTypeToMethod.fImplementation.keySet()) {
 			uniteWithSupertypes(type, type);
 		}
 		fRootReps= new MultiMap<>();
@@ -481,15 +513,17 @@
 				uniteWithSupertypes(anchor, supertype);
 			} else {
 				//check whether method in supertype is really overridden:
-				IMember superMethod= fTypeToMethod.get(supertype);
-				if (JavaModelUtil.isVisibleInHierarchy(superMethod, anchor.getPackageFragment())) {
-					IType rep= fUnionFind.find(anchor);
-					fUnionFind.union(rep, superRep);
-					// current type is no root anymore
-					fRootTypes.remove(anchor);
-					uniteWithSupertypes(supertype, supertype);
-				} else {
-					//Not overridden -> overriding chain ends here.
+				Collection<IMethod> superMethods= fTypeToMethod.get(supertype);
+				for (IMethod superMethod : superMethods) {
+					if (JavaModelUtil.isVisibleInHierarchy(superMethod, anchor.getPackageFragment())) {
+						IType rep= fUnionFind.find(anchor);
+						fUnionFind.union(rep, superRep);
+						// current type is no root anymore
+						fRootTypes.remove(anchor);
+						uniteWithSupertypes(supertype, supertype);
+					} else {
+						//Not overridden -> overriding chain ends here.
+					}
 				}
 			}
 		}
diff --git a/org.eclipse.jdt.ui/ui refactoring/org/eclipse/jdt/internal/ui/refactoring/reorg/RenameLinkedMode.java b/org.eclipse.jdt.ui/ui refactoring/org/eclipse/jdt/internal/ui/refactoring/reorg/RenameLinkedMode.java
index 088c574..04fa907 100644
--- a/org.eclipse.jdt.ui/ui refactoring/org/eclipse/jdt/internal/ui/refactoring/reorg/RenameLinkedMode.java
+++ b/org.eclipse.jdt.ui/ui refactoring/org/eclipse/jdt/internal/ui/refactoring/reorg/RenameLinkedMode.java
@@ -10,12 +10,15 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Nikolay Metchev <nikolaymetchev@gmail.com> - [rename] https://bugs.eclipse.org/99622
  *******************************************************************************/
 package org.eclipse.jdt.internal.ui.refactoring.reorg;
 
 import java.lang.reflect.InvocationTargetException;
-import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.Comparator;
+import java.util.Set;
+import java.util.TreeSet;
 
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.StyledText;
@@ -37,6 +40,7 @@
 import org.eclipse.core.runtime.Assert;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
 import org.eclipse.core.runtime.Status;
 
 import org.eclipse.jface.dialogs.IDialogSettings;
@@ -71,6 +75,8 @@
 import org.eclipse.jdt.core.JavaModelException;
 import org.eclipse.jdt.core.dom.ASTNode;
 import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.IBinding;
+import org.eclipse.jdt.core.dom.IMethodBinding;
 import org.eclipse.jdt.core.dom.Name;
 import org.eclipse.jdt.core.dom.NodeFinder;
 import org.eclipse.jdt.core.dom.SimpleName;
@@ -80,6 +86,7 @@
 
 import org.eclipse.jdt.internal.corext.dom.LinkedNodeFinder;
 import org.eclipse.jdt.internal.corext.refactoring.rename.RenamingNameSuggestor;
+import org.eclipse.jdt.internal.corext.refactoring.rename.RippleMethodFinder2;
 import org.eclipse.jdt.internal.corext.util.JavaConventionsUtil;
 import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
 
@@ -215,12 +222,12 @@
 			ASTNode selectedNode= NodeFinder.perform(root, fOriginalSelection.x, fOriginalSelection.y);
 
 			final int pos;
-			ASTNode[] sameNodes;
+			Name nameNode= null;
 			if (! (selectedNode instanceof Name)) {
 				return; // TODO: show dialog
 			} else if (this.fJavaElement != null &&
 					this.fJavaElement.getElementType() ==  IJavaElement.JAVA_MODULE) {
-				Name nameNode = (Name) selectedNode;
+				nameNode = (Name) selectedNode;
 				ASTNode parent = nameNode.getParent();
 				while(parent instanceof Name) {
 					nameNode = (Name) parent;
@@ -228,14 +235,12 @@
 				}
 				fOriginalName= nameNode.getFullyQualifiedName();
 				pos= nameNode.getStartPosition();
-				sameNodes= LinkedNodeFinder.findByNode(root, nameNode);
 			} else if (! (selectedNode instanceof SimpleName)) {
 				return; // TODO: show dialog
 			} else {
-				SimpleName nameNode = (SimpleName)selectedNode;
-				fOriginalName = nameNode.getIdentifier();
+				nameNode = (SimpleName)selectedNode;
+				fOriginalName = ((SimpleName)nameNode).getIdentifier();
 				pos = nameNode.getStartPosition();
-				sameNodes= LinkedNodeFinder.findByNode(root, nameNode);
 			}
 
 			if (viewer instanceof ITextViewerExtension6) {
@@ -248,9 +253,7 @@
 				}
 			}
 
-			//TODO: copied from LinkedNamesAssistProposal#apply(..):
-			// sort for iteration order, starting with the node @ offset
-			Arrays.sort(sameNodes, new Comparator<ASTNode>() {
+			Set<ASTNode> sameNodes= new TreeSet<>(new Comparator<ASTNode>() {
 				@Override
 				public int compare(ASTNode o1, ASTNode o2) {
 					return rank(o1) - rank(o2);
@@ -270,12 +273,33 @@
 						return relativeRank;
 				}
 			});
-			for (int i= 0; i < sameNodes.length; i++) {
-				ASTNode elem= sameNodes[i];
+			sameNodes.add(nameNode);
+			IBinding resolvedNameNode= nameNode.resolveBinding();
+			if (resolvedNameNode != null && resolvedNameNode instanceof IMethodBinding) {
+				IJavaElement javaElement= resolvedNameNode.getJavaElement();
+				if (javaElement instanceof IMethod) {
+					IMethod[] relatedMethods= RippleMethodFinder2.getRelatedMethodsInCompilationUnit((IMethod) javaElement, new NullProgressMonitor(), null);
+					for (IMethod iMethod : relatedMethods) {
+						sameNodes.add(NodeFinder.perform(root, iMethod.getNameRange()));
+					}
+				}
+			}
+			for (ASTNode astNode : new ArrayList<>(sameNodes)) {
+				if (astNode instanceof SimpleName) {
+					for (SimpleName sameNode : LinkedNodeFinder.findByNode(root, (SimpleName) astNode)) {
+						sameNodes.add(sameNode);
+					}
+				}
+			}
+			//TODO: copied from LinkedNamesAssistProposal#apply(..):
+			// sort for iteration order, starting with the node @ offset
+			int i=0;
+			for (ASTNode elem : sameNodes) {
 				LinkedPosition linkedPosition= new LinkedPosition(document, elem.getStartPosition(), elem.getLength(), i);
 				if (i == 0)
 					fNamePosition= linkedPosition;
 				fLinkedPositionGroup.addPosition(linkedPosition);
+				i++;
 			}
 
 			fLinkedModeModel= new LinkedModeModel();
@@ -300,7 +324,7 @@
 //			startAnimation();
 			fgActiveLinkedMode= this;
 
-		} catch (BadLocationException e) {
+		} catch (BadLocationException | CoreException e) {
 			JavaPlugin.log(e);
 		}
 	}
diff --git a/org.eclipse.jdt.ui/ui refactoring/org/eclipse/jdt/internal/ui/refactoring/reorg/RenameMethodUserInterfaceStarter.java b/org.eclipse.jdt.ui/ui refactoring/org/eclipse/jdt/internal/ui/refactoring/reorg/RenameMethodUserInterfaceStarter.java
deleted file mode 100644
index c83c51b..0000000
--- a/org.eclipse.jdt.ui/ui refactoring/org/eclipse/jdt/internal/ui/refactoring/reorg/RenameMethodUserInterfaceStarter.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2000, 2011 IBM Corporation and others.
- *
- * This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License 2.0
- * which accompanies this distribution, and is available at
- * https://www.eclipse.org/legal/epl-2.0/
- *
- * SPDX-License-Identifier: EPL-2.0
- *
- * Contributors:
- *     IBM Corporation - initial API and implementation
- *******************************************************************************/
-package org.eclipse.jdt.internal.ui.refactoring.reorg;
-
-import org.eclipse.swt.widgets.Shell;
-
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.NullProgressMonitor;
-
-import org.eclipse.jface.dialogs.MessageDialog;
-
-import org.eclipse.ltk.core.refactoring.Refactoring;
-import org.eclipse.ltk.core.refactoring.RefactoringStatus;
-
-import org.eclipse.jdt.core.IMethod;
-
-import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
-import org.eclipse.jdt.internal.corext.refactoring.rename.RenameVirtualMethodProcessor;
-import org.eclipse.jdt.internal.corext.refactoring.util.JavaElementUtil;
-import org.eclipse.jdt.internal.corext.util.Messages;
-
-import org.eclipse.jdt.ui.JavaElementLabels;
-
-public class RenameMethodUserInterfaceStarter extends RenameUserInterfaceStarter {
-
-	@Override
-	public boolean activate(Refactoring refactoring, Shell parent, int saveMode) throws CoreException {
-		RenameVirtualMethodProcessor processor= refactoring.getAdapter(RenameVirtualMethodProcessor.class);
-		if (processor != null) {
-			RefactoringStatus status= processor.checkInitialConditions(new NullProgressMonitor());
-			if (!status.hasFatalError()) {
-				IMethod method= processor.getMethod();
-				if (!method.equals(processor.getOriginalMethod())) {
-					String message= null;
-					if (method.getDeclaringType().isInterface()) {
-						message= Messages.format(
-							RefactoringCoreMessages.MethodChecks_implements,
-							new String[]{
-								JavaElementUtil.createMethodSignature(method),
-								JavaElementLabels.getElementLabel(method.getDeclaringType(), JavaElementLabels.ALL_FULLY_QUALIFIED)});
-					} else {
-						message= Messages.format(
-							RefactoringCoreMessages.MethodChecks_overrides,
-							new String[]{
-								JavaElementUtil.createMethodSignature(method),
-								JavaElementLabels.getElementLabel(method.getDeclaringType(), JavaElementLabels.ALL_FULLY_QUALIFIED)});
-					}
-					message= Messages.format(
-						ReorgMessages.RenameMethodUserInterfaceStarter_message,
-						message);
-					if (!MessageDialog.openQuestion(parent,
-							ReorgMessages.RenameMethodUserInterfaceStarter_name,
-							message)) {
-						return false;
-					}
-				}
-			}
-		}
-		return super.activate(refactoring, parent, saveMode);
-	}
-}
diff --git a/org.eclipse.jdt.ui/ui refactoring/org/eclipse/jdt/internal/ui/refactoring/reorg/RenameUserInterfaceManager.java b/org.eclipse.jdt.ui/ui refactoring/org/eclipse/jdt/internal/ui/refactoring/reorg/RenameUserInterfaceManager.java
index 41dd42e..dd59a6f 100644
--- a/org.eclipse.jdt.ui/ui refactoring/org/eclipse/jdt/internal/ui/refactoring/reorg/RenameUserInterfaceManager.java
+++ b/org.eclipse.jdt.ui/ui refactoring/org/eclipse/jdt/internal/ui/refactoring/reorg/RenameUserInterfaceManager.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2021 IBM Corporation and others.
+ * Copyright (c) 2000, 2022 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -10,6 +10,7 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Nikolay Metchev <nikolaymetchev@gmail.com> - [rename] https://bugs.eclipse.org/99622
  *******************************************************************************/
 package org.eclipse.jdt.internal.ui.refactoring.reorg;
 
@@ -44,8 +45,8 @@
 		put(RenameFieldProcessor.class, RenameUserInterfaceStarter.class, RenameFieldWizard.class);
 		put(RenameEnumConstProcessor.class, RenameUserInterfaceStarter.class, RenameEnumConstWizard.class);
 		put(RenameTypeParameterProcessor.class, RenameUserInterfaceStarter.class, RenameTypeParameterWizard.class);
-		put(RenameNonVirtualMethodProcessor.class, RenameMethodUserInterfaceStarter.class, RenameMethodWizard.class);
-		put(RenameVirtualMethodProcessor.class, RenameMethodUserInterfaceStarter.class, RenameMethodWizard.class);
+		put(RenameNonVirtualMethodProcessor.class, RenameUserInterfaceStarter.class, RenameMethodWizard.class);
+		put(RenameVirtualMethodProcessor.class, RenameUserInterfaceStarter.class, RenameMethodWizard.class);
 		put(RenameLocalVariableProcessor.class, RenameUserInterfaceStarter.class, RenameLocalVariableWizard.class);
 		put(RenameModuleProcessor.class, RenameUserInterfaceStarter.class, RenameModuleWizard.class);
 	}