1 | //===--- Lookup.cpp - Framework for clang refactoring tools ---------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file defines helper methods for clang tools performing name lookup. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "clang/Tooling/Refactoring/Lookup.h" |
14 | #include "clang/AST/ASTContext.h" |
15 | #include "clang/AST/Decl.h" |
16 | #include "clang/AST/DeclCXX.h" |
17 | #include "clang/AST/DeclarationName.h" |
18 | #include "clang/Basic/SourceLocation.h" |
19 | #include "clang/Basic/SourceManager.h" |
20 | #include "llvm/ADT/SmallVector.h" |
21 | using namespace clang; |
22 | using namespace clang::tooling; |
23 | |
24 | // Gets all namespaces that \p Context is in as a vector (ignoring anonymous |
25 | // namespaces). The inner namespaces come before outer namespaces in the vector. |
26 | // For example, if the context is in the following namespace: |
27 | // `namespace a { namespace b { namespace c ( ... ) } }`, |
28 | // the vector will be `{c, b, a}`. |
29 | static llvm::SmallVector<const NamespaceDecl *, 4> |
30 | getAllNamedNamespaces(const DeclContext *Context) { |
31 | llvm::SmallVector<const NamespaceDecl *, 4> Namespaces; |
32 | auto GetNextNamedNamespace = [](const DeclContext *Context) { |
33 | // Look past non-namespaces and anonymous namespaces on FromContext. |
34 | while (Context && (!isa<NamespaceDecl>(Val: Context) || |
35 | cast<NamespaceDecl>(Val: Context)->isAnonymousNamespace())) |
36 | Context = Context->getParent(); |
37 | return Context; |
38 | }; |
39 | for (Context = GetNextNamedNamespace(Context); Context != nullptr; |
40 | Context = GetNextNamedNamespace(Context->getParent())) |
41 | Namespaces.push_back(Elt: cast<NamespaceDecl>(Val: Context)); |
42 | return Namespaces; |
43 | } |
44 | |
45 | // Returns true if the context in which the type is used and the context in |
46 | // which the type is declared are the same semantical namespace but different |
47 | // lexical namespaces. |
48 | static bool |
49 | usingFromDifferentCanonicalNamespace(const DeclContext *FromContext, |
50 | const DeclContext *UseContext) { |
51 | // We can skip anonymous namespace because: |
52 | // 1. `FromContext` and `UseContext` must be in the same anonymous namespaces |
53 | // since referencing across anonymous namespaces is not possible. |
54 | // 2. If `FromContext` and `UseContext` are in the same anonymous namespace, |
55 | // the function will still return `false` as expected. |
56 | llvm::SmallVector<const NamespaceDecl *, 4> FromNamespaces = |
57 | getAllNamedNamespaces(Context: FromContext); |
58 | llvm::SmallVector<const NamespaceDecl *, 4> UseNamespaces = |
59 | getAllNamedNamespaces(Context: UseContext); |
60 | // If `UseContext` has fewer level of nested namespaces, it cannot be in the |
61 | // same canonical namespace as the `FromContext`. |
62 | if (UseNamespaces.size() < FromNamespaces.size()) |
63 | return false; |
64 | unsigned Diff = UseNamespaces.size() - FromNamespaces.size(); |
65 | auto FromIter = FromNamespaces.begin(); |
66 | // Only compare `FromNamespaces` with namespaces in `UseNamespaces` that can |
67 | // collide, i.e. the top N namespaces where N is the number of namespaces in |
68 | // `FromNamespaces`. |
69 | auto UseIter = UseNamespaces.begin() + Diff; |
70 | for (; FromIter != FromNamespaces.end() && UseIter != UseNamespaces.end(); |
71 | ++FromIter, ++UseIter) { |
72 | // Literally the same namespace, not a collision. |
73 | if (*FromIter == *UseIter) |
74 | return false; |
75 | // Now check the names. If they match we have a different canonical |
76 | // namespace with the same name. |
77 | if (cast<NamespaceDecl>(Val: *FromIter)->getDeclName() == |
78 | cast<NamespaceDecl>(Val: *UseIter)->getDeclName()) |
79 | return true; |
80 | } |
81 | assert(FromIter == FromNamespaces.end() && UseIter == UseNamespaces.end()); |
82 | return false; |
83 | } |
84 | |
85 | static StringRef getBestNamespaceSubstr(const DeclContext *DeclA, |
86 | StringRef NewName, |
87 | bool HadLeadingColonColon) { |
88 | while (true) { |
89 | while (DeclA && !isa<NamespaceDecl>(Val: DeclA)) |
90 | DeclA = DeclA->getParent(); |
91 | |
92 | // Fully qualified it is! Leave :: in place if it's there already. |
93 | if (!DeclA) |
94 | return HadLeadingColonColon ? NewName : NewName.substr(Start: 2); |
95 | |
96 | // Otherwise strip off redundant namespace qualifications from the new name. |
97 | // We use the fully qualified name of the namespace and remove that part |
98 | // from NewName if it has an identical prefix. |
99 | std::string NS = |
100 | "::" + cast<NamespaceDecl>(Val: DeclA)->getQualifiedNameAsString() + "::" ; |
101 | if (NewName.consume_front(Prefix: NS)) |
102 | return NewName; |
103 | |
104 | // No match yet. Strip of a namespace from the end of the chain and try |
105 | // again. This allows to get optimal qualifications even if the old and new |
106 | // decl only share common namespaces at a higher level. |
107 | DeclA = DeclA->getParent(); |
108 | } |
109 | } |
110 | |
111 | /// Check if the name specifier begins with a written "::". |
112 | static bool isFullyQualified(const NestedNameSpecifier *NNS) { |
113 | while (NNS) { |
114 | if (NNS->getKind() == NestedNameSpecifier::Global) |
115 | return true; |
116 | NNS = NNS->getPrefix(); |
117 | } |
118 | return false; |
119 | } |
120 | |
121 | // Adds more scope specifier to the spelled name until the spelling is not |
122 | // ambiguous. A spelling is ambiguous if the resolution of the symbol is |
123 | // ambiguous. For example, if QName is "::y::bar", the spelling is "y::bar", and |
124 | // context contains a nested namespace "a::y", then "y::bar" can be resolved to |
125 | // ::a::y::bar in the context, which can cause compile error. |
126 | // FIXME: consider using namespaces. |
127 | static std::string disambiguateSpellingInScope(StringRef Spelling, |
128 | StringRef QName, |
129 | const DeclContext &UseContext, |
130 | SourceLocation UseLoc) { |
131 | assert(QName.starts_with("::" )); |
132 | assert(QName.ends_with(Spelling)); |
133 | if (Spelling.starts_with(Prefix: "::" )) |
134 | return std::string(Spelling); |
135 | |
136 | auto UnspelledSpecifier = QName.drop_back(N: Spelling.size()); |
137 | llvm::SmallVector<llvm::StringRef, 2> UnspelledScopes; |
138 | UnspelledSpecifier.split(A&: UnspelledScopes, Separator: "::" , /*MaxSplit=*/-1, |
139 | /*KeepEmpty=*/false); |
140 | |
141 | llvm::SmallVector<const NamespaceDecl *, 4> EnclosingNamespaces = |
142 | getAllNamedNamespaces(Context: &UseContext); |
143 | auto &AST = UseContext.getParentASTContext(); |
144 | StringRef TrimmedQName = QName.substr(Start: 2); |
145 | const auto &SM = UseContext.getParentASTContext().getSourceManager(); |
146 | UseLoc = SM.getSpellingLoc(Loc: UseLoc); |
147 | |
148 | auto IsAmbiguousSpelling = [&](const llvm::StringRef CurSpelling) { |
149 | if (CurSpelling.starts_with(Prefix: "::" )) |
150 | return false; |
151 | // Lookup the first component of Spelling in all enclosing namespaces |
152 | // and check if there is any existing symbols with the same name but in |
153 | // different scope. |
154 | StringRef Head = CurSpelling.split(Separator: "::" ).first; |
155 | for (const auto *NS : EnclosingNamespaces) { |
156 | auto LookupRes = NS->lookup(Name: DeclarationName(&AST.Idents.get(Name: Head))); |
157 | if (!LookupRes.empty()) { |
158 | for (const NamedDecl *Res : LookupRes) |
159 | // If `Res` is not visible in `UseLoc`, we don't consider it |
160 | // ambiguous. For example, a reference in a header file should not be |
161 | // affected by a potentially ambiguous name in some file that includes |
162 | // the header. |
163 | if (!TrimmedQName.starts_with(Prefix: Res->getQualifiedNameAsString()) && |
164 | SM.isBeforeInTranslationUnit( |
165 | LHS: SM.getSpellingLoc(Loc: Res->getLocation()), RHS: UseLoc)) |
166 | return true; |
167 | } |
168 | } |
169 | return false; |
170 | }; |
171 | |
172 | // Add more qualifiers until the spelling is not ambiguous. |
173 | std::string Disambiguated = std::string(Spelling); |
174 | while (IsAmbiguousSpelling(Disambiguated)) { |
175 | if (UnspelledScopes.empty()) { |
176 | Disambiguated = "::" + Disambiguated; |
177 | } else { |
178 | Disambiguated = (UnspelledScopes.back() + "::" + Disambiguated).str(); |
179 | UnspelledScopes.pop_back(); |
180 | } |
181 | } |
182 | return Disambiguated; |
183 | } |
184 | |
185 | std::string tooling::replaceNestedName(const NestedNameSpecifier *Use, |
186 | SourceLocation UseLoc, |
187 | const DeclContext *UseContext, |
188 | const NamedDecl *FromDecl, |
189 | StringRef ReplacementString) { |
190 | assert(ReplacementString.starts_with("::" ) && |
191 | "Expected fully-qualified name!" ); |
192 | |
193 | // We can do a raw name replacement when we are not inside the namespace for |
194 | // the original class/function and it is not in the global namespace. The |
195 | // assumption is that outside the original namespace we must have a using |
196 | // statement that makes this work out and that other parts of this refactor |
197 | // will automatically fix using statements to point to the new class/function. |
198 | // However, if the `FromDecl` is a class forward declaration, the reference is |
199 | // still considered as referring to the original definition, so we can't do a |
200 | // raw name replacement in this case. |
201 | const bool class_name_only = !Use; |
202 | const bool in_global_namespace = |
203 | isa<TranslationUnitDecl>(Val: FromDecl->getDeclContext()); |
204 | const bool is_class_forward_decl = |
205 | isa<CXXRecordDecl>(Val: FromDecl) && |
206 | !cast<CXXRecordDecl>(Val: FromDecl)->isCompleteDefinition(); |
207 | if (class_name_only && !in_global_namespace && !is_class_forward_decl && |
208 | !usingFromDifferentCanonicalNamespace(FromContext: FromDecl->getDeclContext(), |
209 | UseContext)) { |
210 | auto Pos = ReplacementString.rfind(Str: "::" ); |
211 | return std::string(Pos != StringRef::npos |
212 | ? ReplacementString.substr(Start: Pos + 2) |
213 | : ReplacementString); |
214 | } |
215 | // We did not match this because of a using statement, so we will need to |
216 | // figure out how good a namespace match we have with our destination type. |
217 | // We work backwards (from most specific possible namespace to least |
218 | // specific). |
219 | StringRef Suggested = getBestNamespaceSubstr(DeclA: UseContext, NewName: ReplacementString, |
220 | HadLeadingColonColon: isFullyQualified(NNS: Use)); |
221 | |
222 | return disambiguateSpellingInScope(Spelling: Suggested, QName: ReplacementString, UseContext: *UseContext, |
223 | UseLoc); |
224 | } |
225 | |