diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2017-01-02 19:18:08 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2017-01-02 19:18:08 +0000 |
commit | bab175ec4b075c8076ba14c762900392533f6ee4 (patch) | |
tree | 01f4f29419a2cb10abe13c1e63cd2a66068b0137 /lib/Tooling/Core/Lookup.cpp | |
parent | 8b7a8012d223fac5d17d16a66bb39168a9a1dfc0 (diff) |
Notes
Diffstat (limited to 'lib/Tooling/Core/Lookup.cpp')
-rw-r--r-- | lib/Tooling/Core/Lookup.cpp | 94 |
1 files changed, 66 insertions, 28 deletions
diff --git a/lib/Tooling/Core/Lookup.cpp b/lib/Tooling/Core/Lookup.cpp index 697eeb46ce41..6edf61b8050d 100644 --- a/lib/Tooling/Core/Lookup.cpp +++ b/lib/Tooling/Core/Lookup.cpp @@ -13,37 +13,69 @@ #include "clang/Tooling/Core/Lookup.h" #include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" using namespace clang; using namespace clang::tooling; -static bool isInsideDifferentNamespaceWithSameName(const DeclContext *DeclA, - const DeclContext *DeclB) { - while (true) { - // Look past non-namespaces on DeclA. - while (DeclA && !isa<NamespaceDecl>(DeclA)) - DeclA = DeclA->getParent(); - - // Look past non-namespaces on DeclB. - while (DeclB && !isa<NamespaceDecl>(DeclB)) - DeclB = DeclB->getParent(); - - // We hit the root, no namespace collision. - if (!DeclA || !DeclB) - return false; +// Gets all namespaces that \p Context is in as a vector (ignoring anonymous +// namespaces). The inner namespaces come before outer namespaces in the vector. +// For example, if the context is in the following namespace: +// `namespace a { namespace b { namespace c ( ... ) } }`, +// the vector will be `{c, b, a}`. +static llvm::SmallVector<const NamespaceDecl *, 4> +getAllNamedNamespaces(const DeclContext *Context) { + llvm::SmallVector<const NamespaceDecl *, 4> Namespaces; + auto GetNextNamedNamespace = [](const DeclContext *Context) { + // Look past non-namespaces and anonymous namespaces on FromContext. + while (Context && (!isa<NamespaceDecl>(Context) || + cast<NamespaceDecl>(Context)->isAnonymousNamespace())) + Context = Context->getParent(); + return Context; + }; + for (Context = GetNextNamedNamespace(Context); Context != nullptr; + Context = GetNextNamedNamespace(Context->getParent())) + Namespaces.push_back(cast<NamespaceDecl>(Context)); + return Namespaces; +} +// Returns true if the context in which the type is used and the context in +// which the type is declared are the same semantical namespace but different +// lexical namespaces. +static bool +usingFromDifferentCanonicalNamespace(const DeclContext *FromContext, + const DeclContext *UseContext) { + // We can skip anonymous namespace because: + // 1. `FromContext` and `UseContext` must be in the same anonymous namespaces + // since referencing across anonymous namespaces is not possible. + // 2. If `FromContext` and `UseContext` are in the same anonymous namespace, + // the function will still return `false` as expected. + llvm::SmallVector<const NamespaceDecl *, 4> FromNamespaces = + getAllNamedNamespaces(FromContext); + llvm::SmallVector<const NamespaceDecl *, 4> UseNamespaces = + getAllNamedNamespaces(UseContext); + // If `UseContext` has fewer level of nested namespaces, it cannot be in the + // same canonical namespace as the `FromContext`. + if (UseNamespaces.size() < FromNamespaces.size()) + return false; + unsigned Diff = UseNamespaces.size() - FromNamespaces.size(); + auto FromIter = FromNamespaces.begin(); + // Only compare `FromNamespaces` with namespaces in `UseNamespaces` that can + // collide, i.e. the top N namespaces where N is the number of namespaces in + // `FromNamespaces`. + auto UseIter = UseNamespaces.begin() + Diff; + for (; FromIter != FromNamespaces.end() && UseIter != UseNamespaces.end(); + ++FromIter, ++UseIter) { // Literally the same namespace, not a collision. - if (DeclA == DeclB) + if (*FromIter == *UseIter) return false; - - // Now check the names. If they match we have a different namespace with the - // same name. - if (cast<NamespaceDecl>(DeclA)->getDeclName() == - cast<NamespaceDecl>(DeclB)->getDeclName()) + // Now check the names. If they match we have a different canonical + // namespace with the same name. + if (cast<NamespaceDecl>(*FromIter)->getDeclName() == + cast<NamespaceDecl>(*UseIter)->getDeclName()) return true; - - DeclA = DeclA->getParent(); - DeclB = DeclB->getParent(); } + assert(FromIter == FromNamespaces.end() && UseIter == UseNamespaces.end()); + return false; } static StringRef getBestNamespaceSubstr(const DeclContext *DeclA, @@ -90,16 +122,22 @@ std::string tooling::replaceNestedName(const NestedNameSpecifier *Use, "Expected fully-qualified name!"); // We can do a raw name replacement when we are not inside the namespace for - // the original function and it is not in the global namespace. The + // the original class/function and it is not in the global namespace. The // assumption is that outside the original namespace we must have a using // statement that makes this work out and that other parts of this refactor - // will automatically fix using statements to point to the new function + // will automatically fix using statements to point to the new class/function. + // However, if the `FromDecl` is a class forward declaration, the reference is + // still considered as referring to the original definition, so we can't do a + // raw name replacement in this case. const bool class_name_only = !Use; const bool in_global_namespace = isa<TranslationUnitDecl>(FromDecl->getDeclContext()); - if (class_name_only && !in_global_namespace && - !isInsideDifferentNamespaceWithSameName(FromDecl->getDeclContext(), - UseContext)) { + const bool is_class_forward_decl = + isa<CXXRecordDecl>(FromDecl) && + !cast<CXXRecordDecl>(FromDecl)->isCompleteDefinition(); + if (class_name_only && !in_global_namespace && !is_class_forward_decl && + !usingFromDifferentCanonicalNamespace(FromDecl->getDeclContext(), + UseContext)) { auto Pos = ReplacementString.rfind("::"); return Pos != StringRef::npos ? ReplacementString.substr(Pos + 2) : ReplacementString; |