InsightsHelpers.cpp
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * C++ Insights, copyright (C) by Andreas Fertig
4 * Distributed under an MIT license. See LICENSE for details
5 *
6 ****************************************************************************/
7
8#include "InsightsHelpers.h"
9#include "ASTHelpers.h"
10#include "ClangCompat.h"
11#include "CodeGenerator.h"
12#include "DPrint.h"
13#include "Insights.h"
15#include "OutputFormatHelper.h"
16#include "clang/Frontend/CompilerInstance.h"
17#include "clang/Sema/Lookup.h"
18//-----------------------------------------------------------------------------
19
20namespace clang::insights {
21
23: mStack{mGlobalStack}
24, mHelper{mScope.length()}
25{
26 mStack.push(mHelper);
27
28 if(const auto* recordDecl = dyn_cast_or_null<CXXRecordDecl>(d)) {
29 mScope.append(GetName(*recordDecl));
30
31 if(const auto* classTmplSpec = dyn_cast_or_null<ClassTemplateSpecializationDecl>(recordDecl)) {
33 CodeGenerator codeGenerator{ofm};
34 codeGenerator.InsertTemplateArgs(*classTmplSpec);
35
36 mScope.append(ofm);
37 }
38
39 } else if(const auto* namespaceDecl = dyn_cast_or_null<NamespaceDecl>(d)) {
40 mScope.append(namespaceDecl->getName());
41 }
42
43 if(not mScope.empty()) {
44 mScope.append("::");
45 }
46}
47//-----------------------------------------------------------------------------
48
50{
51 const auto length = mStack.pop()->mLength;
52 mScope.resize(length);
53}
54//-----------------------------------------------------------------------------
55
56std::string ScopeHandler::RemoveCurrentScope(std::string name)
57{
58 if(mScope.length()) {
59 auto findAndReplace = [&name](const std::string& scope) {
60 if(const auto startPos = name.find(scope, 0); std::string::npos != startPos) {
61 if(const auto pos = startPos + scope.length();
62 (pos > name.length()) or (name[pos] != '*')) { // keep member points (See #374)
63 name.replace(startPos, scope.length(), ""sv);
64 return true;
65 }
66 }
67
68 return false;
69 };
70
71 // The default is that we can replace the entire scope. Suppose we are currently in N::X and having a symbol
72 // N::X::y then N::X:: is removed.
73 if(not findAndReplace(mScope)) {
74
75 // A special case where we need to remove the scope without the last item.
76 std::string tmp{mScope};
77 tmp.resize(mGlobalStack.back().mLength);
78
79 findAndReplace(tmp);
80 }
81 }
82
83 return name;
84}
85//-----------------------------------------------------------------------------
86
87static std::string GetNamePlain(const NamedDecl& decl)
88{
89 if(const auto* fd = dyn_cast_or_null<FunctionDecl>(&decl); fd and GetInsightsOptions().UseShow2C) {
90 if(fd->isOverloadedOperator()) {
91 switch(fd->getOverloadedOperator()) {
92#define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemberOnly) \
93 case OO_##Name: return "operator" #Name;
94
95#include "clang/Basic/OperatorKinds.def"
96
97#undef OVERLOADED_OPERATOR
98 default: break;
99 }
100 }
101 }
102
103 return decl.getDeclName().getAsString();
104}
105
106std::string GetPlainName(const DeclRefExpr& DRE)
107{
108 return ScopeHandler::RemoveCurrentScope(GetNamePlain(*DRE.getDecl()));
109}
110//-----------------------------------------------------------------------------
111
112STRONG_BOOL(InsightsSuppressScope);
113//-----------------------------------------------------------------------------
114
115STRONG_BOOL(InsightsCanonicalTypes);
116//-----------------------------------------------------------------------------
117
118static std::string GetUnqualifiedScopelessName(const Type* type, const InsightsSuppressScope supressScope);
119//-----------------------------------------------------------------------------
120
122{
123 unsigned CppInsightsUnqualified : 1; // NOLINT
124 InsightsSuppressScope CppInsightsSuppressScope; // NOLINT
125
126 CppInsightsPrintingPolicy(const Unqualified unqualified,
127 const InsightsSuppressScope supressScope,
128 const InsightsCanonicalTypes insightsCanonicalTypes = InsightsCanonicalTypes::No)
129 : PrintingPolicy{LangOptions{}}
130 {
131 adjustForCPlusPlus();
132 SuppressUnwrittenScope = true;
133 Alignof = true;
134 ConstantsAsWritten = true;
135#if IS_CLANG_NEWER_THAN(22)
136 AnonymousTagNameStyle = llvm::to_underlying(
137 AnonymousTagMode::Plain); // does remove filename and line for from lambdas in parameters
138#else
139 AnonymousTagLocations = false; // does remove filename and line for from lambdas in parameters
140#endif
141#if IS_CLANG_NEWER_THAN(20)
142 PrintAsCanonical
143#else
144 PrintCanonicalTypes
145
146#endif
147 = InsightsCanonicalTypes::Yes == insightsCanonicalTypes;
148
149 CppInsightsUnqualified = (Unqualified::Yes == unqualified);
150 CppInsightsSuppressScope = supressScope;
151 }
152
154 : CppInsightsPrintingPolicy{Unqualified::No, InsightsSuppressScope::No}
155 {
156 }
157};
158//-----------------------------------------------------------------------------
159
160void ReplaceAll(std::string& str, std::string_view from, std::string_view to)
161{
162 size_t start_pos = 0;
163 while((start_pos = str.find(from, start_pos)) != std::string::npos) {
164 str.replace(start_pos, from.length(), to);
165 start_pos += to.length(); // Handles case where 'to' is a substring of 'from'
166 }
167}
168//-----------------------------------------------------------------------------
169
170namespace details {
171static void BuildNamespace(std::string& fullNamespace,
172 const NestedNameSpecifier
173#if not IS_CLANG_NEWER_THAN(22)
174 *
175#endif
176 stmt,
177 const IgnoreNamespace ignoreNamespace)
178{
179 RETURN_IF(not stmt);
180
181#if not IS_CLANG_NEWER_THAN(22)
182 if(const auto* prefix = stmt->getPrefix()) {
183 BuildNamespace(fullNamespace, prefix, ignoreNamespace);
184 }
185
186 switch(stmt->getKind()) {
187 case NestedNameSpecifier::Identifier: fullNamespace.append(stmt->getAsIdentifier()->getName()); break;
188
189 case NestedNameSpecifier::Namespace:
190 RETURN_IF((IgnoreNamespace::Yes == ignoreNamespace) or (stmt->getAsNamespace()->isAnonymousNamespace()));
191
192 fullNamespace.append(stmt->getAsNamespace()->getName());
193 break;
194
195 case NestedNameSpecifier::NamespaceAlias: fullNamespace.append(stmt->getAsNamespaceAlias()->getName()); break;
196#if IS_CLANG_NEWER_THAN(20)
197#else
198 case NestedNameSpecifier::TypeSpecWithTemplate:
199 if(auto* dependentSpecType = stmt->getAsType()->getAs<DependentTemplateSpecializationType>()) {
200 fullNamespace.append(GetElaboratedTypeKeyword(dependentSpecType->getKeyword()));
201 }
202
203 [[fallthrough]];
204#endif
205 case NestedNameSpecifier::TypeSpec:
206 fullNamespace.append(GetUnqualifiedScopelessName(stmt->getAsType(), InsightsSuppressScope::Yes));
207#else
208 switch(stmt.getKind()) {
209 case NestedNameSpecifier::Kind::Namespace: {
210 const auto [ns, prefix] = stmt.getAsNamespaceAndPrefix();
211
212 BuildNamespace(fullNamespace, prefix, ignoreNamespace);
213
214 RETURN_IF((IgnoreNamespace::Yes == ignoreNamespace) or
215 (isa<NamespaceDecl>(ns) and cast<NamespaceDecl>(ns)->isAnonymousNamespace()));
216
217 fullNamespace.append(ns->getName());
218 } break;
219
220 case NestedNameSpecifier::Kind::Type:
221 fullNamespace.append(GetUnqualifiedScopelessName(stmt.getAsType(), InsightsSuppressScope::Yes));
222#endif
223 // The template parameters are already contained in the type we inserted above.
224 break;
225
226 default: break;
227 }
228
229 fullNamespace.append("::"sv);
230}
231//-----------------------------------------------------------------------------
232} // namespace details
233
234std::string GetNestedName(const NestedNameSpecifier
235#if not IS_CLANG_NEWER_THAN(22)
236 *
237#endif
238 nns,
239 const IgnoreNamespace ignoreNamespace)
240{
241 std::string ret{};
242
243 if(nns) {
244 details::BuildNamespace(ret, nns, ignoreNamespace);
245 }
246
247 return ret;
248}
249//-----------------------------------------------------------------------------
250
251static const std::string GetAsCPPStyleString(const QualType& t, const CppInsightsPrintingPolicy& printingPolicy)
252{
253 return t.getAsString(printingPolicy);
254}
255//-----------------------------------------------------------------------------
256
257std::string BuildInternalVarName(const std::string_view& varName)
258{
259 return StrCat("__", varName);
260}
261//-----------------------------------------------------------------------------
262
263static std::string
264BuildInternalVarName(const std::string_view& varName, const SourceLocation& loc, const SourceManager& sm)
265{
266 const auto lineNo = sm.getSpellingLineNumber(loc);
267
268 return StrCat(BuildInternalVarName(varName), lineNo);
269}
270//-----------------------------------------------------------------------------
271
272void InsertBefore(std::string& source, const std::string_view& find, const std::string_view& replace)
273{
274 const std::string::size_type i = source.find(find, 0);
275
276 if(std::string::npos != i) {
277 source.insert(i, replace);
278 }
279}
280//-----------------------------------------------------------------------------
281
282static void InsertAfter(std::string& source, const std::string_view& find, const std::string_view& replace)
283{
284 const std::string::size_type i = source.find(find, 0);
285
286 if(std::string::npos != i) {
287 source.insert(i + find.length(), replace);
288 }
289}
290//-----------------------------------------------------------------------------
291
292std::string MakeLineColumnName(const SourceManager& sm, const SourceLocation& loc, const std::string_view& prefix)
293{
294 // In case of a macro expansion the expansion(line/column) number gives a unique value.
295 const auto lineNo = loc.isMacroID() ? sm.getExpansionLineNumber(loc) : sm.getSpellingLineNumber(loc);
296 const auto columnNo = loc.isMacroID() ? sm.getExpansionColumnNumber(loc) : sm.getSpellingColumnNumber(loc);
297
298 return StrCat(prefix, lineNo, "_"sv, columnNo);
299}
300//-----------------------------------------------------------------------------
301
302static std::string MakeLineColumnName(const Decl& decl, const std::string_view prefix)
303{
304 return MakeLineColumnName(GetSM(decl), decl.getBeginLoc(), prefix);
305}
306//-----------------------------------------------------------------------------
307
308std::string GetLambdaName(const CXXRecordDecl& lambda)
309{
310 static constexpr auto lambdaPrefix{"__lambda_"sv};
311 return MakeLineColumnName(lambda, lambdaPrefix);
312}
313//-----------------------------------------------------------------------------
314
315static std::string GetAnonymStructOrUnionName(const CXXRecordDecl& cxxRecordDecl)
316{
317 static constexpr auto prefix{"__anon_"sv};
318 return MakeLineColumnName(cxxRecordDecl, prefix);
319}
320//-----------------------------------------------------------------------------
321
322std::string BuildRetTypeName(const Decl& decl)
323{
324 static constexpr auto retTypePrefix{"retType_"sv};
325 return MakeLineColumnName(decl, retTypePrefix);
326}
327//-----------------------------------------------------------------------------
328
329const QualType GetDesugarType(const QualType& QT)
330{
331 if(QT.getTypePtrOrNull()) {
332 if(const auto* autoType = QT->getAs<clang::AutoType>(); autoType and autoType->isSugared()) {
333 const auto dt = autoType->getDeducedType();
334
335#if IS_CLANG_NEWER_THAN(22)
336 return dt;
337#else
338 if(const auto* et = dt->getAs<ElaboratedType>()) {
339 return et->getNamedType();
340 } else {
341 return dt;
342 }
343#endif
344
345 } else if(auto declType = QT->getAs<clang::DecltypeType>()) {
346 return declType->desugar();
347 }
348 }
349 return QT;
350}
351//-----------------------------------------------------------------------------
352
353static const VarDecl* GetVarDeclFromDeclRefExpr(const DeclRefExpr& declRefExpr)
354{
355 const auto* valueDecl = declRefExpr.getDecl();
356
357 return dyn_cast_or_null<VarDecl>(valueDecl);
358}
359//-----------------------------------------------------------------------------
360
361// own implementation due to lambdas
362std::string GetDeclContext(const DeclContext* ctx, WithTemplateParameters withTemplateParameters)
363{
364 OutputFormatHelper mOutputFormatHelper{};
365 SmallVector<const DeclContext*, 8> contexts{};
366
367 while(ctx) {
368 if(isa<NamedDecl>(ctx)) {
369 contexts.push_back(ctx);
370 }
371 ctx = ctx->getParent();
372 }
373
374 for(const auto* declContext : llvm::reverse(contexts)) {
375 if(const auto* classTmplSpec = dyn_cast<ClassTemplateSpecializationDecl>(declContext)) {
376 mOutputFormatHelper.Append(classTmplSpec->getName());
377
378 CodeGenerator codeGenerator{mOutputFormatHelper};
379 codeGenerator.InsertTemplateArgs(*classTmplSpec);
380
381 } else if(const auto* nd = dyn_cast<NamespaceDecl>(declContext)) {
382 if(nd->isAnonymousNamespace() or nd->isInline()) {
383 continue;
384 }
385
386 mOutputFormatHelper.Append(nd->getName());
387
388 } else if(const auto* rd = dyn_cast<RecordDecl>(declContext)) {
389 if(not rd->getIdentifier()) {
390 continue;
391 }
392
393 mOutputFormatHelper.Append(rd->getName());
394
395 // A special case at least for out-of-line static member variables of a class template. They need to carry
396 // the template parameters of the class template.
397 if(WithTemplateParameters::Yes == withTemplateParameters /*declContext->isNamespace() or declContext->getLexicalParent()->isNamespace() or declContext->getLexicalParent()->isTranslationUnit()*/) {
398 if(const auto* cxxRecordDecl = dyn_cast_or_null<CXXRecordDecl>(rd)) {
399 if(const auto* classTmpl = cxxRecordDecl->getDescribedClassTemplate()) {
400 CodeGenerator codeGenerator{mOutputFormatHelper};
401 codeGenerator.InsertTemplateParameters(*classTmpl->getTemplateParameters(),
402 CodeGenerator::TemplateParamsOnly::Yes);
403 }
404 }
405 }
406
407 } else if(dyn_cast<FunctionDecl>(declContext)) {
408 continue;
409
410 } else if(const auto* ed = dyn_cast<EnumDecl>(declContext)) {
411 if(not ed->isScoped()) {
412 continue;
413 }
414
415 mOutputFormatHelper.Append(ed->getName());
416
417 } else {
418 mOutputFormatHelper.Append(cast<NamedDecl>(declContext)->getName());
419 }
420
421 mOutputFormatHelper.Append("::"sv);
422 }
423
424 return mOutputFormatHelper.GetString();
425}
426//-----------------------------------------------------------------------------
427
428namespace details {
429
430STRONG_BOOL(RemoveCurrentScope); ///!< In some cases we need to keep the scope for a while, so don't remove the scope
431 /// we are in right now.
432//-----------------------------------------------------------------------------
433
434static std::string GetQualifiedName(const NamedDecl& decl,
435 const RemoveCurrentScope removeCurrentScope = RemoveCurrentScope::Yes)
436{
437 std::string scope{GetDeclContext(decl.getDeclContext())};
438
439 scope += decl.getName();
440
441 if(RemoveCurrentScope::Yes == removeCurrentScope) {
442 return ScopeHandler::RemoveCurrentScope(scope);
443 }
444
445 return scope;
446}
447//-----------------------------------------------------------------------------
448
449static std::string GetScope(const DeclContext* declCtx,
450 const RemoveCurrentScope removeCurrentScope = RemoveCurrentScope::Yes)
451{
452 std::string name{};
453
454 if(not declCtx->isTranslationUnit() and not declCtx->isFunctionOrMethod()) {
455 while(declCtx->isInlineNamespace()) {
456 declCtx = declCtx->getParent();
457 }
458
459 if(not declCtx->isTranslationUnit() and (declCtx->isNamespace() or declCtx->getParent()->isTranslationUnit())) {
460 if(const auto* namedDecl = dyn_cast_or_null<NamedDecl>(declCtx)) {
461 name = GetQualifiedName(*namedDecl, removeCurrentScope);
462 name.append("::"sv);
463 }
464 }
465 }
466
467 return name;
468}
469//-----------------------------------------------------------------------------
470
471/// \brief SimpleTypePrinter a partially substitution of Clang's TypePrinter.
472///
473/// With Clang 9 there seems to be a change in `lib/AST/TypePrinter.cpp` in `printTemplateTypeParmBefore`. It now
474/// inserts `auto` when it is a lambda auto parameter. Which is correct, but C++ Insights needs the
475/// template parameter name to make up compiling code. Hence, this `if` "overrides" the implementation of
476/// TypePrinter in that case.
477///
478/// It also drops some former code which handled `ClassTemplateSpecializationDecl` in a special way. Here the template
479/// parameters can be lambdas. Those they need a proper name.
481{
482private:
483 const QualType& mType;
484 const CppInsightsPrintingPolicy& mPrintingPolicy;
485 OutputFormatHelper mData{};
486 std::string mDataAfter{};
487 bool mHasData{false};
488 bool mSkipSpace{false};
489 bool mScanningArrayDimension{}; //!< Only the outer most ConstantArrayType handles the array dimensions and size
490 std::string mScope{}; //!< A scope coming from an ElaboratedType which is used for a
491 //!< ClassTemplateSpecializationDecl if there is no other scope
492 bool mTmplParamTypeRes{}; //!< When processing FunctionProtoType parameters we need to get the identifiers
493
494 bool HandleType(const TemplateTypeParmType* type)
495 {
496 if(const TemplateTypeParmDecl* decl = type->getDecl();
497 (nullptr == type->getIdentifier()) or
498 (decl and decl->isImplicit()) /* this fixes auto operator()(type_parameter_0_0 container) const */) {
499
500 AppendTemplateTypeParamName(mData, decl, true, type);
501
502 return true;
503
504 } else if(nullptr != type->getIdentifier()) {
505 mData.Append(type->getIdentifier()->getName());
506 }
507
508 return mTmplParamTypeRes;
509 }
510
511 bool HandleType(const LValueReferenceType* type)
512 {
513 mDataAfter += " &"sv;
514
515 return HandleType(type->getPointeeType().getTypePtrOrNull());
516 }
517
518 bool HandleType(const RValueReferenceType* type)
519 {
520 mDataAfter += " &&"sv;
521
522 return HandleType(type->getPointeeType().getTypePtrOrNull());
523 }
524
525 bool HandleType(const PointerType* type)
526 {
527 const bool ret = HandleType(type->getPointeeType().getTypePtrOrNull());
528
529 if(not mData.empty() and (mData.GetString().back() != ' ') and not isa<ParenType>(type->getPointeeType())) {
530 mData.Append(' ');
531 }
532
533 mData.Append("*"sv);
534
535 HandleTypeAfter(type);
536
537 return ret;
538 }
539
540 bool HandleType(const InjectedClassNameType* type)
541 {
542 return
543#if IS_CLANG_NEWER_THAN(22)
544 HandleType(type->getDecl()->getCanonicalTemplateSpecializationType(GetGlobalAST()).getTypePtr());
545#else
546 HandleType(type->getInjectedTST());
547#endif
548 }
549
550 bool HandleType(const RecordType* type)
551 {
552 OutputFormatHelper ofm{};
553 FinalAction _{[&] { mData.Append(ofm); }};
554
555 if(const auto* tt = dyn_cast_or_null<ClassTemplateSpecializationDecl>(type->getDecl())) {
556 // Always get potential template arguments
557 CodeGenerator codeGenerator{ofm};
558 codeGenerator.InsertTemplateArgs(*tt);
559
560 /// In case one of the template parameters is a lambda we need to insert the made up name.
561 if(const auto* identifierName = mType.getBaseTypeIdentifier()) {
562 const auto& scope = GetScope(type->getDecl()->getDeclContext());
563
564 // If we don't have a scope with GetScope use a possible one from ElaboratedType
565 if((InsightsSuppressScope::Yes == mPrintingPolicy.CppInsightsSuppressScope) or scope.empty()) {
566 mData.Append(mScope);
567 } else {
568 mData.Append(scope);
569 }
570
571 mData.Append(identifierName->getName());
572
573 return true;
574 }
575 }
576
577 if(const auto* cxxRecordDecl = type->getAsCXXRecordDecl()) {
578 // Special handling for dependent types. For example, ClassOperatorHandler7Test.cpp A<...* >::B.
579 if(type->isDependentType()) {
580 std::string context{GetDeclContext(type->getDecl()->getDeclContext())};
581
582 if(not context.empty()) {
583 mData.Append(std::move(context), cxxRecordDecl->getName());
584
585 return true;
586 }
587 }
588
589 if(cxxRecordDecl->isLambda()) {
590 mData.Append(GetLambdaName(*cxxRecordDecl));
591
592 return true;
593 }
594
595 // Handle anonymous struct or union.
596 else if(IsAnonymousStructOrUnion(cxxRecordDecl)) {
597 mData.Append(GetAnonymStructOrUnionName(*cxxRecordDecl));
598
599 return true;
600 }
601
602 // we need a name here, as DecltypeType always says true
603#if IS_CLANG_NEWER_THAN(22)
604 // A deduced type carries no written scope, so Clang's printer drops it. Add it here.
605 if(not type->getQualifier()) {
606 if(const auto scope = GetScope(cxxRecordDecl->getDeclContext(), RemoveCurrentScope::No);
607 cxxRecordDecl->getDeclContext()->isNamespace() and not scope.empty()) {
608 mData.Append(ScopeHandler::RemoveCurrentScope(scope + std::string{cxxRecordDecl->getName()}));
609
610 return true;
611 }
612
613 // A member class of a class template specialization, for example `task<int>::promise_type`.
614 std::string memberPath{cxxRecordDecl->getName().str()};
615 const CXXRecordDecl* ctx = dyn_cast_or_null<CXXRecordDecl>(cxxRecordDecl->getDeclContext());
616
617 while(ctx and not isa<ClassTemplateSpecializationDecl>(ctx)) {
618 memberPath.insert(0, ctx->getName().str() + "::");
619 ctx = dyn_cast_or_null<CXXRecordDecl>(ctx->getDeclContext());
620 }
621
622 if(ctx) {
623 mData.Append(ScopeHandler::RemoveCurrentScope(
624 QualType{GetGlobalAST().getCanonicalTagType(ctx)}.getAsString(mPrintingPolicy) +
625 "::" + memberPath));
626
627 return true;
628 }
629 }
630 // A scope as written, for example `task<int>::awaiter`.
631 if(const auto qualifier = type->getQualifier(); qualifier) {
632 mData.Append(GetNestedName(qualifier));
633 }
634#endif
635
636 mData.Append(GetName(*cxxRecordDecl));
637 }
638
639 return false;
640 }
641
642 bool HandleType(const AutoType* type) { return HandleType(type->getDeducedType().getTypePtrOrNull()); }
643
644 bool HandleType(const SubstTemplateTypeParmType* type)
645 {
646 return HandleType(type->getReplacementType().getTypePtrOrNull());
647 }
648
649#if not IS_CLANG_NEWER_THAN(22)
650 bool HandleType(const ElaboratedType* type)
651 {
652 const IgnoreNamespace ignoreNamespace = (mPrintingPolicy.CppInsightsSuppressScope == InsightsSuppressScope::Yes)
653 ? IgnoreNamespace::No
654 : IgnoreNamespace::Yes;
655
656 mScope = GetNestedName(type->getQualifier(), ignoreNamespace);
657
658 const bool ret = HandleType(type->getNamedType().getTypePtrOrNull());
659
660 mScope.clear();
661
662 return ret;
663 }
664
665 bool HandleType(const DependentTemplateSpecializationType* type)
666 {
667 mData.Append(GetElaboratedTypeKeyword(type->getKeyword()),
668 GetNestedName(type->getDependentTemplateName().getQualifier()),
670 GetName(type->getDependentTemplateName()));
671
672 CodeGenerator codeGenerator{mData};
673 codeGenerator.InsertTemplateArgs(*type);
674
675 return true;
676 }
677#endif
678
679 bool HandleType(const DeducedTemplateSpecializationType* type)
680 {
681 return HandleType(type->getDeducedType().getTypePtrOrNull());
682 }
683
684 bool HandleType(const TemplateSpecializationType* type)
685 {
686 if(type->getAsRecordDecl()) {
687#if IS_CLANG_NEWER_THAN(22)
688 // A scope written in front of the template name is not part of the canonical record type.
689 const IgnoreNamespace ignoreNamespace =
690 (mPrintingPolicy.CppInsightsSuppressScope == InsightsSuppressScope::Yes) ? IgnoreNamespace::No
691 : IgnoreNamespace::Yes;
692 BackupAndRestore _{mScope, GetNestedName(type->getTemplateName().getQualifier(), ignoreNamespace)};
693#endif
694
695 // Only if it was some sort of "used" `RecordType` we continue here.
696 if(HandleType(
698 GetGlobalAST().getCanonicalTagType(type->getAsRecordDecl()).getTypePtr()
699#else
700 type->getAsRecordDecl()->getTypeForDecl()
701#endif
702 )) {
703 HandleType(type->getPointeeType().getTypePtrOrNull());
704 return true;
705 }
706 }
707
708 return false;
709 }
710
711 bool HandleType(const MemberPointerType* type)
712 {
713 const auto* pointeeType{type->getPointeeType().getTypePtrOrNull()};
714 {
715 BackupAndRestore _{mSkipSpace,
716 (not type->isMemberFunctionPointer() and (isa<TemplateTypeParmType>(pointeeType) or
717 isa<SubstTemplateTypeParmType>(pointeeType)))};
718 HandleType(pointeeType);
719 }
720
721 // Other types seem to add the otherwise missing space after the typename
722 if(not type->isMemberFunctionPointer() and
723 (isa<TemplateTypeParmType>(pointeeType) or isa<SubstTemplateTypeParmType>(pointeeType))) {
724 mData.Append(' ');
725 }
726
727 const bool ret =
728#if IS_CLANG_NEWER_THAN(20)
729 true;
730
731 mData.Append(GetNestedName(type->getQualifier()));
732
733 mData.Append("*"sv);
734#else
735 HandleType(type->getClass());
736
737 mData.Append("::*)"sv);
738#endif
739
740 HandleTypeAfter(pointeeType);
741
742 return ret;
743 }
744
745 bool HandleType(const FunctionProtoType* type)
746 {
747 BackupAndRestore _{mSkipSpace, true};
748
749 const bool ret = HandleType(type->getReturnType().getTypePtrOrNull());
750 mData.Append(" ("sv);
751
752 return ret;
753 }
754
755 bool HandleType(const ParenType* type)
756 {
757 const bool ret = HandleType(type->getInnerType().getTypePtrOrNull());
758 if(not isa<FunctionType>(type->getInnerType())) {
759 mData.Append('(');
760 }
761
762 return ret;
763 }
764
765 void HandleTypeAfter(const PointerType* type) { HandleTypeAfter(type->getPointeeType().getTypePtrOrNull()); }
766
767 void HandleTypeAfter(const ParenType* type)
768 {
769 if(not isa<FunctionType>(type->getInnerType())) {
770 mData.Append(')');
771 }
772
773 HandleTypeAfter(type->getInnerType().getTypePtrOrNull());
774 }
775
776 void HandleTypeAfter(const FunctionProtoType* type)
777 {
778 BackupAndRestore _{mTmplParamTypeRes, true};
779 BackupAndRestore _1{mSkipSpace, true};
780
781 mData.Append(")("sv);
782
783 for(OnceFalse needsComma{}; const auto& t : type->getParamTypes()) {
784 if(needsComma) {
785 mData.Append(", "sv);
786 }
787
788 HandleType(t.getTypePtrOrNull());
789 }
790
791 mData.Append(')');
792
793 if(not type->getMethodQuals().empty()) {
794 mData.Append(" "sv, type->getMethodQuals().getAsString());
795 }
796
797 /// Currently, we are skipping `T->getRefQualifier()` and the exception specification, as well as the trailing
798 /// return type.
799 }
800
801 bool HandleType(const BuiltinType* type)
802 {
803 mData.Append(type->getName(mPrintingPolicy));
804
805 if(not mSkipSpace) {
806 mData.Append(' ');
807 }
808
809 return false;
810 }
811
812#if IS_CLANG_NEWER_THAN(22)
813 bool HandleType(const PredefinedSugarType* type)
814 {
815 // Clang prints the sugar as `__size_t`, but we need the builtin type.
816 mData.Append(type->desugar().getCanonicalType().getAsString(mPrintingPolicy));
817
818 return true;
819 }
820#endif
821
822 bool HandleType(const TypedefType* type)
823 {
824#if IS_CLANG_NEWER_THAN(22)
825 // Clang adds a type suffix like `UL` to integral template arguments whose parameter type is a sugar like
826 // `size_t`. Print such a member typedef of a template specialization ourselves.
827 if(const auto* tt = dyn_cast_or_null<ClassTemplateSpecializationDecl>(type->getDecl()->getDeclContext());
828 tt and not mPrintingPolicy.PrintAsCanonical and
829 (NestedNameSpecifier::Kind::Type == type->getQualifier().getKind()) and
830 llvm::any_of(tt->getTemplateArgs().asArray(),
831 [](const auto& arg) { return TemplateArgument::Integral == arg.getKind(); })) {
832 OutputFormatHelper ofm{};
833 CodeGenerator codeGenerator{ofm};
834 codeGenerator.InsertTemplateArgs(tt->getTemplateArgs().asArray());
835
836 // Qualifiers derived from the canonical type are not part of the typedef.
837 mData.GetString().clear();
838 mData.Append(GetScope(tt->getDeclContext(), RemoveCurrentScope::No),
839 tt->getName(),
840 ofm,
841 "::"sv,
842 type->getDecl()->getName());
843
844 return true;
845 }
846#endif
847
848 if(const auto* decl = type->getDecl()) {
849 return HandleType(decl->getUnderlyingType().getTypePtrOrNull());
850 }
851
852 return HandleType(type->getPointeeType().getTypePtrOrNull());
853 }
854
855#if IS_CLANG_NEWER_THAN(22)
856 bool HandleType(const UsingType* type)
857 {
858 const auto* target = type->getDecl()->getTargetDecl();
859
860 // Keep the scope as written, for example the `std::` of `std::jmp_buf`, which is a using-declaration.
861 if(const auto nestedName = GetNestedName(type->getQualifier()); not nestedName.empty()) {
862 mData.Append(nestedName, target->getName());
863
864 return true;
865 }
866
867 mData.Append(GetScope(target->getDeclContext()), target->getName());
868
869 return true;
870 }
871#endif
872
873 bool HandleType(const ConstantArrayType* type)
874 {
875 // Only the outer most ConstantArrayType generates the aary dimensions, block others.
876 bool scanningArrayDimension = false;
877 if(not mScanningArrayDimension) {
878 mScanningArrayDimension = true;
879 scanningArrayDimension = true;
880 }
881
882 const bool ret = HandleType(type->getElementType().getTypePtrOrNull());
883
884 // Handle the array dimension after the type has been parsed.
885 if(scanningArrayDimension) {
886 do {
887 mData.Append("["sv, GetSize(type), "]"sv);
888 } while((type = dyn_cast_or_null<ConstantArrayType>(type->getElementType().getTypePtrOrNull())));
889
890 mScanningArrayDimension = false;
891 }
892
893 return ret;
894 }
895
896 bool HandleType(const PackExpansionType* type)
897 {
898 const bool ret = HandleType(type->getPattern().getTypePtrOrNull());
899
900 if(ret) {
901 mData.Append(kwElipsis);
902 }
903
904 return ret;
905 }
906
907 bool HandleType(const DecltypeType* type)
908 {
909 // A DecltypeType in a template definition is unevaluated and refers ti itself. This check ensures, that in such
910 // a situation no expansion is performed.
911 if(not isa_and_nonnull<DecltypeType>(type->desugar().getTypePtrOrNull())) {
912 BackupAndRestore _{mSkipSpace, true};
913
914 HandleType(type->desugar().getTypePtrOrNull());
915
916 // if we hit a DecltypeType always use the expanded version to support things like a DecltypeType wrapped in
917 // an LValueReferenceType
918 return true;
919 }
920
921 if(not isa_and_nonnull<DeclRefExpr>(type->getUnderlyingExpr())) {
922 P0315Visitor visitor{mData};
923
924 return not visitor.TraverseStmt(type->getUnderlyingExpr());
925 }
926
927 return false;
928 }
929
930 bool HandleType(const Type* type)
931 {
932#define HANDLE_TYPE(t) \
933 if(isa<t>(type)) { \
934 return HandleType(dyn_cast_or_null<t>(type)); \
935 }
936
937 if(nullptr == type) {
938 return false;
939 }
940
941 HANDLE_TYPE(FunctionProtoType);
942 HANDLE_TYPE(PointerType);
943 HANDLE_TYPE(ParenType);
944 HANDLE_TYPE(LValueReferenceType);
945 HANDLE_TYPE(RValueReferenceType);
946 HANDLE_TYPE(TemplateTypeParmType);
947 HANDLE_TYPE(RecordType);
948 HANDLE_TYPE(AutoType);
949 HANDLE_TYPE(SubstTemplateTypeParmType);
950#if not IS_CLANG_NEWER_THAN(22)
951 HANDLE_TYPE(ElaboratedType);
952#endif
953 HANDLE_TYPE(TemplateSpecializationType);
954 HANDLE_TYPE(DeducedTemplateSpecializationType);
955 HANDLE_TYPE(MemberPointerType);
956 HANDLE_TYPE(BuiltinType);
957#if IS_CLANG_NEWER_THAN(22)
958 HANDLE_TYPE(PredefinedSugarType);
959#endif
960 HANDLE_TYPE(TypedefType);
961#if IS_CLANG_NEWER_THAN(22)
962 HANDLE_TYPE(UsingType);
963#endif
964 HANDLE_TYPE(ConstantArrayType);
965 HANDLE_TYPE(InjectedClassNameType);
966#if not IS_CLANG_NEWER_THAN(22)
967 HANDLE_TYPE(DependentTemplateSpecializationType);
968#endif
969 HANDLE_TYPE(PackExpansionType);
970 HANDLE_TYPE(DecltypeType);
971
972#undef HANDLE_TYPE
973 return false;
974 }
975
976 void HandleTypeAfter(const Type* type)
977 {
978#define HANDLE_TYPE(t) \
979 if(isa<t>(type)) { \
980 HandleTypeAfter(dyn_cast_or_null<t>(type)); \
981 }
982
983 if(nullptr != type) {
984 HANDLE_TYPE(PointerType);
985 HANDLE_TYPE(FunctionProtoType);
986 HANDLE_TYPE(ParenType);
987 }
988 }
989
990 void AddCVQualifiers(const Qualifiers& quals)
991 {
992 if((false == mPrintingPolicy.CppInsightsUnqualified) and not quals.empty()) {
993 mData.Append(quals.getAsString());
994
995 if(not mData.empty() and not mSkipSpace) {
996 mData.Append(' ');
997 }
998 }
999 }
1000
1001public:
1002 SimpleTypePrinter(const QualType& qt, const CppInsightsPrintingPolicy& printingPolicy)
1003 : mType{qt}
1004 , mPrintingPolicy{printingPolicy}
1005 {
1006 }
1007
1008 std::string& GetString() { return mData.GetString(); }
1009
1011 {
1012 if(const SplitQualType splitted{mType.split()}; splitted.Quals.empty()) {
1013 const auto& canonicalType = mType.getCanonicalType();
1014
1015 if(canonicalType->getPointeeType().getLocalFastQualifiers()) {
1016 AddCVQualifiers(canonicalType->getPointeeType().getLocalQualifiers());
1017 } else if(canonicalType.getLocalFastQualifiers()) {
1018 AddCVQualifiers(canonicalType.getLocalQualifiers());
1019 }
1020 } else {
1021 AddCVQualifiers(splitted.Quals);
1022 }
1023
1024 const auto* typePtr = mType.getTypePtrOrNull();
1025 mHasData = HandleType(typePtr);
1026 mData.Append(mDataAfter);
1027
1028 // Take care of 'char* const'
1029 if(mType.getQualifiers().hasFastQualifiers()) {
1030 const QualType fastQualifierType{typePtr, mType.getQualifiers().getFastQualifiers()};
1031
1032 mSkipSpace = true;
1033 AddCVQualifiers(fastQualifierType.getCanonicalType()->getPointeeType().getLocalQualifiers());
1034 }
1035
1036 return mHasData;
1037 }
1038};
1039//-----------------------------------------------------------------------------
1040
1041static std::string GetName(QualType t,
1042 const Unqualified unqualified = Unqualified::No,
1043 const InsightsSuppressScope supressScope = InsightsSuppressScope::No)
1044{
1045 const CppInsightsPrintingPolicy printingPolicy{unqualified,
1046 supressScope,
1047 (isa<AutoType>(t.getTypePtrOrNull())) ? InsightsCanonicalTypes::Yes
1048 : InsightsCanonicalTypes::No};
1049
1050 if(SimpleTypePrinter st{t, printingPolicy}; st.GetTypeString()) {
1051 return ScopeHandler::RemoveCurrentScope(st.GetString());
1052 }
1053
1054 // To get the namespace handling right we need to look into the ElaboratedType in some cases.
1055 // However, only do that, if the type it not a dependent type. A dependent type might cause type-parameter stuff.
1056#if not IS_CLANG_NEWER_THAN(22)
1057 if(const auto* et = t->getAs<ElaboratedType>();
1058 et and (nullptr == et->getQualifier()) and (nullptr == et->getOwnedTagDecl()) and not et->isDependentType()) {
1059 const auto quals = t.getLocalFastQualifiers();
1060 t = et->getNamedType();
1061 t.setLocalFastQualifiers(quals);
1062 }
1063#endif
1064
1065 if(true == printingPolicy.CppInsightsUnqualified) {
1066 t = t.getUnqualifiedType();
1067 }
1068
1069 return ScopeHandler::RemoveCurrentScope(GetAsCPPStyleString(t, printingPolicy));
1070}
1071} // namespace details
1072//-----------------------------------------------------------------------------
1073
1074static bool HasOverload(const FunctionDecl* fd)
1075{
1076 auto* ncfd = const_cast<FunctionDecl*>(fd);
1077
1078 Sema& sema = GetGlobalCI().getSema();
1079 LookupResult result{sema, ncfd->getDeclName(), {}, Sema::LookupOrdinaryName};
1080
1081 if(sema.LookupName(result, sema.getScopeForContext(ncfd->getDeclContext()))) {
1082#if IS_CLANG_NEWER_THAN(20)
1083 return LookupResultKind::FoundOverloaded == result.getResultKind();
1084#else
1085 return LookupResult::FoundOverloaded == result.getResultKind();
1086#endif
1087 }
1088
1089 return false;
1090}
1091//-----------------------------------------------------------------------------
1092
1093std::string GetSpecialMemberName(const ValueDecl* vd);
1094//-----------------------------------------------------------------------------
1095
1096std::string GetCfrontOverloadedFunctionName(const FunctionDecl* fd)
1097{
1098 std::string name{};
1099
1100 if(fd and GetInsightsOptions().UseShow2C and HasOverload(fd) and not fd->isMain()) {
1101 name = GetSpecialMemberName(fd);
1102
1103 if(not fd->param_empty()) {
1104 name += "_";
1105
1106 for(const auto& param : fd->parameters()) {
1107 QualType t = param->getType();
1108 QualType plainType = t.getNonReferenceType();
1109 plainType.removeLocalConst();
1110 plainType.removeLocalVolatile();
1111
1112 std::string ptr{};
1113
1114 while(plainType->isPointerType()) {
1115 ptr += "p";
1116 plainType = plainType->getPointeeType();
1117
1118 auto quals = plainType.getQualifiers();
1119 auto lquals = plainType.getLocalQualifiers();
1120
1121 if(quals.hasConst() or lquals.hasConst()) {
1122 ptr += "c";
1123 }
1124
1125 plainType.removeLocalConst();
1126 }
1127
1128 if(t.isCanonical()) {
1129 t = t.getCanonicalType();
1130 }
1131
1132 if(plainType->isBuiltinType() and plainType->hasUnsignedIntegerRepresentation()) {
1133 std::string tmp{GetName(plainType)};
1134
1135 ReplaceAll(tmp, "unsigned ", "u");
1136
1137 name += tmp;
1138 } else {
1139 name += GetName(plainType);
1140 }
1141
1142 auto quals = t.getQualifiers();
1143 auto lquals = t.getLocalQualifiers();
1144
1145 if(t->isPointerType()) {
1146 name += ptr;
1147 }
1148
1149 if(quals.hasConst() or lquals.hasConst()) {
1150 name += "c";
1151 }
1152
1153 if(t->isLValueReferenceType()) {
1154 name += "r";
1155 }
1156
1157 if(t->isRValueReferenceType()) {
1158 name += "R";
1159 }
1160 }
1161 }
1162 } else if(const auto* md = dyn_cast_or_null<CXXMethodDecl>(fd);
1163 md and GetInsightsOptions().UseShow2C and md->isVirtual()) {
1164 name = GetName(*md->getParent());
1165 }
1166
1167 return name;
1168}
1169//-----------------------------------------------------------------------------
1170
1171STRONG_BOOL(UseLexicalParent);
1172//-----------------------------------------------------------------------------
1173
1174static bool NeedsNamespace(const Decl& decl, UseLexicalParent useLexicalParent)
1175{
1176 const auto* declCtx = decl.getDeclContext();
1177 if(UseLexicalParent::Yes == useLexicalParent) {
1178 declCtx = declCtx->getLexicalParent();
1179 }
1180
1181 if(nullptr == declCtx) {
1182 return false;
1183 }
1184
1185 const bool isFriend{(decl.getFriendObjectKind() != Decl::FOK_None)};
1186 const bool neitherTransparentNorFriend{not declCtx->isTransparentContext() and not isFriend};
1187
1188 if(UseLexicalParent::Yes == useLexicalParent) {
1189 return (declCtx->isNamespace() and not declCtx->isInlineNamespace()) and neitherTransparentNorFriend;
1190 }
1191
1192 return (declCtx->isNamespace() or declCtx->isInlineNamespace()) and neitherTransparentNorFriend;
1193}
1194//-----------------------------------------------------------------------------
1195
1196static const SubstTemplateTypeParmType* GetSubstTemplateTypeParmType(const Type* t)
1197{
1198 if(const auto* substTemplateTypeParmType = dyn_cast_or_null<SubstTemplateTypeParmType>(t)) {
1199 return substTemplateTypeParmType;
1200 } else if(const auto& pointeeType = t->getPointeeType(); not pointeeType.isNull()) {
1201 return GetSubstTemplateTypeParmType(pointeeType.getTypePtrOrNull());
1202 }
1203
1204 return nullptr;
1205}
1206//-----------------------------------------------------------------------------
1207
1208static const DeclRefExpr* FindVarDeclRef(const Stmt* stmt)
1209{
1210 if(const auto* dref = dyn_cast_or_null<DeclRefExpr>(stmt)) {
1211 if(const auto* vd = dyn_cast_or_null<VarDecl>(dref->getDecl())) {
1212 return dref;
1213 }
1214 }
1215
1216 if(stmt) {
1217 for(const auto* child : stmt->children()) {
1218 if(const auto* childRef = FindVarDeclRef(child)) {
1219 return childRef;
1220 }
1221 }
1222 }
1223
1224 return nullptr;
1225}
1226//-----------------------------------------------------------------------------
1227
1228/*
1229 * \brief Get a usable name from a template parameter pack.
1230 *
1231 * A template parameter pack, args, as in:
1232 * \code
1233template<typename F, typename ...Types>
1234auto forward(F f, Types &&...args) {
1235 f(args...);
1236}
1237
1238forward(f,1, 2,3);
1239 * \endcode
1240 *
1241 * gets expanded by clang as
1242 * \code
1243f(args, args, args);
1244 * \endcode
1245 *
1246 * which would obviously not compile. For clang AST dump it is the right thing. For C++ Insights where the resulting
1247 * code should be compilable it is not. What this function does is, figure out whether it is a pack expansion and if so,
1248 * make the parameters unique, such that \c args becomes \c __args1 to \c __args3.
1249 *
1250 * The expected type for \c T currently is \c ValueDecl or \c VarDecl.
1251 */
1252static std::string GetTemplateParameterPackArgumentName(std::string_view name, const Decl* decl)
1253{
1254 if(const auto* parmVarDecl = dyn_cast_or_null<ParmVarDecl>(decl)) {
1255 if(const auto& originalType = parmVarDecl->getOriginalType(); not originalType.isNull()) {
1256 if(const auto* substTemplateTypeParmType = GetSubstTemplateTypeParmType(originalType.getTypePtrOrNull());
1257 substTemplateTypeParmType and substTemplateTypeParmType->getReplacedParameter()->isParameterPack()) {
1258 return StrCat(BuildInternalVarName(name), parmVarDecl->getFunctionScopeIndex());
1259
1260 } else if(const auto* fd = parmVarDecl->getParentFunctionOrMethod()) {
1261 // Get the primary template, if possible and check whether its parameters contain a parameter pack
1262 if(const auto* primTmpl = dyn_cast_or_null<FunctionDecl>(fd)->getPrimaryTemplate();
1263 primTmpl and primTmpl->getTemplateParameters()->hasParameterPack()) {
1264 // if so, then search for the matching parameter name.
1265 for(const auto* pa : primTmpl->getTemplatedDecl()->parameters()) {
1266 // if one is found we suffix it with its function scope index
1267 if(pa->isParameterPack() and (parmVarDecl->getNameAsString() == pa->getNameAsString())) {
1268 return StrCat(BuildInternalVarName(name), parmVarDecl->getFunctionScopeIndex());
1269 }
1270 }
1271 }
1272 }
1273 }
1274 } else if(const auto* varDecl = dyn_cast_or_null<VarDecl>(decl)) {
1275 // If it is an init-capture in C++2a p0780 brings "Allow pack expansion in lambda init-capture". We
1276 // need to figure out, whether the initializer for this \c VarDecl comes from a parameter pack. If
1277 // so, then we use this ParmVarDecl to get the index.
1278 if(varDecl->isInitCapture()) {
1279 if(const auto* drefExpr = FindVarDeclRef(varDecl->getInit())) {
1280 if(const auto* parmVarDecl = dyn_cast_or_null<ParmVarDecl>(drefExpr->getDecl())) {
1281 return GetTemplateParameterPackArgumentName(name, parmVarDecl);
1282 }
1283 }
1284 }
1285 }
1286
1287 return std::string{name};
1288}
1289//-----------------------------------------------------------------------------
1290
1291#if IS_CLANG_NEWER_THAN(20)
1292StringRef GetName(const DependentTemplateStorage& name)
1293{
1294 return name.getName().getIdentifier()->getName();
1295}
1296#endif
1297//-----------------------------------------------------------------------------
1298
1299std::string GetName(const NamedDecl& nd, const QualifiedName qualifiedName)
1300{
1301 std::string name{};
1302
1303 if(NeedsNamespace(nd, UseLexicalParent::No) or (QualifiedName::Yes == qualifiedName)) {
1304 if(const auto* cxxMedthodDecl = dyn_cast_or_null<CXXMethodDecl>(&nd)) {
1305 if(cxxMedthodDecl->isLambdaStaticInvoker()) {
1306 name = GetName(*cxxMedthodDecl->getParent());
1307 }
1308 }
1309
1310 name += details::GetScope(nd.getDeclContext(), details::RemoveCurrentScope::No);
1311 }
1312
1313 name += GetNamePlain(nd);
1314
1315 name += GetCfrontOverloadedFunctionName(dyn_cast_or_null<FunctionDecl>(&nd));
1316
1318}
1319//-----------------------------------------------------------------------------
1320
1321std::string BuildTemplateParamObjectName(std::string name)
1322{
1323 ReplaceAll(name, "{"sv, "_"sv);
1324 ReplaceAll(name, "}"sv, "_"sv);
1325 ReplaceAll(name, " "sv, ""sv);
1326 ReplaceAll(name, ","sv, "_"sv);
1327 ReplaceAll(name, "."sv, "_"sv);
1328 ReplaceAll(name, "+"sv, "_"sv);
1329 ReplaceAll(name, "-"sv, "n"sv);
1330
1331 return name;
1332}
1333//-----------------------------------------------------------------------------
1334
1335std::string GetName(const TemplateParamObjectDecl& decl)
1336{
1337 StringStream stream{};
1338 stream.Print(decl);
1339
1340 return ScopeHandler::RemoveCurrentScope(BuildTemplateParamObjectName(std::move(stream.str())));
1341}
1342//-----------------------------------------------------------------------------
1343
1344std::string GetName(const CXXRecordDecl& RD)
1345{
1346 if(RD.isLambda()) {
1347 return GetLambdaName(RD);
1348 }
1349
1350 // get the namespace as well
1351 if(NeedsNamespace(RD, UseLexicalParent::Yes)) {
1352 return details::GetQualifiedName(RD);
1353 }
1354
1355 std::string ret{GetNestedName(RD.getQualifier())};
1356
1357 if(auto name = RD.getName(); not name.empty()) {
1358 ret += name;
1359
1360 } else {
1361 ret += GetAnonymStructOrUnionName(RD);
1362 }
1363
1365}
1366//-----------------------------------------------------------------------------
1367
1368std::string GetTemporaryName(const Expr& tmp)
1369{
1370 return BuildInternalVarName(MakeLineColumnName(GetGlobalAST().getSourceManager(), tmp.getEndLoc(), "temporary"sv));
1371}
1372//-----------------------------------------------------------------------------
1373
1374std::string GetName(const CXXTemporaryObjectExpr& tmp)
1375{
1376 return GetTemporaryName(tmp);
1377}
1378//-----------------------------------------------------------------------------
1379
1380std::string GetName(const QualType& t, const Unqualified unqualified)
1381{
1382 return details::GetName(t, unqualified);
1383}
1384//-----------------------------------------------------------------------------
1385
1386static std::string GetUnqualifiedScopelessName(const Type* type, const InsightsSuppressScope supressScope)
1387{
1388 return details::GetName(QualType(type, 0), Unqualified::Yes, supressScope);
1389}
1390//-----------------------------------------------------------------------------
1391
1392std::string GetUnqualifiedScopelessName(const Type* type)
1393{
1394 return GetUnqualifiedScopelessName(type, InsightsSuppressScope::No);
1395}
1396//-----------------------------------------------------------------------------
1397
1398QualType GetType(QualType t)
1399{
1400 if(GetInsightsOptions().UseShow2C and t->isReferenceType()) {
1401 return GetGlobalAST().getPointerType(t.getNonReferenceType());
1402 }
1403
1404 return t;
1405}
1406//-----------------------------------------------------------------------------
1407
1408template<typename QT, typename SUB_T, typename SUB_T2 = void>
1409static bool HasTypeWithSubType(const QualType& t)
1410{
1411 if(const auto* lref = dyn_cast_or_null<QT>(t.getTypePtrOrNull())) {
1412 const auto subType = GetDesugarType(lref->getPointeeType());
1413 const auto& ct = subType.getCanonicalType();
1414 const auto* plainSubType = ct.getTypePtrOrNull();
1415
1416 if(const auto* st = dyn_cast_or_null<SUB_T>(plainSubType)) {
1417 if constexpr(std::is_same_v<void, SUB_T2>) {
1418 return true;
1419
1420 } else {
1421 const auto subType = GetDesugarType(st->getPointeeType());
1422 const auto& ct = subType.getCanonicalType();
1423 const auto* plainSubType = ct.getTypePtrOrNull();
1424
1425 return isa<SUB_T2>(plainSubType);
1426 }
1427 }
1428 }
1429
1430 return false;
1431}
1432//-----------------------------------------------------------------------------
1433
1434template<typename QT, typename SUB_T>
1435static bool HasTypePath(const QualType& t)
1436{
1437 if(const auto* lref = dyn_cast_or_null<QT>(t.getTypePtrOrNull())) {
1438 const auto subType = GetDesugarType(lref->getPointeeType());
1439
1440 return isa<SUB_T>(subType);
1441 }
1442
1443 return false;
1444}
1445//-----------------------------------------------------------------------------
1446
1447std::string GetTypeNameAsParameter(const QualType& t, std::string_view varName, const Unqualified unqualified)
1448{
1449 const bool isFunctionPointer =
1450 HasTypeWithSubType<ReferenceType, FunctionProtoType>(t.getCanonicalType()) or
1451 HasTypeWithSubType<ReferenceType, PointerType, FunctionProtoType>(t.getCanonicalType());
1452 const bool isArrayRef = HasTypeWithSubType<ReferenceType, ArrayType>(t);
1453 // Special case for Issue81, auto returns an array-ref and to catch auto deducing an array (Issue106)
1454 const bool isAutoType = (nullptr != dyn_cast_or_null<AutoType>(t.getTypePtrOrNull()));
1455 const auto pointerToArrayBaseType = isAutoType ? t->getContainedAutoType()->getDeducedType() : t;
1456 const bool isPointerToArray = HasTypeWithSubType<PointerType, ArrayType>(pointerToArrayBaseType);
1457 // Only treat this as an array if it is a top-level arry. Typdef's et all can hide the arrayness.
1458 const bool isRawArrayType = t->isArrayType() and not(isa<TypedefType>(t)
1459#if not IS_CLANG_NEWER_THAN(22)
1460 or isa<ElaboratedType>(t)
1461#endif
1462 or isa<UsingType>(t));
1463
1464 std::string typeName = details::GetName(t, unqualified);
1465
1466 // Sometimes we get char const[2]. If we directly insert the typename we end up with char const__var[2] which is not
1467 // a valid type name. Hence check for this condition and, if necessary, insert a space before __var.
1468 auto getSpaceOrEmpty = [&](const std::string_view& needle) -> std::string_view {
1469 if(not Contains(typeName, needle)) {
1470 return " ";
1471 }
1472
1473 return {};
1474 };
1475
1476 if(isRawArrayType and not t->isLValueReferenceType()) {
1477 const auto space = getSpaceOrEmpty(" ["sv);
1478 InsertBefore(typeName, "["sv, StrCat(space, varName));
1479
1480 } else if(isArrayRef) {
1481 const bool isRValueRef{HasTypeWithSubType<RValueReferenceType, ArrayType>(t)};
1482 const std::string_view contains{isRValueRef ? "(&&" : "(&"};
1483
1484 if(Contains(typeName, contains)) {
1485 InsertAfter(typeName, contains, varName);
1486 } else {
1487 const std::string_view insertBefore{isRValueRef ? "&&[" : "&["};
1488
1489 InsertBefore(typeName, insertBefore, "("sv);
1490
1491 // check whether we are dealing with a function or an array
1492 if(Contains(typeName, contains)) {
1493 InsertAfter(typeName, contains, StrCat(varName, ")"sv));
1494 } else {
1495 InsertAfter(typeName, typeName, StrCat(" "sv, varName));
1496 }
1497 }
1498
1499 } else if(isFunctionPointer) {
1500 const bool isRValueRef{HasTypeWithSubType<RValueReferenceType, FunctionProtoType>(t)};
1501 const auto contains{[&]() {
1502 if(isRValueRef) {
1503 return "(&&"sv;
1504 }
1505
1506 else if(HasTypeWithSubType<LValueReferenceType, PointerType, FunctionProtoType>(t)) {
1507 return "(*&"sv;
1508 } else {
1509 return "(&"sv;
1510 }
1511 }()};
1512
1513 if(Contains(typeName, contains)) {
1514 InsertAfter(typeName, contains, varName);
1515 } else {
1516 typeName += StrCat(" "sv, varName);
1517 }
1518
1519 } else if(isa<MemberPointerType>(t)) {
1520 InsertAfter(typeName, "::*"sv, varName);
1521
1522 } else if(isPointerToArray) {
1523 if(Contains(typeName, "(*"sv)) {
1524 InsertAfter(typeName, "(*"sv, varName);
1525 } else if(Contains(typeName, "*"sv)) {
1526 InsertBefore(typeName, "*"sv, "("sv);
1527 InsertAfter(typeName, "*"sv, StrCat(varName, ")"sv));
1528 }
1529 } else if(t->isFunctionPointerType()) {
1530 if(Contains(typeName, "(*"sv)) {
1531 InsertAfter(typeName, "(*"sv, varName);
1532 } else {
1533 typeName += StrCat(" "sv, varName);
1534 }
1535 } else if(HasTypePath<PointerType, ParenType>(t)) {
1536 InsertAfter(typeName, "(*"sv, varName);
1537
1538 } else if(not isRawArrayType and not varName.empty()) {
1539 typeName += StrCat(" "sv, varName);
1540 }
1541
1542 return typeName;
1543}
1544//-----------------------------------------------------------------------------
1545
1547 const TemplateTypeParmDecl* decl,
1548 const bool isParameter,
1549 const TemplateTypeParmType* type)
1550{
1551 if(decl) {
1552 if(const auto* typeConstraint = decl->getTypeConstraint(); typeConstraint and not isParameter) {
1553 StringStream sstream{};
1554 sstream.Print(*typeConstraint);
1555
1556 ofm.Append(sstream.str(), " "sv);
1557 }
1558 }
1559
1560 const auto depth = decl ? decl->getDepth() : type->getDepth();
1561 const auto index = decl ? decl->getIndex() : type->getIndex();
1562
1563 ofm.Append("type_parameter_"sv, depth, "_"sv, index);
1564}
1565//-----------------------------------------------------------------------------
1566
1567static bool IsTrivialStaticClassVarDecl(const DeclRefExpr& declRefExpr)
1568{
1569 if(const VarDecl* vd = GetVarDeclFromDeclRefExpr(declRefExpr)) {
1570 return IsTrivialStaticClassVarDecl(*vd);
1571 }
1572
1573 return false;
1574}
1575//-----------------------------------------------------------------------------
1576
1577const APValue* GetEvaluatedValue(const VarDecl& varDecl)
1578{
1579 if((nullptr != varDecl.ensureEvaluatedStmt()) and varDecl.ensureEvaluatedStmt()->Value.isValid() and
1580 not varDecl.getInit()->isValueDependent()) {
1581 return varDecl.evaluateValue();
1582 }
1583
1584 return nullptr;
1585}
1586//-----------------------------------------------------------------------------
1587
1588bool IsEvaluatable(const VarDecl& varDecl)
1589{
1590 return (nullptr != GetEvaluatedValue(varDecl));
1591}
1592//-----------------------------------------------------------------------------
1593
1594bool IsTrivialStaticClassVarDecl(const VarDecl& varDecl)
1595{
1596 // Should the VarDecl be evaluatable at compile-time, there is no additional guard added by the compiler.
1597 if(varDecl.isStaticLocal() and not IsEvaluatable(varDecl)) {
1598 if(const auto* cxxRecordDecl = varDecl.getType()->getAsCXXRecordDecl()) {
1599 if(cxxRecordDecl->hasNonTrivialDestructor() or not cxxRecordDecl->hasTrivialDefaultConstructor()) {
1600 return true;
1601 }
1602 }
1603 }
1604
1605 return false;
1606}
1607//-----------------------------------------------------------------------------
1608
1609std::string GetName(const DeclRefExpr& declRefExpr)
1610{
1611 const auto* declRefDecl = declRefExpr.getDecl();
1612 std::string name{};
1613 const auto* declCtx = declRefDecl->getDeclContext();
1614 const bool needsNamespace{NeedsNamespace(*declRefDecl, UseLexicalParent::No)};
1615
1616 // get the namespace as well
1617 if(needsNamespace) {
1618 name = details::GetScope(declCtx);
1619 } else if(declRefExpr.hasQualifier()) {
1620 name = details::GetQualifiedName(*declRefDecl);
1621 }
1622
1623 if(needsNamespace or not declRefExpr.hasQualifier()) {
1624 std::string plainName{GetPlainName(declRefExpr)};
1625
1626 // try to handle the special case of a function local static with class type and non trivial destructor. In
1627 // this case, as we teared that variable apart, we need to adjust the variable named and add a reinterpret
1628 // cast
1629 if(IsTrivialStaticClassVarDecl(declRefExpr)) {
1630 if(const VarDecl* vd = GetVarDeclFromDeclRefExpr(declRefExpr)) {
1631 if(const auto* cxxRecordDecl = vd->getType()->getAsCXXRecordDecl()) {
1632 plainName = StrCat("*"sv,
1634 "<"sv,
1635 GetName(vd->getType()),
1636 "*>("sv,
1637 BuildInternalVarName(plainName),
1638 ")"sv);
1639 }
1640 }
1641 }
1642
1643 name.append(plainName);
1644 }
1645
1646 name += GetCfrontOverloadedFunctionName(dyn_cast_or_null<FunctionDecl>(declRefDecl));
1647
1649}
1650//-----------------------------------------------------------------------------
1651
1652/*
1653 * Go deep in a Stmt if necessary and look to all childs for a DeclRefExpr.
1654 */
1655const DeclRefExpr* FindDeclRef(const Stmt* stmt)
1656{
1657 if(const auto* dref = dyn_cast_or_null<DeclRefExpr>(stmt)) {
1658 return dref;
1659 } else if(const auto* arrayInitExpr = dyn_cast_or_null<ArrayInitLoopExpr>(stmt)) {
1660 const auto* srcExpr = arrayInitExpr->getCommonExpr()->getSourceExpr();
1661
1662 if(const auto* arrayDeclRefExpr = dyn_cast_or_null<DeclRefExpr>(srcExpr)) {
1663 return arrayDeclRefExpr;
1664 }
1665 } else if(const auto func = dyn_cast_or_null<CXXFunctionalCastExpr>(stmt)) {
1666 // TODO(stmt, "");
1667 }
1668
1669 if(stmt) {
1670 for(const auto* child : stmt->children()) {
1671 if(const auto* childRef = FindDeclRef(child)) {
1672 return childRef;
1673 }
1674 }
1675 }
1676
1677 return nullptr;
1678}
1679//-----------------------------------------------------------------------------
1680
1681std::string GetName(const VarDecl& VD)
1682{
1683 // Handle a special case of DecompositionDecl. A DecompositionDecl does not have a name. Hence we make one up from
1684 // the original name of the variable that is decomposed plus line number where the decomposition was written.
1685 if(const auto* decompositionDeclStmt = dyn_cast_or_null<DecompositionDecl>(&VD)) {
1686 const auto baseVarName{[&]() {
1687 if(const auto* declName = FindDeclRef(decompositionDeclStmt->getInit())) {
1688 std::string name = GetPlainName(*declName);
1689
1690 if(Contains(name, kwOperator)) {
1691 return std::string{kwOperator};
1692 }
1693
1694 return name;
1695 }
1696
1697 // We approached an unnamed decl. This happens for example like this: auto& [x, y] = Point{};
1698 return std::string{};
1699 }()};
1700
1701 return BuildInternalVarName(baseVarName, decompositionDeclStmt->getBeginLoc(), GetSM(*decompositionDeclStmt));
1702 }
1703
1704 std::string name{VD.getNameAsString()};
1705
1707}
1708//-----------------------------------------------------------------------------
1709
1710static std::optional<bool> EvaluateAsBoolenCondition(const Expr& expr, const Decl& decl)
1711{
1712 bool r{false};
1713
1714 if(not isa<ConceptSpecializationExpr>(expr) and expr.EvaluateAsBooleanCondition(r, decl.getASTContext())) {
1715 return {r};
1716 }
1717
1718 return std::nullopt;
1719}
1720//-----------------------------------------------------------------------------
1721
1722const std::string GetNoExcept(const FunctionDecl& decl)
1723{
1724 const auto* func = decl.getType()->castAs<FunctionProtoType>();
1725
1726 if(func and func->hasNoexceptExceptionSpec() and not isUnresolvedExceptionSpec(func->getExceptionSpecType())) {
1727 std::string ret{kwSpaceNoexcept};
1728
1729 if(const auto* expr = func->getNoexceptExpr()) {
1730 ret += "("sv;
1731
1732 if(const auto value = EvaluateAsBoolenCondition(*expr, decl); value) {
1733 ret += details::ConvertToBoolString(*value);
1734 } else {
1735 OutputFormatHelper ofm{};
1736 CodeGenerator cg{ofm};
1737 cg.InsertArg(expr);
1738
1739 ret += ofm.GetString();
1740 }
1741
1742 ret += ")"sv;
1743 }
1744
1745 return ret;
1746
1747 } else if(func and isUnresolvedExceptionSpec(func->getExceptionSpecType())) {
1748 // For special members the exception specification is unevaluated as long as the special member is unused.
1750 }
1751
1752 return {};
1753}
1754//-----------------------------------------------------------------------------
1755
1756const std::string_view GetConst(const FunctionDecl& decl)
1757{
1758 if(const auto* methodDecl = dyn_cast_or_null<CXXMethodDecl>(&decl)) {
1759 if(methodDecl->isConst()) {
1760 return kwSpaceConst;
1761 }
1762 }
1763
1764 return {};
1765}
1766//-----------------------------------------------------------------------------
1767
1768std::string GetElaboratedTypeKeyword(const ElaboratedTypeKeyword keyword)
1769{
1770 std::string ret{TypeWithKeyword::getKeywordName(keyword)};
1771
1772 if(not ret.empty()) {
1773 ret += ' ';
1774 }
1775
1776 return ret;
1777}
1778//-----------------------------------------------------------------------------
1779
1780uint64_t GetSize(const ConstantArrayType* arrayType)
1781{
1782 return arrayType->getSize().getZExtValue();
1783}
1784//-----------------------------------------------------------------------------
1785
1786void StringStream::Print(const TemplateArgument& arg)
1787{
1788 arg.print(CppInsightsPrintingPolicy{}, *this, false);
1789}
1790//-----------------------------------------------------------------------------
1791
1792void StringStream::Print(const TemplateSpecializationType& arg)
1793{
1794 arg.getTemplateName().print(*this, CppInsightsPrintingPolicy{}, TemplateName::Qualified::AsWritten);
1795}
1796//-----------------------------------------------------------------------------
1797
1798void StringStream::Print(const TemplateParamObjectDecl& arg)
1799{
1800 arg.printAsExpr(*this);
1801}
1802//-----------------------------------------------------------------------------
1803
1804void StringStream::Print(const TypeConstraint& arg)
1805{
1806 arg.print(*this, CppInsightsPrintingPolicy{});
1807}
1808//-----------------------------------------------------------------------------
1809
1810void StringStream::Print(const StringLiteral& arg)
1811{
1812 arg.outputString(*this);
1813}
1814//-----------------------------------------------------------------------------
1815
1816void StringStream::Print(const CharacterLiteral& arg)
1817{
1818 CharacterLiteral::print(arg.getValue(), arg.getKind(), *this);
1819}
1820//-----------------------------------------------------------------------------
1821
1822template<class... Ts>
1823struct overloaded : Ts...
1824{
1825 using Ts::operator()...;
1826};
1827template<class... Ts>
1829//-----------------------------------------------------------------------------
1830
1831void P0315Visitor_HandleLambdaExpr(OutputFormatHelper& ofm, const LambdaExpr* expr)
1832{
1833 ofm.Append(GetLambdaName(*expr));
1834}
1835//-----------------------------------------------------------------------------
1836
1837void P0315Visitor_HandleLambdaExpr(CodeGenerator& cg, const LambdaExpr* expr)
1838{
1839 cg.InsertArg(expr);
1840}
1841//-----------------------------------------------------------------------------
1842
1843} // namespace clang::insights
#define IS_CLANG_NEWER_THAN(major)
Definition ClangCompat.h:16
const InsightsOptions & GetInsightsOptions()
Get the global C++ Insights options.
Definition Insights.cpp:37
const CompilerInstance & GetGlobalCI()
Get access to the CompilerInstance.
Definition Insights.cpp:78
const ASTContext & GetGlobalAST()
Get access to the ASTContext.
Definition Insights.cpp:71
#define HANDLE_TYPE(t)
constexpr std::string_view kwCommentStart
constexpr std::string_view kwReinterpretCast
constexpr std::string_view kwSpaceConst
constexpr std::string_view kwTemplateSpace
constexpr std::string_view kwSpaceCCommentEnd
constexpr std::string_view kwElipsis
constexpr std::string_view kwOperator
constexpr std::string_view kwSpaceNoexcept
#define STRONG_BOOL(typeName)
A more than simple typsafe bool.
#define RETURN_IF(cond)
! A helper inspired by https://github.com/Microsoft/wil/wiki/Error-handling-helpers
T & back() noexcept
Definition StackList.h:67
T * pop() noexcept
Definition StackList.h:47
void push(TStackListEntry &entry) noexcept
Definition StackList.h:31
More or less the heart of C++ Insights.
virtual void InsertArg(const Decl *stmt)
void InsertTemplateArgs(const T &t)
void InsertTemplateParameters(const TemplateParameterList &list, const TemplateParamsOnly templateParamsOnly=TemplateParamsOnly::No)
! Skip template, type constraints and class/typename.
A helper which invokes a lambda when the scope is destroyed.
void Append(const char c)
Append a single character.
! Find a LambdaExpr inside a Decltype
static std::string RemoveCurrentScope(std::string name)
Remove the current scope from a string.
Specialization for ::llvm::raw_string_ostream with an internal std::string buffer.
void Print(const TemplateArgument &)
SimpleTypePrinter a partially substitution of Clang's TypePrinter.
SimpleTypePrinter(const QualType &qt, const CppInsightsPrintingPolicy &printingPolicy)
static std::string GetQualifiedName(const NamedDecl &decl, const RemoveCurrentScope removeCurrentScope=RemoveCurrentScope::Yes)
void StrCat(std::string &ret, const auto &... args)
static void BuildNamespace(std::string &fullNamespace, const NestedNameSpecifier *stmt, const IgnoreNamespace ignoreNamespace)
static std::string GetName(QualType t, const Unqualified unqualified=Unqualified::No, const InsightsSuppressScope supressScope=InsightsSuppressScope::No)
std::string ConvertToBoolString(bool b)
Convert a boolean value to a string representation of "true" or "false".
static std::string GetScope(const DeclContext *declCtx, const RemoveCurrentScope removeCurrentScope=RemoveCurrentScope::Yes)
void InsertBefore(std::string &source, const std::string_view &find, const std::string_view &replace)
uint64_t GetSize(const ConstantArrayType *arrayType)
std::string GetLambdaName(const CXXRecordDecl &lambda)
static bool NeedsNamespace(const Decl &decl, UseLexicalParent useLexicalParent)
const SourceManager & GetSM(const Decl &decl)
bool Contains(const std::string_view source, const std::string_view search)
static std::string GetTemplateParameterPackArgumentName(std::string_view name, const Decl *decl)
std::string BuildRetTypeName(const Decl &decl)
bool IsEvaluatable(const VarDecl &varDecl)
Check whether a VarDecls initialization can be done a compile-time.
static bool HasTypeWithSubType(const QualType &t)
const std::string GetNoExcept(const FunctionDecl &decl)
const DeclRefExpr * FindDeclRef(const Stmt *stmt)
Go deep in a Stmt if necessary and look to all childs for a DeclRefExpr.
std::string GetPlainName(const DeclRefExpr &DRE)
std::string GetElaboratedTypeKeyword(const ElaboratedTypeKeyword keyword)
std::string MakeLineColumnName(const SourceManager &sm, const SourceLocation &loc, const std::string_view &prefix)
QualType GetType(QualType t)
In Cfront mode we transform references to pointers.
std::string GetName(const NamedDecl &nd, const QualifiedName qualifiedName)
static const DeclRefExpr * FindVarDeclRef(const Stmt *stmt)
static std::string GetAnonymStructOrUnionName(const CXXRecordDecl &cxxRecordDecl)
void ReplaceAll(std::string &str, std::string_view from, std::string_view to)
std::string BuildInternalVarName(const std::string_view &varName)
static const std::string GetAsCPPStyleString(const QualType &t, const CppInsightsPrintingPolicy &printingPolicy)
static const SubstTemplateTypeParmType * GetSubstTemplateTypeParmType(const Type *t)
const std::string_view GetConst(const FunctionDecl &decl)
static bool HasOverload(const FunctionDecl *fd)
std::string GetTemporaryName(const Expr &tmp)
std::string BuildTemplateParamObjectName(std::string name)
static bool HasTypePath(const QualType &t)
void AppendTemplateTypeParamName(OutputFormatHelper &ofm, const TemplateTypeParmDecl *decl, const bool isParameter, const TemplateTypeParmType *type)
std::string GetDeclContext(const DeclContext *ctx, WithTemplateParameters withTemplateParameters)
static std::string GetSpecialMemberName(const ValueDecl *vd, QualType type)
const QualType GetDesugarType(const QualType &QT)
Remove decltype from a QualType, if possible.
static void InsertAfter(std::string &source, const std::string_view &find, const std::string_view &replace)
const APValue * GetEvaluatedValue(const VarDecl &varDecl)
Get the evaluated APValue from a VarDecl
static std::string GetUnqualifiedScopelessName(const Type *type, const InsightsSuppressScope supressScope)
static std::optional< bool > EvaluateAsBoolenCondition(const Expr &expr, const Decl &decl)
std::string GetNestedName(const NestedNameSpecifier *nns, const IgnoreNamespace ignoreNamespace)
static const VarDecl * GetVarDeclFromDeclRefExpr(const DeclRefExpr &declRefExpr)
std::string GetTypeNameAsParameter(const QualType &t, std::string_view varName, const Unqualified unqualified)
void P0315Visitor_HandleLambdaExpr(OutputFormatHelper &ofm, const LambdaExpr *expr)
static std::string GetNamePlain(const NamedDecl &decl)
std::string GetCfrontOverloadedFunctionName(const FunctionDecl *fd)
bool IsAnonymousStructOrUnion(const CXXRecordDecl *cxxRecordDecl)
Check whether this is an anonymous struct or union.
static bool IsTrivialStaticClassVarDecl(const DeclRefExpr &declRefExpr)
CppInsightsPrintingPolicy(const Unqualified unqualified, const InsightsSuppressScope supressScope, const InsightsCanonicalTypes insightsCanonicalTypes=InsightsCanonicalTypes::No)