//===--- Lookup.cpp - Framework for clang refactoring tools ---------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file defines helper methods for clang tools performing name lookup. // //===----------------------------------------------------------------------===// #include "clang/Tooling/Core/Lookup.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" using namespace clang; using namespace clang::tooling; // 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 getAllNamedNamespaces(const DeclContext *Context) { llvm::SmallVector Namespaces; auto GetNextNamedNamespace = [](const DeclContext *Context) { // Look past non-namespaces and anonymous namespaces on FromContext. while (Context && (!isa(Context) || cast(Context)->isAnonymousNamespace())) Context = Context->getParent(); return Context; }; for (Context = GetNextNamedNamespace(Context); Context != nullptr; Context = GetNextNamedNamespace(Context->getParent())) Namespaces.push_back(cast(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 FromNamespaces = getAllNamedNamespaces(FromContext); llvm::SmallVector 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 (*FromIter == *UseIter) return false; // Now check the names. If they match we have a different canonical // namespace with the same name. if (cast(*FromIter)->getDeclName() == cast(*UseIter)->getDeclName()) return true; } assert(FromIter == FromNamespaces.end() && UseIter == UseNamespaces.end()); return false; } static StringRef getBestNamespaceSubstr(const DeclContext *DeclA, StringRef NewName, bool HadLeadingColonColon) { while (true) { while (DeclA && !isa(DeclA)) DeclA = DeclA->getParent(); // Fully qualified it is! Leave :: in place if it's there already. if (!DeclA) return HadLeadingColonColon ? NewName : NewName.substr(2); // Otherwise strip off redundant namespace qualifications from the new name. // We use the fully qualified name of the namespace and remove that part // from NewName if it has an identical prefix. std::string NS = "::" + cast(DeclA)->getQualifiedNameAsString() + "::"; if (NewName.startswith(NS)) return NewName.substr(NS.size()); // No match yet. Strip of a namespace from the end of the chain and try // again. This allows to get optimal qualifications even if the old and new // decl only share common namespaces at a higher level. DeclA = DeclA->getParent(); } } /// Check if the name specifier begins with a written "::". static bool isFullyQualified(const NestedNameSpecifier *NNS) { while (NNS) { if (NNS->getKind() == NestedNameSpecifier::Global) return true; NNS = NNS->getPrefix(); } return false; } std::string tooling::replaceNestedName(const NestedNameSpecifier *Use, const DeclContext *UseContext, const NamedDecl *FromDecl, StringRef ReplacementString) { assert(ReplacementString.startswith("::") && "Expected fully-qualified name!"); // We can do a raw name replacement when we are not inside the namespace for // 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 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(FromDecl->getDeclContext()); const bool is_class_forward_decl = isa(FromDecl) && !cast(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; } // We did not match this because of a using statement, so we will need to // figure out how good a namespace match we have with our destination type. // We work backwards (from most specific possible namespace to least // specific). return getBestNamespaceSubstr(UseContext, ReplacementString, isFullyQualified(Use)); }