diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/callhierarchy/CallHierarchyBugs.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/callhierarchy/CallHierarchyBugs.java index b099d2b22a6..08cc9003dad 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/callhierarchy/CallHierarchyBugs.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/callhierarchy/CallHierarchyBugs.java @@ -16,6 +16,7 @@ import junit.framework.Test; import org.eclipse.core.resources.IFile; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; +import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IPageLayout; import org.eclipse.ui.IViewPart; import org.eclipse.ui.IWorkbenchPage; @@ -29,6 +30,7 @@ import org.eclipse.cdt.ui.CUIPlugin; import org.eclipse.cdt.internal.ui.callhierarchy.CHViewPart; import org.eclipse.cdt.internal.ui.callhierarchy.CallHierarchyUI; +import org.eclipse.cdt.internal.ui.editor.CEditor; public class CallHierarchyBugs extends CallHierarchyBaseTest { @@ -143,9 +145,108 @@ public class CallHierarchyBugs extends CallHierarchyBaseTest { CallHierarchyUI.open(workbenchWindow, (ICElement) obj); } - private void openEditor(IFile file) throws WorkbenchException { + private CEditor openEditor(IFile file) throws WorkbenchException { IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); - IDE.openEditor(page, file, true); + IEditorPart editor= IDE.openEditor(page, file, true); runEventQueue(0); + return (CEditor) editor; + } + + // class Base { + // public: + // virtual void vmethod(); + // void method(); + // }; + // class Derived : public Base { + // public: + // void vmethod(); + // void method(); + // } + // void vrefs() { + // Base* b= 0; + // b->vmethod(); b->method(); + // } + // void regRefs() { + // Base* b= 0; + // b->Base::vmethod(); b->Base::method(); + // } + public void testPolyMorphicMethodCalls_156689() throws Exception { + String content= getContentsForTest(1)[0].toString(); + IFile file= createFile(getProject(), "SomeClass.cpp", content); + waitForIndexer(fIndex, file, CallHierarchyBaseTest.INDEXER_WAIT_TIME); + + final CHViewPart ch= (CHViewPart) activateView(CUIPlugin.ID_CALL_HIERARCHY); + final IWorkbenchWindow workbenchWindow = ch.getSite().getWorkbenchWindow(); + + // open editor, check outline + CEditor editor= openEditor(file); + int idx = content.indexOf("vmethod"); + editor.selectAndReveal(idx, 0); + openCallHierarchy(editor); + + Tree chTree= checkTreeNode(ch, 0, "Base::vmethod()").getParent(); + checkTreeNode(chTree, 0, 0, "regRefs()"); + checkTreeNode(chTree, 0, 1, "vrefs()"); + checkTreeNode(chTree, 0, 2, null); + + idx = content.indexOf("vmethod", idx+1); + editor.selectAndReveal(idx, 0); + openCallHierarchy(editor); + + chTree= checkTreeNode(ch, 0, "Derived::vmethod()").getParent(); + checkTreeNode(chTree, 0, 0, "vrefs()"); + checkTreeNode(chTree, 0, 1, null); + + idx = content.indexOf(" method")+1; + editor.selectAndReveal(idx, 0); + openCallHierarchy(editor); + + chTree= checkTreeNode(ch, 0, "Base::method()").getParent(); + checkTreeNode(chTree, 0, 0, "regRefs()"); + checkTreeNode(chTree, 0, 1, "vrefs()"); + checkTreeNode(chTree, 0, 2, null); + + idx = content.indexOf(" method", idx+1)+1; + editor.selectAndReveal(idx, 0); + openCallHierarchy(editor); + + chTree= checkTreeNode(ch, 0, "Derived::method()").getParent(); + checkTreeNode(chTree, 0, 0, null); + } + + // class Base { + // public: + // virtual void vmethod(); + // }; + // class Derived : public Base { + // public: + // void vmethod(); + // } + // void vrefs() { + // Base* b= 0; + // b->vmethod(); + // } + public void testReversePolyMorphicMethodCalls_156689() throws Exception { + String content= getContentsForTest(1)[0].toString(); + IFile file= createFile(getProject(), "SomeClass.cpp", content); + waitForIndexer(fIndex, file, CallHierarchyBaseTest.INDEXER_WAIT_TIME); + + final CHViewPart ch= (CHViewPart) activateView(CUIPlugin.ID_CALL_HIERARCHY); + final IWorkbenchWindow workbenchWindow = ch.getSite().getWorkbenchWindow(); + + // open editor, check outline + CEditor editor= openEditor(file); + int idx = content.indexOf("vrefs"); + editor.selectAndReveal(idx, 0); + openCallHierarchy(editor, false); + + Tree chTree= checkTreeNode(ch, 0, "vrefs()").getParent(); + TreeItem item= checkTreeNode(chTree, 0, 0, "Base::vmethod()"); + checkTreeNode(chTree, 0, 1, null); + + expandTreeItem(item); + checkTreeNode(item, 0, "Base::vmethod()"); + checkTreeNode(item, 1, "Derived::vmethod()"); + checkTreeNode(item, 2, null); } } diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/callhierarchy/CppCallHierarchyTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/callhierarchy/CppCallHierarchyTest.java index e68d8091121..fb0662ef1b0 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/callhierarchy/CppCallHierarchyTest.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/callhierarchy/CppCallHierarchyTest.java @@ -65,9 +65,9 @@ public class CppCallHierarchyTest extends CallHierarchyBaseTest { IFile headerFile= createFile(getProject(), "testMethods.h", header); IFile sourceFile= createFile(getProject(), "testMethods.cpp", source); IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); - CEditor editor= (CEditor) IDE.openEditor(page, sourceFile); waitForIndexer(fIndex, sourceFile, CallHierarchyBaseTest.INDEXER_WAIT_TIME); + CEditor editor= (CEditor) IDE.openEditor(page, sourceFile); editor.selectAndReveal(source.indexOf("method"), 2); openCallHierarchy(editor); Tree tree = getCHTreeViewer().getTree(); @@ -188,10 +188,9 @@ public class CppCallHierarchyTest extends CallHierarchyBaseTest { IFile sourceFile1= createFile(getProject(), "testMethods1.cpp", source1); IFile sourceFile2= createFile(getProject(), "testMethods2.cpp", source2); - CEditor editor= openFile(sourceFile1); - CCorePlugin.getIndexManager().reindex(fCProject); waitForIndexer(fIndex, sourceFile2, CallHierarchyBaseTest.INDEXER_WAIT_TIME); + CEditor editor= openFile(sourceFile1); editor.selectAndReveal(source1.indexOf("method3"), 2); openCallHierarchy(editor); TreeViewer tv = getCHTreeViewer(); @@ -380,5 +379,4 @@ public class CppCallHierarchyTest extends CallHierarchyBaseTest { checkTreeNode(node, 0, "cfunc()"); checkTreeNode(node, 1, null); } - } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/callhierarchy/CElementSet.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/callhierarchy/CElementSet.java index da6c4d262cd..3cae39f690c 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/callhierarchy/CElementSet.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/callhierarchy/CElementSet.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006 Wind River Systems, Inc. and others. + * Copyright (c) 2006, 2007 Wind River Systems, Inc. and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -13,8 +13,8 @@ package org.eclipse.cdt.internal.ui.callhierarchy; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.Set; import org.eclipse.cdt.core.model.ICElement; @@ -22,7 +22,7 @@ import org.eclipse.cdt.core.model.ICElement; import org.eclipse.cdt.internal.ui.viewsupport.WorkingSetFilterUI; public class CElementSet { - private Set fSet= new HashSet(); + private Set fSet= new LinkedHashSet(); private int fHashCode; CElementSet( ICElement[] elements) { diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/callhierarchy/CHQueries.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/callhierarchy/CHQueries.java index 03f33108935..0b7e18ddfcf 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/callhierarchy/CHQueries.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/callhierarchy/CHQueries.java @@ -11,15 +11,31 @@ package org.eclipse.cdt.internal.ui.callhierarchy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; + import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.cdt.core.dom.ast.DOMException; import org.eclipse.cdt.core.dom.ast.IBinding; +import org.eclipse.cdt.core.dom.ast.IFunctionType; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPBinding; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod; import org.eclipse.cdt.core.index.IIndex; +import org.eclipse.cdt.core.index.IIndexBinding; import org.eclipse.cdt.core.index.IIndexName; import org.eclipse.cdt.core.model.ICElement; import org.eclipse.cdt.core.model.ICProject; import org.eclipse.cdt.core.model.ISourceReference; +import org.eclipse.cdt.core.parser.util.CharArrayUtils; + +import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPVisitor; import org.eclipse.cdt.internal.ui.viewsupport.IndexUI; @@ -43,18 +59,143 @@ public class CHQueries { if (! (callee instanceof ISourceReference)) { return EMPTY_NODES; } - IBinding calleeBinding= IndexUI.elementToBinding(index, callee); - findCalledBy(index, calleeBinding, callee.getCProject(), result); - + final ICProject project = callee.getCProject(); + IIndexBinding calleeBinding= IndexUI.elementToBinding(index, callee); + if (calleeBinding != null) { + findCalledBy(index, calleeBinding, true, project, result); + IBinding[] overriddenBindings= getOverriddenBindings(index, calleeBinding); + for (int i = 0; i < overriddenBindings.length; i++) { + findCalledBy(index, overriddenBindings[i], false, project, result); + } + } return cp.createNodes(node, result); } - private static void findCalledBy(IIndex index, IBinding callee, ICProject project, CalledByResult result) + private static IBinding[] getOverriddenBindings(IIndex index, IIndexBinding binding) { + if (binding instanceof ICPPMethod && !(binding instanceof ICPPConstructor)) { + try { + final ArrayList result= new ArrayList(); + final ICPPMethod m= (ICPPMethod) binding; + final char[] mname= m.getNameCharArray(); + final ICPPClassType mcl= m.getClassOwner(); + final IFunctionType mft= m.getType(); + boolean isVirtual= m.isVirtual(); + ICPPMethod[] allMethods= mcl.getMethods(); + for (int i = 0; i < allMethods.length; i++) { + ICPPMethod method = allMethods[i]; + if (CharArrayUtils.equals(mname, method.getNameCharArray()) && !mcl.isSameType(method.getClassOwner())) { + if (mft.isSameType(method.getType())) { + isVirtual= isVirtual || method.isVirtual(); + result.add(method); + } + } + } + if (isVirtual) { + return (IBinding[]) result.toArray(new IBinding[result.size()]); + } + } catch (DOMException e) { + // index bindings don't throw DOMExceptions + } + } + return IBinding.EMPTY_BINDING_ARRAY; + } + + private static IBinding[] getOverridingBindings(IIndex index, IBinding binding) throws CoreException { + if (binding instanceof ICPPMethod && !(binding instanceof ICPPConstructor)) { + try { + final ICPPMethod m= (ICPPMethod) binding; + if (isVirtual(m)) { + final ArrayList result= new ArrayList(); + final char[] mname= m.getNameCharArray(); + final ICPPClassType mcl= m.getClassOwner(); + final IFunctionType mft= m.getType(); + ICPPClassType[] subclasses= getSubClasses(index, mcl); + for (int i = 0; i < subclasses.length; i++) { + ICPPClassType subClass = subclasses[i]; + ICPPMethod[] methods= subClass.getDeclaredMethods(); + for (int j = 0; j < methods.length; j++) { + ICPPMethod method = methods[j]; + if (CharArrayUtils.equals(mname, method.getNameCharArray()) && + mft.isSameType(method.getType())) { + result.add(method); + } + } + } + return (IBinding[]) result.toArray(new IBinding[result.size()]); + } + } catch (DOMException e) { + // index bindings don't throw DOMExceptions + } + } + return IBinding.EMPTY_BINDING_ARRAY; + } + + private static ICPPClassType[] getSubClasses(IIndex index, ICPPClassType mcl) throws CoreException { + List result= new LinkedList(); + HashSet handled= new HashSet(); + getSubClasses(index, mcl, result, handled); + result.remove(0); + return (ICPPClassType[]) result.toArray(new ICPPClassType[result.size()]); + } + + private static void getSubClasses(IIndex index, ICPPBinding classOrTypedef, List result, HashSet handled) throws CoreException { + try { + final String key = CPPVisitor.renderQualifiedName(classOrTypedef.getQualifiedName()); + if (!handled.add(key)) { + return; + } + } catch (DOMException e) { + return; + } + + if (classOrTypedef instanceof ICPPClassType) { + result.add(classOrTypedef); + } + + IIndexName[] names= index.findNames(classOrTypedef, IIndex.FIND_REFERENCES | IIndex.FIND_DEFINITIONS); + for (int i = 0; i < names.length; i++) { + IIndexName indexName = names[i]; + if (indexName.isBaseSpecifier()) { + IIndexName subClassDef= indexName.getEnclosingDefinition(); + if (subClassDef != null) { + IBinding subClass= index.findBinding(subClassDef); + if (subClass instanceof ICPPBinding) { + getSubClasses(index, (ICPPBinding) subClass, result, handled); + } + } + } + } + } + + private static boolean isVirtual(ICPPMethod m) { + try { + if (m.isVirtual()) { + return true; + } + final char[] mname= m.getNameCharArray(); + final ICPPClassType mcl= m.getClassOwner(); + final IFunctionType mft= m.getType(); + ICPPMethod[] allMethods= mcl.getMethods(); + for (int i = 0; i < allMethods.length; i++) { + ICPPMethod method = allMethods[i]; + if (CharArrayUtils.equals(mname, method.getNameCharArray()) && mft.isSameType(method.getType())) { + if (method.isVirtual()) { + return true; + } + } + } + } catch (DOMException e) { + // index bindings don't throw DOMExceptions + } + return false; + } + + private static void findCalledBy(IIndex index, IBinding callee, boolean includeOrdinaryCalls, ICProject project, CalledByResult result) throws CoreException { - if (callee != null) { - IIndexName[] names= index.findNames(callee, IIndex.FIND_REFERENCES | IIndex.SEARCH_ACCROSS_LANGUAGE_BOUNDARIES); - for (int i = 0; i < names.length; i++) { - IIndexName rname = names[i]; + IIndexName[] names= index.findNames(callee, IIndex.FIND_REFERENCES | IIndex.SEARCH_ACCROSS_LANGUAGE_BOUNDARIES); + for (int i = 0; i < names.length; i++) { + IIndexName rname = names[i]; + if (includeOrdinaryCalls || rname.couldBePolymorphicMethodCall()) { IIndexName caller= rname.getEnclosingDefinition(); if (caller != null) { ICElement elem= IndexUI.getCElementForName(project, index, caller); @@ -80,7 +221,20 @@ public class CHQueries { IIndexName name = refs[i]; IBinding binding= index.findBinding(name); if (CallHierarchyUI.isRelevantForCallHierarchy(binding)) { - ICElement[] defs = IndexUI.findRepresentative(index, binding); + IBinding[] virtualOverriders= getOverridingBindings(index, binding); + ICElement[] defs; + if (virtualOverriders.length == 0) { + defs = IndexUI.findRepresentative(index, binding); + } + else { + ArrayList list= new ArrayList(); + list.addAll(Arrays.asList(IndexUI.findRepresentative(index, binding))); + for (int j = 0; j < virtualOverriders.length; j++) { + IBinding overrider = virtualOverriders[j]; + list.addAll(Arrays.asList(IndexUI.findRepresentative(index, overrider))); + } + defs= (ICElement[]) list.toArray(new ICElement[list.size()]); + } if (defs != null && defs.length > 0) { result.add(defs, name); }