diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/HeuristicResolver.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/HeuristicResolver.java index 6a0a3c013f6..e2f4d2ab14e 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/HeuristicResolver.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/HeuristicResolver.java @@ -12,13 +12,23 @@ package org.eclipse.cdt.internal.core.dom.parser.cpp.semantics; import org.eclipse.cdt.core.dom.ast.DOMException; import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.IASTUnaryExpression; import org.eclipse.cdt.core.dom.ast.IBinding; import org.eclipse.cdt.core.dom.ast.ICompositeType; +import org.eclipse.cdt.core.dom.ast.IEnumerator; import org.eclipse.cdt.core.dom.ast.IField; +import org.eclipse.cdt.core.dom.ast.IFunction; +import org.eclipse.cdt.core.dom.ast.IPointerType; import org.eclipse.cdt.core.dom.ast.IScope; import org.eclipse.cdt.core.dom.ast.IType; +import org.eclipse.cdt.core.dom.ast.IVariable; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateArgument; import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPDeferredClassInstance; import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPEvaluation; +import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPUnknownBinding; +import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPUnknownMember; +import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPUnknownType; public class HeuristicResolver { /** @@ -66,4 +76,128 @@ public class HeuristicResolver { // TODO(nathanridge): Handle more cases. return null; } + + /** + * Helper function for resolveUnknownType() and resolveUnknownBinding(). + * Heuristically resolves the given unknown type and performs name lookup inside it. + * + * @param ownerType the type to perform name lookup inside + * @param isPointerDeref true if 'ownerType' is a pointer type + * @param name the name to be looked up + * @param templateArgs template arguments following the name, if any + * @param point point of instantiation for name lookups + * @return results of the name lookup + */ + private static IBinding[] lookInside(IType ownerType, boolean isPointerDeref, char[] name, + ICPPTemplateArgument[] templateArgs, IASTNode point) { + // The pointer type might be outside of the dependent type... + ownerType = SemanticUtil.getSimplifiedType(ownerType); + if (isPointerDeref && ownerType instanceof IPointerType) { + ownerType = ((IPointerType) ownerType).getType(); + isPointerDeref = false; + } + if (ownerType instanceof ICPPUnknownType) { + IType lookupType = resolveUnknownType((ICPPUnknownType) ownerType, point); + // ... or inside the dependent type. + if (isPointerDeref) { + lookupType = SemanticUtil.getSimplifiedType(lookupType); + if (lookupType instanceof IPointerType) { + lookupType = ((IPointerType) lookupType).getType(); + } else { + lookupType = null; + } + } + if (lookupType instanceof ICPPClassType) { + LookupData lookup = new LookupData(name, templateArgs, point); + lookup.fHeuristicBaseLookup = true; + try { + CPPSemantics.lookup(lookup, ((ICPPClassType) lookupType).getCompositeScope()); + IBinding[] foundBindings = lookup.getFoundBindings(); + if (foundBindings.length > 0) { + return foundBindings; + } + } catch (DOMException e) { + } + } + } + return IBinding.EMPTY_BINDING_ARRAY; + } + + /** + * Helper function for resolveUnknownType(). + * Returns the type of a binding, or if the binding is a type, that type. + */ + private static IType typeForBinding(IBinding binding) { + if (binding instanceof IType) { + return (IType) binding; + } else if (binding instanceof IVariable) { + return ((IVariable) binding).getType(); + } else if (binding instanceof IEnumerator) { + return ((IEnumerator) binding).getType(); + } else if (binding instanceof IFunction) { + return ((IFunction) binding).getType(); + } + return null; + } + + /** + * Given an unknown type, heuristically tries to find a concrete type (i.e. not an unknown type) + * corresponding to it. + * + * Returns null if no heuristic resolution could be performed. + + * @param point the point of instantiation for lookups + */ + private static IType resolveUnknownType(ICPPUnknownType type, IASTNode point) { + if (type instanceof ICPPDeferredClassInstance) { + return ((ICPPDeferredClassInstance) type).getClassTemplate(); + } else if (type instanceof TypeOfDependentExpression) { + ICPPEvaluation evaluation = ((TypeOfDependentExpression) type).getEvaluation(); + if (evaluation instanceof EvalUnary) { + EvalUnary unary = (EvalUnary) evaluation; + // Handle the common case of a dependent type representing the result of + // dereferencing another dependent type. + if (unary.getOperator() == IASTUnaryExpression.op_star) { + IType argument = unary.getArgument().getTypeOrFunctionSet(point); + if (argument instanceof ICPPUnknownType) { + IType resolved = resolveUnknownType((ICPPUnknownType) argument, point); + resolved = SemanticUtil.getSimplifiedType(resolved); + if (resolved instanceof IPointerType) { + return ((IPointerType) resolved).getType(); + } + } + } + } else if (evaluation instanceof EvalID) { + EvalID id = (EvalID) evaluation; + ICPPEvaluation fieldOwner = id.getFieldOwner(); + if (fieldOwner != null) { + IBinding[] candidates = lookInside(fieldOwner.getTypeOrFunctionSet(point), + id.isPointerDeref(), id.getName(), id.getTemplateArgs(), point); + if (candidates.length == 1) { + return typeForBinding(candidates[0]); + } + } + } + // TODO(nathanridge): Handle more cases. + } + return null; + } + + /** + * Given an unknown binding, heuristically tries to find concrete bindings (i.e. not unknown bindings) + * corresponding to it. + * + * Returns an empty array if no heuristic resolution could be performed. + + * @param point the point of instantiation for lookups + */ + public static IBinding[] resolveUnknownBinding(ICPPUnknownBinding binding, IASTNode point) { + if (binding instanceof ICPPDeferredClassInstance) { + return new IBinding[] { ((ICPPDeferredClassInstance) binding).getClassTemplate() }; + } else if (binding instanceof ICPPUnknownMember) { + return lookInside(((ICPPUnknownMember) binding).getOwnerType(), false, + binding.getNameCharArray(), null, point); + } + return IBinding.EMPTY_BINDING_ARRAY; + } } diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/selection/CPPSelectionTestsNoIndexer.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/selection/CPPSelectionTestsNoIndexer.java index ee7d52a896c..c01f748e1a6 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/selection/CPPSelectionTestsNoIndexer.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/selection/CPPSelectionTestsNoIndexer.java @@ -18,9 +18,6 @@ import java.io.InputStream; import java.io.StringWriter; import java.io.Writer; -import junit.framework.Test; -import junit.framework.TestSuite; - import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; @@ -48,6 +45,9 @@ import org.eclipse.cdt.internal.core.dom.parser.ASTNode; import org.eclipse.cdt.internal.ui.search.actions.OpenDeclarationsAction; +import junit.framework.Test; +import junit.framework.TestSuite; + /** * It is required to test the selection performance independent of the indexer to make sure that the DOM * is functioning properly. @@ -1210,7 +1210,7 @@ public class CPPSelectionTestsNoIndexer extends BaseSelectionTests { public void testAmbiguityWithImplicitName_463234() throws Exception { String code = getAboveComment(); IFile file = importFile("testBug463234.cpp", code); - + int offset = code.indexOf("new B") + 6; // There should be two ambiguous targets, the class A and the constructor B::B, // with the class A being the first one (index 0). @@ -1218,4 +1218,34 @@ public class CPPSelectionTestsNoIndexer extends BaseSelectionTests { assertTrue(target instanceof IASTName); assertEquals("A", ((IASTName) target).toString()); } + + // class Other { + // int foo(); + // }; + // + // template + // class Base { + // Other *other; + // }; + // + // template + // class Child : public Base { + // void bar() { + // this->other->foo(); // can't find other and foo + // Base::other->foo(); // can find other can't find foo + // } + // }; + public void testMemberOfDependentBase_421823() throws Exception { + String code = getAboveComment(); + IFile file = importFile("testBug421823.cpp", code); + + int offset = code.indexOf("this->other") + 6; + assertTrue(testF3(file, offset) instanceof IASTName); + + offset += 7; // 'foo' in 'this->other->foo' + assertTrue(testF3(file, offset) instanceof IASTName); + + offset = code.indexOf("::other->foo") + 9; + assertTrue(testF3(file, offset) instanceof IASTName); + } } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/search/actions/OpenDeclarationsJob.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/search/actions/OpenDeclarationsJob.java index 1b0f2320a44..53353a6bd4f 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/search/actions/OpenDeclarationsJob.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/search/actions/OpenDeclarationsJob.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009, 2014 Wind River Systems, Inc. and others. + * Copyright (c) 2009, 2015 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 @@ -8,6 +8,7 @@ * Contributors: * Markus Schorn - initial API and implementation * Sergey Prigogin (Google) + * Nathan Ridge ******************************************************************************/ package org.eclipse.cdt.internal.ui.search.actions; @@ -91,6 +92,7 @@ import org.eclipse.cdt.internal.core.dom.parser.ASTNode; import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPUnknownBinding; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor; +import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.HeuristicResolver; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.LookupData; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil; import org.eclipse.cdt.internal.core.index.IIndexFragmentName; @@ -199,7 +201,20 @@ class OpenDeclarationsJob extends Job implements ASTRunnable { } IName[] targets = IName.EMPTY_ARRAY; String filename = ast.getFilePath(); - for (IBinding binding : bindings) { + for (int i = 0; i < bindings.length; ++i) { + IBinding binding = bindings[i]; + if (binding instanceof ICPPUnknownBinding) { + // We're not going to find declarations for an unknown binding. + // To try to do something useful anyways, we try to heuristically + // resolve the unknown binding to one or more concrete bindings, + // and use those instead. + IBinding[] resolved = HeuristicResolver.resolveUnknownBinding( + (ICPPUnknownBinding) binding, sourceName); + if (resolved.length > 0) { + bindings = ArrayUtil.addAll(bindings, resolved); + continue; + } + } if (binding != null && !(binding instanceof IProblemBinding)) { IName[] names = findDeclNames(ast, kind, binding); for (final IName name : names) {