1//===- NestedNameSpecifier.cpp - C++ nested name specifiers ---------------===//
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 the NestedNameSpecifier class, which represents
10// a C++ nested-name-specifier.
11//
12//===----------------------------------------------------------------------===//
13
14#include "clang/AST/NestedNameSpecifier.h"
15#include "clang/AST/ASTContext.h"
16#include "clang/AST/Decl.h"
17#include "clang/AST/DeclCXX.h"
18#include "clang/AST/DependenceFlags.h"
19#include "clang/AST/PrettyPrinter.h"
20#include "clang/AST/TemplateName.h"
21#include "clang/AST/Type.h"
22#include "clang/AST/TypeLoc.h"
23#include "clang/Basic/LLVM.h"
24#include "clang/Basic/LangOptions.h"
25#include "clang/Basic/SourceLocation.h"
26#include "llvm/ADT/FoldingSet.h"
27#include "llvm/Support/Compiler.h"
28#include "llvm/Support/ErrorHandling.h"
29#include "llvm/Support/raw_ostream.h"
30#include <algorithm>
31#include <cassert>
32#include <cstdlib>
33#include <cstring>
34
35using namespace clang;
36
37const NamespaceAndPrefixStorage *
38NestedNameSpecifier::MakeNamespaceAndPrefixStorage(
39 const ASTContext &Ctx, const NamespaceBaseDecl *Namespace,
40 NestedNameSpecifier Prefix) {
41 llvm::FoldingSetNodeID ID;
42 NamespaceAndPrefixStorage::Profile(ID, Namespace, Prefix);
43
44 void *InsertPos = nullptr;
45 NamespaceAndPrefixStorage *S =
46 Ctx.NamespaceAndPrefixStorages.FindNodeOrInsertPos(ID, InsertPos);
47 if (!S) {
48 S = new (Ctx, alignof(NamespaceAndPrefixStorage))
49 NamespaceAndPrefixStorage(Namespace, Prefix);
50 Ctx.NamespaceAndPrefixStorages.InsertNode(N: S, InsertPos);
51 }
52 return S;
53}
54
55bool NestedNameSpecifier::isFullyQualified() const {
56 switch (getKind()) {
57 case NestedNameSpecifier::Kind::Global:
58 return true;
59 case NestedNameSpecifier::Kind::Null:
60 case NestedNameSpecifier::Kind::MicrosoftSuper:
61 return false;
62 case NestedNameSpecifier::Kind::Namespace:
63 return getAsNamespaceAndPrefix().Prefix.isFullyQualified();
64 case NestedNameSpecifier::Kind::Type:
65 return getAsType()->getPrefix().isFullyQualified();
66 }
67 llvm_unreachable("Invalid NNS Kind!");
68}
69
70NestedNameSpecifierDependence NestedNameSpecifier::getDependence() const {
71 switch (getKind()) {
72 case Kind::Null:
73 case Kind::Global:
74 case Kind::Namespace:
75 return NestedNameSpecifierDependence::None;
76 case Kind::MicrosoftSuper: {
77 CXXRecordDecl *RD = getAsMicrosoftSuper();
78 return RD->isDependentContext()
79 ? NestedNameSpecifierDependence::DependentInstantiation |
80 NestedNameSpecifierDependence::Dependent
81 : NestedNameSpecifierDependence::None;
82 }
83 case Kind::Type:
84 return toNestedNameSpecifierDependence(D: getAsType()->getDependence());
85 }
86 llvm_unreachable("Invalid NNS Kind!");
87}
88
89/// Print this nested name specifier to the given output
90/// stream.
91void NestedNameSpecifier::print(raw_ostream &OS, const PrintingPolicy &Policy,
92 bool ResolveTemplateArguments,
93 bool PrintFinalScopeResOp) const {
94 switch (getKind()) {
95 case Kind::Namespace: {
96 auto [Namespace, Prefix] = getAsNamespaceAndPrefix();
97 Prefix.print(OS, Policy);
98 if (const auto *NS = dyn_cast<NamespaceDecl>(Val: Namespace)) {
99 assert(!NS->isAnonymousNamespace());
100 OS << NS->getName();
101 } else {
102 OS << cast<NamespaceAliasDecl>(Val: Namespace)->getName();
103 }
104 break;
105 }
106 case Kind::Global:
107 OS << "::";
108 return;
109 case Kind::MicrosoftSuper:
110 OS << "__super";
111 break;
112 case Kind::Type: {
113 PrintingPolicy InnerPolicy(Policy);
114 InnerPolicy.SuppressTagKeyword = true;
115 QualType(getAsType(), 0).print(OS, Policy: InnerPolicy);
116 break;
117 }
118 case Kind::Null:
119 return;
120 }
121 if (PrintFinalScopeResOp)
122 OS << "::";
123}
124
125LLVM_DUMP_METHOD void NestedNameSpecifier::dump(llvm::raw_ostream *OS,
126 const LangOptions *LO) const {
127 print(OS&: OS ? *OS : llvm::errs(), Policy: LO ? *LO : LangOptions());
128}
129
130LLVM_DUMP_METHOD void NestedNameSpecifier::dump(const LangOptions &LO) const {
131 dump(/*OS=*/nullptr, LO: &LO);
132}
133LLVM_DUMP_METHOD void NestedNameSpecifier::dump(llvm::raw_ostream &OS) const {
134 dump(OS: &OS);
135}
136LLVM_DUMP_METHOD void NestedNameSpecifier::dump(llvm::raw_ostream &OS,
137 const LangOptions &LO) const {
138 dump(OS: &OS, LO: &LO);
139}
140
141SourceLocation NestedNameSpecifierLoc::getBeginLoc() const {
142 if (!Qualifier)
143 return SourceLocation();
144
145 NestedNameSpecifierLoc First = *this;
146 while (NestedNameSpecifierLoc Prefix = First.getAsNamespaceAndPrefix().Prefix)
147 First = Prefix;
148 return First.getLocalSourceRange().getBegin();
149}
150
151static void Append(char *Start, char *End, char *&Buffer, unsigned &BufferSize,
152 unsigned &BufferCapacity) {
153 if (Start == End)
154 return;
155
156 if (BufferSize + (End - Start) > BufferCapacity) {
157 // Reallocate the buffer.
158 unsigned NewCapacity = std::max(
159 a: (unsigned)(BufferCapacity ? BufferCapacity * 2 : sizeof(void *) * 2),
160 b: (unsigned)(BufferSize + (End - Start)));
161 if (!BufferCapacity) {
162 char *NewBuffer = static_cast<char *>(llvm::safe_malloc(Sz: NewCapacity));
163 if (Buffer)
164 memcpy(dest: NewBuffer, src: Buffer, n: BufferSize);
165 Buffer = NewBuffer;
166 } else {
167 Buffer = static_cast<char *>(llvm::safe_realloc(Ptr: Buffer, Sz: NewCapacity));
168 }
169 BufferCapacity = NewCapacity;
170 }
171 assert(Buffer && Start && End && End > Start && "Illegal memory buffer copy");
172 memcpy(dest: Buffer + BufferSize, src: Start, n: End - Start);
173 BufferSize += End - Start;
174}
175
176/// Save a source location to the given buffer.
177static void SaveSourceLocation(SourceLocation Loc, char *&Buffer,
178 unsigned &BufferSize, unsigned &BufferCapacity) {
179 SourceLocation::UIntTy Raw = Loc.getRawEncoding();
180 Append(Start: reinterpret_cast<char *>(&Raw),
181 End: reinterpret_cast<char *>(&Raw) + sizeof(Raw), Buffer, BufferSize,
182 BufferCapacity);
183}
184
185/// Save a pointer to the given buffer.
186static void SavePointer(void *Ptr, char *&Buffer, unsigned &BufferSize,
187 unsigned &BufferCapacity) {
188 Append(Start: reinterpret_cast<char *>(&Ptr),
189 End: reinterpret_cast<char *>(&Ptr) + sizeof(void *),
190 Buffer, BufferSize, BufferCapacity);
191}
192
193NestedNameSpecifierLocBuilder::
194NestedNameSpecifierLocBuilder(const NestedNameSpecifierLocBuilder &Other)
195 : Representation(Other.Representation) {
196 if (!Other.Buffer)
197 return;
198
199 if (Other.BufferCapacity == 0) {
200 // Shallow copy is okay.
201 Buffer = Other.Buffer;
202 BufferSize = Other.BufferSize;
203 return;
204 }
205
206 // Deep copy
207 Append(Start: Other.Buffer, End: Other.Buffer + Other.BufferSize, Buffer, BufferSize,
208 BufferCapacity);
209}
210
211NestedNameSpecifierLocBuilder::NestedNameSpecifierLocBuilder(
212 NestedNameSpecifierLocBuilder &&Other)
213 : Representation(std::move(Other.Representation)),
214 Buffer(std::exchange(obj&: Other.Buffer, new_val: nullptr)),
215 BufferSize(std::exchange(obj&: Other.BufferSize, new_val: 0)),
216 BufferCapacity(std::exchange(obj&: Other.BufferCapacity, new_val: 0)) {}
217
218NestedNameSpecifierLocBuilder &
219NestedNameSpecifierLocBuilder::
220operator=(const NestedNameSpecifierLocBuilder &Other) {
221 Representation = Other.Representation;
222
223 if (Buffer && Other.Buffer && BufferCapacity >= Other.BufferSize) {
224 // Re-use our storage.
225 BufferSize = Other.BufferSize;
226 memcpy(dest: Buffer, src: Other.Buffer, n: BufferSize);
227 return *this;
228 }
229
230 // Free our storage, if we have any.
231 if (BufferCapacity) {
232 free(ptr: Buffer);
233 BufferCapacity = 0;
234 }
235
236 if (!Other.Buffer) {
237 // Empty.
238 Buffer = nullptr;
239 BufferSize = 0;
240 return *this;
241 }
242
243 if (Other.BufferCapacity == 0) {
244 // Shallow copy is okay.
245 Buffer = Other.Buffer;
246 BufferSize = Other.BufferSize;
247 return *this;
248 }
249
250 // Deep copy.
251 BufferSize = 0;
252 Append(Start: Other.Buffer, End: Other.Buffer + Other.BufferSize, Buffer, BufferSize,
253 BufferCapacity);
254 return *this;
255}
256
257NestedNameSpecifierLocBuilder &NestedNameSpecifierLocBuilder::operator=(
258 NestedNameSpecifierLocBuilder &&Other) {
259 Representation = std::move(Other.Representation);
260
261 // Free our storage, if we have any.
262 if (BufferCapacity) {
263 free(ptr: Buffer);
264 }
265 Buffer = std::exchange(obj&: Other.Buffer, new_val: nullptr);
266 BufferSize = std::exchange(obj&: Other.BufferSize, new_val: 0);
267 BufferCapacity = std::exchange(obj&: Other.BufferCapacity, new_val: 0);
268
269 return *this;
270}
271
272void NestedNameSpecifierLocBuilder::Make(ASTContext &Context, TypeLoc TL,
273 SourceLocation ColonColonLoc) {
274 assert(!Representation);
275 Representation = NestedNameSpecifier(TL.getTypePtr());
276
277 // Push source-location info into the buffer.
278 SavePointer(Ptr: TL.getOpaqueData(), Buffer, BufferSize, BufferCapacity);
279 SaveSourceLocation(Loc: ColonColonLoc, Buffer, BufferSize, BufferCapacity);
280}
281
282void NestedNameSpecifierLocBuilder::Extend(ASTContext &Context,
283 const NamespaceBaseDecl *Namespace,
284 SourceLocation NamespaceLoc,
285 SourceLocation ColonColonLoc) {
286 Representation = NestedNameSpecifier(Context, Namespace, Representation);
287
288 // Push source-location info into the buffer.
289 SaveSourceLocation(Loc: NamespaceLoc, Buffer, BufferSize, BufferCapacity);
290 SaveSourceLocation(Loc: ColonColonLoc, Buffer, BufferSize, BufferCapacity);
291}
292
293void NestedNameSpecifierLocBuilder::MakeGlobal(ASTContext &Context,
294 SourceLocation ColonColonLoc) {
295 assert(!Representation && "Already have a nested-name-specifier!?");
296 Representation = NestedNameSpecifier::getGlobal();
297
298 // Push source-location info into the buffer.
299 SaveSourceLocation(Loc: ColonColonLoc, Buffer, BufferSize, BufferCapacity);
300}
301
302void NestedNameSpecifierLocBuilder::MakeMicrosoftSuper(
303 ASTContext &Context, CXXRecordDecl *RD, SourceLocation SuperLoc,
304 SourceLocation ColonColonLoc) {
305 Representation = NestedNameSpecifier(RD);
306
307 // Push source-location info into the buffer.
308 SaveSourceLocation(Loc: SuperLoc, Buffer, BufferSize, BufferCapacity);
309 SaveSourceLocation(Loc: ColonColonLoc, Buffer, BufferSize, BufferCapacity);
310}
311
312void NestedNameSpecifierLocBuilder::PushTrivial(ASTContext &Context,
313 NestedNameSpecifier Qualifier,
314 SourceRange R) {
315 // Construct bogus (but well-formed) source information for the
316 // nested-name-specifier.
317 switch (Qualifier.getKind()) {
318 case NestedNameSpecifier::Kind::Null:
319 return;
320 case NestedNameSpecifier::Kind::Namespace: {
321 auto [_1, Prefix] = Qualifier.getAsNamespaceAndPrefix();
322 PushTrivial(Context, Qualifier: Prefix, R: R.getBegin());
323 SaveSourceLocation(Loc: R.getBegin(), Buffer, BufferSize, BufferCapacity);
324 break;
325 }
326 case NestedNameSpecifier::Kind::Type: {
327 TypeSourceInfo *TSInfo = Context.getTrivialTypeSourceInfo(
328 T: QualType(Qualifier.getAsType(), 0), Loc: R.getBegin());
329 SavePointer(Ptr: TSInfo->getTypeLoc().getOpaqueData(), Buffer, BufferSize,
330 BufferCapacity);
331 break;
332 }
333 case NestedNameSpecifier::Kind::Global:
334 case NestedNameSpecifier::Kind::MicrosoftSuper:
335 break;
336 }
337 SaveSourceLocation(Loc: R.getEnd(), Buffer, BufferSize, BufferCapacity);
338}
339
340void NestedNameSpecifierLocBuilder::Adopt(NestedNameSpecifierLoc Other) {
341 if (BufferCapacity)
342 free(ptr: Buffer);
343
344 if (!Other) {
345 Representation = std::nullopt;
346 BufferSize = 0;
347 return;
348 }
349
350 // Rather than copying the data (which is wasteful), "adopt" the
351 // pointer (which points into the ASTContext) but set the capacity to zero to
352 // indicate that we don't own it.
353 Representation = Other.getNestedNameSpecifier();
354 Buffer = static_cast<char *>(Other.getOpaqueData());
355 BufferSize = Other.getDataLength();
356 BufferCapacity = 0;
357}
358
359NestedNameSpecifierLoc
360NestedNameSpecifierLocBuilder::getWithLocInContext(ASTContext &Context) const {
361 if (!Representation)
362 return NestedNameSpecifierLoc();
363
364 // If we adopted our data pointer from elsewhere in the AST context, there's
365 // no need to copy the memory.
366 if (BufferCapacity == 0)
367 return NestedNameSpecifierLoc(Representation, Buffer);
368
369 // FIXME: After copying the source-location information, should we free
370 // our (temporary) buffer and adopt the ASTContext-allocated memory?
371 // Doing so would optimize repeated calls to getWithLocInContext().
372 void *Mem = Context.Allocate(Size: BufferSize, Align: alignof(void *));
373 memcpy(dest: Mem, src: Buffer, n: BufferSize);
374 return NestedNameSpecifierLoc(Representation, Mem);
375}
376