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  }(castKind, hideImplicitCasts, stmt->isXValue() or ShowXValueCasts());
2332 
2333  if(not isMatchingCast) {
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  // For a CStyleCast we get an AST like this:
2348  //
2349  // CStyleCastExpr 0x13205cdc0 'uint32_t':'unsigned int' <NoOp>
2350  // `-ImplicitCastExpr 0x13205cda8 'uint32_t':'unsigned int' <IntegralCast> part_of_explicit_cast
2351  // `-CStyleCastExpr 0x13205cd70 'uint16_t':'unsigned short' <NoOp>
2352  //
2353  // Without filtering the `uint32_t` cast appears twice. The code below takes that into account and skips the
2354  // `ImplicitCastExpr` if the sub-expression is a `CStyleCastExpr`.
2355  if(isa<CStyleCastExpr>(subExpr) or isa<CXXNamedCastExpr>(subExpr)) {
2356  InsertArg(subExpr);
2357  } else {
2358  InsertArg(stmt->IgnoreCasts());
2359  }
2360 
2361  } else {
2362  auto castName{GetCastName(castKind)};
2363  const QualType castDestType{[&] {
2364  const auto type{stmt->getType()};
2365 
2366  // In at least the case a structured bindings the compiler adds xvalue casts but the && is missing to
2367  // make it valid C++.
2368  if(VK_XValue == stmt->getValueKind()) {
2369  return GetGlobalAST().getRValueReferenceType(type.getCanonicalType());
2370  } else if(type->isDependentType()) { // In case of a dependent type the canonical type doesn't know the
2371  // parameters name.
2372  return type;
2373  }
2374 
2375  return type.getCanonicalType();
2376  }()};
2377 
2378  FormatCast(castName, castDestType, subExpr, castKind);
2379  }
2380 }
2381 //-----------------------------------------------------------------------------
2382 
2383 void CodeGenerator::InsertArg(const DeclRefExpr* stmt)
2384 {
2385  if(const auto* tmplObjParam = dyn_cast_or_null<TemplateParamObjectDecl>(stmt->getDecl())) {
2386  mOutputFormatHelper.Append(GetName(*tmplObjParam));
2387 
2388  } else if(const auto* vd = dyn_cast_or_null<VarDecl>(stmt->getDecl());
2389  GetInsightsOptions().UseShow2C and IsReferenceType(vd)) {
2390  const auto* init = vd->getInit();
2391 
2392  if(const auto* dref = dyn_cast_or_null<DeclRefExpr>(init)) {
2393  mOutputFormatHelper.Append(GetName(*dref));
2394  return;
2395 
2396  } else if(const auto* inList = dyn_cast_or_null<InitListExpr>(init)) {
2397  mOutputFormatHelper.Append(GetName(*dyn_cast_or_null<DeclRefExpr>(inList->getInit(0))));
2398  return;
2399  }
2400  }
2401 
2402  if(const auto* ctx = stmt->getDecl()->getDeclContext(); not ctx->isFunctionOrMethod() and
2403  not isa<NonTypeTemplateParmDecl>(stmt->getDecl()) and
2404  not GetInsightsOptions().UseShow2C) {
2405  if(const auto* qualifier = stmt->getQualifier();
2406  qualifier and (qualifier->getKind() == NestedNameSpecifier::SpecifierKind::Global)) {
2407  // According to
2408  // https://clang.llvm.org/doxygen/classclang_1_1NestedNameSpecifier.html#ac707a113605ed4283684b8c05664eb6f
2409  // the global specifier is not stored.
2410  mOutputFormatHelper.Append("::"sv, GetPlainName(*stmt));
2411 
2412  } else {
2413  OutputFormatHelper ofm{};
2414  CodeGeneratorVariant codeGenerator{ofm};
2415 
2416  codeGenerator->ParseDeclContext(ctx);
2417 
2418  mOutputFormatHelper.Append(ScopeHandler::RemoveCurrentScope(ofm.GetString()), GetPlainName(*stmt));
2419  }
2420 
2421  } else {
2422  mOutputFormatHelper.Append(GetName(*stmt));
2423  }
2424 
2425  if(const auto* varTmplSpecDecl = dyn_cast_or_null<VarTemplateSpecializationDecl>(stmt->getDecl())) {
2426  InsertTemplateArgs(*varTmplSpecDecl);
2427  } else {
2428  InsertTemplateArgs(*stmt);
2429  }
2430 }
2431 //-----------------------------------------------------------------------------
2432 
2433 void CodeGenerator::InsertArg(const CompoundStmt* stmt)
2434 {
2435  mOutputFormatHelper.OpenScope();
2436  mLifeTimeTracker.StartScope(isa_and_nonnull<FunctionDecl>(mLastDecl));
2437 
2438  // prevent nested CompoundStmt's to insert a return on each leave. Only insert it before closing the most outer
2439  // one.
2440  const bool requiresImplicitReturnZero{std::exchange(mRequiresImplicitReturnZero, false)};
2441 
2442  HandleCompoundStmt(stmt);
2443 
2444  if(requiresImplicitReturnZero) {
2445  InsertArg(Return(Int32(0)));
2446 
2447  if(not mSkipSemi) {
2448  InsertArg(mkNullStmt());
2449  }
2450  }
2451 
2452  mSkipSemi = mLifeTimeTracker.EndScope(mOutputFormatHelper, isa_and_nonnull<ReturnStmt>(mLastStmt));
2453 
2454  mOutputFormatHelper.CloseScope(OutputFormatHelper::NoNewLineBefore::Yes);
2455 }
2456 //-----------------------------------------------------------------------------
2457 
2458 template<typename... Args>
2459 static bool IsStmtRequiringSemi(const Stmt* stmt)
2460 {
2461  return (... and not isa<Args>(stmt));
2462 }
2463 //-----------------------------------------------------------------------------
2464 
2465 void CodeGenerator::HandleCompoundStmt(const CompoundStmt* stmt)
2466 {
2467  for(const auto* item : stmt->body()) {
2468  InsertArg(item);
2469 
2470  // Skip inserting a semicolon, if this is a LambdaExpr and out stack is empty. This addresses a special case
2471  // #344.
2472  const bool skipSemiForLambda{mLambdaStack.empty() and isa<LambdaExpr>(item)};
2473 
2474  if(IsStmtRequiringSemi<IfStmt,
2475  NullStmt,
2476  ForStmt,
2477  DeclStmt,
2478  WhileStmt,
2479  DoStmt,
2480  CXXForRangeStmt,
2481  SwitchStmt,
2482  CXXTryStmt,
2483  CppInsightsCommentStmt>(item) and
2484  InsertSemi() and not skipSemiForLambda and not mSkipSemi) {
2485  mOutputFormatHelper.AppendSemiNewLine();
2486  }
2487 
2488  mSkipSemi = false;
2489  }
2490 }
2491 //-----------------------------------------------------------------------------
2492 
2493 void CodeGenerator::InsertIfOrSwitchInitVariables(same_as_any_of<const IfStmt, const SwitchStmt> auto* stmt)
2494 {
2495  if(const auto* conditionVar = stmt->getConditionVariable()) {
2496  InsertArg(conditionVar);
2497  }
2498 
2499  if(const auto* init = stmt->getInit()) {
2500  InsertArg(init);
2501 
2502  if(not isa<DeclStmt>(init)) {
2503  mOutputFormatHelper.AppendSemiNewLine();
2504  }
2505  }
2506 }
2507 //-----------------------------------------------------------------------------
2508 
2509 void CodeGenerator::InsertArg(const IfStmt* stmt)
2510 {
2511  const bool hasInit{stmt->getInit() or stmt->getConditionVariable()};
2512 
2513  if(hasInit) {
2514  mOutputFormatHelper.OpenScope();
2515 
2516  InsertIfOrSwitchInitVariables(stmt);
2517  }
2518 
2519  mOutputFormatHelper.Append("if"sv, ValueOrDefault(stmt->isConstexpr(), kwSpaceConstExpr));
2520 
2521  WrapInParensIfNeeded(
2522  not stmt->isConsteval(),
2523  [&]() {
2524  mShowConstantExprValue = ShowConstantExprValue::Yes;
2525 
2526  InsertArg(stmt->getCond());
2527 
2528  mShowConstantExprValue = ShowConstantExprValue::No;
2529  },
2530  AddSpaceAtTheEnd::Yes);
2531 
2532  mOutputFormatHelper.Append(ValueOrDefault(stmt->isNegatedConsteval(), " !"sv),
2533  ValueOrDefault(stmt->isConsteval(), kwSpaceConstEvalSpace));
2534 
2535  WrapInCompoundIfNeeded(stmt->getThen(), AddNewLineAfter::No);
2536 
2537  // else
2538  if(const auto* elsePart = stmt->getElse()) {
2539  mOutputFormatHelper.Append(
2540  "else "sv,
2542 
2543  WrapInCompoundIfNeeded(elsePart, AddNewLineAfter::No);
2544  }
2545 
2546  // Add newline after last closing curly (either from if or else if).
2547  mOutputFormatHelper.AppendNewLine();
2548 
2549  if(hasInit) {
2550  mOutputFormatHelper.CloseScope();
2551  mOutputFormatHelper.AppendNewLine();
2552  }
2553 
2554  // one blank line after statement
2555  mOutputFormatHelper.AppendNewLine();
2556 }
2557 //-----------------------------------------------------------------------------
2558 
2559 class ContinueASTTransformer : public StmtVisitor<ContinueASTTransformer>
2560 {
2561  Stmt* mPrevStmt{};
2562  std::string_view mContinueLabel{};
2563 
2564 public:
2565  bool found{};
2566 
2567  ContinueASTTransformer(Stmt* stmt, std::string_view continueLabel)
2568  : mPrevStmt{stmt}
2569  , mContinueLabel{continueLabel}
2570  {
2571  Visit(stmt);
2572  }
2573 
2574  void Visit(Stmt* stmt)
2575  {
2576  if(stmt) {
2578  }
2579  }
2580 
2581  void VisitContinueStmt(ContinueStmt* stmt)
2582  {
2583  found = true;
2584 
2585  ReplaceNode(mPrevStmt, stmt, Goto(mContinueLabel));
2586  }
2587 
2588  void VisitStmt(Stmt* stmt)
2589  {
2590  auto* tmp = mPrevStmt;
2591  mPrevStmt = stmt;
2592 
2593  for(auto* child : stmt->children()) {
2594  Visit(child);
2595  }
2596 
2597  mPrevStmt = tmp;
2598  }
2599 };
2600 //-----------------------------------------------------------------------------
2601 
2602 void CodeGenerator::InsertArg(const ForStmt* stmt)
2603 {
2604  // https://github.com/vtjnash/clang-ast-builder/blob/master/AstBuilder.cpp
2605  // http://clang-developers.42468.n3.nabble.com/Adding-nodes-to-Clang-s-AST-td4054800.html
2606  // https://stackoverflow.com/questions/30451485/how-to-clone-or-create-an-ast-stmt-node-of-clang/38899615
2607 
2608  if(GetInsightsOptions().UseAltForSyntax) {
2609  auto* rwStmt = const_cast<ForStmt*>(stmt);
2610  const auto& ctx = GetGlobalAST();
2611  StmtsContainer bodyStmts{};
2612 
2613  auto continueLabel = MakeLineColumnName(ctx.getSourceManager(), stmt->getBeginLoc(), "__continue_"sv);
2614  const bool insertLabel = ContinueASTTransformer{rwStmt->getBody(), continueLabel}.found;
2615 
2616  bodyStmts.AddBodyStmts(rwStmt->getBody());
2617 
2618  // Build and insert the continue goto label
2619  if(insertLabel) {
2620  bodyStmts.Add(Label(continueLabel));
2621  }
2622 
2623  bodyStmts.Add(rwStmt->getInc());
2624 
2625  auto* condition = [&]() -> Expr* {
2626  if(rwStmt->getCond()) {
2627  return rwStmt->getCond();
2628  }
2629 
2630  return Bool(true);
2631  }();
2632 
2633  auto* outerBody = mkCompoundStmt(bodyStmts, stmt->getBeginLoc(), stmt->getEndLoc());
2634  auto* whileStmt = WhileStmt::Create(
2635  ctx, nullptr, condition, outerBody, stmt->getBeginLoc(), stmt->getLParenLoc(), stmt->getRParenLoc());
2636 
2637  StmtsContainer outerScopeStmts{};
2638  outerScopeStmts.Add(rwStmt->getInit());
2639  outerScopeStmts.Add(whileStmt);
2640 
2641  auto* outerScopeBody = mkCompoundStmt(outerScopeStmts, stmt->getBeginLoc(), stmt->getEndLoc());
2642 
2643  InsertArg(outerScopeBody);
2644  mOutputFormatHelper.AppendNewLine();
2645 
2646  } else {
2647  {
2648  // We need to handle the case that a lambda is used in the init-statement of the for-loop.
2649  LAMBDA_SCOPE_HELPER(VarDecl);
2650 
2651  mOutputFormatHelper.Append("for"sv);
2652 
2653  WrapInParens(
2654  [&]() {
2655  if(const auto* init = stmt->getInit()) {
2656  MultiStmtDeclCodeGenerator codeGenerator{
2657  mOutputFormatHelper, mLambdaStack, InsertVarDecl(nullptr)};
2658  codeGenerator.InsertArg(init);
2659 
2660  } else {
2661  mOutputFormatHelper.Append("; "sv);
2662  }
2663 
2664  InsertArg(stmt->getCond());
2665  mOutputFormatHelper.Append("; "sv);
2666 
2667  InsertArg(stmt->getInc());
2668  },
2669  AddSpaceAtTheEnd::Yes);
2670  }
2671 
2672  WrapInCompoundIfNeeded(stmt->getBody(), AddNewLineAfter::Yes);
2673  }
2674 
2675  mOutputFormatHelper.AppendNewLine();
2676 }
2677 //-----------------------------------------------------------------------------
2678 
2679 static bool IsConstQualifiedType(QualType type)
2680 {
2681  if(not type.isNull()) {
2682  if(auto* typePtr = type.getTypePtrOrNull()) {
2683  if(auto pointee = typePtr->getPointeeType(); not pointee.isNull()) {
2684  return pointee.isConstQualified();
2685  }
2686  }
2687  }
2688 
2689  return false;
2690 }
2691 //-----------------------------------------------------------------------------
2692 
2693 void CodeGenerator::InsertArg(const CStyleCastExpr* stmt)
2694 {
2695  const auto castKind = stmt->getCastKind();
2696  const QualType castDestType = stmt->getType().getCanonicalType();
2697  const auto castName = GetCastName(
2698  castKind, IsConstQualifiedType(castDestType) != IsConstQualifiedType(stmt->getSubExpr()->getType()));
2699 
2700  FormatCast(castName, castDestType, stmt->getSubExpr(), castKind);
2701 }
2702 //-----------------------------------------------------------------------------
2703 
2704 void CodeGenerator::InsertArg(const CXXNewExpr* stmt)
2705 {
2706  const auto noEmptyInitList = mNoEmptyInitList;
2707  FinalAction _{[&] { mNoEmptyInitList = noEmptyInitList; }};
2708  mNoEmptyInitList = GetInsightsOptions().UseShow2C ? NoEmptyInitList::Yes : NoEmptyInitList::No;
2709 
2710  mOutputFormatHelper.Append("new "sv);
2711 
2712  if(stmt->getNumPlacementArgs()) {
2713  /* we have a placement new */
2714 
2715  WrapInParens([&]() {
2716  ForEachArg(stmt->placement_arguments(), [&](const auto& placementArg) { InsertArg(placementArg); });
2717  });
2718  }
2719 
2720  if(const auto* ctorExpr = stmt->getConstructExpr()) {
2721  InsertArg(ctorExpr);
2722 
2723  } else {
2724  auto name = GetName(stmt->getAllocatedType());
2725 
2726  // Special handling for arrays. They differ from one to more dimensions.
2727  if(stmt->isArray()) {
2728  OutputFormatHelper ofm{};
2729  CodeGeneratorVariant codeGenerator{ofm};
2730 
2731  ofm.Append('[');
2732  codeGenerator->InsertArg(stmt->getArraySize().value());
2733  ofm.Append(']');
2734 
2735  // In case of multi dimension the first dimension is the getArraySize() while the others are part of the
2736  // type included in GetName(...).
2737  if(Contains(name, "["sv)) {
2738  InsertBefore(name, "["sv, ofm.GetString());
2739  } else {
2740  // here we have the single dimension case, the dimension is not part of GetName, so add it.
2741  name.append(ofm);
2742  }
2743  }
2744 
2745  mOutputFormatHelper.Append(name);
2746 
2747  if(stmt->hasInitializer()) {
2748  InsertCurlysIfRequired(stmt->getInitializer());
2749  }
2750  }
2751 }
2752 //-----------------------------------------------------------------------------
2753 
2754 void CodeGenerator::InsertArg(const MaterializeTemporaryExpr* stmt)
2755 {
2756  // At least in case of a ternary operator wrapped inside a MaterializeTemporaryExpr parens are necessary
2757  const auto* temporary = stmt->getSubExpr();
2758  WrapInParensIfNeeded(isa_and_nonnull<ConditionalOperator>(temporary), [&] { InsertArg(temporary); });
2759 }
2760 //-----------------------------------------------------------------------------
2761 
2762 void CodeGenerator::InsertArg(const CXXOperatorCallExpr* stmt)
2763 {
2764  LAMBDA_SCOPE_HELPER(OperatorCallExpr);
2765 
2766  const auto* callee = dyn_cast_or_null<DeclRefExpr>(stmt->getCallee()->IgnoreImpCasts());
2767  const bool isCXXMethod{callee and isa<CXXMethodDecl>(callee->getDecl())};
2768 
2769  if(2 == stmt->getNumArgs()) {
2770  auto getArg = [&](unsigned idx) {
2771  const auto* arg = stmt->getArg(idx);
2772 
2773  // In show all casts mode don't filter this. It shows how the compiler adds const to arguments, if the
2774  // argument is non-const but the parameter demands a const object
2775  if(not GetInsightsOptions().ShowAllImplicitCasts) {
2776  arg = arg->IgnoreImpCasts();
2777  }
2778 
2779  return dyn_cast_or_null<DeclRefExpr>(arg);
2780  };
2781 
2782  const auto* param1 = getArg(0);
2783  const auto* param2 = getArg(1);
2784 
2785  if(callee and param1 and param2) {
2786  const std::string replace = [&]() {
2787  // If the argument is a variable template, add the template arguments to the parameter name.
2788  auto nameWithTmplArguments = [](const auto param) {
2789  return FormatVarTemplateSpecializationDecl(param->getDecl(), GetName(*param));
2790  };
2791 
2792  if(isa<CXXMethodDecl>(callee->getDecl())) {
2793  return StrCat(nameWithTmplArguments(param1),
2794  "."sv,
2795  GetName(*callee),
2796  "("sv,
2797  nameWithTmplArguments(param2),
2798  ")"sv);
2799  } else {
2800  return StrCat(GetName(*callee),
2801  "("sv,
2802  nameWithTmplArguments(param1),
2803  ", "sv,
2804  nameWithTmplArguments(param2),
2805  ")"sv);
2806  }
2807  }();
2808 
2809  mOutputFormatHelper.Append(replace);
2810 
2811  return;
2812  }
2813  }
2814 
2815  auto cb = stmt->child_begin();
2816  const auto* fallbackArg0 = stmt->getArg(0);
2817 
2818  // arg0 := operator
2819  // skip arg0
2820  std::advance(cb, 1);
2821 
2822  const auto* arg1 = *cb;
2823 
2824  std::advance(cb, 1);
2825 
2826  // operators in a namespace but outside a class so operator goes first
2827  if(not isCXXMethod) {
2828  // happens for UnresolvedLooupExpr
2829  if(not callee) {
2830  if(const auto* adl = dyn_cast_or_null<UnresolvedLookupExpr>(stmt->getCallee())) {
2831  InsertArg(adl);
2832  }
2833  } else {
2834  mOutputFormatHelper.Append(GetName(*callee));
2835  }
2836 
2837  mOutputFormatHelper.Append("("sv);
2838  }
2839 
2840  // insert the arguments
2841  if(isa<DeclRefExpr>(fallbackArg0)) {
2842  InsertArgWithParensIfNeeded(fallbackArg0);
2843 
2844  } else {
2845  InsertArgWithParensIfNeeded(arg1);
2846  }
2847 
2848  // if it is a class operator the operator follows now
2849  if(isCXXMethod) {
2850  const OverloadedOperatorKind opKind = stmt->getOperator();
2851 
2852  const std::string_view operatorKw{ValueOr((OO_Coawait == opKind), kwOperatorSpace, kwOperator)};
2853 
2854  mOutputFormatHelper.Append("."sv, operatorKw, getOperatorSpelling(opKind), "("sv);
2855  }
2856 
2857  // consume all remaining arguments
2858  const auto childRange = llvm::make_range(cb, stmt->child_end());
2859 
2860  // at least the call-operator can have more than 2 parameters
2861  ForEachArg(childRange, [&](const auto& child) {
2862  if(not isCXXMethod) {
2863  // in global operators we need to separate the two parameters by comma
2864  mOutputFormatHelper.Append(", "sv);
2865  }
2866 
2867  InsertArg(child);
2868  });
2869 
2870  mOutputFormatHelper.Append(')');
2871 }
2872 //-----------------------------------------------------------------------------
2873 
2874 void CodeGenerator::InsertArg(const LambdaExpr* stmt)
2875 {
2876  if(not mLambdaStack.empty()) {
2877  const bool insertName{mLambdaStack.back().insertName()};
2878 
2879  HandleLambdaExpr(stmt, mLambdaStack.back());
2880 
2881  if(insertName) {
2882  mOutputFormatHelper.Append(GetLambdaName(*stmt));
2883  }
2884 
2885  } else if(LambdaInInitCapture::Yes == mLambdaInitCapture) {
2886  LAMBDA_SCOPE_HELPER(InitCapture);
2887  HandleLambdaExpr(stmt, mLambdaStack.back());
2888  } else {
2889  LAMBDA_SCOPE_HELPER(LambdaExpr);
2890  HandleLambdaExpr(stmt, mLambdaStack.back());
2891  }
2892 
2893  if(not mLambdaStack.empty()) {
2894  mLambdaStack.back().insertInits(mOutputFormatHelper);
2895  }
2896 }
2897 //-----------------------------------------------------------------------------
2898 
2899 void CodeGenerator::InsertArg(const CXXThisExpr* stmt)
2900 {
2901  DPrint("thisExpr: imlicit=%d %s\n", stmt->isImplicit(), GetName(GetDesugarType(stmt->getType())));
2902 
2903  mOutputFormatHelper.Append(kwThis);
2904 }
2905 //-----------------------------------------------------------------------------
2906 
2907 void CodeGenerator::InsertArg(const CXXBindTemporaryExpr* stmt)
2908 {
2909  InsertArg(stmt->getSubExpr());
2910 }
2911 //-----------------------------------------------------------------------------
2912 
2913 void CodeGenerator::InsertArg(const CXXFunctionalCastExpr* stmt)
2914 {
2915  const bool isConstructor{isa<CXXConstructExpr>(stmt->getSubExpr())};
2916  const bool isStdListInit{isa<CXXStdInitializerListExpr>(stmt->getSubExpr())};
2917  const bool isListInitialization{stmt->getLParenLoc().isInvalid()};
2918  const bool needsParens{not isConstructor and not isListInitialization and not isStdListInit};
2919 
2920  // If a constructor follows we do not need to insert the type name. This would insert it twice.
2921  if(not isConstructor and not isStdListInit) {
2922  mOutputFormatHelper.Append(GetName(stmt->getTypeAsWritten()));
2923  }
2924 
2925  WrapInParensIfNeeded(needsParens, [&] { InsertArg(stmt->getSubExpr()); });
2926 }
2927 //-----------------------------------------------------------------------------
2928 
2929 void CodeGenerator::InsertArg(const CXXBoolLiteralExpr* stmt)
2930 {
2931  mOutputFormatHelper.Append(details::ConvertToBoolString(stmt->getValue()));
2932 }
2933 //-----------------------------------------------------------------------------
2934 
2935 void CodeGenerator::InsertArg(const GNUNullExpr* /*stmt*/)
2936 {
2937  mOutputFormatHelper.Append(kwNull);
2938 }
2939 //-----------------------------------------------------------------------------
2940 
2941 void CodeGenerator::InsertArg(const CharacterLiteral* stmt)
2942 {
2943  StringStream stream{};
2944  stream.Print(*stmt);
2945 
2946  auto str = std::move(stream.str());
2947 
2948  if(str == "'\\x00'"sv) {
2949  str = "'\\0'"sv;
2950  } else if(str == "'\\xff'"sv) {
2951  str = "255"sv;
2952  }
2953 
2954  mOutputFormatHelper.Append(str);
2955 }
2956 //-----------------------------------------------------------------------------
2957 
2958 void CodeGenerator::InsertArg(const PredefinedExpr* stmt)
2959 {
2960  // Check if getFunctionName returns a valid StringLiteral. It does return a nullptr, if this PredefinedExpr is
2961  // in a UnresolvedLookupExpr. In that case, print the identifier, e.g. __func__.
2962  if(const auto* functionName = stmt->getFunctionName()) {
2963  InsertArg(functionName);
2964  } else {
2965  const auto name = PredefinedExpr::getIdentKindName(stmt->getIdentKind());
2966 
2967  mOutputFormatHelper.Append(name);
2968  }
2969 }
2970 //-----------------------------------------------------------------------------
2971 
2972 void CodeGenerator::InsertArg(const ExprWithCleanups* stmt)
2973 {
2974  mLifeTimeTracker.StartScope(false);
2975  TemporaryDeclFinder temporaryFinder{*this, not mProcessingVarDecl ? stmt->getSubExpr() : nullptr};
2976 
2977  InsertArg(stmt->getSubExpr());
2978 
2979  if(GetInsightsOptions().ShowLifetime and not mProcessingVarDecl) {
2980  mOutputFormatHelper.AppendSemiNewLine();
2981  }
2982 
2983  mSkipSemi = mLifeTimeTracker.EndScope(mOutputFormatHelper, false);
2984 }
2985 //-----------------------------------------------------------------------------
2986 
2987 std::string CodeGenerator::GetValueOfValueInit(const QualType& t)
2988 {
2989  const QualType& type = t.getCanonicalType();
2990 
2991  if(type->isScalarType()) {
2992  switch(type->getScalarTypeKind()) {
2993  case Type::STK_CPointer:
2994  case Type::STK_BlockPointer:
2995  case Type::STK_ObjCObjectPointer:
2996  case Type::STK_MemberPointer: return std::string{kwNullptr};
2997 
2998  case Type::STK_Bool: return std::string{kwFalse};
2999 
3000  case Type::STK_Integral:
3001  case Type::STK_Floating:
3002  if(const auto* bt = type->getAs<BuiltinType>()) {
3003  switch(bt->getKind()) {
3004  // Type::STK_Integral
3005  case BuiltinType::Char_U:
3006  case BuiltinType::UChar:
3007  case BuiltinType::Char_S:
3008  case BuiltinType::SChar: return "'\\0'";
3009  case BuiltinType::WChar_U:
3010  case BuiltinType::WChar_S: return "L'\\0'";
3011  case BuiltinType::Char16: return "u'\\0'";
3012  case BuiltinType::Char32: return "U'\\0'";
3013  // Type::STK_Floating
3014  case BuiltinType::Half:
3015  case BuiltinType::Float: return "0.0f";
3016  case BuiltinType::Double: return "0.0";
3017  default: break;
3018  }
3019  }
3020 
3021  break;
3022 
3023  case Type::STK_FloatingComplex:
3024  case Type::STK_IntegralComplex:
3025  if(const auto* complexType = type->getAs<ComplexType>()) {
3026  return GetValueOfValueInit(complexType->getElementType());
3027  }
3028 
3029  break;
3030 
3031  case Type::STK_FixedPoint: Error("STK_FixedPoint is not implemented"); break;
3032  }
3033 
3034  } else if(const auto* tt = dyn_cast_or_null<ConstantArrayType>(t.getTypePtrOrNull())) {
3035  const auto& elementType{tt->getElementType()};
3036  const std::string elementTypeInitValue{GetValueOfValueInit(elementType)};
3037 
3038  return FillConstantArray(tt, elementTypeInitValue, uint64_t{0});
3039  }
3040 
3041  return std::string{"0"sv};
3042 }
3043 //-----------------------------------------------------------------------------
3044 
3045 void CodeGenerator::InsertArg(const ImplicitValueInitExpr* stmt)
3046 {
3047  mOutputFormatHelper.Append(GetValueOfValueInit(stmt->getType()));
3048 }
3049 //-----------------------------------------------------------------------------
3050 
3051 void CodeGenerator::InsertArg(const CXXScalarValueInitExpr* stmt)
3052 {
3053  mOutputFormatHelper.Append(GetName(stmt->getType()), "()"sv);
3054 }
3055 //-----------------------------------------------------------------------------
3056 
3057 void CodeGenerator::InsertArg(const CXXTryStmt* stmt)
3058 {
3059  mOutputFormatHelper.AppendNewLine(kwTrySpace);
3060 
3061  InsertArg(stmt->getTryBlock());
3062 
3063  for(const auto& i : NumberIterator{stmt->getNumHandlers()}) {
3064  InsertArg(stmt->getHandler(i));
3065  }
3066 
3067  mOutputFormatHelper.AppendNewLine();
3068 }
3069 //-----------------------------------------------------------------------------
3070 
3071 void CodeGenerator::InsertArg(const CXXCatchStmt* stmt)
3072 {
3073  mOutputFormatHelper.Append(" catch"sv);
3074 
3075  WrapInParens(
3076  [&]() {
3077  if(not stmt->getCaughtType().isNull()) {
3078  mOutputFormatHelper.Append(
3079  GetTypeNameAsParameter(stmt->getCaughtType(), stmt->getExceptionDecl()->getName()));
3080  } else {
3081  mOutputFormatHelper.Append(kwElipsis);
3082  }
3083  },
3084  AddSpaceAtTheEnd::Yes);
3085 
3086  InsertArg(stmt->getHandlerBlock());
3087 }
3088 //-----------------------------------------------------------------------------
3089 
3090 void CodeGenerator::InsertArg(const CXXThrowExpr* stmt)
3091 {
3092  mOutputFormatHelper.Append("throw "sv);
3093 
3094  InsertArg(stmt->getSubExpr());
3095 }
3096 //-----------------------------------------------------------------------------
3097 
3098 void CodeGenerator::InsertArg(const ConstantExpr* stmt)
3099 {
3100  if((ShowConstantExprValue::Yes == mShowConstantExprValue) and stmt->hasAPValueResult()) {
3101  if(const auto value = stmt->getAPValueResult(); value.isInt()) {
3102  mOutputFormatHelper.Append(value.getInt());
3103  return;
3104  }
3105  }
3106 
3107  InsertArg(stmt->getSubExpr());
3108 }
3109 //-----------------------------------------------------------------------------
3110 
3111 void CodeGenerator::InsertArg(const TypeAliasDecl* stmt)
3112 {
3113  const auto& underlyingType = stmt->getUnderlyingType();
3114 
3115  LAMBDA_SCOPE_HELPER(Decltype);
3116  P0315Visitor dt{*this};
3117  dt.TraverseType(underlyingType);
3118 
3119  mOutputFormatHelper.Append(kwUsingSpace, GetName(*stmt), hlpAssing);
3120 
3121  if(auto* templateSpecializationType = underlyingType->getAs<TemplateSpecializationType>()) {
3122  const bool carriesNamespace{[&] {
3123  if(const auto tn = templateSpecializationType->getTemplateName();
3124  (TemplateName::QualifiedTemplate == tn.getKind()) or (TemplateName::DependentTemplate == tn.getKind())) {
3125  const auto* qtn = tn.getAsQualifiedTemplateName();
3126 
3127  return qtn->getQualifier() != nullptr;
3128  }
3129 
3130  return false;
3131  }()};
3132 
3133  if(const auto* elaboratedType = underlyingType->getAs<ElaboratedType>()) {
3134  if(templateSpecializationType->isSugared() and not carriesNamespace) {
3135  // do this only if the templateSpecializationType does not carry a nestedns
3136  InsertNamespace(elaboratedType->getQualifier());
3137  }
3138  }
3139 
3140  StringStream stream{};
3141  stream.Print(*templateSpecializationType);
3142 
3143  mOutputFormatHelper.Append(stream.str());
3144 
3145  InsertTemplateArgs(*templateSpecializationType);
3146  } else if(auto* dependentTemplateSpecializationType =
3147  underlyingType->getAs<DependentTemplateSpecializationType>()) {
3148 
3149  mOutputFormatHelper.Append(GetElaboratedTypeKeyword(dependentTemplateSpecializationType->getKeyword()));
3150 
3151  InsertNamespace(dependentTemplateSpecializationType->getQualifier());
3152 
3153  mOutputFormatHelper.Append(kwTemplateSpace, dependentTemplateSpecializationType->getIdentifier()->getName());
3154 
3155  InsertTemplateArgs(*dependentTemplateSpecializationType);
3156 
3157  } else {
3158  mOutputFormatHelper.Append(GetName(underlyingType));
3159  }
3160 
3161  mOutputFormatHelper.AppendSemiNewLine();
3162 }
3163 //-----------------------------------------------------------------------------
3164 
3165 void CodeGenerator::InsertArg(const TypedefDecl* stmt)
3166 {
3167  /* function pointer typedefs are special. Ease up things using "using" */
3168  // outputFormatHelper.AppendNewLine("typedef ", GetName(stmt->getUnderlyingType()), " ", GetName(*stmt),
3169  // ";");
3170  mOutputFormatHelper.AppendSemiNewLine(kwUsingSpace, GetName(*stmt), hlpAssing, GetName(stmt->getUnderlyingType()));
3171 }
3172 //-----------------------------------------------------------------------------
3173 
3174 void CodeGenerator::InsertCXXMethodHeader(const CXXMethodDecl* stmt, OutputFormatHelper& initOutputFormatHelper)
3175 {
3176  LAMBDA_SCOPE_HELPER(CXXMethodDecl);
3177  CXXConstructorDecl* cxxInheritedCtorDecl{nullptr};
3178 
3179  // Traverse the ctor inline init statements first to find a potential CXXInheritedCtorInitExpr. This carries the
3180  // name and the type. The CXXMethodDecl above knows only the type.
3181  if(const auto* ctor = dyn_cast_or_null<CXXConstructorDecl>(stmt)) {
3182  CodeGeneratorVariant codeGenerator{initOutputFormatHelper, mLambdaStack, mProcessingPrimaryTemplate};
3183  codeGenerator->mCurrentVarDeclPos = mCurrentVarDeclPos;
3184  codeGenerator->mCurrentFieldPos = mCurrentFieldPos;
3185  codeGenerator->mCurrentCallExprPos = mCurrentCallExprPos;
3186  codeGenerator->mOutputFormatHelperOutside = &mOutputFormatHelper;
3187 
3188  for(OnceTrue first{}; const auto* init : ctor->inits()) {
3189  initOutputFormatHelper.AppendNewLine();
3190  if(first) {
3191  initOutputFormatHelper.Append(": "sv);
3192  } else {
3193  initOutputFormatHelper.Append(", "sv);
3194  }
3195 
3196  // in case of delegating or base initializer there is no member.
3197 #if 0
3198  if(const auto* member = init->getMember()) {
3199  initOutputFormatHelper.Append(member->getName());
3200  codeGenerator->InsertCurlysIfRequired(init->getInit());
3201  } else {
3202  const auto* inlineInit = init->getInit();
3203  bool useCurlies{false};
3204 
3205  if(const auto* cxxInheritedCtorInitExpr = dyn_cast_or_null<CXXInheritedCtorInitExpr>(inlineInit)) {
3206  cxxInheritedCtorDecl = cxxInheritedCtorInitExpr->getConstructor();
3207 
3208  // Insert the base class name only, if it is not a CXXContructorExpr and not a
3209  // CXXDependentScopeMemberExpr which already carry the type.
3210  } else if(init->isBaseInitializer() and not isa<CXXConstructExpr>(inlineInit)) {
3211  initOutputFormatHelper.Append(GetUnqualifiedScopelessName(init->getBaseClass()));
3212  useCurlies = true;
3213  }
3214 
3215  codeGenerator->WrapInCurliesIfNeeded(useCurlies, [&] { codeGenerator->InsertArg(inlineInit); });
3216  }
3217 #else
3218  const auto* inlineInit = init->getInit();
3219 
3220  // in case of delegating or base initializer there is no member.
3221  if(const auto* member = init->getMember()) {
3222  initOutputFormatHelper.Append(member->getName());
3223 
3224  if(isa<ParenListExpr>(inlineInit)) {
3225  codeGenerator->WrapInParens([&] { codeGenerator->InsertArg(inlineInit); });
3226  } else {
3227  codeGenerator->InsertCurlysIfRequired(inlineInit);
3228  }
3229 
3230  } else if(const auto* cxxInheritedCtorInitExpr = dyn_cast_or_null<CXXInheritedCtorInitExpr>(inlineInit)) {
3231  cxxInheritedCtorDecl = cxxInheritedCtorInitExpr->getConstructor();
3232 
3233  codeGenerator->InsertArg(inlineInit);
3234 
3235  // Insert the base class name only, if it is not a CXXContructorExpr and not a
3236  // CXXDependentScopeMemberExpr which already carry the type.
3237  } else if(init->isBaseInitializer() and not isa<CXXConstructExpr>(inlineInit)) {
3238  initOutputFormatHelper.Append(GetUnqualifiedScopelessName(init->getBaseClass()));
3239 
3240  const auto braceKind = isa<ParenListExpr>(inlineInit) ? BraceKind::Parens : BraceKind::Curlys;
3241 
3242  codeGenerator->WrapInParensOrCurlys(braceKind, [&] { codeGenerator->InsertArg(inlineInit); });
3243  } else {
3244  codeGenerator->InsertArg(inlineInit);
3245  }
3246 #endif
3247  }
3248  }
3249 
3250  InsertTemplateGuardBegin(stmt);
3251  InsertFunctionNameWithReturnType(*stmt, cxxInheritedCtorDecl);
3252 }
3253 //-----------------------------------------------------------------------------
3254 
3255 void CodeGenerator::InsertCXXMethodDecl(const CXXMethodDecl* stmt, SkipBody skipBody)
3256 {
3257  OutputFormatHelper initOutputFormatHelper{};
3258  initOutputFormatHelper.SetIndent(mOutputFormatHelper, OutputFormatHelper::SkipIndenting::Yes);
3259 
3260  const auto posBeforeFunc = mOutputFormatHelper.CurrentPos();
3261 
3262  InsertCXXMethodHeader(stmt, initOutputFormatHelper);
3263 
3264  if(not stmt->isUserProvided() or stmt->isExplicitlyDefaulted()) {
3265  InsertTemplateGuardEnd(stmt);
3266  return;
3267  }
3268 
3269  mOutputFormatHelper.Append(initOutputFormatHelper);
3270 
3271  if(isa<CXXConversionDecl>(stmt)) {
3272  if(stmt->getParent()->isLambda() and not stmt->doesThisDeclarationHaveABody()) {
3273  mOutputFormatHelper.AppendNewLine();
3274  WrapInCurlys([&]() {
3275  mOutputFormatHelper.AppendNewLine();
3276  mOutputFormatHelper.Append(" "sv, kwReturn, " "sv);
3277  if(const auto* invoker = stmt->getParent()->getLambdaStaticInvoker()) {
3278  mOutputFormatHelper.AppendSemiNewLine(invoker->getName());
3279  } else {
3280  mOutputFormatHelper.AppendSemiNewLine(kwOperator, "()"sv);
3281  }
3282  });
3283  }
3284  }
3285 
3286  if((SkipBody::No == skipBody) and stmt->doesThisDeclarationHaveABody() and not stmt->isLambdaStaticInvoker()) {
3287  InsertMethodBody(stmt, posBeforeFunc);
3288 
3289  } else if(not InsertLambdaStaticInvoker(stmt) or (SkipBody::Yes == skipBody)) {
3290  mOutputFormatHelper.AppendSemiNewLine();
3291  }
3292 
3293  InsertTemplateGuardEnd(stmt);
3294 
3295  if(SkipBody::No == skipBody) {
3296  mOutputFormatHelper.AppendNewLine();
3297  }
3298 }
3299 //-----------------------------------------------------------------------------
3300 
3301 void CodeGenerator::InsertArg(const CXXMethodDecl* stmt)
3302 {
3303  // As per [special]/1: "Programs shall not define implicitly-declared special member functions." hide special
3304  // members which are not used and with that not fully evaluated. This also hopefully removes confusion about the
3305  // noexcept, which is not evaluated, if the special member is not used.
3306  RETURN_IF(not GetInsightsOptions().UseShow2C and not stmt->hasBody() and not stmt->isUserProvided() and
3307  not stmt->isExplicitlyDefaulted() and not stmt->isDeleted());
3308 
3309  InsertCXXMethodDecl(stmt, SkipBody::No);
3310 }
3311 //-----------------------------------------------------------------------------
3312 
3313 void CodeGenerator::InsertArg(const EnumDecl* stmt)
3314 {
3315  mOutputFormatHelper.Append(kwEnumSpace);
3316 
3317  if(stmt->isScoped()) {
3318  if(stmt->isScopedUsingClassTag()) {
3319  mOutputFormatHelper.Append(kwClassSpace);
3320  } else {
3321  mOutputFormatHelper.Append(kwStructSpace);
3322  }
3323  }
3324 
3325  mOutputFormatHelper.Append(stmt->getName());
3326 
3327  if(stmt->isFixed()) {
3328  mOutputFormatHelper.Append(" : "sv, GetName(stmt->getIntegerType()));
3329  }
3330 
3331  mOutputFormatHelper.AppendNewLine();
3332 
3333  WrapInCurlys(
3334  [&]() {
3335  mOutputFormatHelper.IncreaseIndent();
3336  mOutputFormatHelper.AppendNewLine();
3337  OnceFalse needsComma{};
3338 
3339  ForEachArg(stmt->enumerators(), [&](const auto* value) {
3340  if(needsComma) {
3341  mOutputFormatHelper.AppendNewLine();
3342  }
3343 
3344  InsertArg(value);
3345  });
3346 
3347  InsertArg(stmt->getBody());
3348 
3349  mOutputFormatHelper.DecreaseIndent();
3350  mOutputFormatHelper.AppendNewLine();
3351  },
3352  AddSpaceAtTheEnd::No);
3353 
3354  mOutputFormatHelper.AppendSemiNewLine();
3355  mOutputFormatHelper.AppendNewLine();
3356 }
3357 //-----------------------------------------------------------------------------
3358 
3359 void CodeGenerator::InsertArg(const EnumConstantDecl* stmt)
3360 {
3361  mOutputFormatHelper.Append(stmt->getName());
3362 
3363  InsertAttributes(stmt);
3364 
3365  if(const auto* initExpr = stmt->getInitExpr()) {
3366  mOutputFormatHelper.Append(hlpAssing);
3367 
3368  InsertArg(initExpr);
3369  }
3370 }
3371 //-----------------------------------------------------------------------------
3372 
3373 static auto& GetRecordLayout(const RecordDecl* recordDecl)
3374 {
3375  return GetGlobalAST().getASTRecordLayout(recordDecl);
3376 }
3377 //-----------------------------------------------------------------------------
3378 
3379 // XXX: replace with std::format once it is available in all std-libs
3380 auto GetSpaces(std::string::size_type offset)
3381 {
3382  static const std::string_view spaces{" "sv};
3383 
3384  if(offset >= spaces.size()) {
3385  return ""sv;
3386  } else {
3387  return spaces.substr(0, spaces.size() - offset);
3388  }
3389 }
3390 //-----------------------------------------------------------------------------
3391 
3392 void CodeGenerator::InsertArg(const FieldDecl* stmt)
3393 {
3394  LAMBDA_SCOPE_HELPER(Decltype);
3395  P0315Visitor dt{*this};
3396 
3397  auto type = GetType(stmt->getType());
3398  dt.TraverseType(type);
3399 
3400  const auto initialSize{mOutputFormatHelper.size()};
3401  InsertAttributes(stmt->attrs());
3402 
3403  if(stmt->isMutable()) {
3404  mOutputFormatHelper.Append(kwMutableSpace);
3405  }
3406 
3407  if(const auto* cxxRecordDecl = dyn_cast_or_null<CXXRecordDecl>(stmt->getParent())) {
3408  std::string name{GetName(*stmt)};
3409 
3410  if(const auto fieldName = GetFieldDeclNameForLambda(*stmt, *cxxRecordDecl)) {
3411  name = std::move(fieldName.value());
3412  }
3413 
3414  mOutputFormatHelper.Append(GetTypeNameAsParameter(type, name));
3415 
3416  if(const auto* constantExpr = dyn_cast_or_null<ConstantExpr>(stmt->getBitWidth())) {
3417  mOutputFormatHelper.Append(':');
3418  InsertArg(constantExpr);
3419  }
3420 
3421  // Keep the inline init for aggregates, as we do not see it somewhere else.
3422  if(const auto* initializer = stmt->getInClassInitializer();
3423  stmt->hasInClassInitializer() and initializer and cxxRecordDecl->isAggregate()) {
3424  const bool isConstructorExpr{isa<CXXConstructExpr>(initializer) or isa<ExprWithCleanups>(initializer)};
3425  if((ICIS_ListInit != stmt->getInClassInitStyle()) or isConstructorExpr) {
3426  mOutputFormatHelper.Append(hlpAssing);
3427  }
3428 
3429  InsertArg(initializer);
3430  }
3431  }
3432 
3433  mOutputFormatHelper.Append(';');
3434 
3435  if(GetInsightsOptions().UseShowPadding) {
3436  const auto* fieldClass = stmt->getParent();
3437  const auto& recordLayout = GetRecordLayout(fieldClass);
3438  auto effectiveFieldSize{GetGlobalAST().getTypeInfoInChars(type).Width.getQuantity()};
3439  auto getFieldOffsetInBytes = [&recordLayout](const FieldDecl* field) {
3440  return recordLayout.getFieldOffset(field->getFieldIndex()) / 8; // this is in bits
3441  };
3442  auto fieldOffset = getFieldOffsetInBytes(stmt);
3443  const auto offset = mOutputFormatHelper.size() - initialSize;
3444 
3445  mOutputFormatHelper.Append(GetSpaces(offset), " /* offset: "sv, fieldOffset, ", size: "sv, effectiveFieldSize);
3446 
3447  // - get next field
3448  // - if this fields offset + size is equal to the next fields offset we are good,
3449  // - if not we insert padding bytes
3450  // - in case there is no next field this is the last field, check this field's offset + size against the
3451  // records
3452  // size. If unequal padding is needed
3453 
3454  const auto expectedOffset = fieldOffset + effectiveFieldSize;
3455  const auto nextOffset = [&]() -> uint64_t {
3456  // find previous field
3457  if(const auto next = stmt->getFieldIndex() + 1; recordLayout.getFieldCount() > next) {
3458  // We are in bounds, means we expect to get back a valid iterator
3459  const auto* field = *std::next(fieldClass->fields().begin(), next);
3460 
3461  return getFieldOffsetInBytes(field);
3462  }
3463 
3464  // no field found means we are the last field
3465  return recordLayout.getSize().getQuantity();
3466  }();
3467 
3468  if(expectedOffset < nextOffset) {
3469  const auto padding = nextOffset - expectedOffset;
3470  mOutputFormatHelper.AppendNewLine();
3471  std::string s = StrCat("char "sv, BuildInternalVarName("padding"sv), "["sv, padding, "];"sv);
3472  mOutputFormatHelper.Append(s, GetSpaces(s.length()), " size: ", padding);
3473  }
3474 
3475  mOutputFormatHelper.AppendNewLine(" */"sv);
3476 
3477  } else {
3478  mOutputFormatHelper.AppendNewLine();
3479  }
3480 }
3481 //-----------------------------------------------------------------------------
3482 
3483 void CodeGenerator::InsertArg(const AccessSpecDecl* stmt)
3484 {
3485  mOutputFormatHelper.AppendNewLine();
3486  mOutputFormatHelper.AppendNewLine(AccessToStringWithColon(stmt->getAccess()));
3487 }
3488 //-----------------------------------------------------------------------------
3489 
3490 void CodeGenerator::InsertArg(const StaticAssertDecl* stmt)
3491 {
3492  LAMBDA_SCOPE_HELPER(CallExpr);
3493 
3494  if(not stmt->isFailed()) {
3495  mOutputFormatHelper.Append("/* PASSED: "sv);
3496  } else {
3497  mOutputFormatHelper.Append("/* FAILED: "sv);
3498  }
3499 
3500  mOutputFormatHelper.Append(kwStaticAssert);
3501 
3502  WrapInParens([&] {
3503  BackupAndRestore _{GetInsightsOptionsRW().ShowLifetime, false};
3504  InsertArg(stmt->getAssertExpr());
3505 
3506  if(stmt->getMessage()) {
3507  mOutputFormatHelper.Append(", "sv);
3508  InsertArg(stmt->getMessage());
3509  }
3510  });
3511 
3512  mOutputFormatHelper.AppendNewLine(";"sv, kwSpaceCCommentEnd);
3513 }
3514 //-----------------------------------------------------------------------------
3515 
3516 void CodeGenerator::InsertArg(const UsingDirectiveDecl* stmt)
3517 {
3518  // We need this due to a wired case in UsingDeclTest.cpp
3519  if(const auto& name = GetName(*stmt->getNominatedNamespace()); not name.empty()) {
3520  mOutputFormatHelper.AppendSemiNewLine(kwUsingSpace, kwNamespaceSpace, name);
3521  }
3522 }
3523 //-----------------------------------------------------------------------------
3524 
3525 void CodeGenerator::InsertArg(const NamespaceDecl* stmt)
3526 {
3527  SCOPE_HELPER(stmt);
3528 
3529  if(stmt->isInline()) {
3530  mOutputFormatHelper.Append(kwInlineSpace);
3531  }
3532 
3533  mOutputFormatHelper.Append(kwNamespace);
3534 
3535  if(not stmt->isAnonymousNamespace()) {
3536  mOutputFormatHelper.Append(" "sv, stmt->getName());
3537  }
3538 
3539  InsertAttributes(stmt);
3540 
3541  mOutputFormatHelper.AppendNewLine();
3542 
3543  mOutputFormatHelper.OpenScope();
3544 
3545  for(const auto* decl : stmt->decls()) {
3546  InsertArg(decl);
3547  }
3548 
3549  mOutputFormatHelper.CloseScope();
3550  mOutputFormatHelper.AppendNewLine();
3551 }
3552 //-----------------------------------------------------------------------------
3553 
3554 void CodeGenerator::ParseDeclContext(const DeclContext* ctx)
3555 {
3556  mOutputFormatHelper.Append(GetDeclContext(ctx));
3557 }
3558 //-----------------------------------------------------------------------------
3559 
3560 void CodeGenerator::InsertArg(const UsingDecl* stmt)
3561 {
3562  OutputFormatHelper ofm{};
3563  ofm.SetIndent(mOutputFormatHelper, OutputFormatHelper::SkipIndenting::Yes);
3564 
3565  // Skip UsingDecl's which have ConstructorUsingShadowDecl attached. This means that we will create the
3566  // associated constructors from the base class later. Having this \c using still in the code prevents compiling
3567  // the transformed code.
3568  if(stmt->shadow_size()) {
3569  for(const auto* shadow : stmt->shadows()) {
3570  RETURN_IF(isa<ConstructorUsingShadowDecl>(shadow));
3571 
3572  if(const auto* shadowUsing = dyn_cast_or_null<UsingShadowDecl>(shadow)) {
3573  if(const auto* targetDecl = shadowUsing->getTargetDecl(); not isa<TypeAliasDecl>(targetDecl)) {
3574  UsingCodeGenerator codeGenerator{ofm};
3575  codeGenerator.InsertArg(targetDecl);
3576  }
3577  }
3578  }
3579  }
3580 
3581  mOutputFormatHelper.Append(kwUsingSpace);
3582 
3583  InsertQualifierAndName(stmt->getDeclName(), stmt->getQualifier(), false);
3584 
3585  mOutputFormatHelper.AppendSemiNewLine();
3586 
3587  // Insert what a using declaration pulled into this scope.
3588  if(not ofm.empty()) {
3589  mOutputFormatHelper.AppendNewLine(ofm);
3590  }
3591 }
3592 //-----------------------------------------------------------------------------
3593 
3594 void CodeGenerator::InsertArg(const UnresolvedUsingValueDecl* stmt)
3595 {
3596  mOutputFormatHelper.Append(kwUsingSpace);
3597 
3598  InsertQualifierAndName(stmt->getDeclName(), stmt->getQualifier(), false);
3599 
3600  mOutputFormatHelper.AppendSemiNewLine(Ellipsis(stmt->isPackExpansion()));
3601 }
3602 //-----------------------------------------------------------------------------
3603 
3604 void CodeGenerator::InsertArg(const NamespaceAliasDecl* stmt)
3605 {
3606  mOutputFormatHelper.AppendNewLine(
3607  kwNamespaceSpace, stmt->getDeclName().getAsString(), hlpAssing, GetName(*stmt->getAliasedNamespace()), ";");
3608 }
3609 //-----------------------------------------------------------------------------
3610 
3611 void CodeGenerator::InsertArg(const FriendDecl* stmt)
3612 {
3613  if(const auto* typeInfo = stmt->getFriendType()) {
3614  mOutputFormatHelper.AppendSemiNewLine(kwFriendSpace, GetName(typeInfo->getType()));
3615 
3616  } else if(const auto* fd = dyn_cast_or_null<FunctionDecl>(stmt->getFriendDecl())) {
3617  InsertArg(fd);
3618 
3619  } else if(const auto* fdt = dyn_cast_or_null<FunctionTemplateDecl>(stmt->getFriendDecl())) {
3620  InsertArg(fdt);
3621 
3622  } else {
3623  std::string cls{};
3624  if(const auto* ctd = dyn_cast_or_null<ClassTemplateDecl>(stmt->getFriendDecl())) {
3625  InsertTemplateParameters(*ctd->getTemplateParameters());
3626 
3627  cls = GetTagDeclTypeName(*ctd->getTemplatedDecl());
3628  }
3629 
3630  mOutputFormatHelper.AppendSemiNewLine(kwFriendSpace, cls, GetName(*stmt->getFriendDecl()));
3631  }
3632 }
3633 //-----------------------------------------------------------------------------
3634 
3635 void CodeGenerator::InsertArg(const CXXNoexceptExpr* stmt)
3636 {
3637  mOutputFormatHelper.Append(kwNoexcept);
3638 
3639  WrapInParens([&] { mOutputFormatHelper.Append(details::ConvertToBoolString(stmt->getValue())); });
3640 }
3641 //-----------------------------------------------------------------------------
3642 
3643 void CodeGenerator::InsertArg(const CXXDeductionGuideDecl* stmt)
3644 {
3645  RETURN_IF(DeductionCandidate::Copy == stmt->getDeductionCandidateKind());
3646 
3647  const bool isImplicit{stmt->isImplicit()};
3648  const bool noSpecializations = [&] {
3649  if(const auto* dt = stmt->getDescribedFunctionTemplate()) {
3650  return dt->specializations().empty();
3651  }
3652 
3653  return false;
3654  }();
3655 
3656  // Block compiler generated deduction guides which are _overridden_ by user provided deduction guides.
3657  RETURN_IF(not stmt->isUsed() and isImplicit and noSpecializations);
3658 
3659  const bool isSpecialization{stmt->isFunctionTemplateSpecialization()};
3660  const bool needsTemplateGuard{isImplicit or isSpecialization};
3661 
3662  if(needsTemplateGuard) {
3663  InsertTemplateGuardBegin(stmt);
3664  }
3665 
3666  const auto* deducedTemplate = stmt->getDeducedTemplate();
3667 
3668  if(isSpecialization) {
3669  InsertTemplateSpecializationHeader(*stmt);
3670  } else if(const auto* e = stmt->getDescribedFunctionTemplate()) {
3671  InsertTemplateParameters(*e->getTemplateParameters());
3672  }
3673 
3674  mOutputFormatHelper.Append(GetName(*deducedTemplate));
3675 
3676  if(stmt->getNumParams()) {
3677  WrapInParens([&] { mOutputFormatHelper.AppendParameterList(stmt->parameters()); });
3678  } else {
3679  mOutputFormatHelper.Append("()"sv);
3680  }
3681 
3682  mOutputFormatHelper.AppendSemiNewLine(hlpArrow, GetName(stmt->getReturnType()));
3683 
3684  if(needsTemplateGuard) {
3685  InsertTemplateGuardEnd(stmt);
3686  }
3687 }
3688 //-----------------------------------------------------------------------------
3689 
3690 void CodeGenerator::InsertTemplate(const FunctionTemplateDecl* stmt, bool withSpec)
3691 {
3692  LAMBDA_SCOPE_HELPER(TemplateHead);
3693 
3694  mProcessingPrimaryTemplate = ProcessingPrimaryTemplate::Yes;
3695 
3696  // InsertTemplateParameters(*stmt->getTemplateParameters());
3697  InsertArg(stmt->getTemplatedDecl());
3698 
3699  mProcessingPrimaryTemplate = ProcessingPrimaryTemplate::No;
3700 
3701  RETURN_IF(not withSpec);
3702 
3703  for(const auto* spec : stmt->specializations()) {
3704  // For specializations we will see them later
3705  if(spec->getPreviousDecl()) {
3706  continue;
3707  }
3708 
3709  mOutputFormatHelper.AppendNewLine();
3710  InsertArg(spec);
3711  mOutputFormatHelper.AppendNewLine();
3712  }
3713 }
3714 //-----------------------------------------------------------------------------
3715 
3716 void CodeGenerator::InsertArg(const FunctionTemplateDecl* stmt)
3717 {
3718  InsertTemplate(stmt, true);
3719 }
3720 //-----------------------------------------------------------------------------
3721 
3722 void CodeGenerator::InsertArg(const TypeAliasTemplateDecl* stmt)
3723 {
3724  InsertTemplateParameters(*stmt->getTemplateParameters());
3725 
3726  InsertArg(stmt->getTemplatedDecl());
3727 }
3728 //-----------------------------------------------------------------------------
3729 
3730 void CodeGenerator::InsertArg(const AttributedStmt* stmt)
3731 {
3732  for(const auto& attr : stmt->getAttrs()) {
3733  InsertAttribute(*attr);
3734  }
3735 
3736  InsertArg(stmt->getSubStmt());
3737 }
3738 //-----------------------------------------------------------------------------
3739 
3740 void CodeGenerator::InsertAttributes(const Decl* stmt)
3741 {
3742  if(stmt->hasAttrs()) {
3743  mOutputFormatHelper.Append(" "sv);
3744 
3745  InsertAttributes(stmt->attrs());
3746  }
3747 }
3748 //-----------------------------------------------------------------------------
3749 
3750 void CodeGenerator::InsertAttributes(const Decl::attr_range& attrs)
3751 {
3752  // attrs required for constinit
3753  for(const auto& attr : attrs) {
3754  InsertAttribute(*attr);
3755  }
3756 }
3757 //-----------------------------------------------------------------------------
3758 
3759 void CodeGenerator::InsertAttribute(const Attr& attr)
3760 {
3761  // skip this attribute. Clang seems to tag virtual methods with override
3762  RETURN_IF(attr::Override == attr.getKind());
3763 
3764  // skip this attribute. Clang seems to tag final methods or classes with final
3765  RETURN_IF(attr::Final == attr.getKind());
3766 
3767  // skip this custom clang attribute
3768  RETURN_IF(attr::NoInline == attr.getKind());
3769 
3770  // Clang's printPretty misses the parameter pack ellipsis. Hence treat this special case here.
3771  if(const auto* alignedAttr = dyn_cast_or_null<AlignedAttr>(&attr)) {
3772  auto insert = [&](const QualType type, const TemplateTypeParmType* tmplTypeParam) {
3773  mOutputFormatHelper.Append(attr.getSpelling(),
3774  "("sv,
3775  kwAlignof,
3776  "("sv,
3777  GetName(type),
3778  ")"sv,
3779  Ellipsis(tmplTypeParam->isParameterPack()),
3780  ") "sv);
3781  };
3782 
3783  if(alignedAttr->isAlignmentExpr()) {
3784  if(const auto* unaryExpr = dyn_cast_or_null<UnaryExprOrTypeTraitExpr>(alignedAttr->getAlignmentExpr())) {
3785  if(const auto* tmplTypeParam =
3786  dyn_cast_or_null<TemplateTypeParmType>(unaryExpr->getArgumentType().getTypePtrOrNull())) {
3787  insert(unaryExpr->getArgumentType(), tmplTypeParam);
3788  return;
3789  }
3790  }
3791  } else if(const auto* tmplTypeParam =
3792  alignedAttr->getAlignmentType()->getType()->getAs<TemplateTypeParmType>()) {
3793  insert(alignedAttr->getAlignmentType()->getType(), tmplTypeParam);
3794  return;
3795  }
3796  }
3797 
3798  StringStream stream{};
3799  PrintingPolicy pp{GetGlobalAST().getLangOpts()};
3800  pp.adjustForCPlusPlus();
3801 
3802  attr.printPretty(stream, pp);
3803 
3804  // attributes start with a space, skip it as it is not required for the first attribute
3805  std::string_view start{stream.str()};
3806 
3807  mOutputFormatHelper.Append(start, " "sv);
3808 }
3809 //-----------------------------------------------------------------------------
3810 
3811 void CodeGenerator::InsertArg(const CXXRecordDecl* stmt)
3812 {
3813  const size_t insertPosBeforeClass{mOutputFormatHelper.CurrentPos()};
3814  const auto indentAtInsertPosBeforeClass{mOutputFormatHelper.GetIndent()};
3815 
3816  SCOPE_HELPER(stmt);
3817 
3818  // Prevent a case like in #205 where the lambda appears twice.
3819  RETURN_IF(stmt->isLambda() and (mLambdaStack.empty() or (nullptr == mLambdaExpr)));
3820 
3821  const auto* classTemplatePartialSpecializationDecl = dyn_cast_or_null<ClassTemplatePartialSpecializationDecl>(stmt);
3822  const auto* classTemplateSpecializationDecl = dyn_cast_or_null<ClassTemplateSpecializationDecl>(stmt);
3823 
3824  // we require the if-guard only if it is a compiler generated specialization. If it is a hand-written variant it
3825  // should compile.
3826  const bool isClassTemplateSpecialization{classTemplatePartialSpecializationDecl or classTemplateSpecializationDecl};
3827  const bool tmplRequiresIfDef{[&] {
3828  if(classTemplatePartialSpecializationDecl) {
3829  return classTemplatePartialSpecializationDecl->isImplicit();
3830 
3831  } else if(classTemplateSpecializationDecl) {
3832  return not classTemplateSpecializationDecl->isExplicitInstantiationOrSpecialization();
3833  }
3834 
3835  return false;
3836  }()};
3837 
3838  FinalAction _{[&] {
3839  if(tmplRequiresIfDef) {
3840  mOutputFormatHelper.InsertEndIfTemplateGuard();
3841  }
3842  }};
3843 
3844  if(isClassTemplateSpecialization) {
3845  if(tmplRequiresIfDef) {
3846  InsertInstantiationPoint(GetSM(*classTemplateSpecializationDecl),
3847  classTemplateSpecializationDecl->getPointOfInstantiation());
3848  mOutputFormatHelper.InsertIfDefTemplateGuard();
3849  }
3850 
3851  if(classTemplatePartialSpecializationDecl) {
3852  InsertTemplateParameters(*classTemplatePartialSpecializationDecl->getTemplateParameters());
3853  } else {
3854  InsertTemplateSpecializationHeader(*stmt);
3855  }
3856  // Render a out-of-line struct declared inside a class template
3857  } else if(stmt->getLexicalDeclContext() != stmt->getDeclContext()) {
3858  if(const auto* parent = dyn_cast_or_null<CXXRecordDecl>(stmt->getDeclContext())) {
3859  if(const auto* outerClasTemplateDecl = parent->getDescribedClassTemplate()) {
3860  InsertTemplateParameters(*outerClasTemplateDecl->getTemplateParameters());
3861  }
3862  }
3863  }
3864 
3865  mOutputFormatHelper.Append(GetTagDeclTypeName(*stmt));
3866 
3867  InsertAttributes(stmt->attrs());
3868 
3869  mOutputFormatHelper.Append(GetName(*stmt));
3870 
3871  if(classTemplateSpecializationDecl) {
3872  InsertTemplateArgs(*classTemplateSpecializationDecl);
3873  }
3874 
3875  if(stmt->hasAttr<FinalAttr>()) {
3876  mOutputFormatHelper.Append(kwSpaceFinal);
3877  }
3878 
3879  // skip classes/struct's without a definition
3880  if(not stmt->hasDefinition() or not stmt->isCompleteDefinition()) {
3881  mOutputFormatHelper.AppendSemiNewLine();
3882  return;
3883  }
3884 
3885  if(stmt->getNumBases()) {
3886  mOutputFormatHelper.Append(" : "sv);
3887 
3888  ForEachArg(stmt->bases(), [&](const auto& base) {
3889  mOutputFormatHelper.Append(getAccessSpelling(base.getAccessSpecifier()),
3890  " "sv,
3891  ValueOrDefault(base.isVirtual(), kwVirtualSpace),
3892  GetName(base.getType()),
3893  Ellipsis(base.isPackExpansion()));
3894  });
3895  }
3896 
3897  if(GetInsightsOptions().UseShowPadding) {
3898  const auto& recordLayout = GetRecordLayout(stmt);
3899  mOutputFormatHelper.AppendNewLine(
3900  " /* size: "sv, recordLayout.getSize(), ", align: "sv, recordLayout.getAlignment(), " */"sv);
3901 
3902  } else {
3903  mOutputFormatHelper.AppendNewLine();
3904  }
3905 
3906  mOutputFormatHelper.OpenScope();
3907 
3908  if(GetInsightsOptions().UseShowPadding) {
3909  for(size_t offset{}; const auto& base : stmt->bases()) {
3910  const auto& baseRecordLayout = GetRecordLayout(base.getType()->getAsRecordDecl());
3911  const auto baseVar = StrCat("/* base ("sv, GetName(base.getType()), ")"sv);
3912  const auto size = baseRecordLayout.getSize().getQuantity();
3913 
3914  mOutputFormatHelper.AppendNewLine(
3915  baseVar, GetSpaces(baseVar.size()), " offset: "sv, offset, ", size: "sv, size, " */"sv);
3916 
3917  offset += size;
3918  }
3919  }
3920 
3921  UpdateCurrentPos(mCurrentFieldPos);
3922 
3923  OnceTrue firstRecordDecl{};
3924  OnceTrue firstDecl{};
3925  Decl::Kind formerKind{};
3926  AccessSpecifier lastAccess{stmt->isClass() ? AS_private : AS_public};
3927  for(const auto* d : stmt->decls()) {
3928  if(isa<CXXRecordDecl>(d) and firstRecordDecl) {
3929  continue;
3930  }
3931 
3932  // Insert a newline when the decl kind changes. This for example, inserts a newline when after a FieldDecl
3933  // we see a CXXMethodDecl.
3934  if(not firstDecl and (d->getKind() != formerKind)) {
3935  // mOutputFormatHelper.AppendNewLine();
3936  }
3937 
3938  if((stmt->isLambda() and isa<CXXDestructorDecl>(d)) and not d->isUsed()) {
3939  continue;
3940  }
3941 
3942  // Insert the access modifier, as at least some of the compiler generated classes do not contain an access
3943  // specifier which results in a default ctor being private if we do not insert the access modifier.
3944  if(lastAccess != d->getAccess()) {
3945  lastAccess = d->getAccess();
3946 
3947  // skip inserting an access specifier of our own, if there is a real one coming.
3948  if(not isa<AccessSpecDecl>(d)) {
3949  mOutputFormatHelper.AppendNewLine(AccessToStringWithColon(lastAccess));
3950  }
3951  }
3952 
3953  InsertArg(d);
3954 
3955  formerKind = d->getKind();
3956  }
3957 
3958  if(stmt->isLambda()) {
3959  const LambdaCallerType lambdaCallerType = mLambdaStack.back().callerType();
3960  const bool ctorRequired{stmt->capture_size() or stmt->lambdaIsDefaultConstructibleAndAssignable()};
3961 
3962  if(ctorRequired) {
3963  if(AS_public != lastAccess) {
3964  mOutputFormatHelper.AppendNewLine();
3965  // XXX avoid diff in tests. AccessToStringWithColon add "public: " before there was no space.
3966  const auto pub{AccessToStringWithColon(AS_public)};
3967  std::string_view p{pub};
3968  p.remove_suffix(1);
3969  mOutputFormatHelper.AppendNewLine(p);
3970  }
3971 
3972  if(stmt->lambdaIsDefaultConstructibleAndAssignable()) {
3973  mOutputFormatHelper.Append(kwCppCommentStartSpace);
3974 
3975  if(stmt->hasConstexprDefaultConstructor()) {
3976  mOutputFormatHelper.Append(kwCommentStart, kwConstExprSpace, kwCCommentEndSpace);
3977  }
3978  }
3979 
3980  mOutputFormatHelper.Append(GetName(*stmt), "("sv);
3981  }
3982 
3983  SmallVector<std::string, 5> ctorInitializerList{};
3984  std::string ctorArguments{'{'};
3985  OnceTrue firstCtorArgument{};
3986 
3987  auto addToInits =
3988  [&](std::string_view name, const FieldDecl* fd, bool isThis, const Expr* expr, bool /*useBraces*/) {
3989  if(firstCtorArgument) {
3990  } else {
3991  mOutputFormatHelper.Append(", "sv);
3992  ctorArguments.append(", "sv);
3993  }
3994 
3995  bool byConstRef{false};
3996  auto fieldName{isThis ? kwInternalThis : name};
3997  auto fieldDeclType{fd->getType()};
3998  bool isMoved{};
3999 
4000  std::string fname = StrCat("_"sv, name);
4001 
4002  // Special handling for lambdas with init captures which contain a move. In such a case, copy the
4003  // initial move statement and make the variable a &&.
4004  if(const auto* cxxConstructExpr = dyn_cast_or_null<CXXConstructExpr>(expr);
4005  cxxConstructExpr and cxxConstructExpr->getConstructor()->isMoveConstructor()) {
4006 
4007  OutputFormatHelper ofm{};
4008  LambdaInitCaptureCodeGenerator codeGenerator{ofm, mLambdaStack, name};
4009 
4010  if(cxxConstructExpr->getNumArgs()) {
4011  ForEachArg(cxxConstructExpr->arguments(),
4012  [&](const auto& arg) { codeGenerator.InsertArg(arg); });
4013  }
4014 
4015  fieldDeclType = stmt->getASTContext().getRValueReferenceType(fieldDeclType);
4016 
4017  fname = ofm;
4018 
4019  // If it is not an object, check for other conditions why we take the variable by const &/&& in the
4020  // ctor
4021  } else if(not fieldDeclType->isReferenceType() and not fieldDeclType->isAnyPointerType() and
4022  not fieldDeclType->isUndeducedAutoType()) {
4023  byConstRef = true;
4024  const auto* exprWithoutImpCasts = expr->IgnoreParenImpCasts();
4025 
4026  // treat a move of a primitive type
4027  if(exprWithoutImpCasts->isXValue()) {
4028  byConstRef = false;
4029 
4030  OutputFormatHelper ofm{};
4031  LambdaInitCaptureCodeGenerator codeGenerator{ofm, mLambdaStack, name};
4032  codeGenerator.InsertArg(expr);
4033 
4034  fname = ofm;
4035 
4036  } else if(exprWithoutImpCasts
4037  ->isPRValue() // If we are looking at an rvalue (temporary) we need a const ref
4038  or exprWithoutImpCasts->getType().isConstQualified() // If the captured variable is const
4039  // we can take it only by const ref
4040 
4041  ) {
4042  // this must go before adding the L or R-value reference, otherwise we get T& const instead of
4043  // const T&
4044 
4045  if(exprWithoutImpCasts->isPRValue() and isa<CXXBindTemporaryExpr>(exprWithoutImpCasts) and
4046  not exprWithoutImpCasts->getType().isConstQualified()) {
4047  fieldDeclType = stmt->getASTContext().getRValueReferenceType(fieldDeclType);
4048  EnableGlobalInsert(GlobalInserts::HeaderUtility);
4049  fname = StrCat("std::move("sv, fname, ")"sv);
4050  isMoved = true;
4051 
4052  } else {
4053  fieldDeclType.addConst();
4054  }
4055  }
4056 
4057  if(exprWithoutImpCasts->isXValue()) {
4058  fieldDeclType = stmt->getASTContext().getRValueReferenceType(fieldDeclType);
4059 
4060  } else if(not isMoved) {
4061  fieldDeclType = stmt->getASTContext().getLValueReferenceType(fieldDeclType);
4062  }
4063  }
4064 
4065  const std::string_view elips{
4066  Ellipsis(isa_and_nonnull<PackExpansionType>(fieldDeclType->getPointeeType().getTypePtrOrNull()))};
4067 
4068  // To avoid seeing the templates stuff from std::move (typename...) the canonical type is used here.
4069  fieldDeclType = fieldDeclType.getCanonicalType();
4070 
4071  ctorInitializerList.push_back(StrCat(fieldName, "{"sv, fname, elips, "}"sv));
4072 
4073  if(not isThis and expr) {
4074  LAMBDA_SCOPE_HELPER(Decltype);
4075  OutputFormatHelper ofmLambdaInCtor{};
4076  ofmLambdaInCtor.SetIndent(indentAtInsertPosBeforeClass);
4077  CodeGenerator cgLambdaInCtor{ofmLambdaInCtor, LambdaInInitCapture::Yes};
4078 
4079  if(P0315Visitor dt{cgLambdaInCtor}; dt.TraverseStmt(const_cast<Expr*>(expr))) {
4080 
4081  OutputFormatHelper ofm{};
4082  CodeGeneratorVariant codeGenerator{ofm, mLambdaStack, mProcessingPrimaryTemplate};
4083 
4084  if(const auto* ctorExpr = dyn_cast_or_null<CXXConstructExpr>(expr);
4085  ctorExpr and byConstRef and (1 == ctorExpr->getNumArgs())) {
4086  codeGenerator->InsertArg(ctorExpr->getArg(0));
4087 
4088  } else {
4089  codeGenerator->InsertArg(expr);
4090  }
4091 
4092  // if(isa<PackExpansionType>(stmt->getDecl()->getType().getTypePtrOrNull())) {
4093  // mOutputFormatHelper.Append(kwElipsisSpace);
4094  // }
4095 
4096  ctorArguments.append(ofm);
4097 
4098  } else {
4099  OutputFormatHelper ofm{};
4100  LambdaNameOnlyCodeGenerator ccg{ofm};
4101  ccg.InsertArg(expr);
4102 
4103  ctorArguments.append(ofm.GetString());
4104 
4105  mOutputFormatHelper.InsertAt(insertPosBeforeClass, ofmLambdaInCtor);
4106  }
4107  } else {
4108  if(isThis and not fieldDeclType->isPointerType()) {
4109  ctorArguments.append("*"sv);
4110  }
4111 
4112  ctorArguments.append(name);
4113  }
4114 
4115  mOutputFormatHelper.Append(GetTypeNameAsParameter(fieldDeclType, StrCat("_"sv, name)));
4116  };
4117 
4118  llvm::DenseMap<const ValueDecl*, FieldDecl*> captures{};
4119  FieldDecl* thisCapture{};
4120 
4121  stmt->getCaptureFields(captures, thisCapture);
4122 
4123  // Check if it captures this
4124  if(thisCapture) {
4125  const auto* captureInit = mLambdaExpr->capture_init_begin();
4126 
4127  addToInits(kwThis, thisCapture, true, *captureInit, false);
4128  }
4129 
4130  // Find the corresponding capture in the DenseMap. The DenseMap seems to be change its order each time.
4131  // Hence we use \c captures() to keep the order stable. While using \c Captures to generate the code as
4132  // it carries the better type infos.
4133  for(const auto& [c, cinit] : zip(mLambdaExpr->captures(), mLambdaExpr->capture_inits())) {
4134  if(not c.capturesVariable()) {
4135  continue;
4136  }
4137 
4138  const auto* capturedVar = c.getCapturedVar();
4139  if(const auto* value = captures[capturedVar]) {
4140  // Since C++20 lambdas can capture structured bindings
4141  if(const auto* bindingDecl = dyn_cast_or_null<BindingDecl>(capturedVar)) {
4142  const auto* decompositionDecl = cast<DecompositionDecl>(bindingDecl->getDecomposedDecl());
4143  addToInits(GetName(*capturedVar),
4144  value,
4145  false,
4146  cinit,
4147  VarDecl::ListInit == decompositionDecl->getInitStyle());
4148  continue;
4149  }
4150 
4151  addToInits(GetName(*capturedVar),
4152  value,
4153  false,
4154  cinit,
4155  VarDecl::ListInit == dyn_cast_or_null<VarDecl>(capturedVar)->getInitStyle());
4156  }
4157  }
4158 
4159  ctorArguments.append("}"sv);
4160 
4161  // generate the ctor only if it is required, i.e. we have captures. This is in fact a trick to get
4162  // compiling code out of it. The compiler itself does not generate a constructor in many many cases.
4163  if(ctorRequired) {
4164  mOutputFormatHelper.Append(")"sv);
4165 
4166  if(stmt->lambdaIsDefaultConstructibleAndAssignable()) {
4167  mOutputFormatHelper.AppendNewLine(kwSpaceEqualsDefault);
4168 
4169  } else {
4170  mOutputFormatHelper.AppendNewLine();
4171 
4172  for(OnceTrue firstCtorInitializer{}; const auto& initializer : ctorInitializerList) {
4173  if(firstCtorInitializer) {
4174  mOutputFormatHelper.Append(": "sv);
4175  } else {
4176  mOutputFormatHelper.Append(", "sv);
4177  }
4178 
4179  mOutputFormatHelper.AppendNewLine(initializer);
4180  }
4181 
4182  mOutputFormatHelper.AppendNewLine("{}"sv);
4183  }
4184  }
4185 
4186  // close the class scope
4187  mOutputFormatHelper.CloseScope();
4188 
4189  if(not is{lambdaCallerType}.any_of(LambdaCallerType::VarDecl,
4190  LambdaCallerType::InitCapture,
4191  LambdaCallerType::CallExpr,
4192  LambdaCallerType::MemberCallExpr,
4193  LambdaCallerType::TemplateHead,
4194  LambdaCallerType::Decltype)) {
4195  mOutputFormatHelper.Append(" "sv, GetLambdaName(*stmt), ctorArguments);
4196  } else if(not is{lambdaCallerType}.any_of(LambdaCallerType::TemplateHead, LambdaCallerType::Decltype)) {
4197  mLambdaStack.back().inits().append(ctorArguments);
4198  }
4199  } else {
4200  mOutputFormatHelper.CloseScope(OutputFormatHelper::NoNewLineBefore::Yes);
4201  }
4202 
4203  if(GetInsightsOptions().UseShow2C) {
4204  mOutputFormatHelper.Append(" "sv, GetName(*stmt));
4205  }
4206 
4207  mOutputFormatHelper.AppendSemiNewLine();
4208  mOutputFormatHelper.AppendNewLine();
4209 }
4210 //-----------------------------------------------------------------------------
4211 
4212 void CodeGenerator::InsertArg(const DeclStmt* stmt)
4213 {
4214  for(const auto* decl : stmt->decls()) {
4215  InsertArg(decl);
4216  }
4217 }
4218 //-----------------------------------------------------------------------------
4219 
4220 void CodeGenerator::InsertArg(const SubstNonTypeTemplateParmExpr* stmt)
4221 {
4222  InsertArg(stmt->getReplacement());
4223 }
4224 //-----------------------------------------------------------------------------
4225 
4226 void CodeGenerator::InsertArg(const SizeOfPackExpr* stmt)
4227 {
4228  if(stmt->isPartiallySubstituted()) {
4229  mOutputFormatHelper.Append(stmt->getPartialArguments().size());
4230  } else if(not stmt->isValueDependent()) {
4231  mOutputFormatHelper.Append(stmt->getPackLength());
4232  } else {
4233  mOutputFormatHelper.Append(kwSizeof, kwElipsis, "("sv, GetName(*stmt->getPack()), ")"sv);
4234  }
4235 }
4236 //-----------------------------------------------------------------------------
4237 
4238 void CodeGenerator::InsertArg(const ReturnStmt* stmt)
4239 {
4240  LAMBDA_SCOPE_HELPER(ReturnStmt);
4241 
4242  UpdateCurrentPos(mCurrentReturnPos);
4243 
4244  { // dedicated scope to first clear everything found in the return statement. Then clear all others.
4245  TemporaryDeclFinder temporaryFinder{*this, stmt->getRetValue(), true};
4246 
4247  mOutputFormatHelper.Append(kwReturn);
4248 
4249  if(const auto* retVal = stmt->getRetValue()) {
4250  mOutputFormatHelper.Append(' ');
4251 
4252  if(not temporaryFinder.Found()) {
4253  if(const auto* nrvoVD = stmt->getNRVOCandidate()) {
4254  mOutputFormatHelper.Append(GetName(*nrvoVD));
4255  } else {
4256  InsertArg(retVal);
4257  }
4258  } else {
4259  mOutputFormatHelper.Append(temporaryFinder.Name());
4260  }
4261  }
4262  }
4263 
4264  mSkipSemi = mLifeTimeTracker.Return(mOutputFormatHelper);
4265 
4266  // the InsertArg above changes the start
4267  mLastStmt = stmt;
4268 
4269  mCurrentReturnPos.reset();
4270 }
4271 //-----------------------------------------------------------------------------
4272 
4273 void CodeGenerator::InsertArg(const NullStmt* /*stmt*/)
4274 {
4275  mOutputFormatHelper.AppendSemiNewLine();
4276  mSkipSemi = true;
4277 }
4278 //-----------------------------------------------------------------------------
4279 
4280 void CodeGenerator::InsertArg(const StmtExpr* stmt)
4281 {
4282  WrapInParens([&] { InsertArg(stmt->getSubStmt()); });
4283 }
4284 //-----------------------------------------------------------------------------
4285 
4286 void CodeGenerator::InsertArg(const CppInsightsCommentStmt* stmt)
4287 {
4288  mOutputFormatHelper.AppendCommentNewLine(stmt->Comment());
4289 }
4290 //-----------------------------------------------------------------------------
4291 
4292 void CodeGenerator::InsertArg(const ConceptSpecializationExpr* stmt)
4293 {
4294  if(const auto* namedConcept = stmt->getNamedConcept()) {
4295  mOutputFormatHelper.Append(GetName(*namedConcept));
4296  InsertTemplateArgs(stmt->getTemplateArgsAsWritten()->arguments());
4297 
4298 #if 0
4299  if(not stmt->isValueDependent()) {
4300  mOutputFormatHelper.Append(kwCCommentStartSpace, stmt->isSatisfied(), kwSpaceCCommentEndSpace);
4301  }
4302 #endif
4303  }
4304 }
4305 //-----------------------------------------------------------------------------
4306 
4307 void CodeGenerator::InsertArg(const RequiresExpr* stmt)
4308 {
4309  mOutputFormatHelper.Append(kwRequires);
4310 
4311  const auto localParameters = stmt->getLocalParameters();
4312  WrapInParensIfNeeded(
4313  not localParameters.empty(),
4314  [&] { mOutputFormatHelper.AppendParameterList(localParameters); },
4315  AddSpaceAtTheEnd::Yes);
4316 
4317  mOutputFormatHelper.OpenScope();
4318 
4319  const auto noEmptyInitList = mNoEmptyInitList;
4320  FinalAction _{[&] { mNoEmptyInitList = noEmptyInitList; }};
4321  mNoEmptyInitList = NoEmptyInitList::Yes;
4322 
4323  for(const auto& requirement : stmt->getRequirements()) {
4324  if(const auto* typeRequirement = dyn_cast_or_null<concepts::TypeRequirement>(requirement)) {
4325  if(typeRequirement->isSubstitutionFailure()) {
4326  mOutputFormatHelper.Append(kwRequiresSpace, kwFalse);
4327  } else {
4328  mOutputFormatHelper.Append(GetName(typeRequirement->getType()->getType()));
4329  }
4330 
4331  // SimpleRequirement
4332  } else if(const auto* exprRequirement = dyn_cast_or_null<concepts::ExprRequirement>(requirement)) {
4333  if(exprRequirement->isExprSubstitutionFailure()) {
4334  // The requirement failed. We need some way to express that. Using a nested
4335  // requirement with false seems to be the simplest solution.
4336  mOutputFormatHelper.Append(kwRequiresSpace, kwFalse);
4337  } else {
4338  WrapInCurliesIfNeeded(exprRequirement->isCompound(), [&] { InsertArg(exprRequirement->getExpr()); });
4339 
4340  if(exprRequirement->hasNoexceptRequirement()) {
4341  mOutputFormatHelper.Append(kwSpaceNoexcept);
4342  }
4343 
4344  if(const auto& returnTypeRequirement = exprRequirement->getReturnTypeRequirement();
4345  not returnTypeRequirement.isEmpty()) {
4346  if(auto typeConstraint = GetTypeConstraintAsString(returnTypeRequirement.getTypeConstraint());
4347  not typeConstraint.empty()) {
4348  mOutputFormatHelper.Append(hlpArrow, std::move(typeConstraint));
4349  }
4350  }
4351  }
4352  } else if(const auto* nestedRequirement = dyn_cast_or_null<concepts::NestedRequirement>(requirement)) {
4353  mOutputFormatHelper.Append(kwRequiresSpace);
4354 
4355  if(nestedRequirement->hasInvalidConstraint()) {
4356  // The requirement failed. We need some way to express that. Using a nested
4357  // requirement with false seems to be the simplest solution.
4358  mOutputFormatHelper.Append(kwFalse);
4359  } else {
4360  InsertArg(nestedRequirement->getConstraintExpr());
4361  }
4362  }
4363 
4364  mOutputFormatHelper.AppendSemiNewLine();
4365  }
4366 
4367  mOutputFormatHelper.CloseScope(OutputFormatHelper::NoNewLineBefore::Yes);
4368 }
4369 //-----------------------------------------------------------------------------
4370 
4371 void CodeGenerator::InsertArg(const CXXDefaultArgExpr* stmt)
4372 {
4373  InsertArg(stmt->getExpr());
4374 }
4375 //-----------------------------------------------------------------------------
4376 
4377 void CodeGenerator::InsertArg(const CXXStdInitializerListExpr* stmt)
4378 {
4379  // No qualifiers like const or volatile here. This appears in function calls or operators as a parameter.
4380  // CV's are not allowed there.
4381  const auto typeName{GetName(stmt->getType(), Unqualified::Yes)};
4382 
4383  if(GetInsightsOptions().UseShowInitializerList) {
4384  RETURN_IF(not mCurrentVarDeclPos.has_value() and not mCurrentFieldPos.has_value() and
4385  not mCurrentReturnPos.has_value() and not mCurrentCallExprPos.has_value());
4386 
4387  const auto* subExpr = stmt->getSubExpr();
4388 
4389  if(const auto* dref = dyn_cast_or_null<DeclRefExpr>(subExpr); dref and GetInsightsOptions().ShowLifetime) {
4390  const auto size = GetSize(dyn_cast_or_null<ConstantArrayType>(subExpr->getType()));
4391 
4392  mOutputFormatHelper.Append(typeName, "{"sv, GetName(*dref), ", "sv, size, "}"sv);
4393  return;
4394  }
4395 
4396  std::string modifiers{};
4397  size_t variableInsertPos = mCurrentReturnPos.value_or(
4398  mCurrentVarDeclPos.value_or(mCurrentCallExprPos.value_or(0))); // order is important!
4399 
4400  auto& ofmToInsert = [&]() -> decltype(auto) {
4401  if(not mCurrentVarDeclPos.has_value() and not mCurrentReturnPos.has_value() and
4402  not mCurrentCallExprPos.has_value()) {
4403  variableInsertPos = mCurrentFieldPos.value_or(0);
4404  mCurrentVarDeclPos = variableInsertPos;
4405  modifiers = StrCat(kwStaticSpace, kwInlineSpace);
4406  return (*mOutputFormatHelperOutside);
4407  }
4408 
4409  return (mOutputFormatHelper);
4410  }();
4411 
4412  OutputFormatHelper ofm{};
4413  ofm.SetIndent(ofmToInsert, OutputFormatHelper::SkipIndenting::Yes);
4414 
4415  const auto size = [&]() -> size_t {
4416  if(const auto* mat = dyn_cast<MaterializeTemporaryExpr>(subExpr)) {
4417  if(const auto* list = dyn_cast_or_null<InitListExpr>(mat->getSubExpr())) {
4418  return list->getNumInits();
4419  }
4420  }
4421 
4422  return 0;
4423  }();
4424 
4425  auto internalListName =
4426  MakeLineColumnName(GetGlobalAST().getSourceManager(), stmt->getBeginLoc(), BuildInternalVarName("list"sv));
4427 
4428  ofm.Append(modifiers, GetTypeNameAsParameter(subExpr->getType(), internalListName));
4429  CodeGeneratorVariant codeGenerator{ofm};
4430  codeGenerator->InsertArg(subExpr);
4431  ofm.AppendSemiNewLine();
4432 
4433  ofmToInsert.InsertAt(variableInsertPos, ofm);
4434 
4435  mOutputFormatHelper.Append(typeName, "{"sv, internalListName, ", "sv, size, "}"sv);
4436 
4437  if(mCurrentReturnPos.has_value()) {
4438  mCurrentReturnPos = mCurrentReturnPos.value() + ofm.size();
4439  } else if(mCurrentVarDeclPos.has_value()) {
4440  mCurrentVarDeclPos = mCurrentVarDeclPos.value() + ofm.size();
4441  } else {
4442  mCurrentCallExprPos = mCurrentCallExprPos.value() + ofm.size();
4443  }
4444 
4445  } else {
4446  mOutputFormatHelper.Append(typeName);
4447  InsertArg(stmt->getSubExpr());
4448  }
4449 }
4450 //-----------------------------------------------------------------------------
4451 
4452 void CodeGenerator::InsertArg(const CXXNullPtrLiteralExpr* /*stmt*/)
4453 {
4454  mOutputFormatHelper.Append(kwNullptr);
4455 }
4456 //-----------------------------------------------------------------------------
4457 
4458 void CodeGenerator::InsertArg(const LabelDecl* stmt)
4459 {
4460  mOutputFormatHelper.Append(stmt->getName());
4461 }
4462 //-----------------------------------------------------------------------------
4463 
4464 void CodeGenerator::InsertArg(const Decl* stmt)
4465 {
4466  mLastDecl = stmt;
4467 
4468 #define SUPPORTED_DECL(type) \
4469  if(isa<type>(stmt)) { \
4470  InsertArg(static_cast<const type*>(stmt)); \
4471  return; \
4472  }
4473 
4474 #define IGNORED_DECL SUPPORTED_DECL
4475 
4476 #include "CodeGeneratorTypes.h"
4477 
4478  ToDo(stmt, mOutputFormatHelper);
4479 }
4480 //-----------------------------------------------------------------------------
4481 
4482 void CodeGenerator::InsertArg(const Stmt* stmt)
4483 {
4484  if(not stmt) {
4485  DPrint("Null stmt\n");
4486  return;
4487  }
4488 
4489  mLastStmt = stmt;
4490 
4491 #define SUPPORTED_STMT(type) \
4492  if(isa<type>(stmt)) { \
4493  InsertArg(dyn_cast_or_null<type>(stmt)); \
4494  return; \
4495  }
4496 
4497 #define IGNORED_STMT SUPPORTED_STMT
4498 
4499 #include "CodeGeneratorTypes.h"
4500 
4501  ToDo(stmt, mOutputFormatHelper);
4502 }
4503 //-----------------------------------------------------------------------------
4504 
4505 void CodeGenerator::FormatCast(const std::string_view castName,
4506  const QualType& castDestType,
4507  const Expr* subExpr,
4508  const CastKind& castKind)
4509 {
4510  const bool isCastToBase{is{castKind}.any_of(CK_DerivedToBase, CK_UncheckedDerivedToBase) and
4511  castDestType->isRecordType()};
4512  const std::string castDestTypeText{
4513  StrCat(GetName(castDestType), ((isCastToBase and not castDestType->isAnyPointerType()) ? "&"sv : ""sv))};
4514 
4515  mOutputFormatHelper.Append(castName, "<"sv, castDestTypeText, ">("sv);
4516  InsertArg(subExpr);
4517  mOutputFormatHelper.Append(')');
4518 }
4519 //-----------------------------------------------------------------------------
4520 
4521 void CodeGenerator::InsertArgWithParensIfNeeded(const Stmt* stmt)
4522 {
4523  const bool needsParens = [&]() {
4524  if(const auto* expr = dyn_cast_or_null<Expr>(stmt)) {
4525  if(const auto* dest = dyn_cast_or_null<UnaryOperator>(expr->IgnoreImplicit())) {
4526  return (dest->getOpcode() == clang::UO_Deref);
4527  }
4528  }
4529 
4530  return false;
4531  }();
4532 
4533  WrapInParensIfNeeded(needsParens, [&] { InsertArg(stmt); });
4534 }
4535 //-----------------------------------------------------------------------------
4536 
4537 void CodeGenerator::InsertSuffix(const QualType& type)
4538 {
4539  if(const auto* typePtr = type.getTypePtrOrNull(); typePtr and typePtr->isBuiltinType()) {
4540  if(const auto* bt = dyn_cast_or_null<BuiltinType>(typePtr)) {
4541  const auto kind = bt->getKind();
4542 
4543  mOutputFormatHelper.Append(GetBuiltinTypeSuffix(kind));
4544  }
4545  }
4546 }
4547 //-----------------------------------------------------------------------------
4548 
4549 void CodeGenerator::InsertTemplateArgs(const ClassTemplateSpecializationDecl& clsTemplateSpe)
4550 {
4551  if(const auto* ar = clsTemplateSpe.getTemplateArgsAsWritten()) {
4552  InsertTemplateArgs(ar->arguments());
4553  } else {
4554  InsertTemplateArgs(clsTemplateSpe.getTemplateArgs());
4555  }
4556 }
4557 //-----------------------------------------------------------------------------
4558 
4559 void CodeGenerator::HandleTemplateParameterPack(const ArrayRef<TemplateArgument>& args)
4560 {
4561  ForEachArg(args, [&](const auto& arg) { InsertTemplateArg(arg); });
4562 }
4563 //-----------------------------------------------------------------------------
4564 
4565 void CodeGenerator::InsertTemplateArg(const TemplateArgument& arg)
4566 {
4567  switch(arg.getKind()) {
4568  case TemplateArgument::Type: mOutputFormatHelper.Append(GetName(arg.getAsType())); break;
4569  case TemplateArgument::Declaration:
4570  // TODO: handle pointers
4571  if(const auto decl = dyn_cast_or_null<TemplateParamObjectDecl>(arg.getAsDecl())) {
4572  mOutputFormatHelper.Append(GetName(*decl));
4573  } else {
4574  mOutputFormatHelper.Append("&"sv, GetName(*arg.getAsDecl(), QualifiedName::Yes));
4575  }
4576  break;
4577  case TemplateArgument::NullPtr: mOutputFormatHelper.Append(kwNullptr); break;
4578  case TemplateArgument::Integral:
4579 
4580  if(const auto& integral = arg.getAsIntegral(); arg.getIntegralType()->isCharType()) {
4581  const char c{static_cast<char>(integral.getZExtValue())};
4582  mOutputFormatHelper.Append("'"sv, std::string{c}, "'"sv);
4583  } else {
4584  mOutputFormatHelper.Append(integral);
4585  }
4586 
4587  break;
4588  case TemplateArgument::Expression: {
4589  if(auto val = EvaluateNTTPAsConstantExpr(arg.getAsExpr()->IgnoreParenImpCasts())) {
4590  mOutputFormatHelper.Append(
4591  GetName(val->first),
4592  BuildTemplateParamObjectName(val->second.getAsString(GetGlobalAST(), val->first)));
4593  } else {
4594  InsertArg(arg.getAsExpr());
4595  }
4596  }
4597 
4598  break;
4599  case TemplateArgument::Pack: HandleTemplateParameterPack(arg.pack_elements()); break;
4600  case TemplateArgument::Template:
4601  mOutputFormatHelper.Append(GetName(*arg.getAsTemplate().getAsTemplateDecl()));
4602  break;
4603  case TemplateArgument::TemplateExpansion:
4604  mOutputFormatHelper.Append(GetName(*arg.getAsTemplateOrTemplatePattern().getAsTemplateDecl()));
4605  break;
4606  case TemplateArgument::Null: mOutputFormatHelper.Append("null"sv); break;
4607  case TemplateArgument::StructuralValue: mOutputFormatHelper.Append(arg.getAsStructuralValue()); break;
4608  }
4609 }
4610 //-----------------------------------------------------------------------------
4611 
4612 void CodeGenerator::HandleLocalStaticNonTrivialClass(const VarDecl* stmt)
4613 {
4614  EnableGlobalInsert(GlobalInserts::HeaderNew);
4615 
4616  const auto& ctx = GetGlobalAST();
4617 
4618  auto& langOpts{GetLangOpts(*stmt)};
4619  const bool threadSafe{langOpts.ThreadsafeStatics and langOpts.CPlusPlus11 and
4620  (stmt->isLocalVarDecl() /*|| NonTemplateInline*/) and not stmt->getTLSKind()};
4621 
4622  const std::string internalVarName{BuildInternalVarName(GetName(*stmt))};
4623  const std::string compilerBoolVarName{StrCat(internalVarName, "Guard"sv)};
4624 
4625  // insert compiler bool to track init state
4626  auto* compilerGuardVar =
4627  Variable(compilerBoolVarName, threadSafe ? Typedef("uint64_t"sv, ctx.UnsignedLongTy) : ctx.BoolTy);
4628  compilerGuardVar->setStorageClass(StorageClass::SC_Static);
4629  InsertArg(compilerGuardVar);
4630 
4631  // insert compiler memory place holder
4632  auto* compilerStorageVar = Variable(internalVarName,
4633  ctx.getConstantArrayType(ctx.CharTy,
4634  llvm::APInt(ctx.getTypeSize(ctx.getSizeType()), 0),
4635  Sizeof(stmt->getType()),
4636  ArraySizeModifier::Normal,
4637  0));
4638 
4639  compilerStorageVar->setStorageClass(StorageClass::SC_Static);
4640 
4641  auto* alignedAttr =
4642  AlignedAttr::CreateImplicit(const_cast<ASTContext&>(ctx),
4643  true,
4644  Sizeof(stmt->getType()), // ctx.getTrivialTypeSourceInfo(stmt->getType()),
4645  {},
4646  AlignedAttr::Spelling::Keyword_alignas);
4647 
4648  compilerStorageVar->addAttr(alignedAttr);
4649 
4650  const std::string typeName{GetName(stmt->getType())};
4651  mOutputFormatHelper.AppendSemiNewLine(
4652  "alignas("sv, typeName, ") static char "sv, internalVarName, "[sizeof("sv, typeName, ")]"sv);
4653 
4654  // insert compiler init if
4655  mOutputFormatHelper.AppendNewLine();
4656 
4657  // try to find out whether this ctor or the CallExpr can throw. If, then additional code needs to be generated
4658  // for exception handling.
4659  const bool canThrow{[&] {
4660  const ValueDecl* decl = [&]() -> const ValueDecl* {
4661  const auto* init = stmt->getInit()->IgnoreCasts();
4662  if(const auto* ctorExpr = dyn_cast_or_null<CXXConstructExpr>(init)) {
4663  return ctorExpr->getConstructor();
4664  } else if(const auto* callExpr = dyn_cast_or_null<CallExpr>(init)) {
4665  return callExpr->getDirectCallee();
4666  }
4667 
4668  return nullptr;
4669  }();
4670 
4671  if(decl) {
4672  if(const auto* func = decl->getType()->castAs<FunctionProtoType>()) {
4673  return not func->isNothrow();
4674  }
4675  }
4676 
4677  return false;
4678  }()};
4679 
4680  // VarDecl of a static expression always have an initializer
4681  auto* init = const_cast<Expr*>(stmt->getInit());
4682 
4683  if(const bool isCallExpr{not isa<CXXConstructExpr>(init->IgnoreCasts())}; isCallExpr) {
4684  // we have a function call
4685  init = Call("std::move"sv, {init});
4686 
4687  // Tests show that the compiler does better than std::move
4688  EnableGlobalInsert(GlobalInserts::HeaderUtility);
4689  }
4690 
4691  // the allocation and guard update:
4692  // new (&__s)T();
4693  // __sGuard = true;
4694  auto type = stmt->getType();
4695  type.removeLocalConst(); // Issue369.cpp is a const static variable. Should the ctor use the const?
4696  SmallVector<Stmt*, 4> allocAndFlagBodyStmts{New({Ref(compilerStorageVar)}, init, type),
4697  Assign(compilerGuardVar, Bool(true))};
4698  auto allocAndFlagBodyCompound = mkCompoundStmt(allocAndFlagBodyStmts);
4699 
4700  StmtsContainer innerBodyStmts{};
4701 
4702  // Need to insert a try catch, if the constructor/initializer can throw
4703  if(canThrow) {
4704  innerBodyStmts.AddBodyStmts(
4705  Try(allocAndFlagBodyCompound, Catch({Call("__cxa_guard_abort"sv, {Ref(compilerGuardVar)}), Throw()})));
4706  } else {
4707  innerBodyStmts.AddBodyStmts(allocAndFlagBodyCompound);
4708  }
4709 
4710  StmtsContainer bodyStmts{};
4711 
4712  if(threadSafe) {
4713  innerBodyStmts.AddBodyStmts(Call("__cxa_guard_release"sv, {Ref(compilerGuardVar)}));
4714  innerBodyStmts.Add(Comment(
4715  StrCat("__cxa_atexit("sv, typeName, "::~"sv, typeName, ", &"sv, internalVarName, ", &__dso_handle);"sv)));
4716 
4717  auto* aquireIf = If(Call("__cxa_guard_acquire"sv, {Ref(compilerGuardVar)}), innerBodyStmts);
4718  bodyStmts.AddBodyStmts(aquireIf);
4719  } else {
4720  bodyStmts.AddBodyStmts(allocAndFlagBodyCompound);
4721  }
4722 
4723  InsertArg(If(Equal(And(compilerGuardVar, Int32(0xff)), Int32(0)), bodyStmts));
4724 }
4725 //-----------------------------------------------------------------------------
4726 
4727 std::string_view CodeGenerator::GetBuiltinTypeSuffix(const BuiltinType::Kind& kind)
4728 {
4729 #define CASE(K, retVal) \
4730  case BuiltinType::K: return retVal
4731  switch(kind) {
4732  CASE(UInt, "U"sv);
4733  CASE(ULong, "UL"sv);
4734  CASE(ULongLong, "ULL"sv);
4735  CASE(UInt128, "ULLL"sv);
4736  CASE(Long, "L"sv);
4737  CASE(LongLong, "LL"sv);
4738  CASE(Float, "F"sv);
4739  CASE(LongDouble, "L"sv);
4740  default: return {};
4741  }
4742 #undef BTCASE
4743 }
4744 //-----------------------------------------------------------------------------
4745 
4746 void CodeGenerator::HandleLambdaExpr(const LambdaExpr* lambda, LambdaHelper& lambdaHelper)
4747 {
4748  OutputFormatHelper& outputFormatHelper = lambdaHelper.buffer();
4749 
4750  outputFormatHelper.AppendNewLine();
4751  LambdaCodeGenerator codeGenerator{outputFormatHelper, mLambdaStack, mProcessingPrimaryTemplate};
4752  codeGenerator.mCapturedThisAsCopy = ranges::any_of(
4753  lambda->captures(), [](auto& c) { return (c.capturesThis() and (c.getCaptureKind() == LCK_StarThis)); });
4754 
4755  codeGenerator.mLambdaExpr = lambda;
4756  codeGenerator.InsertArg(lambda->getLambdaClass());
4757 }
4758 //-----------------------------------------------------------------------------
4759 
4760 void CodeGenerator::InsertConceptConstraint(const llvm::SmallVectorImpl<const Expr*>& constraints,
4761  const InsertInline insertInline)
4762 {
4763  for(OnceTrue first{}; const auto* c : constraints) {
4764  if(first and (InsertInline::Yes == insertInline)) {
4765  mOutputFormatHelper.Append(' ');
4766  }
4767 
4768  mOutputFormatHelper.Append(kwRequiresSpace);
4769  InsertArg(c);
4770 
4771  if(InsertInline::No == insertInline) {
4772  mOutputFormatHelper.AppendNewLine();
4773  }
4774  }
4775 }
4776 //-----------------------------------------------------------------------------
4777 
4778 // This inserts the requires clause after template<...>
4779 void CodeGenerator::InsertConceptConstraint(const TemplateParameterList& tmplDecl)
4780 {
4781  if(const auto* reqClause = tmplDecl.getRequiresClause()) {
4782  SmallVector<const Expr*, 1> constraints{reqClause};
4783 
4784  InsertConceptConstraint(constraints, InsertInline::No);
4785  }
4786 }
4787 //-----------------------------------------------------------------------------
4788 
4789 // This inserts the requires clause after the function header
4790 void CodeGenerator::InsertConceptConstraint(const FunctionDecl* tmplDecl)
4791 {
4792  SmallVector<const Expr*, 5> constraints{};
4793  tmplDecl->getAssociatedConstraints(constraints);
4794 
4795  InsertConceptConstraint(constraints, InsertInline::Yes);
4796 }
4797 //-----------------------------------------------------------------------------
4798 
4799 // This inserts the requires clause after a variable type
4800 void CodeGenerator::InsertConceptConstraint(const VarDecl* varDecl)
4801 {
4802  if(const auto* t = varDecl->getType()->getContainedAutoType()) {
4803  if(t->getTypeConstraintConcept()) {
4804 #if 0
4805  mOutputFormatHelper.Append(kwCommentStart, t->getTypeConstraintConcept()->getName(), kwCCommentEndSpace);
4806 #endif
4807  }
4808  }
4809 }
4810 //-----------------------------------------------------------------------------
4811 
4812 void CodeGenerator::InsertFunctionNameWithReturnType(const FunctionDecl& decl,
4813  const CXXConstructorDecl* cxxInheritedCtorDecl)
4814 {
4815  bool isLambda{false};
4816  bool isFirstCxxMethodDecl{true};
4817  const auto* methodDecl{dyn_cast_or_null<CXXMethodDecl>(&decl)};
4818  bool isCXXMethodDecl{nullptr != methodDecl};
4819  const bool isClassTemplateSpec{isCXXMethodDecl and isa<ClassTemplateSpecializationDecl>(methodDecl->getParent())};
4820  const bool requiresComment{isCXXMethodDecl and not methodDecl->isUserProvided() and
4821  not methodDecl->isExplicitlyDefaulted()};
4822  // [expr.prim.lambda.closure] p7 consteval/constexpr are obtained from the call operator
4823  const bool isLambdaStaticInvoker{isCXXMethodDecl and methodDecl->isLambdaStaticInvoker()};
4824  const FunctionDecl& constExprDecl{not isLambdaStaticInvoker ? decl
4825  : *methodDecl->getParent()->getLambdaCallOperator()};
4826  const auto desugaredReturnType = GetType(GetDesugarReturnType(decl));
4827 
4828  if(methodDecl) {
4829  if(requiresComment) {
4830  mOutputFormatHelper.Append(kwCppCommentStartSpace);
4831  }
4832 
4833  isLambda = methodDecl->getParent()->isLambda();
4834  isFirstCxxMethodDecl = (nullptr == methodDecl->getPreviousDecl());
4835  }
4836 
4837  // types of conversion decls can be invalid to type at this place. So introduce a using
4838  if(isa<CXXConversionDecl>(decl) and TypeContainsSubType<PointerType, FunctionProtoType>(desugaredReturnType)) {
4839  mOutputFormatHelper.AppendSemiNewLine(
4840  kwUsingSpace, BuildRetTypeName(decl), hlpAssing, GetName(desugaredReturnType));
4841  }
4842 
4843  if(isCXXMethodDecl and decl.isOutOfLine()) {
4844  if(const auto* parent = methodDecl->getParent()) {
4845  if(const auto* outerClasTemplateDecl = parent->getDescribedClassTemplate()) {
4846  InsertTemplateParameters(*outerClasTemplateDecl->getTemplateParameters());
4847  }
4848  }
4849  }
4850 
4851  if(decl.isTemplated()) {
4852  if(decl.getDescribedTemplate()) {
4853  InsertTemplateParameters(*decl.getDescribedTemplate()->getTemplateParameters());
4854  }
4855 
4856  } else if(decl.isFunctionTemplateSpecialization() or (isClassTemplateSpec and decl.isOutOfLine() and
4857  (decl.getLexicalDeclContext() != methodDecl->getParent()))) {
4858  InsertTemplateSpecializationHeader(decl);
4859  }
4860 
4861  InsertAttributes(decl.attrs());
4862 
4863  if(not decl.isFunctionTemplateSpecialization() or (isCXXMethodDecl and isFirstCxxMethodDecl)) {
4864  if(not decl.isOutOfLine() or (decl.getStorageClass() == SC_Extern)) {
4865  mOutputFormatHelper.Append(GetStorageClassAsStringWithSpace(decl.getStorageClass()));
4866  }
4867 
4868  // [class.free]: Any allocation function for a class T is a static member (even if not explicitly declared
4869  // static). (https://eel.is/c++draft/class.free#1)
4870  // However, Clang does not add `static` to `getStorageClass` so this needs to be check independently.
4871  if(isCXXMethodDecl and not decl.isOutOfLine()) {
4872  // GetStorageClassAsStringWithSpace already carries static, if the method was marked so explicitly
4873  if((not IsStaticStorageClass(methodDecl)) and (methodDecl->isStatic())) {
4874  mOutputFormatHelper.Append(kwStaticSpace);
4875  }
4876  }
4877  }
4878 
4879  if(Decl::FOK_None != decl.getFriendObjectKind()) {
4880  mOutputFormatHelper.Append(kwFriendSpace);
4881  }
4882 
4883  if(decl.isInlined()) {
4884  mOutputFormatHelper.Append(kwInlineSpace);
4885  }
4886 
4887  if(methodDecl and isFirstCxxMethodDecl) {
4888  if(methodDecl->isVirtual()) {
4889  mOutputFormatHelper.Append(kwVirtualSpace);
4890  }
4891 
4892  const auto exspec = ExplicitSpecifier::getFromDecl(methodDecl);
4893 
4894  if(const auto* expr = exspec.getExpr()) {
4895  mOutputFormatHelper.Append(kwExplicit);
4896 
4897  WrapInParens(
4898  [&] {
4899  switch(exspec.getKind()) {
4900  case ExplicitSpecKind::Unresolved: InsertArg(expr); break;
4901  case ExplicitSpecKind::ResolvedFalse: mOutputFormatHelper.Append(kwFalse); break;
4902  case ExplicitSpecKind::ResolvedTrue: mOutputFormatHelper.Append("true"sv); break;
4903  }
4904  },
4905  AddSpaceAtTheEnd::Yes);
4906 
4907  } else if(exspec.isExplicit()) {
4908  mOutputFormatHelper.Append(kwExplicitSpace);
4909  }
4910  }
4911 
4912  if(constExprDecl.isConstexpr()) {
4913  const bool skipConstexpr{isLambda and not isa<CXXConversionDecl>(constExprDecl)};
4914  // Special treatment for a conversion operator in a captureless lambda. It appears that if the call operator
4915  // is consteval the conversion operator must be as well, otherwise it cannot take the address of the invoke
4916  // function.
4917  const bool isConversionOpWithConstevalCallOp{[&]() {
4918  if(methodDecl) {
4919  if(const auto callOp = methodDecl->getParent()->getLambdaCallOperator()) {
4920  return callOp->isConsteval();
4921  }
4922  }
4923 
4924  return false;
4925  }()};
4926 
4927  if(not isConversionOpWithConstevalCallOp and constExprDecl.isConstexprSpecified()) {
4928  if(skipConstexpr) {
4929  mOutputFormatHelper.Append(kwCommentStart);
4930  }
4931 
4932  mOutputFormatHelper.Append(kwConstExprSpace);
4933 
4934  if(skipConstexpr) {
4935  mOutputFormatHelper.Append(kwCCommentEndSpace);
4936  }
4937 
4938  } else if(isConversionOpWithConstevalCallOp or constExprDecl.isConsteval()) {
4939  mOutputFormatHelper.Append(kwConstEvalSpace);
4940  }
4941  }
4942 
4943  // temporary output to be able to handle a return value of array reference
4944  OutputFormatHelper outputFormatHelper{};
4945 
4946  if(methodDecl) {
4947  if(not isFirstCxxMethodDecl or InsertNamespace() and decl.getQualifier()) {
4948  CodeGeneratorVariant cg{outputFormatHelper};
4949  cg->InsertNamespace(decl.getQualifier());
4950 
4951  // This comes from a using Base::SomeFunc
4952  } else if(not isFirstCxxMethodDecl or InsertNamespace() and not decl.getQualifier()) {
4953  const auto* parent = methodDecl->getParent();
4954  outputFormatHelper.Append(parent->getName(), "::"sv);
4955  }
4956  }
4957 
4958  if(not isa<CXXConversionDecl>(decl)) {
4959  if(isa<CXXConstructorDecl>(decl) or isa<CXXDestructorDecl>(decl)) {
4960  if(methodDecl) {
4961  if(isa<CXXDestructorDecl>(decl)) {
4962  outputFormatHelper.Append('~');
4963  }
4964 
4965  outputFormatHelper.Append(GetName(*methodDecl->getParent()));
4966  }
4967 
4968  } else {
4969  outputFormatHelper.Append(GetName(decl));
4970  }
4971 
4972  if(isFirstCxxMethodDecl and decl.isFunctionTemplateSpecialization()) {
4973  CodeGeneratorVariant codeGenerator{outputFormatHelper};
4974  codeGenerator->InsertTemplateArgs(decl);
4975  }
4976 
4977  outputFormatHelper.Append('(');
4978  }
4979 
4980  // if a CXXInheritedCtorDecl was passed as a pointer us this to get the parameters from.
4981  if(cxxInheritedCtorDecl) {
4982  outputFormatHelper.AppendParameterList(cxxInheritedCtorDecl->parameters(),
4983  OutputFormatHelper::NameOnly::No,
4984  OutputFormatHelper::GenMissingParamName::Yes);
4985  } else {
4986  // The static invoker needs parameter names to forward parameters to the call operator even when the call
4987  // operator doesn't care about them.
4988  const OutputFormatHelper::GenMissingParamName genMissingParamName{
4989  isLambdaStaticInvoker ? OutputFormatHelper::GenMissingParamName::Yes
4990  : OutputFormatHelper::GenMissingParamName::No};
4991 
4992  outputFormatHelper.AppendParameterList(
4993  decl.parameters(), OutputFormatHelper::NameOnly::No, genMissingParamName);
4994 
4995  if(GetInsightsOptions().UseShow2C and not decl.isVariadic() and decl.param_empty()) {
4996  outputFormatHelper.Append("void"sv);
4997  }
4998  }
4999 
5000  if(decl.isVariadic()) {
5001  outputFormatHelper.Append(", ..."sv);
5002  }
5003 
5004  outputFormatHelper.Append(')');
5005 
5006  if(not isa<CXXConstructorDecl>(decl) and not isa<CXXDestructorDecl>(decl)) {
5007  if(isa<CXXConversionDecl>(decl)) {
5008  const std::string typeName{TypeContainsSubType<PointerType, FunctionProtoType>(desugaredReturnType)
5009  ? BuildRetTypeName(decl)
5010  : GetName(desugaredReturnType)};
5011 
5012  mOutputFormatHelper.Append(kwOperatorSpace, typeName, " ("sv, outputFormatHelper.GetString());
5013  } else {
5014  mOutputFormatHelper.Append(GetTypeNameAsParameter(desugaredReturnType, outputFormatHelper.GetString()));
5015  }
5016  } else {
5017  mOutputFormatHelper.Append(outputFormatHelper);
5018  }
5019 
5020  mOutputFormatHelper.Append(GetConst(decl));
5021 
5022  if(methodDecl) {
5023  if(methodDecl->isVolatile()) {
5024  mOutputFormatHelper.Append(kwSpaceVolatile);
5025  }
5026 
5027  if(methodDecl->hasAttr<FinalAttr>()) {
5028  mOutputFormatHelper.Append(kwSpaceFinal);
5029  }
5030  }
5031 
5032  switch(decl.getType()->getAs<FunctionProtoType>()->getRefQualifier()) {
5033  case RQ_None: break;
5034  case RQ_LValue: mOutputFormatHelper.Append(" &"sv); break;
5035  case RQ_RValue: mOutputFormatHelper.Append(" &&"sv); break;
5036  }
5037 
5038  mOutputFormatHelper.Append(GetNoExcept(decl));
5039 
5040  // insert the trailing requires-clause, if any. In case, this is a template then we already inserted the
5041  // template requires-clause during creation of the template head.
5042  InsertConceptConstraint(&decl);
5043 
5044  if(decl.isPureVirtual()) {
5045  mOutputFormatHelper.Append(" = 0"sv);
5046  }
5047 
5048  if(decl.isDeleted()) {
5049  mOutputFormatHelper.Append(kwSpaceEqualsDelete);
5050  if(auto* delInfo = decl.getDefalutedOrDeletedInfo()) {
5051  WrapInParens([&]() { InsertArg(delInfo->getDeletedMessage()); }, AddSpaceAtTheEnd::No);
5052  } else {
5053  mOutputFormatHelper.AppendSemiNewLine();
5054  }
5055 
5056  } else if(decl.isDefaulted()) {
5057  mOutputFormatHelper.AppendNewLine(kwSpaceEqualsDefault);
5058  }
5059 }
5060 //-----------------------------------------------------------------------------
5061 
5062 void CodeGenerator::InsertCurlysIfRequired(const Stmt* stmt)
5063 {
5064  const bool requiresCurlys{not isa<InitListExpr>(stmt) and not isa<ParenExpr>(stmt) and
5065  not isa<CXXDefaultInitExpr>(stmt)};
5066 
5067  if(requiresCurlys) {
5068  mOutputFormatHelper.Append('{');
5069  }
5070 
5071  InsertArg(stmt);
5072 
5073  if(requiresCurlys) {
5074  mOutputFormatHelper.Append('}');
5075  }
5076 }
5077 //-----------------------------------------------------------------------------
5078 
5079 void CodeGenerator::WrapInParensOrCurlys(const BraceKind braceKind,
5080  void_func_ref lambda,
5081  const AddSpaceAtTheEnd addSpaceAtTheEnd)
5082 {
5083  if(BraceKind::Curlys == braceKind) {
5084  mOutputFormatHelper.Append('{');
5085  } else {
5086  mOutputFormatHelper.Append('(');
5087  }
5088 
5089  lambda();
5090 
5091  if(BraceKind::Curlys == braceKind) {
5092  mOutputFormatHelper.Append('}');
5093  } else {
5094  mOutputFormatHelper.Append(')');
5095  }
5096 
5097  if(AddSpaceAtTheEnd::Yes == addSpaceAtTheEnd) {
5098  mOutputFormatHelper.Append(' ');
5099  }
5100 }
5101 //-----------------------------------------------------------------------------
5102 
5103 void CodeGenerator::WrapInCompoundIfNeeded(const Stmt* stmt, const AddNewLineAfter addNewLineAfter)
5104 {
5105  const bool hasNoCompoundStmt = not(isa<CompoundStmt>(stmt) or isa<AttributedStmt>(stmt));
5106 
5107  if(hasNoCompoundStmt) {
5108  mOutputFormatHelper.OpenScope();
5109  }
5110 
5111  if(not isa<NullStmt>(stmt)) {
5112  InsertArg(stmt);
5113 
5114  const bool isAttrWithCompound{[&] {
5115  auto* attrStmt = dyn_cast_or_null<AttributedStmt>(stmt);
5116  return attrStmt and isa<CompoundStmt>(attrStmt->getSubStmt());
5117  }()};
5118 
5119  // Add semi-colon if necessary. A do{} while does already add one.
5120  if(IsStmtRequiringSemi<IfStmt, CompoundStmt, NullStmt, WhileStmt, DoStmt>(stmt) and not isAttrWithCompound) {
5121  mOutputFormatHelper.AppendSemiNewLine();
5122  }
5123  }
5124 
5125  if(hasNoCompoundStmt) {
5126  mOutputFormatHelper.CloseScope(OutputFormatHelper::NoNewLineBefore::Yes);
5127  }
5128 
5129  const bool addNewLine = (AddNewLineAfter::Yes == addNewLineAfter);
5130  if(addNewLine or (hasNoCompoundStmt and addNewLine)) {
5131  mOutputFormatHelper.AppendNewLine();
5132  } else if(not addNewLine or (hasNoCompoundStmt and not addNewLine)) {
5133  mOutputFormatHelper.Append(' ');
5134  }
5135 }
5136 //-----------------------------------------------------------------------------
5137 
5138 void CodeGenerator::WrapInParens(void_func_ref lambda, const AddSpaceAtTheEnd addSpaceAtTheEnd)
5139 {
5140  WrapInParensOrCurlys(BraceKind::Parens, lambda, addSpaceAtTheEnd);
5141 }
5142 //-----------------------------------------------------------------------------
5143 
5144 void CodeGenerator::WrapInParensIfNeeded(bool needsParens,
5145  void_func_ref lambda,
5146  const AddSpaceAtTheEnd addSpaceAtTheEnd)
5147 {
5148  if(needsParens) {
5149  WrapInParensOrCurlys(BraceKind::Parens, lambda, addSpaceAtTheEnd);
5150  } else {
5151  lambda();
5152  }
5153 }
5154 //-----------------------------------------------------------------------------
5155 
5156 void CodeGenerator::WrapInCurliesIfNeeded(bool needsParens,
5157  void_func_ref lambda,
5158  const AddSpaceAtTheEnd addSpaceAtTheEnd)
5159 {
5160  if(needsParens) {
5161  WrapInParensOrCurlys(BraceKind::Curlys, lambda, addSpaceAtTheEnd);
5162  } else {
5163  lambda();
5164  }
5165 }
5166 //-----------------------------------------------------------------------------
5167 
5168 void CodeGenerator::WrapInCurlys(void_func_ref lambda, const AddSpaceAtTheEnd addSpaceAtTheEnd)
5169 {
5170  WrapInParensOrCurlys(BraceKind::Curlys, lambda, addSpaceAtTheEnd);
5171 }
5172 //-----------------------------------------------------------------------------
5173 
5174 void CodeGenerator::InsertArg(const BindingDecl*)
5175 {
5176  // We ignore this here in the global level. In some cases a BindingDecl appears _before_ the DecompositionDecl
5177  // which leads to invalid code. See StructuredBindingsHandler3Test.cpp.
5178 }
5179 //-----------------------------------------------------------------------------
5180 
5181 void StructuredBindingsCodeGenerator::InsertArg(const BindingDecl* stmt)
5182 {
5183  const auto* bindingStmt = stmt->getBinding();
5184 
5185  // In a dependent context we have no binding and with that no type. Leave this as it is, we are looking at a
5186  // primary template here.
5187  RETURN_IF(not bindingStmt);
5188 
5189  // Assume that we are looking at a builtin type. We have to construct the variable declaration information.
5190  auto type = stmt->getType();
5191 
5192  // If we have a holding var we are looking at a user defined type like tuple and those the defaults from above
5193  // are wrong. This type contains the variable declaration so we insert this.
5194  if(const auto* holdingVar = stmt->getHoldingVar()) {
5195  // Initial paper: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0144r0.pdf
5196 
5197  // The type of the binding depends on the initializer. In case the initializer is an lvalue we get a T&,
5198  // otherwise a T&&. We typically look at an lvalue if the decomposition declaration was auto& [a,b]. Note
5199  // the & here We have a rvalue in case the decomposition declaration was auto [a,b]. Note no reference. The
5200  // standard std::get returns a lvalue reference in case e in get(e) is an lvalue, otherwise it returns an
5201  // rvalue reference because then the call is get(std::move(e))
5202  type = holdingVar->getType().getCanonicalType();
5203 
5204  bindingStmt = holdingVar->getAnyInitializer();
5205 
5206  } else if(not type->isLValueReferenceType()) {
5207  type = stmt->getASTContext().getLValueReferenceType(type);
5208  }
5209 
5210  InsertAttributes(stmt->attrs());
5211 
5212  mOutputFormatHelper.Append(GetQualifiers(*dyn_cast_or_null<VarDecl>(stmt->getDecomposedDecl())),
5213  GetTypeNameAsParameter(type, GetName(*stmt)),
5214  hlpAssing);
5215 
5216  InsertArg(bindingStmt);
5217 
5218  mOutputFormatHelper.AppendSemiNewLine();
5219 }
5220 //-----------------------------------------------------------------------------
5221 
5222 void StructuredBindingsCodeGenerator::InsertDecompositionBindings(const DecompositionDecl& decompositionDeclStmt)
5223 {
5224  for(const auto* bindingDecl : decompositionDeclStmt.bindings()) {
5225  InsertArg(bindingDecl);
5226  }
5227 }
5228 //-----------------------------------------------------------------------------
5229 
5230 void StructuredBindingsCodeGenerator::InsertArg(const DeclRefExpr* stmt)
5231 {
5232  const auto name = GetName(*stmt);
5233 
5234  mOutputFormatHelper.Append(name);
5235 
5236  if(name.empty()) {
5237  mOutputFormatHelper.Append(mVarName);
5238  } else {
5239  InsertTemplateArgs(*stmt);
5240  }
5241 }
5242 //-----------------------------------------------------------------------------
5243 
5244 void LambdaCodeGenerator::InsertArg(const CXXThisExpr* stmt)
5245 {
5246  DPrint("thisExpr: imlicit=%d %s\n", stmt->isImplicit(), GetName(GetDesugarType(stmt->getType())));
5247 
5248  if(mCapturedThisAsCopy) {
5249  mOutputFormatHelper.Append("(&"sv, kwInternalThis, ")"sv);
5250 
5251  } else {
5252  mOutputFormatHelper.Append(kwInternalThis);
5253  }
5254 }
5255 //-----------------------------------------------------------------------------
5256 
5257 } // 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