1 //===--- Lookup.cpp - Framework for clang refactoring tools ---------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This file defines helper methods for clang tools performing name lookup. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "clang/Tooling/Core/Lookup.h" 15 #include "clang/AST/Decl.h" 16 using namespace clang; 17 using namespace clang::tooling; 18 19 static bool isInsideDifferentNamespaceWithSameName(const DeclContext *DeclA, 20 const DeclContext *DeclB) { 21 while (true) { 22 // Look past non-namespaces on DeclA. 23 while (DeclA && !isa<NamespaceDecl>(DeclA)) 24 DeclA = DeclA->getParent(); 25 26 // Look past non-namespaces on DeclB. 27 while (DeclB && !isa<NamespaceDecl>(DeclB)) 28 DeclB = DeclB->getParent(); 29 30 // We hit the root, no namespace collision. 31 if (!DeclA || !DeclB) 32 return false; 33 34 // Literally the same namespace, not a collision. 35 if (DeclA == DeclB) 36 return false; 37 38 // Now check the names. If they match we have a different namespace with the 39 // same name. 40 if (cast<NamespaceDecl>(DeclA)->getDeclName() == 41 cast<NamespaceDecl>(DeclB)->getDeclName()) 42 return true; 43 44 DeclA = DeclA->getParent(); 45 DeclB = DeclB->getParent(); 46 } 47 } 48 49 static StringRef getBestNamespaceSubstr(const DeclContext *DeclA, 50 StringRef NewName, 51 bool HadLeadingColonColon) { 52 while (true) { 53 while (DeclA && !isa<NamespaceDecl>(DeclA)) 54 DeclA = DeclA->getParent(); 55 56 // Fully qualified it is! Leave :: in place if it's there already. 57 if (!DeclA) 58 return HadLeadingColonColon ? NewName : NewName.substr(2); 59 60 // Otherwise strip off redundant namespace qualifications from the new name. 61 // We use the fully qualified name of the namespace and remove that part 62 // from NewName if it has an identical prefix. 63 std::string NS = 64 "::" + cast<NamespaceDecl>(DeclA)->getQualifiedNameAsString() + "::"; 65 if (NewName.startswith(NS)) 66 return NewName.substr(NS.size()); 67 68 // No match yet. Strip of a namespace from the end of the chain and try 69 // again. This allows to get optimal qualifications even if the old and new 70 // decl only share common namespaces at a higher level. 71 DeclA = DeclA->getParent(); 72 } 73 } 74 75 /// Check if the name specifier begins with a written "::". 76 static bool isFullyQualified(const NestedNameSpecifier *NNS) { 77 while (NNS) { 78 if (NNS->getKind() == NestedNameSpecifier::Global) 79 return true; 80 NNS = NNS->getPrefix(); 81 } 82 return false; 83 } 84 85 std::string tooling::replaceNestedName(const NestedNameSpecifier *Use, 86 const DeclContext *UseContext, 87 const NamedDecl *FromDecl, 88 StringRef ReplacementString) { 89 assert(ReplacementString.startswith("::") && 90 "Expected fully-qualified name!"); 91 92 // We can do a raw name replacement when we are not inside the namespace for 93 // the original function and it is not in the global namespace. The 94 // assumption is that outside the original namespace we must have a using 95 // statement that makes this work out and that other parts of this refactor 96 // will automatically fix using statements to point to the new function 97 const bool class_name_only = !Use; 98 const bool in_global_namespace = 99 isa<TranslationUnitDecl>(FromDecl->getDeclContext()); 100 if (class_name_only && !in_global_namespace && 101 !isInsideDifferentNamespaceWithSameName(FromDecl->getDeclContext(), 102 UseContext)) { 103 auto Pos = ReplacementString.rfind("::"); 104 return Pos != StringRef::npos ? ReplacementString.substr(Pos + 2) 105 : ReplacementString; 106 } 107 // We did not match this because of a using statement, so we will need to 108 // figure out how good a namespace match we have with our destination type. 109 // We work backwards (from most specific possible namespace to least 110 // specific). 111 return getBestNamespaceSubstr(UseContext, ReplacementString, 112 isFullyQualified(Use)); 113 } 114