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 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
149void 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
159namespace details {
160static void
161BuildNamespace(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
202std::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
214static const std::string GetAsCPPStyleString(const QualType& t, const CppInsightsPrintingPolicy& printingPolicy)
215{
216 return t.getAsString(printingPolicy);
217}
218//-----------------------------------------------------------------------------
219
220std::string BuildInternalVarName(const std::string_view& varName)
221{
222 return StrCat("__", varName);
223}
224//-----------------------------------------------------------------------------
225
226static std::string
227BuildInternalVarName(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
235void 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
245static 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
255std::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
265static std::string MakeLineColumnName(const Decl& decl, const std::string_view prefix)
266{
267 return MakeLineColumnName(GetSM(decl), decl.getBeginLoc(), prefix);
268}
269//-----------------------------------------------------------------------------
270
271std::string GetLambdaName(const CXXRecordDecl& lambda)
272{
273 static constexpr auto lambdaPrefix{"__lambda_"sv};
274 return MakeLineColumnName(lambda, lambdaPrefix);
275}
276//-----------------------------------------------------------------------------
277
278static std::string GetAnonymStructOrUnionName(const CXXRecordDecl& cxxRecordDecl)
279{
280 static constexpr auto prefix{"__anon_"sv};
281 return MakeLineColumnName(cxxRecordDecl, prefix);
282}
283//-----------------------------------------------------------------------------
284
285std::string BuildRetTypeName(const Decl& decl)
286{
287 static constexpr auto retTypePrefix{"retType_"sv};
288 return MakeLineColumnName(decl, retTypePrefix);
289}
290//-----------------------------------------------------------------------------
291
292const 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
312static 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
321std::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
387namespace details {
388
389STRONG_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
393static 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) {
402 }
403
404 return scope;
405}
406//-----------------------------------------------------------------------------
407
408static 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{
441private:
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
811public:
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
851static 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
882static 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
897std::string GetSpecialMemberName(const ValueDecl* vd);
898//-----------------------------------------------------------------------------
899
900std::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
975STRONG_BOOL(UseLexicalParent);
976//-----------------------------------------------------------------------------
977
978static 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
1000static 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
1012static 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
1037template<typename F, typename ...Types>
1038auto forward(F f, Types &&...args) {
1039 f(args...);
1040}
1041
1042forward(f,1, 2,3);
1043 * \endcode
1044 *
1045 * gets expanded by clang as
1046 * \code
1047f(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 */
1056static 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
1095std::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
1117std::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
1131std::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
1140std::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
1164std::string GetTemporaryName(const Expr& tmp)
1165{
1166 return BuildInternalVarName(MakeLineColumnName(GetGlobalAST().getSourceManager(), tmp.getEndLoc(), "temporary"sv));
1167}
1168//-----------------------------------------------------------------------------
1169
1170std::string GetName(const CXXTemporaryObjectExpr& tmp)
1171{
1172 return GetTemporaryName(tmp);
1173}
1174//-----------------------------------------------------------------------------
1175
1176std::string GetName(const QualType& t, const Unqualified unqualified)
1177{
1178 return details::GetName(t, unqualified);
1179}
1180//-----------------------------------------------------------------------------
1181
1182static std::string GetUnqualifiedScopelessName(const Type* type, const InsightsSuppressScope supressScope)
1183{
1184 return details::GetName(QualType(type, 0), Unqualified::Yes, supressScope);
1185}
1186//-----------------------------------------------------------------------------
1187
1188std::string GetUnqualifiedScopelessName(const Type* type)
1189{
1190 return GetUnqualifiedScopelessName(type, InsightsSuppressScope::No);
1191}
1192//-----------------------------------------------------------------------------
1193
1194QualType 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
1204template<typename QT, typename SUB_T, typename SUB_T2 = void>
1205static 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
1230template<typename QT, typename SUB_T>
1231static 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
1243std::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
1360static 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
1370APValue* 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
1381bool IsEvaluatable(const VarDecl& varDecl)
1382{
1383 return (nullptr != GetEvaluatedValue(varDecl));
1384}
1385//-----------------------------------------------------------------------------
1386
1387bool 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
1402std::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 */
1448const 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
1474std::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
1503static 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
1515const 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
1549const 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
1561std::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
1573uint64_t GetSize(const ConstantArrayType* arrayType)
1574{
1575 return arrayType->getSize().getZExtValue();
1576}
1577//-----------------------------------------------------------------------------
1578
1579void StringStream::Print(const TemplateArgument& arg)
1580{
1581 arg.print(CppInsightsPrintingPolicy{}, *this, false);
1582}
1583//-----------------------------------------------------------------------------
1584
1585void StringStream::Print(const TemplateSpecializationType& arg)
1586{
1587 arg.getTemplateName().print(*this, CppInsightsPrintingPolicy{}, TemplateName::Qualified::AsWritten);
1588}
1589//-----------------------------------------------------------------------------
1590
1591void StringStream::Print(const TemplateParamObjectDecl& arg)
1592{
1593 arg.printAsExpr(*this);
1594}
1595//-----------------------------------------------------------------------------
1596
1597void StringStream::Print(const TypeConstraint& arg)
1598{
1599 arg.print(*this, CppInsightsPrintingPolicy{});
1600}
1601//-----------------------------------------------------------------------------
1602
1603void StringStream::Print(const StringLiteral& arg)
1604{
1605 arg.outputString(*this);
1606}
1607//-----------------------------------------------------------------------------
1608
1609void StringStream::Print(const CharacterLiteral& arg)
1610{
1611 CharacterLiteral::print(arg.getValue(), arg.getKind(), *this);
1612}
1613//-----------------------------------------------------------------------------
1614
1615template<class... Ts>
1616struct overloaded : Ts...
1617{
1618 using Ts::operator()...;
1619};
1620template<class... Ts>
1622
1623bool 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 InsightsOptions & GetInsightsOptions()
Get the global C++ Insights options.
Definition Insights.cpp:37
const CompilerInstance & GetGlobalCI()
Get access to the CompilerInstance.
Definition Insights.cpp:88
const ASTContext & GetGlobalAST()
Get access to the ASTContext.
Definition Insights.cpp:81
#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.
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
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)
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)
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)