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 mProcessingVarDecl = false;
1416 BackupAndRestore _{mProcessingVarDecl, true};
1417
1418 if(MyOptional<const InitListExpr*> initList{dyn_cast_or_null<InitListExpr>(init)};
1419 GetInsightsOptions().UseShow2C and
1420 initList.and_then(CanonicalType).and_then(Isa<RecordType>).and_not(IsPointer).and_then(IsPOD) and
1421 not isa<ArrayType>(stmt->getType())) {
1422 auto* callMemset = Call("memset"sv, {Ref(stmt), Int32(0), Sizeof(stmt->getType())});
1423
1425
1426 if(stmt->hasGlobalStorage()) {
1427 PushGlobalVariable(callMemset);
1428
1429 } else {
1431 InsertArg(callMemset);
1432 }
1433
1434 } else if(not(GetInsightsOptions().UseShow2C and
1435 initList.and_then(CanonicalType)
1436 .and_then(Isa<RecordType>)
1437 .and_not(IsPointer)
1438 .and_not(IsPOD) and
1439 (0 == initList.value()->getNumInits()))) {
1440
1441 // Skip the init statement in case we have a class type with a trivial default-constructor which
1442 // is used for this initialization.
1443 if(not(ctorExpr and ctorExpr->getConstructor()->isDefaultConstructor() and
1444 ctorExpr->getConstructor()->getParent()->hasTrivialDefaultConstructor())) {
1445
1446 const bool isPrimaryTemplatePackExpansionExpr{
1447 IsPrimaryTemplatePackExpansionExpr(dyn_cast_or_null<ParenListExpr>(init))};
1448
1449 if(not isa<CXXParenListInitExpr>(init) and not isPrimaryTemplatePackExpansionExpr) {
1451 }
1452
1453 WrapInParensIfNeeded(isPrimaryTemplatePackExpansionExpr, [&] {
1454 if(GetInsightsOptions().ShowLifetime and init->isXValue() and
1455 stmt->getType()->isRValueReferenceType()) {
1456
1457 if(GetInsightsOptions().UseShow2C) {
1458 mOutputFormatHelper.Append("&");
1459 }
1460
1461 InsertArg(StaticCast(stmt->getType(), init, false));
1462
1463 } else {
1464 InsertArg(init);
1465 }
1466 });
1467 }
1468 }
1469 }
1470 } else if(GetInsightsOptions().UseShow2C and stmt->hasGlobalStorage() and
1471 (stmt->getStorageDuration() == SD_Static) and
1472 (stmt->getDeclContext()->isNamespace() or
1473 (is{stmt->getStorageClass()}.any_of(SC_Static, SC_Extern)))) {
1474 PushGlobalVariable(Assign(stmt, Int32(0)));
1475 }
1476
1477 if(stmt->isNRVOVariable()) {
1478 mOutputFormatHelper.Append(" /* NRVO variable */"sv);
1479 }
1480
1481 if(InsertSemi()) {
1483 }
1484
1485 // Insert the bindings of a DecompositionDecl if this VarDecl is a DecompositionDecl.
1486 if(const auto* decompDecl = dyn_cast_or_null<DecompositionDecl>(stmt)) {
1487 StructuredBindingsCodeGenerator codeGenerator{mOutputFormatHelper, GetName(*stmt)};
1488
1489 codeGenerator.InsertDecompositionBindings(*decompDecl);
1490 }
1491 }
1492
1493 if(needsGuard) {
1494 mOutputFormatHelper.InsertEndIfTemplateGuard();
1495 }
1496}
1497//-----------------------------------------------------------------------------
1498
1499bool CodeGenerator::InsertLambdaStaticInvoker(const CXXMethodDecl* cxxMethodDecl)
1500{
1501 if(not(cxxMethodDecl and cxxMethodDecl->isLambdaStaticInvoker())) {
1502 return false;
1503 }
1504
1505 // A special case for a lambda with a static invoker. The standard says, that in such a case invoking the call
1506 // operator gives the same result as invoking the function pointer (see [expr.prim.lambda.closure] p9). When it
1507 // comes to block local statics having a body for both functions reveals a difference. This special code
1508 // generates a forwarding call from the call operator to the static invoker. However, the compiler does better
1509 // here. As this way we end up with copies of the parameters which is hard to avoid.
1510
1513
1514 if(not cxxMethodDecl->getReturnType()->isVoidType()) {
1516 }
1517
1518 mOutputFormatHelper.Append(GetName(*cxxMethodDecl->getParent()), "{}.operator()"sv);
1519
1520 if(cxxMethodDecl->isFunctionTemplateSpecialization()) {
1521 InsertTemplateArgs(*dyn_cast_or_null<FunctionDecl>(cxxMethodDecl));
1522 }
1523
1524 if(cxxMethodDecl->isTemplated()) {
1525 if(cxxMethodDecl->getDescribedTemplate()) {
1526 InsertTemplateParameters(*cxxMethodDecl->getDescribedTemplate()->getTemplateParameters(),
1527 TemplateParamsOnly::Yes);
1528 }
1529 /*else if(decl.isFunctionTemplateSpecialization()) {
1530 InsertTemplateSpecializationHeader();
1531 }*/
1532 }
1533
1534 WrapInParens([&] {
1535 mOutputFormatHelper.AppendParameterList(cxxMethodDecl->parameters(),
1536 OutputFormatHelper::NameOnly::Yes,
1537 OutputFormatHelper::GenMissingParamName::Yes);
1538 });
1539
1541 mOutputFormatHelper.CloseScope(OutputFormatHelper::NoNewLineBefore::Yes);
1543
1544 return true;
1545}
1546//-----------------------------------------------------------------------------
1547
1548/// \brief Inserts the instantiation point of a template.
1549//
1550// This reveals at which place the template is first used.
1551void CodeGenerator::InsertInstantiationPoint(const SourceManager& sm,
1552 const SourceLocation& instLoc,
1553 std::string_view text)
1554{
1555 const auto lineNo = sm.getSpellingLineNumber(instLoc);
1556 const auto& fileId = sm.getFileID(instLoc);
1557 if(const auto file = sm.getFileEntryRefForID(fileId)) {
1558 const auto fileWithDirName = file->getName();
1559 const auto fileName = llvm::sys::path::filename(fileWithDirName);
1560
1561 if(text.empty()) {
1562 text = "First instantiated from: "sv;
1563 }
1564
1565 mOutputFormatHelper.AppendCommentNewLine(text, fileName, ":"sv, lineNo);
1566 }
1567}
1568//-----------------------------------------------------------------------------
1569
1570void CodeGenerator::InsertTemplateGuardBegin(const FunctionDecl* stmt)
1571{
1572 if(stmt->isTemplateInstantiation() and stmt->isFunctionTemplateSpecialization()) {
1573 InsertInstantiationPoint(GetSM(*stmt), stmt->getPointOfInstantiation());
1575 }
1576}
1577//-----------------------------------------------------------------------------
1578
1579void CodeGenerator::InsertTemplateGuardEnd(const FunctionDecl* stmt)
1580{
1581 if(stmt->isTemplateInstantiation() and stmt->isFunctionTemplateSpecialization()) {
1583 }
1584}
1585//-----------------------------------------------------------------------------
1586
1587void CodeGenerator::InsertArg(const CoroutineBodyStmt* stmt)
1588{
1589 InsertArg(stmt->getBody());
1590}
1591//-----------------------------------------------------------------------------
1592
1593void CodeGenerator::InsertArg(const DependentCoawaitExpr* stmt)
1594{
1596
1597 InsertArg(stmt->getOperand());
1598}
1599//-----------------------------------------------------------------------------
1600
1601void CodeGenerator::InsertArg(const CoroutineSuspendExpr* stmt)
1602{
1603 // co_await or co_yield
1604 if(isa<CoyieldExpr>(stmt)) {
1606 } else {
1608 }
1609
1610 // peal of __promise.yield_value
1611 if(const auto* matTemp = dyn_cast_or_null<MaterializeTemporaryExpr>(stmt->getCommonExpr())) {
1612 const auto* temporary = matTemp->getSubExpr();
1613
1614 if(const auto* memExpr = dyn_cast_or_null<CXXMemberCallExpr>(temporary)) {
1615 ForEachArg(memExpr->arguments(), [&](const auto& arg) { InsertArg(arg); });
1616
1617 // Seems to be the path for a co_await expr
1618 } else {
1619 InsertArg(temporary);
1620 }
1621 } else if(const auto* unaryexpr = dyn_cast_or_null<UnaryOperator>(stmt->getOperand())) {
1622 if(const auto* callExpr = dyn_cast_or_null<CallExpr>(unaryexpr->getSubExpr())) {
1623 InsertArg(callExpr->getArg(0));
1624 }
1625 }
1626}
1627//-----------------------------------------------------------------------------
1628
1629void CodeGenerator::InsertArg(const CoreturnStmt* stmt)
1630{
1632 InsertArg(stmt->getOperand());
1633}
1634//-----------------------------------------------------------------------------
1635
1636void CodeGenerator::InsertMethodBody(const FunctionDecl* stmt, const size_t posBeforeFunc)
1637{
1638 auto IsPrimaryTemplate = [&] {
1639 // For now, don't transform the primary template of a coroutine
1640 if(const auto* cxxMethod = dyn_cast_or_null<CXXMethodDecl>(stmt)) {
1641 if(const auto* tmpl = cxxMethod->getParent()->getDescribedClassTemplate();
1642 tmpl and not isa<ClassTemplateSpecializationDecl>(cxxMethod->getParent())) {
1643 return true;
1644 }
1645 }
1646
1647 return (FunctionDecl::TK_FunctionTemplate == stmt->getTemplatedKind()) or
1648 (ProcessingPrimaryTemplate::Yes == mProcessingPrimaryTemplate);
1649 };
1650
1651 if(stmt->doesThisDeclarationHaveABody()) {
1653
1654 // If this function has a CoroutineBodyStmt as direct descend and coroutine transformation is enabled use
1655 // the \c CoroutinesCodeGenerator, otherwise insert the body as usual.
1656 if(const auto* corBody = dyn_cast_or_null<CoroutineBodyStmt>(stmt->getBody());
1657 (nullptr != corBody) and not IsPrimaryTemplate() and GetInsightsOptions().ShowCoroutineTransformation) {
1658
1659 CoroutinesCodeGenerator codeGenerator{mOutputFormatHelper, posBeforeFunc};
1660 codeGenerator.InsertCoroutine(*stmt, corBody);
1661 } else {
1662 const auto exSpec = stmt->getExceptionSpecType();
1663 const bool showNoexcept =
1664 GetInsightsOptions().UseShowNoexcept and is{exSpec}.any_of(EST_BasicNoexcept, EST_NoexceptTrue);
1665
1666 // handle C++ [basic.start.main] §5: main can have no return statement
1667 if(stmt->hasImplicitReturnZero()) {
1668 mRequiresImplicitReturnZero = ranges::none_of(dyn_cast<CompoundStmt>(stmt->getBody())->body(),
1669 [](const Stmt* e) { return isa<ReturnStmt>(e); });
1670 }
1671
1672 const auto* body = stmt->getBody();
1673
1674 if(showNoexcept) {
1676
1677 body = mkCompoundStmt(Try(body, Catch(Call("std::terminate"sv, {}))));
1678 }
1679
1680 if(GetInsightsOptions().ShowLifetime) {
1681 for(const auto* param : stmt->parameters()) {
1682 auto paramType = param->getType();
1683 const bool isPassByValue{not paramType->isPointerType() and not paramType->isReferenceType()};
1684 if(const auto* rd = paramType->getAsRecordDecl(); rd and isPassByValue) {
1685 mLifeTimeTracker.Add(param);
1686 }
1687 }
1688 }
1689
1690 InsertArg(body);
1691 }
1692
1694 } else {
1696 }
1697}
1698//-----------------------------------------------------------------------------
1699
1700void CodeGenerator::InsertArg(const FunctionDecl* stmt)
1701{
1702 {
1703 LAMBDA_SCOPE_HELPER(Decltype); // Needed for P0315Checker
1704
1705 // Special handling for C++20's P0315 (lambda in unevaluated context). See p0315_2Test.cpp
1706 // We have to look for the lambda expression in the decltype.
1707 P0315Visitor dt{*this};
1708 dt.TraverseType(stmt->getReturnType());
1709
1710 // The arguments can contain a lambda as well
1711 for(const auto& param : stmt->parameters()) {
1712 P0315Visitor dt{*this};
1713 dt.TraverseType(param->getType());
1714 }
1715 }
1716
1717 if(const auto* deductionGuide = dyn_cast_or_null<CXXDeductionGuideDecl>(stmt)) {
1718 InsertArg(deductionGuide);
1719 } else if(const auto* ctor = dyn_cast_or_null<CXXConstructorDecl>(stmt)) {
1720 InsertArg(ctor);
1721 } else {
1722 // skip a case at least in lambdas with a templated conversion operator which is not used and has auto
1723 // return type. This is hard to build with using.
1724 RETURN_IF(isa<CXXConversionDecl>(stmt) and not stmt->hasBody());
1725
1726 const auto posBeforeFunc = mOutputFormatHelper.CurrentPos();
1727
1730
1731 if(not InsertLambdaStaticInvoker(dyn_cast_or_null<CXXMethodDecl>(stmt))) {
1732 InsertMethodBody(stmt, posBeforeFunc);
1733 }
1734
1736 }
1737}
1738//-----------------------------------------------------------------------------
1739
1740static std::string GetTypeConstraintAsString(const TypeConstraint* typeConstraint)
1741{
1742 if(typeConstraint) {
1743 StringStream sstream{};
1744 sstream.Print(*typeConstraint);
1745
1746 return sstream.str();
1747 }
1748
1749 return {};
1750}
1751//-----------------------------------------------------------------------------
1752
1753static std::string_view Ellipsis(bool b)
1754{
1755 return ValueOrDefault(b, kwElipsis);
1756}
1757//-----------------------------------------------------------------------------
1758
1759static std::string_view EllipsisSpace(bool b)
1760{
1761 return ValueOrDefault(b, kwElipsisSpace);
1762}
1763//-----------------------------------------------------------------------------
1764
1765/// \brief Evaluates a potential NTTP as a constant expression.
1766///
1767/// Used for C++20's struct/class as NTTP.
1768static std::optional<std::pair<QualType, APValue>> EvaluateNTTPAsConstantExpr(const Expr* expr)
1769{
1770 expr = expr->IgnoreParenImpCasts();
1771
1772 // The marker when it is a C++20 class as NTTP seems to be CXXFunctionalCastExpr
1773 if(Expr::EvalResult evalResult{};
1774 isa<CXXFunctionalCastExpr>(expr) and
1775 expr->EvaluateAsConstantExpr(evalResult, GetGlobalAST(), ConstantExprKind::Normal)) {
1776 return std::pair<QualType, APValue>{expr->getType(), evalResult.Val};
1777 }
1778
1779 return {};
1780}
1781//-----------------------------------------------------------------------------
1782
1783void CodeGenerator::InsertTemplateParameters(const TemplateParameterList& list,
1784 const TemplateParamsOnly templateParamsOnly)
1785{
1786 const bool full{TemplateParamsOnly::No == templateParamsOnly};
1787
1788 if(full) {
1789 for(const auto* param : list) {
1790 if(const auto* nonTmplParam = dyn_cast_or_null<NonTypeTemplateParmDecl>(param);
1791 nonTmplParam and nonTmplParam->hasDefaultArgument()) {
1792 if(auto val =
1793 EvaluateNTTPAsConstantExpr(nonTmplParam->getDefaultArgument().getArgument().getAsExpr())) {
1794 auto* init = GetGlobalAST().getTemplateParamObjectDecl(val->first, val->second);
1795
1797 }
1798 }
1799 }
1800
1802 }
1803
1805
1806 for(OnceFalse needsComma{}; const auto* param : list) {
1807 mOutputFormatHelper.AppendComma(needsComma);
1808
1809 const auto& typeName = GetName(*param);
1810
1811 if(const auto* tt = dyn_cast_or_null<TemplateTypeParmDecl>(param)) {
1812 if(full) {
1813 if(tt->wasDeclaredWithTypename()) {
1815 } else if(not tt->hasTypeConstraint()) {
1817 }
1818
1819 mOutputFormatHelper.Append(EllipsisSpace(tt->isParameterPack()));
1820 }
1821
1822 if(0 == typeName.size() or tt->isImplicit() /* fixes class container:auto*/) {
1824
1825 } else {
1826 if(auto typeConstraint = GetTypeConstraintAsString(tt->getTypeConstraint());
1827 not typeConstraint.empty()) {
1828 mOutputFormatHelper.Append(std::move(typeConstraint), " "sv);
1829 }
1830
1831 mOutputFormatHelper.Append(typeName);
1832 }
1833
1834 mOutputFormatHelper.Append(EllipsisSpace(not full and tt->isParameterPack()));
1835
1836 if(tt->hasDefaultArgument() and not tt->defaultArgumentWasInherited()) {
1837 const auto& defaultArg = tt->getDefaultArgument();
1838
1839 if(const auto decltypeType = dyn_cast_or_null<DecltypeType>(defaultArg.getArgument().getAsType())) {
1841
1842 InsertArg(decltypeType->getUnderlyingExpr());
1843
1844 } else {
1846 InsertTemplateArg(defaultArg.getArgument());
1847 }
1848 }
1849
1850 } else if(const auto* nonTmplParam = dyn_cast_or_null<NonTypeTemplateParmDecl>(param)) {
1851 if(full) {
1852 if(const auto nttpType = nonTmplParam->getType();
1853 nttpType->isFunctionPointerType() or nttpType->isMemberFunctionPointerType()) {
1855
1856 } else {
1858 GetName(nttpType), " "sv, Ellipsis(nonTmplParam->isParameterPack()), typeName);
1859 }
1860
1861 if(nonTmplParam->hasDefaultArgument()) {
1863 InsertTemplateArg(nonTmplParam->getDefaultArgument().getArgument());
1864 }
1865 } else {
1866 mOutputFormatHelper.Append(typeName, EllipsisSpace(nonTmplParam->isParameterPack()));
1867 }
1868 } else if(const auto* tmplTmplParam = dyn_cast_or_null<TemplateTemplateParmDecl>(param)) {
1869 auto pack{ValueOr(tmplTmplParam->isParameterPack(), kwElipsisSpace, " "sv)};
1870
1871 mOutputFormatHelper.Append(kwTemplateSpace, "<typename> typename"sv, pack, typeName);
1872
1873 if(tmplTmplParam->hasDefaultArgument()) {
1875 InsertTemplateArg(tmplTmplParam->getDefaultArgument().getArgument());
1876 }
1877 }
1878 }
1879
1881
1882 if(full) {
1885 }
1886}
1887//-----------------------------------------------------------------------------
1888
1889void CodeGenerator::InsertArg(const ClassTemplateDecl* stmt)
1890{
1891 {
1892 LAMBDA_SCOPE_HELPER(Decltype); // Needed for P0315Checker
1894
1895 InsertTemplateParameters(*stmt->getTemplateParameters());
1896 }
1897
1898 InsertArg(stmt->getTemplatedDecl());
1899
1900 SmallVector<const ClassTemplateSpecializationDecl*, 10> specializations{};
1901
1902 // XXX C++23: replace with filter and ranges::to<>
1903 for(const auto* spec : stmt->specializations()) {
1904 // Explicit specializations and instantiations will appear later in the AST as dedicated node. Don't
1905 // generate code for them now, otherwise they are there twice.
1906 if(TSK_ImplicitInstantiation == spec->getSpecializationKind()) {
1907 specializations.push_back(spec);
1908 }
1909 }
1910
1911 // Sort specializations by POI to make dependent specializations work.
1912 ranges::sort(specializations,
1913 [](const ClassTemplateSpecializationDecl* a, const ClassTemplateSpecializationDecl* b) {
1914 return a->getPointOfInstantiation() < b->getPointOfInstantiation();
1915 });
1916
1917 for(const auto* spec : specializations) {
1918 InsertArg(spec);
1919 }
1920}
1921//-----------------------------------------------------------------------------
1922
1923void CodeGenerator::InsertArg(const ParenListExpr* stmt)
1924{
1925 for(OnceFalse needsComma{}; const auto& expr : stmt->children()) {
1926 mOutputFormatHelper.AppendComma(needsComma);
1927
1928 InsertArg(expr);
1929 }
1930}
1931//-----------------------------------------------------------------------------
1932
1933/// Fill the values of a constant array.
1934///
1935/// This is either called by \c InitListExpr (which may contain an offset, as the user already provided certain
1936/// values) or by \c GetValueOfValueInit.
1937std::string
1938CodeGenerator::FillConstantArray(const ConstantArrayType* ct, const std::string& value, const uint64_t startAt)
1939{
1940 OutputFormatHelper ret{};
1941
1942 if(ct) {
1943 const auto size{std::clamp(GetSize(ct), uint64_t{0}, MAX_FILL_VALUES_FOR_ARRAYS)};
1944
1945 OnceFalse needsComma{uint64_t{0} != startAt};
1946 for_each(startAt, size, [&](auto) {
1947 ret.AppendComma(needsComma);
1948 ret.Append(value);
1949 });
1950 }
1951
1952 return ret.GetString();
1953}
1954//-----------------------------------------------------------------------------
1955
1956void CodeGenerator::InsertArg(const InitListExpr* stmt)
1957{
1958 // At least in case of a requires-clause containing T{} we don't want to get T{{}}.
1959 RETURN_IF((NoEmptyInitList::Yes == mNoEmptyInitList) and (0 == stmt->getNumInits()));
1960
1961 WrapInCurliesIfNeeded(not GetInsightsOptions().UseShow2C or
1962 (GetInsightsOptions().UseShow2C and
1963 ((stmt->getNumInits() > 1) or stmt->getArrayFiller() or
1964 ((0 < stmt->getNumInits()) and isa<ImplicitValueInitExpr>(stmt->getInit(0))))),
1965 [&]() {
1966 mOutputFormatHelper.IncreaseIndent();
1967
1968 ForEachArg(stmt->inits(), [&](const auto& init) { InsertArg(init); });
1969
1970 if((0 == stmt->getNumInits()) and GetInsightsOptions().UseShow2C) {
1971 if(stmt->getType().getCanonicalType()->isScalarType()) {
1973 return;
1974 } else {
1975 // for a non scalar type that shoud become a memset?
1976 // A a{}; -> A a; memset(&a, 0, sizef(a));
1977 // mOutputFormatHelper.Append("memset("sv);
1978 }
1979 }
1980
1981 // If we have a filler, fill the rest of the array with the filler expr.
1982 if(const auto* filler = stmt->getArrayFiller()) {
1983 OutputFormatHelper ofm{};
1984 CodeGeneratorVariant codeGenerator{ofm};
1985 codeGenerator->InsertArg(filler);
1986
1987 const auto ret = FillConstantArray(
1988 dyn_cast_or_null<ConstantArrayType>(stmt->getType().getTypePtrOrNull()),
1989 ofm.GetString(),
1990 stmt->getNumInits());
1991
1993 }
1994 });
1995
1996 mOutputFormatHelper.DecreaseIndent();
1997}
1998//-----------------------------------------------------------------------------
1999
2000void CodeGenerator::InsertArg(const CXXDefaultInitExpr* stmt)
2001{
2002 const auto* subExpr = stmt->getExpr();
2003
2004 InsertCurlysIfRequired(subExpr);
2005}
2006//-----------------------------------------------------------------------------
2007
2008void CodeGenerator::InsertArg(const CXXDeleteExpr* stmt)
2009{
2011
2012 if(stmt->isArrayForm()) {
2014 }
2015
2017
2018 InsertArg(stmt->getArgument());
2019}
2020//-----------------------------------------------------------------------------
2021
2023{
2024 {
2025 CONDITIONAL_LAMBDA_SCOPE_HELPER(Decltype, not isa<DecltypeType>(stmt->getType()))
2026
2027 P0315Visitor dt{*this};
2028 dt.TraverseType(stmt->getType());
2029
2030 if(not mLambdaStack.empty()) {
2031 for(const auto& e : mLambdaStack) {
2032 RETURN_IF((LambdaCallerType::MemberCallExpr == e.callerType()) and isa<DecltypeType>(stmt->getType()));
2033 }
2034 }
2035 }
2036
2037 mOutputFormatHelper.Append(GetName(stmt->getType(), Unqualified::Yes));
2038
2039 const BraceKind braceKind = ValueOr(stmt->isListInitialization(), BraceKind::Curlys, BraceKind::Parens);
2040
2041 WrapInParensOrCurlys(braceKind, [&]() {
2042 if(const auto& arguments = stmt->arguments(); not arguments.empty()) {
2043 ForEachArg(stmt->arguments(), [&](const auto& arg) { InsertArg(arg); });
2044 }
2045 });
2046}
2047//-----------------------------------------------------------------------------
2048
2049void CodeGenerator::InsertArg(const CXXConstructExpr* stmt)
2050{
2052}
2053//-----------------------------------------------------------------------------
2054
2055void CodeGenerator::InsertArg(const CXXUnresolvedConstructExpr* stmt)
2056{
2057 BackupAndRestore _{mNoEmptyInitList, NoEmptyInitList::Yes};
2058
2060}
2061//-----------------------------------------------------------------------------
2062
2063void CodeGenerator::InsertArg(const UnresolvedMemberExpr* stmt)
2064{
2065 // InsertArg(stmt->getBase());
2066 // const std::string op{}; // stmt->isArrow() ? "->" : "."};
2067
2068 // mOutputFormatHelper.Append(op, stmt->getMemberNameInfo().getAsString());
2069 mOutputFormatHelper.Append(stmt->getMemberNameInfo().getAsString());
2070
2071 if(stmt->getNumTemplateArgs()) {
2072 InsertTemplateArgs(*stmt);
2073 }
2074}
2075//-----------------------------------------------------------------------------
2076
2077void CodeGenerator::InsertArg(const PackExpansionExpr* stmt)
2078{
2079 InsertArg(stmt->getPattern());
2081}
2082//-----------------------------------------------------------------------------
2083
2084void CodeGenerator::InsertArg(const CXXFoldExpr* stmt)
2085{
2086 auto operatorStr = BinaryOperator::getOpcodeStr(stmt->getOperator());
2087
2088 WrapInParens([&] {
2089 // We have a binary NNN fold. If init is nullptr, then it is a unary NNN fold.
2090 const auto* init = stmt->getInit();
2091
2092 if(stmt->isLeftFold()) {
2093 if(init) {
2094 InsertArg(init);
2095 mOutputFormatHelper.Append(" "sv, operatorStr, " "sv);
2096 }
2097
2098 mOutputFormatHelper.Append(kwElipsisSpace, operatorStr, " "sv);
2099 }
2100
2101 InsertArg(stmt->getPattern());
2102
2103 if(stmt->isRightFold()) {
2104 mOutputFormatHelper.Append(" "sv, operatorStr, " "sv, kwElipsis);
2105
2106 if(init) {
2107 mOutputFormatHelper.Append(" "sv, operatorStr, " "sv);
2108 InsertArg(init);
2109 }
2110 }
2111 });
2112}
2113//-----------------------------------------------------------------------------
2114
2115void CodeGenerator::InsertArg(const PackIndexingExpr* stmt)
2116{
2117 if(stmt->isFullySubstituted()) {
2118 const auto* constExpr = dyn_cast_or_null<ConstantExpr>(stmt->getIndexExpr());
2119
2121 constExpr->getAPValueResult().getInt());
2122
2123 } else {
2124 mOutputFormatHelper.Append(GetName(*stmt->getPackDecl()), "...[");
2125
2126 InsertArg(stmt->getIndexExpr());
2128 }
2129}
2130//-----------------------------------------------------------------------------
2131
2132void CodeGenerator::InsertArg(const CXXInheritedCtorInitExpr* stmt)
2133{
2134 const auto& constructorDecl = *stmt->getConstructor();
2135
2136 mOutputFormatHelper.Append(GetName(GetDesugarType(stmt->getType()), Unqualified::Yes));
2137 WrapInParens([&]() {
2138 mOutputFormatHelper.AppendParameterList(constructorDecl.parameters(),
2139 OutputFormatHelper::NameOnly::Yes,
2140 OutputFormatHelper::GenMissingParamName::Yes);
2141 });
2142}
2143//-----------------------------------------------------------------------------
2144
2149//-----------------------------------------------------------------------------
2150
2151void CodeGenerator::InsertArg(const CXXPseudoDestructorExpr* stmt)
2152{
2153 InsertArg(stmt->getBase());
2154
2155 mOutputFormatHelper.Append(ArrowOrDot(stmt->isArrow()), "~", GetName(stmt->getDestroyedType()));
2156}
2157//-----------------------------------------------------------------------------
2158
2159void CodeGenerator::InsertArg(const CXXMemberCallExpr* stmt)
2160{
2162
2163 InsertArg(stmt->getCallee());
2164
2165 WrapInParens([&]() { ForEachArg(stmt->arguments(), [&](const auto& arg) { InsertArg(arg); }); });
2166}
2167//-----------------------------------------------------------------------------
2168
2169void CodeGenerator::InsertArg(const ParenExpr* stmt)
2170{
2171 WrapInParens([&]() { InsertArg(stmt->getSubExpr()); });
2172}
2173//-----------------------------------------------------------------------------
2174
2175void CodeGenerator::InsertArg(const CXXParenListInitExpr* stmt)
2176{
2177 WrapInParens([&]() { ForEachArg(stmt->getInitExprs(), [&](const auto& init) { InsertArg(init); }); });
2178}
2179//-----------------------------------------------------------------------------
2180
2181void CodeGenerator::InsertArg(const UnaryOperator* stmt)
2182{
2183 StringRef opCodeName = UnaryOperator::getOpcodeStr(stmt->getOpcode());
2184 const bool insertBefore{not stmt->isPostfix()};
2185
2186 if(insertBefore) {
2187 mOutputFormatHelper.Append(opCodeName);
2188 }
2189
2190 InsertArg(stmt->getSubExpr());
2191
2192 if(not insertBefore) {
2193 mOutputFormatHelper.Append(opCodeName);
2194 }
2195}
2196//------------- ----------------------------------------------------------------
2197
2198void CodeGenerator::InsertArg(const StringLiteral* stmt)
2199{
2200 StringStream stream{};
2201 stream.Print(*stmt);
2202
2203 mOutputFormatHelper.Append(stream.str());
2204}
2205//-----------------------------------------------------------------------------
2206
2207void CodeGenerator::InsertArg(const ArrayInitIndexExpr* stmt)
2208{
2209 Error(stmt, "ArrayInitIndexExpr should not be reached in CodeGenerator");
2210}
2211//-----------------------------------------------------------------------------
2212
2213void CodeGenerator::InsertArg(const ArraySubscriptExpr* stmt)
2214{
2215 if((not GetInsightsOptions().UseAltArraySubscriptionSyntax) or stmt->getLHS()->isLValue()) {
2216 InsertArg(stmt->getLHS());
2217
2219 InsertArg(stmt->getRHS());
2221 } else {
2222
2223 mOutputFormatHelper.Append("(*("sv);
2224 InsertArg(stmt->getLHS());
2225 mOutputFormatHelper.Append(" + "sv);
2226
2227 InsertArg(stmt->getRHS());
2229 }
2230}
2231//-----------------------------------------------------------------------------
2232
2233void CodeGenerator::InsertArg(const ArrayInitLoopExpr* stmt)
2234{
2235 WrapInCurlys([&]() {
2236 const uint64_t size = stmt->getArraySize().getZExtValue();
2237
2238 ForEachArg(NumberIterator(size), [&](const auto& i) {
2239 ArrayInitCodeGenerator codeGenerator{mOutputFormatHelper, i};
2240 codeGenerator.InsertArg(stmt->getSubExpr());
2241 });
2242 });
2243}
2244//-----------------------------------------------------------------------------
2245
2246void CodeGenerator::InsertArg(const OpaqueValueExpr* stmt)
2247{
2248 InsertArg(stmt->getSourceExpr());
2249}
2250//-----------------------------------------------------------------------------
2251
2252void CodeGenerator::InsertArg(const CallExpr* stmt)
2253{
2254 const bool insideDecltype{InsideDecltype()};
2255
2256 CONDITIONAL_LAMBDA_SCOPE_HELPER(CallExpr, not insideDecltype)
2257 if(insideDecltype) {
2259 }
2260
2262
2263 InsertArg(stmt->getCallee());
2264
2265 if(const auto* declRefExpr = dyn_cast_or_null<DeclRefExpr>(stmt->getCallee()->IgnoreImpCasts())) {
2266 if(const auto* fd = dyn_cast_or_null<FunctionDecl>(declRefExpr->getDecl())) {
2267 if((not declRefExpr->getNumTemplateArgs() and GetInsightsOptions().ShowAllCallExprTemplateParameters) or
2268 isa<UserDefinedLiteral>(stmt)) {
2269 InsertTemplateArgs(*fd);
2270 }
2271 }
2272 }
2273
2274 WrapInParens([&]() {
2275 auto* funcDecl = dyn_cast_or_null<FunctionDecl>(stmt->getCalleeDecl());
2276 unsigned parmIdx{};
2277
2278 ForEachArg(stmt->arguments(), [&](const auto* arg) {
2279 ++parmIdx;
2280
2281 if(const auto* tmpExpr = dyn_cast_or_null<CXXBindTemporaryExpr>(arg)) {
2282 if(const auto* tmp = dyn_cast_or_null<CXXTemporaryObjectExpr>(tmpExpr->getSubExpr())) {
2283 if(GetInsightsOptions().UseShow2C) {
2284 // De-reference the argument since we can only pass temporary objects to functions, not
2285 // pointers
2286 arg = Dref(arg);
2287
2288 } else if(GetInsightsOptions().ShowLifetime) {
2289 mOutputFormatHelper.Append(GetName(*tmp));
2290 return; // from lambda, which is like a continue
2291 }
2292 }
2293 }
2294
2295 if(GetInsightsOptions().UseShow2C and funcDecl and (funcDecl->getNumParams() >= parmIdx) and
2296 IsReferenceType(funcDecl->getParamDecl(parmIdx - 1))) {
2297 if(auto* unop = dyn_cast_or_null<UnaryOperator>(arg); not unop or (unop->getOpcode() != UO_AddrOf)) {
2298 arg = Ref(arg);
2299 }
2300 }
2301
2302 InsertArg(arg);
2303 });
2304 });
2305
2306 if(insideDecltype) {
2307 mLambdaStack.back().setInsertName(false);
2308 }
2309
2310 mCurrentCallExprPos.reset();
2311}
2312//-----------------------------------------------------------------------------
2313
2314void CodeGenerator::InsertArg(const CXXNamedCastExpr* stmt)
2315{
2316 const QualType castDestType = stmt->getTypeAsWritten();
2317 const Expr* subExpr = stmt->getSubExpr();
2318
2319 FormatCast(stmt->getCastName(), castDestType, subExpr, stmt->getCastKind());
2320}
2321//-----------------------------------------------------------------------------
2322
2323void CodeGenerator::InsertArg(const ImplicitCastExpr* stmt)
2324{
2325 const Expr* subExpr = stmt->getSubExpr();
2326 const auto castKind = stmt->getCastKind();
2327 const bool hideImplicitCasts{not GetInsightsOptions().ShowAllImplicitCasts};
2328
2329 auto isMatchingCast = [](const CastKind kind, const bool hideImplicitCasts, const bool showXValueCasts) {
2330 switch(kind) {
2331 case CastKind::CK_Dependent: [[fallthrough]];
2332 case CastKind::CK_IntegralCast: [[fallthrough]];
2333 case CastKind::CK_IntegralToBoolean: [[fallthrough]];
2334 case CastKind::CK_IntegralToPointer: [[fallthrough]];
2335 case CastKind::CK_PointerToIntegral: [[fallthrough]];
2336 case CastKind::CK_BitCast: [[fallthrough]];
2337 case CastKind::CK_UncheckedDerivedToBase: [[fallthrough]];
2338 case CastKind::CK_ToUnion:
2339 [[fallthrough]];
2340 // case CastKind::CK_UserDefinedConversion: [[fallthrough]];
2341 case CastKind::CK_AtomicToNonAtomic: [[fallthrough]];
2342 case CastKind::CK_DerivedToBase: [[fallthrough]];
2343 case CastKind::CK_FloatingCast: [[fallthrough]];
2344 case CastKind::CK_IntegralToFloating: [[fallthrough]];
2345 case CastKind::CK_FloatingToIntegral: [[fallthrough]];
2346 case CastKind::CK_NonAtomicToAtomic: return true;
2347 default:
2348 // Special case for structured bindings
2349 if((showXValueCasts or not hideImplicitCasts) and (CastKind::CK_NoOp == kind)) {
2350 return true;
2351 }
2352
2353 // Show this casts only if ShowAllImplicitCasts is turned on.
2354 if(not hideImplicitCasts) {
2355 switch(kind) {
2356 case CastKind::CK_NullToPointer: [[fallthrough]];
2357 case CastKind::CK_NullToMemberPointer: [[fallthrough]];
2358 /* these are implicit conversions. We get them right, but they may end up in a compiler
2359 * internal type, which leads to compiler errors */
2360 case CastKind::CK_NoOp: [[fallthrough]];
2361 case CastKind::CK_ArrayToPointerDecay: return true;
2362 default: break;
2363 }
2364 }
2365
2366 return false;
2367 }
2368 }(castKind, hideImplicitCasts, stmt->isXValue() or ShowXValueCasts());
2369
2370 if(not isMatchingCast) {
2371 if(GetInsightsOptions().UseShow2C and (castKind == CastKind::CK_LValueToRValue) and
2372 IsReferenceType(dyn_cast_or_null<DeclRefExpr>(subExpr))) {
2374 }
2375
2376 InsertArg(subExpr);
2377
2378 } else if(isa<IntegerLiteral>(subExpr) and hideImplicitCasts) {
2379 InsertArg(stmt->IgnoreCasts());
2380
2381 // If this is part of an explicit cast, for example a CStyleCast or static_cast, ignore it, because it
2382 // belongs to the cast written by the user.
2383 } else if(stmt->isPartOfExplicitCast()) {
2384 // For a CStyleCast we get an AST like this:
2385 //
2386 // CStyleCastExpr 0x13205cdc0 'uint32_t':'unsigned int' <NoOp>
2387 // `-ImplicitCastExpr 0x13205cda8 'uint32_t':'unsigned int' <IntegralCast> part_of_explicit_cast
2388 // `-CStyleCastExpr 0x13205cd70 'uint16_t':'unsigned short' <NoOp>
2389 //
2390 // Without filtering the `uint32_t` cast appears twice. The code below takes that into account and skips the
2391 // `ImplicitCastExpr` if the sub-expression is a `CStyleCastExpr`.
2392 if(isa<CStyleCastExpr>(subExpr) or isa<CXXNamedCastExpr>(subExpr)) {
2393 InsertArg(subExpr);
2394 } else {
2395 InsertArg(stmt->IgnoreCasts());
2396 }
2397
2398 } else {
2399 auto castName{GetCastName(castKind)};
2400 const QualType castDestType{[&] {
2401 const auto type{stmt->getType()};
2402
2403 // In at least the case a structured bindings the compiler adds xvalue casts but the && is missing to
2404 // make it valid C++.
2405 if(VK_XValue == stmt->getValueKind()) {
2406 return GetGlobalAST().getRValueReferenceType(type.getCanonicalType());
2407 } else if(type->isDependentType()) { // In case of a dependent type the canonical type doesn't know the
2408 // parameters name.
2409 return type;
2410 }
2411
2412 return type.getCanonicalType();
2413 }()};
2414
2415 FormatCast(castName, castDestType, subExpr, castKind);
2416 }
2417}
2418//-----------------------------------------------------------------------------
2419
2420void CodeGenerator::InsertArg(const DeclRefExpr* stmt)
2421{
2422 if(const auto* tmplObjParam = dyn_cast_or_null<TemplateParamObjectDecl>(stmt->getDecl())) {
2423 mOutputFormatHelper.Append(GetName(*tmplObjParam));
2424
2425 } else if(const auto* vd = dyn_cast_or_null<VarDecl>(stmt->getDecl());
2426 GetInsightsOptions().UseShow2C and IsReferenceType(vd)) {
2427 const auto* init = vd->getInit();
2428
2429 if(const auto* dref = dyn_cast_or_null<DeclRefExpr>(init)) {
2431 return;
2432
2433 } else if(const auto* inList = dyn_cast_or_null<InitListExpr>(init)) {
2434 mOutputFormatHelper.Append(GetName(*dyn_cast_or_null<DeclRefExpr>(inList->getInit(0))));
2435 return;
2436 }
2437 }
2438
2439 if(const auto* ctx = stmt->getDecl()->getDeclContext(); not ctx->isFunctionOrMethod() and
2440 not isa<NonTypeTemplateParmDecl>(stmt->getDecl()) and
2441 not GetInsightsOptions().UseShow2C) {
2442 if(const auto* qualifier = stmt->getQualifier();
2443 qualifier and (qualifier->getKind() == NestedNameSpecifier::SpecifierKind::Global)) {
2444 // According to
2445 // https://clang.llvm.org/doxygen/classclang_1_1NestedNameSpecifier.html#ac707a113605ed4283684b8c05664eb6f
2446 // the global specifier is not stored.
2447 mOutputFormatHelper.Append("::"sv, GetPlainName(*stmt));
2448
2449 } else {
2450 OutputFormatHelper ofm{};
2451 CodeGeneratorVariant codeGenerator{ofm};
2452
2453 codeGenerator->ParseDeclContext(ctx);
2454
2456 }
2457
2458 } else {
2460 }
2461
2462 if(const auto* varTmplSpecDecl = dyn_cast_or_null<VarTemplateSpecializationDecl>(stmt->getDecl())) {
2463 InsertTemplateArgs(*varTmplSpecDecl);
2464 } else {
2465 InsertTemplateArgs(*stmt);
2466 }
2467}
2468//-----------------------------------------------------------------------------
2469
2470void CodeGenerator::InsertArg(const CompoundStmt* stmt)
2471{
2473 mLifeTimeTracker.StartScope(isa_and_nonnull<FunctionDecl>(mLastDecl));
2474
2475 // prevent nested CompoundStmt's to insert a return on each leave. Only insert it before closing the most outer
2476 // one.
2477 const bool requiresImplicitReturnZero{std::exchange(mRequiresImplicitReturnZero, false)};
2478
2479 HandleCompoundStmt(stmt);
2480
2481 if(requiresImplicitReturnZero) {
2482 InsertArg(Return(Int32(0)));
2483
2484 if(not mSkipSemi) {
2486 }
2487 }
2488
2489 mSkipSemi = mLifeTimeTracker.EndScope(mOutputFormatHelper, isa_and_nonnull<ReturnStmt>(mLastStmt));
2490
2491 mOutputFormatHelper.CloseScope(OutputFormatHelper::NoNewLineBefore::Yes);
2492}
2493//-----------------------------------------------------------------------------
2494
2495template<typename... Args>
2496static bool IsStmtRequiringSemi(const Stmt* stmt)
2497{
2498 return (... and not isa<Args>(stmt));
2499}
2500//-----------------------------------------------------------------------------
2501
2502void CodeGenerator::HandleCompoundStmt(const CompoundStmt* stmt)
2503{
2504 for(const auto* item : stmt->body()) {
2505 InsertArg(item);
2506
2507 // Skip inserting a semicolon, if this is a LambdaExpr and out stack is empty. This addresses a special case
2508 // #344.
2509 const bool skipSemiForLambda{mLambdaStack.empty() and isa<LambdaExpr>(item)};
2510
2511 if(IsStmtRequiringSemi<IfStmt,
2512 NullStmt,
2513 ForStmt,
2514 DeclStmt,
2515 WhileStmt,
2516 DoStmt,
2517 CXXForRangeStmt,
2518 SwitchStmt,
2519 CXXTryStmt,
2520 CppInsightsCommentStmt>(item) and
2521 InsertSemi() and not skipSemiForLambda and not mSkipSemi) {
2523 }
2524
2525 mSkipSemi = false;
2526 }
2527}
2528//-----------------------------------------------------------------------------
2529
2531{
2532 if(const auto* conditionVar = stmt->getConditionVariable()) {
2533 InsertArg(conditionVar);
2534 }
2535
2536 if(const auto* init = stmt->getInit()) {
2537 InsertArg(init);
2538
2539 if(not isa<DeclStmt>(init)) {
2541 }
2542 }
2543}
2544//-----------------------------------------------------------------------------
2545
2546void CodeGenerator::InsertArg(const IfStmt* stmt)
2547{
2548 const bool hasInit{stmt->getInit() or stmt->getConditionVariable()};
2549
2550 if(hasInit) {
2552
2554 }
2555
2556 mOutputFormatHelper.Append("if"sv, ValueOrDefault(stmt->isConstexpr(), kwSpaceConstExpr));
2557
2559 not stmt->isConsteval(),
2560 [&]() {
2561 mShowConstantExprValue = ShowConstantExprValue::Yes;
2562
2563 InsertArg(stmt->getCond());
2564
2565 mShowConstantExprValue = ShowConstantExprValue::No;
2566 },
2567 AddSpaceAtTheEnd::Yes);
2568
2569 mOutputFormatHelper.Append(ValueOrDefault(stmt->isNegatedConsteval(), " !"sv),
2570 ValueOrDefault(stmt->isConsteval(), kwSpaceConstEvalSpace));
2571
2572 WrapInCompoundIfNeeded(stmt->getThen(), AddNewLineAfter::No);
2573
2574 // else
2575 if(const auto* elsePart = stmt->getElse()) {
2577 "else "sv,
2579
2580 WrapInCompoundIfNeeded(elsePart, AddNewLineAfter::No);
2581 }
2582
2583 // Add newline after last closing curly (either from if or else if).
2585
2586 if(hasInit) {
2589 }
2590
2591 // one blank line after statement
2593}
2594//-----------------------------------------------------------------------------
2595
2596class ContinueASTTransformer : public StmtVisitor<ContinueASTTransformer>
2597{
2598 Stmt* mPrevStmt{};
2599 std::string_view mContinueLabel{};
2600
2601public:
2602 bool found{};
2603
2604 ContinueASTTransformer(Stmt* stmt, std::string_view continueLabel)
2605 : mPrevStmt{stmt}
2606 , mContinueLabel{continueLabel}
2607 {
2608 Visit(stmt);
2609 }
2610
2611 void Visit(Stmt* stmt)
2612 {
2613 if(stmt) {
2615 }
2616 }
2617
2618 void VisitContinueStmt(ContinueStmt* stmt)
2619 {
2620 found = true;
2621
2622 ReplaceNode(mPrevStmt, stmt, Goto(mContinueLabel));
2623 }
2624
2625 void VisitStmt(Stmt* stmt)
2626 {
2627 auto* tmp = mPrevStmt;
2628 mPrevStmt = stmt;
2629
2630 for(auto* child : stmt->children()) {
2631 Visit(child);
2632 }
2633
2634 mPrevStmt = tmp;
2635 }
2636};
2637//-----------------------------------------------------------------------------
2638
2639void CodeGenerator::InsertArg(const ForStmt* stmt)
2640{
2641 // https://github.com/vtjnash/clang-ast-builder/blob/master/AstBuilder.cpp
2642 // http://clang-developers.42468.n3.nabble.com/Adding-nodes-to-Clang-s-AST-td4054800.html
2643 // https://stackoverflow.com/questions/30451485/how-to-clone-or-create-an-ast-stmt-node-of-clang/38899615
2644
2645 if(GetInsightsOptions().UseAltForSyntax) {
2646 auto* rwStmt = const_cast<ForStmt*>(stmt);
2647 const auto& ctx = GetGlobalAST();
2648 StmtsContainer bodyStmts{};
2649
2650 auto continueLabel = MakeLineColumnName(ctx.getSourceManager(), stmt->getBeginLoc(), "__continue_"sv);
2651 const bool insertLabel = ContinueASTTransformer{rwStmt->getBody(), continueLabel}.found;
2652
2653 bodyStmts.AddBodyStmts(rwStmt->getBody());
2654
2655 // Build and insert the continue goto label
2656 if(insertLabel) {
2657 bodyStmts.Add(Label(continueLabel));
2658 }
2659
2660 bodyStmts.Add(rwStmt->getInc());
2661
2662 auto* condition = [&]() -> Expr* {
2663 if(rwStmt->getCond()) {
2664 return rwStmt->getCond();
2665 }
2666
2667 return Bool(true);
2668 }();
2669
2670 auto* outerBody = mkCompoundStmt(bodyStmts, stmt->getBeginLoc(), stmt->getEndLoc());
2671 auto* whileStmt = WhileStmt::Create(
2672 ctx, nullptr, condition, outerBody, stmt->getBeginLoc(), stmt->getLParenLoc(), stmt->getRParenLoc());
2673
2674 StmtsContainer outerScopeStmts{};
2675 outerScopeStmts.Add(rwStmt->getInit());
2676 outerScopeStmts.Add(whileStmt);
2677
2678 auto* outerScopeBody = mkCompoundStmt(outerScopeStmts, stmt->getBeginLoc(), stmt->getEndLoc());
2679
2680 InsertArg(outerScopeBody);
2682
2683 } else {
2684 {
2685 // We need to handle the case that a lambda is used in the init-statement of the for-loop.
2687
2688 mOutputFormatHelper.Append("for"sv);
2689
2691 [&]() {
2692 if(const auto* init = stmt->getInit()) {
2693 MultiStmtDeclCodeGenerator codeGenerator{
2694 mOutputFormatHelper, mLambdaStack, InsertVarDecl(nullptr)};
2695 codeGenerator.InsertArg(init);
2696
2697 } else {
2699 }
2700
2701 InsertArg(stmt->getCond());
2703
2704 InsertArg(stmt->getInc());
2705 },
2706 AddSpaceAtTheEnd::Yes);
2707 }
2708
2709 WrapInCompoundIfNeeded(stmt->getBody(), AddNewLineAfter::Yes);
2710 }
2711
2712 mOutputFormatHelper.AppendNewLine();
2713}
2714//-----------------------------------------------------------------------------
2715
2716static bool IsConstQualifiedType(QualType type)
2717{
2718 if(not type.isNull()) {
2719 if(auto* typePtr = type.getTypePtrOrNull()) {
2720 if(auto pointee = typePtr->getPointeeType(); not pointee.isNull()) {
2721 return pointee.isConstQualified();
2722 }
2723 }
2724 }
2725
2726 return false;
2727}
2728//-----------------------------------------------------------------------------
2729
2730void CodeGenerator::InsertArg(const CStyleCastExpr* stmt)
2731{
2732 const auto castKind = stmt->getCastKind();
2733 const QualType castDestType = stmt->getType().getCanonicalType();
2734 const auto castName = GetCastName(
2735 castKind, IsConstQualifiedType(castDestType) != IsConstQualifiedType(stmt->getSubExpr()->getType()));
2736
2737 FormatCast(castName, castDestType, stmt->getSubExpr(), castKind);
2738}
2739//-----------------------------------------------------------------------------
2740
2741void CodeGenerator::InsertArg(const CXXNewExpr* stmt)
2742{
2743 const auto noEmptyInitList = mNoEmptyInitList;
2744 FinalAction _{[&] { mNoEmptyInitList = noEmptyInitList; }};
2745 mNoEmptyInitList = GetInsightsOptions().UseShow2C ? NoEmptyInitList::Yes : NoEmptyInitList::No;
2746
2747 mOutputFormatHelper.Append("new "sv);
2748
2749 if(stmt->getNumPlacementArgs()) {
2750 /* we have a placement new */
2751
2752 WrapInParens([&]() {
2753 ForEachArg(stmt->placement_arguments(), [&](const auto& placementArg) { InsertArg(placementArg); });
2754 });
2755 }
2756
2757 if(const auto* ctorExpr = stmt->getConstructExpr()) {
2758 InsertArg(ctorExpr);
2759
2760 } else {
2761 auto name = GetName(stmt->getAllocatedType());
2762
2763 // Special handling for arrays. They differ from one to more dimensions.
2764 if(stmt->isArray()) {
2765 OutputFormatHelper ofm{};
2766 CodeGeneratorVariant codeGenerator{ofm};
2767
2768 ofm.Append('[');
2769 codeGenerator->InsertArg(stmt->getArraySize().value());
2770 ofm.Append(']');
2771
2772 // In case of multi dimension the first dimension is the getArraySize() while the others are part of the
2773 // type included in GetName(...).
2774 if(Contains(name, "["sv)) {
2775 InsertBefore(name, "["sv, ofm.GetString());
2776 } else {
2777 // here we have the single dimension case, the dimension is not part of GetName, so add it.
2778 name.append(ofm);
2779 }
2780 }
2781
2783
2784 if(stmt->hasInitializer()) {
2785 InsertCurlysIfRequired(stmt->getInitializer());
2786 }
2787 }
2788}
2789//-----------------------------------------------------------------------------
2790
2791void CodeGenerator::InsertArg(const MaterializeTemporaryExpr* stmt)
2792{
2793 // At least in case of a ternary operator wrapped inside a MaterializeTemporaryExpr parens are necessary
2794 const auto* temporary = stmt->getSubExpr();
2795 WrapInParensIfNeeded(isa_and_nonnull<ConditionalOperator>(temporary), [&] { InsertArg(temporary); });
2796}
2797//-----------------------------------------------------------------------------
2798
2799void CodeGenerator::InsertArg(const CXXOperatorCallExpr* stmt)
2800{
2802
2803 const auto* callee = dyn_cast_or_null<DeclRefExpr>(stmt->getCallee()->IgnoreImpCasts());
2804 const bool isCXXMethod{callee and isa<CXXMethodDecl>(callee->getDecl())};
2805
2806 if(2 == stmt->getNumArgs()) {
2807 auto getArg = [&](unsigned idx) {
2808 const auto* arg = stmt->getArg(idx);
2809
2810 // In show all casts mode don't filter this. It shows how the compiler adds const to arguments, if the
2811 // argument is non-const but the parameter demands a const object
2812 if(not GetInsightsOptions().ShowAllImplicitCasts) {
2813 arg = arg->IgnoreImpCasts();
2814 }
2815
2816 return dyn_cast_or_null<DeclRefExpr>(arg);
2817 };
2818
2819 const auto* param1 = getArg(0);
2820 const auto* param2 = getArg(1);
2821
2822 if(callee and param1 and param2) {
2823 const std::string replace = [&]() {
2824 // If the argument is a variable template, add the template arguments to the parameter name.
2825 auto nameWithTmplArguments = [](const auto param) {
2826 return FormatVarTemplateSpecializationDecl(param->getDecl(), GetName(*param));
2827 };
2828
2829 if(isa<CXXMethodDecl>(callee->getDecl())) {
2830 return StrCat(nameWithTmplArguments(param1),
2831 "."sv,
2832 GetName(*callee),
2833 "("sv,
2834 nameWithTmplArguments(param2),
2835 ")"sv);
2836 } else {
2837 return StrCat(GetName(*callee),
2838 "("sv,
2839 nameWithTmplArguments(param1),
2840 ", "sv,
2841 nameWithTmplArguments(param2),
2842 ")"sv);
2843 }
2844 }();
2845
2846 mOutputFormatHelper.Append(replace);
2847
2848 return;
2849 }
2850 }
2851
2852 auto cb = stmt->child_begin();
2853 const auto* fallbackArg0 = stmt->getArg(0);
2854
2855 // arg0 := operator
2856 // skip arg0
2857 std::advance(cb, 1);
2858
2859 const auto* arg1 = *cb;
2860
2861 std::advance(cb, 1);
2862
2863 // operators in a namespace but outside a class so operator goes first
2864 if(not isCXXMethod) {
2865 // happens for UnresolvedLooupExpr
2866 if(not callee) {
2867 if(const auto* adl = dyn_cast_or_null<UnresolvedLookupExpr>(stmt->getCallee())) {
2868 InsertArg(adl);
2869 }
2870 } else {
2872 }
2873
2875 }
2876
2877 // insert the arguments
2878 if(isa<DeclRefExpr>(fallbackArg0)) {
2879 InsertArgWithParensIfNeeded(fallbackArg0);
2880
2881 } else {
2883 }
2884
2885 // if it is a class operator the operator follows now
2886 if(isCXXMethod) {
2887 const OverloadedOperatorKind opKind = stmt->getOperator();
2888
2889 const std::string_view operatorKw{ValueOr((OO_Coawait == opKind), kwOperatorSpace, kwOperator)};
2890
2891 mOutputFormatHelper.Append("."sv, operatorKw, getOperatorSpelling(opKind), "("sv);
2892 }
2893
2894 // consume all remaining arguments
2895 const auto childRange = llvm::make_range(cb, stmt->child_end());
2896
2897 // at least the call-operator can have more than 2 parameters
2898 ForEachArg(childRange, [&](const auto& child) {
2899 if(not isCXXMethod) {
2900 // in global operators we need to separate the two parameters by comma
2902 }
2903
2904 InsertArg(child);
2905 });
2906
2908}
2909//-----------------------------------------------------------------------------
2910
2911void CodeGenerator::InsertArg(const LambdaExpr* stmt)
2912{
2913 if(not mLambdaStack.empty()) {
2914 const bool insertName{mLambdaStack.back().insertName()};
2915
2917
2918 if(insertName) {
2920 }
2921
2922 } else if(LambdaInInitCapture::Yes == mLambdaInitCapture) {
2925 } else {
2928 }
2929
2930 if(not mLambdaStack.empty()) {
2932 }
2933}
2934//-----------------------------------------------------------------------------
2935
2936void CodeGenerator::InsertArg(const CXXThisExpr*)
2937{
2939}
2940//-----------------------------------------------------------------------------
2941
2942void CodeGenerator::InsertArg(const CXXBindTemporaryExpr* stmt)
2943{
2944 InsertArg(stmt->getSubExpr());
2945}
2946//-----------------------------------------------------------------------------
2947
2948void CodeGenerator::InsertArg(const CXXFunctionalCastExpr* stmt)
2949{
2950 const bool isConstructor{isa<CXXConstructExpr>(stmt->getSubExpr())};
2951 const bool isStdListInit{isa<CXXStdInitializerListExpr>(stmt->getSubExpr())};
2952 const bool isListInitialization{stmt->getLParenLoc().isInvalid()};
2953 const bool needsParens{not isConstructor and not isListInitialization and not isStdListInit};
2954
2955 // If a constructor follows we do not need to insert the type name. This would insert it twice.
2956 if(not isConstructor and not isStdListInit) {
2957 mOutputFormatHelper.Append(GetName(stmt->getTypeAsWritten()));
2958 }
2959
2960 WrapInParensIfNeeded(needsParens, [&] { InsertArg(stmt->getSubExpr()); });
2961}
2962//-----------------------------------------------------------------------------
2963
2964void CodeGenerator::InsertArg(const CXXBoolLiteralExpr* stmt)
2965{
2967}
2968//-----------------------------------------------------------------------------
2969
2970void CodeGenerator::InsertArg(const GNUNullExpr* /*stmt*/)
2971{
2973}
2974//-----------------------------------------------------------------------------
2975
2976void CodeGenerator::InsertArg(const CharacterLiteral* stmt)
2977{
2978 StringStream stream{};
2979 stream.Print(*stmt);
2980
2981 auto str = std::move(stream.str());
2982
2983 if(str == "'\\x00'"sv) {
2984 str = "'\\0'"sv;
2985 } else if(str == "'\\xff'"sv) {
2986 str = "255"sv;
2987 }
2988
2990}
2991//-----------------------------------------------------------------------------
2992
2993void CodeGenerator::InsertArg(const PredefinedExpr* stmt)
2994{
2995 // Check if getFunctionName returns a valid StringLiteral. It does return a nullptr, if this PredefinedExpr is
2996 // in a UnresolvedLookupExpr. In that case, print the identifier, e.g. __func__.
2997 if(const auto* functionName = stmt->getFunctionName()) {
2998 InsertArg(functionName);
2999 } else {
3000 const auto name = PredefinedExpr::getIdentKindName(stmt->getIdentKind());
3001
3003 }
3004}
3005//-----------------------------------------------------------------------------
3006
3007void CodeGenerator::InsertArg(const ExprWithCleanups* stmt)
3008{
3010 TemporaryDeclFinder temporaryFinder{*this, not mProcessingVarDecl ? stmt->getSubExpr() : nullptr};
3011
3012 InsertArg(stmt->getSubExpr());
3013
3014 if(GetInsightsOptions().ShowLifetime and not mProcessingVarDecl) {
3016 }
3017
3019}
3020//-----------------------------------------------------------------------------
3021
3022std::string CodeGenerator::GetValueOfValueInit(const QualType& t)
3023{
3024 const QualType& type = t.getCanonicalType();
3025
3026 if(type->isScalarType()) {
3027 switch(type->getScalarTypeKind()) {
3028 case Type::STK_CPointer:
3029 case Type::STK_BlockPointer:
3030 case Type::STK_ObjCObjectPointer:
3031 case Type::STK_MemberPointer: return std::string{kwNullptr};
3032
3033 case Type::STK_Bool: return std::string{kwFalse};
3034
3035 case Type::STK_Integral:
3036 case Type::STK_Floating:
3037 if(const auto* bt = type->getAs<BuiltinType>()) {
3038 switch(bt->getKind()) {
3039 // Type::STK_Integral
3040 case BuiltinType::Char_U:
3041 case BuiltinType::UChar:
3042 case BuiltinType::Char_S:
3043 case BuiltinType::SChar: return "'\\0'";
3044 case BuiltinType::WChar_U:
3045 case BuiltinType::WChar_S: return "L'\\0'";
3046 case BuiltinType::Char16: return "u'\\0'";
3047 case BuiltinType::Char32: return "U'\\0'";
3048 // Type::STK_Floating
3049 case BuiltinType::Half:
3050 case BuiltinType::Float: return "0.0f";
3051 case BuiltinType::Double: return "0.0";
3052 default: break;
3053 }
3054 }
3055
3056 break;
3057
3058 case Type::STK_FloatingComplex:
3059 case Type::STK_IntegralComplex:
3060 if(const auto* complexType = type->getAs<ComplexType>()) {
3061 return GetValueOfValueInit(complexType->getElementType());
3062 }
3063
3064 break;
3065
3066 case Type::STK_FixedPoint: Error("STK_FixedPoint is not implemented"); break;
3067 }
3068
3069 } else if(const auto* tt = dyn_cast_or_null<ConstantArrayType>(t.getTypePtrOrNull())) {
3070 const auto& elementType{tt->getElementType()};
3071 const std::string elementTypeInitValue{GetValueOfValueInit(elementType)};
3072
3073 return FillConstantArray(tt, elementTypeInitValue, uint64_t{0});
3074 }
3075
3076 return std::string{"0"sv};
3077}
3078//-----------------------------------------------------------------------------
3079
3080void CodeGenerator::InsertArg(const ImplicitValueInitExpr* stmt)
3081{
3083}
3084//-----------------------------------------------------------------------------
3085
3086void CodeGenerator::InsertArg(const CXXScalarValueInitExpr* stmt)
3087{
3088 mOutputFormatHelper.Append(GetName(stmt->getType()), "()"sv);
3089}
3090//-----------------------------------------------------------------------------
3091
3092void CodeGenerator::InsertArg(const CXXTryStmt* stmt)
3093{
3095
3096 InsertArg(stmt->getTryBlock());
3097
3098 for(const auto& i : NumberIterator{stmt->getNumHandlers()}) {
3099 InsertArg(stmt->getHandler(i));
3100 }
3101
3103}
3104//-----------------------------------------------------------------------------
3105
3106void CodeGenerator::InsertArg(const CXXCatchStmt* stmt)
3107{
3108 mOutputFormatHelper.Append(" catch"sv);
3109
3111 [&]() {
3112 if(not stmt->getCaughtType().isNull()) {
3113 mOutputFormatHelper.Append(
3114 GetTypeNameAsParameter(stmt->getCaughtType(), stmt->getExceptionDecl()->getName()));
3115 } else {
3116 mOutputFormatHelper.Append(kwElipsis);
3117 }
3118 },
3119 AddSpaceAtTheEnd::Yes);
3120
3121 InsertArg(stmt->getHandlerBlock());
3122}
3123//-----------------------------------------------------------------------------
3124
3125void CodeGenerator::InsertArg(const CXXThrowExpr* stmt)
3126{
3127 mOutputFormatHelper.Append("throw "sv);
3128
3129 InsertArg(stmt->getSubExpr());
3130}
3131//-----------------------------------------------------------------------------
3132
3133void CodeGenerator::InsertArg(const ConstantExpr* stmt)
3134{
3135 if((ShowConstantExprValue::Yes == mShowConstantExprValue) and stmt->hasAPValueResult()) {
3136 if(const auto value = stmt->getAPValueResult(); value.isInt()) {
3137 mOutputFormatHelper.Append(value.getInt());
3138 return;
3139 }
3140 }
3141
3142 InsertArg(stmt->getSubExpr());
3143}
3144//-----------------------------------------------------------------------------
3145
3146void CodeGenerator::InsertArg(const TypeAliasDecl* stmt)
3147{
3148 const auto& underlyingType = stmt->getUnderlyingType();
3149
3151 P0315Visitor dt{*this};
3152 dt.TraverseType(underlyingType);
3153
3155
3156 if(auto* templateSpecializationType = underlyingType->getAs<TemplateSpecializationType>()) {
3157 const bool carriesNamespace{[&] {
3158 if(const auto tn = templateSpecializationType->getTemplateName();
3159 (TemplateName::QualifiedTemplate == tn.getKind()) or (TemplateName::DependentTemplate == tn.getKind())) {
3160 const auto* qtn = tn.getAsQualifiedTemplateName();
3161
3162 return qtn->getQualifier() != nullptr;
3163 }
3164
3165 return false;
3166 }()};
3167
3168 if(const auto* elaboratedType = underlyingType->getAs<ElaboratedType>()) {
3169 if(templateSpecializationType->isSugared() and not carriesNamespace) {
3170 // do this only if the templateSpecializationType does not carry a nestedns
3171 InsertNamespace(elaboratedType->getQualifier());
3172 }
3173 }
3174
3175 StringStream stream{};
3176 stream.Print(*templateSpecializationType);
3177
3178 mOutputFormatHelper.Append(stream.str());
3179
3180 InsertTemplateArgs(*templateSpecializationType);
3181 } else if(auto* dependentTemplateSpecializationType =
3182 underlyingType->getAs<DependentTemplateSpecializationType>()) {
3183
3184 mOutputFormatHelper.Append(GetElaboratedTypeKeyword(dependentTemplateSpecializationType->getKeyword()));
3185
3186 InsertNamespace(dependentTemplateSpecializationType->getQualifier());
3187
3188 mOutputFormatHelper.Append(kwTemplateSpace, dependentTemplateSpecializationType->getIdentifier()->getName());
3189
3190 InsertTemplateArgs(*dependentTemplateSpecializationType);
3191
3192 } else {
3193 mOutputFormatHelper.Append(GetName(underlyingType));
3194 }
3195
3197}
3198//-----------------------------------------------------------------------------
3199
3200void CodeGenerator::InsertArg(const TypedefDecl* stmt)
3201{
3202 /* function pointer typedefs are special. Ease up things using "using" */
3203 // outputFormatHelper.AppendNewLine("typedef ", GetName(stmt->getUnderlyingType()), " ", GetName(*stmt),
3204 // ";");
3205 mOutputFormatHelper.AppendSemiNewLine(kwUsingSpace, GetName(*stmt), hlpAssing, GetName(stmt->getUnderlyingType()));
3206}
3207//-----------------------------------------------------------------------------
3208
3209void CodeGenerator::InsertCXXMethodHeader(const CXXMethodDecl* stmt, OutputFormatHelper& initOutputFormatHelper)
3210{
3212 CXXConstructorDecl* cxxInheritedCtorDecl{nullptr};
3213
3214 // Traverse the ctor inline init statements first to find a potential CXXInheritedCtorInitExpr. This carries the
3215 // name and the type. The CXXMethodDecl above knows only the type.
3216 if(const auto* ctor = dyn_cast_or_null<CXXConstructorDecl>(stmt)) {
3217 CodeGeneratorVariant codeGenerator{initOutputFormatHelper, mLambdaStack, mProcessingPrimaryTemplate};
3218 codeGenerator->mCurrentVarDeclPos = mCurrentVarDeclPos;
3219 codeGenerator->mCurrentFieldPos = mCurrentFieldPos;
3222
3223 for(OnceTrue first{}; const auto* init : ctor->inits()) {
3224 initOutputFormatHelper.AppendNewLine();
3225 if(first) {
3226 initOutputFormatHelper.Append(": "sv);
3227 } else {
3228 initOutputFormatHelper.Append(", "sv);
3229 }
3230
3231 // in case of delegating or base initializer there is no member.
3232#if 0
3233 if(const auto* member = init->getMember()) {
3234 initOutputFormatHelper.Append(member->getName());
3235 codeGenerator->InsertCurlysIfRequired(init->getInit());
3236 } else {
3237 const auto* inlineInit = init->getInit();
3238 bool useCurlies{false};
3239
3240 if(const auto* cxxInheritedCtorInitExpr = dyn_cast_or_null<CXXInheritedCtorInitExpr>(inlineInit)) {
3241 cxxInheritedCtorDecl = cxxInheritedCtorInitExpr->getConstructor();
3242
3243 // Insert the base class name only, if it is not a CXXContructorExpr and not a
3244 // CXXDependentScopeMemberExpr which already carry the type.
3245 } else if(init->isBaseInitializer() and not isa<CXXConstructExpr>(inlineInit)) {
3246 initOutputFormatHelper.Append(GetUnqualifiedScopelessName(init->getBaseClass()));
3247 useCurlies = true;
3248 }
3249
3250 codeGenerator->WrapInCurliesIfNeeded(useCurlies, [&] { codeGenerator->InsertArg(inlineInit); });
3251 }
3252#else
3253 const auto* inlineInit = init->getInit();
3254
3255 // in case of delegating or base initializer there is no member.
3256 if(const auto* member = init->getMember()) {
3257 initOutputFormatHelper.Append(member->getName());
3258
3259 if(isa<ParenListExpr>(inlineInit)) {
3260 codeGenerator->WrapInParens([&] { codeGenerator->InsertArg(inlineInit); });
3261 } else {
3262 codeGenerator->InsertCurlysIfRequired(inlineInit);
3263 }
3264
3265 } else if(const auto* cxxInheritedCtorInitExpr = dyn_cast_or_null<CXXInheritedCtorInitExpr>(inlineInit)) {
3266 cxxInheritedCtorDecl = cxxInheritedCtorInitExpr->getConstructor();
3267
3268 codeGenerator->InsertArg(inlineInit);
3269
3270 // Insert the base class name only, if it is not a CXXContructorExpr and not a
3271 // CXXDependentScopeMemberExpr which already carry the type.
3272 } else if(init->isBaseInitializer() and not isa<CXXConstructExpr>(inlineInit)) {
3273 initOutputFormatHelper.Append(GetUnqualifiedScopelessName(init->getBaseClass()));
3274
3275 const auto braceKind = isa<ParenListExpr>(inlineInit) ? BraceKind::Parens : BraceKind::Curlys;
3276
3277 codeGenerator->WrapInParensOrCurlys(braceKind, [&] { codeGenerator->InsertArg(inlineInit); });
3278 } else {
3279 codeGenerator->InsertArg(inlineInit);
3280 }
3281#endif
3282 }
3283 }
3284
3286 InsertFunctionNameWithReturnType(*stmt, cxxInheritedCtorDecl);
3287}
3288//-----------------------------------------------------------------------------
3289
3290void CodeGenerator::InsertCXXMethodDecl(const CXXMethodDecl* stmt, SkipBody skipBody)
3291{
3292 OutputFormatHelper initOutputFormatHelper{};
3293 initOutputFormatHelper.SetIndent(mOutputFormatHelper, OutputFormatHelper::SkipIndenting::Yes);
3294
3295 const auto posBeforeFunc = mOutputFormatHelper.CurrentPos();
3296
3297 InsertCXXMethodHeader(stmt, initOutputFormatHelper);
3298
3299 if(not stmt->isUserProvided() or stmt->isExplicitlyDefaulted()) {
3301 return;
3302 }
3303
3304 mOutputFormatHelper.Append(initOutputFormatHelper);
3305
3306 if(isa<CXXConversionDecl>(stmt)) {
3307 if(stmt->getParent()->isLambda() and not stmt->doesThisDeclarationHaveABody()) {
3309 WrapInCurlys([&]() {
3311 mOutputFormatHelper.Append(" "sv, kwReturn, " "sv);
3312 if(const auto* invoker = stmt->getParent()->getLambdaStaticInvoker()) {
3313 mOutputFormatHelper.AppendSemiNewLine(invoker->getName());
3314 } else {
3315 mOutputFormatHelper.AppendSemiNewLine(kwOperator, "()"sv);
3316 }
3317 });
3318 }
3319 }
3320
3321 if((SkipBody::No == skipBody) and stmt->doesThisDeclarationHaveABody() and not stmt->isLambdaStaticInvoker()) {
3322 InsertMethodBody(stmt, posBeforeFunc);
3323
3324 } else if(not InsertLambdaStaticInvoker(stmt) or (SkipBody::Yes == skipBody)) {
3326 }
3327
3329
3330 if(SkipBody::No == skipBody) {
3332 }
3333}
3334//-----------------------------------------------------------------------------
3335
3336void CodeGenerator::InsertArg(const CXXMethodDecl* stmt)
3337{
3338 // As per [special]/1: "Programs shall not define implicitly-declared special member functions." hide special
3339 // members which are not used and with that not fully evaluated. This also hopefully removes confusion about the
3340 // noexcept, which is not evaluated, if the special member is not used.
3341 RETURN_IF(not GetInsightsOptions().UseShow2C and not stmt->hasBody() and not stmt->isUserProvided() and
3342 not stmt->isExplicitlyDefaulted() and not stmt->isDeleted());
3343
3344 InsertCXXMethodDecl(stmt, SkipBody::No);
3345}
3346//-----------------------------------------------------------------------------
3347
3348void CodeGenerator::InsertArg(const EnumDecl* stmt)
3349{
3351
3352 if(stmt->isScoped()) {
3353 if(stmt->isScopedUsingClassTag()) {
3355 } else {
3357 }
3358 }
3359
3360 mOutputFormatHelper.Append(stmt->getName());
3361
3362 if(stmt->isFixed()) {
3363 mOutputFormatHelper.Append(" : "sv, GetName(stmt->getIntegerType()));
3364 }
3365
3367
3369 [&]() {
3372 OnceFalse needsComma{};
3373
3374 ForEachArg(stmt->enumerators(), [&](const auto* value) {
3375 if(needsComma) {
3376 mOutputFormatHelper.AppendNewLine();
3377 }
3378
3379 InsertArg(value);
3380 });
3381
3382 InsertArg(stmt->getBody());
3383
3386 },
3387 AddSpaceAtTheEnd::No);
3388
3389 mOutputFormatHelper.AppendSemiNewLine();
3390 mOutputFormatHelper.AppendNewLine();
3391}
3392//-----------------------------------------------------------------------------
3393
3394void CodeGenerator::InsertArg(const EnumConstantDecl* stmt)
3395{
3396 mOutputFormatHelper.Append(stmt->getName());
3397
3398 InsertAttributes(stmt);
3399
3400 if(const auto* initExpr = stmt->getInitExpr()) {
3402
3403 InsertArg(initExpr);
3404 }
3405}
3406//-----------------------------------------------------------------------------
3407
3408static auto& GetRecordLayout(const RecordDecl* recordDecl)
3409{
3410 return GetGlobalAST().getASTRecordLayout(recordDecl);
3411}
3412//-----------------------------------------------------------------------------
3413
3414// XXX: replace with std::format once it is available in all std-libs
3415auto GetSpaces(std::string::size_type offset)
3416{
3417 static const std::string_view spaces{" "sv};
3418
3419 if(offset >= spaces.size()) {
3420 return ""sv;
3421 } else {
3422 return spaces.substr(0, spaces.size() - offset);
3423 }
3424}
3425//-----------------------------------------------------------------------------
3426
3427void CodeGenerator::InsertArg(const FieldDecl* stmt)
3428{
3430 P0315Visitor dt{*this};
3431
3432 auto type = GetType(stmt->getType());
3433 dt.TraverseType(type);
3434
3435 const auto initialSize{mOutputFormatHelper.size()};
3436 InsertAttributes(stmt->attrs());
3437
3438 if(stmt->isMutable()) {
3440 }
3441
3442 if(const auto* cxxRecordDecl = dyn_cast_or_null<CXXRecordDecl>(stmt->getParent())) {
3443 std::string name{GetName(*stmt)};
3444
3445 if(const auto fieldName = GetFieldDeclNameForLambda(*stmt, *cxxRecordDecl)) {
3446 name = std::move(fieldName.value());
3447 }
3448
3450
3451 if(const auto* constantExpr = dyn_cast_or_null<ConstantExpr>(stmt->getBitWidth())) {
3453 InsertArg(constantExpr);
3454 }
3455
3456 // Keep the inline init for aggregates, as we do not see it somewhere else.
3457 if(const auto* initializer = stmt->getInClassInitializer();
3458 stmt->hasInClassInitializer() and initializer and cxxRecordDecl->isAggregate()) {
3459 const bool isConstructorExpr{isa<CXXConstructExpr>(initializer) or isa<ExprWithCleanups>(initializer)};
3460 if((ICIS_ListInit != stmt->getInClassInitStyle()) or isConstructorExpr) {
3462 }
3463
3464 InsertArg(initializer);
3465 }
3466 }
3467
3469
3470 if(GetInsightsOptions().UseShowPadding) {
3471 const auto* fieldClass = stmt->getParent();
3472 const auto& recordLayout = GetRecordLayout(fieldClass);
3473 auto effectiveFieldSize{GetGlobalAST().getTypeInfoInChars(type).Width.getQuantity()};
3474 auto getFieldOffsetInBytes = [&recordLayout](const FieldDecl* field) {
3475 return recordLayout.getFieldOffset(field->getFieldIndex()) / 8; // this is in bits
3476 };
3477 auto fieldOffset = getFieldOffsetInBytes(stmt);
3478 const auto offset = mOutputFormatHelper.size() - initialSize;
3479
3480 mOutputFormatHelper.Append(GetSpaces(offset), " /* offset: "sv, fieldOffset, ", size: "sv, effectiveFieldSize);
3481
3482 // - get next field
3483 // - if this fields offset + size is equal to the next fields offset we are good,
3484 // - if not we insert padding bytes
3485 // - in case there is no next field this is the last field, check this field's offset + size against the
3486 // records
3487 // size. If unequal padding is needed
3488
3489 const auto expectedOffset = fieldOffset + effectiveFieldSize;
3490 const auto nextOffset = [&]() -> uint64_t {
3491 // find previous field
3492 if(const auto next = stmt->getFieldIndex() + 1; recordLayout.getFieldCount() > next) {
3493 // We are in bounds, means we expect to get back a valid iterator
3494 const auto* field = *std::next(fieldClass->fields().begin(), next);
3495
3496 return getFieldOffsetInBytes(field);
3497 }
3498
3499 // no field found means we are the last field
3500 return recordLayout.getSize().getQuantity();
3501 }();
3502
3503 if(expectedOffset < nextOffset) {
3504 const auto padding = nextOffset - expectedOffset;
3506 std::string s = StrCat("char "sv, BuildInternalVarName("padding"sv), "["sv, padding, "];"sv);
3507 mOutputFormatHelper.Append(s, GetSpaces(s.length()), " size: ", padding);
3508 }
3509
3511
3512 } else {
3514 }
3515}
3516//-----------------------------------------------------------------------------
3517
3518void CodeGenerator::InsertArg(const AccessSpecDecl* stmt)
3519{
3522}
3523//-----------------------------------------------------------------------------
3524
3525void CodeGenerator::InsertArg(const StaticAssertDecl* stmt)
3526{
3528
3529 if(not stmt->isFailed()) {
3530 mOutputFormatHelper.Append("/* PASSED: "sv);
3531 } else {
3532 mOutputFormatHelper.Append("/* FAILED: "sv);
3533 }
3534
3536
3537 WrapInParens([&] {
3538 BackupAndRestore _{GetInsightsOptionsRW().ShowLifetime, false};
3539 InsertArg(stmt->getAssertExpr());
3540
3541 if(stmt->getMessage()) {
3543 InsertArg(stmt->getMessage());
3544 }
3545 });
3546
3548}
3549//-----------------------------------------------------------------------------
3550
3551void CodeGenerator::InsertArg(const UsingDirectiveDecl* stmt)
3552{
3553 // We need this due to a wired case in UsingDeclTest.cpp
3554 if(const auto& name = GetName(*stmt->getNominatedNamespace()); not name.empty()) {
3556 }
3557}
3558//-----------------------------------------------------------------------------
3559
3560void CodeGenerator::InsertArg(const NamespaceDecl* stmt)
3561{
3562 SCOPE_HELPER(stmt);
3563
3564 if(stmt->isInline()) {
3566 }
3567
3569
3570 InsertAttributes(stmt);
3571
3572 if(not stmt->isAnonymousNamespace()) {
3573 mOutputFormatHelper.Append(" "sv, stmt->getName());
3574 }
3575
3577
3579
3580 for(const auto* decl : stmt->decls()) {
3581 InsertArg(decl);
3582 }
3583
3586}
3587//-----------------------------------------------------------------------------
3588
3589void CodeGenerator::ParseDeclContext(const DeclContext* ctx)
3590{
3592}
3593//-----------------------------------------------------------------------------
3594
3595void CodeGenerator::InsertArg(const UsingDecl* stmt)
3596{
3597 OutputFormatHelper ofm{};
3598 ofm.SetIndent(mOutputFormatHelper, OutputFormatHelper::SkipIndenting::Yes);
3599
3600 // Skip UsingDecl's which have ConstructorUsingShadowDecl attached. This means that we will create the
3601 // associated constructors from the base class later. Having this \c using still in the code prevents compiling
3602 // the transformed code.
3603 if(stmt->shadow_size()) {
3604 for(const auto* shadow : stmt->shadows()) {
3605 RETURN_IF(isa<ConstructorUsingShadowDecl>(shadow));
3606
3607 if(const auto* shadowUsing = dyn_cast_or_null<UsingShadowDecl>(shadow)) {
3608 if(const auto* targetDecl = shadowUsing->getTargetDecl(); not isa<TypeAliasDecl>(targetDecl)) {
3609 UsingCodeGenerator codeGenerator{ofm};
3610 codeGenerator.InsertArg(targetDecl);
3611 }
3612 }
3613 }
3614 }
3615
3617
3618 InsertQualifierAndName(stmt->getDeclName(), stmt->getQualifier(), false);
3619
3621
3622 // Insert what a using declaration pulled into this scope.
3623 if(not ofm.empty()) {
3625 }
3626}
3627//-----------------------------------------------------------------------------
3628
3629void CodeGenerator::InsertArg(const UnresolvedUsingValueDecl* stmt)
3630{
3632
3633 InsertQualifierAndName(stmt->getDeclName(), stmt->getQualifier(), false);
3634
3635 mOutputFormatHelper.AppendSemiNewLine(Ellipsis(stmt->isPackExpansion()));
3636}
3637//-----------------------------------------------------------------------------
3638
3639void CodeGenerator::InsertArg(const NamespaceAliasDecl* stmt)
3640{
3642 kwNamespaceSpace, stmt->getDeclName().getAsString(), hlpAssing, GetName(*stmt->getAliasedNamespace()), ";");
3643}
3644//-----------------------------------------------------------------------------
3645
3646void CodeGenerator::InsertArg(const FriendDecl* stmt)
3647{
3648 if(const auto* typeInfo = stmt->getFriendType()) {
3650
3651 } else if(const auto* fd = dyn_cast_or_null<FunctionDecl>(stmt->getFriendDecl())) {
3652 InsertArg(fd);
3653
3654 } else if(const auto* fdt = dyn_cast_or_null<FunctionTemplateDecl>(stmt->getFriendDecl())) {
3655 InsertArg(fdt);
3656
3657 } else {
3658 std::string cls{};
3659 if(const auto* ctd = dyn_cast_or_null<ClassTemplateDecl>(stmt->getFriendDecl())) {
3660 InsertTemplateParameters(*ctd->getTemplateParameters());
3661
3662 cls = GetTagDeclTypeName(*ctd->getTemplatedDecl());
3663 }
3664
3665 mOutputFormatHelper.AppendSemiNewLine(kwFriendSpace, cls, GetName(*stmt->getFriendDecl()));
3666 }
3667}
3668//-----------------------------------------------------------------------------
3669
3670void CodeGenerator::InsertArg(const CXXNoexceptExpr* stmt)
3671{
3673
3675}
3676//-----------------------------------------------------------------------------
3677
3678void CodeGenerator::InsertArg(const CXXDeductionGuideDecl* stmt)
3679{
3680 RETURN_IF(DeductionCandidate::Copy == stmt->getDeductionCandidateKind());
3681
3682 const bool isImplicit{stmt->isImplicit()};
3683 const bool noSpecializations = [&] {
3684 if(const auto* dt = stmt->getDescribedFunctionTemplate()) {
3685 return dt->specializations().empty();
3686 }
3687
3688 return false;
3689 }();
3690
3691 // Block compiler generated deduction guides which are _overridden_ by user provided deduction guides.
3692 RETURN_IF(not stmt->isUsed() and isImplicit and noSpecializations);
3693
3694 const bool isSpecialization{stmt->isFunctionTemplateSpecialization()};
3695 const bool needsTemplateGuard{isImplicit or isSpecialization};
3696
3697 if(needsTemplateGuard) {
3699 }
3700
3701 const auto* deducedTemplate = stmt->getDeducedTemplate();
3702
3703 if(isSpecialization) {
3705 } else if(const auto* e = stmt->getDescribedFunctionTemplate()) {
3706 InsertTemplateParameters(*e->getTemplateParameters());
3707 }
3708
3709 mOutputFormatHelper.Append(GetName(*deducedTemplate));
3710
3711 if(stmt->getNumParams()) {
3712 WrapInParens([&] { mOutputFormatHelper.AppendParameterList(stmt->parameters()); });
3713 } else {
3715 }
3716
3717 mOutputFormatHelper.AppendSemiNewLine(hlpArrow, GetName(stmt->getReturnType()));
3718
3719 if(needsTemplateGuard) {
3721 }
3722}
3723//-----------------------------------------------------------------------------
3724
3725void CodeGenerator::InsertTemplate(const FunctionTemplateDecl* stmt, bool withSpec)
3726{
3728
3729 mProcessingPrimaryTemplate = ProcessingPrimaryTemplate::Yes;
3730
3731 // InsertTemplateParameters(*stmt->getTemplateParameters());
3732 InsertArg(stmt->getTemplatedDecl());
3733
3734 mProcessingPrimaryTemplate = ProcessingPrimaryTemplate::No;
3735
3736 RETURN_IF(not withSpec);
3737
3738 for(const auto* spec : stmt->specializations()) {
3739 // For specializations we will see them later
3740 if(spec->getPreviousDecl()) {
3741 continue;
3742 }
3743
3745 InsertArg(spec);
3747 }
3748}
3749//-----------------------------------------------------------------------------
3750
3751void CodeGenerator::InsertArg(const FunctionTemplateDecl* stmt)
3752{
3753 InsertTemplate(stmt, true);
3754}
3755//-----------------------------------------------------------------------------
3756
3757void CodeGenerator::InsertArg(const TypeAliasTemplateDecl* stmt)
3758{
3759 InsertTemplateParameters(*stmt->getTemplateParameters());
3760
3761 InsertArg(stmt->getTemplatedDecl());
3762}
3763//-----------------------------------------------------------------------------
3764
3765void CodeGenerator::InsertArg(const AttributedStmt* stmt)
3766{
3767 for(const auto& attr : stmt->getAttrs()) {
3768 InsertAttribute(*attr);
3769 }
3770
3771 InsertArg(stmt->getSubStmt());
3772}
3773//-----------------------------------------------------------------------------
3774
3776{
3777 if(stmt->hasAttrs()) {
3779
3780 InsertAttributes(stmt->attrs());
3781 }
3782}
3783//-----------------------------------------------------------------------------
3784
3785void CodeGenerator::InsertAttributes(const Decl::attr_range& attrs)
3786{
3787 // attrs required for constinit
3788 for(const auto& attr : attrs) {
3789 InsertAttribute(*attr);
3790 }
3791}
3792//-----------------------------------------------------------------------------
3793
3795{
3796 // skip this attribute. Clang seems to tag virtual methods with override
3797 RETURN_IF(attr::Override == attr.getKind());
3798
3799 // skip this attribute. Clang seems to tag final methods or classes with final
3800 RETURN_IF(attr::Final == attr.getKind());
3801
3802 // skip this custom clang attribute
3803 RETURN_IF(attr::NoInline == attr.getKind());
3804
3805 // Clang's printPretty misses the parameter pack ellipsis. Hence treat this special case here.
3806 if(const auto* alignedAttr = dyn_cast_or_null<AlignedAttr>(&attr)) {
3807 auto insert = [&](const QualType type, const TemplateTypeParmType* tmplTypeParam) {
3808 mOutputFormatHelper.Append(attr.getSpelling(),
3809 "("sv,
3810 kwAlignof,
3811 "("sv,
3812 GetName(type),
3813 ")"sv,
3814 Ellipsis(tmplTypeParam->isParameterPack()),
3815 ") "sv);
3816 };
3817
3818 if(alignedAttr->isAlignmentExpr()) {
3819 if(const auto* unaryExpr = dyn_cast_or_null<UnaryExprOrTypeTraitExpr>(alignedAttr->getAlignmentExpr())) {
3820 if(const auto* tmplTypeParam =
3821 dyn_cast_or_null<TemplateTypeParmType>(unaryExpr->getArgumentType().getTypePtrOrNull())) {
3822 insert(unaryExpr->getArgumentType(), tmplTypeParam);
3823 return;
3824 }
3825 }
3826 } else if(const auto* tmplTypeParam =
3827 alignedAttr->getAlignmentType()->getType()->getAs<TemplateTypeParmType>()) {
3828 insert(alignedAttr->getAlignmentType()->getType(), tmplTypeParam);
3829 return;
3830 }
3831 }
3832
3833 StringStream stream{};
3834 PrintingPolicy pp{GetGlobalAST().getLangOpts()};
3835 pp.adjustForCPlusPlus();
3836
3837 attr.printPretty(stream, pp);
3838
3839 // attributes start with a space, skip it as it is not required for the first attribute
3840 std::string_view start{stream.str()};
3841
3842 mOutputFormatHelper.Append(start, " "sv);
3843}
3844//-----------------------------------------------------------------------------
3845
3846void CodeGenerator::InsertArg(const CXXRecordDecl* stmt)
3847{
3848 const size_t insertPosBeforeClass{mOutputFormatHelper.CurrentPos()};
3849 const auto indentAtInsertPosBeforeClass{mOutputFormatHelper.GetIndent()};
3850
3851 SCOPE_HELPER(stmt);
3852
3853 // Prevent a case like in #205 where the lambda appears twice.
3854 RETURN_IF(stmt->isLambda() and (mLambdaStack.empty() or (nullptr == mLambdaExpr)));
3855
3856 const auto* classTemplatePartialSpecializationDecl = dyn_cast_or_null<ClassTemplatePartialSpecializationDecl>(stmt);
3857 const auto* classTemplateSpecializationDecl = dyn_cast_or_null<ClassTemplateSpecializationDecl>(stmt);
3858
3859 // we require the if-guard only if it is a compiler generated specialization. If it is a hand-written variant it
3860 // should compile.
3861 const bool isClassTemplateSpecialization{classTemplatePartialSpecializationDecl or classTemplateSpecializationDecl};
3862 const bool tmplRequiresIfDef{[&] {
3863 if(classTemplatePartialSpecializationDecl) {
3864 return classTemplatePartialSpecializationDecl->isImplicit();
3865
3866 } else if(classTemplateSpecializationDecl) {
3867 return not classTemplateSpecializationDecl->isExplicitInstantiationOrSpecialization();
3868 }
3869
3870 return false;
3871 }()};
3872
3873 FinalAction _{[&] {
3874 if(tmplRequiresIfDef) {
3876 }
3877 }};
3878
3879 if(isClassTemplateSpecialization) {
3880 if(tmplRequiresIfDef) {
3881 InsertInstantiationPoint(GetSM(*classTemplateSpecializationDecl),
3882 classTemplateSpecializationDecl->getPointOfInstantiation());
3884 }
3885
3886 if(classTemplatePartialSpecializationDecl) {
3887 InsertTemplateParameters(*classTemplatePartialSpecializationDecl->getTemplateParameters());
3888 } else {
3890 }
3891 // Render a out-of-line struct declared inside a class template
3892 } else if(stmt->getLexicalDeclContext() != stmt->getDeclContext()) {
3893 if(const auto* parent = dyn_cast_or_null<CXXRecordDecl>(stmt->getDeclContext())) {
3894 if(const auto* outerClasTemplateDecl = parent->getDescribedClassTemplate()) {
3895 InsertTemplateParameters(*outerClasTemplateDecl->getTemplateParameters());
3896 }
3897 }
3898 }
3899
3901
3902 InsertAttributes(stmt->attrs());
3903
3905
3906 if(classTemplateSpecializationDecl) {
3907 InsertTemplateArgs(*classTemplateSpecializationDecl);
3908 }
3909
3910 if(stmt->hasAttr<FinalAttr>()) {
3912 }
3913
3914 // skip classes/struct's without a definition
3915 if(not stmt->hasDefinition() or not stmt->isCompleteDefinition()) {
3917 return;
3918 }
3919
3920 if(stmt->getNumBases()) {
3921 mOutputFormatHelper.Append(" : "sv);
3922
3923 ForEachArg(stmt->bases(), [&](const auto& base) {
3924 mOutputFormatHelper.Append(getAccessSpelling(base.getAccessSpecifier()),
3925 " "sv,
3926 ValueOrDefault(base.isVirtual(), kwVirtualSpace),
3927 GetName(base.getType()),
3928 Ellipsis(base.isPackExpansion()));
3929 });
3930 }
3931
3932 if(GetInsightsOptions().UseShowPadding) {
3933 const auto& recordLayout = GetRecordLayout(stmt);
3935 " /* size: "sv, recordLayout.getSize(), ", align: "sv, recordLayout.getAlignment(), " */"sv);
3936
3937 } else {
3939 }
3940
3942
3943 if(GetInsightsOptions().UseShowPadding) {
3944 for(size_t offset{}; const auto& base : stmt->bases()) {
3945 const auto& baseRecordLayout = GetRecordLayout(base.getType()->getAsRecordDecl());
3946 const auto baseVar = StrCat("/* base ("sv, GetName(base.getType()), ")"sv);
3947 const auto size = baseRecordLayout.getSize().getQuantity();
3948
3950 baseVar, GetSpaces(baseVar.size()), " offset: "sv, offset, ", size: "sv, size, " */"sv);
3951
3952 offset += size;
3953 }
3954 }
3955
3957
3958 OnceTrue firstRecordDecl{};
3959 OnceTrue firstDecl{};
3960 Decl::Kind formerKind{};
3961 AccessSpecifier lastAccess{stmt->isClass() ? AS_private : AS_public};
3962 for(const auto* d : stmt->decls()) {
3963 if(isa<CXXRecordDecl>(d) and firstRecordDecl) {
3964 continue;
3965 }
3966
3967 // Insert a newline when the decl kind changes. This for example, inserts a newline when after a FieldDecl
3968 // we see a CXXMethodDecl.
3969 if(not firstDecl and (d->getKind() != formerKind)) {
3970 // mOutputFormatHelper.AppendNewLine();
3971 }
3972
3973 if((stmt->isLambda() and isa<CXXDestructorDecl>(d)) and not d->isUsed()) {
3974 continue;
3975 }
3976
3977 // Insert the access modifier, as at least some of the compiler generated classes do not contain an access
3978 // specifier which results in a default ctor being private if we do not insert the access modifier.
3979 if(lastAccess != d->getAccess()) {
3980 lastAccess = d->getAccess();
3981
3982 // skip inserting an access specifier of our own, if there is a real one coming.
3983 if(not isa<AccessSpecDecl>(d)) {
3985 }
3986 }
3987
3988 InsertArg(d);
3989
3990 formerKind = d->getKind();
3991 }
3992
3993 if(stmt->isLambda()) {
3994 const LambdaCallerType lambdaCallerType = mLambdaStack.back().callerType();
3995 const bool ctorRequired{stmt->capture_size() or stmt->lambdaIsDefaultConstructibleAndAssignable()};
3996
3997 if(ctorRequired) {
3998 if(AS_public != lastAccess) {
4000 // XXX avoid diff in tests. AccessToStringWithColon add "public: " before there was no space.
4001 const auto pub{AccessToStringWithColon(AS_public)};
4002 std::string_view p{pub};
4003 p.remove_suffix(1);
4005 }
4006
4007 if(stmt->lambdaIsDefaultConstructibleAndAssignable()) {
4009
4010 if(stmt->hasConstexprDefaultConstructor()) {
4012 }
4013 }
4014
4015 mOutputFormatHelper.Append(GetName(*stmt), "("sv);
4016 }
4017
4018 SmallVector<std::string, 5> ctorInitializerList{};
4019 std::string ctorArguments{'{'};
4020 OnceTrue firstCtorArgument{};
4021
4022 auto addToInits =
4023 [&](std::string_view name, const FieldDecl* fd, bool isThis, const Expr* expr, bool /*useBraces*/) {
4024 if(firstCtorArgument) {
4025 } else {
4027 ctorArguments.append(", "sv);
4028 }
4029
4030 bool byConstRef{false};
4031 auto fieldName{isThis ? kwInternalThis : name};
4032 auto fieldDeclType{fd->getType()};
4033 bool isMoved{};
4034
4035 std::string fname = StrCat("_"sv, name);
4036
4037 // Special handling for lambdas with init captures which contain a move. In such a case, copy the
4038 // initial move statement and make the variable a &&.
4039 if(const auto* cxxConstructExpr = dyn_cast_or_null<CXXConstructExpr>(expr);
4040 cxxConstructExpr and cxxConstructExpr->getConstructor()->isMoveConstructor()) {
4041
4042 OutputFormatHelper ofm{};
4043 LambdaInitCaptureCodeGenerator codeGenerator{ofm, mLambdaStack, name};
4044
4045 if(cxxConstructExpr->getNumArgs()) {
4046 ForEachArg(cxxConstructExpr->arguments(),
4047 [&](const auto& arg) { codeGenerator.InsertArg(arg); });
4048 }
4049
4050 fieldDeclType = stmt->getASTContext().getRValueReferenceType(fieldDeclType);
4051
4052 fname = ofm;
4053
4054 // If it is not an object, check for other conditions why we take the variable by const &/&& in the
4055 // ctor
4056 } else if(not fieldDeclType->isReferenceType() and not fieldDeclType->isAnyPointerType() and
4057 not fieldDeclType->isUndeducedAutoType()) {
4058 byConstRef = true;
4059 const auto* exprWithoutImpCasts = expr->IgnoreParenImpCasts();
4060
4061 // treat a move of a primitive type
4062 if(exprWithoutImpCasts->isXValue()) {
4063 byConstRef = false;
4064
4065 OutputFormatHelper ofm{};
4066 LambdaInitCaptureCodeGenerator codeGenerator{ofm, mLambdaStack, name};
4067 codeGenerator.InsertArg(expr);
4068
4069 fname = ofm;
4070
4071 } else if(exprWithoutImpCasts
4072 ->isPRValue() // If we are looking at an rvalue (temporary) we need a const ref
4073 or exprWithoutImpCasts->getType().isConstQualified() // If the captured variable is const
4074 // we can take it only by const ref
4075
4076 ) {
4077 // this must go before adding the L or R-value reference, otherwise we get T& const instead of
4078 // const T&
4079
4080 if(exprWithoutImpCasts->isPRValue() and isa<CXXBindTemporaryExpr>(exprWithoutImpCasts) and
4081 not exprWithoutImpCasts->getType().isConstQualified()) {
4082 fieldDeclType = stmt->getASTContext().getRValueReferenceType(fieldDeclType);
4084 fname = StrCat("std::move("sv, fname, ")"sv);
4085 isMoved = true;
4086
4087 } else {
4088 fieldDeclType.addConst();
4089 }
4090 }
4091
4092 if(exprWithoutImpCasts->isXValue()) {
4093 fieldDeclType = stmt->getASTContext().getRValueReferenceType(fieldDeclType);
4094
4095 } else if(not isMoved) {
4096 fieldDeclType = stmt->getASTContext().getLValueReferenceType(fieldDeclType);
4097 }
4098 }
4099
4100 const std::string_view elips{
4101 Ellipsis(isa_and_nonnull<PackExpansionType>(fieldDeclType->getPointeeType().getTypePtrOrNull()))};
4102
4103 // To avoid seeing the templates stuff from std::move (typename...) the canonical type is used here.
4104 fieldDeclType = fieldDeclType.getCanonicalType();
4105
4106 ctorInitializerList.push_back(StrCat(fieldName, "{"sv, fname, elips, "}"sv));
4107
4108 if(not isThis and expr) {
4110 OutputFormatHelper ofmLambdaInCtor{};
4111 ofmLambdaInCtor.SetIndent(indentAtInsertPosBeforeClass);
4112 CodeGenerator cgLambdaInCtor{ofmLambdaInCtor, LambdaInInitCapture::Yes};
4113
4114 if(P0315Visitor dt{cgLambdaInCtor}; dt.TraverseStmt(const_cast<Expr*>(expr))) {
4115
4116 OutputFormatHelper ofm{};
4118
4119 if(const auto* ctorExpr = dyn_cast_or_null<CXXConstructExpr>(expr);
4120 ctorExpr and byConstRef and (1 == ctorExpr->getNumArgs())) {
4121 codeGenerator->InsertArg(ctorExpr->getArg(0));
4122
4123 } else {
4124 codeGenerator->InsertArg(expr);
4125 }
4126
4127 // if(isa<PackExpansionType>(stmt->getDecl()->getType().getTypePtrOrNull())) {
4128 // mOutputFormatHelper.Append(kwElipsisSpace);
4129 // }
4130
4131 ctorArguments.append(ofm);
4132
4133 } else {
4134 OutputFormatHelper ofm{};
4135 LambdaNameOnlyCodeGenerator ccg{ofm};
4136 ccg.InsertArg(expr);
4137
4138 ctorArguments.append(ofm.GetString());
4139
4140 mOutputFormatHelper.InsertAt(insertPosBeforeClass, ofmLambdaInCtor);
4141 }
4142 } else {
4143 if(isThis and not fieldDeclType->isPointerType()) {
4144 ctorArguments.append("*"sv);
4145 }
4146
4147 ctorArguments.append(name);
4148 }
4149
4150 mOutputFormatHelper.Append(GetTypeNameAsParameter(fieldDeclType, StrCat("_"sv, name)));
4151 };
4152
4153 llvm::DenseMap<const ValueDecl*, FieldDecl*> captures{};
4154 FieldDecl* thisCapture{};
4155
4156 stmt->getCaptureFields(captures, thisCapture);
4157
4158 // Check if it captures this
4159 if(thisCapture) {
4160 const auto* captureInit = mLambdaExpr->capture_init_begin();
4161
4162 addToInits(kwThis, thisCapture, true, *captureInit, false);
4163 }
4164
4165 // Find the corresponding capture in the DenseMap. The DenseMap seems to be change its order each time.
4166 // Hence we use \c captures() to keep the order stable. While using \c Captures to generate the code as
4167 // it carries the better type infos.
4168 for(const auto& [c, cinit] : zip(mLambdaExpr->captures(), mLambdaExpr->capture_inits())) {
4169 if(not c.capturesVariable()) {
4170 continue;
4171 }
4172
4173 const auto* capturedVar = c.getCapturedVar();
4174 if(const auto* value = captures[capturedVar]) {
4175 // Since C++20 lambdas can capture structured bindings
4176 if(const auto* bindingDecl = dyn_cast_or_null<BindingDecl>(capturedVar)) {
4177 const auto* decompositionDecl = cast<DecompositionDecl>(bindingDecl->getDecomposedDecl());
4178 addToInits(GetName(*capturedVar),
4179 value,
4180 false,
4181 cinit,
4182 VarDecl::ListInit == decompositionDecl->getInitStyle());
4183 continue;
4184 }
4185
4186 addToInits(GetName(*capturedVar),
4187 value,
4188 false,
4189 cinit,
4190 VarDecl::ListInit == dyn_cast_or_null<VarDecl>(capturedVar)->getInitStyle());
4191 }
4192 }
4193
4194 ctorArguments.append("}"sv);
4195
4196 // generate the ctor only if it is required, i.e. we have captures. This is in fact a trick to get
4197 // compiling code out of it. The compiler itself does not generate a constructor in many many cases.
4198 if(ctorRequired) {
4200
4201 if(stmt->lambdaIsDefaultConstructibleAndAssignable()) {
4203
4204 } else {
4206
4207 for(OnceTrue firstCtorInitializer{}; const auto& initializer : ctorInitializerList) {
4208 if(firstCtorInitializer) {
4210 } else {
4212 }
4213
4215 }
4216
4218 }
4219 }
4220
4221 // close the class scope
4223
4224 if(not is{lambdaCallerType}.any_of(LambdaCallerType::VarDecl,
4230 mOutputFormatHelper.Append(" "sv, GetLambdaName(*stmt), ctorArguments);
4231 } else if(not is{lambdaCallerType}.any_of(LambdaCallerType::TemplateHead, LambdaCallerType::Decltype)) {
4232 mLambdaStack.back().inits().append(ctorArguments);
4233 }
4234 } else {
4235 mOutputFormatHelper.CloseScope(OutputFormatHelper::NoNewLineBefore::Yes);
4236 }
4237
4238 if(GetInsightsOptions().UseShow2C) {
4239 mOutputFormatHelper.Append(" "sv, GetName(*stmt));
4240 }
4241
4244}
4245//-----------------------------------------------------------------------------
4246
4247void CodeGenerator::InsertArg(const DeclStmt* stmt)
4248{
4249 for(const auto* decl : stmt->decls()) {
4250 InsertArg(decl);
4251 }
4252}
4253//-----------------------------------------------------------------------------
4254
4255void CodeGenerator::InsertArg(const SubstNonTypeTemplateParmExpr* stmt)
4256{
4257 InsertArg(stmt->getReplacement());
4258}
4259//-----------------------------------------------------------------------------
4260
4261void CodeGenerator::InsertArg(const SizeOfPackExpr* stmt)
4262{
4263 if(stmt->isPartiallySubstituted()) {
4264 mOutputFormatHelper.Append(stmt->getPartialArguments().size());
4265 } else if(not stmt->isValueDependent()) {
4266 mOutputFormatHelper.Append(stmt->getPackLength());
4267 } else {
4268 mOutputFormatHelper.Append(kwSizeof, kwElipsis, "("sv, GetName(*stmt->getPack()), ")"sv);
4269 }
4270}
4271//-----------------------------------------------------------------------------
4272
4273void CodeGenerator::InsertArg(const ReturnStmt* stmt)
4274{
4276
4278
4279 { // dedicated scope to first clear everything found in the return statement. Then clear all others.
4280 TemporaryDeclFinder temporaryFinder{*this, stmt->getRetValue(), true};
4281
4283
4284 if(const auto* retVal = stmt->getRetValue()) {
4286
4287 if(not temporaryFinder.Found()) {
4288 if(const auto* nrvoVD = stmt->getNRVOCandidate()) {
4290 } else {
4291 InsertArg(retVal);
4292 }
4293 } else {
4294 mOutputFormatHelper.Append(temporaryFinder.Name());
4295 }
4296 }
4297 }
4298
4300
4301 // the InsertArg above changes the start
4302 mLastStmt = stmt;
4303
4304 mCurrentReturnPos.reset();
4305}
4306//-----------------------------------------------------------------------------
4307
4308void CodeGenerator::InsertArg(const NullStmt* /*stmt*/)
4309{
4311 mSkipSemi = true;
4312}
4313//-----------------------------------------------------------------------------
4314
4315void CodeGenerator::InsertArg(const StmtExpr* stmt)
4316{
4317 WrapInParens([&] { InsertArg(stmt->getSubStmt()); });
4318}
4319//-----------------------------------------------------------------------------
4320
4321void CodeGenerator::InsertArg(const CppInsightsCommentStmt* stmt)
4322{
4324}
4325//-----------------------------------------------------------------------------
4326
4327void CodeGenerator::InsertArg(const ConceptSpecializationExpr* stmt)
4328{
4329 if(const auto* namedConcept = stmt->getNamedConcept()) {
4330 mOutputFormatHelper.Append(GetName(*namedConcept));
4331 InsertTemplateArgs(stmt->getTemplateArgsAsWritten()->arguments());
4332
4333#if 0
4334 if(not stmt->isValueDependent()) {
4336 }
4337#endif
4338 }
4339}
4340//-----------------------------------------------------------------------------
4341
4342void CodeGenerator::InsertArg(const RequiresExpr* stmt)
4343{
4345
4346 const auto localParameters = stmt->getLocalParameters();
4348 not localParameters.empty(),
4349 [&] { mOutputFormatHelper.AppendParameterList(localParameters); },
4350 AddSpaceAtTheEnd::Yes);
4351
4353
4354 const auto noEmptyInitList = mNoEmptyInitList;
4355 FinalAction _{[&] { mNoEmptyInitList = noEmptyInitList; }};
4356 mNoEmptyInitList = NoEmptyInitList::Yes;
4357
4358 for(const auto& requirement : stmt->getRequirements()) {
4359 if(const auto* typeRequirement = dyn_cast_or_null<concepts::TypeRequirement>(requirement)) {
4360 if(typeRequirement->isSubstitutionFailure()) {
4362 } else {
4363 mOutputFormatHelper.Append(GetName(typeRequirement->getType()->getType()));
4364 }
4365
4366 // SimpleRequirement
4367 } else if(const auto* exprRequirement = dyn_cast_or_null<concepts::ExprRequirement>(requirement)) {
4368 if(exprRequirement->isExprSubstitutionFailure()) {
4369 // The requirement failed. We need some way to express that. Using a nested
4370 // requirement with false seems to be the simplest solution.
4372 } else {
4373 WrapInCurliesIfNeeded(exprRequirement->isCompound(), [&] { InsertArg(exprRequirement->getExpr()); });
4374
4375 if(exprRequirement->hasNoexceptRequirement()) {
4377 }
4378
4379 if(const auto& returnTypeRequirement = exprRequirement->getReturnTypeRequirement();
4380 not returnTypeRequirement.isEmpty()) {
4381 if(auto typeConstraint = GetTypeConstraintAsString(returnTypeRequirement.getTypeConstraint());
4382 not typeConstraint.empty()) {
4383 mOutputFormatHelper.Append(hlpArrow, std::move(typeConstraint));
4384 }
4385 }
4386 }
4387 } else if(const auto* nestedRequirement = dyn_cast_or_null<concepts::NestedRequirement>(requirement)) {
4389
4390 if(nestedRequirement->hasInvalidConstraint()) {
4391 // The requirement failed. We need some way to express that. Using a nested
4392 // requirement with false seems to be the simplest solution.
4394 } else {
4395 InsertArg(nestedRequirement->getConstraintExpr());
4396 }
4397 }
4398
4400 }
4401
4402 mOutputFormatHelper.CloseScope(OutputFormatHelper::NoNewLineBefore::Yes);
4403}
4404//-----------------------------------------------------------------------------
4405
4406void CodeGenerator::InsertArg(const CXXDefaultArgExpr* stmt)
4407{
4408 InsertArg(stmt->getExpr());
4409}
4410//-----------------------------------------------------------------------------
4411
4412void CodeGenerator::InsertArg(const CXXStdInitializerListExpr* stmt)
4413{
4414 // No qualifiers like const or volatile here. This appears in function calls or operators as a parameter.
4415 // CV's are not allowed there.
4416 const auto typeName{GetName(stmt->getType(), Unqualified::Yes)};
4417
4418 if(GetInsightsOptions().UseShowInitializerList) {
4419 RETURN_IF(not mCurrentVarDeclPos.has_value() and not mCurrentFieldPos.has_value() and
4420 not mCurrentReturnPos.has_value() and not mCurrentCallExprPos.has_value());
4421
4422 const auto* subExpr = stmt->getSubExpr();
4423
4424 if(const auto* dref = dyn_cast_or_null<DeclRefExpr>(subExpr); dref and GetInsightsOptions().ShowLifetime) {
4425 const auto size = GetSize(dyn_cast_or_null<ConstantArrayType>(subExpr->getType()));
4426
4427 mOutputFormatHelper.Append(typeName, "{"sv, GetName(*dref), ", "sv, size, "}"sv);
4428 return;
4429 }
4430
4431 std::string modifiers{};
4432 size_t variableInsertPos = mCurrentReturnPos.value_or(
4433 mCurrentVarDeclPos.value_or(mCurrentCallExprPos.value_or(0))); // order is important!
4434
4435 auto& ofmToInsert = [&]() -> decltype(auto) {
4436 if(not mCurrentVarDeclPos.has_value() and not mCurrentReturnPos.has_value() and
4437 not mCurrentCallExprPos.has_value()) {
4438 variableInsertPos = mCurrentFieldPos.value_or(0);
4439 mCurrentVarDeclPos = variableInsertPos;
4440 modifiers = StrCat(kwStaticSpace, kwInlineSpace);
4442 }
4443
4444 return (mOutputFormatHelper);
4445 }();
4446
4447 OutputFormatHelper ofm{};
4448 ofm.SetIndent(ofmToInsert, OutputFormatHelper::SkipIndenting::Yes);
4449
4450 const auto size = [&]() -> size_t {
4451 if(const auto* mat = dyn_cast<MaterializeTemporaryExpr>(subExpr)) {
4452 if(const auto* list = dyn_cast_or_null<InitListExpr>(mat->getSubExpr())) {
4453 return list->getNumInits();
4454 }
4455 }
4456
4457 return 0;
4458 }();
4459
4460 auto internalListName =
4461 MakeLineColumnName(GetGlobalAST().getSourceManager(), stmt->getBeginLoc(), BuildInternalVarName("list"sv));
4462
4463 ofm.Append(modifiers, GetTypeNameAsParameter(subExpr->getType(), internalListName));
4464 CodeGeneratorVariant codeGenerator{ofm};
4465 codeGenerator->InsertArg(subExpr);
4466 ofm.AppendSemiNewLine();
4467
4468 ofmToInsert.InsertAt(variableInsertPos, ofm);
4469
4470 mOutputFormatHelper.Append(typeName, "{"sv, internalListName, ", "sv, size, "}"sv);
4471
4472 if(mCurrentReturnPos.has_value()) {
4473 mCurrentReturnPos = mCurrentReturnPos.value() + ofm.size();
4474 } else if(mCurrentVarDeclPos.has_value()) {
4475 mCurrentVarDeclPos = mCurrentVarDeclPos.value() + ofm.size();
4476 } else {
4477 mCurrentCallExprPos = mCurrentCallExprPos.value() + ofm.size();
4478 }
4479
4480 } else {
4481 mOutputFormatHelper.Append(typeName);
4482 InsertArg(stmt->getSubExpr());
4483 }
4484}
4485//-----------------------------------------------------------------------------
4486
4487void CodeGenerator::InsertArg(const CXXNullPtrLiteralExpr* /*stmt*/)
4488{
4490}
4491//-----------------------------------------------------------------------------
4492
4493void CodeGenerator::InsertArg(const LabelDecl* stmt)
4494{
4495 mOutputFormatHelper.Append(stmt->getName());
4496}
4497//-----------------------------------------------------------------------------
4498
4499void CodeGenerator::InsertArg(const Decl* stmt)
4500{
4501 mLastDecl = stmt;
4502
4503#define SUPPORTED_DECL(type) \
4504 if(isa<type>(stmt)) { \
4505 InsertArg(static_cast<const type*>(stmt)); \
4506 return; \
4507 }
4508
4509#define IGNORED_DECL SUPPORTED_DECL
4510
4511#include "CodeGeneratorTypes.h"
4512
4514}
4515//-----------------------------------------------------------------------------
4516
4518{
4519 if(not stmt) {
4520 DPrint("Null stmt\n");
4521 return;
4522 }
4523
4524 mLastStmt = stmt;
4525
4526#define SUPPORTED_STMT(type) \
4527 if(isa<type>(stmt)) { \
4528 InsertArg(dyn_cast_or_null<type>(stmt)); \
4529 return; \
4530 }
4531
4532#define IGNORED_STMT SUPPORTED_STMT
4533
4534#include "CodeGeneratorTypes.h"
4535
4537}
4538//-----------------------------------------------------------------------------
4539
4540void CodeGenerator::FormatCast(const std::string_view castName,
4541 const QualType& castDestType,
4542 const Expr* subExpr,
4543 const CastKind& castKind)
4544{
4545 const bool isCastToBase{is{castKind}.any_of(CK_DerivedToBase, CK_UncheckedDerivedToBase) and
4546 castDestType->isRecordType()};
4547 const std::string castDestTypeText{
4548 StrCat(GetName(castDestType), ((isCastToBase and not castDestType->isAnyPointerType()) ? "&"sv : ""sv))};
4549
4550 mOutputFormatHelper.Append(castName, "<"sv, castDestTypeText, ">("sv);
4551 InsertArg(subExpr);
4553}
4554//-----------------------------------------------------------------------------
4555
4557{
4558 const bool needsParens = [&]() {
4559 if(const auto* expr = dyn_cast_or_null<Expr>(stmt)) {
4560 if(const auto* dest = dyn_cast_or_null<UnaryOperator>(expr->IgnoreImplicit())) {
4561 return (dest->getOpcode() == clang::UO_Deref);
4562 }
4563 }
4564
4565 return false;
4566 }();
4567
4568 WrapInParensIfNeeded(needsParens, [&] { InsertArg(stmt); });
4569}
4570//-----------------------------------------------------------------------------
4571
4572void CodeGenerator::InsertSuffix(const QualType& type)
4573{
4574 if(const auto* typePtr = type.getTypePtrOrNull(); typePtr and typePtr->isBuiltinType()) {
4575 if(const auto* bt = dyn_cast_or_null<BuiltinType>(typePtr)) {
4576 const auto kind = bt->getKind();
4577
4579 }
4580 }
4581}
4582//-----------------------------------------------------------------------------
4583
4584void CodeGenerator::InsertTemplateArgs(const ClassTemplateSpecializationDecl& clsTemplateSpe)
4585{
4586 if(const auto* ar = clsTemplateSpe.getTemplateArgsAsWritten()) {
4587 InsertTemplateArgs(ar->arguments());
4588 } else {
4589 InsertTemplateArgs(clsTemplateSpe.getTemplateArgs());
4590 }
4591}
4592//-----------------------------------------------------------------------------
4593
4594void CodeGenerator::HandleTemplateParameterPack(const ArrayRef<TemplateArgument>& args)
4595{
4596 ForEachArg(args, [&](const auto& arg) { InsertTemplateArg(arg); });
4597}
4598//-----------------------------------------------------------------------------
4599
4600void CodeGenerator::InsertTemplateArg(const TemplateArgument& arg)
4601{
4602 switch(arg.getKind()) {
4603 case TemplateArgument::Type: mOutputFormatHelper.Append(GetName(arg.getAsType())); break;
4604 case TemplateArgument::Declaration:
4605 // TODO: handle pointers
4606 if(const auto decl = dyn_cast_or_null<TemplateParamObjectDecl>(arg.getAsDecl())) {
4608 } else {
4609 mOutputFormatHelper.Append("&"sv, GetName(*arg.getAsDecl(), QualifiedName::Yes));
4610 }
4611 break;
4612 case TemplateArgument::NullPtr: mOutputFormatHelper.Append(kwNullptr); break;
4613 case TemplateArgument::Integral:
4614
4615 if(const auto& integral = arg.getAsIntegral(); arg.getIntegralType()->isCharType()) {
4616 const char c{static_cast<char>(integral.getZExtValue())};
4617 mOutputFormatHelper.Append("'"sv, std::string{c}, "'"sv);
4618 } else {
4619 mOutputFormatHelper.Append(integral);
4620 }
4621
4622 break;
4623 case TemplateArgument::Expression: {
4624 if(auto val = EvaluateNTTPAsConstantExpr(arg.getAsExpr()->IgnoreParenImpCasts())) {
4626 GetName(val->first),
4627 BuildTemplateParamObjectName(val->second.getAsString(GetGlobalAST(), val->first)));
4628 } else {
4629 InsertArg(arg.getAsExpr());
4630 }
4631 }
4632
4633 break;
4634 case TemplateArgument::Pack: HandleTemplateParameterPack(arg.pack_elements()); break;
4635 case TemplateArgument::Template: [[fallthrough]];
4636 case TemplateArgument::TemplateExpansion:
4637 if(const auto* tmplDecl = arg.getAsTemplateOrTemplatePattern().getAsTemplateDecl()) {
4639
4640 } else if(const auto* depName = arg.getAsTemplateOrTemplatePattern().getAsDependentTemplateName();
4641 depName->isIdentifier()) {
4642 InsertNamespace(depName->getQualifier());
4643
4644 mOutputFormatHelper.Append(kwTemplateSpace, depName->getIdentifier()->getName());
4645 } else {
4647 }
4648
4649 break;
4650 case TemplateArgument::Null: mOutputFormatHelper.Append("null"sv); break;
4651 case TemplateArgument::StructuralValue: mOutputFormatHelper.Append(arg.getAsStructuralValue()); break;
4652 }
4653}
4654//-----------------------------------------------------------------------------
4655
4657{
4659
4660 const auto& ctx = GetGlobalAST();
4661
4662 auto& langOpts{GetLangOpts(*stmt)};
4663 const bool threadSafe{langOpts.ThreadsafeStatics and langOpts.CPlusPlus11 and
4664 (stmt->isLocalVarDecl() /*|| NonTemplateInline*/) and not stmt->getTLSKind()};
4665
4666 const std::string internalVarName{BuildInternalVarName(GetName(*stmt))};
4667 const std::string compilerBoolVarName{StrCat(internalVarName, "Guard"sv)};
4668
4669 // insert compiler bool to track init state
4670 auto* compilerGuardVar =
4671 Variable(compilerBoolVarName, threadSafe ? Typedef("uint64_t"sv, ctx.UnsignedLongTy) : ctx.BoolTy);
4672 compilerGuardVar->setStorageClass(StorageClass::SC_Static);
4673 InsertArg(compilerGuardVar);
4674
4675 // insert compiler memory place holder
4676 auto* compilerStorageVar = Variable(internalVarName,
4677 ctx.getConstantArrayType(ctx.CharTy,
4678 llvm::APInt(ctx.getTypeSize(ctx.getSizeType()), 0),
4679 Sizeof(stmt->getType()),
4680 ArraySizeModifier::Normal,
4681 0));
4682
4683 compilerStorageVar->setStorageClass(StorageClass::SC_Static);
4684
4685 auto* alignedAttr =
4686 AlignedAttr::CreateImplicit(const_cast<ASTContext&>(ctx),
4687 true,
4688 Sizeof(stmt->getType()), // ctx.getTrivialTypeSourceInfo(stmt->getType()),
4689 {},
4690 AlignedAttr::Spelling::Keyword_alignas);
4691
4692 compilerStorageVar->addAttr(alignedAttr);
4693
4694 const std::string typeName{GetName(stmt->getType())};
4696 "alignas("sv, typeName, ") static char "sv, internalVarName, "[sizeof("sv, typeName, ")]"sv);
4697
4698 // insert compiler init if
4700
4701 // try to find out whether this ctor or the CallExpr can throw. If, then additional code needs to be generated
4702 // for exception handling.
4703 const bool canThrow{[&] {
4704 const ValueDecl* decl = [&]() -> const ValueDecl* {
4705 const auto* init = stmt->getInit()->IgnoreCasts();
4706 if(const auto* ctorExpr = dyn_cast_or_null<CXXConstructExpr>(init)) {
4707 return ctorExpr->getConstructor();
4708 } else if(const auto* callExpr = dyn_cast_or_null<CallExpr>(init)) {
4709 return callExpr->getDirectCallee();
4710 }
4711
4712 return nullptr;
4713 }();
4714
4715 if(decl) {
4716 if(const auto* func = decl->getType()->castAs<FunctionProtoType>()) {
4717 return not func->isNothrow();
4718 }
4719 }
4720
4721 return false;
4722 }()};
4723
4724 // VarDecl of a static expression always have an initializer
4725 auto* init = const_cast<Expr*>(stmt->getInit());
4726
4727 if(const bool isCallExpr{not isa<CXXConstructExpr>(init->IgnoreCasts())}; isCallExpr) {
4728 // we have a function call
4729 init = Call("std::move"sv, {init});
4730
4731 // Tests show that the compiler does better than std::move
4733 }
4734
4735 // the allocation and guard update:
4736 // new (&__s)T();
4737 // __sGuard = true;
4738 auto type = stmt->getType();
4739 type.removeLocalConst(); // Issue369.cpp is a const static variable. Should the ctor use the const?
4740 SmallVector<Stmt*, 4> allocAndFlagBodyStmts{New({Ref(compilerStorageVar)}, init, type),
4741 Assign(compilerGuardVar, Bool(true))};
4742 auto allocAndFlagBodyCompound = mkCompoundStmt(allocAndFlagBodyStmts);
4743
4744 StmtsContainer innerBodyStmts{};
4745
4746 // Need to insert a try catch, if the constructor/initializer can throw
4747 if(canThrow) {
4748 innerBodyStmts.AddBodyStmts(
4749 Try(allocAndFlagBodyCompound, Catch({Call("__cxa_guard_abort"sv, {Ref(compilerGuardVar)}), Throw()})));
4750 } else {
4751 innerBodyStmts.AddBodyStmts(allocAndFlagBodyCompound);
4752 }
4753
4754 StmtsContainer bodyStmts{};
4755
4756 if(threadSafe) {
4757 innerBodyStmts.AddBodyStmts(Call("__cxa_guard_release"sv, {Ref(compilerGuardVar)}));
4758 innerBodyStmts.Add(Comment(
4759 StrCat("__cxa_atexit("sv, typeName, "::~"sv, typeName, ", &"sv, internalVarName, ", &__dso_handle);"sv)));
4760
4761 auto* aquireIf = If(Call("__cxa_guard_acquire"sv, {Ref(compilerGuardVar)}), innerBodyStmts);
4762 bodyStmts.AddBodyStmts(aquireIf);
4763 } else {
4764 bodyStmts.AddBodyStmts(allocAndFlagBodyCompound);
4765 }
4766
4767 InsertArg(If(Equal(And(compilerGuardVar, Int32(0xff)), Int32(0)), bodyStmts));
4768}
4769//-----------------------------------------------------------------------------
4770
4771std::string_view CodeGenerator::GetBuiltinTypeSuffix(const BuiltinType::Kind& kind)
4772{
4773#define CASE(K, retVal) \
4774 case BuiltinType::K: return retVal
4775 switch(kind) {
4776 CASE(UInt, "U"sv);
4777 CASE(ULong, "UL"sv);
4778 CASE(ULongLong, "ULL"sv);
4779 CASE(UInt128, "ULLL"sv);
4780 CASE(Long, "L"sv);
4781 CASE(LongLong, "LL"sv);
4782 CASE(Float, "F"sv);
4783 CASE(LongDouble, "L"sv);
4784 default: return {};
4785 }
4786#undef BTCASE
4787}
4788//-----------------------------------------------------------------------------
4789
4790void CodeGenerator::HandleLambdaExpr(const LambdaExpr* lambda, LambdaHelper& lambdaHelper)
4791{
4792 OutputFormatHelper& outputFormatHelper = lambdaHelper.buffer();
4793
4794 outputFormatHelper.AppendNewLine();
4795 LambdaCodeGenerator codeGenerator{outputFormatHelper, mLambdaStack, mProcessingPrimaryTemplate};
4796 codeGenerator.mCapturedThisAsCopy = ranges::any_of(
4797 lambda->captures(), [](auto& c) { return (c.capturesThis() and (c.getCaptureKind() == LCK_StarThis)); });
4798
4799 codeGenerator.mLambdaExpr = lambda;
4800 codeGenerator.InsertArg(lambda->getLambdaClass());
4801}
4802//-----------------------------------------------------------------------------
4803
4804void CodeGenerator::InsertConceptConstraint(const llvm::SmallVectorImpl<const Expr*>& constraints,
4805 const InsertInline insertInline)
4806{
4807 for(OnceTrue first{}; const auto* c : constraints) {
4808 if(first and (InsertInline::Yes == insertInline)) {
4810 }
4811
4813 InsertArg(c);
4814
4815 if(InsertInline::No == insertInline) {
4817 }
4818 }
4819}
4820//-----------------------------------------------------------------------------
4821
4822// This inserts the requires clause after template<...>
4823void CodeGenerator::InsertConceptConstraint(const TemplateParameterList& tmplDecl)
4824{
4825 if(const auto* reqClause = tmplDecl.getRequiresClause()) {
4826 SmallVector<const Expr*, 1> constraints{reqClause};
4827
4828 InsertConceptConstraint(constraints, InsertInline::No);
4829 }
4830}
4831//-----------------------------------------------------------------------------
4832
4833// This inserts the requires clause after the function header
4834void CodeGenerator::InsertConceptConstraint(const FunctionDecl* tmplDecl)
4835{
4836 SmallVector<const Expr*, 5> constraints{};
4837 tmplDecl->getAssociatedConstraints(constraints);
4838
4839 InsertConceptConstraint(constraints, InsertInline::Yes);
4840}
4841//-----------------------------------------------------------------------------
4842
4843// This inserts the requires clause after a variable type
4844void CodeGenerator::InsertConceptConstraint(const VarDecl* varDecl)
4845{
4846 if(const auto* t = varDecl->getType()->getContainedAutoType()) {
4847 if(t->getTypeConstraintConcept()) {
4848#if 0
4849 mOutputFormatHelper.Append(kwCommentStart, t->getTypeConstraintConcept()->getName(), kwCCommentEndSpace);
4850#endif
4851 }
4852 }
4853}
4854//-----------------------------------------------------------------------------
4855
4857 const CXXConstructorDecl* cxxInheritedCtorDecl)
4858{
4859 bool isLambda{false};
4860 bool isFirstCxxMethodDecl{true};
4861 const auto* methodDecl{dyn_cast_or_null<CXXMethodDecl>(&decl)};
4862 bool isCXXMethodDecl{nullptr != methodDecl};
4863 const bool isClassTemplateSpec{isCXXMethodDecl and isa<ClassTemplateSpecializationDecl>(methodDecl->getParent())};
4864 const bool requiresComment{isCXXMethodDecl and not methodDecl->isUserProvided() and
4865 not methodDecl->isExplicitlyDefaulted()};
4866 // [expr.prim.lambda.closure] p7 consteval/constexpr are obtained from the call operator
4867 const bool isLambdaStaticInvoker{isCXXMethodDecl and methodDecl->isLambdaStaticInvoker()};
4868 const FunctionDecl& constExprDecl{not isLambdaStaticInvoker ? decl
4869 : *methodDecl->getParent()->getLambdaCallOperator()};
4870 const auto desugaredReturnType = GetType(GetDesugarReturnType(decl));
4871
4872 if(methodDecl) {
4873 if(requiresComment) {
4875 }
4876
4877 isLambda = methodDecl->getParent()->isLambda();
4878 isFirstCxxMethodDecl = (nullptr == methodDecl->getPreviousDecl());
4879 }
4880
4881 // types of conversion decls can be invalid to type at this place. So introduce a using
4882 if(isa<CXXConversionDecl>(decl) and TypeContainsSubType<PointerType, FunctionProtoType>(desugaredReturnType)) {
4884 kwUsingSpace, BuildRetTypeName(decl), hlpAssing, GetName(desugaredReturnType));
4885 }
4886
4887 if(isCXXMethodDecl and decl.isOutOfLine()) {
4888 if(const auto* parent = methodDecl->getParent()) {
4889 if(const auto* outerClasTemplateDecl = parent->getDescribedClassTemplate()) {
4890 InsertTemplateParameters(*outerClasTemplateDecl->getTemplateParameters());
4891 }
4892 }
4893 }
4894
4895 if(decl.isTemplated()) {
4896 if(decl.getDescribedTemplate()) {
4897 InsertTemplateParameters(*decl.getDescribedTemplate()->getTemplateParameters());
4898 }
4899
4900 } else if(decl.isFunctionTemplateSpecialization() or (isClassTemplateSpec and decl.isOutOfLine() and
4901 (decl.getLexicalDeclContext() != methodDecl->getParent()))) {
4903 }
4904
4905 InsertAttributes(decl.attrs());
4906
4907 if(not decl.isFunctionTemplateSpecialization() or (isCXXMethodDecl and isFirstCxxMethodDecl)) {
4908 if(not decl.isOutOfLine() or (decl.getStorageClass() == SC_Extern)) {
4910 }
4911
4912 // [class.free]: Any allocation function for a class T is a static member (even if not explicitly declared
4913 // static). (https://eel.is/c++draft/class.free#1)
4914 // However, Clang does not add `static` to `getStorageClass` so this needs to be check independently.
4915 if(isCXXMethodDecl and not decl.isOutOfLine()) {
4916 // GetStorageClassAsStringWithSpace already carries static, if the method was marked so explicitly
4917 if((not IsStaticStorageClass(methodDecl)) and (methodDecl->isStatic())) {
4919 }
4920 }
4921 }
4922
4923 if(Decl::FOK_None != decl.getFriendObjectKind()) {
4925 }
4926
4927 if(decl.isInlined()) {
4929 }
4930
4931 if(methodDecl and isFirstCxxMethodDecl) {
4932 if(methodDecl->isVirtual()) {
4934 }
4935
4936 const auto exspec = ExplicitSpecifier::getFromDecl(methodDecl);
4937
4938 if(const auto* expr = exspec.getExpr()) {
4940
4942 [&] {
4943 switch(exspec.getKind()) {
4944 case ExplicitSpecKind::Unresolved: InsertArg(expr); break;
4945 case ExplicitSpecKind::ResolvedFalse: mOutputFormatHelper.Append(kwFalse); break;
4946 case ExplicitSpecKind::ResolvedTrue: mOutputFormatHelper.Append("true"sv); break;
4947 }
4948 },
4949 AddSpaceAtTheEnd::Yes);
4950
4951 } else if(exspec.isExplicit()) {
4953 }
4954 }
4955
4956 if(constExprDecl.isConstexpr()) {
4957 const bool skipConstexpr{isLambda and not isa<CXXConversionDecl>(constExprDecl)};
4958 // Special treatment for a conversion operator in a captureless lambda. It appears that if the call operator
4959 // is consteval the conversion operator must be as well, otherwise it cannot take the address of the invoke
4960 // function.
4961 const bool isConversionOpWithConstevalCallOp{[&]() {
4962 if(methodDecl) {
4963 if(const auto callOp = methodDecl->getParent()->getLambdaCallOperator()) {
4964 return callOp->isConsteval();
4965 }
4966 }
4967
4968 return false;
4969 }()};
4970
4971 if(not isConversionOpWithConstevalCallOp and constExprDecl.isConstexprSpecified()) {
4972 if(skipConstexpr) {
4974 }
4975
4977
4978 if(skipConstexpr) {
4980 }
4981
4982 } else if(isConversionOpWithConstevalCallOp or constExprDecl.isConsteval()) {
4984 }
4985 }
4986
4987 // temporary output to be able to handle a return value of array reference
4988 OutputFormatHelper outputFormatHelper{};
4989
4990 if(methodDecl) {
4991 if(not isFirstCxxMethodDecl or InsertNamespace() and decl.getQualifier()) {
4992 CodeGeneratorVariant cg{outputFormatHelper};
4993 cg->InsertNamespace(decl.getQualifier());
4994
4995 // This comes from a using Base::SomeFunc
4996 } else if(not isFirstCxxMethodDecl or InsertNamespace() and not decl.getQualifier()) {
4997 const auto* parent = methodDecl->getParent();
4998 outputFormatHelper.Append(parent->getName(), "::"sv);
4999 }
5000 }
5001
5002 if(not isa<CXXConversionDecl>(decl)) {
5003 if(isa<CXXConstructorDecl>(decl) or isa<CXXDestructorDecl>(decl)) {
5004 if(methodDecl) {
5005 if(isa<CXXDestructorDecl>(decl)) {
5006 outputFormatHelper.Append('~');
5007 }
5008
5009 outputFormatHelper.Append(GetName(*methodDecl->getParent()));
5010 }
5011
5012 } else {
5013 outputFormatHelper.Append(GetName(decl));
5014 }
5015
5016 if(isFirstCxxMethodDecl and decl.isFunctionTemplateSpecialization()) {
5017 CodeGeneratorVariant codeGenerator{outputFormatHelper};
5018 codeGenerator->InsertTemplateArgs(decl);
5019 }
5020
5021 outputFormatHelper.Append('(');
5022 }
5023
5024 // if a CXXInheritedCtorDecl was passed as a pointer us this to get the parameters from.
5025 if(cxxInheritedCtorDecl) {
5026 outputFormatHelper.AppendParameterList(cxxInheritedCtorDecl->parameters(),
5027 OutputFormatHelper::NameOnly::No,
5028 OutputFormatHelper::GenMissingParamName::Yes);
5029 } else {
5030 // The static invoker needs parameter names to forward parameters to the call operator even when the call
5031 // operator doesn't care about them.
5032 const OutputFormatHelper::GenMissingParamName genMissingParamName{
5033 isLambdaStaticInvoker ? OutputFormatHelper::GenMissingParamName::Yes
5034 : OutputFormatHelper::GenMissingParamName::No};
5035
5036 outputFormatHelper.AppendParameterList(
5037 decl.parameters(), OutputFormatHelper::NameOnly::No, genMissingParamName);
5038
5039 if(GetInsightsOptions().UseShow2C and not decl.isVariadic() and decl.param_empty()) {
5040 outputFormatHelper.Append("void"sv);
5041 }
5042 }
5043
5044 if(decl.isVariadic()) {
5045 outputFormatHelper.Append(", ..."sv);
5046 }
5047
5048 outputFormatHelper.Append(')');
5049
5050 if(not isa<CXXConstructorDecl>(decl) and not isa<CXXDestructorDecl>(decl)) {
5051 if(isa<CXXConversionDecl>(decl)) {
5052 const std::string typeName{TypeContainsSubType<PointerType, FunctionProtoType>(desugaredReturnType)
5053 ? BuildRetTypeName(decl)
5054 : GetName(desugaredReturnType)};
5055
5056 mOutputFormatHelper.Append(kwOperatorSpace, typeName, " ("sv, outputFormatHelper.GetString());
5057 } else {
5058 mOutputFormatHelper.Append(GetTypeNameAsParameter(desugaredReturnType, outputFormatHelper.GetString()));
5059 }
5060 } else {
5061 mOutputFormatHelper.Append(outputFormatHelper);
5062 }
5063
5065
5066 if(methodDecl) {
5067 if(methodDecl->isVolatile()) {
5069 }
5070
5071 if(methodDecl->hasAttr<FinalAttr>()) {
5073 }
5074 }
5075
5076 switch(decl.getType()->getAs<FunctionProtoType>()->getRefQualifier()) {
5077 case RQ_None: break;
5078 case RQ_LValue: mOutputFormatHelper.Append(" &"sv); break;
5079 case RQ_RValue: mOutputFormatHelper.Append(" &&"sv); break;
5080 }
5081
5083
5084 // insert the trailing requires-clause, if any. In case, this is a template then we already inserted the
5085 // template requires-clause during creation of the template head.
5087
5088 if(decl.isPureVirtual()) {
5089 mOutputFormatHelper.Append(" = 0"sv);
5090 }
5091
5092 if(decl.isDeleted()) {
5094 if(auto* delInfo = decl.getDefalutedOrDeletedInfo()) {
5095 WrapInParens([&]() { InsertArg(delInfo->getDeletedMessage()); }, AddSpaceAtTheEnd::No);
5096 } else {
5098 }
5099
5100 } else if(decl.isDefaulted()) {
5102 }
5103}
5104//-----------------------------------------------------------------------------
5105
5107{
5108 const bool requiresCurlys{not isa<InitListExpr>(stmt) and not isa<ParenExpr>(stmt) and
5109 not isa<CXXDefaultInitExpr>(stmt)};
5110
5111 if(requiresCurlys) {
5113 }
5114
5115 InsertArg(stmt);
5116
5117 if(requiresCurlys) {
5119 }
5120}
5121//-----------------------------------------------------------------------------
5122
5124 void_func_ref lambda,
5125 const AddSpaceAtTheEnd addSpaceAtTheEnd)
5126{
5127 if(BraceKind::Curlys == braceKind) {
5129 } else {
5131 }
5132
5133 lambda();
5134
5135 if(BraceKind::Curlys == braceKind) {
5137 } else {
5139 }
5140
5141 if(AddSpaceAtTheEnd::Yes == addSpaceAtTheEnd) {
5143 }
5144}
5145//-----------------------------------------------------------------------------
5146
5147void CodeGenerator::WrapInCompoundIfNeeded(const Stmt* stmt, const AddNewLineAfter addNewLineAfter)
5148{
5149 const bool hasNoCompoundStmt = not(isa<CompoundStmt>(stmt) or isa<AttributedStmt>(stmt));
5150
5151 if(hasNoCompoundStmt) {
5153 }
5154
5155 if(not isa<NullStmt>(stmt)) {
5156 InsertArg(stmt);
5157
5158 const bool isAttrWithCompound{[&] {
5159 auto* attrStmt = dyn_cast_or_null<AttributedStmt>(stmt);
5160 return attrStmt and isa<CompoundStmt>(attrStmt->getSubStmt());
5161 }()};
5162
5163 // Add semi-colon if necessary. A do{} while does already add one.
5164 if(IsStmtRequiringSemi<IfStmt, CompoundStmt, NullStmt, WhileStmt, DoStmt>(stmt) and not isAttrWithCompound) {
5166 }
5167 }
5168
5169 if(hasNoCompoundStmt) {
5170 mOutputFormatHelper.CloseScope(OutputFormatHelper::NoNewLineBefore::Yes);
5171 }
5172
5173 const bool addNewLine = (AddNewLineAfter::Yes == addNewLineAfter);
5174 if(addNewLine or (hasNoCompoundStmt and addNewLine)) {
5176 } else if(not addNewLine or (hasNoCompoundStmt and not addNewLine)) {
5178 }
5179}
5180//-----------------------------------------------------------------------------
5181
5182void CodeGenerator::WrapInParens(void_func_ref lambda, const AddSpaceAtTheEnd addSpaceAtTheEnd)
5183{
5184 WrapInParensOrCurlys(BraceKind::Parens, lambda, addSpaceAtTheEnd);
5185}
5186//-----------------------------------------------------------------------------
5187
5189 void_func_ref lambda,
5190 const AddSpaceAtTheEnd addSpaceAtTheEnd)
5191{
5192 if(needsParens) {
5193 WrapInParensOrCurlys(BraceKind::Parens, lambda, addSpaceAtTheEnd);
5194 } else {
5195 lambda();
5196 }
5197}
5198//-----------------------------------------------------------------------------
5199
5201 void_func_ref lambda,
5202 const AddSpaceAtTheEnd addSpaceAtTheEnd)
5203{
5204 if(needsParens) {
5205 WrapInParensOrCurlys(BraceKind::Curlys, lambda, addSpaceAtTheEnd);
5206 } else {
5207 lambda();
5208 }
5209}
5210//-----------------------------------------------------------------------------
5211
5212void CodeGenerator::WrapInCurlys(void_func_ref lambda, const AddSpaceAtTheEnd addSpaceAtTheEnd)
5213{
5214 WrapInParensOrCurlys(BraceKind::Curlys, lambda, addSpaceAtTheEnd);
5215}
5216//-----------------------------------------------------------------------------
5217
5218void CodeGenerator::InsertArg(const BindingDecl*)
5219{
5220 // We ignore this here in the global level. In some cases a BindingDecl appears _before_ the DecompositionDecl
5221 // which leads to invalid code. See StructuredBindingsHandler3Test.cpp.
5222}
5223//-----------------------------------------------------------------------------
5224
5226{
5227 const auto* bindingStmt = stmt->getBinding();
5228
5229 // In a dependent context we have no binding and with that no type. Leave this as it is, we are looking at a
5230 // primary template here.
5231 RETURN_IF(not bindingStmt);
5232
5233 // Assume that we are looking at a builtin type. We have to construct the variable declaration information.
5234 auto type = stmt->getType();
5235
5236 // If we have a holding var we are looking at a user defined type like tuple and those the defaults from above
5237 // are wrong. This type contains the variable declaration so we insert this.
5238 if(const auto* holdingVar = stmt->getHoldingVar()) {
5239 // Initial paper: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0144r0.pdf
5240
5241 // The type of the binding depends on the initializer. In case the initializer is an lvalue we get a T&,
5242 // otherwise a T&&. We typically look at an lvalue if the decomposition declaration was auto& [a,b]. Note
5243 // the & here We have a rvalue in case the decomposition declaration was auto [a,b]. Note no reference. The
5244 // standard std::get returns a lvalue reference in case e in get(e) is an lvalue, otherwise it returns an
5245 // rvalue reference because then the call is get(std::move(e))
5246 type = holdingVar->getType().getCanonicalType();
5247
5248 bindingStmt = holdingVar->getAnyInitializer();
5249
5250 } else if(not type->isLValueReferenceType()) {
5251 type = stmt->getASTContext().getLValueReferenceType(type);
5252 }
5253
5254 InsertAttributes(stmt->attrs());
5255
5256 mOutputFormatHelper.Append(GetQualifiers(*dyn_cast_or_null<VarDecl>(stmt->getDecomposedDecl())),
5257 GetTypeNameAsParameter(type, GetName(*stmt)),
5258 hlpAssing);
5259
5260 InsertArg(bindingStmt);
5261
5263}
5264//-----------------------------------------------------------------------------
5265
5266void StructuredBindingsCodeGenerator::InsertDecompositionBindings(const DecompositionDecl& decompositionDeclStmt)
5267{
5268 for(const auto* bindingDecl : decompositionDeclStmt.bindings()) {
5269 InsertArg(bindingDecl);
5270 }
5271}
5272//-----------------------------------------------------------------------------
5273
5275{
5276 const auto name = GetName(*stmt);
5277
5279
5280 if(name.empty()) {
5281 mOutputFormatHelper.Append(mVarName);
5282 } else {
5283 InsertTemplateArgs(*stmt);
5284 }
5285}
5286//-----------------------------------------------------------------------------
5287
5288void LambdaCodeGenerator::InsertArg(const CXXThisExpr*)
5289{
5292
5293 } else {
5295 }
5296}
5297//-----------------------------------------------------------------------------
5298
5299} // 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