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