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