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
isInsideDifferentNamespaceWithSameName(const DeclContext * DeclA,const DeclContext * DeclB)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
getBestNamespaceSubstr(const DeclContext * DeclA,StringRef NewName,bool HadLeadingColonColon)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 "::".
isFullyQualified(const NestedNameSpecifier * NNS)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
replaceNestedName(const NestedNameSpecifier * Use,const DeclContext * UseContext,const NamedDecl * FromDecl,StringRef ReplacementString)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