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:
parent
001e2c82f0
commit
6886023638
3 changed files with 185 additions and 6 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Add table
Reference in a new issue