mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-08-12 10:45:37 +02:00
Bug 422765: New method to find IBindings from qualifiedName
This creates a new method in CPPSemantics that will lookup a list of IBindings from a qualifiedName. E.g., given: namespace A { namespace B { int b; } } CPPSemantics.findBindingsByQualifiedName(scope, "A::B::b"); will return an array with the CPPVariable binding for b. This commit contains a new test case for various cases that I've thought about. I had expected that by using the existing lookup functions (in CPPSemantics) I wouldn't have to think too hard about various matches. However, the existing functions didn't work quite the way that I expected. Change-Id: I8a5aacba4a02d87f71ed4698aa432c3161395a31 Signed-off-by: Andrew Eidsness <eclipse@jfront.com> Reviewed-on: https://git.eclipse.org/r/19067 Tested-by: Hudson CI Reviewed-by: Doug Schaefer <dschaefer@qnx.com> IP-Clean: Doug Schaefer <dschaefer@qnx.com>
This commit is contained in:
parent
026b9325f0
commit
08c7d10763
2 changed files with 184 additions and 7 deletions
|
@ -9809,7 +9809,7 @@ public class AST2CPPTests extends AST2TestBase {
|
||||||
public void testFriendTemplateParameter() throws Exception {
|
public void testFriendTemplateParameter() throws Exception {
|
||||||
parseAndCheckBindings();
|
parseAndCheckBindings();
|
||||||
}
|
}
|
||||||
|
|
||||||
// struct foo {
|
// struct foo {
|
||||||
// foo();
|
// foo();
|
||||||
// ~foo();
|
// ~foo();
|
||||||
|
@ -10176,7 +10176,7 @@ public class AST2CPPTests extends AST2TestBase {
|
||||||
public void testIsBaseOf_399353() throws Exception {
|
public void testIsBaseOf_399353() throws Exception {
|
||||||
parseAndCheckBindings(getAboveComment(), CPP, true);
|
parseAndCheckBindings(getAboveComment(), CPP, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// struct base {};
|
// struct base {};
|
||||||
// struct derived : base {};
|
// struct derived : base {};
|
||||||
// typedef derived derived2;
|
// typedef derived derived2;
|
||||||
|
@ -10334,7 +10334,7 @@ public class AST2CPPTests extends AST2TestBase {
|
||||||
ICPPClassType privateNestedClass = bh.assertNonProblem("privateNestedClass");
|
ICPPClassType privateNestedClass = bh.assertNonProblem("privateNestedClass");
|
||||||
assertVisibility(ICPPClassType.v_private, aClass.getVisibility(privateNestedClass));
|
assertVisibility(ICPPClassType.v_private, aClass.getVisibility(privateNestedClass));
|
||||||
}
|
}
|
||||||
|
|
||||||
// int main() {
|
// int main() {
|
||||||
// int i = 0;
|
// int i = 0;
|
||||||
// __sync_bool_compare_and_swap(& i, 0, 1);
|
// __sync_bool_compare_and_swap(& i, 0, 1);
|
||||||
|
@ -10344,12 +10344,12 @@ public class AST2CPPTests extends AST2TestBase {
|
||||||
public void testGNUSyncBuiltins_bug389578() throws Exception {
|
public void testGNUSyncBuiltins_bug389578() throws Exception {
|
||||||
parseAndCheckBindings(getAboveComment(), CPP, true);
|
parseAndCheckBindings(getAboveComment(), CPP, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// class Waldo {
|
// class Waldo {
|
||||||
// typedef int type;
|
// typedef int type;
|
||||||
// static int value;
|
// static int value;
|
||||||
// };
|
// };
|
||||||
//
|
//
|
||||||
// int main() {
|
// int main() {
|
||||||
// Waldo w;
|
// Waldo w;
|
||||||
// decltype(w)::type i;
|
// decltype(w)::type i;
|
||||||
|
@ -10385,15 +10385,65 @@ public class AST2CPPTests extends AST2TestBase {
|
||||||
// typedef underlying_type<e_long>::type loong_type;
|
// typedef underlying_type<e_long>::type loong_type;
|
||||||
public void testUnderlyingTypeBuiltin_bug411196() throws Exception {
|
public void testUnderlyingTypeBuiltin_bug411196() throws Exception {
|
||||||
BindingAssertionHelper helper = getAssertionHelper();
|
BindingAssertionHelper helper = getAssertionHelper();
|
||||||
|
|
||||||
assertSameType((ITypedef) helper.assertNonProblem("short1_type"), CPPVisitor.SHORT_TYPE);
|
assertSameType((ITypedef) helper.assertNonProblem("short1_type"), CPPVisitor.SHORT_TYPE);
|
||||||
assertSameType((ITypedef) helper.assertNonProblem("short2_type"), CPPVisitor.SHORT_TYPE);
|
assertSameType((ITypedef) helper.assertNonProblem("short2_type"), CPPVisitor.SHORT_TYPE);
|
||||||
|
|
||||||
assertSameType((ITypedef) helper.assertNonProblem("scoped_type"), CPPVisitor.INT_TYPE);
|
assertSameType((ITypedef) helper.assertNonProblem("scoped_type"), CPPVisitor.INT_TYPE);
|
||||||
|
|
||||||
assertSameType((ITypedef) helper.assertNonProblem("unsigned_type"), CPPVisitor.UNSIGNED_INT);
|
assertSameType((ITypedef) helper.assertNonProblem("unsigned_type"), CPPVisitor.UNSIGNED_INT);
|
||||||
assertSameType((ITypedef) helper.assertNonProblem("int_type"), CPPVisitor.INT_TYPE);
|
assertSameType((ITypedef) helper.assertNonProblem("int_type"), CPPVisitor.INT_TYPE);
|
||||||
assertSameType((ITypedef) helper.assertNonProblem("ulong_type"), CPPVisitor.UNSIGNED_LONG);
|
assertSameType((ITypedef) helper.assertNonProblem("ulong_type"), CPPVisitor.UNSIGNED_LONG);
|
||||||
assertSameType((ITypedef) helper.assertNonProblem("loong_type"), CPPVisitor.LONG_TYPE);
|
assertSameType((ITypedef) helper.assertNonProblem("loong_type"), CPPVisitor.LONG_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// namespace A {
|
||||||
|
// int a;
|
||||||
|
// namespace B {
|
||||||
|
// int b;
|
||||||
|
// namespace C {
|
||||||
|
// int c;
|
||||||
|
// }
|
||||||
|
// namespace A {
|
||||||
|
// int a;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
public void testQualifiedNameLookup() throws Exception {
|
||||||
|
IASTTranslationUnit tu = parse(getAboveComment(), CPP);
|
||||||
|
|
||||||
|
IScope scope = tu.getScope();
|
||||||
|
assertNotNull(scope);
|
||||||
|
|
||||||
|
IBinding[] bindings = CPPSemantics.findBindingsForQualifiedName(scope, " A::a");
|
||||||
|
assertNotNull(bindings);
|
||||||
|
assertEquals(1, bindings.length);
|
||||||
|
IBinding a = bindings[0];
|
||||||
|
assertEquals("a", a.getName());
|
||||||
|
|
||||||
|
bindings = CPPSemantics.findBindingsForQualifiedName(scope, "A::B::b ");
|
||||||
|
assertNotNull(bindings);
|
||||||
|
assertEquals(1, bindings.length);
|
||||||
|
IBinding b = bindings[0];
|
||||||
|
assertEquals("b", b.getName());
|
||||||
|
|
||||||
|
bindings = CPPSemantics.findBindingsForQualifiedName(scope, "A:: B ::C::c");
|
||||||
|
assertNotNull(bindings);
|
||||||
|
assertEquals(1, bindings.length);
|
||||||
|
IBinding c = bindings[0];
|
||||||
|
assertEquals("c", c.getName());
|
||||||
|
|
||||||
|
// From the level of c, there should be two A::a (::A::a and ::A::B::A::a).
|
||||||
|
IScope scopeC = c.getScope();
|
||||||
|
assertNotNull(scopeC);
|
||||||
|
bindings = CPPSemantics.findBindingsForQualifiedName(scopeC, "A::a");
|
||||||
|
assertNotNull(bindings);
|
||||||
|
assertEquals(2, bindings.length);
|
||||||
|
|
||||||
|
// From the level of c, there should be only one ::A::a.
|
||||||
|
assertNotNull(scopeC);
|
||||||
|
bindings = CPPSemantics.findBindingsForQualifiedName(scopeC, "::A::a");
|
||||||
|
assertNotNull(bindings);
|
||||||
|
assertEquals(1, bindings.length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,12 +32,16 @@ import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUti
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.eclipse.cdt.core.CCorePlugin;
|
||||||
import org.eclipse.cdt.core.dom.IName;
|
import org.eclipse.cdt.core.dom.IName;
|
||||||
import org.eclipse.cdt.core.dom.ast.ASTNodeProperty;
|
import org.eclipse.cdt.core.dom.ast.ASTNodeProperty;
|
||||||
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
|
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
|
||||||
|
@ -249,6 +253,22 @@ public class CPPSemantics {
|
||||||
// special return value for costForFunctionCall
|
// special return value for costForFunctionCall
|
||||||
private static final FunctionCost CONTAINS_DEPENDENT_TYPES = new FunctionCost(null, 0, null);
|
private static final FunctionCost CONTAINS_DEPENDENT_TYPES = new FunctionCost(null, 0, null);
|
||||||
|
|
||||||
|
// A regular expression for matching qualified names. This allows for optional global qualification
|
||||||
|
// (leading ::) and then separates the first part of the name from the rest (if present). There are
|
||||||
|
// three capture groups:
|
||||||
|
// (1) If the input name specifies the global namespace (leading ::) then capture group 1 will
|
||||||
|
// be ::. Group 1 will be null otherwise.
|
||||||
|
// (2) The text of the first component of the qualified name, including leading :: if present in
|
||||||
|
// the input string. Leading and trailing whitespace is trimmed. There is no effort to check
|
||||||
|
// that the name contains valid C++ identifier characters.
|
||||||
|
// (3) The text of everything after the first component of the qualified name.
|
||||||
|
//
|
||||||
|
// E.g., -- Input Name -- ---- Capture Groups ----
|
||||||
|
// "::nsA::nsB::b" => { "::", "nsA", "nsB::b" }
|
||||||
|
// "a" => { null, "a", null }
|
||||||
|
// ":: i" => { "::", "i", null }
|
||||||
|
private static final Pattern QUALNAME_REGEX = Pattern.compile("^\\s*(::)?\\s*([^\\s:]+)\\s*(?:::(.*))?$"); //$NON-NLS-1$
|
||||||
|
|
||||||
static protected IBinding resolveBinding(IASTName name) {
|
static protected IBinding resolveBinding(IASTName name) {
|
||||||
if (traceBindingResolution) {
|
if (traceBindingResolution) {
|
||||||
for (int i = 0; i < traceIndent; i++)
|
for (int i = 0; i < traceIndent; i++)
|
||||||
|
@ -3574,6 +3594,113 @@ public class CPPSemantics {
|
||||||
return contentAssistLookup(data, nsScopes);
|
return contentAssistLookup(data, nsScopes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static IScope getLookupScope(IASTNode node) {
|
||||||
|
if (node == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (node instanceof IASTCompositeTypeSpecifier)
|
||||||
|
return ((IASTCompositeTypeSpecifier) node).getScope();
|
||||||
|
|
||||||
|
if (node instanceof ICPPASTNamespaceDefinition)
|
||||||
|
return ((ICPPASTNamespaceDefinition) node).getScope();
|
||||||
|
|
||||||
|
if (!(node instanceof ICPPInternalBinding))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
IASTNode defn = ((ICPPInternalBinding) node).getDefinition();
|
||||||
|
if (defn == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return getLookupScope(defn.getParent());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IScope getLookupScope(IBinding binding) {
|
||||||
|
if (binding == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (binding instanceof IASTCompositeTypeSpecifier)
|
||||||
|
return ((IASTCompositeTypeSpecifier) binding).getScope();
|
||||||
|
|
||||||
|
if (!(binding instanceof ICPPInternalBinding))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
IASTNode defn = ((ICPPInternalBinding) binding).getDefinition();
|
||||||
|
if (defn == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return getLookupScope(defn.getParent());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use C++ lookup semantics to find the possible bindings for the given qualified name starting
|
||||||
|
* in the given scope.
|
||||||
|
*/
|
||||||
|
public static IBinding[] findBindingsForQualifiedName(IScope scope, String qualifiedName) {
|
||||||
|
|
||||||
|
// Return immediately if the qualifiedName does not match a known format.
|
||||||
|
Matcher m = QUALNAME_REGEX.matcher(qualifiedName);
|
||||||
|
if (!m.matches())
|
||||||
|
return IBinding.EMPTY_BINDING_ARRAY;
|
||||||
|
|
||||||
|
// If the qualified name is rooted in the global namespace, then navigate to that scope.
|
||||||
|
boolean isGlobal = m.group(1) != null;
|
||||||
|
if (isGlobal) {
|
||||||
|
IScope global = scope;
|
||||||
|
try {
|
||||||
|
while(global.getParent() != null)
|
||||||
|
global = global.getParent();
|
||||||
|
} catch(DOMException e) {
|
||||||
|
CCorePlugin.log(e);
|
||||||
|
}
|
||||||
|
scope = global;
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<IBinding> bindings = new HashSet<IBinding>();
|
||||||
|
|
||||||
|
// Look for the name in the given scope.
|
||||||
|
findBindingsForQualifiedName(scope, qualifiedName, bindings);
|
||||||
|
|
||||||
|
// If the qualified name is not rooted in the global namespace (with a leading ::), then
|
||||||
|
// look at all parent scopes.
|
||||||
|
if (!isGlobal)
|
||||||
|
try {
|
||||||
|
while(scope != null) {
|
||||||
|
scope = scope.getParent();
|
||||||
|
if (scope != null)
|
||||||
|
findBindingsForQualifiedName(scope, qualifiedName, bindings);
|
||||||
|
}
|
||||||
|
} catch (DOMException e) {
|
||||||
|
CCorePlugin.log(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bindings.size() <= 0 ? IBinding.EMPTY_BINDING_ARRAY : bindings.toArray(new IBinding[bindings.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void findBindingsForQualifiedName(IScope scope, String qualifiedName, Collection<IBinding> bindings) {
|
||||||
|
// Split the qualified name into the first part (before the first :: qualifier) and the rest. All
|
||||||
|
// bindings for the first part are found and their scope is used to find the rest of the name. When
|
||||||
|
// the call tree gets to a leaf (non-qualified name) then a simple lookup happens and all matching
|
||||||
|
// bindings are added to the result.
|
||||||
|
|
||||||
|
Matcher m = QUALNAME_REGEX.matcher(qualifiedName);
|
||||||
|
if (!m.matches())
|
||||||
|
return;
|
||||||
|
|
||||||
|
String part1 = m.group(2);
|
||||||
|
String part2 = m.group(3);
|
||||||
|
|
||||||
|
// When we're down to a single component name, then use the normal lookup method.
|
||||||
|
if (part2 == null || part2.isEmpty()) {
|
||||||
|
bindings.addAll(Arrays.asList(findBindings(scope, part1, false)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find all bindings that match the first part of the name. For each such binding,
|
||||||
|
// lookup the second part of the name.
|
||||||
|
for(IBinding binding : CPPSemantics.findBindings(scope, part1, false))
|
||||||
|
findBindingsForQualifiedName(getLookupScope(binding), part2, bindings);
|
||||||
|
}
|
||||||
|
|
||||||
private static ICPPScope getNamespaceScope(CPPASTTranslationUnit tu, String[] namespaceParts, IASTNode point)
|
private static ICPPScope getNamespaceScope(CPPASTTranslationUnit tu, String[] namespaceParts, IASTNode point)
|
||||||
throws DOMException {
|
throws DOMException {
|
||||||
ICPPScope nsScope= tu.getScope();
|
ICPPScope nsScope= tu.getScope();
|
||||||
|
|
Loading…
Add table
Reference in a new issue