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