From f0d7871fea71199c318bf85e505a5bfdc7b93140 Mon Sep 17 00:00:00 2001 From: Sergey Prigogin Date: Mon, 20 Sep 2010 03:09:50 +0000 Subject: [PATCH] Support for overridden virtual methods in LinkedNamesFinder. --- .../dom/parser/cpp/CPPASTTranslationUnit.java | 1 + .../core/dom/parser/cpp/ClassTypeHelper.java | 40 +++++++++-- .../parser/cpp/semantics/SemanticUtil.java | 9 +-- .../tests/search/LinkedNamesFinderTest.java | 27 +++++++ .../internal/ui/search/LinkedNamesFinder.java | 71 ++++++++++++++++++- 5 files changed, 138 insertions(+), 10 deletions(-) diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTTranslationUnit.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTTranslationUnit.java index 94ee1d1f2b0..57dd28b9016 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTTranslationUnit.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTTranslationUnit.java @@ -156,6 +156,7 @@ public class CPPASTTranslationUnit extends ASTTranslationUnit implements ICPPAST public IScope mapToASTScope(IIndexScope scope) { return fScopeMapper.mapToASTScope(scope); } + // bug 262719: class types from the index have to be mapped back to the AST. public ICPPClassType mapToAST(ICPPClassType binding) { return fScopeMapper.mapToAST(binding); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/ClassTypeHelper.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/ClassTypeHelper.java index 7e91d3e0253..2333153491a 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/ClassTypeHelper.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/ClassTypeHelper.java @@ -234,7 +234,7 @@ public class ClassTypeHelper { } /** - * Returns all direct and indirect base classes that have at least a given visibility level. + * Returns all direct and indirect base classes. * @param classType a class * @return An array of visible base classes in arbitrary order. */ @@ -258,7 +258,28 @@ public class ClassTypeHelper { } } } - + + /** + * Checks inheritance relationship between two classes. + * @return true if {@code subclass} is a subclass of {@code superclass}. + */ + public static boolean isSubclass(ICPPClassType subclass, ICPPClassType superclass) { + ICPPBase[] bases= subclass.getBases(); + for (ICPPBase base : bases) { + IBinding b= base.getBaseClass(); + if (b instanceof ICPPClassType) { + ICPPClassType baseClass = (ICPPClassType) b; + if (baseClass.isSameType(superclass)) { + return true; + } + if (isSubclass(baseClass, superclass)) { + return true; + } + } + } + return false; + } + public static ICPPMethod[] getAllDeclaredMethods(ICPPClassType ct) { ICPPMethod[] methods= ct.getDeclaredMethods(); ICPPClassType[] bases= getAllBases(ct); @@ -572,17 +593,26 @@ public class ClassTypeHelper { * @throws CoreException */ public static ICPPMethod[] findOverriders(IIndex index, ICPPMethod method) throws DOMException, CoreException { - if (!isVirtual(method)) + if (!isVirtual(method)) return ICPPMethod.EMPTY_CPPMETHOD_ARRAY; final ICPPClassType mcl= method.getClassOwner(); if (mcl == null) return ICPPMethod.EMPTY_CPPMETHOD_ARRAY; - final ArrayList result= new ArrayList(); + ICPPClassType[] subclasses= getSubClasses(index, mcl); + return findOverriders(subclasses, method); + } + + /** + * Returns all methods belonging to the given set of classes that override the given {@code method}. + * @throws DOMException + */ + public static ICPPMethod[] findOverriders(ICPPClassType[] subclasses, ICPPMethod method) + throws DOMException { final char[] mname= method.getNameCharArray(); final ICPPFunctionType mft= method.getType(); - ICPPClassType[] subclasses= getSubClasses(index, mcl); + final ArrayList result= new ArrayList(); for (ICPPClassType subClass : subclasses) { ICPPMethod[] methods= subClass.getDeclaredMethods(); for (ICPPMethod candidate : methods) { diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/SemanticUtil.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/SemanticUtil.java index ccb899a822b..ec49fbce932 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/SemanticUtil.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/SemanticUtil.java @@ -6,10 +6,11 @@ * http://www.eclipse.org/legal/epl-v10.html * * Contributors: - * IBM - Initial API and implementation - * Markus Schorn (Wind River Systems) - * Bryan Wilkinson (QNX) - * Andrew Ferguson (Symbian) + * IBM - Initial API and implementation + * Markus Schorn (Wind River Systems) + * Bryan Wilkinson (QNX) + * Andrew Ferguson (Symbian) + * Sergey Prigogin (Google) *******************************************************************************/ package org.eclipse.cdt.internal.core.dom.parser.cpp.semantics; diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/search/LinkedNamesFinderTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/search/LinkedNamesFinderTest.java index 2970cd2a847..68e20027ad9 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/search/LinkedNamesFinderTest.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/search/LinkedNamesFinderTest.java @@ -124,6 +124,33 @@ public class LinkedNamesFinderTest extends AST2BaseTest { assertTrue(Arrays.equals(regions3, regions)); } + // class A { + // public: + // virtual void m(int a); + // virtual void m(); + // }; + // + // class B : public A { + // public: + // void m(); + // }; + // + // class C : public B { + // public: + // void m(int c); + // }; + public void testVirtualMethod() throws Exception { + String code = getAboveComment(); + IRegion[] regions = getLinkedRegions(code, "m(int c)", 1, true); + assertEquals(2, regions.length); + assertContents(code, regions[0].getOffset(), "m(int a)"); + assertContents(code, regions[1].getOffset(), "m(int c)"); + regions = getLinkedRegions(code, "m(int a)", 1, true); + assertEquals(2, regions.length); + assertContents(code, regions[0].getOffset(), "m(int a)"); + assertContents(code, regions[1].getOffset(), "m(int c)"); + } + // #ifndef GUARD //1 // #define GUARD //2 // // This is a GUARD test diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/search/LinkedNamesFinder.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/search/LinkedNamesFinder.java index 70a7e9248a7..cd4ddfcf4f4 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/search/LinkedNamesFinder.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/search/LinkedNamesFinder.java @@ -12,11 +12,15 @@ package org.eclipse.cdt.internal.ui.search; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.List; +import java.util.Set; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.Region; +import org.eclipse.cdt.core.dom.ast.ASTVisitor; +import org.eclipse.cdt.core.dom.ast.DOMException; import org.eclipse.cdt.core.dom.ast.IASTComment; import org.eclipse.cdt.core.dom.ast.IASTFileLocation; import org.eclipse.cdt.core.dom.ast.IASTName; @@ -37,9 +41,10 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor; import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod; import org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit; +import org.eclipse.cdt.internal.core.dom.parser.cpp.ClassTypeHelper; /** - * Finds locations of linked names. Used by Rename in File. + * Finds locations of linked names. Used by Rename in File and Rename in Workspace. */ public class LinkedNamesFinder { private static final IRegion[] EMPTY_LOCATIONS_ARRAY = new IRegion[0]; @@ -92,9 +97,38 @@ public class LinkedNamesFinder { findBinding(method); } } + } else if (target instanceof ICPPMethod) { + ICPPMethod method= (ICPPMethod) target; + try { + for (ICPPMethod m : ClassTypeHelper.findOverridden(method)) { + findBinding(m); + } + } catch (DOMException e) { + // Ignore. + } + try { + for (ICPPMethod m : findOverridersInAST(method)) { + findBinding(m); + } + } catch (DOMException e) { + // Ignore. + } } } + private ICPPMethod[] findOverridersInAST(ICPPMethod method) throws DOMException { + if (!ClassTypeHelper.isVirtual(method)) + return ICPPMethod.EMPTY_CPPMETHOD_ARRAY; + + final ICPPClassType ownerClass = method.getClassOwner(); + if (ownerClass == null) + return ICPPMethod.EMPTY_CPPMETHOD_ARRAY; + + SubclassFinder subclassFinder = new SubclassFinder(ownerClass); + root.accept(subclassFinder); + return ClassTypeHelper.findOverriders(subclassFinder.getSubclasses(), method); + } + public IRegion[] getLocations() { if (locations.isEmpty()) { return EMPTY_LOCATIONS_ARRAY; @@ -261,4 +295,39 @@ public class LinkedNamesFinder { } } } + + /** + * Finds subclasses of the given class referenced by the AST. + */ + static class SubclassFinder extends ASTVisitor { + { + shouldVisitNames= true; + } + + private final ICPPClassType baseClass; + private Set subclasses = new HashSet(); + private Set seenClasses = new HashSet(); + + SubclassFinder(ICPPClassType baseClass) { + this.baseClass = baseClass; + } + + @Override + public int visit(IASTName name) { + IBinding binding = name.resolveBinding(); + if (binding instanceof ICPPClassType) { + if (seenClasses.add(binding)) { + ICPPClassType candidate = (ICPPClassType) binding; + if (ClassTypeHelper.isSubclass(candidate, baseClass)) { + subclasses.add(candidate); + } + } + } + return PROCESS_CONTINUE; + } + + public ICPPClassType[] getSubclasses() { + return subclasses.toArray(new ICPPClassType[subclasses.size()]); + } + } }