InsightsHelpers.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 "InsightsHelpers.h"
9 #include "ASTHelpers.h"
10 #include "ClangCompat.h"
11 #include "CodeGenerator.h"
12 #include "DPrint.h"
13 #include "Insights.h"
14 #include "InsightsStaticStrings.h"
15 #include "OutputFormatHelper.h"
16 #include "clang/Frontend/CompilerInstance.h"
17 #include "clang/Sema/Lookup.h"
18 //-----------------------------------------------------------------------------
19 
20 namespace clang::insights {
21 
23 : mStack{mGlobalStack}
24 , mHelper{mScope.length()}
25 {
26  mStack.push(mHelper);
27 
28  if(const auto* recordDecl = dyn_cast_or_null<CXXRecordDecl>(d)) {
29  mScope.append(GetName(*recordDecl));
30 
31  if(const auto* classTmplSpec = dyn_cast_or_null<ClassTemplateSpecializationDecl>(recordDecl)) {
32  OutputFormatHelper ofm{};
33  CodeGenerator codeGenerator{ofm};
34  codeGenerator.InsertTemplateArgs(*classTmplSpec);
35 
36  mScope.append(ofm);
37  }
38 
39  } else if(const auto* namespaceDecl = dyn_cast_or_null<NamespaceDecl>(d)) {
40  mScope.append(namespaceDecl->getName());
41  }
42 
43  if(not mScope.empty()) {
44  mScope.append("::");
45  }
46 }
47 //-----------------------------------------------------------------------------
48 
50 {
51  const auto length = mStack.pop()->mLength;
52  mScope.resize(length);
53 }
54 //-----------------------------------------------------------------------------
55 
56 std::string ScopeHandler::RemoveCurrentScope(std::string name)
57 {
58  if(mScope.length()) {
59  auto findAndReplace = [&name](const std::string& scope) {
60  if(const auto startPos = name.find(scope, 0); std::string::npos != startPos) {
61  if(const auto pos = startPos + scope.length();
62  (pos > name.length()) or (name[pos] != '*')) { // keep member points (See #374)
63  name.replace(startPos, scope.length(), ""sv);
64  return true;
65  }
66  }
67 
68  return false;
69  };
70 
71  // The default is that we can replace the entire scope. Suppose we are currently in N::X and having a symbol
72  // N::X::y then N::X:: is removed.
73  if(not findAndReplace(mScope)) {
74 
75  // A special case where we need to remove the scope without the last item.
76  std::string tmp{mScope};
77  tmp.resize(mGlobalStack.back().mLength);
78 
79  findAndReplace(tmp);
80  }
81  }
82 
83  return name;
84 }
85 //-----------------------------------------------------------------------------
86 
87 static std::string GetNamePlain(const NamedDecl& decl)
88 {
89  if(const auto* fd = dyn_cast_or_null<FunctionDecl>(&decl); fd and GetInsightsOptions().UseShow2C) {
90  if(fd->isOverloadedOperator()) {
91  switch(fd->getOverloadedOperator()) {
92 #define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemberOnly) \
93  case OO_##Name: return "operator" #Name;
94 
95 #include "clang/Basic/OperatorKinds.def"
96 
97 #undef OVERLOADED_OPERATOR
98  default: break;
99  }
100  }
101  }
102 
103  return decl.getDeclName().getAsString();
104 }
105 
106 std::string GetPlainName(const DeclRefExpr& DRE)
107 {
108  return ScopeHandler::RemoveCurrentScope(GetNamePlain(*DRE.getDecl()));
109 }
110 //-----------------------------------------------------------------------------
111 
112 STRONG_BOOL(InsightsSuppressScope);
113 //-----------------------------------------------------------------------------
114 
115 STRONG_BOOL(InsightsCanonicalTypes);
116 //-----------------------------------------------------------------------------
117 
118 static std::string GetUnqualifiedScopelessName(const Type* type, const InsightsSuppressScope supressScope);
119 //-----------------------------------------------------------------------------
120 
122 {
123  unsigned CppInsightsUnqualified : 1; // NOLINT
124  InsightsSuppressScope CppInsightsSuppressScope; // NOLINT
125 
126  CppInsightsPrintingPolicy(const Unqualified unqualified,
127  const InsightsSuppressScope supressScope,
128  const InsightsCanonicalTypes insightsCanonicalTypes = InsightsCanonicalTypes::No)
129  : PrintingPolicy{LangOptions{}}
130  {
131  adjustForCPlusPlus();
132  SuppressUnwrittenScope = true;
133  Alignof = true;
134  ConstantsAsWritten = true;
135  AnonymousTagLocations = false; // does remove filename and line for from lambdas in parameters
136  PrintCanonicalTypes = InsightsCanonicalTypes::Yes == insightsCanonicalTypes;
137 
138  CppInsightsUnqualified = (Unqualified::Yes == unqualified);
139  CppInsightsSuppressScope = supressScope;
140  }
141 
143  : CppInsightsPrintingPolicy{Unqualified::No, InsightsSuppressScope::No}
144  {
145  }
146 };
147 //-----------------------------------------------------------------------------
148 
149 void ReplaceAll(std::string& str, std::string_view from, std::string_view to)
150 {
151  size_t start_pos = 0;
152  while((start_pos = str.find(from, start_pos)) != std::string::npos) {
153  str.replace(start_pos, from.length(), to);
154  start_pos += to.length(); // Handles case where 'to' is a substring of 'from'
155  }
156 }
157 //-----------------------------------------------------------------------------
158 
159 namespace details {
160 static void
161 BuildNamespace(std::string& fullNamespace, const NestedNameSpecifier* stmt, const IgnoreNamespace ignoreNamespace)
162 {
163  RETURN_IF(not stmt);
164 
165  if(const auto* prefix = stmt->getPrefix();
166  prefix and not((NestedNameSpecifier::TypeSpecWithTemplate == stmt->getKind()) and
167  isa<DependentTemplateSpecializationType>(stmt->getAsType()))) {
168  BuildNamespace(fullNamespace, prefix, ignoreNamespace);
169  }
170 
171  switch(stmt->getKind()) {
172  case NestedNameSpecifier::Identifier: fullNamespace.append(stmt->getAsIdentifier()->getName()); break;
173 
174  case NestedNameSpecifier::Namespace:
175  RETURN_IF((IgnoreNamespace::Yes == ignoreNamespace) or (stmt->getAsNamespace()->isAnonymousNamespace()));
176 
177  fullNamespace.append(stmt->getAsNamespace()->getName());
178  break;
179 
180  case NestedNameSpecifier::NamespaceAlias: fullNamespace.append(stmt->getAsNamespaceAlias()->getName()); break;
181 
182  case NestedNameSpecifier::TypeSpecWithTemplate:
183  if(auto* dependentSpecType = stmt->getAsType()->getAs<DependentTemplateSpecializationType>()) {
184  fullNamespace.append(GetElaboratedTypeKeyword(dependentSpecType->getKeyword()));
185  }
186 
187  [[fallthrough]];
188 
189  case NestedNameSpecifier::TypeSpec:
190  fullNamespace.append(GetUnqualifiedScopelessName(stmt->getAsType(), InsightsSuppressScope::Yes));
191  // The template parameters are already contained in the type we inserted above.
192  break;
193 
194  default: break;
195  }
196 
197  fullNamespace.append("::"sv);
198 }
199 //-----------------------------------------------------------------------------
200 } // namespace details
201 
202 std::string GetNestedName(const NestedNameSpecifier* nns, const IgnoreNamespace ignoreNamespace)
203 {
204  std::string ret{};
205 
206  if(nns) {
207  details::BuildNamespace(ret, nns, ignoreNamespace);
208  }
209 
210  return ret;
211 }
212 //-----------------------------------------------------------------------------
213 
214 static const std::string GetAsCPPStyleString(const QualType& t, const CppInsightsPrintingPolicy& printingPolicy)
215 {
216  return t.getAsString(printingPolicy);
217 }
218 //-----------------------------------------------------------------------------
219 
220 std::string BuildInternalVarName(const std::string_view& varName)
221 {
222  return StrCat("__", varName);
223 }
224 //-----------------------------------------------------------------------------
225 
226 static std::string
227 BuildInternalVarName(const std::string_view& varName, const SourceLocation& loc, const SourceManager& sm)
228 {
229  const auto lineNo = sm.getSpellingLineNumber(loc);
230 
231  return StrCat(BuildInternalVarName(varName), lineNo);
232 }
233 //-----------------------------------------------------------------------------
234 
235 void InsertBefore(std::string& source, const std::string_view& find, const std::string_view& replace)
236 {
237  const std::string::size_type i = source.find(find, 0);
238 
239  if(std::string::npos != i) {
240  source.insert(i, replace);
241  }
242 }
243 //-----------------------------------------------------------------------------
244 
245 static void InsertAfter(std::string& source, const std::string_view& find, const std::string_view& replace)
246 {
247  const std::string::size_type i = source.find(find, 0);
248 
249  if(std::string::npos != i) {
250  source.insert(i + find.length(), replace);
251  }
252 }
253 //-----------------------------------------------------------------------------
254 
255 std::string MakeLineColumnName(const SourceManager& sm, const SourceLocation& loc, const std::string_view& prefix)
256 {
257  // In case of a macro expansion the expansion(line/column) number gives a unique value.
258  const auto lineNo = loc.isMacroID() ? sm.getExpansionLineNumber(loc) : sm.getSpellingLineNumber(loc);
259  const auto columnNo = loc.isMacroID() ? sm.getExpansionColumnNumber(loc) : sm.getSpellingColumnNumber(loc);
260 
261  return StrCat(prefix, lineNo, "_"sv, columnNo);
262 }
263 //-----------------------------------------------------------------------------
264 
265 static std::string MakeLineColumnName(const Decl& decl, const std::string_view prefix)
266 {
267  return MakeLineColumnName(GetSM(decl), decl.getBeginLoc(), prefix);
268 }
269 //-----------------------------------------------------------------------------
270 
271 std::string GetLambdaName(const CXXRecordDecl& lambda)
272 {
273  static constexpr auto lambdaPrefix{"__lambda_"sv};
274  return MakeLineColumnName(lambda, lambdaPrefix);
275 }
276 //-----------------------------------------------------------------------------
277 
278 static std::string GetAnonymStructOrUnionName(const CXXRecordDecl& cxxRecordDecl)
279 {
280  static constexpr auto prefix{"__anon_"sv};
281  return MakeLineColumnName(cxxRecordDecl, prefix);
282 }
283 //-----------------------------------------------------------------------------
284 
285 std::string BuildRetTypeName(const Decl& decl)
286 {
287  static constexpr auto retTypePrefix{"retType_"sv};
288  return MakeLineColumnName(decl, retTypePrefix);
289 }
290 //-----------------------------------------------------------------------------
291 
292 const QualType GetDesugarType(const QualType& QT)
293 {
294  if(QT.getTypePtrOrNull()) {
295  if(const auto* autoType = QT->getAs<clang::AutoType>(); autoType and autoType->isSugared()) {
296  const auto dt = autoType->getDeducedType();
297 
298  if(const auto* et = dt->getAs<ElaboratedType>()) {
299  return et->getNamedType();
300  } else {
301  return dt;
302  }
303 
304  } else if(auto declType = QT->getAs<clang::DecltypeType>()) {
305  return declType->desugar();
306  }
307  }
308  return QT;
309 }
310 //-----------------------------------------------------------------------------
311 
312 static const VarDecl* GetVarDeclFromDeclRefExpr(const DeclRefExpr& declRefExpr)
313 {
314  const auto* valueDecl = declRefExpr.getDecl();
315 
316  return dyn_cast_or_null<VarDecl>(valueDecl);
317 }
318 //-----------------------------------------------------------------------------
319 
320 // own implementation due to lambdas
321 std::string GetDeclContext(const DeclContext* ctx, WithTemplateParameters withTemplateParameters)
322 {
323  OutputFormatHelper mOutputFormatHelper{};
324  SmallVector<const DeclContext*, 8> contexts{};
325 
326  while(ctx) {
327  if(isa<NamedDecl>(ctx)) {
328  contexts.push_back(ctx);
329  }
330  ctx = ctx->getParent();
331  }
332 
333  for(const auto* declContext : llvm::reverse(contexts)) {
334  if(const auto* classTmplSpec = dyn_cast<ClassTemplateSpecializationDecl>(declContext)) {
335  mOutputFormatHelper.Append(classTmplSpec->getName());
336 
337  CodeGenerator codeGenerator{mOutputFormatHelper};
338  codeGenerator.InsertTemplateArgs(*classTmplSpec);
339 
340  } else if(const auto* nd = dyn_cast<NamespaceDecl>(declContext)) {
341  if(nd->isAnonymousNamespace() or nd->isInline()) {
342  continue;
343  }
344 
345  mOutputFormatHelper.Append(nd->getName());
346 
347  } else if(const auto* rd = dyn_cast<RecordDecl>(declContext)) {
348  if(not rd->getIdentifier()) {
349  continue;
350  }
351 
352  mOutputFormatHelper.Append(rd->getName());
353 
354  // A special case at least for out-of-line static member variables of a class template. They need to carry
355  // the template parameters of the class template.
356  if(WithTemplateParameters::Yes == withTemplateParameters /*declContext->isNamespace() or declContext->getLexicalParent()->isNamespace() or declContext->getLexicalParent()->isTranslationUnit()*/) {
357  if(const auto* cxxRecordDecl = dyn_cast_or_null<CXXRecordDecl>(rd)) {
358  if(const auto* classTmpl = cxxRecordDecl->getDescribedClassTemplate()) {
359  CodeGenerator codeGenerator{mOutputFormatHelper};
360  codeGenerator.InsertTemplateParameters(*classTmpl->getTemplateParameters(),
361  CodeGenerator::TemplateParamsOnly::Yes);
362  }
363  }
364  }
365 
366  } else if(dyn_cast<FunctionDecl>(declContext)) {
367  continue;
368 
369  } else if(const auto* ed = dyn_cast<EnumDecl>(declContext)) {
370  if(not ed->isScoped()) {
371  continue;
372  }
373 
374  mOutputFormatHelper.Append(ed->getName());
375 
376  } else {
377  mOutputFormatHelper.Append(cast<NamedDecl>(declContext)->getName());
378  }
379 
380  mOutputFormatHelper.Append("::"sv);
381  }
382 
383  return mOutputFormatHelper.GetString();
384 }
385 //-----------------------------------------------------------------------------
386 
387 namespace details {
388 
389 STRONG_BOOL(RemoveCurrentScope); ///!< In some cases we need to keep the scope for a while, so don't remove the scope
390  /// we are in right now.
391 //-----------------------------------------------------------------------------
392 
393 static std::string GetQualifiedName(const NamedDecl& decl,
394  const RemoveCurrentScope removeCurrentScope = RemoveCurrentScope::Yes)
395 {
396  std::string scope{GetDeclContext(decl.getDeclContext())};
397 
398  scope += decl.getName();
399 
400  if(RemoveCurrentScope::Yes == removeCurrentScope) {
401  return ScopeHandler::RemoveCurrentScope(scope);
402  }
403 
404  return scope;
405 }
406 //-----------------------------------------------------------------------------
407 
408 static std::string GetScope(const DeclContext* declCtx,
409  const RemoveCurrentScope removeCurrentScope = RemoveCurrentScope::Yes)
410 {
411  std::string name{};
412 
413  if(not declCtx->isTranslationUnit() and not declCtx->isFunctionOrMethod()) {
414  while(declCtx->isInlineNamespace()) {
415  declCtx = declCtx->getParent();
416  }
417 
418  if(not declCtx->isTranslationUnit() and (declCtx->isNamespace() or declCtx->getParent()->isTranslationUnit())) {
419  if(const auto* namedDecl = dyn_cast_or_null<NamedDecl>(declCtx)) {
420  name = GetQualifiedName(*namedDecl, removeCurrentScope);
421  name.append("::"sv);
422  }
423  }
424  }
425 
426  return name;
427 }
428 //-----------------------------------------------------------------------------
429 
430 /// \brief SimpleTypePrinter a partially substitution of Clang's TypePrinter.
431 ///
432 /// With Clang 9 there seems to be a change in `lib/AST/TypePrinter.cpp` in `printTemplateTypeParmBefore`. It now
433 /// inserts `auto` when it is a lambda auto parameter. Which is correct, but C++ Insights needs the
434 /// template parameter name to make up compiling code. Hence, this `if` "overrides" the implementation of
435 /// TypePrinter in that case.
436 ///
437 /// It also drops some former code which handled `ClassTemplateSpecializationDecl` in a special way. Here the template
438 /// parameters can be lambdas. Those they need a proper name.
440 {
441 private:
442  const QualType& mType;
443  const CppInsightsPrintingPolicy& mPrintingPolicy;
444  OutputFormatHelper mData{};
445  std::string mDataAfter{};
446  bool mHasData{false};
447  bool mSkipSpace{false};
448  bool mScanningArrayDimension{}; //!< Only the outer most ConstantArrayType handles the array dimensions and size
449  std::string mScope{}; //!< A scope coming from an ElaboratedType which is used for a
450  //!< ClassTemplateSpecializationDecl if there is no other scope
451 
452  bool HandleType(const TemplateTypeParmType* type)
453  {
454  const TemplateTypeParmDecl* decl = type->getDecl();
455 
456  if((nullptr == type->getIdentifier()) or
457  (decl and decl->isImplicit()) /* this fixes auto operator()(type_parameter_0_0 container) const */) {
458 
459  AppendTemplateTypeParamName(mData, decl, true, type);
460 
461  return true;
462  }
463 
464  return false;
465  }
466 
467  bool HandleType(const LValueReferenceType* type)
468  {
469  mDataAfter += " &"sv;
470 
471  return HandleType(type->getPointeeType().getTypePtrOrNull());
472  }
473 
474  bool HandleType(const RValueReferenceType* type)
475  {
476  mDataAfter += " &&"sv;
477 
478  return HandleType(type->getPointeeType().getTypePtrOrNull());
479  }
480 
481  bool HandleType(const PointerType* type)
482  {
483  mDataAfter += " *"sv;
484 
485  return HandleType(type->getPointeeType().getTypePtrOrNull());
486  }
487 
488  bool HandleType(const InjectedClassNameType* type) { return HandleType(type->getInjectedTST()); }
489 
490  bool HandleType(const RecordType* type)
491  {
492  /// In case one of the template parameters is a lambda we need to insert the made up name.
493  if(const auto* tt = dyn_cast_or_null<ClassTemplateSpecializationDecl>(type->getDecl())) {
494  if(const auto* identifierName = mType.getBaseTypeIdentifier()) {
495  const auto& scope = GetScope(type->getDecl()->getDeclContext());
496 
497  // If we don't have a scope with GetScope use a possible one from ElaboratedType
498  if((InsightsSuppressScope::Yes == mPrintingPolicy.CppInsightsSuppressScope) or scope.empty()) {
499  mData.Append(mScope);
500  } else {
501  mData.Append(scope);
502  }
503 
504  mData.Append(identifierName->getName());
505  CodeGenerator codeGenerator{mData};
506  codeGenerator.InsertTemplateArgs(*tt);
507 
508  return true;
509  }
510  } else if(const auto* cxxRecordDecl = type->getAsCXXRecordDecl()) {
511  // Special handling for dependent types. For example, ClassOperatorHandler7Test.cpp A<...* >::B.
512  if(type->isDependentType()) {
513  std::string context{GetDeclContext(type->getDecl()->getDeclContext())};
514 
515  if(not context.empty()) {
516  mData.Append(std::move(context));
517  mData.Append(cxxRecordDecl->getName());
518 
519  return true;
520  }
521  }
522 
523  if(cxxRecordDecl->isLambda()) {
524  mData.Append(GetLambdaName(*cxxRecordDecl));
525 
526  return true;
527  }
528 
529  // Handle anonymous struct or union.
530  if(IsAnonymousStructOrUnion(cxxRecordDecl)) {
531  mData.Append(GetAnonymStructOrUnionName(*cxxRecordDecl));
532 
533  return true;
534  }
535 
536  // we need a name here, as DecltypeType always says true
537  mData.Append(GetName(*cxxRecordDecl));
538  }
539 
540  return false;
541  }
542 
543  bool HandleType(const AutoType* type) { return HandleType(type->getDeducedType().getTypePtrOrNull()); }
544 
545  bool HandleType(const SubstTemplateTypeParmType* type)
546  {
547  return HandleType(type->getReplacementType().getTypePtrOrNull());
548  }
549 
550  bool HandleType(const ElaboratedType* type)
551  {
552  const IgnoreNamespace ignoreNamespace = (mPrintingPolicy.CppInsightsSuppressScope == InsightsSuppressScope::Yes)
553  ? IgnoreNamespace::No
554  : IgnoreNamespace::Yes;
555 
556  mScope = GetNestedName(type->getQualifier(), ignoreNamespace);
557 
558  const bool ret = HandleType(type->getNamedType().getTypePtrOrNull());
559 
560  mScope.clear();
561 
562  return ret;
563  }
564 
565  bool HandleType(const DependentTemplateSpecializationType* type)
566  {
567  mData.Append(GetElaboratedTypeKeyword(type->getKeyword()),
568  GetNestedName(type->getQualifier()),
570  type->getIdentifier()->getName());
571 
572  CodeGenerator codeGenerator{mData};
573  codeGenerator.InsertTemplateArgs(*type);
574 
575  return true;
576  }
577 
578  bool HandleType(const DeducedTemplateSpecializationType* type)
579  {
580  return HandleType(type->getDeducedType().getTypePtrOrNull());
581  }
582 
583  bool HandleType(const TemplateSpecializationType* type)
584  {
585  if(type->getAsRecordDecl()) {
586  // Only if it was some sort of "used" `RecordType` we continue here.
587  if(HandleType(type->getAsRecordDecl()->getTypeForDecl())) {
588  HandleType(type->getPointeeType().getTypePtrOrNull());
589  return true;
590  }
591  }
592 
593  /// This is a specialty discovered with #188_2. In some cases there is a `TemplateTypeParmDecl` which has no
594  /// identifier name. Then it will end up as `type-parameter-...`. At least in #188_2: _Head_base<_Idx,
595  /// type_parameter_0_1, true> the repetition of the template specialization arguments is not required.
596  /// `hasNoName` tries to detect this case and does then print the name of the template only.
597  const bool hasNoName{[&] {
598  for(const auto& arg : type->template_arguments()) {
599  StringStream sstream{};
600  sstream.Print(arg);
601 
602  if(Contains(sstream.str(), "type-parameter"sv)) {
603  return true;
604  }
605  }
606 
607  return false;
608  }()};
609 
610  if(hasNoName) {
611  StringStream sstream{};
612  sstream.Print(*type);
613 
614  mData.Append(sstream.str());
615 
616  return true;
617  }
618 
619  return false;
620  }
621 
622  bool HandleType(const MemberPointerType* type)
623  {
624  HandleType(type->getPointeeType().getTypePtrOrNull());
625 
626  mData.Append('(');
627 
628  const bool ret = HandleType(type->getClass());
629 
630  mData.Append("::*)"sv);
631 
632  HandleTypeAfter(type->getPointeeType().getTypePtrOrNull());
633 
634  return ret;
635  }
636 
637  bool HandleType(const FunctionProtoType* type) { return HandleType(type->getReturnType().getTypePtrOrNull()); }
638 
639  void HandleTypeAfter(const FunctionProtoType* type)
640  {
641  mData.Append('(');
642 
643  mSkipSpace = true;
644  for(OnceFalse needsComma{}; const auto& t : type->getParamTypes()) {
645  if(needsComma) {
646  mData.Append(", "sv);
647  }
648 
649  HandleType(t.getTypePtrOrNull());
650  }
651 
652  mSkipSpace = false;
653 
654  mData.Append(')');
655 
656  if(not type->getMethodQuals().empty()) {
657  mData.Append(" "sv, type->getMethodQuals().getAsString());
658  }
659 
660  /// Currently, we are skipping `T->getRefQualifier()` and the exception specification, as well as the trailing
661  /// return type.
662  }
663 
664  bool HandleType(const BuiltinType* type)
665  {
666  mData.Append(type->getName(mPrintingPolicy));
667 
668  if(not mSkipSpace) {
669  mData.Append(' ');
670  }
671 
672  return false;
673  }
674 
675  bool HandleType(const TypedefType* type)
676  {
677  if(const auto* decl = type->getDecl()) {
678  /// Another filter place for type-parameter where it is contained in the FQN but leads to none compiling
679  /// code. Remove it to keep the code valid.
680  if(Contains(decl->getQualifiedNameAsString(), "type-parameter"sv)) {
681  auto* identifierInfo = decl->getIdentifier();
682  mData.Append(identifierInfo->getName());
683 
684  return true;
685  }
686 
687  return HandleType(decl->getUnderlyingType().getTypePtrOrNull());
688  }
689 
690  return HandleType(type->getPointeeType().getTypePtrOrNull());
691  }
692 
693  bool HandleType(const ConstantArrayType* type)
694  {
695  // Only the outer most ConstantArrayType generates the aary dimensions, block others.
696  bool scanningArrayDimension = false;
697  if(not mScanningArrayDimension) {
698  mScanningArrayDimension = true;
699  scanningArrayDimension = true;
700  }
701 
702  const bool ret = HandleType(type->getElementType().getTypePtrOrNull());
703 
704  // Handle the array dimension after the type has been parsed.
705  if(scanningArrayDimension) {
706  do {
707  mData.Append("["sv, GetSize(type), "]"sv);
708  } while((type = dyn_cast_or_null<ConstantArrayType>(type->getElementType().getTypePtrOrNull())));
709 
710  mScanningArrayDimension = false;
711  }
712 
713  return ret;
714  }
715 
716  bool HandleType(const PackExpansionType* type)
717  {
718  const bool ret = HandleType(type->getPattern().getTypePtrOrNull());
719 
720  if(ret) {
721  mData.Append(kwElipsis);
722  }
723 
724  return ret;
725  }
726 
727  bool HandleType(const DecltypeType* type)
728  {
729  // A DecltypeType in a template definition is unevaluated and refers ti itself. This check ensures, that in such
730  // a situation no expansion is performed.
731  if(not isa_and_nonnull<DecltypeType>(type->desugar().getTypePtrOrNull())) {
732  const bool skipSpace{mSkipSpace};
733  mSkipSpace = true;
734 
735  HandleType(type->desugar().getTypePtrOrNull());
736 
737  mSkipSpace = skipSpace;
738 
739  // if we hit a DecltypeType always use the expanded version to support things like a DecltypeType wrapped in
740  // an LValueReferenceType
741  return true;
742  }
743 
744  if(not isa_and_nonnull<DeclRefExpr>(type->getUnderlyingExpr())) {
745  P0315Visitor visitor{mData};
746 
747  return not visitor.TraverseStmt(type->getUnderlyingExpr());
748  }
749 
750  return false;
751  }
752 
753  bool HandleType(const Type* type)
754  {
755 #define HANDLE_TYPE(t) \
756  if(isa<t>(type)) { \
757  return HandleType(dyn_cast_or_null<t>(type)); \
758  }
759 
760  if(nullptr == type) {
761  return false;
762  }
763 
764  HANDLE_TYPE(FunctionProtoType);
765  HANDLE_TYPE(PointerType);
766  HANDLE_TYPE(LValueReferenceType);
767  HANDLE_TYPE(RValueReferenceType);
768  HANDLE_TYPE(TemplateTypeParmType);
769  HANDLE_TYPE(RecordType);
770  HANDLE_TYPE(AutoType);
771  HANDLE_TYPE(SubstTemplateTypeParmType);
772  HANDLE_TYPE(ElaboratedType);
773  HANDLE_TYPE(TemplateSpecializationType);
774  HANDLE_TYPE(DeducedTemplateSpecializationType);
775  HANDLE_TYPE(MemberPointerType);
776  HANDLE_TYPE(BuiltinType);
777  HANDLE_TYPE(TypedefType);
778  HANDLE_TYPE(ConstantArrayType);
779  HANDLE_TYPE(InjectedClassNameType);
780  HANDLE_TYPE(DependentTemplateSpecializationType);
781  HANDLE_TYPE(PackExpansionType);
782  HANDLE_TYPE(DecltypeType);
783 
784 #undef HANDLE_TYPE
785  return false;
786  }
787 
788  void HandleTypeAfter(const Type* type)
789  {
790 #define HANDLE_TYPE(t) \
791  if(isa<t>(type)) { \
792  HandleTypeAfter(dyn_cast_or_null<t>(type)); \
793  }
794 
795  if(nullptr != type) {
796  HANDLE_TYPE(FunctionProtoType);
797  }
798  }
799 
800  void AddCVQualifiers(const Qualifiers& quals)
801  {
802  if((false == mPrintingPolicy.CppInsightsUnqualified) and not quals.empty()) {
803  mData.Append(quals.getAsString());
804 
805  if(not mData.empty() and not mSkipSpace) {
806  mData.Append(' ');
807  }
808  }
809  }
810 
811 public:
812  SimpleTypePrinter(const QualType& qt, const CppInsightsPrintingPolicy& printingPolicy)
813  : mType{qt}
814  , mPrintingPolicy{printingPolicy}
815  {
816  }
817 
818  std::string& GetString() { return mData.GetString(); }
819 
821  {
822  if(const SplitQualType splitted{mType.split()}; splitted.Quals.empty()) {
823  const auto& canonicalType = mType.getCanonicalType();
824 
825  if(canonicalType->getPointeeType().getLocalFastQualifiers()) {
826  AddCVQualifiers(canonicalType->getPointeeType().getLocalQualifiers());
827  } else if(canonicalType.getLocalFastQualifiers()) {
828  AddCVQualifiers(canonicalType.getLocalQualifiers());
829  }
830  } else {
831  AddCVQualifiers(splitted.Quals);
832  }
833 
834  const auto* typePtr = mType.getTypePtrOrNull();
835  mHasData = HandleType(typePtr);
836  mData.Append(mDataAfter);
837 
838  // Take care of 'char* const'
839  if(mType.getQualifiers().hasFastQualifiers()) {
840  const QualType fastQualifierType{typePtr, mType.getQualifiers().getFastQualifiers()};
841 
842  mSkipSpace = true;
843  AddCVQualifiers(fastQualifierType.getCanonicalType()->getPointeeType().getLocalQualifiers());
844  }
845 
846  return mHasData;
847  }
848 };
849 //-----------------------------------------------------------------------------
850 
851 static std::string GetName(QualType t,
852  const Unqualified unqualified = Unqualified::No,
853  const InsightsSuppressScope supressScope = InsightsSuppressScope::No)
854 {
855  const CppInsightsPrintingPolicy printingPolicy{unqualified,
856  supressScope,
857  (isa<AutoType>(t.getTypePtrOrNull())) ? InsightsCanonicalTypes::Yes
858  : InsightsCanonicalTypes::No};
859 
860  QualType tt = t;
861 
862  if(const auto* et = tt->getAs<ElaboratedType>()) {
863  if((nullptr == et->getQualifier()) and (nullptr == et->getOwnedTagDecl())) {
864  const auto quals = tt.getLocalFastQualifiers();
865  tt = et->getNamedType();
866  tt.setLocalFastQualifiers(quals);
867  }
868  }
869 
870  if(SimpleTypePrinter st{t, printingPolicy}; st.GetTypeString()) {
871  return ScopeHandler::RemoveCurrentScope(st.GetString());
872 
873  } else if(true == printingPolicy.CppInsightsUnqualified) {
874  return ScopeHandler::RemoveCurrentScope(GetAsCPPStyleString(tt.getUnqualifiedType(), printingPolicy));
875  }
876 
877  return ScopeHandler::RemoveCurrentScope(GetAsCPPStyleString(tt, printingPolicy));
878 }
879 } // namespace details
880 //-----------------------------------------------------------------------------
881 
882 static bool HasOverload(const FunctionDecl* fd)
883 {
884  auto* ncfd = const_cast<FunctionDecl*>(fd);
885 
886  Sema& sema = GetGlobalCI().getSema();
887  LookupResult result{sema, ncfd->getDeclName(), {}, Sema::LookupOrdinaryName};
888 
889  if(sema.LookupName(result, sema.getScopeForContext(ncfd->getDeclContext()))) {
890  return LookupResult::FoundOverloaded == result.getResultKind();
891  }
892 
893  return false;
894 }
895 //-----------------------------------------------------------------------------
896 
897 std::string GetSpecialMemberName(const ValueDecl* vd);
898 //-----------------------------------------------------------------------------
899 
900 std::string GetCfrontOverloadedFunctionName(const FunctionDecl* fd)
901 {
902  std::string name{};
903 
904  if(fd and GetInsightsOptions().UseShow2C and HasOverload(fd) and not fd->isMain()) {
905  name = GetSpecialMemberName(fd);
906 
907  if(not fd->param_empty()) {
908  name += "_";
909 
910  for(const auto& param : fd->parameters()) {
911  QualType t = param->getType();
912  QualType plainType = t.getNonReferenceType();
913  plainType.removeLocalConst();
914  plainType.removeLocalVolatile();
915 
916  std::string ptr{};
917 
918  while(plainType->isPointerType()) {
919  ptr += "p";
920  plainType = plainType->getPointeeType();
921 
922  auto quals = plainType.getQualifiers();
923  auto lquals = plainType.getLocalQualifiers();
924 
925  if(quals.hasConst() or lquals.hasConst()) {
926  ptr += "c";
927  }
928 
929  plainType.removeLocalConst();
930  }
931 
932  if(t.isCanonical()) {
933  t = t.getCanonicalType();
934  }
935 
936  if(plainType->isBuiltinType() and plainType->hasUnsignedIntegerRepresentation()) {
937  std::string tmp{GetName(plainType)};
938 
939  ReplaceAll(tmp, "unsigned ", "u");
940 
941  name += tmp;
942  } else {
943  name += GetName(plainType);
944  }
945 
946  auto quals = t.getQualifiers();
947  auto lquals = t.getLocalQualifiers();
948 
949  if(t->isPointerType()) {
950  name += ptr;
951  }
952 
953  if(quals.hasConst() or lquals.hasConst()) {
954  name += "c";
955  }
956 
957  if(t->isLValueReferenceType()) {
958  name += "r";
959  }
960 
961  if(t->isRValueReferenceType()) {
962  name += "R";
963  }
964  }
965  }
966  } else if(const auto* md = dyn_cast_or_null<CXXMethodDecl>(fd);
967  md and GetInsightsOptions().UseShow2C and md->isVirtual()) {
968  name = GetName(*md->getParent());
969  }
970 
971  return name;
972 }
973 //-----------------------------------------------------------------------------
974 
975 STRONG_BOOL(UseLexicalParent);
976 //-----------------------------------------------------------------------------
977 
978 static bool NeedsNamespace(const Decl& decl, UseLexicalParent useLexicalParent)
979 {
980  const auto* declCtx = decl.getDeclContext();
981  if(UseLexicalParent::Yes == useLexicalParent) {
982  declCtx = declCtx->getLexicalParent();
983  }
984 
985  if(nullptr == declCtx) {
986  return false;
987  }
988 
989  const bool isFriend{(decl.getFriendObjectKind() != Decl::FOK_None)};
990  const bool neitherTransparentNorFriend{not declCtx->isTransparentContext() and not isFriend};
991 
992  if(UseLexicalParent::Yes == useLexicalParent) {
993  return (declCtx->isNamespace() and not declCtx->isInlineNamespace()) and neitherTransparentNorFriend;
994  }
995 
996  return (declCtx->isNamespace() or declCtx->isInlineNamespace()) and neitherTransparentNorFriend;
997 }
998 //-----------------------------------------------------------------------------
999 
1000 static const SubstTemplateTypeParmType* GetSubstTemplateTypeParmType(const Type* t)
1001 {
1002  if(const auto* substTemplateTypeParmType = dyn_cast_or_null<SubstTemplateTypeParmType>(t)) {
1003  return substTemplateTypeParmType;
1004  } else if(const auto& pointeeType = t->getPointeeType(); not pointeeType.isNull()) {
1005  return GetSubstTemplateTypeParmType(pointeeType.getTypePtrOrNull());
1006  }
1007 
1008  return nullptr;
1009 }
1010 //-----------------------------------------------------------------------------
1011 
1012 static const DeclRefExpr* FindVarDeclRef(const Stmt* stmt)
1013 {
1014  if(const auto* dref = dyn_cast_or_null<DeclRefExpr>(stmt)) {
1015  if(const auto* vd = dyn_cast_or_null<VarDecl>(dref->getDecl())) {
1016  return dref;
1017  }
1018  }
1019 
1020  if(stmt) {
1021  for(const auto* child : stmt->children()) {
1022  if(const auto* childRef = FindVarDeclRef(child)) {
1023  return childRef;
1024  }
1025  }
1026  }
1027 
1028  return nullptr;
1029 }
1030 //-----------------------------------------------------------------------------
1031 
1032 /*
1033  * \brief Get a usable name from a template parameter pack.
1034  *
1035  * A template parameter pack, args, as in:
1036  * \code
1037 template<typename F, typename ...Types>
1038 auto forward(F f, Types &&...args) {
1039  f(args...);
1040 }
1041 
1042 forward(f,1, 2,3);
1043  * \endcode
1044  *
1045  * gets expanded by clang as
1046  * \code
1047 f(args, args, args);
1048  * \endcode
1049  *
1050  * which would obviously not compile. For clang AST dump it is the right thing. For C++ Insights where the resulting
1051  * code should be compilable it is not. What this function does is, figure out whether it is a pack expansion and if so,
1052  * make the parameters unique, such that \c args becomes \c __args1 to \c __args3.
1053  *
1054  * The expected type for \c T currently is \c ValueDecl or \c VarDecl.
1055  */
1056 static std::string GetTemplateParameterPackArgumentName(std::string_view name, const Decl* decl)
1057 {
1058  if(const auto* parmVarDecl = dyn_cast_or_null<ParmVarDecl>(decl)) {
1059  if(const auto& originalType = parmVarDecl->getOriginalType(); not originalType.isNull()) {
1060  if(const auto* substTemplateTypeParmType = GetSubstTemplateTypeParmType(originalType.getTypePtrOrNull());
1061  substTemplateTypeParmType and substTemplateTypeParmType->getReplacedParameter()->isParameterPack()) {
1062  return StrCat(BuildInternalVarName(name), parmVarDecl->getFunctionScopeIndex());
1063 
1064  } else if(const auto* fd = parmVarDecl->getParentFunctionOrMethod()) {
1065  // Get the primary template, if possible and check whether its parameters contain a parameter pack
1066  if(const auto* primTmpl = dyn_cast_or_null<FunctionDecl>(fd)->getPrimaryTemplate();
1067  primTmpl and primTmpl->getTemplateParameters()->hasParameterPack()) {
1068  // if so, then search for the matching parameter name.
1069  for(const auto* pa : primTmpl->getTemplatedDecl()->parameters()) {
1070  // if one is found we suffix it with its function scope index
1071  if(pa->isParameterPack() and (parmVarDecl->getNameAsString() == pa->getNameAsString())) {
1072  return StrCat(BuildInternalVarName(name), parmVarDecl->getFunctionScopeIndex());
1073  }
1074  }
1075  }
1076  }
1077  }
1078  } else if(const auto* varDecl = dyn_cast_or_null<VarDecl>(decl)) {
1079  // If it is an init-capture in C++2a p0780 brings "Allow pack expansion in lambda init-capture". We
1080  // need to figure out, whether the initializer for this \c VarDecl comes from a parameter pack. If
1081  // so, then we use this ParmVarDecl to get the index.
1082  if(varDecl->isInitCapture()) {
1083  if(const auto* drefExpr = FindVarDeclRef(varDecl->getInit())) {
1084  if(const auto* parmVarDecl = dyn_cast_or_null<ParmVarDecl>(drefExpr->getDecl())) {
1085  return GetTemplateParameterPackArgumentName(name, parmVarDecl);
1086  }
1087  }
1088  }
1089  }
1090 
1091  return std::string{name};
1092 }
1093 //-----------------------------------------------------------------------------
1094 
1095 std::string GetName(const NamedDecl& nd, const QualifiedName qualifiedName)
1096 {
1097  std::string name{};
1098 
1099  if(NeedsNamespace(nd, UseLexicalParent::No) or (QualifiedName::Yes == qualifiedName)) {
1100  if(const auto* cxxMedthodDecl = dyn_cast_or_null<CXXMethodDecl>(&nd)) {
1101  if(cxxMedthodDecl->isLambdaStaticInvoker()) {
1102  name = GetName(*cxxMedthodDecl->getParent());
1103  }
1104  }
1105 
1106  name += details::GetScope(nd.getDeclContext(), details::RemoveCurrentScope::No);
1107  }
1108 
1109  name += GetNamePlain(nd);
1110 
1111  name += GetCfrontOverloadedFunctionName(dyn_cast_or_null<FunctionDecl>(&nd));
1112 
1114 }
1115 //-----------------------------------------------------------------------------
1116 
1117 std::string BuildTemplateParamObjectName(std::string name)
1118 {
1119  ReplaceAll(name, "{"sv, "_"sv);
1120  ReplaceAll(name, "}"sv, "_"sv);
1121  ReplaceAll(name, " "sv, ""sv);
1122  ReplaceAll(name, ","sv, "_"sv);
1123  ReplaceAll(name, "."sv, "_"sv);
1124  ReplaceAll(name, "+"sv, "_"sv);
1125  ReplaceAll(name, "-"sv, "n"sv);
1126 
1127  return name;
1128 }
1129 //-----------------------------------------------------------------------------
1130 
1131 std::string GetName(const TemplateParamObjectDecl& decl)
1132 {
1133  StringStream stream{};
1134  stream.Print(decl);
1135 
1136  return ScopeHandler::RemoveCurrentScope(BuildTemplateParamObjectName(std::move(stream.str())));
1137 }
1138 //-----------------------------------------------------------------------------
1139 
1140 std::string GetName(const CXXRecordDecl& RD)
1141 {
1142  if(RD.isLambda()) {
1143  return GetLambdaName(RD);
1144  }
1145 
1146  // get the namespace as well
1147  if(NeedsNamespace(RD, UseLexicalParent::Yes)) {
1148  return details::GetQualifiedName(RD);
1149  }
1150 
1151  std::string ret{GetNestedName(RD.getQualifier())};
1152 
1153  if(auto name = RD.getName(); not name.empty()) {
1154  ret += name;
1155 
1156  } else {
1157  ret += GetAnonymStructOrUnionName(RD);
1158  }
1159 
1161 }
1162 //-----------------------------------------------------------------------------
1163 
1164 std::string GetTemporaryName(const Expr& tmp)
1165 {
1166  return BuildInternalVarName(MakeLineColumnName(GetGlobalAST().getSourceManager(), tmp.getEndLoc(), "temporary"sv));
1167 }
1168 //-----------------------------------------------------------------------------
1169 
1170 std::string GetName(const CXXTemporaryObjectExpr& tmp)
1171 {
1172  return GetTemporaryName(tmp);
1173 }
1174 //-----------------------------------------------------------------------------
1175 
1176 std::string GetName(const QualType& t, const Unqualified unqualified)
1177 {
1178  return details::GetName(t, unqualified);
1179 }
1180 //-----------------------------------------------------------------------------
1181 
1182 static std::string GetUnqualifiedScopelessName(const Type* type, const InsightsSuppressScope supressScope)
1183 {
1184  return details::GetName(QualType(type, 0), Unqualified::Yes, supressScope);
1185 }
1186 //-----------------------------------------------------------------------------
1187 
1188 std::string GetUnqualifiedScopelessName(const Type* type)
1189 {
1190  return GetUnqualifiedScopelessName(type, InsightsSuppressScope::No);
1191 }
1192 //-----------------------------------------------------------------------------
1193 
1194 QualType GetType(QualType t)
1195 {
1196  if(GetInsightsOptions().UseShow2C and t->isReferenceType()) {
1197  return GetGlobalAST().getPointerType(t.getNonReferenceType());
1198  }
1199 
1200  return t;
1201 }
1202 //-----------------------------------------------------------------------------
1203 
1204 template<typename QT, typename SUB_T, typename SUB_T2 = void>
1205 static bool HasTypeWithSubType(const QualType& t)
1206 {
1207  if(const auto* lref = dyn_cast_or_null<QT>(t.getTypePtrOrNull())) {
1208  const auto subType = GetDesugarType(lref->getPointeeType());
1209  const auto& ct = subType.getCanonicalType();
1210  const auto* plainSubType = ct.getTypePtrOrNull();
1211 
1212  if(const auto* st = dyn_cast_or_null<SUB_T>(plainSubType)) {
1213  if constexpr(std::is_same_v<void, SUB_T2>) {
1214  return true;
1215 
1216  } else {
1217  const auto subType = GetDesugarType(st->getPointeeType());
1218  const auto& ct = subType.getCanonicalType();
1219  const auto* plainSubType = ct.getTypePtrOrNull();
1220 
1221  return isa<SUB_T2>(plainSubType);
1222  }
1223  }
1224  }
1225 
1226  return false;
1227 }
1228 //-----------------------------------------------------------------------------
1229 
1230 template<typename QT, typename SUB_T>
1231 static bool HasTypePath(const QualType& t)
1232 {
1233  if(const auto* lref = dyn_cast_or_null<QT>(t.getTypePtrOrNull())) {
1234  const auto subType = GetDesugarType(lref->getPointeeType());
1235 
1236  return isa<SUB_T>(subType);
1237  }
1238 
1239  return false;
1240 }
1241 //-----------------------------------------------------------------------------
1242 
1243 std::string GetTypeNameAsParameter(const QualType& t, std::string_view varName, const Unqualified unqualified)
1244 {
1245  const bool isFunctionPointer =
1246  HasTypeWithSubType<ReferenceType, FunctionProtoType>(t.getCanonicalType()) or
1247  HasTypeWithSubType<ReferenceType, PointerType, FunctionProtoType>(t.getCanonicalType());
1248  const bool isArrayRef = HasTypeWithSubType<ReferenceType, ArrayType>(t);
1249  // Special case for Issue81, auto returns an array-ref and to catch auto deducing an array (Issue106)
1250  const bool isAutoType = (nullptr != dyn_cast_or_null<AutoType>(t.getTypePtrOrNull()));
1251  const auto pointerToArrayBaseType = isAutoType ? t->getContainedAutoType()->getDeducedType() : t;
1252  const bool isPointerToArray = HasTypeWithSubType<PointerType, ArrayType>(pointerToArrayBaseType);
1253  // Only treat this as an array if it is a top-level arry. Typdef's et all can hide the arrayness.
1254  const bool isRawArrayType =
1255  t->isArrayType() and not(isa<TypedefType>(t) or isa<ElaboratedType>(t) or isa<UsingType>(t));
1256 
1257  std::string typeName = details::GetName(t, unqualified);
1258 
1259  // Sometimes we get char const[2]. If we directly insert the typename we end up with char const__var[2] which is not
1260  // a valid type name. Hence check for this condition and, if necessary, insert a space before __var.
1261  auto getSpaceOrEmpty = [&](const std::string_view& needle) -> std::string_view {
1262  if(not Contains(typeName, needle)) {
1263  return " ";
1264  }
1265 
1266  return {};
1267  };
1268 
1269  if(isRawArrayType and not t->isLValueReferenceType()) {
1270  const auto space = getSpaceOrEmpty(" ["sv);
1271  InsertBefore(typeName, "["sv, StrCat(space, varName));
1272 
1273  } else if(isArrayRef) {
1274  const bool isRValueRef{HasTypeWithSubType<RValueReferenceType, ArrayType>(t)};
1275  const std::string_view contains{isRValueRef ? "(&&" : "(&"};
1276 
1277  if(Contains(typeName, contains)) {
1278  InsertAfter(typeName, contains, varName);
1279  } else {
1280  const std::string_view insertBefore{isRValueRef ? "&&[" : "&["};
1281 
1282  InsertBefore(typeName, insertBefore, "("sv);
1283 
1284  // check whether we are dealing with a function or an array
1285  if(Contains(typeName, contains)) {
1286  InsertAfter(typeName, contains, StrCat(varName, ")"sv));
1287  } else {
1288  InsertAfter(typeName, typeName, StrCat(" "sv, varName));
1289  }
1290  }
1291 
1292  } else if(isFunctionPointer) {
1293  const bool isRValueRef{HasTypeWithSubType<RValueReferenceType, FunctionProtoType>(t)};
1294  const auto contains{[&]() {
1295  if(isRValueRef) {
1296  return "(&&"sv;
1297  }
1298 
1299  else if(HasTypeWithSubType<LValueReferenceType, PointerType, FunctionProtoType>(t)) {
1300  return "(*&"sv;
1301  } else {
1302  return "(&"sv;
1303  }
1304  }()};
1305 
1306  if(Contains(typeName, contains)) {
1307  InsertAfter(typeName, contains, varName);
1308  } else {
1309  typeName += StrCat(" "sv, varName);
1310  }
1311 
1312  } else if(isa<MemberPointerType>(t)) {
1313  InsertAfter(typeName, "::*"sv, varName);
1314 
1315  } else if(isPointerToArray) {
1316  if(Contains(typeName, "(*"sv)) {
1317  InsertAfter(typeName, "(*"sv, varName);
1318  } else if(Contains(typeName, "*"sv)) {
1319  InsertBefore(typeName, "*"sv, "("sv);
1320  InsertAfter(typeName, "*"sv, StrCat(varName, ")"sv));
1321  }
1322  } else if(t->isFunctionPointerType()) {
1323  if(Contains(typeName, "(*"sv)) {
1324  InsertAfter(typeName, "(*"sv, varName);
1325  } else {
1326  typeName += StrCat(" "sv, varName);
1327  }
1328  } else if(HasTypePath<PointerType, ParenType>(t)) {
1329  InsertAfter(typeName, "(*"sv, varName);
1330 
1331  } else if(not isRawArrayType and not varName.empty()) {
1332  typeName += StrCat(" "sv, varName);
1333  }
1334 
1335  return typeName;
1336 }
1337 //-----------------------------------------------------------------------------
1338 
1340  const TemplateTypeParmDecl* decl,
1341  const bool isParameter,
1342  const TemplateTypeParmType* type)
1343 {
1344  if(decl) {
1345  if(const auto* typeConstraint = decl->getTypeConstraint(); typeConstraint and not isParameter) {
1346  StringStream sstream{};
1347  sstream.Print(*typeConstraint);
1348 
1349  ofm.Append(sstream.str(), " "sv);
1350  }
1351  }
1352 
1353  const auto depth = decl ? decl->getDepth() : type->getDepth();
1354  const auto index = decl ? decl->getIndex() : type->getIndex();
1355 
1356  ofm.Append("type_parameter_"sv, depth, "_"sv, index);
1357 }
1358 //-----------------------------------------------------------------------------
1359 
1360 static bool IsTrivialStaticClassVarDecl(const DeclRefExpr& declRefExpr)
1361 {
1362  if(const VarDecl* vd = GetVarDeclFromDeclRefExpr(declRefExpr)) {
1363  return IsTrivialStaticClassVarDecl(*vd);
1364  }
1365 
1366  return false;
1367 }
1368 //-----------------------------------------------------------------------------
1369 
1370 APValue* GetEvaluatedValue(const VarDecl& varDecl)
1371 {
1372  if((nullptr != varDecl.ensureEvaluatedStmt()) and varDecl.ensureEvaluatedStmt()->Value.isValid() and
1373  not varDecl.getInit()->isValueDependent()) {
1374  return varDecl.evaluateValue();
1375  }
1376 
1377  return nullptr;
1378 }
1379 //-----------------------------------------------------------------------------
1380 
1381 bool IsEvaluatable(const VarDecl& varDecl)
1382 {
1383  return (nullptr != GetEvaluatedValue(varDecl));
1384 }
1385 //-----------------------------------------------------------------------------
1386 
1387 bool IsTrivialStaticClassVarDecl(const VarDecl& varDecl)
1388 {
1389  // Should the VarDecl be evaluatable at compile-time, there is no additional guard added by the compiler.
1390  if(varDecl.isStaticLocal() and not IsEvaluatable(varDecl)) {
1391  if(const auto* cxxRecordDecl = varDecl.getType()->getAsCXXRecordDecl()) {
1392  if(cxxRecordDecl->hasNonTrivialDestructor() or cxxRecordDecl->hasNonTrivialDefaultConstructor()) {
1393  return true;
1394  }
1395  }
1396  }
1397 
1398  return false;
1399 }
1400 //-----------------------------------------------------------------------------
1401 
1402 std::string GetName(const DeclRefExpr& declRefExpr)
1403 {
1404  const auto* declRefDecl = declRefExpr.getDecl();
1405  std::string name{};
1406  const auto* declCtx = declRefDecl->getDeclContext();
1407  const bool needsNamespace{NeedsNamespace(*declRefDecl, UseLexicalParent::No)};
1408 
1409  // get the namespace as well
1410  if(needsNamespace) {
1411  name = details::GetScope(declCtx);
1412  } else if(declRefExpr.hasQualifier()) {
1413  name = details::GetQualifiedName(*declRefDecl);
1414  }
1415 
1416  if(needsNamespace or not declRefExpr.hasQualifier()) {
1417  std::string plainName{GetPlainName(declRefExpr)};
1418 
1419  // try to handle the special case of a function local static with class type and non trivial destructor. In
1420  // this case, as we teared that variable apart, we need to adjust the variable named and add a reinterpret
1421  // cast
1422  if(IsTrivialStaticClassVarDecl(declRefExpr)) {
1423  if(const VarDecl* vd = GetVarDeclFromDeclRefExpr(declRefExpr)) {
1424  if(const auto* cxxRecordDecl = vd->getType()->getAsCXXRecordDecl()) {
1425  plainName = StrCat("*"sv,
1427  "<"sv,
1428  GetName(vd->getType()),
1429  "*>("sv,
1430  BuildInternalVarName(plainName),
1431  ")"sv);
1432  }
1433  }
1434  }
1435 
1436  name.append(plainName);
1437  }
1438 
1439  name += GetCfrontOverloadedFunctionName(dyn_cast_or_null<FunctionDecl>(declRefDecl));
1440 
1442 }
1443 //-----------------------------------------------------------------------------
1444 
1445 /*
1446  * Go deep in a Stmt if necessary and look to all childs for a DeclRefExpr.
1447  */
1448 const DeclRefExpr* FindDeclRef(const Stmt* stmt)
1449 {
1450  if(const auto* dref = dyn_cast_or_null<DeclRefExpr>(stmt)) {
1451  return dref;
1452  } else if(const auto* arrayInitExpr = dyn_cast_or_null<ArrayInitLoopExpr>(stmt)) {
1453  const auto* srcExpr = arrayInitExpr->getCommonExpr()->getSourceExpr();
1454 
1455  if(const auto* arrayDeclRefExpr = dyn_cast_or_null<DeclRefExpr>(srcExpr)) {
1456  return arrayDeclRefExpr;
1457  }
1458  } else if(const auto func = dyn_cast_or_null<CXXFunctionalCastExpr>(stmt)) {
1459  // TODO(stmt, "");
1460  }
1461 
1462  if(stmt) {
1463  for(const auto* child : stmt->children()) {
1464  if(const auto* childRef = FindDeclRef(child)) {
1465  return childRef;
1466  }
1467  }
1468  }
1469 
1470  return nullptr;
1471 }
1472 //-----------------------------------------------------------------------------
1473 
1474 std::string GetName(const VarDecl& VD)
1475 {
1476  // Handle a special case of DecompositionDecl. A DecompositionDecl does not have a name. Hence we make one up from
1477  // the original name of the variable that is decomposed plus line number where the decomposition was written.
1478  if(const auto* decompositionDeclStmt = dyn_cast_or_null<DecompositionDecl>(&VD)) {
1479  const auto baseVarName{[&]() {
1480  if(const auto* declName = FindDeclRef(decompositionDeclStmt->getInit())) {
1481  std::string name = GetPlainName(*declName);
1482 
1483  if(Contains(name, kwOperator)) {
1484  return std::string{kwOperator};
1485  }
1486 
1487  return name;
1488  }
1489 
1490  // We approached an unnamed decl. This happens for example like this: auto& [x, y] = Point{};
1491  return std::string{};
1492  }()};
1493 
1494  return BuildInternalVarName(baseVarName, decompositionDeclStmt->getBeginLoc(), GetSM(*decompositionDeclStmt));
1495  }
1496 
1497  std::string name{VD.getNameAsString()};
1498 
1500 }
1501 //-----------------------------------------------------------------------------
1502 
1503 static std::optional<bool> EvaluateAsBoolenCondition(const Expr& expr, const Decl& decl)
1504 {
1505  bool r{false};
1506 
1507  if(expr.EvaluateAsBooleanCondition(r, decl.getASTContext())) {
1508  return {r};
1509  }
1510 
1511  return std::nullopt;
1512 }
1513 //-----------------------------------------------------------------------------
1514 
1515 const std::string GetNoExcept(const FunctionDecl& decl)
1516 {
1517  const auto* func = decl.getType()->castAs<FunctionProtoType>();
1518 
1519  if(func and func->hasNoexceptExceptionSpec() and not isUnresolvedExceptionSpec(func->getExceptionSpecType())) {
1520  std::string ret{kwSpaceNoexcept};
1521 
1522  if(const auto* expr = func->getNoexceptExpr()) {
1523  ret += "("sv;
1524 
1525  if(const auto value = EvaluateAsBoolenCondition(*expr, decl); value) {
1526  ret += details::ConvertToBoolString(*value);
1527  } else {
1528  OutputFormatHelper ofm{};
1529  CodeGenerator cg{ofm};
1530  cg.InsertArg(expr);
1531 
1532  ret += ofm.GetString();
1533  }
1534 
1535  ret += ")"sv;
1536  }
1537 
1538  return ret;
1539 
1540  } else if(func and isUnresolvedExceptionSpec(func->getExceptionSpecType())) {
1541  // For special members the exception specification is unevaluated as long as the special member is unused.
1543  }
1544 
1545  return {};
1546 }
1547 //-----------------------------------------------------------------------------
1548 
1549 const std::string_view GetConst(const FunctionDecl& decl)
1550 {
1551  if(const auto* methodDecl = dyn_cast_or_null<CXXMethodDecl>(&decl)) {
1552  if(methodDecl->isConst()) {
1553  return kwSpaceConst;
1554  }
1555  }
1556 
1557  return {};
1558 }
1559 //-----------------------------------------------------------------------------
1560 
1561 std::string GetElaboratedTypeKeyword(const ElaboratedTypeKeyword keyword)
1562 {
1563  std::string ret{TypeWithKeyword::getKeywordName(keyword)};
1564 
1565  if(not ret.empty()) {
1566  ret += ' ';
1567  }
1568 
1569  return ret;
1570 }
1571 //-----------------------------------------------------------------------------
1572 
1573 uint64_t GetSize(const ConstantArrayType* arrayType)
1574 {
1575  return arrayType->getSize().getZExtValue();
1576 }
1577 //-----------------------------------------------------------------------------
1578 
1579 void StringStream::Print(const TemplateArgument& arg)
1580 {
1581  arg.print(CppInsightsPrintingPolicy{}, *this, false);
1582 }
1583 //-----------------------------------------------------------------------------
1584 
1585 void StringStream::Print(const TemplateSpecializationType& arg)
1586 {
1587  arg.getTemplateName().print(*this, CppInsightsPrintingPolicy{}, TemplateName::Qualified::AsWritten);
1588 }
1589 //-----------------------------------------------------------------------------
1590 
1591 void StringStream::Print(const TemplateParamObjectDecl& arg)
1592 {
1593  arg.printAsExpr(*this);
1594 }
1595 //-----------------------------------------------------------------------------
1596 
1597 void StringStream::Print(const TypeConstraint& arg)
1598 {
1599  arg.print(*this, CppInsightsPrintingPolicy{});
1600 }
1601 //-----------------------------------------------------------------------------
1602 
1603 void StringStream::Print(const StringLiteral& arg)
1604 {
1605  arg.outputString(*this);
1606 }
1607 //-----------------------------------------------------------------------------
1608 
1609 void StringStream::Print(const CharacterLiteral& arg)
1610 {
1611  CharacterLiteral::print(arg.getValue(), arg.getKind(), *this);
1612 }
1613 //-----------------------------------------------------------------------------
1614 
1615 template<class... Ts>
1616 struct overloaded : Ts...
1617 {
1618  using Ts::operator()...;
1619 };
1620 template<class... Ts>
1622 
1623 bool P0315Visitor::VisitLambdaExpr(const LambdaExpr* expr)
1624 {
1625  mLambdaExpr = expr;
1626 
1627  std::visit(overloaded{
1628  [&](OutputFormatHelper& ofm) { ofm.Append(GetLambdaName(*expr)); },
1629  [&](CodeGenerator& cg) { cg.InsertArg(expr); },
1630  },
1631  mConsumer);
1632 
1633  return false;
1634 }
1635 //-----------------------------------------------------------------------------
1636 
1637 } // namespace clang::insights
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 HANDLE_TYPE(t)
constexpr std::string_view kwCommentStart
constexpr std::string_view kwReinterpretCast
constexpr std::string_view kwSpaceConst
constexpr std::string_view kwTemplateSpace
constexpr std::string_view kwSpaceCCommentEnd
constexpr std::string_view kwElipsis
constexpr std::string_view kwOperator
constexpr std::string_view kwSpaceNoexcept
#define RETURN_IF(cond)
! A helper inspired by https://github.com/Microsoft/wil/wiki/Error-handling-helpers
T * pop() noexcept
Definition: StackList.h:47
T & back() noexcept
Definition: StackList.h:67
void push(TStackListEntry &entry) noexcept
Definition: StackList.h:31
More or less the heart of C++ Insights.
Definition: CodeGenerator.h:91
virtual void InsertArg(const Decl *stmt)
void InsertTemplateArgs(const T &t)
void InsertTemplateParameters(const TemplateParameterList &list, const TemplateParamsOnly templateParamsOnly=TemplateParamsOnly::No)
! Skip template, type constraints and class/typename.
The C++ Insights formatter.
void Append(const char c)
Append a single character.
bool empty() const
Check whether the buffer is empty.
std::string & GetString()
Returns a reference to the underlying string buffer.
! Find a LambdaExpr inside a Decltype
bool VisitLambdaExpr(const LambdaExpr *expr)
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 &)
SimpleTypePrinter a partially substitution of Clang's TypePrinter.
SimpleTypePrinter(const QualType &qt, const CppInsightsPrintingPolicy &printingPolicy)
static std::string GetQualifiedName(const NamedDecl &decl, const RemoveCurrentScope removeCurrentScope=RemoveCurrentScope::Yes)
static void BuildNamespace(std::string &fullNamespace, const NestedNameSpecifier *stmt, const IgnoreNamespace ignoreNamespace)
STRONG_BOOL(RemoveCurrentScope)
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 GetScope(const DeclContext *declCtx, const RemoveCurrentScope removeCurrentScope=RemoveCurrentScope::Yes)
void InsertBefore(std::string &source, const std::string_view &find, const std::string_view &replace)
uint64_t GetSize(const ConstantArrayType *arrayType)
std::string GetLambdaName(const CXXRecordDecl &lambda)
static bool NeedsNamespace(const Decl &decl, UseLexicalParent useLexicalParent)
bool Contains(const std::string_view source, const std::string_view search)
static std::string GetTemplateParameterPackArgumentName(std::string_view name, const Decl *decl)
std::string BuildRetTypeName(const Decl &decl)
bool IsEvaluatable(const VarDecl &varDecl)
Check whether a VarDecls initialization can be done a compile-time.
static bool HasTypeWithSubType(const QualType &t)
const std::string GetNoExcept(const FunctionDecl &decl)
const SourceManager & GetSM(const Decl &decl)
const DeclRefExpr * FindDeclRef(const Stmt *stmt)
Go deep in a Stmt if necessary and look to all childs for a DeclRefExpr.
std::string GetPlainName(const DeclRefExpr &DRE)
std::string GetElaboratedTypeKeyword(const ElaboratedTypeKeyword keyword)
std::string MakeLineColumnName(const SourceManager &sm, const SourceLocation &loc, const std::string_view &prefix)
static const DeclRefExpr * FindVarDeclRef(const Stmt *stmt)
QualType GetType(QualType t)
In Cfront mode we transform references to pointers.
std::string GetName(const NamedDecl &nd, const QualifiedName qualifiedName)
STRONG_BOOL(InsightsSuppressScope)
static std::string GetAnonymStructOrUnionName(const CXXRecordDecl &cxxRecordDecl)
void ReplaceAll(std::string &str, std::string_view from, std::string_view to)
std::string BuildInternalVarName(const std::string_view &varName)
static const std::string GetAsCPPStyleString(const QualType &t, const CppInsightsPrintingPolicy &printingPolicy)
const std::string_view GetConst(const FunctionDecl &decl)
static bool HasOverload(const FunctionDecl *fd)
static std::optional< bool > EvaluateAsBoolenCondition(const Expr &expr, const Decl &decl)
std::string GetTemporaryName(const Expr &tmp)
std::string BuildTemplateParamObjectName(std::string name)
static bool HasTypePath(const QualType &t)
void AppendTemplateTypeParamName(OutputFormatHelper &ofm, const TemplateTypeParmDecl *decl, const bool isParameter, const TemplateTypeParmType *type)
overloaded(Ts...) -> overloaded< Ts... >
std::string GetDeclContext(const DeclContext *ctx, WithTemplateParameters withTemplateParameters)
static const VarDecl * GetVarDeclFromDeclRefExpr(const DeclRefExpr &declRefExpr)
static std::string GetSpecialMemberName(const ValueDecl *vd, QualType type)
const QualType GetDesugarType(const QualType &QT)
Remove decltype from a QualType, if possible.
static void InsertAfter(std::string &source, const std::string_view &find, const std::string_view &replace)
static std::string GetUnqualifiedScopelessName(const Type *type, const InsightsSuppressScope supressScope)
static const SubstTemplateTypeParmType * GetSubstTemplateTypeParmType(const Type *t)
APValue * GetEvaluatedValue(const VarDecl &varDecl)
Get the evaluated APValue from a VarDecl
std::string GetNestedName(const NestedNameSpecifier *nns, const IgnoreNamespace ignoreNamespace)
std::string GetTypeNameAsParameter(const QualType &t, std::string_view varName, const Unqualified unqualified)
static std::string GetNamePlain(const NamedDecl &decl)
std::string GetCfrontOverloadedFunctionName(const FunctionDecl *fd)
bool IsAnonymousStructOrUnion(const CXXRecordDecl *cxxRecordDecl)
Check whether this is an anonymous struct or union.
static bool IsTrivialStaticClassVarDecl(const DeclRefExpr &declRefExpr)
std::string StrCat(const auto &... args)
CppInsightsPrintingPolicy(const Unqualified unqualified, const InsightsSuppressScope supressScope, const InsightsCanonicalTypes insightsCanonicalTypes=InsightsCanonicalTypes::No)