//===--- 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" 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(DeclA)) DeclA = DeclA->getParent(); // Look past non-namespaces on DeclB. while (DeclB && !isa(DeclB)) DeclB = DeclB->getParent(); // We hit the root, no namespace collision. if (!DeclA || !DeclB) return false; // Literally the same namespace, not a collision. if (DeclA == DeclB) return false; // Now check the names. If they match we have a different namespace with the // same name. if (cast(DeclA)->getDeclName() == cast(DeclB)->getDeclName()) return true; DeclA = DeclA->getParent(); DeclB = DeclB->getParent(); } } 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 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 const bool class_name_only = !Use; const bool in_global_namespace = isa(FromDecl->getDeclContext()); if (class_name_only && !in_global_namespace && !isInsideDifferentNamespaceWithSameName(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)); }