1
0
Fork 0
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:
Andrew Eidsness 2013-11-27 14:59:13 -05:00 committed by Doug Schaefer
parent 026b9325f0
commit 08c7d10763
2 changed files with 184 additions and 7 deletions

View file

@ -10396,4 +10396,54 @@ public class AST2CPPTests extends AST2TestBase {
assertSameType((ITypedef) helper.assertNonProblem("ulong_type"), CPPVisitor.UNSIGNED_LONG);
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);
}
}

View file

@ -32,12 +32,16 @@ import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUti
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
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.ast.ASTNodeProperty;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
@ -249,6 +253,22 @@ public class CPPSemantics {
// special return value for costForFunctionCall
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) {
if (traceBindingResolution) {
for (int i = 0; i < traceIndent; i++)
@ -3574,6 +3594,113 @@ public class CPPSemantics {
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)
throws DOMException {
ICPPScope nsScope= tu.getScope();