clang 22.0.0git
Lookup.cpp
Go to the documentation of this file.
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
15#include "clang/AST/Decl.h"
16#include "clang/AST/DeclCXX.h"
20#include "llvm/ADT/SmallVector.h"
21using namespace clang;
22using 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}`.
32 auto GetNextNamedNamespace = [](const DeclContext *Context) {
33 // Look past non-namespaces and anonymous namespaces on FromContext.
34 while (Context && (!isa<NamespaceDecl>(Context) ||
35 cast<NamespaceDecl>(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(cast<NamespaceDecl>(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.
48static bool
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.
57 getAllNamedNamespaces(FromContext);
59 getAllNamedNamespaces(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>(*FromIter)->getDeclName() ==
78 cast<NamespaceDecl>(*UseIter)->getDeclName())
79 return true;
80 }
81 assert(FromIter == FromNamespaces.end() && UseIter == UseNamespaces.end());
82 return false;
83}
84
85static StringRef getBestNamespaceSubstr(const DeclContext *DeclA,
86 StringRef NewName,
87 bool HadLeadingColonColon) {
88 while (true) {
89 while (DeclA && !isa<NamespaceDecl>(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(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>(DeclA)->getQualifiedNameAsString() + "::";
101 if (NewName.consume_front(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// Adds more scope specifier to the spelled name until the spelling is not
112// ambiguous. A spelling is ambiguous if the resolution of the symbol is
113// ambiguous. For example, if QName is "::y::bar", the spelling is "y::bar", and
114// context contains a nested namespace "a::y", then "y::bar" can be resolved to
115// ::a::y::bar in the context, which can cause compile error.
116// FIXME: consider using namespaces.
117static std::string disambiguateSpellingInScope(StringRef Spelling,
118 StringRef QName,
119 const DeclContext &UseContext,
120 SourceLocation UseLoc) {
121 assert(QName.starts_with("::"));
122 assert(QName.ends_with(Spelling));
123 if (Spelling.starts_with("::"))
124 return std::string(Spelling);
125
126 auto UnspelledSpecifier = QName.drop_back(Spelling.size());
128 UnspelledSpecifier.split(UnspelledScopes, "::", /*MaxSplit=*/-1,
129 /*KeepEmpty=*/false);
130
132 getAllNamedNamespaces(&UseContext);
133 auto &AST = UseContext.getParentASTContext();
134 StringRef TrimmedQName = QName.substr(2);
135 const auto &SM = UseContext.getParentASTContext().getSourceManager();
136 UseLoc = SM.getSpellingLoc(UseLoc);
137
138 auto IsAmbiguousSpelling = [&](const llvm::StringRef CurSpelling) {
139 if (CurSpelling.starts_with("::"))
140 return false;
141 // Lookup the first component of Spelling in all enclosing namespaces
142 // and check if there is any existing symbols with the same name but in
143 // different scope.
144 StringRef Head = CurSpelling.split("::").first;
145 for (const auto *NS : EnclosingNamespaces) {
146 auto LookupRes = NS->lookup(DeclarationName(&AST.Idents.get(Head)));
147 if (!LookupRes.empty()) {
148 for (const NamedDecl *Res : LookupRes)
149 // If `Res` is not visible in `UseLoc`, we don't consider it
150 // ambiguous. For example, a reference in a header file should not be
151 // affected by a potentially ambiguous name in some file that includes
152 // the header.
153 if (!TrimmedQName.starts_with(Res->getQualifiedNameAsString()) &&
154 SM.isBeforeInTranslationUnit(
155 SM.getSpellingLoc(Res->getLocation()), UseLoc))
156 return true;
157 }
158 }
159 return false;
160 };
161
162 // Add more qualifiers until the spelling is not ambiguous.
163 std::string Disambiguated = std::string(Spelling);
164 while (IsAmbiguousSpelling(Disambiguated)) {
165 if (UnspelledScopes.empty()) {
166 Disambiguated = "::" + Disambiguated;
167 } else {
168 Disambiguated = (UnspelledScopes.back() + "::" + Disambiguated).str();
169 UnspelledScopes.pop_back();
170 }
171 }
172 return Disambiguated;
173}
174
176 SourceLocation UseLoc,
177 const DeclContext *UseContext,
178 const NamedDecl *FromDecl,
179 StringRef ReplacementString) {
180 assert(ReplacementString.starts_with("::") &&
181 "Expected fully-qualified name!");
182
183 // We can do a raw name replacement when we are not inside the namespace for
184 // the original class/function and it is not in the global namespace. The
185 // assumption is that outside the original namespace we must have a using
186 // statement that makes this work out and that other parts of this refactor
187 // will automatically fix using statements to point to the new class/function.
188 // However, if the `FromDecl` is a class forward declaration, the reference is
189 // still considered as referring to the original definition, so we can't do a
190 // raw name replacement in this case.
191 const bool class_name_only = !Use;
192 const bool in_global_namespace =
194 const bool is_class_forward_decl =
195 isa<CXXRecordDecl>(FromDecl) &&
196 !cast<CXXRecordDecl>(FromDecl)->isCompleteDefinition();
197 if (class_name_only && !in_global_namespace && !is_class_forward_decl &&
199 UseContext)) {
200 auto Pos = ReplacementString.rfind("::");
201 return std::string(Pos != StringRef::npos
202 ? ReplacementString.substr(Pos + 2)
203 : ReplacementString);
204 }
205 // We did not match this because of a using statement, so we will need to
206 // figure out how good a namespace match we have with our destination type.
207 // We work backwards (from most specific possible namespace to least
208 // specific).
209 StringRef Suggested = getBestNamespaceSubstr(UseContext, ReplacementString,
210 Use.isFullyQualified());
211
212 return disambiguateSpellingInScope(Suggested, ReplacementString, *UseContext,
213 UseLoc);
214}
Defines the clang::ASTContext interface.
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
static llvm::SmallVector< const NamespaceDecl *, 4 > getAllNamedNamespaces(const DeclContext *Context)
Definition Lookup.cpp:30
static bool usingFromDifferentCanonicalNamespace(const DeclContext *FromContext, const DeclContext *UseContext)
Definition Lookup.cpp:49
static StringRef getBestNamespaceSubstr(const DeclContext *DeclA, StringRef NewName, bool HadLeadingColonColon)
Definition Lookup.cpp:85
static std::string disambiguateSpellingInScope(StringRef Spelling, StringRef QName, const DeclContext &UseContext, SourceLocation UseLoc)
Definition Lookup.cpp:117
#define SM(sm)
Defines the clang::SourceLocation class and associated facilities.
Defines the SourceManager interface.
SourceManager & getSourceManager()
Definition ASTContext.h:798
DeclContext - This is used only as base class of specific decl types that can act as declaration cont...
Definition DeclBase.h:1449
DeclContext * getParent()
getParent - Returns the containing DeclContext.
Definition DeclBase.h:2109
ASTContext & getParentASTContext() const
Definition DeclBase.h:2138
DeclContext * getDeclContext()
Definition DeclBase.h:448
The name of a declaration.
This represents a decl that may have a name.
Definition Decl.h:273
Represents a C++ nested name specifier, such as "\::std::vector<int>::".
bool isFullyQualified() const
Whether this nested name specifier starts with a '::'.
Encodes a location in the source.
std::string replaceNestedName(NestedNameSpecifier Use, SourceLocation UseLoc, const DeclContext *UseContext, const NamedDecl *FromDecl, StringRef ReplacementString)
Emulate a lookup to replace one nested name specifier with another using as few additional namespace ...
Definition Lookup.cpp:175
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
Definition Address.h:330
U cast(CodeGen::Address addr)
Definition Address.h:327