CodeGenerator.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 <algorithm>
9#include <optional>
10#include <vector>
11
12#include "ASTHelpers.h"
13#include "ClangCompat.h"
14#include "CodeGenerator.h"
15#include "DPrint.h"
16#include "Insights.h"
17#include "InsightsHelpers.h"
18#include "InsightsOnce.h"
19#include "InsightsStrCat.h"
20#include "NumberIterator.h"
21#include "clang/AST/RecordLayout.h"
22#include "clang/Frontend/CompilerInstance.h"
23#include "clang/Sema/Sema.h"
24#include "llvm/ADT/StringExtras.h"
25#include "llvm/Support/Path.h"
26//-----------------------------------------------------------------------------
27
28/// \brief Convenience macro to create a \ref LambdaScopeHandler on the stack.
29#define LAMBDA_SCOPE_HELPER(type) \
30 LambdaScopeHandler lambdaScopeHandler{mLambdaStack, mOutputFormatHelper, LambdaCallerType::type};
31//-----------------------------------------------------------------------------
32
33/// \brief The lambda scope helper is only created if cond is true
34#define CONDITIONAL_LAMBDA_SCOPE_HELPER(type, cond) \
35 std::optional<LambdaScopeHandler> lambdaScopeHandler; \
36 if(cond) { \
37 lambdaScopeHandler.emplace(mLambdaStack, mOutputFormatHelper, LambdaCallerType::type); \
38 }
39//-----------------------------------------------------------------------------
40
41namespace ranges = std::ranges;
42//-----------------------------------------------------------------------------
43namespace clang::insights {
44
45#define BUILD_OPT_AND(name, param) std::function name = [](param t)->MyOptional<param>
46#define BUILD_OPT_AND_O(name, param, ret) std::function name = [](param t)->MyOptional<ret>
47
48BUILD_OPT_AND(IsPointer, QualType)
49{
50 if(t->isPointerType()) {
51 return t;
52 }
53
54 return {};
55};
56
57BUILD_OPT_AND(IsPOD, QualType)
58{
59 if(t.isPODType(GetGlobalAST())) {
60 return t;
61 }
62
63 return {};
64};
65
66template<typename T>
67std::function Isa = [](QualType t) -> MyOptional<QualType> {
68 if(isa<T>(t.getTypePtrOrNull())) {
69 return t;
70 }
71
72 return {};
73};
74
75BUILD_OPT_AND_O(CanonicalType, const InitListExpr&, QualType)
76{
77 return t.getType().getCanonicalType();
78};
79
80STRONG_BOOL(TrailingSpace);
81static std::string AccessToStringWithColon(const AccessSpecifier& access,
82 const TrailingSpace endWithTrailingSpace = TrailingSpace::Yes)
83{
84 std::string accessStr{getAccessSpelling(access)};
85 if(not accessStr.empty()) {
86 if(TrailingSpace::Yes == endWithTrailingSpace) {
87 accessStr += ": "sv;
88 } else {
89 accessStr += ":"sv;
90 }
91 }
92
93 return accessStr;
94}
95//-----------------------------------------------------------------------------
96
97using namespace asthelpers;
98
99static std::string_view GetCastName(const CastKind castKind, bool constnessChange = false)
100{
101 if(is{castKind}.any_of(CastKind::CK_BitCast, CastKind::CK_IntegralToPointer, CastKind::CK_PointerToIntegral)) {
102 return kwReinterpretCast;
103 }
104
105 if((CastKind::CK_NoOp == castKind) and constnessChange) {
106 return "const_cast"sv;
107 }
108
109 return kwStaticCast;
110}
111//-----------------------------------------------------------------------------
112
113static std::string_view GetTagDeclTypeName(const TagDecl& decl)
114{
115 if(decl.isClass()) {
116 return kwClassSpace;
117
118 } else if(decl.isUnion()) {
119 return kwUnionSpace;
120
121 } else {
122 return kwStructSpace;
123 }
124}
125//-----------------------------------------------------------------------------
126
128{
129 const uint64_t mIndex;
130
131public:
132 ArrayInitCodeGenerator(OutputFormatHelper& _outputFormatHelper, const uint64_t index)
133 : CodeGenerator{_outputFormatHelper}
134 , mIndex{index}
135 {
136 }
137
139 void InsertArg(const ArrayInitIndexExpr*) override { mOutputFormatHelper.Append(mIndex); }
140};
141//-----------------------------------------------------------------------------
142
143/// Handling specialties for decomposition declarations.
144///
145/// Decompositions declarations have no name. This class stores the made up name and returns it each time the anonymous
146/// declaration is asked for a name.
148{
149 std::string mVarName;
150
151public:
152 StructuredBindingsCodeGenerator(OutputFormatHelper& _outputFormatHelper, std::string&& varName)
153 : CodeGenerator{_outputFormatHelper}
154 , mVarName{std::move(varName)}
155 {
156 }
157
159 void InsertArg(const DeclRefExpr* stmt) override;
160 void InsertArg(const BindingDecl* stmt) override;
161
162 /// Inserts the bindings of a decompositions declaration.
163 void InsertDecompositionBindings(const DecompositionDecl& decompositionDeclStmt);
164
165protected:
166 virtual bool ShowXValueCasts() const override { return true; }
167};
168//-----------------------------------------------------------------------------
169
170/// Handle using statements which pull functions ore members from a base class into the class.
172{
173public:
175 : CodeGenerator{_outputFormatHelper}
176 {
177 }
178
180 void InsertArg(const CXXMethodDecl* stmt) override
181 {
183
184 InsertCXXMethodDecl(stmt, SkipBody::Yes);
185 }
187 void InsertArg(const FieldDecl* stmt) override
188 {
191 }
192
193 // makes no sense to insert the class when applying it to using
194 void InsertArg(const CXXRecordDecl*) override {}
195
196 // makes no sense to insert the typedef when applying it to using
197 void InsertArg(const TypedefDecl*) override {}
198
199protected:
200 bool InsertNamespace() const override { return true; }
201};
202//-----------------------------------------------------------------------------
203
205{
206public:
209
210 void InsertArg(const LambdaExpr* stmt) override { mOutputFormatHelper.Append(GetLambdaName(*stmt), "{}"sv); }
211};
212//-----------------------------------------------------------------------------
213
215 OutputFormatHelper& outputFormatHelper,
216 const LambdaCallerType lambdaCallerType)
217: mStack{stack}
218, mHelper{lambdaCallerType, GetBuffer(outputFormatHelper)}
219{
220 mStack.push(mHelper);
221}
222//-----------------------------------------------------------------------------
223
225{
226 if(not mStack.empty()) {
227 mStack.pop()->finish();
228 }
229}
230//-----------------------------------------------------------------------------
231
232OutputFormatHelper& CodeGenerator::LambdaScopeHandler::GetBuffer(OutputFormatHelper& outputFormatHelper) const
233{
234 // Find the most outer element to place the lambda class definition. For example, if we have this:
235 // Test( [&]() {} );
236 // The lambda's class definition needs to be placed _before_ the CallExpr to Test.
237 for(auto& l : mStack) {
238 switch(l.callerType()) {
239 case LambdaCallerType::CallExpr: [[fallthrough]];
240 case LambdaCallerType::VarDecl: [[fallthrough]];
241 case LambdaCallerType::ReturnStmt: [[fallthrough]];
242 case LambdaCallerType::OperatorCallExpr: [[fallthrough]];
243 case LambdaCallerType::MemberCallExpr: [[fallthrough]];
244 case LambdaCallerType::BinaryOperator: [[fallthrough]];
245 case LambdaCallerType::CXXMethodDecl: return l.buffer();
246 default: break;
247 }
248 }
249
250 return outputFormatHelper;
251}
252//-----------------------------------------------------------------------------
253
254static std::string_view ArrowOrDot(bool isArrow)
255{
256 return isArrow ? "->"sv : "."sv;
257}
258//-----------------------------------------------------------------------------
259
260template<typename T>
261static T ValueOrDefault(bool b, T v)
262{
263 if(b) {
264 return v;
265 }
266
267 return {};
268}
269//-----------------------------------------------------------------------------
270
271template<typename T>
272static T ValueOr(bool b, T val, T el)
273{
274 if(b) {
275 return val;
276 }
277
278 return el;
279}
280//-----------------------------------------------------------------------------
281
282void CodeGenerator::InsertArg(const CXXDependentScopeMemberExpr* stmt)
283{
284 if(not stmt->isImplicitAccess()) {
285 InsertArg(stmt->getBase());
286 } else {
287 InsertNamespace(stmt->getQualifier());
288 }
289
290 std::string_view op{ValueOrDefault(not stmt->isImplicitAccess(), ArrowOrDot(stmt->isArrow()))};
291
292 mOutputFormatHelper.Append(op, stmt->getMemberNameInfo().getAsString());
293}
294//-----------------------------------------------------------------------------
295
296void CodeGenerator::InsertArg(const CXXForRangeStmt* rangeForStmt)
297{
298 auto& langOpts{GetLangOpts(*rangeForStmt->getLoopVariable())};
299 const bool onlyCpp11{not langOpts.CPlusPlus17};
300
301 auto* rwStmt = const_cast<CXXForRangeStmt*>(rangeForStmt);
302
303 StmtsContainer outerScopeStmts{};
304
305 // C++20 init-statement
306 outerScopeStmts.Add(rangeForStmt->getInit());
307
308 // range statement
309 outerScopeStmts.Add(rangeForStmt->getRangeStmt());
310
311 if(not onlyCpp11) {
312 outerScopeStmts.Add(rangeForStmt->getBeginStmt());
313 outerScopeStmts.Add(rangeForStmt->getEndStmt());
314 }
315
316 // add the loop variable to the body
317 StmtsContainer bodyStmts{};
318 bodyStmts.Add(rangeForStmt->getLoopVarStmt());
319
320 // add the body itself, without the CompoundStmt
321 bodyStmts.AddBodyStmts(rwStmt->getBody());
322
323 const auto& ctx = rangeForStmt->getLoopVariable()->getASTContext();
324
325 // In case of a range-based for-loop inside an unevaluated template the begin and end statements are not present. In
326 // this case just add a nullptr.
327 auto* declStmt = [&]() -> DeclStmt* {
328 if(onlyCpp11) {
329 return mkDeclStmt(rwStmt->getBeginStmt() ? rwStmt->getBeginStmt()->getSingleDecl() : nullptr,
330 rwStmt->getEndStmt() ? rwStmt->getEndStmt()->getSingleDecl() : nullptr);
331 }
332
333 return nullptr;
334 }();
335
336 auto* innerScope = mkCompoundStmt(bodyStmts, rangeForStmt->getBeginLoc(), rangeForStmt->getEndLoc());
337
338 auto* forStmt = new(ctx) ForStmt(ctx,
339 declStmt,
340 rwStmt->getCond(),
341 rwStmt->getLoopVariable(),
342 rwStmt->getInc(),
343 innerScope,
344 rangeForStmt->getBeginLoc(),
345 rangeForStmt->getEndLoc(),
346 rangeForStmt->getEndLoc());
347
348 outerScopeStmts.Add(forStmt);
349
350 auto* outerScope = mkCompoundStmt(outerScopeStmts, rangeForStmt->getBeginLoc(), rangeForStmt->getEndLoc());
351
352 InsertArg(outerScope);
353
355}
356//-----------------------------------------------------------------------------
357
358void CodeGenerator::InsertQualifierAndName(const DeclarationName& declName,
359 const NestedNameSpecifier
360#if not IS_CLANG_NEWER_THAN(22)
361 *
362#endif
363 qualifier,
364 const bool hasTemplateKeyword)
365{
367 ValueOrDefault(hasTemplateKeyword, kwTemplateSpace),
368 declName.getAsString());
369}
370//-----------------------------------------------------------------------------
371
372void CodeGenerator::InsertNamespace(const NestedNameSpecifier
373#if not IS_CLANG_NEWER_THAN(22)
374 *
375#endif
376 stmt)
377{
379}
380//-----------------------------------------------------------------------------
381
382void CodeGenerator::InsertArg(const UnresolvedLookupExpr* stmt)
383{
384 InsertQualifierAndNameWithTemplateArgs(stmt->getName(), stmt);
385}
386//-----------------------------------------------------------------------------
387
388void CodeGenerator::InsertArg(const DependentScopeDeclRefExpr* stmt)
389{
390 InsertQualifierAndNameWithTemplateArgs(stmt->getDeclName(), stmt);
391}
392//-----------------------------------------------------------------------------
393
394void CodeGenerator::InsertArg(const VarTemplateDecl* stmt)
395{
396 const auto* templatedDecl = stmt->getTemplatedDecl();
397
398 // Insert only the primary template here. The specializations are inserted via their instantiated
399 // VarTemplateSpecializationDecl which resolved to a VarDecl. It looks like whether the variable has an initializer
400 // or not can be used to distinguish between the primary template and one appearing in a templated class.
401 RETURN_IF(not templatedDecl->hasInit());
402
403 // VarTemplatedDecl's can have lambdas as initializers. Push a VarDecl on the stack, otherwise the lambda would
404 // appear in the middle of template<....> and the variable itself.
405 {
406 LAMBDA_SCOPE_HELPER(DecltypeWithName); // Needed for P0315Checker
407
408 InsertTemplateParameters(*stmt->getTemplateParameters());
409 }
410
412
413 InsertArg(templatedDecl);
414
415 for(OnceTrue first{}; const auto* spec : stmt->specializations()) {
416 if(TSK_ExplicitSpecialization == spec->getSpecializationKind()) {
417 continue;
418 }
419
420 if(first) {
422 }
423
424 InsertArg(spec);
425 }
426}
427//-----------------------------------------------------------------------------
428
429void CodeGenerator::InsertArg(const ConceptDecl* stmt)
430{
432
433 InsertTemplateParameters(*stmt->getTemplateParameters());
435
436 InsertArg(stmt->getConstraintExpr());
439}
440//-----------------------------------------------------------------------------
441
442void CodeGenerator::InsertArg(const ConditionalOperator* stmt)
443{
444 InsertArg(stmt->getCond());
446 InsertArg(stmt->getLHS());
448 InsertArg(stmt->getRHS());
449}
450//-----------------------------------------------------------------------------
451
452void CodeGenerator::InsertArg(const DoStmt* stmt)
453{
455
456 WrapInCompoundIfNeeded(stmt->getBody(), AddNewLineAfter::No);
457
459 WrapInParens([&]() { InsertArg(stmt->getCond()); }, AddSpaceAtTheEnd::No);
460
463}
464//-----------------------------------------------------------------------------
465
466void CodeGenerator::InsertArg(const CaseStmt* stmt)
467{
469 InsertArg(stmt->getLHS());
470
472 InsertArg(stmt->getSubStmt());
473}
474//-----------------------------------------------------------------------------
475
476void CodeGenerator::InsertArg(const BreakStmt* /*stmt*/)
477{
479}
480//-----------------------------------------------------------------------------
481
482void CodeGenerator::InsertArg(const DefaultStmt* stmt)
483{
484 mOutputFormatHelper.Append("default: "sv);
485 InsertArg(stmt->getSubStmt());
486}
487//-----------------------------------------------------------------------------
488
489void CodeGenerator::InsertArg(const ContinueStmt* /*stmt*/)
490{
492}
493//-----------------------------------------------------------------------------
494
495void CodeGenerator::InsertArg(const GotoStmt* stmt)
496{
498 InsertArg(stmt->getLabel());
499}
500//-----------------------------------------------------------------------------
501
502void CodeGenerator::InsertArg(const LabelStmt* stmt)
503{
504 mOutputFormatHelper.AppendNewLine(stmt->getName(), ":"sv);
505
506 if(stmt->getSubStmt()) {
507 InsertArg(stmt->getSubStmt());
508 }
509}
510//-----------------------------------------------------------------------------
511
512void CodeGenerator::InsertArg(const SwitchStmt* stmt)
513{
514 const bool hasInit{stmt->getInit() or stmt->getConditionVariable()};
515
516 if(hasInit) {
518
520 }
521
523
524 WrapInParens([&]() { InsertArg(stmt->getCond()); }, AddSpaceAtTheEnd::Yes);
525
526 InsertArg(stmt->getBody());
527
528 if(hasInit) {
530 }
531
533}
534//-----------------------------------------------------------------------------
535
536void CodeGenerator::InsertArg(const WhileStmt* stmt)
537{
538 auto* rwStmt = const_cast<WhileStmt*>(stmt);
539 auto* conditionVar{rwStmt->getConditionVariable()};
540
541 {
542 // We need to handle the case that a lambda is used in the init-statement of the for-loop.
544
545 if(conditionVar) {
547
548 InsertArg(conditionVar);
549 }
550
552 WrapInParens([&]() { InsertArg(stmt->getCond()); }, AddSpaceAtTheEnd::Yes);
553 }
554
555 if(not conditionVar) {
556 WrapInCompoundIfNeeded(stmt->getBody(), AddNewLineAfter::Yes);
557 } else {
558 const auto& ctx = GetGlobalAST();
559 StmtsContainer bodyStmts{};
560
561 bodyStmts.AddBodyStmts(rwStmt->getBody());
562 bodyStmts.AddBodyStmts(Assign(conditionVar, conditionVar->getInit()));
563
564 InsertArg(mkCompoundStmt(bodyStmts, stmt->getBeginLoc(), stmt->getEndLoc()));
565 }
566
567 if(conditionVar) {
569 }
570
572}
573//-----------------------------------------------------------------------------
574
575/// Get the name of a \c FieldDecl in case this \c FieldDecl is part of a lambda. The name has to be retrieved from the
576/// capture fields or can be \c __this.
577static std::optional<std::string> GetFieldDeclNameForLambda(const FieldDecl& fieldDecl,
578 const CXXRecordDecl& cxxRecordDecl)
579{
580 if(cxxRecordDecl.isLambda()) {
581 llvm::DenseMap<const ValueDecl*, FieldDecl*> captures{};
582 FieldDecl* thisCapture{};
583
584 cxxRecordDecl.getCaptureFields(captures, thisCapture);
585
586 if(&fieldDecl == thisCapture) {
587 return std::string{kwInternalThis};
588 } else {
589 for(const auto& [key, value] : captures) {
590 if(&fieldDecl == value) {
591 return GetName(*key);
592 }
593 }
594 }
595 }
596
597 return {};
598}
599//-----------------------------------------------------------------------------
600
601void CodeGenerator::InsertArg(const SourceLocExpr* stmt)
602{
603 mOutputFormatHelper.Append(stmt->getBuiltinStr(), "()"sv);
604}
605//-----------------------------------------------------------------------------
606
607void CodeGenerator::InsertArg(const MemberExpr* stmt)
608{
609 const auto* base = stmt->getBase();
610 const bool skipBase{[&] {
611 if(const auto* implicitCast = dyn_cast_or_null<ImplicitCastExpr>(base)) {
612 if(CastKind::CK_UncheckedDerivedToBase == implicitCast->getCastKind()) {
613 // if this calls a protected function we cannot cast it to the base, this would not compile
614 return isa<CXXThisExpr>(implicitCast->IgnoreImpCasts());
615 }
616 }
617
618 return false;
619 }()};
620
621 if(skipBase) {
623 }
624
625 InsertArg(base);
626
627 const auto* meDecl = stmt->getMemberDecl();
628 bool skipTemplateArgs{false};
629 const auto name = [&]() -> std::string {
630 // Handle a special case where we have a lambda static invoke operator. In that case use the appropriate
631 // using retType as return type
632 if(const auto* m = dyn_cast_or_null<CXXMethodDecl>(meDecl)) {
633 if(const auto* rd = m->getParent(); rd and rd->isLambda() and isa<CXXConversionDecl>(m)) {
634 skipTemplateArgs = true;
635
636 return StrCat(kwOperatorSpace, GetLambdaName(*rd), "::"sv, BuildRetTypeName(*rd));
637 }
638 }
639
640 // This is at least the case for lambdas, where members are created by capturing a structured binding. See #181.
641 else if(const auto* fd = dyn_cast_or_null<FieldDecl>(meDecl)) {
642 if(const auto* cxxRecordDecl = dyn_cast_or_null<CXXRecordDecl>(fd->getParent())) {
643 if(const auto& fieldName = GetFieldDeclNameForLambda(*fd, *cxxRecordDecl)) {
644 return fieldName.value();
645 }
646 }
647 }
648
649 // Special case. If this is a CXXConversionDecl it might be:
650 // a) a template so we need the template arguments from this type
651 // b) in a namespace and need want to preserve that one.
652 if(const auto* convDecl = dyn_cast_or_null<CXXConversionDecl>(meDecl)) {
653 return StrCat(kwOperatorSpace, GetName(convDecl->getConversionType()));
654 }
655
656 return stmt->getMemberNameInfo().getName().getAsString();
657 }();
658
659 mOutputFormatHelper.Append(ArrowOrDot(stmt->isArrow()));
660
661 if(skipBase) {
663 }
664
666
667 RETURN_IF(skipTemplateArgs);
668
669 if(const auto cxxMethod = dyn_cast_or_null<CXXMethodDecl>(meDecl)) {
670 if(const auto* tmplArgs = cxxMethod->getTemplateSpecializationArgs()) {
671 OutputFormatHelper ofm{};
672
673 ofm.Append('<');
674
675 bool haveArg{false};
676 for(OnceFalse needsComma{}; const auto& arg : tmplArgs->asArray()) {
677 if(arg.getKind() == TemplateArgument::Integral) {
678 ofm.AppendComma(needsComma);
679
680 ofm.Append(arg.getAsIntegral());
681 haveArg = true;
682 } else {
683
684 break;
685 }
686 }
687
688 if(haveArg) {
689 mOutputFormatHelper.Append(ofm, ">"sv);
690
691 } else if(not isa<CXXConversionDecl>(meDecl)) { // A special case from p0892 a templated conversion
692 // operator does not carry the specialization args...
693 InsertTemplateArgs(*tmplArgs);
694 }
695 }
696 }
697}
698//-----------------------------------------------------------------------------
699
700void CodeGenerator::InsertArg(const UnaryExprOrTypeTraitExpr* stmt)
701{
702 mOutputFormatHelper.Append(std::string_view{getTraitSpelling(stmt->getKind())});
703
704 if(not stmt->isArgumentType()) {
705 const auto* argExpr = stmt->getArgumentExpr();
706 const bool needsParens{not isa<ParenExpr>(argExpr)};
707
708 WrapInParensIfNeeded(needsParens, [&] { InsertArg(argExpr); });
709
710 } else {
711 WrapInParens([&] { mOutputFormatHelper.Append(GetName(stmt->getTypeOfArgument())); });
712 }
713}
714//-----------------------------------------------------------------------------
715
716void CodeGenerator::InsertArg(const IntegerLiteral* stmt)
717{
718 const auto& type = stmt->getType();
719 const bool isSigned = type->isSignedIntegerType();
720
721 mOutputFormatHelper.Append(llvm::toString(stmt->getValue(), 10, isSigned));
722
723 InsertSuffix(type);
724}
725//-----------------------------------------------------------------------------
726
727void CodeGenerator::InsertArg(const FloatingLiteral* stmt)
728{
729 mOutputFormatHelper.Append(stmt->getValue());
730 InsertSuffix(stmt->getType());
731}
732//-----------------------------------------------------------------------------
733
734void CodeGenerator::InsertArg(const CXXTypeidExpr* stmt)
735{
737 WrapInParens([&]() {
738 if(stmt->isTypeOperand()) {
739 mOutputFormatHelper.Append(GetName(stmt->getTypeOperand(const_cast<ASTContext&>(GetGlobalAST()))));
740 } else {
741 InsertArg(stmt->getExprOperand());
742 }
743 });
744}
745//-----------------------------------------------------------------------------
746
748{
750
751 BackupAndRestore _{mLastExpr, stmt->getLHS()};
752
753 const bool needLHSParens{isa<BinaryOperator>(stmt->getLHS()->IgnoreImpCasts())};
754 WrapInParensIfNeeded(needLHSParens, [&] { InsertArg(stmt->getLHS()); });
755
756 mOutputFormatHelper.Append(" "sv, stmt->getOpcodeStr(), " "sv);
757
758 const bool needRHSParens{isa<BinaryOperator>(stmt->getRHS()->IgnoreImpCasts())};
759 WrapInParensIfNeeded(needRHSParens, [&] { InsertArg(stmt->getRHS()); });
760}
761//-----------------------------------------------------------------------------
762
763void CodeGenerator::InsertArg(const CompoundAssignOperator* stmt)
764{
766
767 const bool needLHSParens{isa<BinaryOperator>(stmt->getLHS()->IgnoreImpCasts())};
768 WrapInParensIfNeeded(needLHSParens, [&] { InsertArg(stmt->getLHS()); });
769
771
772 // we may need a cast around this back to the src type
773 const bool needCast{stmt->getLHS()->getType() != stmt->getComputationLHSType()};
774 if(needCast) {
775 mOutputFormatHelper.Append(kwStaticCast, "<"sv, GetName(stmt->getLHS()->getType()), ">("sv);
776 }
777
778 WrapInParensIfNeeded(needLHSParens, [&] {
779 if(stmt->getDependence() != ExprDependenceScope::ExprDependence::None) {
780 InsertArg(stmt->getLHS());
781 } else {
782 clang::ExprResult res = stmt->getLHS();
783 // This cast is not present in the AST. However, if the LHS type is smaller than RHS there is an implicit
784 // cast to RHS-type and the result is casted back to LHS-type: static_cast<LHSTy>( static_cast<RHSTy>(LHS) +
785 // RHS )
786 if(const auto resultingType = GetGlobalCI().getSema().PrepareScalarCast(res, stmt->getComputationLHSType());
787 resultingType != CK_NoOp) {
788 const QualType castDestType = stmt->getComputationLHSType();
789 FormatCast(kwStaticCast, castDestType, stmt->getLHS(), resultingType);
790 } else {
791 InsertArg(stmt->getLHS());
792 }
793 }
794 });
795
797 " "sv, BinaryOperator::getOpcodeStr(BinaryOperator::getOpForCompoundAssignment(stmt->getOpcode())), " "sv);
798
799 const bool needRHSParens{isa<BinaryOperator>(stmt->getRHS()->IgnoreImpCasts())};
800 WrapInParensIfNeeded(needRHSParens, [&] { InsertArg(stmt->getRHS()); });
801
802 if(needCast) {
804 }
805}
806//-----------------------------------------------------------------------------
807
808void CodeGenerator::InsertArg(const CXXRewrittenBinaryOperator* stmt)
809{
811
812 InsertArg(stmt->getSemanticForm());
813}
814//-----------------------------------------------------------------------------
815
816static std::string_view GetStorageClassAsString(const StorageClass& sc)
817{
818 if(SC_None != sc) {
819 return VarDecl::getStorageClassSpecifierString(sc);
820 }
821
822 return {};
823}
824//-----------------------------------------------------------------------------
825
826static std::string GetStorageClassAsStringWithSpace(const StorageClass& sc)
827{
828 std::string ret{GetStorageClassAsString(sc)};
829
830 if(not ret.empty()) {
831 ret.append(" "sv);
832 }
833
834 return ret;
835}
836//-----------------------------------------------------------------------------
837
838static std::string GetQualifiers(const VarDecl& vd)
839{
840 std::string qualifiers{};
841
842 if(vd.isInline() or vd.isInlineSpecified()) {
843 qualifiers += kwInlineSpace;
844 }
845
846 qualifiers += GetStorageClassAsStringWithSpace(vd.getStorageClass());
847
848 if(vd.isConstexpr()) {
849 qualifiers += kwConstExprSpace;
850 }
851
852 return qualifiers;
853}
854//-----------------------------------------------------------------------------
855
856static std::string FormatVarTemplateSpecializationDecl(const Decl* decl, std::string&& defaultName)
857{
858 std::string name{std::move(defaultName)};
859
860 if(const auto* tvd = dyn_cast_or_null<VarTemplateSpecializationDecl>(decl)) {
861 OutputFormatHelper outputFormatHelper{};
862 CodeGeneratorVariant codeGenerator{outputFormatHelper};
863
864 codeGenerator->InsertTemplateArgs(tvd->getTemplateArgs());
865
866 name += outputFormatHelper;
867 }
868
869 return name;
870}
871//-----------------------------------------------------------------------------
872
873/// \brief Find a \c DeclRefExpr belonging to a \c DecompositionDecl
874class BindingDeclFinder : public ConstStmtVisitor<BindingDeclFinder>
875{
876 bool mIsBinding{};
877
878public:
879 BindingDeclFinder() = default;
880
881 void VisitDeclRefExpr(const DeclRefExpr* expr)
882 {
883 if(isa<DecompositionDecl>(expr->getDecl())) {
884 mIsBinding = true;
885 }
886 }
887
888 void VisitStmt(const Stmt* stmt)
889 {
890 for(const auto* child : stmt->children()) {
891 if(child) {
892 Visit(child);
893 }
894
895 RETURN_IF(mIsBinding);
896 }
897 }
898
899 bool Find(const Stmt* stmt)
900 {
901 if(stmt) {
902 VisitStmt(stmt);
903 }
904
905 return mIsBinding;
906 }
907};
908//-----------------------------------------------------------------------------
909
910/// \brief Find a \c DeclRefExpr belonging to a \c DecompositionDecl
911class TemporaryDeclFinder : public StmtVisitor<TemporaryDeclFinder>
912{
913 CodeGenerator& codeGenerator;
914 bool mFound{};
915 bool mHaveTemporary{};
916 Stmt* mPrevStmt{};
917 std::string mTempName{};
918 std::vector<VarDecl*> mDecls{};
919
920public:
921 TemporaryDeclFinder(CodeGenerator& _codeGenerator, const Stmt* stmt, bool inspectReturn = false)
922 : codeGenerator{_codeGenerator}
923 , mPrevStmt{const_cast<Stmt*>(stmt)}
924 {
925 RETURN_IF(not GetInsightsOptions().ShowLifetime);
926
927 Visit(mPrevStmt);
928
929 for(auto d : mDecls) {
930 codeGenerator.InsertArg(d);
931 }
932
933 RETURN_IF(not GetInsightsOptions().UseShow2C or mFound or not inspectReturn);
934
935 if(auto* expr = dyn_cast_or_null<CXXConstructExpr>(stmt)) {
936 mTempName = GetTemporaryName(*expr);
937#if 0
938 auto* dummy = Function("dummy"sv, VoidTy(), {});
939
940 auto* vd = Variable(mTempName, expr->getType(), dummy->getDeclContext());
941 dummy->getDeclContext()->addDecl(vd);
942 vd->setInit(const_cast<CXXConstructExpr*>(expr));
943 vd->setStorageClass(SC_None);
944#else
945 // XXX hack. Our normal VarDecl is at TU level. It then appears in cxa_start...
946 auto& ctx = GetGlobalAST();
947 auto* vd = ImplicitParamDecl::Create(const_cast<ASTContext&>(ctx),
948 ctx.getTranslationUnitDecl(),
949 {},
950 &ctx.Idents.get(mTempName),
951 expr->getType(),
952 ImplicitParamKind::Other);
953
954#endif
955
956 mFound = true;
957 codeGenerator.InsertArg(vd);
958
959 } else if(auto* expr = dyn_cast_or_null<InitListExpr>(stmt)) {
960 mTempName = GetTemporaryName(*expr);
961 auto* vd = Variable(mTempName, expr->getType());
962 vd->setInit(const_cast<InitListExpr*>(expr));
963 mFound = true;
964 codeGenerator.InsertArg(vd);
965 }
966 }
967
969 {
970 if(mHaveTemporary) {
971 codeGenerator.EndLifetimeScope();
972 }
973 }
974
975 bool Found() const { return mFound; }
976
977 std::string Name() const { return mTempName; }
978
979 void VisitCXXTemporaryObjectExpr(CXXTemporaryObjectExpr* expr)
980 {
981 mTempName = GetName(*expr);
982 mFound = true;
983
984 auto* vd = Variable(mTempName, expr->getType());
985
986 // In the Cfront case the contents of the expression go after the generated constructor. In the lifetime
987 // _only_ case go with the variable.
988 if(not GetInsightsOptions().UseShow2C) {
989 // Since we insert the statement below we must clone this expression otherwise we look at a recursion.
990 auto* ctorConstructExpr = CXXConstructExpr::Create(GetGlobalAST(),
991 expr->getType(),
992 expr->getBeginLoc(),
993 expr->getConstructor(),
994 expr->isElidable(),
995 {expr->getArgs(), expr->getNumArgs()},
996 expr->hadMultipleCandidates(),
997 expr->isListInitialization(),
998 expr->isStdInitListInitialization(),
999 expr->requiresZeroInitialization(),
1000 expr->getConstructionKind(),
1001 expr->getParenOrBraceRange());
1002
1003 vd->setInit(ctorConstructExpr);
1004
1005 auto* newValue = mkDeclRefExpr(vd);
1006 ReplaceNode(mPrevStmt, expr, newValue);
1007 }
1008
1009 mDecls.push_back(vd);
1010 }
1011
1012#if 0
1013 void VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr* stmt)
1014 {
1015 auto& ctx = GetGlobalAST();
1016
1017 const auto name = MakeLineColumnName(ctx.getSourceManager(), stmt->getBeginLoc(), "temp"sv);
1018 mFound = true;
1019
1020 auto* vd = Variable(name, stmt->getType());
1021 codeGenerator.InsertArg(vd);
1022
1024 }
1025#endif
1026
1027 void VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr* stmt)
1028 {
1030
1031 RETURN_IF(mFound);
1032
1033 auto* vd = Variable(GetTemporaryName(*stmt), stmt->getType());
1034 vd->setInit(stmt->getSubExpr());
1035
1036 auto* newValue = mkDeclRefExpr(vd);
1037 ReplaceNode(mPrevStmt, stmt, newValue);
1038
1039 if(SD_FullExpression == stmt->getStorageDuration() and not mHaveTemporary) {
1040 codeGenerator.StartLifetimeScope();
1041 mHaveTemporary = true;
1042 } else if(const auto* extending = stmt->getExtendingDecl()) {
1043 codeGenerator.LifetimeAddExtended(vd, extending);
1044 }
1045
1046 mDecls.push_back(vd);
1047 }
1048
1049 void VisitStmt(Stmt* stmt)
1050 {
1051 auto* tmp = mPrevStmt;
1052 mPrevStmt = stmt;
1053
1054 for(auto* child : stmt->children()) {
1055 Visit(child);
1056 }
1057
1058 mPrevStmt = tmp;
1059 }
1060
1061 void Visit(Stmt* stmt)
1062 {
1063 if(stmt) {
1065 }
1066 }
1067};
1068//-----------------------------------------------------------------------------
1069
1070/*constinit*/ static SmallVector<std::pair<std::pair<const CXXRecordDecl*, const CXXRecordDecl*>, VarDecl*>, 10>
1072/*constinit*/ static SmallVector<Expr*, 10> globalVarCtors{};
1073/*constinit*/ static SmallVector<Expr*, 10> globalVarDtors{};
1074//-----------------------------------------------------------------------------
1075
1076int GetGlobalVtablePos(const CXXRecordDecl* record, const CXXRecordDecl* recordB)
1077{
1078 auto iter = std::ranges::find_if(
1079 gVtables, [&](const auto& e) { return (e.first.first == record) and (e.first.second == recordB); });
1080
1081 if(iter == gVtables.end()) {
1082 iter = std::ranges::find_if(gVtables, [&](const auto& e) { return e.first.first == record; });
1083 }
1084
1085 return std::distance(gVtables.begin(), iter);
1086}
1087//-----------------------------------------------------------------------------
1088
1089void PushVtableEntry(const CXXRecordDecl* record, const CXXRecordDecl* recordB, VarDecl* decl)
1090{
1091 gVtables.push_back({{record, recordB}, decl});
1092}
1093//-----------------------------------------------------------------------------
1094
1095static void PushGlobalVariable(const Expr* callExpr)
1096{
1097 globalVarCtors.push_back(const_cast<Expr*>(callExpr));
1098}
1099//-----------------------------------------------------------------------------
1100
1101static void PushGlobalVariableDtor(const Expr* callExpr)
1102{
1103 globalVarDtors.push_back(const_cast<Expr*>(callExpr));
1104}
1105//-----------------------------------------------------------------------------
1106
1108{
1109 StmtsContainer bodyStmts{};
1110
1111 for(auto& e : globalVarCtors) {
1112 bodyStmts.AddBodyStmts(e);
1113 }
1114
1115 auto* cxaStartFun = Function(cxaStart, VoidTy(), {});
1116 cxaStartFun->setBody(mkCompoundStmt(bodyStmts));
1117
1118 OutputFormatHelper ofm{};
1119 ofm.AppendNewLine();
1120 ofm.AppendNewLine();
1121 CodeGeneratorVariant cg{ofm};
1122
1123 if(gVtables.size()) {
1124 SmallVector<Expr*, 16> mInitExprs{};
1125
1126 for(auto& e : gVtables) {
1127 cg->InsertArg(e.second);
1128 mInitExprs.push_back(mkDeclRefExpr(e.second));
1129 }
1130
1131 ofm.AppendNewLine();
1132
1133 // struct __mptr *__ptbl_vec__c___src_C_[]
1134 auto* vtable = CfrontCodeGenerator::VtableData().VtblArrayVar(mInitExprs.size());
1135 vtable->setInit(InitList(mInitExprs, vtable->getType()));
1136
1137 cg->InsertArg(vtable);
1138
1139 ofm.AppendNewLine();
1140 }
1141
1142 cg->InsertArg(cxaStartFun);
1143
1144 StmtsContainer bodyStmtsDtors{};
1145
1146 for(auto& e : globalVarDtors) {
1147 bodyStmtsDtors.AddBodyStmts(e);
1148 }
1149
1150 auto* cxaAtExitFun = Function(cxaAtExit, VoidTy(), {});
1151 cxaAtExitFun->setBody(mkCompoundStmt(bodyStmtsDtors));
1152
1153 ofm.AppendNewLine();
1154 cg->InsertArg(cxaAtExitFun);
1155
1156 return ofm.GetString();
1157}
1158//-----------------------------------------------------------------------------
1159
1160void CodeGenerator::LifetimeAddExtended(const VarDecl* vd, const ValueDecl* extending)
1161{
1162 mLifeTimeTracker.AddExtended(vd, extending);
1163}
1164//-----------------------------------------------------------------------------
1165
1170//-----------------------------------------------------------------------------
1171
1176//-----------------------------------------------------------------------------
1177
1178// In a primary template we can see a ParenListExpr with a PackExpansionExpr. With the equal sign we need a type.
1179static bool IsPrimaryTemplatePackExpansionExpr(const ParenListExpr* stmt)
1180{
1181 return stmt and stmt->getNumExprs() and isa_and_nonnull<PackExpansionExpr>(stmt->getExpr(0)) and
1182 stmt->getType().isNull();
1183}
1184//-----------------------------------------------------------------------------
1185
1186void CodeGenerator::InsertArg(const LinkageSpecDecl* stmt)
1187{
1188 mOutputFormatHelper.Append("extern \"", (LinkageSpecLanguageIDs::C == stmt->getLanguage()) ? "C"sv : "C++"sv, "\"");
1190
1191 for(const auto* decl : stmt->decls()) {
1192 InsertArg(decl);
1193 }
1194
1197}
1198//-----------------------------------------------------------------------------
1199
1200void CodeGenerator::InsertTemplateArgsObjectParam(const TemplateParamObjectDecl& param)
1201{
1202 PrintingPolicy pp{GetGlobalAST().getLangOpts()};
1203 pp.adjustForCPlusPlus();
1204
1205 if(auto varName = GetName(param); not mSeenDecls.contains(varName)) {
1206 std::string init{};
1207 ::llvm::raw_string_ostream stream{init};
1208 param.printAsInit(stream, pp);
1209
1210 // https://eel.is/c++draft/temp.param#8 says the variable is `static const`. However, to make the
1211 // compiler accept the generated code the storage object must be constexpr.
1212 // The initialization itself is on the lowest level, int's, floating point or nested structs with them. For
1213 // classes this could fail a all fields even the hidden ones are observed. However, for NTTPs the rule is that
1214 // only structs/classes with _only_ public data members are accepted.
1216 "static constexpr ", GetName(param.getType().getUnqualifiedType()), " ", varName, init);
1217 mSeenDecls[varName] = true;
1218 }
1219}
1220//-----------------------------------------------------------------------------
1221
1222void CodeGenerator::InsertTemplateArgsObjectParam(const ArrayRef<TemplateArgument>& array)
1223{
1224 for(const auto& arg : array) {
1225 if(TemplateArgument::Declaration != arg.getKind()) {
1226 continue;
1227 } else if(const auto decl = dyn_cast_or_null<TemplateParamObjectDecl>(arg.getAsDecl())) {
1229 }
1230 }
1231}
1232//-----------------------------------------------------------------------------
1233
1235{
1236 if(const auto* fd = dyn_cast_or_null<FunctionDecl>(&decl)) {
1237 if(const auto* specArgs = fd->getTemplateSpecializationArgs()) {
1238 InsertTemplateArgsObjectParam(specArgs->asArray());
1239 }
1240 } else if(const auto* vd = dyn_cast_or_null<VarTemplateSpecializationDecl>(&decl)) {
1241 InsertTemplateArgsObjectParam(vd->getTemplateArgs().asArray());
1242 } else if(const auto* clsTemplateSpe = dyn_cast_or_null<ClassTemplateSpecializationDecl>(&decl)) {
1243 InsertTemplateArgsObjectParam(clsTemplateSpe->getTemplateArgs().asArray());
1244 }
1245
1247}
1248//-----------------------------------------------------------------------------
1249
1250void CodeGenerator::InsertArg(const VarDecl* stmt)
1251{
1252 if(auto* init = stmt->getInit();
1253 GetInsightsOptions().UseShow2C and IsReferenceType(stmt) and
1254 (not(isa<CallExpr>(init) or
1255 (isa<ExprWithCleanups>(init) and
1256 (isa<CallExpr>(dyn_cast_or_null<ExprWithCleanups>(init)->getSubExpr()) or
1257 isa<MaterializeTemporaryExpr>(dyn_cast_or_null<ExprWithCleanups>(init)->getSubExpr())))))) {
1258 return;
1259 }
1260
1261 mLifeTimeTracker.Add(stmt);
1262
1263 // If this is part of a DecompositionDecl then ignore this VarDecl as we already have seen and inserted it. This
1264 // happens in StructuredBindingsHandler3Test.cpp
1265 if(BindingDeclFinder isBindingDecl{}; isBindingDecl.Find(stmt->getInit())) {
1266 return;
1267 }
1268
1271
1272 TemporaryDeclFinder temporaryFinder{*this, stmt->getInit()};
1273
1274 if(InsertComma()) {
1276 }
1277
1278 // If we are looking at a static member variable of a class template which is defined out-of-line we need to
1279 // protect the resulting instantiations.
1280 const bool needsGuard = stmt->isOutOfLine() and isTemplateInstantiation(stmt->getTemplateSpecializationKind());
1281
1282 // We are looking at the primary definition of a out-of-line member variable of a class template. We need to add
1283 // the template head.
1284 if(stmt->isOutOfLine()) {
1285 if(const auto* recordDecl = dyn_cast_or_null<CXXRecordDecl>(stmt->getDeclContext())) {
1286 if(const auto* classTmpl = recordDecl->getDescribedClassTemplate()) {
1287 InsertTemplateParameters(*classTmpl->getTemplateParameters());
1288 }
1289 }
1290 }
1291
1292 if(isa<VarTemplateSpecializationDecl>(stmt)) {
1294 } else if(needsGuard) {
1296 }
1297
1298 InsertAttributes(stmt->attrs());
1300
1301 if(IsTrivialStaticClassVarDecl(*stmt)) {
1303
1304 } else {
1305 if(InsertVarDecl(stmt)) {
1306 const auto desugaredType = GetType(GetDesugarType(stmt->getType()));
1307
1308 const bool isMemberPointer{isa<MemberPointerType>(desugaredType.getTypePtrOrNull())};
1309 if(desugaredType->isFunctionPointerType() or isMemberPointer) {
1310 const auto lineNo = GetSM(*stmt).getSpellingLineNumber(stmt->getSourceRange().getBegin());
1311 const auto ptrPrefix = isMemberPointer ? memberVariablePointerPrefix : functionPointerPrefix;
1312 const auto funcPtrName{StrCat(ptrPrefix, lineNo)};
1313
1315 mOutputFormatHelper.Append(GetQualifiers(*stmt), funcPtrName, " "sv, GetName(*stmt));
1316
1317 } else {
1319
1320 const auto scope = [&] {
1321 if(const auto* ctx = stmt->getDeclContext(); stmt->getLexicalDeclContext() != ctx) {
1322 OutputFormatHelper scopeOfm{};
1323 scopeOfm.Append(GetDeclContext(ctx, WithTemplateParameters::Yes));
1324
1325 return ScopeHandler::RemoveCurrentScope(scopeOfm.GetString());
1326 }
1327
1328 return std::string{};
1329 }();
1330
1331 const auto varName = FormatVarTemplateSpecializationDecl(stmt, StrCat(scope, GetName(*stmt)));
1332
1333 // TODO: to keep the special handling for lambdas, do this only for template specializations
1334 mOutputFormatHelper.Append(GetTypeNameAsParameter(GetType(stmt->getType()), varName));
1335 }
1336 } else {
1337 const std::string_view pointer = [&]() {
1338 if(SkipSpaceAfterVarDecl()) {
1339 return ""sv;
1340 }
1341
1342 if(stmt->getType()->isAnyPointerType()) {
1343 return " *"sv;
1344 }
1345 return " "sv;
1346 }();
1347
1348 mOutputFormatHelper.Append(pointer, GetName(*stmt));
1349 }
1350
1351 if(const auto* init = stmt->getInit()) {
1352 if(const auto* ctorExpr = dyn_cast_or_null<CXXConstructExpr>(init);
1353 GetInsightsOptions().UseShow2C and ctorExpr) {
1354
1355 // https://stackoverflow.com/questions/45471470/how-to-generate-code-for-initializing-global-variables-with-non-const-values-in
1356 // https://llvm.org/docs/LangRef.html#the-llvm-global-ctors-global-variable
1357 // https://stackoverflow.com/questions/72971935/why-does-clang-do-thread-safe-init-for-some-globals-but-not-others
1358 // __cxx_global_var_init
1359 // https://discourse.llvm.org/t/static-constructors-cxx-global-var-initn-vs-global-sub-i-xxx/39442
1360 // if(stmt->needsDestruction(stmt->getASTContext())) {
1361 //
1362 // at_exit
1363 // https://clang.llvm.org/doxygen/CGDeclCXX_8cpp_source.html
1364 // __cxa_atexit
1365
1366 // if(not ctorExpr->getConstructor()->isTrivial()) {
1367 if(stmt->hasGlobalStorage()) {
1368 if(ctorExpr->getConstructor()->isDefaultConstructor() and
1369 ctorExpr->getConstructor()->getParent()->hasTrivialDefaultConstructor()) {
1370
1371 auto* callMemset = Call("memset"sv, {Ref(stmt), Int32(0), Sizeof(stmt->getType())});
1372
1374 PushGlobalVariable(callMemset);
1375
1376 } else {
1377
1378 // push to __cxx_global_var_init
1379 auto* callExpr = CallConstructor(
1380 stmt->getType(), stmt, ArgsToExprVector(ctorExpr), DoCast::No, AsReference::Yes);
1381
1382 PushGlobalVariable(callExpr);
1383
1385 }
1386
1387 } else {
1389 InsertArg(init);
1390 }
1391 // }
1392
1393 } else {
1394 BackupAndRestore _{mProcessingVarDecl, true};
1395
1396 if(MyOptional<const InitListExpr*> initList{dyn_cast_or_null<InitListExpr>(init)};
1397 GetInsightsOptions().UseShow2C and
1398 initList.and_then(CanonicalType).and_then(Isa<RecordType>).and_not(IsPointer).and_then(IsPOD) and
1399 not isa<ArrayType>(stmt->getType())) {
1400 auto* callMemset = Call("memset"sv, {Ref(stmt), Int32(0), Sizeof(stmt->getType())});
1401
1403
1404 if(stmt->hasGlobalStorage()) {
1405 PushGlobalVariable(callMemset);
1406
1407 } else {
1409 InsertArg(callMemset);
1410 }
1411
1412 } else if(not(GetInsightsOptions().UseShow2C and
1413 initList.and_then(CanonicalType)
1414 .and_then(Isa<RecordType>)
1415 .and_not(IsPointer)
1416 .and_not(IsPOD) and
1417 (0 == initList.value()->getNumInits()))) {
1418
1419 // Skip the init statement in case we have a class type with a trivial default-constructor which
1420 // is used for this initialization.
1421 if(not(ctorExpr and ctorExpr->getConstructor()->isDefaultConstructor() and
1422 ctorExpr->getConstructor()->getParent()->hasTrivialDefaultConstructor())) {
1423
1424 const bool isPrimaryTemplatePackExpansionExpr{
1425 IsPrimaryTemplatePackExpansionExpr(dyn_cast_or_null<ParenListExpr>(init))};
1426
1427 if(not isa<CXXParenListInitExpr>(init) and not isPrimaryTemplatePackExpansionExpr) {
1429 }
1430
1431 WrapInParensIfNeeded(isPrimaryTemplatePackExpansionExpr, [&] {
1432 if(GetInsightsOptions().ShowLifetime and init->isXValue() and
1433 stmt->getType()->isRValueReferenceType()) {
1434
1435 if(GetInsightsOptions().UseShow2C) {
1436 mOutputFormatHelper.Append("&");
1437 }
1438
1439 InsertArg(StaticCast(stmt->getType(), init, false));
1440
1441 } else {
1442 InsertArg(init);
1443 }
1444 });
1445 }
1446 }
1447 }
1448 } else if(GetInsightsOptions().UseShow2C and stmt->hasGlobalStorage() and
1449 (stmt->getStorageDuration() == SD_Static) and
1450 (stmt->getDeclContext()->isNamespace() or
1451 (is{stmt->getStorageClass()}.any_of(SC_Static, SC_Extern)))) {
1452 PushGlobalVariable(Assign(stmt, Int32(0)));
1453 }
1454
1455 if(stmt->isNRVOVariable()) {
1456 mOutputFormatHelper.Append(" /* NRVO variable */"sv);
1457 }
1458
1459 if(InsertSemi()) {
1461 }
1462
1463 // Insert the bindings of a DecompositionDecl if this VarDecl is a DecompositionDecl.
1464 if(const auto* decompDecl = dyn_cast_or_null<DecompositionDecl>(stmt)) {
1465 StructuredBindingsCodeGenerator codeGenerator{mOutputFormatHelper, GetName(*stmt)};
1466
1467 codeGenerator.InsertDecompositionBindings(*decompDecl);
1468 }
1469 }
1470
1471 if(needsGuard) {
1472 mOutputFormatHelper.InsertEndIfTemplateGuard();
1473 }
1474}
1475//-----------------------------------------------------------------------------
1476
1477bool CodeGenerator::InsertLambdaStaticInvoker(const CXXMethodDecl* cxxMethodDecl)
1478{
1479 if(not(cxxMethodDecl and cxxMethodDecl->isLambdaStaticInvoker())) {
1480 return false;
1481 }
1482
1483 // A special case for a lambda with a static invoker. The standard says, that in such a case invoking the call
1484 // operator gives the same result as invoking the function pointer (see [expr.prim.lambda.closure] p9). When it
1485 // comes to block local statics having a body for both functions reveals a difference. This special code
1486 // generates a forwarding call from the call operator to the static invoker. However, the compiler does better
1487 // here. As this way we end up with copies of the parameters which is hard to avoid.
1488
1491
1492 if(not cxxMethodDecl->getReturnType()->isVoidType()) {
1494 }
1495
1496 mOutputFormatHelper.Append(GetName(*cxxMethodDecl->getParent()), "{}.operator()"sv);
1497
1498 if(cxxMethodDecl->isFunctionTemplateSpecialization()) {
1499 InsertTemplateArgs(*dyn_cast_or_null<FunctionDecl>(cxxMethodDecl));
1500 }
1501
1502 if(cxxMethodDecl->isTemplated()) {
1503 if(cxxMethodDecl->getDescribedTemplate()) {
1504 InsertTemplateParameters(*cxxMethodDecl->getDescribedTemplate()->getTemplateParameters(),
1505 TemplateParamsOnly::Yes);
1506 }
1507 /*else if(decl.isFunctionTemplateSpecialization()) {
1508 InsertTemplateSpecializationHeader();
1509 }*/
1510 }
1511
1512 WrapInParens([&] {
1513 mOutputFormatHelper.AppendParameterList(cxxMethodDecl->parameters(),
1514 OutputFormatHelper::NameOnly::Yes,
1515 OutputFormatHelper::GenMissingParamName::Yes);
1516 });
1517
1519 mOutputFormatHelper.CloseScope(OutputFormatHelper::NoNewLineBefore::Yes);
1521
1522 return true;
1523}
1524//-----------------------------------------------------------------------------
1525
1526/// \brief Inserts the instantiation point of a template.
1527//
1528// This reveals at which place the template is first used.
1529void CodeGenerator::InsertInstantiationPoint(const SourceManager& sm,
1530 const SourceLocation& instLoc,
1531 std::string_view text)
1532{
1533 const auto lineNo = sm.getSpellingLineNumber(instLoc);
1534 const auto& fileId = sm.getFileID(instLoc);
1535 if(const auto file = sm.getFileEntryRefForID(fileId)) {
1536 const auto fileWithDirName = file->getName();
1537 const auto fileName = llvm::sys::path::filename(fileWithDirName);
1538
1539 if(text.empty()) {
1540 text = "First instantiated from: "sv;
1541 }
1542
1543 mOutputFormatHelper.AppendCommentNewLine(text, fileName, ":"sv, lineNo);
1544 }
1545}
1546//-----------------------------------------------------------------------------
1547
1548void CodeGenerator::InsertTemplateGuardBegin(const FunctionDecl* stmt)
1549{
1550 if(stmt->isTemplateInstantiation() and stmt->isFunctionTemplateSpecialization()) {
1551 InsertInstantiationPoint(GetSM(*stmt), stmt->getPointOfInstantiation());
1553 }
1554}
1555//-----------------------------------------------------------------------------
1556
1557void CodeGenerator::InsertTemplateGuardEnd(const FunctionDecl* stmt)
1558{
1559 if(stmt->isTemplateInstantiation() and stmt->isFunctionTemplateSpecialization()) {
1561 }
1562}
1563//-----------------------------------------------------------------------------
1564
1565void CodeGenerator::InsertArg(const CoroutineBodyStmt* stmt)
1566{
1567 InsertArg(stmt->getBody());
1568}
1569//-----------------------------------------------------------------------------
1570
1571void CodeGenerator::InsertArg(const DependentCoawaitExpr* stmt)
1572{
1574
1575 InsertArg(stmt->getOperand());
1576}
1577//-----------------------------------------------------------------------------
1578
1579void CodeGenerator::InsertArg(const CoroutineSuspendExpr* stmt)
1580{
1581 // co_await or co_yield
1582 if(isa<CoyieldExpr>(stmt)) {
1584 } else {
1586 }
1587
1588 // peal of __promise.yield_value
1589 if(const auto* matTemp = dyn_cast_or_null<MaterializeTemporaryExpr>(stmt->getCommonExpr())) {
1590 const auto* temporary = matTemp->getSubExpr();
1591
1592 if(const auto* memExpr = dyn_cast_or_null<CXXMemberCallExpr>(temporary)) {
1593 ForEachArg(memExpr->arguments(), [&](const auto& arg) { InsertArg(arg); });
1594
1595 // Seems to be the path for a co_await expr
1596 } else {
1597 InsertArg(temporary);
1598 }
1599 } else if(const auto* unaryexpr = dyn_cast_or_null<UnaryOperator>(stmt->getOperand())) {
1600 if(const auto* callExpr = dyn_cast_or_null<CallExpr>(unaryexpr->getSubExpr())) {
1601 InsertArg(callExpr->getArg(0));
1602 }
1603 }
1604}
1605//-----------------------------------------------------------------------------
1606
1607void CodeGenerator::InsertArg(const CoreturnStmt* stmt)
1608{
1610 InsertArg(stmt->getOperand());
1611}
1612//-----------------------------------------------------------------------------
1613
1614void CodeGenerator::InsertMethodBody(const FunctionDecl* stmt, const size_t posBeforeFunc)
1615{
1616 auto IsPrimaryTemplate = [&] {
1617 // For now, don't transform the primary template of a coroutine
1618 if(const auto* cxxMethod = dyn_cast_or_null<CXXMethodDecl>(stmt)) {
1619 if(const auto* tmpl = cxxMethod->getParent()->getDescribedClassTemplate();
1620 tmpl and not isa<ClassTemplateSpecializationDecl>(cxxMethod->getParent())) {
1621 return true;
1622 }
1623 }
1624
1625 return (FunctionDecl::TK_FunctionTemplate == stmt->getTemplatedKind()) or
1626 (ProcessingPrimaryTemplate::Yes == mProcessingPrimaryTemplate);
1627 };
1628
1629 if(stmt->doesThisDeclarationHaveABody()) {
1631
1632 // If this function has a CoroutineBodyStmt as direct descend and coroutine transformation is enabled use
1633 // the \c CoroutinesCodeGenerator, otherwise insert the body as usual.
1634 if(const auto* corBody = dyn_cast_or_null<CoroutineBodyStmt>(stmt->getBody());
1635 (nullptr != corBody) and not IsPrimaryTemplate() and GetInsightsOptions().ShowCoroutineTransformation) {
1636
1637 CoroutinesCodeGenerator codeGenerator{mOutputFormatHelper, posBeforeFunc};
1638 codeGenerator.InsertCoroutine(*stmt, corBody);
1639 } else {
1640 const auto exSpec = stmt->getExceptionSpecType();
1641 const bool showNoexcept =
1642 GetInsightsOptions().UseShowNoexcept and is{exSpec}.any_of(EST_BasicNoexcept, EST_NoexceptTrue);
1643
1644 // handle C++ [basic.start.main] §5: main can have no return statement
1645 if(stmt->hasImplicitReturnZero()) {
1646 mRequiresImplicitReturnZero = ranges::none_of(dyn_cast<CompoundStmt>(stmt->getBody())->body(),
1647 [](const Stmt* e) { return isa<ReturnStmt>(e); });
1648 }
1649
1650 const auto* body = stmt->getBody();
1651
1652 if(showNoexcept) {
1654
1655 body = mkCompoundStmt(Try(body, Catch(Call("std::terminate"sv, {}))));
1656 }
1657
1658 if(GetInsightsOptions().ShowLifetime) {
1659 for(const auto* param : stmt->parameters()) {
1660 auto paramType = param->getType();
1661 const bool isPassByValue{not paramType->isPointerType() and not paramType->isReferenceType()};
1662 if(const auto* rd = paramType->getAsRecordDecl(); rd and isPassByValue) {
1663 mLifeTimeTracker.Add(param);
1664 }
1665 }
1666 }
1667
1668 InsertArg(body);
1669 }
1670
1672 } else {
1674 }
1675}
1676//-----------------------------------------------------------------------------
1677
1678void CodeGenerator::InsertArg(const FunctionDecl* stmt)
1679{
1680 {
1681 LAMBDA_SCOPE_HELPER(Decltype); // Needed for P0315Checker
1682
1683 // Special handling for C++20's P0315 (lambda in unevaluated context). See p0315_2Test.cpp
1684 // We have to look for the lambda expression in the decltype.
1685 P0315Visitor dt{*this};
1686 dt.TraverseType(stmt->getReturnType());
1687
1688 // The arguments can contain a lambda as well
1689 for(const auto& param : stmt->parameters()) {
1690 P0315Visitor dt{*this};
1691 dt.TraverseType(param->getType());
1692 }
1693 }
1694
1695 if(const auto* deductionGuide = dyn_cast_or_null<CXXDeductionGuideDecl>(stmt)) {
1696 InsertArg(deductionGuide);
1697 } else if(const auto* ctor = dyn_cast_or_null<CXXConstructorDecl>(stmt)) {
1698 InsertArg(ctor);
1699 } else {
1700 // skip a case at least in lambdas with a templated conversion operator which is not used and has auto
1701 // return type. This is hard to build with using.
1702 RETURN_IF(isa<CXXConversionDecl>(stmt) and not stmt->hasBody());
1703
1704 const auto posBeforeFunc = mOutputFormatHelper.CurrentPos();
1705
1708
1709 if(not InsertLambdaStaticInvoker(dyn_cast_or_null<CXXMethodDecl>(stmt))) {
1710 InsertMethodBody(stmt, posBeforeFunc);
1711 }
1712
1714 }
1715}
1716//-----------------------------------------------------------------------------
1717
1718static std::string GetTypeConstraintAsString(const TypeConstraint* typeConstraint)
1719{
1720 if(typeConstraint) {
1721 StringStream sstream{};
1722 sstream.Print(*typeConstraint);
1723
1724 return sstream.str();
1725 }
1726
1727 return {};
1728}
1729//-----------------------------------------------------------------------------
1730
1731static std::string_view Ellipsis(bool b)
1732{
1733 return ValueOrDefault(b, kwElipsis);
1734}
1735//-----------------------------------------------------------------------------
1736
1737static std::string_view EllipsisSpace(bool b)
1738{
1739 return ValueOrDefault(b, kwElipsisSpace);
1740}
1741//-----------------------------------------------------------------------------
1742
1743/// \brief Evaluates a potential NTTP as a constant expression.
1744///
1745/// Used for C++20's struct/class as NTTP.
1746static std::optional<std::pair<QualType, APValue>> EvaluateNTTPAsConstantExpr(const Expr* expr)
1747{
1748 expr = expr->IgnoreParenImpCasts();
1749
1750 // The marker when it is a C++20 class as NTTP seems to be CXXFunctionalCastExpr
1751 if(Expr::EvalResult evalResult{};
1752 isa<CXXFunctionalCastExpr>(expr) and
1753 expr->EvaluateAsConstantExpr(evalResult, GetGlobalAST(), ConstantExprKind::Normal)) {
1754 return std::pair<QualType, APValue>{expr->getType(), evalResult.Val};
1755 }
1756
1757 return {};
1758}
1759//-----------------------------------------------------------------------------
1760
1761void CodeGenerator::InsertTemplateParameters(const TemplateParameterList& list,
1762 const TemplateParamsOnly templateParamsOnly)
1763{
1764 const bool full{TemplateParamsOnly::No == templateParamsOnly};
1765
1766 if(full) {
1767 for(const auto* param : list) {
1768 if(const auto* nonTmplParam = dyn_cast_or_null<NonTypeTemplateParmDecl>(param);
1769 nonTmplParam and nonTmplParam->hasDefaultArgument()) {
1770 if(auto val =
1771 EvaluateNTTPAsConstantExpr(nonTmplParam->getDefaultArgument().getArgument().getAsExpr())) {
1772 auto* init = GetGlobalAST().getTemplateParamObjectDecl(val->first, val->second);
1773
1775 }
1776 }
1777 }
1778
1780 }
1781
1783
1784 for(OnceFalse needsComma{}; const auto* param : list) {
1785 mOutputFormatHelper.AppendComma(needsComma);
1786
1787 const auto& typeName = GetName(*param);
1788
1789 if(const auto* tt = dyn_cast_or_null<TemplateTypeParmDecl>(param)) {
1790 if(full) {
1791 if(tt->wasDeclaredWithTypename()) {
1793 } else if(not tt->hasTypeConstraint()) {
1795 }
1796
1797 mOutputFormatHelper.Append(EllipsisSpace(tt->isParameterPack()));
1798 }
1799
1800 if(0 == typeName.size() or tt->isImplicit() /* fixes class container:auto*/) {
1802
1803 } else {
1804 if(auto typeConstraint = GetTypeConstraintAsString(tt->getTypeConstraint());
1805 not typeConstraint.empty()) {
1806 mOutputFormatHelper.Append(std::move(typeConstraint), " "sv);
1807 }
1808
1809 mOutputFormatHelper.Append(typeName);
1810 }
1811
1812 mOutputFormatHelper.Append(EllipsisSpace(not full and tt->isParameterPack()));
1813
1814 if(tt->hasDefaultArgument() and not tt->defaultArgumentWasInherited()) {
1815 const auto& defaultArg = tt->getDefaultArgument();
1816
1817 if(const auto decltypeType = dyn_cast_or_null<DecltypeType>(defaultArg.getArgument().getAsType())) {
1819
1820 InsertArg(decltypeType->getUnderlyingExpr());
1821
1822 } else {
1824 InsertTemplateArg(defaultArg.getArgument());
1825 }
1826 }
1827
1828 } else if(const auto* nonTmplParam = dyn_cast_or_null<NonTypeTemplateParmDecl>(param)) {
1829 if(full) {
1830 if(const auto nttpType = nonTmplParam->getType();
1831 nttpType->isFunctionPointerType() or nttpType->isMemberFunctionPointerType()) {
1833
1834 } else {
1836 GetName(nttpType), " "sv, Ellipsis(nonTmplParam->isParameterPack()), typeName);
1837 }
1838
1839 if(nonTmplParam->hasDefaultArgument()) {
1841 InsertTemplateArg(nonTmplParam->getDefaultArgument().getArgument());
1842 }
1843 } else {
1844 mOutputFormatHelper.Append(typeName, EllipsisSpace(nonTmplParam->isParameterPack()));
1845 }
1846 } else if(const auto* tmplTmplParam = dyn_cast_or_null<TemplateTemplateParmDecl>(param)) {
1847 auto pack{ValueOr(tmplTmplParam->isParameterPack(), kwElipsisSpace, " "sv)};
1848
1849 mOutputFormatHelper.Append(kwTemplateSpace, "<typename> typename"sv, pack, typeName);
1850
1851 if(tmplTmplParam->hasDefaultArgument()) {
1853 InsertTemplateArg(tmplTmplParam->getDefaultArgument().getArgument());
1854 }
1855 }
1856 }
1857
1859
1860 if(full) {
1863 }
1864}
1865//-----------------------------------------------------------------------------
1866
1867void CodeGenerator::InsertArg(const ClassTemplateDecl* stmt)
1868{
1869 {
1870 LAMBDA_SCOPE_HELPER(DecltypeWithName); // Needed for P0315Checker
1871
1872 InsertTemplateParameters(*stmt->getTemplateParameters());
1873 }
1874
1875 InsertArg(stmt->getTemplatedDecl());
1876
1877 SmallVector<const ClassTemplateSpecializationDecl*, 10> specializations{};
1878
1879 // XXX C++23: replace with filter and ranges::to<>
1880 for(const auto* spec : stmt->specializations()) {
1881 // Explicit specializations and instantiations will appear later in the AST as dedicated node. Don't
1882 // generate code for them now, otherwise they are there twice.
1883 if(TSK_ImplicitInstantiation == spec->getSpecializationKind()) {
1884 specializations.push_back(spec);
1885 }
1886 }
1887
1888 // Sort specializations by POI to make dependent specializations work.
1889 ranges::sort(specializations,
1890 [](const ClassTemplateSpecializationDecl* a, const ClassTemplateSpecializationDecl* b) {
1891 return a->getPointOfInstantiation() < b->getPointOfInstantiation();
1892 });
1893
1894 for(const auto* spec : specializations) {
1895 InsertArg(spec);
1896 }
1897}
1898//-----------------------------------------------------------------------------
1899
1900void CodeGenerator::InsertArg(const ParenListExpr* stmt)
1901{
1902 for(OnceFalse needsComma{}; const auto& expr : stmt->children()) {
1903 mOutputFormatHelper.AppendComma(needsComma);
1904
1905 InsertArg(expr);
1906 }
1907}
1908//-----------------------------------------------------------------------------
1909
1910/// Fill the values of a constant array.
1911///
1912/// This is either called by \c InitListExpr (which may contain an offset, as the user already provided certain
1913/// values) or by \c GetValueOfValueInit.
1914std::string
1915CodeGenerator::FillConstantArray(const ConstantArrayType* ct, const std::string& value, const uint64_t startAt)
1916{
1917 OutputFormatHelper ret{};
1918
1919 if(ct) {
1920 const auto size{std::clamp(GetSize(ct), uint64_t{0}, MAX_FILL_VALUES_FOR_ARRAYS)};
1921
1922 OnceFalse needsComma{uint64_t{0} != startAt};
1923 for_each(startAt, size, [&](auto) {
1924 ret.AppendComma(needsComma);
1925 ret.Append(value);
1926 });
1927 }
1928
1929 return ret.GetString();
1930}
1931//-----------------------------------------------------------------------------
1932
1933void CodeGenerator::InsertArg(const InitListExpr* stmt)
1934{
1935 // At least in case of a requires-clause containing T{} we don't want to get T{{}}.
1936 RETURN_IF((NoEmptyInitList::Yes == mNoEmptyInitList) and (0 == stmt->getNumInits()));
1937
1938 WrapInCurliesIfNeeded(not GetInsightsOptions().UseShow2C or
1939 (GetInsightsOptions().UseShow2C and
1940 ((stmt->getNumInits() > 1) or stmt->getArrayFiller() or
1941 ((0 < stmt->getNumInits()) and isa<ImplicitValueInitExpr>(stmt->getInit(0))))),
1942 [&]() {
1943 mOutputFormatHelper.IncreaseIndent();
1944
1945 ForEachArg(stmt->inits(), [&](const auto& init) { InsertArg(init); });
1946
1947 if((0 == stmt->getNumInits()) and GetInsightsOptions().UseShow2C) {
1948 if(stmt->getType().getCanonicalType()->isScalarType()) {
1950 return;
1951 } else {
1952 // for a non scalar type that shoud become a memset?
1953 // A a{}; -> A a; memset(&a, 0, sizef(a));
1954 // mOutputFormatHelper.Append("memset("sv);
1955 }
1956 }
1957
1958 // If we have a filler, fill the rest of the array with the filler expr.
1959 if(const auto* filler = stmt->getArrayFiller()) {
1960 OutputFormatHelper ofm{};
1961 CodeGeneratorVariant codeGenerator{ofm};
1962 codeGenerator->InsertArg(filler);
1963
1964 const auto ret = FillConstantArray(
1965 dyn_cast_or_null<ConstantArrayType>(stmt->getType().getTypePtrOrNull()),
1966 ofm.GetString(),
1967 stmt->getNumInits());
1968
1970 }
1971 });
1972
1973 mOutputFormatHelper.DecreaseIndent();
1974}
1975//-----------------------------------------------------------------------------
1976
1977void CodeGenerator::InsertArg(const CXXDefaultInitExpr* stmt)
1978{
1979 const auto* subExpr = stmt->getExpr();
1980
1981 InsertCurlysIfRequired(subExpr);
1982}
1983//-----------------------------------------------------------------------------
1984
1985void CodeGenerator::InsertArg(const CXXDeleteExpr* stmt)
1986{
1988
1989 if(stmt->isArrayForm()) {
1991 }
1992
1994
1995 InsertArg(stmt->getArgument());
1996}
1997//-----------------------------------------------------------------------------
1998
2000{
2001 {
2002 // only use this a insert point, if we are not coming from a decltype, for example decltype(decltype(...)) or
2003 // decltype inside a concept.
2005
2006 P0315Visitor dt{*this};
2007 dt.TraverseType(stmt->getType());
2008 }
2009
2010 mOutputFormatHelper.Append(GetName(stmt->getType(), Unqualified::Yes));
2011
2012 const BraceKind braceKind = ValueOr(stmt->isListInitialization(), BraceKind::Curlys, BraceKind::Parens);
2013
2014 WrapInParensOrCurlys(braceKind, [&]() {
2015 if(const auto& arguments = stmt->arguments(); not arguments.empty()) {
2016 ForEachArg(stmt->arguments(), [&](const auto& arg) { InsertArg(arg); });
2017 }
2018 });
2019}
2020//-----------------------------------------------------------------------------
2021
2022void CodeGenerator::InsertArg(const CXXConstructExpr* stmt)
2023{
2025}
2026//-----------------------------------------------------------------------------
2027
2028void CodeGenerator::InsertArg(const CXXUnresolvedConstructExpr* stmt)
2029{
2030 BackupAndRestore _{mNoEmptyInitList, NoEmptyInitList::Yes};
2031
2033}
2034//-----------------------------------------------------------------------------
2035
2036void CodeGenerator::InsertArg(const UnresolvedMemberExpr* stmt)
2037{
2038 // InsertArg(stmt->getBase());
2039 // const std::string op{}; // stmt->isArrow() ? "->" : "."};
2040
2041 // mOutputFormatHelper.Append(op, stmt->getMemberNameInfo().getAsString());
2042 mOutputFormatHelper.Append(stmt->getMemberNameInfo().getAsString());
2043
2044 if(stmt->getNumTemplateArgs()) {
2045 InsertTemplateArgs(*stmt);
2046 }
2047}
2048//-----------------------------------------------------------------------------
2049
2050void CodeGenerator::InsertArg(const PackExpansionExpr* stmt)
2051{
2052 InsertArg(stmt->getPattern());
2054}
2055//-----------------------------------------------------------------------------
2056
2057void CodeGenerator::InsertArg(const CXXFoldExpr* stmt)
2058{
2059 auto operatorStr = BinaryOperator::getOpcodeStr(stmt->getOperator());
2060
2061 WrapInParens([&] {
2062 // We have a binary NNN fold. If init is nullptr, then it is a unary NNN fold.
2063 const auto* init = stmt->getInit();
2064
2065 if(stmt->isLeftFold()) {
2066 if(init) {
2067 InsertArg(init);
2068 mOutputFormatHelper.Append(" "sv, operatorStr, " "sv);
2069 }
2070
2071 mOutputFormatHelper.Append(kwElipsisSpace, operatorStr, " "sv);
2072 }
2073
2074 InsertArg(stmt->getPattern());
2075
2076 if(stmt->isRightFold()) {
2077 mOutputFormatHelper.Append(" "sv, operatorStr, " "sv, kwElipsis);
2078
2079 if(init) {
2080 mOutputFormatHelper.Append(" "sv, operatorStr, " "sv);
2081 InsertArg(init);
2082 }
2083 }
2084 });
2085}
2086//-----------------------------------------------------------------------------
2087
2088void CodeGenerator::InsertArg(const PackIndexingExpr* stmt)
2089{
2090 if(stmt->isFullySubstituted()) {
2091 const auto* constExpr = dyn_cast_or_null<ConstantExpr>(stmt->getIndexExpr());
2092
2094 constExpr->getAPValueResult().getInt());
2095
2096 } else {
2097 mOutputFormatHelper.Append(GetName(*stmt->getPackDecl()), "...[");
2098
2099 InsertArg(stmt->getIndexExpr());
2101 }
2102}
2103//-----------------------------------------------------------------------------
2104
2105void CodeGenerator::InsertArg(const CXXInheritedCtorInitExpr* stmt)
2106{
2107 const auto& constructorDecl = *stmt->getConstructor();
2108
2109 mOutputFormatHelper.Append(GetName(GetDesugarType(stmt->getType()), Unqualified::Yes));
2110 WrapInParens([&]() {
2111 mOutputFormatHelper.AppendParameterList(constructorDecl.parameters(),
2112 OutputFormatHelper::NameOnly::Yes,
2113 OutputFormatHelper::GenMissingParamName::Yes);
2114 });
2115}
2116//-----------------------------------------------------------------------------
2117
2122//-----------------------------------------------------------------------------
2123
2124void CodeGenerator::InsertArg(const CXXPseudoDestructorExpr* stmt)
2125{
2126 InsertArg(stmt->getBase());
2127
2128 mOutputFormatHelper.Append(ArrowOrDot(stmt->isArrow()), "~", GetName(stmt->getDestroyedType()));
2129}
2130//-----------------------------------------------------------------------------
2131
2132void CodeGenerator::InsertArg(const CXXMemberCallExpr* stmt)
2133{
2135
2136 InsertArg(stmt->getCallee());
2137
2138 WrapInParens([&]() { ForEachArg(stmt->arguments(), [&](const auto& arg) { InsertArg(arg); }); });
2139}
2140//-----------------------------------------------------------------------------
2141
2142void CodeGenerator::InsertArg(const ParenExpr* stmt)
2143{
2144 WrapInParens([&]() { InsertArg(stmt->getSubExpr()); });
2145}
2146//-----------------------------------------------------------------------------
2147
2148void CodeGenerator::InsertArg(const CXXParenListInitExpr* stmt)
2149{
2150 WrapInParens([&]() { ForEachArg(stmt->getInitExprs(), [&](const auto& init) { InsertArg(init); }); });
2151}
2152//-----------------------------------------------------------------------------
2153
2154void CodeGenerator::InsertArg(const UnaryOperator* stmt)
2155{
2156 StringRef opCodeName = UnaryOperator::getOpcodeStr(stmt->getOpcode());
2157 const bool insertBefore{not stmt->isPostfix()};
2158
2159 if(insertBefore) {
2160 mOutputFormatHelper.Append(opCodeName);
2161 }
2162
2163 InsertArg(stmt->getSubExpr());
2164
2165 if(not insertBefore) {
2166 mOutputFormatHelper.Append(opCodeName);
2167 }
2168}
2169//------------- ----------------------------------------------------------------
2170
2171void CodeGenerator::InsertArg(const StringLiteral* stmt)
2172{
2173 StringStream stream{};
2174 stream.Print(*stmt);
2175
2176 mOutputFormatHelper.Append(stream.str());
2177}
2178//-----------------------------------------------------------------------------
2179
2180void CodeGenerator::InsertArg(const ArrayInitIndexExpr* stmt)
2181{
2182 Error(stmt, "ArrayInitIndexExpr should not be reached in CodeGenerator");
2183}
2184//-----------------------------------------------------------------------------
2185
2186void CodeGenerator::InsertArg(const ArraySubscriptExpr* stmt)
2187{
2188 if((not GetInsightsOptions().UseAltArraySubscriptionSyntax) or stmt->getLHS()->isLValue()) {
2189 InsertArg(stmt->getLHS());
2190
2192 InsertArg(stmt->getRHS());
2194 } else {
2195
2196 mOutputFormatHelper.Append("(*("sv);
2197 InsertArg(stmt->getLHS());
2198 mOutputFormatHelper.Append(" + "sv);
2199
2200 InsertArg(stmt->getRHS());
2202 }
2203}
2204//-----------------------------------------------------------------------------
2205
2206void CodeGenerator::InsertArg(const ArrayInitLoopExpr* stmt)
2207{
2208 WrapInCurlys([&]() {
2209 const uint64_t size = stmt->getArraySize().getZExtValue();
2210
2211 ForEachArg(NumberIterator(size), [&](const auto& i) {
2212 ArrayInitCodeGenerator codeGenerator{mOutputFormatHelper, i};
2213 codeGenerator.InsertArg(stmt->getSubExpr());
2214 });
2215 });
2216}
2217//-----------------------------------------------------------------------------
2218
2219void CodeGenerator::InsertArg(const OpaqueValueExpr* stmt)
2220{
2221 InsertArg(stmt->getSourceExpr());
2222}
2223//-----------------------------------------------------------------------------
2224
2225void CodeGenerator::InsertArg(const CallExpr* stmt)
2226{
2227 const bool insideDecltype{InsideDecltype()};
2228
2229 CONDITIONAL_LAMBDA_SCOPE_HELPER(CallExpr, not insideDecltype)
2230 if(insideDecltype) {
2232 }
2233
2235
2236 InsertArg(stmt->getCallee());
2237
2238 if(const auto* declRefExpr = dyn_cast_or_null<DeclRefExpr>(stmt->getCallee()->IgnoreImpCasts())) {
2239 if(const auto* fd = dyn_cast_or_null<FunctionDecl>(declRefExpr->getDecl())) {
2240 if((not declRefExpr->getNumTemplateArgs() and GetInsightsOptions().ShowAllCallExprTemplateParameters) or
2241 isa<UserDefinedLiteral>(stmt)) {
2242 InsertTemplateArgs(*fd);
2243 }
2244 }
2245 }
2246
2247 WrapInParens([&]() {
2248 auto* funcDecl = dyn_cast_or_null<FunctionDecl>(stmt->getCalleeDecl());
2249 unsigned parmIdx{};
2250
2251 ForEachArg(stmt->arguments(), [&](const auto* arg) {
2252 ++parmIdx;
2253
2254 if(const auto* tmpExpr = dyn_cast_or_null<CXXBindTemporaryExpr>(arg)) {
2255 if(const auto* tmp = dyn_cast_or_null<CXXTemporaryObjectExpr>(tmpExpr->getSubExpr())) {
2256 if(GetInsightsOptions().UseShow2C) {
2257 // De-reference the argument since we can only pass temporary objects to functions, not
2258 // pointers
2259 arg = Dref(arg);
2260
2261 } else if(GetInsightsOptions().ShowLifetime) {
2262 mOutputFormatHelper.Append(GetName(*tmp));
2263 return; // from lambda, which is like a continue
2264 }
2265 }
2266 }
2267
2268 if(GetInsightsOptions().UseShow2C and funcDecl and (funcDecl->getNumParams() >= parmIdx) and
2269 IsReferenceType(funcDecl->getParamDecl(parmIdx - 1))) {
2270 if(auto* unop = dyn_cast_or_null<UnaryOperator>(arg); not unop or (unop->getOpcode() != UO_AddrOf)) {
2271 arg = Ref(arg);
2272 }
2273 }
2274
2275 InsertArg(arg);
2276 });
2277 });
2278
2279 if(insideDecltype) {
2280 mLambdaStack.back().setCallerType(LambdaCallerType::Decltype);
2281 }
2282
2283 mCurrentCallExprPos.reset();
2284}
2285//-----------------------------------------------------------------------------
2286
2287void CodeGenerator::InsertArg(const CXXNamedCastExpr* stmt)
2288{
2289 const QualType castDestType = stmt->getTypeAsWritten();
2290 const Expr* subExpr = stmt->getSubExpr();
2291
2292 FormatCast(stmt->getCastName(), castDestType, subExpr, stmt->getCastKind());
2293}
2294//-----------------------------------------------------------------------------
2295
2296void CodeGenerator::InsertArg(const ImplicitCastExpr* stmt)
2297{
2298 const Expr* subExpr = stmt->getSubExpr();
2299 const auto castKind = stmt->getCastKind();
2300 const bool hideImplicitCasts{not GetInsightsOptions().ShowAllImplicitCasts};
2301
2302 auto isMatchingCast = [](const CastKind kind, const bool hideImplicitCasts, const bool showXValueCasts) {
2303 switch(kind) {
2304 case CastKind::CK_Dependent: [[fallthrough]];
2305 case CastKind::CK_IntegralCast: [[fallthrough]];
2306 case CastKind::CK_IntegralToBoolean: [[fallthrough]];
2307 case CastKind::CK_IntegralToPointer: [[fallthrough]];
2308 case CastKind::CK_PointerToIntegral: [[fallthrough]];
2309 case CastKind::CK_BitCast: [[fallthrough]];
2310 case CastKind::CK_UncheckedDerivedToBase: [[fallthrough]];
2311 case CastKind::CK_ToUnion:
2312 [[fallthrough]];
2313 // case CastKind::CK_UserDefinedConversion: [[fallthrough]];
2314 case CastKind::CK_AtomicToNonAtomic: [[fallthrough]];
2315 case CastKind::CK_DerivedToBase: [[fallthrough]];
2316 case CastKind::CK_FloatingCast: [[fallthrough]];
2317 case CastKind::CK_IntegralToFloating: [[fallthrough]];
2318 case CastKind::CK_FloatingToIntegral: [[fallthrough]];
2319 case CastKind::CK_NonAtomicToAtomic: return true;
2320 default:
2321 // Special case for structured bindings
2322 if((showXValueCasts or not hideImplicitCasts) and (CastKind::CK_NoOp == kind)) {
2323 return true;
2324 }
2325
2326 // Show this casts only if ShowAllImplicitCasts is turned on.
2327 if(not hideImplicitCasts) {
2328 switch(kind) {
2329 case CastKind::CK_NullToPointer: [[fallthrough]];
2330 case CastKind::CK_NullToMemberPointer: [[fallthrough]];
2331 /* these are implicit conversions. We get them right, but they may end up in a compiler
2332 * internal type, which leads to compiler errors */
2333 case CastKind::CK_NoOp: [[fallthrough]];
2334 case CastKind::CK_ArrayToPointerDecay: return true;
2335 default: break;
2336 }
2337 }
2338
2339 return false;
2340 }
2341 }(castKind, hideImplicitCasts, stmt->isXValue() or ShowXValueCasts());
2342
2343 if(not isMatchingCast) {
2344 if(GetInsightsOptions().UseShow2C and (castKind == CastKind::CK_LValueToRValue) and
2345 IsReferenceType(dyn_cast_or_null<DeclRefExpr>(subExpr))) {
2347 }
2348
2349 InsertArg(subExpr);
2350
2351 } else if(isa<IntegerLiteral>(subExpr) and hideImplicitCasts) {
2352 InsertArg(stmt->IgnoreCasts());
2353
2354 // If this is part of an explicit cast, for example a CStyleCast or static_cast, ignore it, because it
2355 // belongs to the cast written by the user.
2356 } else if(stmt->isPartOfExplicitCast()) {
2357 // For a CStyleCast we get an AST like this:
2358 //
2359 // CStyleCastExpr 0x13205cdc0 'uint32_t':'unsigned int' <NoOp>
2360 // `-ImplicitCastExpr 0x13205cda8 'uint32_t':'unsigned int' <IntegralCast> part_of_explicit_cast
2361 // `-CStyleCastExpr 0x13205cd70 'uint16_t':'unsigned short' <NoOp>
2362 //
2363 // Without filtering the `uint32_t` cast appears twice. The code below takes that into account and skips the
2364 // `ImplicitCastExpr` if the sub-expression is a `CStyleCastExpr`.
2365 if(isa<CStyleCastExpr>(subExpr) or isa<CXXNamedCastExpr>(subExpr)) {
2366 InsertArg(subExpr);
2367 } else {
2368 InsertArg(stmt->IgnoreCasts());
2369 }
2370
2371 } else {
2372 auto castName{GetCastName(castKind)};
2373 const QualType castDestType{[&] {
2374 auto type{stmt->getType()};
2375
2376 // In case of a dependent type the canonical type doesn't know the parameters name.
2377 if(not type->isDependentType()) {
2378 type = type.getCanonicalType();
2379 }
2380
2381 // In at least the case a structured bindings the compiler adds xvalue casts but the && is missing to
2382 // make it valid C++.
2383 if(VK_XValue == stmt->getValueKind()) {
2384 return GetGlobalAST().getRValueReferenceType(type);
2385 }
2386
2387 return type;
2388 }()};
2389
2390 FormatCast(castName, castDestType, subExpr, castKind);
2391 }
2392}
2393//-----------------------------------------------------------------------------
2394
2395void CodeGenerator::InsertArg(const DeclRefExpr* stmt)
2396{
2397 if(const auto* tmplObjParam = dyn_cast_or_null<TemplateParamObjectDecl>(stmt->getDecl())) {
2398 mOutputFormatHelper.Append(GetName(*tmplObjParam));
2399
2400 } else if(const auto* vd = dyn_cast_or_null<VarDecl>(stmt->getDecl());
2401 GetInsightsOptions().UseShow2C and IsReferenceType(vd)) {
2402 const auto* init = vd->getInit();
2403
2404 if(const auto* dref = dyn_cast_or_null<DeclRefExpr>(init)) {
2406 return;
2407
2408 } else if(const auto* inList = dyn_cast_or_null<InitListExpr>(init)) {
2409 mOutputFormatHelper.Append(GetName(*dyn_cast_or_null<DeclRefExpr>(inList->getInit(0))));
2410 return;
2411 }
2412 }
2413
2414 if(const auto* ctx = stmt->getDecl()->getDeclContext(); not ctx->isFunctionOrMethod() and
2415 not isa<NonTypeTemplateParmDecl>(stmt->getDecl()) and
2416 not GetInsightsOptions().UseShow2C) {
2417 if(const auto qualifier = stmt->getQualifier();
2418 qualifier and (
2419#if IS_CLANG_NEWER_THAN(22)
2420 qualifier.getKind() == NestedNameSpecifier::Kind::Global
2421#else
2422 qualifier->getKind() == NestedNameSpecifier::SpecifierKind::Global
2423#endif
2424 )) {
2425 // According to
2426 // https://clang.llvm.org/doxygen/classclang_1_1NestedNameSpecifier.html#ac707a113605ed4283684b8c05664eb6f
2427 // the global specifier is not stored.
2428 mOutputFormatHelper.Append("::"sv, GetPlainName(*stmt));
2429
2430 } else {
2431 OutputFormatHelper ofm{};
2432 CodeGeneratorVariant codeGenerator{ofm};
2433
2434 codeGenerator->ParseDeclContext(ctx);
2435
2437 }
2438
2439 } else {
2441 }
2442
2443 if(const auto* varTmplSpecDecl = dyn_cast_or_null<VarTemplateSpecializationDecl>(stmt->getDecl())) {
2444 InsertTemplateArgs(*varTmplSpecDecl);
2445 } else {
2446 InsertTemplateArgs(*stmt);
2447 }
2448}
2449//-----------------------------------------------------------------------------
2450
2451void CodeGenerator::InsertArg(const CompoundStmt* stmt)
2452{
2454 mLifeTimeTracker.StartScope(isa_and_nonnull<FunctionDecl>(mLastDecl));
2455
2456 // prevent nested CompoundStmt's to insert a return on each leave. Only insert it before closing the most outer
2457 // one.
2458 const bool requiresImplicitReturnZero{std::exchange(mRequiresImplicitReturnZero, false)};
2459
2460 HandleCompoundStmt(stmt);
2461
2462 if(requiresImplicitReturnZero) {
2463 InsertArg(Return(Int32(0)));
2464
2465 if(not mSkipSemi) {
2467 }
2468 }
2469
2470 mSkipSemi = mLifeTimeTracker.EndScope(mOutputFormatHelper, isa_and_nonnull<ReturnStmt>(mLastStmt));
2471
2472 mOutputFormatHelper.CloseScope(OutputFormatHelper::NoNewLineBefore::Yes);
2473}
2474//-----------------------------------------------------------------------------
2475
2476template<typename... Args>
2477static bool IsStmtRequiringSemi(const Stmt* stmt)
2478{
2479 return (... and not isa<Args>(stmt));
2480}
2481//-----------------------------------------------------------------------------
2482
2483void CodeGenerator::HandleCompoundStmt(const CompoundStmt* stmt)
2484{
2485 for(const auto* item : stmt->body()) {
2486 InsertArg(item);
2487
2488 // Skip inserting a semicolon, if this is a LambdaExpr and out stack is empty. This addresses a special case
2489 // #344.
2490 const bool skipSemiForLambda{mLambdaStack.empty() and isa<LambdaExpr>(item)};
2491
2492 if(IsStmtRequiringSemi<IfStmt,
2493 NullStmt,
2494 ForStmt,
2495 DeclStmt,
2496 WhileStmt,
2497 DoStmt,
2498 CXXForRangeStmt,
2499 SwitchStmt,
2500 CXXTryStmt,
2501 CppInsightsCommentStmt>(item) and
2502 InsertSemi() and not skipSemiForLambda and not mSkipSemi) {
2504 }
2505
2506 mSkipSemi = false;
2507 }
2508}
2509//-----------------------------------------------------------------------------
2510
2512{
2513 if(const auto* conditionVar = stmt->getConditionVariable()) {
2514 InsertArg(conditionVar);
2515 }
2516
2517 if(const auto* init = stmt->getInit()) {
2518 InsertArg(init);
2519
2520 if(not isa<DeclStmt>(init)) {
2522 }
2523 }
2524}
2525//-----------------------------------------------------------------------------
2526
2527void CodeGenerator::InsertArg(const IfStmt* stmt)
2528{
2529 const bool hasInit{stmt->getInit() or stmt->getConditionVariable()};
2530
2531 if(hasInit) {
2533
2535 }
2536
2537 mOutputFormatHelper.Append("if"sv, ValueOrDefault(stmt->isConstexpr(), kwSpaceConstExpr));
2538
2540 not stmt->isConsteval(),
2541 [&]() {
2542 mShowConstantExprValue = ShowConstantExprValue::Yes;
2543
2544 InsertArg(stmt->getCond());
2545
2546 mShowConstantExprValue = ShowConstantExprValue::No;
2547 },
2548 AddSpaceAtTheEnd::Yes);
2549
2550 mOutputFormatHelper.Append(ValueOrDefault(stmt->isNegatedConsteval(), " !"sv),
2551 ValueOrDefault(stmt->isConsteval(), kwSpaceConstEvalSpace));
2552
2553 WrapInCompoundIfNeeded(stmt->getThen(), AddNewLineAfter::No);
2554
2555 // else
2556 if(const auto* elsePart = stmt->getElse()) {
2558 "else "sv,
2560
2561 WrapInCompoundIfNeeded(elsePart, AddNewLineAfter::No);
2562 }
2563
2564 // Add newline after last closing curly (either from if or else if).
2566
2567 if(hasInit) {
2570 }
2571
2572 // one blank line after statement
2574}
2575//-----------------------------------------------------------------------------
2576
2577class ContinueASTTransformer : public StmtVisitor<ContinueASTTransformer>
2578{
2579 Stmt* mPrevStmt{};
2580 std::string_view mContinueLabel{};
2581
2582public:
2583 bool found{};
2584
2585 ContinueASTTransformer(Stmt* stmt, std::string_view continueLabel)
2586 : mPrevStmt{stmt}
2587 , mContinueLabel{continueLabel}
2588 {
2589 Visit(stmt);
2590 }
2591
2592 void Visit(Stmt* stmt)
2593 {
2594 if(stmt) {
2596 }
2597 }
2598
2599 void VisitContinueStmt(ContinueStmt* stmt)
2600 {
2601 found = true;
2602
2603 ReplaceNode(mPrevStmt, stmt, Goto(mContinueLabel));
2604 }
2605
2606 void VisitStmt(Stmt* stmt)
2607 {
2608 auto* tmp = mPrevStmt;
2609 mPrevStmt = stmt;
2610
2611 for(auto* child : stmt->children()) {
2612 Visit(child);
2613 }
2614
2615 mPrevStmt = tmp;
2616 }
2617};
2618//-----------------------------------------------------------------------------
2619
2620void CodeGenerator::InsertArg(const ForStmt* stmt)
2621{
2622 // https://github.com/vtjnash/clang-ast-builder/blob/master/AstBuilder.cpp
2623 // http://clang-developers.42468.n3.nabble.com/Adding-nodes-to-Clang-s-AST-td4054800.html
2624 // https://stackoverflow.com/questions/30451485/how-to-clone-or-create-an-ast-stmt-node-of-clang/38899615
2625
2626 if(GetInsightsOptions().UseAltForSyntax) {
2627 auto* rwStmt = const_cast<ForStmt*>(stmt);
2628 const auto& ctx = GetGlobalAST();
2629 StmtsContainer bodyStmts{};
2630
2631 auto continueLabel = MakeLineColumnName(ctx.getSourceManager(), stmt->getBeginLoc(), "__continue_"sv);
2632 const bool insertLabel = ContinueASTTransformer{rwStmt->getBody(), continueLabel}.found;
2633
2634 bodyStmts.AddBodyStmts(rwStmt->getBody());
2635
2636 // Build and insert the continue goto label
2637 if(insertLabel) {
2638 bodyStmts.Add(Label(continueLabel));
2639 }
2640
2641 bodyStmts.Add(rwStmt->getInc());
2642
2643 auto* condition = [&]() -> Expr* {
2644 if(rwStmt->getCond()) {
2645 return rwStmt->getCond();
2646 }
2647
2648 return Bool(true);
2649 }();
2650
2651 auto* outerBody = mkCompoundStmt(bodyStmts, stmt->getBeginLoc(), stmt->getEndLoc());
2652 auto* whileStmt = WhileStmt::Create(
2653 ctx, nullptr, condition, outerBody, stmt->getBeginLoc(), stmt->getLParenLoc(), stmt->getRParenLoc());
2654
2655 StmtsContainer outerScopeStmts{};
2656 outerScopeStmts.Add(rwStmt->getInit());
2657 outerScopeStmts.Add(whileStmt);
2658
2659 auto* outerScopeBody = mkCompoundStmt(outerScopeStmts, stmt->getBeginLoc(), stmt->getEndLoc());
2660
2661 InsertArg(outerScopeBody);
2663
2664 } else {
2665 {
2666 // We need to handle the case that a lambda is used in the init-statement of the for-loop.
2668
2669 mOutputFormatHelper.Append("for"sv);
2670
2672 [&]() {
2673 if(const auto* init = stmt->getInit()) {
2674 MultiStmtDeclCodeGenerator codeGenerator{
2675 mOutputFormatHelper, mLambdaStack, InsertVarDecl(nullptr)};
2676 codeGenerator.InsertArg(init);
2677
2678 } else {
2680 }
2681
2682 InsertArg(stmt->getCond());
2684
2685 InsertArg(stmt->getInc());
2686 },
2687 AddSpaceAtTheEnd::Yes);
2688 }
2689
2690 WrapInCompoundIfNeeded(stmt->getBody(), AddNewLineAfter::Yes);
2691 }
2692
2693 mOutputFormatHelper.AppendNewLine();
2694}
2695//-----------------------------------------------------------------------------
2696
2697static bool IsConstQualifiedType(QualType type)
2698{
2699 if(not type.isNull()) {
2700 if(auto* typePtr = type.getTypePtrOrNull()) {
2701 if(auto pointee = typePtr->getPointeeType(); not pointee.isNull()) {
2702 return pointee.isConstQualified();
2703 }
2704 }
2705 }
2706
2707 return false;
2708}
2709//-----------------------------------------------------------------------------
2710
2711void CodeGenerator::InsertArg(const CStyleCastExpr* stmt)
2712{
2713 const auto castKind = stmt->getCastKind();
2714 const QualType castDestType = stmt->getType().getCanonicalType();
2715 const auto castName = GetCastName(
2716 castKind, IsConstQualifiedType(castDestType) != IsConstQualifiedType(stmt->getSubExpr()->getType()));
2717
2718 FormatCast(castName, castDestType, stmt->getSubExpr(), castKind);
2719}
2720//-----------------------------------------------------------------------------
2721
2722void CodeGenerator::InsertArg(const CXXNewExpr* stmt)
2723{
2724 const auto noEmptyInitList = mNoEmptyInitList;
2725 FinalAction _{[&] { mNoEmptyInitList = noEmptyInitList; }};
2726 mNoEmptyInitList = GetInsightsOptions().UseShow2C ? NoEmptyInitList::Yes : NoEmptyInitList::No;
2727
2728 mOutputFormatHelper.Append("new "sv);
2729
2730 if(stmt->getNumPlacementArgs()) {
2731 /* we have a placement new */
2732
2733 WrapInParens([&]() {
2734 ForEachArg(stmt->placement_arguments(), [&](const auto& placementArg) { InsertArg(placementArg); });
2735 });
2736 }
2737
2738 if(const auto* ctorExpr = stmt->getConstructExpr()) {
2739 InsertArg(ctorExpr);
2740
2741 } else {
2742 auto name = GetName(stmt->getAllocatedType());
2743
2744 // Special handling for arrays. They differ from one to more dimensions.
2745 if(stmt->isArray()) {
2746 OutputFormatHelper ofm{};
2747 CodeGeneratorVariant codeGenerator{ofm};
2748
2749 ofm.Append('[');
2750 codeGenerator->InsertArg(stmt->getArraySize().value());
2751 ofm.Append(']');
2752
2753 // In case of multi dimension the first dimension is the getArraySize() while the others are part of the
2754 // type included in GetName(...).
2755 if(Contains(name, "["sv)) {
2756 InsertBefore(name, "["sv, ofm.GetString());
2757 } else {
2758 // here we have the single dimension case, the dimension is not part of GetName, so add it.
2759 name.append(ofm);
2760 }
2761 }
2762
2764
2765 if(stmt->hasInitializer()) {
2766 InsertCurlysIfRequired(stmt->getInitializer());
2767 }
2768 }
2769}
2770//-----------------------------------------------------------------------------
2771
2772void CodeGenerator::InsertArg(const MaterializeTemporaryExpr* stmt)
2773{
2774 // At least in case of a ternary operator wrapped inside a MaterializeTemporaryExpr parens are necessary
2775 const auto* temporary = stmt->getSubExpr();
2776 WrapInParensIfNeeded(isa_and_nonnull<ConditionalOperator>(temporary), [&] { InsertArg(temporary); });
2777}
2778//-----------------------------------------------------------------------------
2779
2780void CodeGenerator::InsertArg(const CXXOperatorCallExpr* stmt)
2781{
2783
2784 const auto* callee = dyn_cast_or_null<DeclRefExpr>(stmt->getCallee()->IgnoreImpCasts());
2785 const bool isCXXMethod{callee and isa<CXXMethodDecl>(callee->getDecl())};
2786
2787 if(2 == stmt->getNumArgs()) {
2788 auto getArg = [&](unsigned idx) {
2789 const auto* arg = stmt->getArg(idx);
2790
2791 // In show all casts mode don't filter this. It shows how the compiler adds const to arguments, if the
2792 // argument is non-const but the parameter demands a const object
2793 if(not GetInsightsOptions().ShowAllImplicitCasts) {
2794 arg = arg->IgnoreImpCasts();
2795 }
2796
2797 return dyn_cast_or_null<DeclRefExpr>(arg);
2798 };
2799
2800 const auto* param1 = getArg(0);
2801 const auto* param2 = getArg(1);
2802
2803 if(callee and param1 and param2) {
2804 const std::string replace = [&]() {
2805 // If the argument is a variable template, add the template arguments to the parameter name.
2806 auto nameWithTmplArguments = [](const auto param) {
2807 return FormatVarTemplateSpecializationDecl(param->getDecl(), GetName(*param));
2808 };
2809
2810 if(isa<CXXMethodDecl>(callee->getDecl())) {
2811 return StrCat(nameWithTmplArguments(param1),
2812 "."sv,
2813 GetName(*callee),
2814 "("sv,
2815 nameWithTmplArguments(param2),
2816 ")"sv);
2817 } else {
2818 return StrCat(GetName(*callee),
2819 "("sv,
2820 nameWithTmplArguments(param1),
2821 ", "sv,
2822 nameWithTmplArguments(param2),
2823 ")"sv);
2824 }
2825 }();
2826
2827 mOutputFormatHelper.Append(replace);
2828
2829 return;
2830 }
2831 }
2832
2833 auto cb = stmt->child_begin();
2834 const auto* fallbackArg0 = stmt->getArg(0);
2835
2836 // arg0 := operator
2837 // skip arg0
2838 std::advance(cb, 1);
2839
2840 const auto* arg1 = *cb;
2841
2842 std::advance(cb, 1);
2843
2844 // operators in a namespace but outside a class so operator goes first
2845 if(not isCXXMethod) {
2846 // happens for UnresolvedLooupExpr
2847 if(not callee) {
2848 if(const auto* adl = dyn_cast_or_null<UnresolvedLookupExpr>(stmt->getCallee())) {
2849 InsertArg(adl);
2850 }
2851 } else {
2853 }
2854
2856 }
2857
2858 // insert the arguments
2859 if(isa<DeclRefExpr>(fallbackArg0)) {
2860 InsertArgWithParensIfNeeded(fallbackArg0);
2861
2862 } else {
2864 }
2865
2866 // if it is a class operator the operator follows now
2867 if(isCXXMethod) {
2868 const OverloadedOperatorKind opKind = stmt->getOperator();
2869
2870 const std::string_view operatorKw{ValueOr((OO_Coawait == opKind), kwOperatorSpace, kwOperator)};
2871
2872 mOutputFormatHelper.Append("."sv, operatorKw, getOperatorSpelling(opKind), "("sv);
2873 }
2874
2875 // consume all remaining arguments
2876 const auto childRange = llvm::make_range(cb, stmt->child_end());
2877
2878 // at least the call-operator can have more than 2 parameters
2879 ForEachArg(childRange, [&](const auto& child) {
2880 if(not isCXXMethod) {
2881 // in global operators we need to separate the two parameters by comma
2883 }
2884
2885 InsertArg(child);
2886 });
2887
2889}
2890//-----------------------------------------------------------------------------
2891
2892void CodeGenerator::InsertArg(const CXXThisExpr*)
2893{
2894 if(not mLambdaExpr) {
2896 } else if(ranges::any_of(mLambdaExpr->captures(),
2897 [](const LambdaCapture& c) { return (c.getCaptureKind() == LCK_StarThis); })) {
2899
2900 } else {
2902 }
2903}
2904//-----------------------------------------------------------------------------
2905
2906void CodeGenerator::InsertArg(const CXXBindTemporaryExpr* stmt)
2907{
2908 InsertArg(stmt->getSubExpr());
2909}
2910//-----------------------------------------------------------------------------
2911
2912void CodeGenerator::InsertArg(const CXXFunctionalCastExpr* stmt)
2913{
2914 const bool isConstructor{isa<CXXConstructExpr>(stmt->getSubExpr())};
2915 const bool isStdListInit{isa<CXXStdInitializerListExpr>(stmt->getSubExpr())};
2916 const bool isListInitialization{stmt->getLParenLoc().isInvalid()};
2917 const bool needsParens{not isConstructor and not isListInitialization and not isStdListInit};
2918
2919 // If a constructor follows we do not need to insert the type name. This would insert it twice.
2920 if(not isConstructor and not isStdListInit) {
2921 mOutputFormatHelper.Append(GetName(stmt->getTypeAsWritten()));
2922 }
2923
2924 WrapInParensIfNeeded(needsParens, [&] { InsertArg(stmt->getSubExpr()); });
2925}
2926//-----------------------------------------------------------------------------
2927
2928void CodeGenerator::InsertArg(const CXXBoolLiteralExpr* stmt)
2929{
2931}
2932//-----------------------------------------------------------------------------
2933
2934void CodeGenerator::InsertArg(const GNUNullExpr* /*stmt*/)
2935{
2937}
2938//-----------------------------------------------------------------------------
2939
2940void CodeGenerator::InsertArg(const CharacterLiteral* stmt)
2941{
2942 StringStream stream{};
2943 stream.Print(*stmt);
2944
2945 auto str = std::move(stream.str());
2946
2947 if(str == "'\\x00'"sv) {
2948 str = "'\\0'"sv;
2949 } else if(str == "'\\xff'"sv) {
2950 str = "255"sv;
2951 }
2952
2954}
2955//-----------------------------------------------------------------------------
2956
2957void CodeGenerator::InsertArg(const PredefinedExpr* stmt)
2958{
2959 // Check if getFunctionName returns a valid StringLiteral. It does return a nullptr, if this PredefinedExpr is
2960 // in a UnresolvedLookupExpr. In that case, print the identifier, e.g. __func__.
2961 if(const auto* functionName = stmt->getFunctionName()) {
2962 InsertArg(functionName);
2963 } else {
2964 const auto name = PredefinedExpr::getIdentKindName(stmt->getIdentKind());
2965
2967 }
2968}
2969//-----------------------------------------------------------------------------
2970
2971void CodeGenerator::InsertArg(const ExprWithCleanups* stmt)
2972{
2974 TemporaryDeclFinder temporaryFinder{*this, not mProcessingVarDecl ? stmt->getSubExpr() : nullptr};
2975
2976 InsertArg(stmt->getSubExpr());
2977
2978 if(GetInsightsOptions().ShowLifetime and not mProcessingVarDecl) {
2980 }
2981
2983}
2984//-----------------------------------------------------------------------------
2985
2986std::string CodeGenerator::GetValueOfValueInit(const QualType& t)
2987{
2988 const QualType& type = t.getCanonicalType();
2989
2990 if(type->isScalarType()) {
2991 switch(type->getScalarTypeKind()) {
2992 case Type::STK_CPointer:
2993 case Type::STK_BlockPointer:
2994 case Type::STK_ObjCObjectPointer:
2995 case Type::STK_MemberPointer: return std::string{kwNullptr};
2996
2997 case Type::STK_Bool: return std::string{kwFalse};
2998
2999 case Type::STK_Integral:
3000 case Type::STK_Floating:
3001 if(const auto* bt = type->getAs<BuiltinType>()) {
3002 switch(bt->getKind()) {
3003 // Type::STK_Integral
3004 case BuiltinType::Char_U:
3005 case BuiltinType::UChar:
3006 case BuiltinType::Char_S:
3007 case BuiltinType::SChar: return "'\\0'";
3008 case BuiltinType::WChar_U:
3009 case BuiltinType::WChar_S: return "L'\\0'";
3010 case BuiltinType::Char16: return "u'\\0'";
3011 case BuiltinType::Char32: return "U'\\0'";
3012 // Type::STK_Floating
3013 case BuiltinType::Half:
3014 case BuiltinType::Float: return "0.0f";
3015 case BuiltinType::Double: return "0.0";
3016 default: break;
3017 }
3018 }
3019
3020 break;
3021
3022 case Type::STK_FloatingComplex:
3023 case Type::STK_IntegralComplex:
3024 if(const auto* complexType = type->getAs<ComplexType>()) {
3025 return GetValueOfValueInit(complexType->getElementType());
3026 }
3027
3028 break;
3029
3030 case Type::STK_FixedPoint: Error("STK_FixedPoint is not implemented"); break;
3031 }
3032
3033 } else if(const auto* tt = dyn_cast_or_null<ConstantArrayType>(t.getTypePtrOrNull())) {
3034 const auto& elementType{tt->getElementType()};
3035 const std::string elementTypeInitValue{GetValueOfValueInit(elementType)};
3036
3037 return FillConstantArray(tt, elementTypeInitValue, uint64_t{0});
3038 }
3039
3040 return std::string{"0"sv};
3041}
3042//-----------------------------------------------------------------------------
3043
3044void CodeGenerator::InsertArg(const ImplicitValueInitExpr* stmt)
3045{
3047}
3048//-----------------------------------------------------------------------------
3049
3050void CodeGenerator::InsertArg(const CXXScalarValueInitExpr* stmt)
3051{
3052 mOutputFormatHelper.Append(GetName(stmt->getType()), "()"sv);
3053}
3054//-----------------------------------------------------------------------------
3055
3056void CodeGenerator::InsertArg(const CXXTryStmt* stmt)
3057{
3059
3060 InsertArg(stmt->getTryBlock());
3061
3062 for(const auto& i : NumberIterator{stmt->getNumHandlers()}) {
3063 InsertArg(stmt->getHandler(i));
3064 }
3065
3067}
3068//-----------------------------------------------------------------------------
3069
3070void CodeGenerator::InsertArg(const CXXCatchStmt* stmt)
3071{
3072 mOutputFormatHelper.Append(" catch"sv);
3073
3075 [&]() {
3076 if(not stmt->getCaughtType().isNull()) {
3077 mOutputFormatHelper.Append(
3078 GetTypeNameAsParameter(stmt->getCaughtType(), stmt->getExceptionDecl()->getName()));
3079 } else {
3080 mOutputFormatHelper.Append(kwElipsis);
3081 }
3082 },
3083 AddSpaceAtTheEnd::Yes);
3084
3085 InsertArg(stmt->getHandlerBlock());
3086}
3087//-----------------------------------------------------------------------------
3088
3089void CodeGenerator::InsertArg(const CXXThrowExpr* stmt)
3090{
3091 mOutputFormatHelper.Append("throw "sv);
3092
3093 InsertArg(stmt->getSubExpr());
3094}
3095//-----------------------------------------------------------------------------
3096
3097void CodeGenerator::InsertArg(const ConstantExpr* stmt)
3098{
3099 if((ShowConstantExprValue::Yes == mShowConstantExprValue) and stmt->hasAPValueResult()) {
3100 if(const auto value = stmt->getAPValueResult(); value.isInt()) {
3101 mOutputFormatHelper.Append(value.getInt());
3102 return;
3103 }
3104 }
3105
3106 InsertArg(stmt->getSubExpr());
3107}
3108//-----------------------------------------------------------------------------
3109
3110void CodeGenerator::InsertArg(const TypeAliasDecl* stmt)
3111{
3112 const auto& underlyingType = stmt->getUnderlyingType();
3113
3115 P0315Visitor dt{*this};
3116 dt.TraverseType(underlyingType);
3117
3119
3120 if(auto* templateSpecializationType = underlyingType->getAs<TemplateSpecializationType>()) {
3121 const bool carriesNamespace{[&] {
3122 if(const auto tn = templateSpecializationType->getTemplateName();
3123 (TemplateName::QualifiedTemplate == tn.getKind()) or (TemplateName::DependentTemplate == tn.getKind())) {
3124 if(const auto* qtn = tn.getAsQualifiedTemplateName()) {
3125 return static_cast<bool>(qtn->getQualifier());
3126 }
3127 }
3128
3129 return false;
3130 }()};
3131
3132#if not IS_CLANG_NEWER_THAN(22)
3133 if(const auto* elaboratedType = underlyingType->getAs<ElaboratedType>()) {
3134 if(templateSpecializationType->isSugared() and not carriesNamespace) {
3135 // do this only if the templateSpecializationType does not carry a nestedns
3136 InsertNamespace(elaboratedType->getQualifier());
3137 }
3138 }
3139#else
3140 if(TemplateName::DependentTemplate == templateSpecializationType->getTemplateName().getKind()) {
3141 mOutputFormatHelper.Append(GetElaboratedTypeKeyword(templateSpecializationType->getKeyword()));
3142 }
3143
3144 if(templateSpecializationType->isSugared() and not carriesNamespace) {
3145 // The scope was written in front of the typedef, for example std::chrono::nanoseconds.
3146 if(const auto* typedefType = dyn_cast_or_null<TypedefType>(underlyingType.getTypePtrOrNull())) {
3147 InsertNamespace(typedefType->getQualifier());
3148 }
3149 }
3150#endif
3151
3152 StringStream stream{};
3153 stream.Print(*templateSpecializationType);
3154
3155 mOutputFormatHelper.Append(stream.str());
3156
3157 InsertTemplateArgs(*templateSpecializationType);
3158
3159#if not IS_CLANG_NEWER_THAN(22)
3160 } else if(auto* depSpecType = underlyingType->getAs<DependentTemplateSpecializationType>()) {
3161 mOutputFormatHelper.Append(GetElaboratedTypeKeyword(depSpecType->getKeyword()));
3162
3163 InsertNamespace(depSpecType->getDependentTemplateName().getQualifier());
3164
3165 mOutputFormatHelper.Append(kwTemplateSpace, GetName(depSpecType->getDependentTemplateName()));
3166
3167 InsertTemplateArgs(*depSpecType);
3168#endif
3169 } else {
3170 mOutputFormatHelper.Append(GetName(underlyingType));
3171 }
3172
3174}
3175//-----------------------------------------------------------------------------
3176
3177void CodeGenerator::InsertArg(const TypedefDecl* stmt)
3178{
3179 /* function pointer typedefs are special. Ease up things using "using" */
3180 // outputFormatHelper.AppendNewLine("typedef ", GetName(stmt->getUnderlyingType()), " ", GetName(*stmt),
3181 // ";");
3182 mOutputFormatHelper.AppendSemiNewLine(kwUsingSpace, GetName(*stmt), hlpAssing, GetName(stmt->getUnderlyingType()));
3183}
3184//-----------------------------------------------------------------------------
3185
3186void CodeGenerator::InsertCXXMethodHeader(const CXXMethodDecl* stmt, OutputFormatHelper& initOutputFormatHelper)
3187{
3189 CXXConstructorDecl* cxxInheritedCtorDecl{nullptr};
3190
3191 // Traverse the ctor inline init statements first to find a potential CXXInheritedCtorInitExpr. This carries the
3192 // name and the type. The CXXMethodDecl above knows only the type.
3193 if(const auto* ctor = dyn_cast_or_null<CXXConstructorDecl>(stmt)) {
3194 CodeGeneratorVariant codeGenerator{initOutputFormatHelper, mLambdaStack, mProcessingPrimaryTemplate};
3195 codeGenerator->mCurrentVarDeclPos = mCurrentVarDeclPos;
3196 codeGenerator->mCurrentFieldPos = mCurrentFieldPos;
3199
3200 for(OnceTrue first{}; const auto* init : ctor->inits()) {
3201 initOutputFormatHelper.AppendNewLine();
3202 if(first) {
3203 initOutputFormatHelper.Append(": "sv);
3204 } else {
3205 initOutputFormatHelper.Append(", "sv);
3206 }
3207
3208 // in case of delegating or base initializer there is no member.
3209#if 0
3210 if(const auto* member = init->getMember()) {
3211 initOutputFormatHelper.Append(member->getName());
3212 codeGenerator->InsertCurlysIfRequired(init->getInit());
3213 } else {
3214 const auto* inlineInit = init->getInit();
3215 bool useCurlies{false};
3216
3217 if(const auto* cxxInheritedCtorInitExpr = dyn_cast_or_null<CXXInheritedCtorInitExpr>(inlineInit)) {
3218 cxxInheritedCtorDecl = cxxInheritedCtorInitExpr->getConstructor();
3219
3220 // Insert the base class name only, if it is not a CXXContructorExpr and not a
3221 // CXXDependentScopeMemberExpr which already carry the type.
3222 } else if(init->isBaseInitializer() and not isa<CXXConstructExpr>(inlineInit)) {
3223 initOutputFormatHelper.Append(GetUnqualifiedScopelessName(init->getBaseClass()));
3224 useCurlies = true;
3225 }
3226
3227 codeGenerator->WrapInCurliesIfNeeded(useCurlies, [&] { codeGenerator->InsertArg(inlineInit); });
3228 }
3229#else
3230 const auto* inlineInit = init->getInit();
3231
3232 // in case of delegating or base initializer there is no member.
3233 if(const auto* member = init->getMember()) {
3234 initOutputFormatHelper.Append(member->getName());
3235
3236 if(isa<ParenListExpr>(inlineInit)) {
3237 codeGenerator->WrapInParens([&] { codeGenerator->InsertArg(inlineInit); });
3238 } else {
3239 codeGenerator->InsertCurlysIfRequired(inlineInit);
3240 }
3241
3242 } else if(const auto* cxxInheritedCtorInitExpr = dyn_cast_or_null<CXXInheritedCtorInitExpr>(inlineInit)) {
3243 cxxInheritedCtorDecl = cxxInheritedCtorInitExpr->getConstructor();
3244
3245 codeGenerator->InsertArg(inlineInit);
3246
3247 // Insert the base class name only, if it is not a CXXContructorExpr and not a
3248 // CXXDependentScopeMemberExpr which already carry the type.
3249 } else if(init->isBaseInitializer() and not isa<CXXConstructExpr>(inlineInit)) {
3250 initOutputFormatHelper.Append(GetUnqualifiedScopelessName(init->getBaseClass()));
3251
3252 const auto braceKind = isa<ParenListExpr>(inlineInit) ? BraceKind::Parens : BraceKind::Curlys;
3253
3254 codeGenerator->WrapInParensOrCurlys(braceKind, [&] { codeGenerator->InsertArg(inlineInit); });
3255 } else {
3256 codeGenerator->InsertArg(inlineInit);
3257 }
3258#endif
3259 }
3260 }
3261
3263 InsertFunctionNameWithReturnType(*stmt, cxxInheritedCtorDecl);
3264}
3265//-----------------------------------------------------------------------------
3266
3267void CodeGenerator::InsertCXXMethodDecl(const CXXMethodDecl* stmt, SkipBody skipBody)
3268{
3269 OutputFormatHelper initOutputFormatHelper{};
3270 initOutputFormatHelper.SetIndent(mOutputFormatHelper, OutputFormatHelper::SkipIndenting::Yes);
3271
3272 const auto posBeforeFunc = mOutputFormatHelper.CurrentPos();
3273
3274 InsertCXXMethodHeader(stmt, initOutputFormatHelper);
3275
3276 if(not stmt->isUserProvided() or stmt->isExplicitlyDefaulted()) {
3278 return;
3279 }
3280
3281 mOutputFormatHelper.Append(initOutputFormatHelper);
3282
3283 if(isa<CXXConversionDecl>(stmt)) {
3284 if(stmt->getParent()->isLambda() and not stmt->doesThisDeclarationHaveABody()) {
3286 WrapInCurlys([&]() {
3288 mOutputFormatHelper.Append(" "sv, kwReturn, " "sv);
3289 if(const auto* invoker = stmt->getParent()->getLambdaStaticInvoker()) {
3290 mOutputFormatHelper.AppendSemiNewLine(invoker->getName());
3291 } else {
3292 mOutputFormatHelper.AppendSemiNewLine(kwOperator, "()"sv);
3293 }
3294 });
3295 }
3296 }
3297
3298 if((SkipBody::No == skipBody) and stmt->doesThisDeclarationHaveABody() and not stmt->isLambdaStaticInvoker()) {
3299 InsertMethodBody(stmt, posBeforeFunc);
3300
3301 } else if(not InsertLambdaStaticInvoker(stmt) or (SkipBody::Yes == skipBody)) {
3303 }
3304
3306
3307 if(SkipBody::No == skipBody) {
3309 }
3310}
3311//-----------------------------------------------------------------------------
3312
3313void CodeGenerator::InsertArg(const CXXMethodDecl* stmt)
3314{
3315 // As per [special]/1: "Programs shall not define implicitly-declared special member functions." hide special
3316 // members which are not used and with that not fully evaluated. This also hopefully removes confusion about the
3317 // noexcept, which is not evaluated, if the special member is not used.
3318 RETURN_IF(not GetInsightsOptions().UseShow2C and not stmt->hasBody() and not stmt->isUserProvided() and
3319 not stmt->isExplicitlyDefaulted() and not stmt->isDeleted());
3320
3321 InsertCXXMethodDecl(stmt, SkipBody::No);
3322}
3323//-----------------------------------------------------------------------------
3324
3325void CodeGenerator::InsertArg(const EnumDecl* stmt)
3326{
3328
3329 if(stmt->isScoped()) {
3330 if(stmt->isScopedUsingClassTag()) {
3332 } else {
3334 }
3335 }
3336
3337 mOutputFormatHelper.Append(stmt->getName());
3338
3339 if(stmt->isFixed()) {
3340 mOutputFormatHelper.Append(" : "sv, GetName(stmt->getIntegerType()));
3341 }
3342
3344
3346 [&]() {
3349 OnceFalse needsComma{};
3350
3351 ForEachArg(stmt->enumerators(), [&](const auto* value) {
3352 if(needsComma) {
3353 mOutputFormatHelper.AppendNewLine();
3354 }
3355
3356 InsertArg(value);
3357 });
3358
3359 InsertArg(stmt->getBody());
3360
3363 },
3364 AddSpaceAtTheEnd::No);
3365
3366 mOutputFormatHelper.AppendSemiNewLine();
3367 mOutputFormatHelper.AppendNewLine();
3368}
3369//-----------------------------------------------------------------------------
3370
3371void CodeGenerator::InsertArg(const EnumConstantDecl* stmt)
3372{
3373 mOutputFormatHelper.Append(stmt->getName());
3374
3375 InsertAttributes(stmt);
3376
3377 if(const auto* initExpr = stmt->getInitExpr()) {
3379
3380 InsertArg(initExpr);
3381 }
3382}
3383//-----------------------------------------------------------------------------
3384
3385static auto& GetRecordLayout(const RecordDecl* recordDecl)
3386{
3387 return GetGlobalAST().getASTRecordLayout(recordDecl);
3388}
3389//-----------------------------------------------------------------------------
3390
3391// XXX: replace with std::format once it is available in all std-libs
3392auto GetSpaces(std::string::size_type offset)
3393{
3394 static const std::string_view spaces{" "sv};
3395
3396 if(offset >= spaces.size()) {
3397 return ""sv;
3398 } else {
3399 return spaces.substr(0, spaces.size() - offset);
3400 }
3401}
3402//-----------------------------------------------------------------------------
3403
3404void CodeGenerator::InsertArg(const FieldDecl* stmt)
3405{
3407 P0315Visitor dt{*this};
3408
3409 auto type = GetType(stmt->getType());
3410 dt.TraverseType(type);
3411
3412 const auto initialSize{mOutputFormatHelper.size()};
3413 InsertAttributes(stmt->attrs());
3414
3415 if(stmt->isMutable()) {
3417 }
3418
3419 if(const auto* cxxRecordDecl = dyn_cast_or_null<CXXRecordDecl>(stmt->getParent())) {
3420 std::string name{GetName(*stmt)};
3421
3422 if(const auto fieldName = GetFieldDeclNameForLambda(*stmt, *cxxRecordDecl)) {
3423 name = std::move(fieldName.value());
3424 }
3425
3427
3428 if(const auto* constantExpr = dyn_cast_or_null<ConstantExpr>(stmt->getBitWidth())) {
3430 InsertArg(constantExpr);
3431 }
3432
3433 // Keep the inline init for aggregates, as we do not see it somewhere else.
3434 if(const auto* initializer = stmt->getInClassInitializer();
3435 stmt->hasInClassInitializer() and initializer and cxxRecordDecl->isAggregate()) {
3436 const bool isConstructorExpr{isa<CXXConstructExpr>(initializer) or isa<ExprWithCleanups>(initializer)};
3437 if((ICIS_ListInit != stmt->getInClassInitStyle()) or isConstructorExpr) {
3439 }
3440
3441 InsertArg(initializer);
3442 }
3443 }
3444
3446
3447 if(GetInsightsOptions().UseShowPadding) {
3448 const auto* fieldClass = stmt->getParent();
3449 const auto& recordLayout = GetRecordLayout(fieldClass);
3450 auto effectiveFieldSize{GetGlobalAST().getTypeInfoInChars(type).Width.getQuantity()};
3451 auto getFieldOffsetInBytes = [&recordLayout](const FieldDecl* field) {
3452 return recordLayout.getFieldOffset(field->getFieldIndex()) / 8; // this is in bits
3453 };
3454 auto fieldOffset = getFieldOffsetInBytes(stmt);
3455 const auto offset = mOutputFormatHelper.size() - initialSize;
3456
3457 mOutputFormatHelper.Append(GetSpaces(offset), " /* offset: "sv, fieldOffset, ", size: "sv, effectiveFieldSize);
3458
3459 // - get next field
3460 // - if this fields offset + size is equal to the next fields offset we are good,
3461 // - if not we insert padding bytes
3462 // - in case there is no next field this is the last field, check this field's offset + size against the
3463 // records
3464 // size. If unequal padding is needed
3465
3466 const auto expectedOffset = fieldOffset + effectiveFieldSize;
3467 const auto nextOffset = [&]() -> uint64_t {
3468 // find previous field
3469 if(const auto next = stmt->getFieldIndex() + 1; recordLayout.getFieldCount() > next) {
3470 // We are in bounds, means we expect to get back a valid iterator
3471 const auto* field = *std::next(fieldClass->fields().begin(), next);
3472
3473 return getFieldOffsetInBytes(field);
3474 }
3475
3476 // no field found means we are the last field
3477 return recordLayout.getSize().getQuantity();
3478 }();
3479
3480 if(expectedOffset < nextOffset) {
3481 const auto padding = nextOffset - expectedOffset;
3483 std::string s = StrCat("char "sv, BuildInternalVarName("padding"sv), "["sv, padding, "];"sv);
3484 mOutputFormatHelper.Append(s, GetSpaces(s.length()), " size: ", padding);
3485 }
3486
3488
3489 } else {
3491 }
3492}
3493//-----------------------------------------------------------------------------
3494
3495void CodeGenerator::InsertArg(const AccessSpecDecl* stmt)
3496{
3499}
3500//-----------------------------------------------------------------------------
3501
3502void CodeGenerator::InsertArg(const StaticAssertDecl* stmt)
3503{
3505
3506 if(not stmt->isFailed()) {
3507 mOutputFormatHelper.Append("/* PASSED: "sv);
3508 } else {
3509 mOutputFormatHelper.Append("/* FAILED: "sv);
3510 }
3511
3513
3514 WrapInParens([&] {
3515 BackupAndRestore _{GetInsightsOptionsRW().ShowLifetime, false};
3516 InsertArg(stmt->getAssertExpr());
3517
3518 if(stmt->getMessage()) {
3520 InsertArg(stmt->getMessage());
3521 }
3522 });
3523
3525}
3526//-----------------------------------------------------------------------------
3527
3528void CodeGenerator::InsertArg(const UsingDirectiveDecl* stmt)
3529{
3530 // We need this due to a wired case in UsingDeclTest.cpp
3531 if(const auto& name = GetName(*stmt->getNominatedNamespace()); not name.empty()) {
3533 }
3534}
3535//-----------------------------------------------------------------------------
3536
3537void CodeGenerator::InsertArg(const NamespaceDecl* stmt)
3538{
3539 SCOPE_HELPER(stmt);
3540
3541 if(stmt->isInline()) {
3543 }
3544
3546
3547 InsertAttributes(stmt);
3548
3549 if(not stmt->isAnonymousNamespace()) {
3550 mOutputFormatHelper.Append(" "sv, stmt->getName());
3551 }
3552
3554
3556
3557 for(const auto* decl : stmt->decls()) {
3558 InsertArg(decl);
3559 }
3560
3563}
3564//-----------------------------------------------------------------------------
3565
3566void CodeGenerator::ParseDeclContext(const DeclContext* ctx)
3567{
3569}
3570//-----------------------------------------------------------------------------
3571
3572void CodeGenerator::InsertArg(const UsingDecl* stmt)
3573{
3574 OutputFormatHelper ofm{};
3575 ofm.SetIndent(mOutputFormatHelper, OutputFormatHelper::SkipIndenting::Yes);
3576
3577 // Skip UsingDecl's which have ConstructorUsingShadowDecl attached. This means that we will create the
3578 // associated constructors from the base class later. Having this \c using still in the code prevents compiling
3579 // the transformed code.
3580 if(stmt->shadow_size()) {
3581 for(const auto* shadow : stmt->shadows()) {
3582 RETURN_IF(isa<ConstructorUsingShadowDecl>(shadow));
3583
3584 if(const auto* shadowUsing = dyn_cast_or_null<UsingShadowDecl>(shadow)) {
3585 if(const auto* targetDecl = shadowUsing->getTargetDecl(); not isa<TypeAliasDecl>(targetDecl)) {
3586 UsingCodeGenerator codeGenerator{ofm};
3587 codeGenerator.InsertArg(targetDecl);
3588 }
3589 }
3590 }
3591 }
3592
3594
3595 InsertQualifierAndName(stmt->getDeclName(), stmt->getQualifier(), false);
3596
3598
3599 // Insert what a using declaration pulled into this scope.
3600 if(not ofm.empty()) {
3602 }
3603}
3604//-----------------------------------------------------------------------------
3605
3606void CodeGenerator::InsertArg(const UnresolvedUsingValueDecl* stmt)
3607{
3609
3610 InsertQualifierAndName(stmt->getDeclName(), stmt->getQualifier(), false);
3611
3612 mOutputFormatHelper.AppendSemiNewLine(Ellipsis(stmt->isPackExpansion()));
3613}
3614//-----------------------------------------------------------------------------
3615
3616void CodeGenerator::InsertArg(const NamespaceAliasDecl* stmt)
3617{
3619 kwNamespaceSpace, stmt->getDeclName().getAsString(), hlpAssing, GetName(*stmt->getAliasedNamespace()), ";");
3620}
3621//-----------------------------------------------------------------------------
3622
3623void CodeGenerator::InsertArg(const FriendDecl* stmt)
3624{
3625 if(const auto* typeInfo = stmt->getFriendType()) {
3627
3628 } else if(const auto* fd = dyn_cast_or_null<FunctionDecl>(stmt->getFriendDecl())) {
3629 InsertArg(fd);
3630
3631 } else if(const auto* fdt = dyn_cast_or_null<FunctionTemplateDecl>(stmt->getFriendDecl())) {
3632 InsertArg(fdt);
3633
3634 } else {
3635 std::string cls{};
3636 if(const auto* ctd = dyn_cast_or_null<ClassTemplateDecl>(stmt->getFriendDecl())) {
3637 InsertTemplateParameters(*ctd->getTemplateParameters());
3638
3639 cls = GetTagDeclTypeName(*ctd->getTemplatedDecl());
3640 }
3641
3642 mOutputFormatHelper.AppendSemiNewLine(kwFriendSpace, cls, GetName(*stmt->getFriendDecl()));
3643 }
3644}
3645//-----------------------------------------------------------------------------
3646
3647void CodeGenerator::InsertArg(const CXXNoexceptExpr* stmt)
3648{
3650
3652}
3653//-----------------------------------------------------------------------------
3654
3655void CodeGenerator::InsertArg(const CXXDeductionGuideDecl* stmt)
3656{
3657 RETURN_IF(DeductionCandidate::Copy == stmt->getDeductionCandidateKind());
3658
3659 const bool isImplicit{stmt->isImplicit()};
3660 const bool noSpecializations = [&] {
3661 if(const auto* dt = stmt->getDescribedFunctionTemplate()) {
3662 return dt->specializations().empty();
3663 }
3664
3665 return false;
3666 }();
3667
3668 // Block compiler generated deduction guides which are _overridden_ by user provided deduction guides.
3669 RETURN_IF(not stmt->isUsed() and isImplicit and noSpecializations);
3670
3671 const bool isSpecialization{stmt->isFunctionTemplateSpecialization()};
3672 const bool needsTemplateGuard{isImplicit or isSpecialization};
3673
3674 if(needsTemplateGuard) {
3676 }
3677
3678 const auto* deducedTemplate = stmt->getDeducedTemplate();
3679
3680 if(isSpecialization) {
3682 } else if(const auto* e = stmt->getDescribedFunctionTemplate()) {
3683 InsertTemplateParameters(*e->getTemplateParameters());
3684 }
3685
3686 mOutputFormatHelper.Append(GetName(*deducedTemplate));
3687
3688 if(stmt->getNumParams()) {
3689 WrapInParens([&] { mOutputFormatHelper.AppendParameterList(stmt->parameters()); });
3690 } else {
3692 }
3693
3694 mOutputFormatHelper.AppendSemiNewLine(hlpArrow, GetName(stmt->getReturnType()));
3695
3696 if(needsTemplateGuard) {
3698 }
3699}
3700//-----------------------------------------------------------------------------
3701
3702void CodeGenerator::InsertTemplate(const FunctionTemplateDecl* stmt, bool withSpec)
3703{
3705
3706 mProcessingPrimaryTemplate = ProcessingPrimaryTemplate::Yes;
3707
3708 // InsertTemplateParameters(*stmt->getTemplateParameters());
3709 InsertArg(stmt->getTemplatedDecl());
3710
3711 mProcessingPrimaryTemplate = ProcessingPrimaryTemplate::No;
3712
3713 RETURN_IF(not withSpec);
3714
3715 for(const auto* spec : stmt->specializations()) {
3716 // For specializations we will see them later
3717 if(spec->getPreviousDecl()) {
3718 continue;
3719 }
3720
3722 InsertArg(spec);
3724 }
3725}
3726//-----------------------------------------------------------------------------
3727
3728void CodeGenerator::InsertArg(const FunctionTemplateDecl* stmt)
3729{
3730 InsertTemplate(stmt, true);
3731}
3732//-----------------------------------------------------------------------------
3733
3734void CodeGenerator::InsertArg(const TypeAliasTemplateDecl* stmt)
3735{
3736 InsertTemplateParameters(*stmt->getTemplateParameters());
3737
3738 InsertArg(stmt->getTemplatedDecl());
3739}
3740//-----------------------------------------------------------------------------
3741
3742void CodeGenerator::InsertArg(const AttributedStmt* stmt)
3743{
3744 for(const auto& attr : stmt->getAttrs()) {
3745 InsertAttribute(*attr);
3746 }
3747
3748 InsertArg(stmt->getSubStmt());
3749}
3750//-----------------------------------------------------------------------------
3751
3753{
3754 if(stmt->hasAttrs()) {
3756
3757 InsertAttributes(stmt->attrs());
3758 }
3759}
3760//-----------------------------------------------------------------------------
3761
3762void CodeGenerator::InsertAttributes(const Decl::attr_range& attrs)
3763{
3764 // attrs required for constinit
3765 for(const auto& attr : attrs) {
3766 InsertAttribute(*attr);
3767 }
3768}
3769//-----------------------------------------------------------------------------
3770
3772{
3773 // skip this attribute. Clang seems to tag virtual methods with override
3774 RETURN_IF(attr::Override == attr.getKind());
3775
3776 // skip this attribute. Clang seems to tag final methods or classes with final
3777 RETURN_IF(attr::Final == attr.getKind());
3778
3779 // skip this custom clang attribute
3780 RETURN_IF(attr::NoInline == attr.getKind());
3781
3782#if IS_CLANG_NEWER_THAN(20)
3783 // skip this custom clang attribute
3784 RETURN_IF(attr::InferredNoReturn == attr.getKind());
3785#endif
3786
3787 // Clang's printPretty misses the parameter pack ellipsis. Hence treat this special case here.
3788 if(const auto* alignedAttr = dyn_cast_or_null<AlignedAttr>(&attr)) {
3789 auto insert = [&](const QualType type, const TemplateTypeParmType* tmplTypeParam) {
3790 mOutputFormatHelper.Append(attr.getSpelling(),
3791 "("sv,
3792 kwAlignof,
3793 "("sv,
3794 GetName(type),
3795 ")"sv,
3796 Ellipsis(tmplTypeParam->isParameterPack()),
3797 ") "sv);
3798 };
3799
3800 if(alignedAttr->isAlignmentExpr()) {
3801 if(const auto* unaryExpr = dyn_cast_or_null<UnaryExprOrTypeTraitExpr>(alignedAttr->getAlignmentExpr())) {
3802 if(const auto* tmplTypeParam =
3803 dyn_cast_or_null<TemplateTypeParmType>(unaryExpr->getArgumentType().getTypePtrOrNull())) {
3804 insert(unaryExpr->getArgumentType(), tmplTypeParam);
3805 return;
3806 }
3807 }
3808 } else if(const auto* tmplTypeParam =
3809 alignedAttr->getAlignmentType()->getType()->getAs<TemplateTypeParmType>()) {
3810 insert(alignedAttr->getAlignmentType()->getType(), tmplTypeParam);
3811 return;
3812 }
3813 }
3814
3815 StringStream stream{};
3816 PrintingPolicy pp{GetGlobalAST().getLangOpts()};
3817 pp.adjustForCPlusPlus();
3818
3819 attr.printPretty(stream, pp);
3820
3821 // attributes start with a space, skip it as it is not required for the first attribute
3822 std::string_view start{stream.str()};
3823
3824 mOutputFormatHelper.Append(start, " "sv);
3825}
3826//-----------------------------------------------------------------------------
3827
3828void CodeGenerator::InsertArg(const CXXRecordDecl* stmt)
3829{
3830 SCOPE_HELPER(stmt);
3831
3832 // Prevent a case like in #205 where the lambda appears twice.
3833 RETURN_IF(stmt->isLambda() and (mLambdaStack.empty()));
3834
3835 const auto* classTemplatePartialSpecializationDecl = dyn_cast_or_null<ClassTemplatePartialSpecializationDecl>(stmt);
3836 const auto* classTemplateSpecializationDecl = dyn_cast_or_null<ClassTemplateSpecializationDecl>(stmt);
3837
3838 // we require the if-guard only if it is a compiler generated specialization. If it is a hand-written variant it
3839 // should compile.
3840 const bool isClassTemplateSpecialization{classTemplatePartialSpecializationDecl or classTemplateSpecializationDecl};
3841 const bool tmplRequiresIfDef{[&] {
3842 if(classTemplatePartialSpecializationDecl) {
3843 return classTemplatePartialSpecializationDecl->isImplicit();
3844
3845 } else if(classTemplateSpecializationDecl) {
3846 return not classTemplateSpecializationDecl->isExplicitInstantiationOrSpecialization();
3847 }
3848
3849 return false;
3850 }()};
3851
3852 FinalAction _{[&] {
3853 if(tmplRequiresIfDef) {
3855 }
3856 }};
3857
3858 if(isClassTemplateSpecialization) {
3859 if(tmplRequiresIfDef) {
3860 InsertInstantiationPoint(GetSM(*classTemplateSpecializationDecl),
3861 classTemplateSpecializationDecl->getPointOfInstantiation());
3863 }
3864
3865 if(classTemplatePartialSpecializationDecl) {
3866 InsertTemplateParameters(*classTemplatePartialSpecializationDecl->getTemplateParameters());
3867 } else {
3869 }
3870 // Render a out-of-line struct declared inside a class template
3871 } else if(stmt->getLexicalDeclContext() != stmt->getDeclContext()) {
3872 if(const auto* parent = dyn_cast_or_null<CXXRecordDecl>(stmt->getDeclContext())) {
3873 if(const auto* outerClasTemplateDecl = parent->getDescribedClassTemplate()) {
3874 InsertTemplateParameters(*outerClasTemplateDecl->getTemplateParameters());
3875 }
3876 }
3877 }
3878
3880
3881 InsertAttributes(stmt->attrs());
3882
3884
3885 if(classTemplateSpecializationDecl) {
3886 InsertTemplateArgs(*classTemplateSpecializationDecl);
3887 }
3888
3889 if(stmt->hasAttr<FinalAttr>()) {
3891 }
3892
3893 // skip classes/struct's without a definition
3894 if(not stmt->hasDefinition() or not stmt->isCompleteDefinition()) {
3896 return;
3897 }
3898
3899 if(stmt->getNumBases()) {
3900 mOutputFormatHelper.Append(" : "sv);
3901
3902 ForEachArg(stmt->bases(), [&](const auto& base) {
3903 mOutputFormatHelper.Append(getAccessSpelling(base.getAccessSpecifier()),
3904 " "sv,
3905 ValueOrDefault(base.isVirtual(), kwVirtualSpace),
3906 GetName(base.getType()),
3907 Ellipsis(base.isPackExpansion()));
3908 });
3909 }
3910
3911 if(GetInsightsOptions().UseShowPadding) {
3912 const auto& recordLayout = GetRecordLayout(stmt);
3914 " /* size: "sv, recordLayout.getSize(), ", align: "sv, recordLayout.getAlignment(), " */"sv);
3915
3916 } else {
3918 }
3919
3921
3922 if(GetInsightsOptions().UseShowPadding) {
3923 for(size_t offset{}; const auto& base : stmt->bases()) {
3924 const auto& baseRecordLayout = GetRecordLayout(base.getType()->getAsRecordDecl());
3925 const auto baseVar = StrCat("/* base ("sv, GetName(base.getType()), ")"sv);
3926 const auto size = baseRecordLayout.getSize().getQuantity();
3927
3929 baseVar, GetSpaces(baseVar.size()), " offset: "sv, offset, ", size: "sv, size, " */"sv);
3930
3931 offset += size;
3932 }
3933 }
3934
3936
3937 const bool isLambda{stmt->isLambda()};
3938 Decl::Kind formerKind{};
3939 AccessSpecifier lastAccess{stmt->isClass() ? AS_private : AS_public};
3940 for(OnceTrue firstRecordDecl{}, firstDecl{}; const auto* d : stmt->decls()) {
3941 if(isa<CXXRecordDecl>(d) and firstRecordDecl) {
3942 continue;
3943 }
3944
3945 // Insert a newline when the decl kind changes. This for example, inserts a newline when after a FieldDecl
3946 // we see a CXXMethodDecl.
3947 if(not firstDecl and (d->getKind() != formerKind)) {
3948 // mOutputFormatHelper.AppendNewLine();
3949 }
3950
3951 if((isLambda and isa<CXXDestructorDecl>(d)) and not d->isUsed()) {
3952 continue;
3953 }
3954
3955 // Insert the access modifier, as at least some of the compiler generated classes do not contain an access
3956 // specifier which results in a default ctor being private if we do not insert the access modifier.
3957 if(lastAccess != d->getAccess()) {
3958 lastAccess = d->getAccess();
3959
3960 // skip inserting an access specifier of our own, if there is a real one coming.
3961 if(not isa<AccessSpecDecl>(d)) {
3962 // For the captures of a lambda mimic the behavior of GCC. They create an aggregate first and later flip
3963 // it to a class:
3964 // https://github.com/gcc-mirror/gcc/blob/62a80185db84f20f3efb05c81598bffa95bcd63d/gcc/cp/lambda.cc#L56
3965 // https://github.com/gcc-mirror/gcc/blob/62a80185db84f20f3efb05c81598bffa95bcd63d/gcc/cp/lambda.cc#L121
3966 if(isa<FieldDecl>(d) and isLambda) {
3967 // ToDo: Leave a comment in a potential -with-comments mode that the standard requires the captures
3968 // to be private?
3970 }
3971
3973 }
3974 }
3975
3976 InsertArg(d);
3977
3978 formerKind = d->getKind();
3979 }
3980
3981 // Only a default constructible, i.e. captureless, lambda has a defaulted default constructor
3982 if(isLambda and stmt->lambdaIsDefaultConstructibleAndAssignable()) {
3983 if(AS_public != lastAccess) {
3985 mOutputFormatHelper.AppendNewLine(AccessToStringWithColon(AS_public, TrailingSpace::No));
3986 }
3987
3989
3990 if(stmt->hasConstexprDefaultConstructor()) {
3993 }
3994
3996 }
3997
3998 // close the class scope
3999 mOutputFormatHelper.CloseScope(OutputFormatHelper::NoNewLineBefore::Yes);
4000
4001 if(GetInsightsOptions().UseShow2C) {
4002 mOutputFormatHelper.Append(" "sv, GetName(*stmt));
4003 }
4004
4007}
4008//-----------------------------------------------------------------------------
4009
4010void CodeGenerator::InsertArg(const DeclStmt* stmt)
4011{
4012 for(const auto* decl : stmt->decls()) {
4013 InsertArg(decl);
4014 }
4015}
4016//-----------------------------------------------------------------------------
4017
4018void CodeGenerator::InsertArg(const SubstNonTypeTemplateParmExpr* stmt)
4019{
4020 InsertArg(stmt->getReplacement());
4021}
4022//-----------------------------------------------------------------------------
4023
4024void CodeGenerator::InsertArg(const SizeOfPackExpr* stmt)
4025{
4026 if(stmt->isPartiallySubstituted()) {
4027 mOutputFormatHelper.Append(stmt->getPartialArguments().size());
4028 } else if(not stmt->isValueDependent()) {
4029 mOutputFormatHelper.Append(stmt->getPackLength());
4030 } else {
4031 mOutputFormatHelper.Append(kwSizeof, kwElipsis, "("sv, GetName(*stmt->getPack()), ")"sv);
4032 }
4033}
4034//-----------------------------------------------------------------------------
4035
4036void CodeGenerator::InsertArg(const ReturnStmt* stmt)
4037{
4039
4041
4042 { // dedicated scope to first clear everything found in the return statement. Then clear all others.
4043 TemporaryDeclFinder temporaryFinder{*this, stmt->getRetValue(), true};
4044
4046
4047 if(const auto* retVal = stmt->getRetValue()) {
4049
4050 if(not temporaryFinder.Found()) {
4051 if(const auto* nrvoVD = stmt->getNRVOCandidate()) {
4053 } else {
4054 InsertArg(retVal);
4055 }
4056 } else {
4057 mOutputFormatHelper.Append(temporaryFinder.Name());
4058 }
4059 }
4060 }
4061
4063
4064 // the InsertArg above changes the start
4065 mLastStmt = stmt;
4066
4067 mCurrentReturnPos.reset();
4068}
4069//-----------------------------------------------------------------------------
4070
4071void CodeGenerator::InsertArg(const NullStmt* /*stmt*/)
4072{
4074 mSkipSemi = true;
4075}
4076//-----------------------------------------------------------------------------
4077
4078void CodeGenerator::InsertArg(const StmtExpr* stmt)
4079{
4080 WrapInParens([&] { InsertArg(stmt->getSubStmt()); });
4081}
4082//-----------------------------------------------------------------------------
4083
4084void CodeGenerator::InsertArg(const CppInsightsCommentStmt* stmt)
4085{
4087}
4088//-----------------------------------------------------------------------------
4089
4090void CodeGenerator::InsertArg(const ConceptSpecializationExpr* stmt)
4091{
4092 if(const auto* namedConcept = stmt->getNamedConcept()) {
4093 mOutputFormatHelper.Append(GetName(*namedConcept));
4094 InsertTemplateArgs(stmt->getTemplateArgsAsWritten()->arguments());
4095
4096#if 0
4097 if(not stmt->isValueDependent()) {
4099 }
4100#endif
4101 }
4102}
4103//-----------------------------------------------------------------------------
4104
4105void CodeGenerator::InsertArg(const RequiresExpr* stmt)
4106{
4108
4109 const auto localParameters = stmt->getLocalParameters();
4111 not localParameters.empty(),
4112 [&] { mOutputFormatHelper.AppendParameterList(localParameters); },
4113 AddSpaceAtTheEnd::Yes);
4114
4116
4117 const auto noEmptyInitList = mNoEmptyInitList;
4118 FinalAction _{[&] { mNoEmptyInitList = noEmptyInitList; }};
4119 mNoEmptyInitList = NoEmptyInitList::Yes;
4120
4121 for(const auto& requirement : stmt->getRequirements()) {
4122 if(const auto* typeRequirement = dyn_cast_or_null<concepts::TypeRequirement>(requirement)) {
4123 if(typeRequirement->isSubstitutionFailure()) {
4125 } else {
4126 mOutputFormatHelper.Append(GetName(typeRequirement->getType()->getType()));
4127 }
4128
4129 // SimpleRequirement
4130 } else if(const auto* exprRequirement = dyn_cast_or_null<concepts::ExprRequirement>(requirement)) {
4131 if(exprRequirement->isExprSubstitutionFailure()) {
4132 // The requirement failed. We need some way to express that. Using a nested
4133 // requirement with false seems to be the simplest solution.
4135 } else {
4136 WrapInCurliesIfNeeded(exprRequirement->isCompound(), [&] { InsertArg(exprRequirement->getExpr()); });
4137
4138 if(exprRequirement->hasNoexceptRequirement()) {
4140 }
4141
4142 if(const auto& returnTypeRequirement = exprRequirement->getReturnTypeRequirement();
4143 not returnTypeRequirement.isEmpty()) {
4144 if(auto typeConstraint = GetTypeConstraintAsString(returnTypeRequirement.getTypeConstraint());
4145 not typeConstraint.empty()) {
4146 mOutputFormatHelper.Append(hlpArrow, std::move(typeConstraint));
4147 }
4148 }
4149 }
4150 } else if(const auto* nestedRequirement = dyn_cast_or_null<concepts::NestedRequirement>(requirement)) {
4152
4153 if(nestedRequirement->hasInvalidConstraint()) {
4154 // The requirement failed. We need some way to express that. Using a nested
4155 // requirement with false seems to be the simplest solution.
4157 } else {
4158 InsertArg(nestedRequirement->getConstraintExpr());
4159 }
4160 }
4161
4163 }
4164
4165 mOutputFormatHelper.CloseScope(OutputFormatHelper::NoNewLineBefore::Yes);
4166}
4167//-----------------------------------------------------------------------------
4168
4169void CodeGenerator::InsertArg(const CXXDefaultArgExpr* stmt)
4170{
4171 InsertArg(stmt->getExpr());
4172}
4173//-----------------------------------------------------------------------------
4174
4175void CodeGenerator::InsertArg(const CXXStdInitializerListExpr* stmt)
4176{
4177 // No qualifiers like const or volatile here. This appears in function calls or operators as a parameter.
4178 // CV's are not allowed there.
4179 const auto typeName{GetName(stmt->getType(), Unqualified::Yes)};
4180
4181 if(GetInsightsOptions().UseShowInitializerList) {
4182 RETURN_IF(not mCurrentVarDeclPos.has_value() and not mCurrentFieldPos.has_value() and
4183 not mCurrentReturnPos.has_value() and not mCurrentCallExprPos.has_value());
4184
4185 const auto* subExpr = stmt->getSubExpr();
4186
4187 if(const auto* dref = dyn_cast_or_null<DeclRefExpr>(subExpr); dref and GetInsightsOptions().ShowLifetime) {
4188 const auto size = GetSize(dyn_cast_or_null<ConstantArrayType>(subExpr->getType()));
4189
4190 mOutputFormatHelper.Append(typeName, "{"sv, GetName(*dref), ", "sv, size, "}"sv);
4191 return;
4192 }
4193
4194 std::string modifiers{};
4195 size_t variableInsertPos = mCurrentReturnPos.value_or(
4196 mCurrentVarDeclPos.value_or(mCurrentCallExprPos.value_or(0))); // order is important!
4197
4198 auto& ofmToInsert = [&]() -> decltype(auto) {
4199 if(not mCurrentVarDeclPos.has_value() and not mCurrentReturnPos.has_value() and
4200 not mCurrentCallExprPos.has_value()) {
4201 variableInsertPos = mCurrentFieldPos.value_or(0);
4202 mCurrentVarDeclPos = variableInsertPos;
4203 modifiers = StrCat(kwStaticSpace, kwInlineSpace);
4205 }
4206
4207 return (mOutputFormatHelper);
4208 }();
4209
4210 OutputFormatHelper ofm{};
4211 ofm.SetIndent(ofmToInsert, OutputFormatHelper::SkipIndenting::Yes);
4212
4213 const auto size = [&]() -> size_t {
4214 if(const auto* mat = dyn_cast<MaterializeTemporaryExpr>(subExpr)) {
4215 if(const auto* list = dyn_cast_or_null<InitListExpr>(mat->getSubExpr())) {
4216 return list->getNumInits();
4217 }
4218 }
4219
4220 return 0;
4221 }();
4222
4223 auto internalListName =
4224 MakeLineColumnName(GetGlobalAST().getSourceManager(), stmt->getBeginLoc(), BuildInternalVarName("list"sv));
4225
4226 ofm.Append(modifiers, GetTypeNameAsParameter(subExpr->getType().getCanonicalType(), internalListName));
4227 CodeGeneratorVariant codeGenerator{ofm};
4228 codeGenerator->InsertArg(subExpr);
4229 ofm.AppendSemiNewLine();
4230
4231 ofmToInsert.InsertAt(variableInsertPos, ofm);
4232
4233 mOutputFormatHelper.Append(typeName, "{"sv, internalListName, ", "sv, size, "}"sv);
4234
4235 if(mCurrentReturnPos.has_value()) {
4236 mCurrentReturnPos = mCurrentReturnPos.value() + ofm.size();
4237 } else if(mCurrentVarDeclPos.has_value()) {
4238 mCurrentVarDeclPos = mCurrentVarDeclPos.value() + ofm.size();
4239 } else {
4240 mCurrentCallExprPos = mCurrentCallExprPos.value() + ofm.size();
4241 }
4242
4243 } else {
4244 mOutputFormatHelper.Append(typeName);
4245 InsertArg(stmt->getSubExpr());
4246 }
4247}
4248//-----------------------------------------------------------------------------
4249
4250void CodeGenerator::InsertArg(const CXXNullPtrLiteralExpr* /*stmt*/)
4251{
4253}
4254//-----------------------------------------------------------------------------
4255
4256void CodeGenerator::InsertArg(const LabelDecl* stmt)
4257{
4258 mOutputFormatHelper.Append(stmt->getName());
4259}
4260//-----------------------------------------------------------------------------
4261
4262void CodeGenerator::InsertArg(const Decl* stmt)
4263{
4264 mLastDecl = stmt;
4265
4266#define SUPPORTED_DECL(type) \
4267 if(isa<type>(stmt)) { \
4268 InsertArg(static_cast<const type*>(stmt)); \
4269 return; \
4270 }
4271
4272#define IGNORED_DECL SUPPORTED_DECL
4273
4274#include "CodeGeneratorTypes.h"
4275
4277}
4278//-----------------------------------------------------------------------------
4279
4281{
4282 if(not stmt) {
4283 DPrint("Null stmt\n");
4284 return;
4285 }
4286
4287 mLastStmt = stmt;
4288
4289#define SUPPORTED_STMT(type) \
4290 if(isa<type>(stmt)) { \
4291 InsertArg(dyn_cast_or_null<type>(stmt)); \
4292 return; \
4293 }
4294
4295#define IGNORED_STMT SUPPORTED_STMT
4296
4297#include "CodeGeneratorTypes.h"
4298
4300}
4301//-----------------------------------------------------------------------------
4302
4303void CodeGenerator::FormatCast(const std::string_view castName,
4304 const QualType& castDestType,
4305 const Expr* subExpr,
4306 const CastKind& castKind)
4307{
4308 const bool isCastToBase{is{castKind}.any_of(CK_DerivedToBase, CK_UncheckedDerivedToBase) and
4309 castDestType->isRecordType()};
4310 const std::string castDestTypeText{
4311 StrCat(GetName(castDestType), ((isCastToBase and not castDestType->isAnyPointerType()) ? "&"sv : ""sv))};
4312
4313 mOutputFormatHelper.Append(castName, "<"sv, castDestTypeText, ">("sv);
4314 InsertArg(subExpr);
4316}
4317//-----------------------------------------------------------------------------
4318
4320{
4321 const bool needsParens = [&]() {
4322 if(const auto* expr = dyn_cast_or_null<Expr>(stmt)) {
4323 if(const auto* dest = dyn_cast_or_null<UnaryOperator>(expr->IgnoreImplicit())) {
4324 return (dest->getOpcode() == clang::UO_Deref);
4325 }
4326 }
4327
4328 return false;
4329 }();
4330
4331 WrapInParensIfNeeded(needsParens, [&] { InsertArg(stmt); });
4332}
4333//-----------------------------------------------------------------------------
4334
4335void CodeGenerator::InsertSuffix(const QualType& type)
4336{
4337 if(const auto* typePtr = type.getTypePtrOrNull(); typePtr and typePtr->isBuiltinType()) {
4338 if(const auto* bt = type->getAs<clang::BuiltinType>()) {
4339 const auto kind = bt->getKind();
4340
4342 }
4343 }
4344}
4345//-----------------------------------------------------------------------------
4346
4347void CodeGenerator::InsertTemplateArgs(const ClassTemplateSpecializationDecl& clsTemplateSpe)
4348{
4349 if(const auto* ar = clsTemplateSpe.getTemplateArgsAsWritten()) {
4350 InsertTemplateArgs(ar->arguments());
4351 } else {
4352 InsertTemplateArgs(clsTemplateSpe.getTemplateArgs());
4353#if 0
4354 if(const TypeSourceInfo* typeAsWritten = clsTemplateSpe.getTypeAsWritten()) {
4355 const TemplateSpecializationType* tmplSpecType = cast<TemplateSpecializationType>(typeAsWritten->getType());
4356 InsertTemplateArgs(*tmplSpecType);
4357 } else {
4358 InsertTemplateArgs(clsTemplateSpe.getTemplateArgs());
4359#endif
4360 }
4361}
4362//-----------------------------------------------------------------------------
4363
4364void CodeGenerator::HandleTemplateParameterPack(const ArrayRef<TemplateArgument>& args)
4365{
4366 ForEachArg(args, [&](const auto& arg) { InsertTemplateArg(arg); });
4367}
4368//-----------------------------------------------------------------------------
4369
4370void CodeGenerator::InsertTemplateArg(const TemplateArgument& arg)
4371{
4372 switch(arg.getKind()) {
4373 case TemplateArgument::Type: mOutputFormatHelper.Append(GetName(arg.getAsType())); break;
4374 case TemplateArgument::Declaration:
4375 // TODO: handle pointers
4376 if(const auto decl = dyn_cast_or_null<TemplateParamObjectDecl>(arg.getAsDecl())) {
4378 } else {
4379 mOutputFormatHelper.Append("&"sv, GetName(*arg.getAsDecl(), QualifiedName::Yes));
4380 }
4381 break;
4382 case TemplateArgument::NullPtr: mOutputFormatHelper.Append(kwNullptr); break;
4383 case TemplateArgument::Integral:
4384
4385 if(const auto& integral = arg.getAsIntegral(); arg.getIntegralType()->isCharType()) {
4386 const char c{static_cast<char>(integral.getZExtValue())};
4387 mOutputFormatHelper.Append("'"sv, std::string{c}, "'"sv);
4388 } else {
4389 mOutputFormatHelper.Append(integral);
4390 }
4391
4392 break;
4393 case TemplateArgument::Expression: {
4394 if(auto val = EvaluateNTTPAsConstantExpr(arg.getAsExpr()->IgnoreParenImpCasts())) {
4396 GetName(val->first),
4397 BuildTemplateParamObjectName(val->second.getAsString(GetGlobalAST(), val->first)));
4398 } else {
4399 InsertArg(arg.getAsExpr());
4400 }
4401 }
4402
4403 break;
4404 case TemplateArgument::Pack: HandleTemplateParameterPack(arg.pack_elements()); break;
4405 case TemplateArgument::Template: [[fallthrough]];
4406 case TemplateArgument::TemplateExpansion:
4407 if(const auto* tmplDecl = arg.getAsTemplateOrTemplatePattern().getAsTemplateDecl()) {
4408 mOutputFormatHelper.Append(GetName(*tmplDecl, QualifiedName::Yes));
4409
4410#if IS_CLANG_NEWER_THAN(20)
4411 } else if(const auto* depName = arg.getAsTemplateOrTemplatePattern().getAsDependentTemplateName();
4412 depName->getName().getIdentifier()) {
4413 InsertNamespace(depName->getQualifier());
4414
4416#else
4417 } else if(const auto* depName = arg.getAsTemplateOrTemplatePattern().getAsDependentTemplateName();
4418 depName->isIdentifier()) {
4419 InsertNamespace(depName->getQualifier());
4420
4421 mOutputFormatHelper.Append(kwTemplateSpace, depName->getIdentifier()->getName());
4422#endif
4423 } else {
4425 }
4426
4427 break;
4428 case TemplateArgument::Null: mOutputFormatHelper.Append("null"sv); break;
4429 case TemplateArgument::StructuralValue: mOutputFormatHelper.Append(arg.getAsStructuralValue()); break;
4430 }
4431}
4432//-----------------------------------------------------------------------------
4433
4435{
4437
4438 const auto& ctx = GetGlobalAST();
4439
4440 auto& langOpts{GetLangOpts(*stmt)};
4441 const bool threadSafe{langOpts.ThreadsafeStatics and langOpts.CPlusPlus11 and
4442 (stmt->isLocalVarDecl() /*|| NonTemplateInline*/) and not stmt->getTLSKind()};
4443
4444 const std::string internalVarName{BuildInternalVarName(GetName(*stmt))};
4445 const std::string compilerBoolVarName{StrCat(internalVarName, "Guard"sv)};
4446
4447 // insert compiler bool to track init state
4448 auto* compilerGuardVar =
4449 Variable(compilerBoolVarName, threadSafe ? Typedef("uint64_t"sv, ctx.UnsignedLongTy) : ctx.BoolTy);
4450 compilerGuardVar->setStorageClass(StorageClass::SC_Static);
4451 InsertArg(compilerGuardVar);
4452
4453 // insert compiler memory place holder
4454 auto* compilerStorageVar = Variable(internalVarName,
4455 ctx.getConstantArrayType(ctx.CharTy,
4456 llvm::APInt(ctx.getTypeSize(ctx.getSizeType()), 0),
4457 Sizeof(stmt->getType()),
4458 ArraySizeModifier::Normal,
4459 0));
4460
4461 compilerStorageVar->setStorageClass(StorageClass::SC_Static);
4462
4463 auto* alignedAttr =
4464 AlignedAttr::CreateImplicit(const_cast<ASTContext&>(ctx),
4465 true,
4466 Sizeof(stmt->getType()), // ctx.getTrivialTypeSourceInfo(stmt->getType()),
4467 {},
4468 AlignedAttr::Spelling::Keyword_alignas);
4469
4470 compilerStorageVar->addAttr(alignedAttr);
4471
4472 const std::string typeName{GetName(stmt->getType())};
4474 "alignas("sv, typeName, ") static char "sv, internalVarName, "[sizeof("sv, typeName, ")]"sv);
4475
4476 // insert compiler init if
4478
4479 // try to find out whether this ctor or the CallExpr can throw. If, then additional code needs to be generated
4480 // for exception handling.
4481 const bool canThrow{[&] {
4482 const ValueDecl* decl = [&]() -> const ValueDecl* {
4483 const auto* init = stmt->getInit()->IgnoreCasts();
4484 if(const auto* ctorExpr = dyn_cast_or_null<CXXConstructExpr>(init)) {
4485 return ctorExpr->getConstructor();
4486 } else if(const auto* callExpr = dyn_cast_or_null<CallExpr>(init)) {
4487 return callExpr->getDirectCallee();
4488 }
4489
4490 return nullptr;
4491 }();
4492
4493 if(decl) {
4494 if(const auto* func = decl->getType()->castAs<FunctionProtoType>()) {
4495 return not func->isNothrow();
4496 }
4497 }
4498
4499 return false;
4500 }()};
4501
4502 // VarDecl of a static expression always have an initializer
4503 auto* init = const_cast<Expr*>(stmt->getInit());
4504
4505 if(const bool isCallExpr{not isa<CXXConstructExpr>(init->IgnoreCasts())}; isCallExpr) {
4506 // we have a function call
4507 init = Call("std::move"sv, {init});
4508
4509 // Tests show that the compiler does better than std::move
4511 }
4512
4513 // the allocation and guard update:
4514 // new (&__s)T();
4515 // __sGuard = true;
4516 auto type = stmt->getType();
4517 type.removeLocalConst(); // Issue369.cpp is a const static variable. Should the ctor use the const?
4518 SmallVector<Stmt*, 4> allocAndFlagBodyStmts{New({Ref(compilerStorageVar)}, init, type),
4519 Assign(compilerGuardVar, Bool(true))};
4520 auto allocAndFlagBodyCompound = mkCompoundStmt(allocAndFlagBodyStmts);
4521
4522 StmtsContainer innerBodyStmts{};
4523
4524 // Need to insert a try catch, if the constructor/initializer can throw
4525 if(canThrow) {
4526 innerBodyStmts.AddBodyStmts(
4527 Try(allocAndFlagBodyCompound, Catch({Call("__cxa_guard_abort"sv, {Ref(compilerGuardVar)}), Throw()})));
4528 } else {
4529 innerBodyStmts.AddBodyStmts(allocAndFlagBodyCompound);
4530 }
4531
4532 StmtsContainer bodyStmts{};
4533
4534 if(threadSafe) {
4535 innerBodyStmts.AddBodyStmts(Call("__cxa_guard_release"sv, {Ref(compilerGuardVar)}));
4536 innerBodyStmts.Add(Comment(
4537 StrCat("__cxa_atexit("sv, typeName, "::~"sv, typeName, ", &"sv, internalVarName, ", &__dso_handle);"sv)));
4538
4539 auto* aquireIf = If(Call("__cxa_guard_acquire"sv, {Ref(compilerGuardVar)}), innerBodyStmts);
4540 bodyStmts.AddBodyStmts(aquireIf);
4541 } else {
4542 bodyStmts.AddBodyStmts(allocAndFlagBodyCompound);
4543 }
4544
4545 InsertArg(If(Equal(And(compilerGuardVar, Int32(0xff)), Int32(0)), bodyStmts));
4546}
4547//-----------------------------------------------------------------------------
4548
4549std::string_view CodeGenerator::GetBuiltinTypeSuffix(const BuiltinType::Kind& kind)
4550{
4551#define CASE(K, retVal) \
4552 case BuiltinType::K: return retVal
4553 switch(kind) {
4554 CASE(UInt, "U"sv);
4555 CASE(ULong, "UL"sv);
4556 CASE(ULongLong, "ULL"sv);
4557 CASE(UInt128, "ULLL"sv);
4558 CASE(Long, "L"sv);
4559 CASE(LongLong, "LL"sv);
4560 CASE(Float, "F"sv);
4561 CASE(LongDouble, "L"sv);
4562 default: return {};
4563 }
4564#undef BTCASE
4565}
4566//-----------------------------------------------------------------------------
4567
4568void CodeGenerator::InsertArg(const LambdaExpr* stmt)
4569{
4570 const auto stackEmpty{mLambdaStack.empty()}; // If the stack is empty we have a IIFE
4572
4573 LambdaHelper& lambdaHelper{mLambdaStack.back()};
4574 OutputFormatHelper& outputFormatHelper{lambdaHelper.buffer()};
4575 const size_t insertPosBeforeClass{outputFormatHelper.CurrentPos()};
4576 const auto indentAtInsertPosBeforeClass{outputFormatHelper.GetIndent()};
4577
4578 outputFormatHelper.AppendNewLine();
4579 CodeGeneratorVariant codeGenerator{outputFormatHelper, mLambdaStack, mProcessingPrimaryTemplate};
4580 codeGenerator->mLambdaExpr = stmt;
4581 codeGenerator->InsertArg(stmt->getLambdaClass());
4582
4583 if(lambdaHelper.insertName()) {
4585 }
4586
4587 // type only discarding captures as this is a capture-less context
4588 if(is{lambdaHelper.callerType()}.any_of(LambdaCallerType::Decltype, LambdaCallerType::DecltypeWithName)) {
4589 return;
4590 }
4591
4592 // Process the captures, transfer them to initializer for an aggregate init call.
4593 OutputFormatHelper ctorArguments{};
4594 ctorArguments.Append("{"sv);
4595
4596 // We have to process the captures even if the stack was previously empty and we will discard all the initializers,
4597 // because one of the captures could contain another lambda.
4598 if(const auto* recordDecl{stmt->getLambdaClass()}; not recordDecl->lambdaIsDefaultConstructibleAndAssignable()) {
4599 OnceFalse needsComma{};
4600 llvm::DenseMap<const ValueDecl*, FieldDecl*> captures{};
4601 FieldDecl* _{};
4602
4603 recordDecl->getCaptureFields(captures, _);
4604
4605 // Find the corresponding capture in the DenseMap. The DenseMap seems to be change its order each time.
4606 // Hence we use \c captures() to keep the order stable. While using \c Captures to generate the code as
4607 // it carries the better type infos.
4608 for(const auto& [c, initExpr] : zip(stmt->captures(), stmt->capture_inits())) {
4609 if(not c.capturesVariable() and not c.capturesThis()) {
4610 // that can be a VLA
4611 continue;
4612 }
4613
4614 ctorArguments.AppendComma(needsComma);
4615
4617 OutputFormatHelper ofmLambdaInCtor{};
4618 ofmLambdaInCtor.SetIndent(indentAtInsertPosBeforeClass);
4619 CodeGenerator cgLambdaInCtor{ofmLambdaInCtor};
4620 OutputFormatHelper ofm{};
4621
4622 if(P0315Visitor dt{cgLambdaInCtor}; dt.TraverseStmt(const_cast<Expr*>(initExpr))) {
4624
4625 if(const auto* ctorExpr = dyn_cast_or_null<CXXConstructExpr>(initExpr);
4626 ctorExpr and (1 == ctorExpr->getNumArgs())) {
4627 codeGenerator->InsertArg(ctorExpr->getArg(0));
4628
4629 } else {
4630 codeGenerator->InsertArg(initExpr);
4631 }
4632
4633 } else {
4634 LambdaNameOnlyCodeGenerator ccg{ofm};
4635 ccg.InsertArg(initExpr);
4636
4637 outputFormatHelper.InsertAt(insertPosBeforeClass, ofmLambdaInCtor);
4638 }
4639
4640 ctorArguments.Append(ofm);
4641
4642 if(c.capturesVariable() and c.getCapturedVar()->isParameterPack()) {
4643 ctorArguments.Append(kwElipsis);
4644 }
4645 }
4646 }
4647
4648 ctorArguments.Append("}"sv);
4649
4650 if(not stackEmpty) {
4651 mOutputFormatHelper.Append(ctorArguments);
4652 }
4653}
4654//-----------------------------------------------------------------------------
4655
4656void CodeGenerator::InsertConceptConstraint(const llvm::SmallVectorImpl<const Expr*>& constraints,
4657 const InsertInline insertInline)
4658{
4659 for(OnceTrue first{}; const auto* c : constraints) {
4660 if(first and (InsertInline::Yes == insertInline)) {
4662 }
4663
4665 InsertArg(c);
4666
4667 if(InsertInline::No == insertInline) {
4669 }
4670 }
4671}
4672//-----------------------------------------------------------------------------
4673
4674// This inserts the requires clause after template<...>
4675void CodeGenerator::InsertConceptConstraint(const TemplateParameterList& tmplDecl)
4676{
4677 if(const auto* reqClause = tmplDecl.getRequiresClause()) {
4678 SmallVector<const Expr*, 1> constraints{reqClause};
4679
4680 InsertConceptConstraint(constraints, InsertInline::No);
4681 }
4682}
4683//-----------------------------------------------------------------------------
4684
4685// This inserts the requires clause after the function header
4686void CodeGenerator::InsertConceptConstraint(const FunctionDecl* tmplDecl)
4687{
4688 SmallVector<const Expr*, 5> constraints{};
4689
4690#if IS_CLANG_NEWER_THAN(20)
4691 SmallVector<AssociatedConstraint, 5> assocConstraints{};
4692 tmplDecl->getAssociatedConstraints(assocConstraints);
4693
4694 for(auto& e : assocConstraints) {
4695 constraints.push_back(e.ConstraintExpr);
4696 }
4697#else
4698 tmplDecl->getAssociatedConstraints(constraints);
4699#endif
4700
4701 InsertConceptConstraint(constraints, InsertInline::Yes);
4702}
4703//-----------------------------------------------------------------------------
4704
4705// This inserts the requires clause after a variable type
4707{
4708 if(const auto* t = varDecl->getType()->getContainedAutoType()) {
4709 if(t->getTypeConstraintConcept()) {
4710#if 0
4711 mOutputFormatHelper.Append(kwCommentStart, t->getTypeConstraintConcept()->getName(), kwCCommentEndSpace);
4712#endif
4713 }
4714 }
4715}
4716//-----------------------------------------------------------------------------
4717
4719 const CXXConstructorDecl* cxxInheritedCtorDecl)
4720{
4721 bool isLambda{false};
4722 bool isFirstCxxMethodDecl{true};
4723 const auto* methodDecl{dyn_cast_or_null<CXXMethodDecl>(&decl)};
4724 bool isCXXMethodDecl{nullptr != methodDecl};
4725 const bool isClassTemplateSpec{isCXXMethodDecl and isa<ClassTemplateSpecializationDecl>(methodDecl->getParent())};
4726 const bool requiresComment{isCXXMethodDecl and not methodDecl->isUserProvided() and
4727 not methodDecl->isExplicitlyDefaulted()};
4728 // [expr.prim.lambda.closure] p7 consteval/constexpr are obtained from the call operator
4729 const bool isLambdaStaticInvoker{isCXXMethodDecl and methodDecl->isLambdaStaticInvoker()};
4730 const auto desugaredReturnType = GetType(GetDesugarReturnType(decl));
4731
4732 if(methodDecl) {
4733 if(requiresComment) {
4735 }
4736
4737 isLambda = methodDecl->getParent()->isLambda();
4738 isFirstCxxMethodDecl = (nullptr == methodDecl->getPreviousDecl());
4739 }
4740
4741 // types of conversion decls can be invalid to type at this place. So introduce a using
4742 if(isa<CXXConversionDecl>(decl) and TypeContainsSubType<PointerType, FunctionProtoType>(desugaredReturnType)) {
4744 kwUsingSpace, BuildRetTypeName(decl), hlpAssing, GetName(desugaredReturnType));
4745 }
4746
4747 if(isCXXMethodDecl and decl.isOutOfLine()) {
4748 if(const auto* parent = methodDecl->getParent()) {
4749 if(const auto* outerClasTemplateDecl = parent->getDescribedClassTemplate()) {
4750 InsertTemplateParameters(*outerClasTemplateDecl->getTemplateParameters());
4751 }
4752 }
4753 }
4754
4755 if(decl.isTemplated()) {
4756 if(decl.getDescribedTemplate()) {
4757 InsertTemplateParameters(*decl.getDescribedTemplate()->getTemplateParameters());
4758 }
4759
4760 } else if(decl.isFunctionTemplateSpecialization() or (isClassTemplateSpec and decl.isOutOfLine() and
4761 (decl.getLexicalDeclContext() != methodDecl->getParent()))) {
4763 }
4764
4765 InsertAttributes(decl.attrs());
4766
4767 if(not decl.isFunctionTemplateSpecialization() or (isCXXMethodDecl and isFirstCxxMethodDecl)) {
4768 if(not decl.isOutOfLine() or (decl.getStorageClass() == SC_Extern)) {
4770 }
4771
4772 // [class.free]: Any allocation function for a class T is a static member (even if not explicitly declared
4773 // static). (https://eel.is/c++draft/class.free#1)
4774 // However, Clang does not add `static` to `getStorageClass` so this needs to be check independently.
4775 if(isCXXMethodDecl and not decl.isOutOfLine()) {
4776 // GetStorageClassAsStringWithSpace already carries static, if the method was marked so explicitly
4777 if((not IsStaticStorageClass(methodDecl)) and (methodDecl->isStatic())) {
4779 }
4780 }
4781 }
4782
4783 if(Decl::FOK_None != decl.getFriendObjectKind()) {
4785 }
4786
4787 if(decl.isInlined()) {
4789 }
4790
4791 if(methodDecl and isFirstCxxMethodDecl) {
4792 if(methodDecl->isVirtual()) {
4794 }
4795
4796 const auto exspec = ExplicitSpecifier::getFromDecl(methodDecl);
4797
4798 if(const auto* expr = exspec.getExpr()) {
4800
4802 [&] {
4803 switch(exspec.getKind()) {
4804 case ExplicitSpecKind::Unresolved: InsertArg(expr); break;
4805 case ExplicitSpecKind::ResolvedFalse: mOutputFormatHelper.Append(kwFalse); break;
4806 case ExplicitSpecKind::ResolvedTrue: mOutputFormatHelper.Append("true"sv); break;
4807 }
4808 },
4809 AddSpaceAtTheEnd::Yes);
4810
4811 } else if(exspec.isExplicit()) {
4813 }
4814 }
4815
4816 if(const FunctionDecl& constExprDecl{isLambdaStaticInvoker ? *methodDecl->getParent()->getLambdaCallOperator()
4817 : decl};
4818 constExprDecl.isConstexpr()) {
4819
4820 ConstexprSpecKind constexrKind{constExprDecl.isImmediateFunction() ? ConstexprSpecKind::Consteval
4821 : constExprDecl.getConstexprKind()};
4822
4823 if(isLambda) {
4824 // Special treatment for a conversion operator in a captureless lambda. It appears that if the call operator
4825 // is consteval the conversion operator must be manually promoted to consteval as well, otherwise it cannot
4826 // take the address of the invoke function.
4827 if(const auto* callOp{methodDecl->getParent()->getLambdaCallOperator()}; isa<CXXConversionDecl>(decl)) {
4828 if(callOp->isConsteval()) {
4829 constexrKind = ConstexprSpecKind::Consteval;
4830 }
4831
4832 } else if(llvm::SmallVector<clang::PartialDiagnosticAt, 8> Diags{};
4833 // remove a potential constexpr from a lambda if the function is unusable in a constant expression
4834 not clang::Expr::isPotentialConstantExpr(callOp, Diags)) {
4835 constexrKind = ConstexprSpecKind::Unspecified;
4836 }
4837 }
4838
4839 switch(constexrKind) {
4840 case ConstexprSpecKind::Unspecified: break;
4841 case ConstexprSpecKind::Constexpr: mOutputFormatHelper.Append(kwConstExprSpace); break;
4842 case ConstexprSpecKind::Consteval: mOutputFormatHelper.Append(kwConstEvalSpace); break;
4843 case ConstexprSpecKind::Constinit: break;
4844 }
4845 }
4846
4847 // temporary output to be able to handle a return value of array reference
4848 OutputFormatHelper outputFormatHelper{};
4849
4850 if(methodDecl) {
4851 if(not isFirstCxxMethodDecl or InsertNamespace() and decl.getQualifier()) {
4852 CodeGeneratorVariant cg{outputFormatHelper};
4853 cg->InsertNamespace(decl.getQualifier());
4854
4855 // This comes from a using Base::SomeFunc
4856 } else if(not isFirstCxxMethodDecl or InsertNamespace() and not decl.getQualifier()) {
4857 const auto* parent = methodDecl->getParent();
4858 outputFormatHelper.Append(parent->getName(), "::"sv);
4859 }
4860 }
4861
4862 if(not isa<CXXConversionDecl>(decl)) {
4863 if(isa<CXXConstructorDecl>(decl) or isa<CXXDestructorDecl>(decl)) {
4864 if(methodDecl) {
4865 if(isa<CXXDestructorDecl>(decl)) {
4866 outputFormatHelper.Append('~');
4867 }
4868
4869 outputFormatHelper.Append(GetName(*methodDecl->getParent()));
4870 }
4871
4872 } else {
4873 outputFormatHelper.Append(GetName(decl));
4874 }
4875
4876 if(isFirstCxxMethodDecl and decl.isFunctionTemplateSpecialization()) {
4877 CodeGeneratorVariant codeGenerator{outputFormatHelper};
4878 codeGenerator->InsertTemplateArgs(decl);
4879 }
4880
4881 outputFormatHelper.Append('(');
4882 }
4883
4884 // if a CXXInheritedCtorDecl was passed as a pointer us this to get the parameters from.
4885 if(cxxInheritedCtorDecl) {
4886 outputFormatHelper.AppendParameterList(cxxInheritedCtorDecl->parameters(),
4887 OutputFormatHelper::NameOnly::No,
4888 OutputFormatHelper::GenMissingParamName::Yes);
4889 } else {
4890 // The static invoker needs parameter names to forward parameters to the call operator even when the call
4891 // operator doesn't care about them.
4892 const OutputFormatHelper::GenMissingParamName genMissingParamName{
4893 isLambdaStaticInvoker ? OutputFormatHelper::GenMissingParamName::Yes
4894 : OutputFormatHelper::GenMissingParamName::No};
4895
4896 outputFormatHelper.AppendParameterList(
4897 decl.parameters(), OutputFormatHelper::NameOnly::No, genMissingParamName);
4898
4899 if(GetInsightsOptions().UseShow2C and not decl.isVariadic() and decl.param_empty()) {
4900 outputFormatHelper.Append("void"sv);
4901 }
4902 }
4903
4904 if(decl.isVariadic()) {
4905 outputFormatHelper.Append(", ..."sv);
4906 }
4907
4908 outputFormatHelper.Append(')');
4909
4910 if(not isa<CXXConstructorDecl>(decl) and not isa<CXXDestructorDecl>(decl)) {
4911 if(isa<CXXConversionDecl>(decl)) {
4912 const std::string typeName{TypeContainsSubType<PointerType, FunctionProtoType>(desugaredReturnType)
4913 ? BuildRetTypeName(decl)
4914 : GetName(desugaredReturnType)};
4915
4916 mOutputFormatHelper.Append(kwOperatorSpace, typeName, " ("sv, outputFormatHelper.GetString());
4917 } else {
4918 mOutputFormatHelper.Append(GetTypeNameAsParameter(desugaredReturnType, outputFormatHelper.GetString()));
4919 }
4920 } else {
4921 mOutputFormatHelper.Append(outputFormatHelper);
4922 }
4923
4925
4926 if(methodDecl) {
4927 if(methodDecl->isVolatile()) {
4929 }
4930
4931 if(methodDecl->hasAttr<FinalAttr>()) {
4933 }
4934 }
4935
4936 switch(decl.getType()->getAs<FunctionProtoType>()->getRefQualifier()) {
4937 case RQ_None: break;
4938 case RQ_LValue: mOutputFormatHelper.Append(" &"sv); break;
4939 case RQ_RValue: mOutputFormatHelper.Append(" &&"sv); break;
4940 }
4941
4943
4944 // insert the trailing requires-clause, if any. In case, this is a template then we already inserted the
4945 // template requires-clause during creation of the template head.
4947
4948 if(decl.isPureVirtual()) {
4949 mOutputFormatHelper.Append(" = 0"sv);
4950 }
4951
4952 if(decl.isDeleted()) {
4954 if(auto* delInfo =
4955#if IS_CLANG_NEWER_THAN(22)
4956 decl.getDefaultedOrDeletedInfo()
4957#else
4958 decl.getDefalutedOrDeletedInfo()
4959#endif
4960 ) {
4961 WrapInParens([&]() { InsertArg(delInfo->getDeletedMessage()); }, AddSpaceAtTheEnd::No);
4962 } else {
4964 }
4965
4966 } else if(decl.isDefaulted()) {
4968 }
4969}
4970//-----------------------------------------------------------------------------
4971
4973{
4974 const bool requiresCurlys{not isa<InitListExpr>(stmt) and not isa<ParenExpr>(stmt) and
4975 not isa<CXXDefaultInitExpr>(stmt)};
4976
4977 if(requiresCurlys) {
4979 }
4980
4981 InsertArg(stmt);
4982
4983 if(requiresCurlys) {
4985 }
4986}
4987//-----------------------------------------------------------------------------
4988
4990 void_func_ref lambda,
4991 const AddSpaceAtTheEnd addSpaceAtTheEnd)
4992{
4993 if(BraceKind::Curlys == braceKind) {
4995 } else {
4997 }
4998
4999 lambda();
5000
5001 if(BraceKind::Curlys == braceKind) {
5003 } else {
5005 }
5006
5007 if(AddSpaceAtTheEnd::Yes == addSpaceAtTheEnd) {
5009 }
5010}
5011//-----------------------------------------------------------------------------
5012
5013void CodeGenerator::WrapInCompoundIfNeeded(const Stmt* stmt, const AddNewLineAfter addNewLineAfter)
5014{
5015 const bool hasNoCompoundStmt = not(isa<CompoundStmt>(stmt) or isa<AttributedStmt>(stmt));
5016
5017 if(hasNoCompoundStmt) {
5019 }
5020
5021 if(not isa<NullStmt>(stmt)) {
5022 InsertArg(stmt);
5023
5024 const bool isAttrWithCompound{[&] {
5025 auto* attrStmt = dyn_cast_or_null<AttributedStmt>(stmt);
5026 return attrStmt and isa<CompoundStmt>(attrStmt->getSubStmt());
5027 }()};
5028
5029 // Add semi-colon if necessary. A do{} while does already add one.
5030 if(IsStmtRequiringSemi<IfStmt, CompoundStmt, NullStmt, WhileStmt, DoStmt>(stmt) and not isAttrWithCompound) {
5032 }
5033 }
5034
5035 if(hasNoCompoundStmt) {
5036 mOutputFormatHelper.CloseScope(OutputFormatHelper::NoNewLineBefore::Yes);
5037 }
5038
5039 const bool addNewLine = (AddNewLineAfter::Yes == addNewLineAfter);
5040 if(addNewLine or (hasNoCompoundStmt and addNewLine)) {
5042 } else if(not addNewLine or (hasNoCompoundStmt and not addNewLine)) {
5044 }
5045}
5046//-----------------------------------------------------------------------------
5047
5048void CodeGenerator::WrapInParens(void_func_ref lambda, const AddSpaceAtTheEnd addSpaceAtTheEnd)
5049{
5050 WrapInParensOrCurlys(BraceKind::Parens, lambda, addSpaceAtTheEnd);
5051}
5052//-----------------------------------------------------------------------------
5053
5055 void_func_ref lambda,
5056 const AddSpaceAtTheEnd addSpaceAtTheEnd)
5057{
5058 if(needsParens) {
5059 WrapInParensOrCurlys(BraceKind::Parens, lambda, addSpaceAtTheEnd);
5060 } else {
5061 lambda();
5062 }
5063}
5064//-----------------------------------------------------------------------------
5065
5067 void_func_ref lambda,
5068 const AddSpaceAtTheEnd addSpaceAtTheEnd)
5069{
5070 if(needsParens) {
5071 WrapInParensOrCurlys(BraceKind::Curlys, lambda, addSpaceAtTheEnd);
5072 } else {
5073 lambda();
5074 }
5075}
5076//-----------------------------------------------------------------------------
5077
5078void CodeGenerator::WrapInCurlys(void_func_ref lambda, const AddSpaceAtTheEnd addSpaceAtTheEnd)
5079{
5080 WrapInParensOrCurlys(BraceKind::Curlys, lambda, addSpaceAtTheEnd);
5081}
5082//-----------------------------------------------------------------------------
5083
5084void CodeGenerator::InsertArg(const BindingDecl*)
5085{
5086 // We ignore this here in the global level. In some cases a BindingDecl appears _before_ the DecompositionDecl
5087 // which leads to invalid code. See StructuredBindingsHandler3Test.cpp.
5088}
5089//-----------------------------------------------------------------------------
5090
5092{
5093 const auto* bindingStmt = stmt->getBinding();
5094
5095 // In a dependent context we have no binding and with that no type. Leave this as it is, we are looking at a
5096 // primary template here.
5097 RETURN_IF(not bindingStmt);
5098
5099 // Assume that we are looking at a builtin type. We have to construct the variable declaration information.
5100 auto type = stmt->getType();
5101 bool addConstCast{};
5102
5103 // If we have a holding var we are looking at a user defined type like tuple and those the defaults from above
5104 // are wrong. This type contains the variable declaration so we insert this.
5105 if(const auto* holdingVar = stmt->getHoldingVar()) {
5106 // Initial paper: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0144r0.pdf
5107
5108 // The type of the binding depends on the initializer. In case the initializer is an lvalue we get a T&,
5109 // otherwise a T&&. We typically look at an lvalue if the decomposition declaration was auto& [a,b]. Note
5110 // the & here We have a rvalue in case the decomposition declaration was auto [a,b]. Note no reference. The
5111 // standard std::get returns a lvalue reference in case e in get(e) is an lvalue, otherwise it returns an
5112 // rvalue reference because then the call is get(std::move(e))
5113 type = holdingVar->getType().getCanonicalType();
5114
5115 // The holding variable's type is always a reference. Make sure we do not lose it.
5116 if(not type->isReferenceType()) {
5117 type = stmt->getASTContext().getRValueReferenceType(type);
5118 }
5119
5120 bindingStmt = holdingVar->getAnyInitializer();
5121
5122 // Newer Clang versions no longer add a NoOp cast to bind a `T&&` with const to a prvalue of a non-class type.
5123 addConstCast = type->isRValueReferenceType() and type->getPointeeType().isConstQualified() and
5124 not type->getPointeeType()->isRecordType() and bindingStmt and bindingStmt->isPRValue() and
5125 not isa<CastExpr>(bindingStmt->IgnoreParens());
5126
5127 } else if(not type->isLValueReferenceType()) {
5128 type = stmt->getASTContext().getLValueReferenceType(type);
5129 }
5130
5131 InsertAttributes(stmt->attrs());
5132
5133 mOutputFormatHelper.Append(GetQualifiers(*dyn_cast_or_null<VarDecl>(stmt->getDecomposedDecl())),
5134 GetTypeNameAsParameter(type, GetName(*stmt)),
5135 hlpAssing);
5136
5137 if(addConstCast) {
5138 mOutputFormatHelper.Append(kwStaticCast, "<"sv, GetName(type->getPointeeType()), ">"sv);
5139 WrapInParens([&] { InsertArg(bindingStmt); });
5140
5141 } else {
5142 InsertArg(bindingStmt);
5143 }
5144
5146}
5147//-----------------------------------------------------------------------------
5148
5149void StructuredBindingsCodeGenerator::InsertDecompositionBindings(const DecompositionDecl& decompositionDeclStmt)
5150{
5151 for(const auto* bindingDecl : decompositionDeclStmt.bindings()) {
5152 InsertArg(bindingDecl);
5153 }
5154}
5155//-----------------------------------------------------------------------------
5156
5158{
5159 const auto name = GetName(*stmt);
5160
5162
5163 if(name.empty()) {
5164 mOutputFormatHelper.Append(mVarName);
5165 } else {
5166 InsertTemplateArgs(*stmt);
5167 }
5168}
5169//-----------------------------------------------------------------------------
5170
5171} // namespace clang::insights
#define IS_CLANG_NEWER_THAN(major)
Definition ClangCompat.h:16
#define CASE(K, retVal)
#define BUILD_OPT_AND_O(name, param, ret)
#define BUILD_OPT_AND(name, param)
#define LAMBDA_SCOPE_HELPER(type)
Convenience macro to create a LambdaScopeHandler on the stack.
#define CONDITIONAL_LAMBDA_SCOPE_HELPER(type, cond)
The lambda scope helper is only created if cond is true.
const InsightsOptions & GetInsightsOptions()
Get the global C++ Insights options.
Definition Insights.cpp:37
const ASTContext & GetGlobalAST()
Get access to the ASTContext.
Definition Insights.cpp:71
InsightsOptions & GetInsightsOptionsRW()
Definition Insights.cpp:43
#define SCOPE_HELPER(d)
Helper to create a ScopeHandler on the stack which adds the current Decl to it and removes it once th...
constexpr std::string_view kwTemplate
constexpr std::string_view kwNull
constexpr std::string_view kwCommentStart
constexpr std::string_view kwRequiresSpace
constexpr std::string_view kwStaticAssert
constexpr std::string_view kwCoAwaitSpace
constexpr std::string_view kwReturn
constexpr std::string_view kwReinterpretCast
constexpr std::string_view kwNamespaceSpace
constexpr std::string_view kwContinue
constexpr std::string_view kwSpaceConstEvalSpace
constexpr std::string_view cxaStart
constexpr std::string_view kwCCommentEndSpace
constexpr std::string_view kwInlineSpace
constexpr std::string_view kwMutableSpace
constexpr std::string_view kwUsingSpace
constexpr std::string_view kwDelete
constexpr std::string_view kwConstExpr
constexpr std::string_view kwConstExprSpace
constexpr std::string_view kwTemplateSpace
constexpr std::string_view kwCoReturnSpace
constexpr std::string_view kwCoYieldSpace
constexpr std::string_view kwSpaceConstExpr
constexpr std::string_view kwSpaceCCommentEndSpace
constexpr std::string_view kwNamespace
constexpr std::string_view kwCaseSpace
constexpr std::string_view kwSizeof
constexpr std::string_view kwUnionSpace
constexpr std::string_view kwSpaceCCommentEnd
constexpr std::string_view kwEnumSpace
constexpr std::string_view kwElipsisSpace
constexpr std::string_view kwClassSpace
constexpr std::string_view kwTypeId
constexpr std::string_view kwConstEvalSpace
constexpr std::string_view kwSpaceEqualsDefault
constexpr std::string_view kwThis
constexpr std::string_view kwSpaceVolatile
constexpr std::string_view kwStructSpace
constexpr std::string_view functionPointerPrefix
constexpr std::string_view kwStaticCast
constexpr std::string_view kwElipsis
constexpr std::string_view kwOperator
constexpr std::string_view kwSpaceFinal
constexpr std::string_view kwNoexcept
constexpr std::string_view kwCCommentStartSpace
constexpr std::string_view kwTypeNameSpace
constexpr std::string_view kwExplicitSpace
constexpr std::string_view kwSwitch
constexpr std::string_view kwFriendSpace
constexpr std::string_view kwOperatorSpace
constexpr std::string_view kwInternalThis
constexpr std::string_view kwCppCommentStartSpace
constexpr std::string_view kwStaticSpace
constexpr std::string_view kwSpaceNoexcept
constexpr std::string_view kwSpaceEqualsDelete
constexpr std::string_view kwAlignof
constexpr std::string_view kwRequires
constexpr std::string_view kwNullptr
constexpr std::string_view kwFalse
constexpr std::string_view kwTrySpace
constexpr std::string_view kwExplicit
constexpr std::string_view hlpArrow
constexpr std::string_view hlpAssing
constexpr std::string_view kwBreak
constexpr std::string_view kwDoSpace
constexpr std::string_view memberVariablePointerPrefix
constexpr std::string_view kwWhile
constexpr std::string_view cxaAtExit
constexpr std::string_view kwVirtualSpace
constexpr std::string_view kwGotoSpace
constexpr std::string_view kwConceptSpace
#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
llvm::function_ref< void()> void_func_ref
T & back() noexcept
Definition StackList.h:67
constexpr bool empty() const noexcept
Definition StackList.h:69
void push(TStackListEntry &entry) noexcept
Definition StackList.h:31
ArrayInitCodeGenerator(OutputFormatHelper &_outputFormatHelper, const uint64_t index)
void InsertArg(const ArrayInitIndexExpr *) override
Find a DeclRefExpr belonging to a DecompositionDecl.
void VisitDeclRefExpr(const DeclRefExpr *expr)
void setCallerType(LambdaCallerType lambdaCallerType)
LambdaScopeHandler(LambdaStackType &stack, OutputFormatHelper &outputFormatHelper, const LambdaCallerType lambdaCallerType)
More or less the heart of C++ Insights.
void InsertFunctionNameWithReturnType(const FunctionDecl &decl, const CXXConstructorDecl *cxxInheritedCtorDecl=nullptr)
Insert the code for a FunctionDecl.
void WrapInParensIfNeeded(bool needsParens, void_func_ref lambda, const AddSpaceAtTheEnd addSpaceAtTheEnd=AddSpaceAtTheEnd::No)
void WrapInCompoundIfNeeded(const Stmt *stmt, const AddNewLineAfter addNewLineAfter)
static std::map< std::string, bool > mSeenDecls
void HandleCompoundStmt(const CompoundStmt *stmt)
void InsertQualifierAndNameWithTemplateArgs(const DeclarationName &declName, const auto *stmt)
void WrapInParens(void_func_ref lambda, const AddSpaceAtTheEnd addSpaceAtTheEnd=AddSpaceAtTheEnd::No)
static std::string FillConstantArray(const ConstantArrayType *ct, const std::string &value, const uint64_t startAt)
virtual void InsertArg(const Decl *stmt)
void InsertTemplateSpecializationHeader(const Decl &)
Insert template<> to introduce a template specialization.
void WrapInCurlys(void_func_ref lambda, const AddSpaceAtTheEnd addSpaceAtTheEnd=AddSpaceAtTheEnd::No)
virtual bool InsertVarDecl(const VarDecl *)
void InsertInstantiationPoint(const SourceManager &sm, const SourceLocation &instLoc, std::string_view text={})
Inserts the instantiation point of a template.
ProcessingPrimaryTemplate mProcessingPrimaryTemplate
void InsertTemplateArgs(const T &t)
std::optional< size_t > mCurrentVarDeclPos
OutputFormatHelper * mOutputFormatHelperOutside
Helper output buffer for std::initializer_list expansion.
void WrapInCurliesIfNeeded(bool needsParens, void_func_ref lambda, const AddSpaceAtTheEnd addSpaceAtTheEnd=AddSpaceAtTheEnd::No)
void InsertTemplateGuardEnd(const FunctionDecl *stmt)
static std::string GetValueOfValueInit(const QualType &t)
void LifetimeAddExtended(const VarDecl *, const ValueDecl *)
void HandleLocalStaticNonTrivialClass(const VarDecl *stmt)
Show what is behind a local static variable.
virtual void FormatCast(const std::string_view castName, const QualType &CastDestType, const Expr *SubExpr, const CastKind &castKind)
std::optional< size_t > mCurrentCallExprPos
virtual void InsertCXXMethodDecl(const CXXMethodDecl *stmt, SkipBody skipBody)
void InsertTemplateParameters(const TemplateParameterList &list, const TemplateParamsOnly templateParamsOnly=TemplateParamsOnly::No)
! Skip template, type constraints and class/typename.
void HandleTemplateParameterPack(const ArrayRef< TemplateArgument > &args)
void InsertIfOrSwitchInitVariables(same_as_any_of< const IfStmt, const SwitchStmt > auto *stmt)
void UpdateCurrentPos(std::optional< size_t > &pos)
bool InsertLambdaStaticInvoker(const CXXMethodDecl *cxxMethodDecl)
void InsertTemplateGuardBegin(const FunctionDecl *stmt)
void InsertConceptConstraint(const llvm::SmallVectorImpl< const Expr * > &constraints, const InsertInline insertInline)
virtual bool InsertNamespace() const
void ForEachArg(const auto &arguments, auto &&lambda)
void InsertQualifierAndName(const DeclarationName &declName, const NestedNameSpecifier *qualifier, const bool hasTemplateKeyword)
NoEmptyInitList mNoEmptyInitList
At least in case if a requires-clause containing T{} we don't want to get T{{}}.
std::optional< size_t > mCurrentReturnPos
void InsertMethodBody(const FunctionDecl *stmt, const size_t posBeforeFunc)
bool mRequiresImplicitReturnZero
Track whether this is a function with an imlpicit return 0.
void InsertConstructorExpr(const T *stmt)
Generalized function to insert either a CXXConstructExpr or CXXUnresolvedConstructExpr.
void InsertArgWithParensIfNeeded(const Stmt *stmt)
static constexpr auto MAX_FILL_VALUES_FOR_ARRAYS
void WrapInParensOrCurlys(const BraceKind curlys, void_func_ref lambda, const AddSpaceAtTheEnd addSpaceAtTheEnd=AddSpaceAtTheEnd::No)
std::optional< size_t > mCurrentFieldPos
void InsertTemplateArg(const TemplateArgument &arg)
void InsertSuffix(const QualType &type)
void InsertCurlysIfRequired(const Stmt *stmt)
Check whether or not this statement will add curlys or parentheses and add them only if required.
OutputFormatHelper & mOutputFormatHelper
void InsertTemplate(const FunctionTemplateDecl *, bool withSpec)
constexpr CodeGenerator(OutputFormatHelper &_outputFormatHelper)
! We do not want to transform a primary template which contains a Coroutine.
void InsertCXXMethodHeader(const CXXMethodDecl *stmt, OutputFormatHelper &initOutputFormatHelper)
void InsertTemplateArgsObjectParam(const ArrayRef< TemplateArgument > &array)
virtual bool ShowXValueCasts() const
Show casts to xvalues independent from the show all casts option.
static std::string_view GetBuiltinTypeSuffix(const BuiltinType::Kind &kind)
ShowConstantExprValue mShowConstantExprValue
void ParseDeclContext(const DeclContext *Ctx)
A special container which creates either a CodeGenerator or a CfrontCodeGenerator depending on the co...
ContinueASTTransformer(Stmt *stmt, std::string_view continueLabel)
A special generator for coroutines. It is only activated, if -show-coroutines-transformation is given...
void InsertArg(const LambdaExpr *stmt) override
bool EndScope(OutputFormatHelper &ofm, bool clear)
void Add(const VarDecl *decl)
bool Return(OutputFormatHelper &ofm)
void AddExtended(const VarDecl *decl, const ValueDecl *extending)
void DecreaseIndent()
Decrease the current indention by SCOPE_INDENT.
void OpenScope()
Open a scope by inserting a '{' followed by an indented newline.
void AppendNewLine(const char c)
Same as Append but adds a newline after the last argument.
void AppendParameterList(const ArrayRef< ParmVarDecl * > parameters, const NameOnly nameOnly=NameOnly::No, const GenMissingParamName genMissingParamName=GenMissingParamName::No)
Append a ParamVarDecl array.
void AppendComma(OnceFalse &needsComma)
Append a comma if needed.
void AppendComment(const std::string_view &arg)
void Append(const char c)
Append a single character.
std::string & GetString()
Returns a reference to the underlying string buffer.
void CloseScope(const NoNewLineBefore newLineBefore=NoNewLineBefore::No)
Close a scope by inserting a '}'.
void AppendCommentNewLine(const std::string_view &arg)
void IncreaseIndent()
Increase the current indention by SCOPE_INDENT.
void SetIndent(const unsigned indent, const SkipIndenting skipIndenting=SkipIndenting::No)
Set the indent level of this class to indent.
size_t CurrentPos() const
Returns the current position in the output buffer.
void AppendSemiNewLine()
Append a semicolon and a newline.
void InsertAt(const size_t atPos, std::string_view data)
Insert a string before the position atPos.
! 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 &)
void InsertDecompositionBindings(const DecompositionDecl &decompositionDeclStmt)
Inserts the bindings of a decompositions declaration.
StructuredBindingsCodeGenerator(OutputFormatHelper &_outputFormatHelper, std::string &&varName)
void InsertArg(const DeclRefExpr *stmt) override
virtual bool ShowXValueCasts() const override
Show casts to xvalues independent from the show all casts option.
Find a DeclRefExpr belonging to a DecompositionDecl.
TemporaryDeclFinder(CodeGenerator &_codeGenerator, const Stmt *stmt, bool inspectReturn=false)
void VisitCXXTemporaryObjectExpr(CXXTemporaryObjectExpr *expr)
void VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *stmt)
Handle using statements which pull functions ore members from a base class into the class.
void InsertArg(const FieldDecl *stmt) override
UsingCodeGenerator(OutputFormatHelper &_outputFormatHelper)
void InsertArg(const CXXMethodDecl *stmt) override
void InsertArg(const CXXRecordDecl *) override
void InsertArg(const TypedefDecl *) override
BinaryOperator * Equal(Expr *var, Expr *assignExpr)
DeclStmt * mkDeclStmt(Dcls... dcls)
Definition ASTHelpers.h:91
DeclRefExpr * mkDeclRefExpr(const ValueDecl *vd)
QualType Typedef(std::string_view name, QualType underlayingType)
CXXNewExpr * New(ArrayRef< Expr * > placementArgs, const Expr *expr, QualType t)
CallExpr * Call(const FunctionDecl *fd, ArrayRef< Expr * > params)
UnaryOperator * Ref(const Expr *e)
VarDecl * Variable(std::string_view name, QualType type, DeclContext *dc)
static CallExpr * CallConstructor(QualType ctorType, DeclRefExpr *lhsDeclRef, Expr *lhsMemberExpr, ArrayRef< Expr * > callParams, DoCast doCast, AsReference asReference)
SmallVector< Expr *, 5 > ArgsToExprVector(const Expr *expr)
ReturnStmt * Return(Expr *stmt)
Stmt * Comment(std::string_view comment)
IfStmt * If(const Expr *condition, ArrayRef< Stmt * > bodyStmts)
BinaryOperator * And(VarDecl *lhs, Expr *rhs)
GotoStmt * Goto(std::string_view labelName)
InitListExpr * InitList(ArrayRef< Expr * > initExprs, QualType t)
CXXCatchStmt * Catch(ArrayRef< Stmt * > body)
CXXThrowExpr * Throw(const Expr *expr)
void ReplaceNode(Stmt *parent, Stmt *oldNode, Stmt *newNode)
UnaryExprOrTypeTraitExpr * Sizeof(QualType toType)
LabelStmt * Label(std::string_view name)
CXXBoolLiteralExpr * Bool(bool b)
FunctionDecl * Function(std::string_view name, QualType returnType, const params_vector &parameters)
CompoundStmt * mkCompoundStmt(ArrayRef< Stmt * > bodyStmts, SourceLocation beginLoc, SourceLocation endLoc)
IntegerLiteral * Int32(uint64_t value)
CXXStaticCastExpr * StaticCast(QualType toType, const Expr *toExpr, bool makePointer)
BinaryOperator * Assign(const VarDecl *var, Expr *assignExpr)
CXXTryStmt * Try(const Stmt *tryBody, CXXCatchStmt *catchAllBody)
CallExpr * CallDestructor(const VarDecl *varDecl)
std::string ConvertToBoolString(bool b)
Convert a boolean value to a string representation of "true" or "false".
void InsertBefore(std::string &source, const std::string_view &find, const std::string_view &replace)
static void PushGlobalVariableDtor(const Expr *callExpr)
uint64_t GetSize(const ConstantArrayType *arrayType)
std::string GetLambdaName(const CXXRecordDecl &lambda)
static std::optional< std::pair< QualType, APValue > > EvaluateNTTPAsConstantExpr(const Expr *expr)
Evaluates a potential NTTP as a constant expression.
@ HeaderUtility
Track whether there was a std::move inserted.
@ HeaderException
Track whether there was a noexcept transformation requireing the exception header.
const SourceManager & GetSM(const Decl &decl)
bool Contains(const std::string_view source, const std::string_view search)
const LangOptions & GetLangOpts(const Decl &decl)
static std::string AccessToStringWithColon(const AccessSpecifier &access, const TrailingSpace endWithTrailingSpace=TrailingSpace::Yes)
std::string BuildRetTypeName(const Decl &decl)
static void ToDo(std::string_view name, OutputFormatHelper &outputFormatHelper, std::source_location loc)
Definition DPrint.cpp:19
static std::optional< std::string > GetFieldDeclNameForLambda(const FieldDecl &fieldDecl, const CXXRecordDecl &cxxRecordDecl)
const std::string GetNoExcept(const FunctionDecl &decl)
std::string GetPlainName(const DeclRefExpr &DRE)
static std::string_view ArrowOrDot(bool isArrow)
std::string GetElaboratedTypeKeyword(const ElaboratedTypeKeyword keyword)
void PushVtableEntry(const CXXRecordDecl *record, const CXXRecordDecl *recordB, VarDecl *decl)
int GetGlobalVtablePos(const CXXRecordDecl *record, const CXXRecordDecl *recordB)
static T ValueOr(bool b, T val, T el)
void Error(const char *fmt, const auto &... args)
Log an error.
Definition DPrint.h:80
Once< false > OnceFalse
Returns false only once, following checks return true.
std::string MakeLineColumnName(const SourceManager &sm, const SourceLocation &loc, const std::string_view &prefix)
static std::string_view GetCastName(const CastKind castKind, bool constnessChange=false)
static std::string GetTypeConstraintAsString(const TypeConstraint *typeConstraint)
static bool IsStmtRequiringSemi(const Stmt *stmt)
QualType GetType(QualType t)
In Cfront mode we transform references to pointers.
std::string GetName(const NamedDecl &nd, const QualifiedName qualifiedName)
std::string EmitGlobalVariableCtors()
static std::string_view EllipsisSpace(bool b)
void DPrint(const char *fmt, const auto &... args)
Debug print which is disabled in release-mode.
Definition DPrint.h:71
std::string BuildInternalVarName(const std::string_view &varName)
auto GetSpaces(std::string::size_type offset)
static void PushGlobalVariable(const Expr *callExpr)
static std::string FormatVarTemplateSpecializationDecl(const Decl *decl, std::string &&defaultName)
static std::string GetStorageClassAsStringWithSpace(const StorageClass &sc)
static SmallVector< Expr *, 10 > globalVarDtors
static std::string_view GetTagDeclTypeName(const TagDecl &decl)
const std::string_view GetConst(const FunctionDecl &decl)
static bool IsConstQualifiedType(QualType type)
std::string GetTemporaryName(const Expr &tmp)
std::string BuildTemplateParamObjectName(std::string name)
static std::string GetQualifiers(const VarDecl &vd)
bool IsReferenceType(const ValueDecl *decl)
static T ValueOrDefault(bool b, T v)
void AppendTemplateTypeParamName(OutputFormatHelper &ofm, const TemplateTypeParmDecl *decl, const bool isParameter, const TemplateTypeParmType *type)
static std::string_view Ellipsis(bool b)
Once< true > OnceTrue
Returns true only once, following checks return false.
std::string GetDeclContext(const DeclContext *ctx, WithTemplateParameters withTemplateParameters)
const QualType GetDesugarType(const QualType &QT)
Remove decltype from a QualType, if possible.
static SmallVector< Expr *, 10 > globalVarCtors
static std::string GetUnqualifiedScopelessName(const Type *type, const InsightsSuppressScope supressScope)
bool IsStaticStorageClass(const CXXMethodDecl *md)
static bool IsPrimaryTemplatePackExpansionExpr(const ParenListExpr *stmt)
void EnableGlobalInsert(GlobalInserts idx)
Definition Insights.cpp:96
std::string GetNestedName(const NestedNameSpecifier *nns, const IgnoreNamespace ignoreNamespace)
static SmallVector< std::pair< std::pair< const CXXRecordDecl *, const CXXRecordDecl * >, VarDecl * >, 10 > gVtables
void for_each(T start, T end, TFunc &&func)
static std::string_view GetStorageClassAsString(const StorageClass &sc)
std::string GetTypeNameAsParameter(const QualType &t, std::string_view varName, const Unqualified unqualified)
std::function Isa
static auto & GetRecordLayout(const RecordDecl *recordDecl)
static bool IsTrivialStaticClassVarDecl(const DeclRefExpr &declRefExpr)
std::string StrCat(const auto &... args)
QualType GetDesugarReturnType(const FunctionDecl &FD)
! A helper type to have a container for ArrayRef
Definition ASTHelpers.h:64
Handy helper to avoid longish comparisons.
constexpr bool any_of(const auto &... ts) const