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