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