LifetimeTracker.cpp
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * C++ Insights, copyright (C) by Andreas Fertig
4 * Distributed under an MIT license. See LICENSE for details
5 *
6 ****************************************************************************/
7
8#include <algorithm>
9
10#include "ASTHelpers.h"
11#include "CodeGenerator.h"
12#include "DPrint.h"
13#include "Insights.h"
14#include "InsightsHelpers.h"
15#include "NumberIterator.h"
16//-----------------------------------------------------------------------------
17
18namespace clang::insights {
19
20using namespace asthelpers;
21//-----------------------------------------------------------------------------
22
23void LifetimeTracker::StartScope(bool funcStart)
24{
25 RETURN_IF(not GetInsightsOptions().ShowLifetime)
26
27 ++scopeCounter;
28
29 objects.push_back(
30 {.funcStart = funcStart ? LifetimeEntry::FuncStart::Yes : LifetimeEntry::FuncStart::No, .scope = scopeCounter});
31}
32//-----------------------------------------------------------------------------
33
34void LifetimeTracker::AddExtended(const VarDecl* decl, const ValueDecl* extending)
35{
36 // Search for the extending VarlDecl which is already in `objects`. Insert this decl _after_
37 if(auto it = std::ranges::find_if(objects, [&](const auto& e) { return e.item == extending; });
38 it != objects.end()) {
39 const auto scope = (*it).scope;
40 objects.insert(std::next(it), {decl, LifetimeEntry::FuncStart::No, scope});
41 }
42}
43//-----------------------------------------------------------------------------
44
45void LifetimeTracker::Add(const VarDecl* decl)
46{
47 RETURN_IF(not GetInsightsOptions().ShowLifetime)
48
49 QualType type{decl->getType()};
50
51 RETURN_IF(type->isPointerType() or type->isRValueReferenceType());
52
53 // For life-time extended objects
54 // XXX contains in C++23
55 RETURN_IF(std::ranges::find_if(objects, [&](const auto& e) { return e.item == decl; }) != objects.end());
56
57 objects.push_back({decl, LifetimeEntry::FuncStart::No, scopeCounter});
58}
59//-----------------------------------------------------------------------------
60
61void LifetimeTracker::InsertDtorCall(const VarDecl* vd, OutputFormatHelper& ofm)
62{
63 QualType type{vd->getType()};
64
65 if(const auto* ar = dyn_cast_or_null<ConstantArrayType>(type)) {
66 type = ar->getElementType();
67 }
68
69 if(type->isLValueReferenceType()) {
70 type = type.getNonReferenceType();
71 }
72
73 if(const auto& ctx = GetGlobalAST(); QualType::DK_cxx_destructor != vd->needsDestruction(ctx)) {
74 CodeGeneratorVariant cg{ofm};
75 cg->InsertArg(Comment(StrCat(GetName(*vd), " // lifetime ends here")));
76
77 return;
78 }
79
80 auto* dtorDecl = type->getAsCXXRecordDecl()->getDestructor();
81 auto* ic = CastLToRValue(vd);
82 CodeGeneratorVariant cg{ofm};
83
84 auto insertDtor = [&](Expr* member) {
85 auto* mem = AccessMember(member, dtorDecl, vd->getType()->isPointerType());
86
87 if(GetInsightsOptions().UseShow2C) {
88 cg->InsertArg(CallMemberFun(mem, dtorDecl->getType()));
90
91 } else {
92 OutputFormatHelper ofmTmp{};
93 CodeGeneratorVariant cg2{ofmTmp};
94 cg2->InsertArg(CallMemberFun(mem, dtorDecl->getType()));
95 cg->InsertArg(Comment(ofmTmp.GetString()));
96 }
97 };
98
99 if(const auto* ar = dyn_cast_or_null<ConstantArrayType>(vd->getType()); ar and not GetInsightsOptions().UseShow2C) {
100 // not nice but call the destructor for each array element
101 for(const auto& i : NumberIterator{GetSize(ar)}) {
102 insertDtor(ArraySubscript(ic, i, type));
103 }
104
105 return;
106 }
107
108 insertDtor(ic);
109}
110//-----------------------------------------------------------------------------
111
113{
114 RETURN_FALSE_IF(not GetInsightsOptions().ShowLifetime or objects.empty())
115
116 bool ret{};
117
118 for(OnceTrue needsSemi{}; auto& e : llvm::reverse(objects)) {
119 if(LifetimeEntry::FuncStart::Yes == e.funcStart) {
120 break;
121 }
122
123 if(nullptr == e.item) {
124 continue;
125 }
126
127 if(needsSemi) {
128 CodeGeneratorVariant cg{ofm};
129 cg->InsertArg(mkNullStmt());
130 }
131
132 InsertDtorCall(e.item, ofm);
133 ret = true;
134 }
135
136 return ret;
137}
138//-----------------------------------------------------------------------------
139
141{
142 objects.pop_back();
143
144 auto it = std::ranges::remove_if(objects, [&](const LifetimeEntry& e) { return (e.scope == scopeCounter); });
145 objects.erase(it.begin(), it.end());
146
147 --scopeCounter;
148}
149//-----------------------------------------------------------------------------
150
151bool LifetimeTracker::EndScope(OutputFormatHelper& ofm, bool coveredByReturn)
152{
153 RETURN_FALSE_IF(not GetInsightsOptions().ShowLifetime or objects.empty())
154
155 bool ret{};
156
157 if(not coveredByReturn) {
158 for(auto& e : llvm::reverse(objects)) {
159 if(e.scope != scopeCounter) {
160 break;
161 }
162
163 if(nullptr == e.item) {
164 break;
165 }
166
167 InsertDtorCall(e.item, ofm);
168 ret = true;
169 }
170 }
171
172 removeTop();
173
174 return ret;
175}
176//-----------------------------------------------------------------------------
177
178} // namespace clang::insights
const InsightsOptions & GetInsightsOptions()
Get the global C++ Insights options.
Definition Insights.cpp:37
const ASTContext & GetGlobalAST()
Get access to the ASTContext.
Definition Insights.cpp:81
#define RETURN_IF(cond)
! A helper inspired by https://github.com/Microsoft/wil/wiki/Error-handling-helpers
#define RETURN_FALSE_IF(cond)
A special container which creates either a CodeGenerator or a CfrontCodeGenerator depending on the co...
bool EndScope(OutputFormatHelper &ofm, bool clear)
void Add(const VarDecl *decl)
bool Return(OutputFormatHelper &ofm)
void AddExtended(const VarDecl *decl, const ValueDecl *extending)
void AppendSemiNewLine()
Append a semicolon and a newline.
MemberExpr * AccessMember(const Expr *expr, const ValueDecl *vd, bool isArrow)
ArraySubscriptExpr * ArraySubscript(const Expr *lhs, uint64_t index, QualType type)
Stmt * Comment(std::string_view comment)
ImplicitCastExpr * CastLToRValue(const VarDecl *vd)
CXXMemberCallExpr * CallMemberFun(Expr *memExpr, QualType retType)
uint64_t GetSize(const ConstantArrayType *arrayType)
std::string GetName(const NamedDecl &nd, const QualifiedName qualifiedName)
std::string StrCat(const auto &... args)
int scope