1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-23 22:52:11 +02:00

Bug 421823 - Heuristically resolve unknown bindings during 'Open

Declaration'

Change-Id: Id48769cae0ded784228780bbf60a30e7e990b073
Signed-off-by: Nathan Ridge <zeratul976@hotmail.com>
This commit is contained in:
Nathan Ridge 2015-07-13 00:53:58 -04:00 committed by Sergey Prigogin
parent 001e2c82f0
commit 6886023638
3 changed files with 185 additions and 6 deletions

View file

@ -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;
}
}

View file

@ -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<A>") + 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 X>
// class Base {
// Other *other;
// };
//
// template<class X>
// class Child : public Base<X> {
// void bar() {
// this->other->foo(); // can't find other and foo
// Base<X>::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);
}
}

View file

@ -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) {