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