/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Tooling/Refactoring/Lookup.cpp
Line | Count | Source (jump to first uncovered line) |
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 | 372 | getAllNamedNamespaces(const DeclContext *Context) { |
31 | 372 | llvm::SmallVector<const NamespaceDecl *, 4> Namespaces; |
32 | 716 | auto GetNextNamedNamespace = [](const DeclContext *Context) { |
33 | | // Look past non-namespaces and anonymous namespaces on FromContext. |
34 | 1.28k | while (Context && (915 !isa<NamespaceDecl>(Context)915 || |
35 | 915 | cast<NamespaceDecl>(Context)->isAnonymousNamespace()346 )) |
36 | 571 | Context = Context->getParent(); |
37 | 716 | return Context; |
38 | 716 | }; |
39 | 716 | for (Context = GetNextNamedNamespace(Context); Context != nullptr; |
40 | 372 | Context = GetNextNamedNamespace(Context->getParent())344 ) |
41 | 344 | Namespaces.push_back(cast<NamespaceDecl>(Context)); |
42 | 372 | return Namespaces; |
43 | 372 | } |
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 | 100 | 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 | 100 | llvm::SmallVector<const NamespaceDecl *, 4> FromNamespaces = |
57 | 100 | getAllNamedNamespaces(FromContext); |
58 | 100 | llvm::SmallVector<const NamespaceDecl *, 4> UseNamespaces = |
59 | 100 | getAllNamedNamespaces(UseContext); |
60 | | // If `UseContext` has fewer level of nested namespaces, it cannot be in the |
61 | | // same canonical namespace as the `FromContext`. |
62 | 100 | if (UseNamespaces.size() < FromNamespaces.size()) |
63 | 4 | return false; |
64 | 96 | unsigned Diff = UseNamespaces.size() - FromNamespaces.size(); |
65 | 96 | 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 | 96 | auto UseIter = UseNamespaces.begin() + Diff; |
70 | 98 | for (; FromIter != FromNamespaces.end() && UseIter != UseNamespaces.end()96 ; |
71 | 96 | ++FromIter, ++UseIter2 ) { |
72 | | // Literally the same namespace, not a collision. |
73 | 96 | if (*FromIter == *UseIter) |
74 | 62 | return false; |
75 | | // Now check the names. If they match we have a different canonical |
76 | | // namespace with the same name. |
77 | 34 | if (cast<NamespaceDecl>(*FromIter)->getDeclName() == |
78 | 34 | cast<NamespaceDecl>(*UseIter)->getDeclName()) |
79 | 32 | return true; |
80 | 34 | } |
81 | 2 | assert(FromIter == FromNamespaces.end() && UseIter == UseNamespaces.end()); |
82 | 0 | return false; |
83 | 96 | } |
84 | | |
85 | | static StringRef getBestNamespaceSubstr(const DeclContext *DeclA, |
86 | | StringRef NewName, |
87 | 177 | bool HadLeadingColonColon) { |
88 | 230 | while (true) { |
89 | 497 | while (DeclA && !isa<NamespaceDecl>(DeclA)358 ) |
90 | 267 | DeclA = DeclA->getParent(); |
91 | | |
92 | | // Fully qualified it is! Leave :: in place if it's there already. |
93 | 230 | if (!DeclA) |
94 | 139 | return HadLeadingColonColon ? NewName5 : NewName.substr(2)134 ; |
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 | 91 | std::string NS = |
100 | 91 | "::" + cast<NamespaceDecl>(DeclA)->getQualifiedNameAsString() + "::"; |
101 | 91 | if (NewName.startswith(NS)) |
102 | 38 | return NewName.substr(NS.size()); |
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 | 53 | DeclA = DeclA->getParent(); |
108 | 53 | } |
109 | 177 | } |
110 | | |
111 | | /// Check if the name specifier begins with a written "::". |
112 | 177 | static bool isFullyQualified(const NestedNameSpecifier *NNS) { |
113 | 315 | while (NNS) { |
114 | 146 | if (NNS->getKind() == NestedNameSpecifier::Global) |
115 | 8 | return true; |
116 | 138 | NNS = NNS->getPrefix(); |
117 | 138 | } |
118 | 169 | return false; |
119 | 177 | } |
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 | 177 | SourceLocation UseLoc) { |
131 | 177 | assert(QName.startswith("::")); |
132 | 0 | assert(QName.endswith(Spelling)); |
133 | 177 | if (Spelling.startswith("::")) |
134 | 5 | return std::string(Spelling); |
135 | | |
136 | 172 | auto UnspelledSpecifier = QName.drop_back(Spelling.size()); |
137 | 172 | llvm::SmallVector<llvm::StringRef, 2> UnspelledScopes; |
138 | 172 | UnspelledSpecifier.split(UnspelledScopes, "::", /*MaxSplit=*/-1, |
139 | 172 | /*KeepEmpty=*/false); |
140 | | |
141 | 172 | llvm::SmallVector<const NamespaceDecl *, 4> EnclosingNamespaces = |
142 | 172 | getAllNamedNamespaces(&UseContext); |
143 | 172 | auto &AST = UseContext.getParentASTContext(); |
144 | 172 | StringRef TrimmedQName = QName.substr(2); |
145 | 172 | const auto &SM = UseContext.getParentASTContext().getSourceManager(); |
146 | 172 | UseLoc = SM.getSpellingLoc(UseLoc); |
147 | | |
148 | 175 | auto IsAmbiguousSpelling = [&](const llvm::StringRef CurSpelling) { |
149 | 175 | if (CurSpelling.startswith("::")) |
150 | 2 | 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 | 173 | StringRef Head = CurSpelling.split("::").first; |
155 | 173 | for (const auto *NS : EnclosingNamespaces) { |
156 | 97 | auto LookupRes = NS->lookup(DeclarationName(&AST.Idents.get(Head))); |
157 | 97 | if (!LookupRes.empty()) { |
158 | 4 | 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 | 4 | if (!TrimmedQName.startswith(Res->getQualifiedNameAsString()) && |
164 | 4 | SM.isBeforeInTranslationUnit( |
165 | 4 | SM.getSpellingLoc(Res->getLocation()), UseLoc)) |
166 | 3 | return true; |
167 | 4 | } |
168 | 97 | } |
169 | 170 | return false; |
170 | 173 | }; |
171 | | |
172 | | // Add more qualifiers until the spelling is not ambiguous. |
173 | 172 | std::string Disambiguated = std::string(Spelling); |
174 | 175 | while (IsAmbiguousSpelling(Disambiguated)) { |
175 | 3 | if (UnspelledScopes.empty()) { |
176 | 2 | Disambiguated = "::" + Disambiguated; |
177 | 2 | } else { |
178 | 1 | Disambiguated = (UnspelledScopes.back() + "::" + Disambiguated).str(); |
179 | 1 | UnspelledScopes.pop_back(); |
180 | 1 | } |
181 | 3 | } |
182 | 172 | return Disambiguated; |
183 | 177 | } |
184 | | |
185 | | std::string tooling::replaceNestedName(const NestedNameSpecifier *Use, |
186 | | SourceLocation UseLoc, |
187 | | const DeclContext *UseContext, |
188 | | const NamedDecl *FromDecl, |
189 | 245 | StringRef ReplacementString) { |
190 | 245 | assert(ReplacementString.startswith("::") && |
191 | 245 | "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 | 0 | const bool class_name_only = !Use; |
202 | 245 | const bool in_global_namespace = |
203 | 245 | isa<TranslationUnitDecl>(FromDecl->getDeclContext()); |
204 | 245 | const bool is_class_forward_decl = |
205 | 245 | isa<CXXRecordDecl>(FromDecl) && |
206 | 245 | !cast<CXXRecordDecl>(FromDecl)->isCompleteDefinition()140 ; |
207 | 245 | if (class_name_only && !in_global_namespace121 && !is_class_forward_decl102 && |
208 | 245 | !usingFromDifferentCanonicalNamespace(FromDecl->getDeclContext(), |
209 | 100 | UseContext)) { |
210 | 68 | auto Pos = ReplacementString.rfind("::"); |
211 | 68 | return std::string(Pos != StringRef::npos |
212 | 68 | ? ReplacementString.substr(Pos + 2) |
213 | 68 | : ReplacementString0 ); |
214 | 68 | } |
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 | 177 | StringRef Suggested = getBestNamespaceSubstr(UseContext, ReplacementString, |
220 | 177 | isFullyQualified(Use)); |
221 | | |
222 | 177 | return disambiguateSpellingInScope(Suggested, ReplacementString, *UseContext, |
223 | 177 | UseLoc); |
224 | 245 | } |