InsightsHelpers.h
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#ifndef INSIGHTS_HELPERS_H
9#define INSIGHTS_HELPERS_H
10//-----------------------------------------------------------------------------
11
12#include "clang/AST/AST.h"
13#include "clang/AST/ASTContext.h"
14#include "clang/AST/RecursiveASTVisitor.h"
15#include "clang/Lex/Lexer.h"
16
17#include <functional>
18#include <optional>
19#include <string>
20#include <variant>
21
22#include "InsightsStrongTypes.h"
23#include "StackList.h"
24//-----------------------------------------------------------------------------
25
26namespace clang::insights {
27
28std::string BuildTemplateParamObjectName(std::string name);
29//-----------------------------------------------------------------------------
30
31std::string BuildInternalVarName(const std::string_view& varName);
32//-----------------------------------------------------------------------------
33
34std::string MakeLineColumnName(const SourceManager& sm, const SourceLocation& loc, const std::string_view& prefix);
35//-----------------------------------------------------------------------------
36
37inline bool IsStaticStorageClass(const CXXMethodDecl* md)
38{
39 return SC_Static == md->getStorageClass();
40}
41//-----------------------------------------------------------------------------
42
43inline bool IsReferenceType(const ValueDecl* decl)
44{
45 return decl and decl->getType()->isReferenceType();
46}
47//-----------------------------------------------------------------------------
48
49inline bool IsReferenceType(const DeclRefExpr* decl)
50{
51 return decl and IsReferenceType(decl->getDecl());
52}
53//-----------------------------------------------------------------------------
54
55std::string BuildRetTypeName(const Decl& decl);
56//-----------------------------------------------------------------------------
57
58inline bool Contains(const std::string_view source, const std::string_view search)
59{
60 return std::string::npos != source.find(search, 0);
61}
62//-----------------------------------------------------------------------------
63
64template<typename K, typename V, typename U>
65inline bool Contains(const llvm::DenseMap<K, V>& map, const U& key)
66{
67 return map.find(key) != map.end();
68}
69//-----------------------------------------------------------------------------
70
71void ReplaceAll(std::string& str, std::string_view from, std::string_view to);
72//-----------------------------------------------------------------------------
73
74void InsertBefore(std::string& source, const std::string_view& find, const std::string_view& replace);
75//-----------------------------------------------------------------------------
76
77inline const SourceManager& GetSM(const Decl& decl)
78{
79 return decl.getASTContext().getSourceManager();
80}
81//-----------------------------------------------------------------------------
82
83inline const LangOptions& GetLangOpts(const Decl& decl)
84{
85 return decl.getASTContext().getLangOpts();
86}
87//-----------------------------------------------------------------------------
88
89/// \brief Get the evaluated APValue from a `VarDecl`
90///
91/// Returns `nullptr` is the \c VarDecl is not evaluatable.
92APValue* GetEvaluatedValue(const VarDecl& varDecl);
93//-----------------------------------------------------------------------------
94
95/// \brief Check whether a `VarDecl`s initialization can be done a compile-time.
96///
97/// This method checks, whether a \c VarDecl is initialized by a constant expression.
98bool IsEvaluatable(const VarDecl& varDecl);
99//-----------------------------------------------------------------------------
100
101bool IsTrivialStaticClassVarDecl(const VarDecl& varDecl);
102//-----------------------------------------------------------------------------
103
104/*
105 * Get the name of a DeclRefExpr without the namespace
106 */
107std::string GetPlainName(const DeclRefExpr& DRE);
108
109std::string GetName(const DeclRefExpr& declRefExpr);
110std::string GetName(const VarDecl& VD);
111std::string GetName(const TemplateParamObjectDecl& decl);
112//-----------------------------------------------------------------------------
113
114STRONG_BOOL(QualifiedName);
115std::string GetName(const NamedDecl& nd, const QualifiedName qualifiedName = QualifiedName::No);
116//-----------------------------------------------------------------------------
117
118std::string GetNameAsFunctionPointer(const QualType& t);
119//-----------------------------------------------------------------------------
120
121std::string GetLambdaName(const CXXRecordDecl& lambda);
122
123inline std::string GetLambdaName(const LambdaExpr& lambda)
124{
125 return GetLambdaName(*lambda.getLambdaClass());
126}
127//-----------------------------------------------------------------------------
128
129std::string GetName(const CXXRecordDecl& RD);
130//-----------------------------------------------------------------------------
131
132std::string GetName(const CXXTemporaryObjectExpr& tmp);
133//-----------------------------------------------------------------------------
134
135std::string GetTemporaryName(const Expr& tmp);
136//-----------------------------------------------------------------------------
137
138/// \brief In Cfront mode we transform references to pointers
139QualType GetType(QualType);
140//-----------------------------------------------------------------------------
141
142/// \brief Check whether this is an anonymous struct or union.
143///
144/// There is a dedicated function `isAnonymousStructOrUnion` which at this point no longer returns true. Hence this
145/// method uses an empty record decl name as indication for an anonymous struct/union.
146inline bool IsAnonymousStructOrUnion(const CXXRecordDecl* cxxRecordDecl)
147{
148 if(cxxRecordDecl) {
149 return cxxRecordDecl->getName().empty();
150 }
151
152 return false;
153}
154//-----------------------------------------------------------------------------
155
156void AppendTemplateTypeParamName(class OutputFormatHelper& ofm,
157 const TemplateTypeParmDecl* decl,
158 const bool isParameter,
159 const TemplateTypeParmType* type = nullptr);
160//-----------------------------------------------------------------------------
161
162/// \brief Remove decltype from a QualType, if possible.
163const QualType GetDesugarType(const QualType& QT);
164// -----------------------------------------------------------------------------
165
166inline QualType GetDesugarReturnType(const FunctionDecl& FD)
167{
168 return GetDesugarType(FD.getReturnType());
169}
170//-----------------------------------------------------------------------------
171
172STRONG_BOOL(Unqualified);
173
174std::string GetName(const QualType& t, const Unqualified unqualified = Unqualified::No);
175//-----------------------------------------------------------------------------
176
177std::string GetUnqualifiedScopelessName(const Type* type);
178//-----------------------------------------------------------------------------
179
180std::string
181GetTypeNameAsParameter(const QualType& t, std::string_view varName, const Unqualified unqualified = Unqualified::No);
182//-----------------------------------------------------------------------------
183
184STRONG_BOOL(WithTemplateParameters);
185STRONG_BOOL(IgnoreNamespace);
186
187std::string GetNestedName(const NestedNameSpecifier* nns, const IgnoreNamespace ignoreNamespace = IgnoreNamespace::No);
188std::string GetDeclContext(const DeclContext* ctx,
189 WithTemplateParameters withTemplateParameters = WithTemplateParameters::No);
190//-----------------------------------------------------------------------------
191
192const std::string GetNoExcept(const FunctionDecl& decl);
193const std::string_view GetConst(const FunctionDecl& decl);
194//-----------------------------------------------------------------------------
195
196std::string GetElaboratedTypeKeyword(const ElaboratedTypeKeyword keyword);
197//-----------------------------------------------------------------------------
198
199uint64_t GetSize(const ConstantArrayType*);
200//-----------------------------------------------------------------------------
201
202template<typename QT, typename SUB_T>
203static bool TypeContainsSubType(const QualType& t)
204{
205 if(const auto* lref = dyn_cast_or_null<QT>(t.getTypePtrOrNull())) {
206 const auto subType = GetDesugarType(lref->getPointeeType());
207 const auto& ct = subType.getCanonicalType();
208 const auto* plainSubType = ct.getTypePtrOrNull();
209
210 return isa<SUB_T>(plainSubType);
211 }
212
213 return false;
214}
215//-----------------------------------------------------------------------------
216
217template<typename T, typename TFunc>
218void for_each(T start, T end, TFunc&& func)
219{
220 for(; start < end; ++start) {
221 func(start);
222 }
223}
224//-----------------------------------------------------------------------------
225
226/// \brief Track the scope we are currently in to build a properly scoped variable.
227///
228/// The AST only knows about absolute scopes (namespace, struct, class), as once a declaration is parsed it is either in
229/// a scope or not. Each request to give me the namespace automatically leads to the entire scope the item is in. This
230/// makes it hard to have constructs like this: \code struct One
231/// {
232/// static const int o{};
233///
234/// struct Two
235/// {
236/// static const int d = o;
237/// };
238///
239/// static const int a = Two::d;
240///};
241/// \endcode
242///
243/// Here the initializer of \c a is in the scope \c One::Two::d. At this point the qualification \c Two::d is enought.
244///
245/// \c ScopeHelper tracks whether we are currently in a class or namespace and simply remove the path we already in from
246/// the scope.
247struct ScopeHelper : public StackListEntry<ScopeHelper>
248{
249 ScopeHelper(const size_t len)
250 : mLength{len}
251 {
252 }
253
254 const size_t mLength; //!< Length of the scope as it was _before_ this declaration was appended.
255};
256//-----------------------------------------------------------------------------
257
258/// \brief The ScopeHandler tracks the current scope.
259///
260/// The \c ScopeHandler tracks the current scope, knows about all the parts and is able to remove the current scope part
261/// from a name.
263{
264public:
265 ScopeHandler(const Decl* d);
266
268
269 /// \brief Remove the current scope from a string.
270 ///
271 /// The default is that the entire scope is replaced. Suppose we are
272 /// currently in N::X and having a symbol N::X::y then N::X:: is removed. However, there is a special case, where
273 /// the last item is skipped.
274 static std::string RemoveCurrentScope(std::string name);
275
276private:
278
279 ScopeStackType& mStack; //!< Access to the global \c ScopeHelper stack.
280 ScopeHelper mHelper; //!< The \c ScopeHelper this item refers to.
281
282 static inline ScopeStackType mGlobalStack; //!< Global stack to keep track of the scope elements.
283 static inline std::string mScope; //!< The entire scope we are already in.
284};
285//-----------------------------------------------------------------------------
286
287/// \brief Helper to create a \c ScopeHandler on the stack which adds the current \c Decl to it and removes it once the
288/// scope is left.
289#define SCOPE_HELPER(d) \
290 ScopeHandler _scopeHandler \
291 { \
292 d \
293 }
294//-----------------------------------------------------------------------------
295
296/// \brief Specialization for \c ::llvm::raw_string_ostream with an internal \c std::string buffer.
297class StringStream : public ::llvm::raw_string_ostream
298{
299private:
300 std::string mData{};
301
302public:
304 : ::llvm::raw_string_ostream{mData}
305 {
306 }
307
308 void Print(const TemplateArgument&);
309 void Print(const TemplateSpecializationType&);
310 void Print(const TemplateParamObjectDecl&);
311 void Print(const TypeConstraint&);
312 void Print(const StringLiteral&);
313 void Print(const CharacterLiteral&);
314};
315//-----------------------------------------------------------------------------
316
317/// \brief A helper which invokes a lambda when the scope is destroyed.
318template<typename T>
320{
321public:
322 explicit FinalAction(T&& action)
323 : mAction{std::forward<T>(action)}
324 {
325 }
326
327 ~FinalAction() { mAction(); }
328
329private:
330 T mAction;
331};
332//-----------------------------------------------------------------------------
333
334/// \brief Handy helper to avoid longish comparisons.
335///
336/// The idea is taken from a talk from Björn Fahller at NDC TechTown 2019: Modern Techniques for Keeping Your Code DRY
337/// (https://youtu.be/YUWuNpxZa5k)
338/// \code
339/// if( is{v}.any_of(A, B, C) ) { ... }
340/// \endcode
341template<typename T>
342struct is
343{
344 T t;
345
346 constexpr bool any_of(const auto&... ts) const { return ((t == ts) or ...); }
347};
348//-----------------------------------------------------------------------------
349
350template<typename T>
351is(T) -> is<T>;
352//-----------------------------------------------------------------------------
353
354/// Go deep in a Stmt if necessary and look to all childs for a DeclRefExpr.
355const DeclRefExpr* FindDeclRef(const Stmt* stmt);
356//-----------------------------------------------------------------------------
357
358///! Find a LambdaExpr inside a Decltype
359class P0315Visitor : public RecursiveASTVisitor<P0315Visitor>
360{
361 std::variant<std::reference_wrapper<class OutputFormatHelper>, std::reference_wrapper<class CodeGenerator>>
362 mConsumer;
363 const LambdaExpr* mLambdaExpr{};
364
365public:
367 : mConsumer{ofm}
368 {
369 }
370
372 : mConsumer{cg}
373 {
374 }
375
376 bool VisitLambdaExpr(const LambdaExpr* expr);
377
378 const LambdaExpr* Get() const { return mLambdaExpr; }
379};
380//-----------------------------------------------------------------------------
381
382template<typename T, typename U>
384{
387
388 BackupAndRestore(T& value, U&& newVal)
389 : mValue(value)
390 , mBackup{mValue}
391 {
392 mValue = std::forward<U>(newVal);
393 }
394
396};
397//-----------------------------------------------------------------------------
398
399template<class T>
400class MyOptional : public std::optional<T>
401{
402
403public:
404 using std::optional<T>::optional;
405
406 template<class Return>
408 requires(not std::is_pointer_v<T>)
409 {
410 if(not this->has_value()) {
411 return {};
412 }
413
414 return func(this->value());
415 }
416
417 template<class Return>
418 MyOptional<Return> and_then(std::function<MyOptional<Return>(std::remove_pointer_t<T>&)> func)
419 requires(std::is_pointer_v<T>)
420 {
421 if(not this->has_value()) {
422 return {};
423 }
424
425 if(this->value() == nullptr) {
426 return {};
427 } else {
428 return func(*this->value());
429 }
430 }
431
432 template<class Return>
434 {
435 if(not this->has_value()) {
436 return {};
437 }
438
439 if(not func(this->value()).has_value()) {
440 return *this;
441 }
442
443 return {};
444 }
445};
446//-----------------------------------------------------------------------------
447
448} // namespace clang::insights
449
450#endif /* INSIGHTS_HELPERS_H */
#define STRONG_BOOL(typeName)
A more than simple typsafe bool.
StackList is a container for a list which elements exist only on the stack.
Definition StackList.h:25
More or less the heart of C++ Insights.
A helper which invokes a lambda when the scope is destroyed.
MyOptional< Return > and_then(std::function< MyOptional< Return >(T)> func)
MyOptional< Return > and_then(std::function< MyOptional< Return >(std::remove_pointer_t< T > &)> func)
MyOptional< Return > and_not(std::function< MyOptional< Return >(T)> func)
! Find a LambdaExpr inside a Decltype
P0315Visitor(class OutputFormatHelper &ofm)
const LambdaExpr * Get() const
P0315Visitor(class CodeGenerator &cg)
bool VisitLambdaExpr(const LambdaExpr *expr)
The ScopeHandler tracks the current scope.
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 &)
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)
const SourceManager & GetSM(const Decl &decl)
bool Contains(const std::string_view source, const std::string_view search)
const LangOptions & GetLangOpts(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.
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)
void ReplaceAll(std::string &str, std::string_view from, std::string_view to)
std::string BuildInternalVarName(const std::string_view &varName)
const std::string_view GetConst(const FunctionDecl &decl)
std::string GetNameAsFunctionPointer(const QualType &t)
std::string GetTemporaryName(const Expr &tmp)
std::string BuildTemplateParamObjectName(std::string name)
bool IsReferenceType(const ValueDecl *decl)
void AppendTemplateTypeParamName(OutputFormatHelper &ofm, const TemplateTypeParmDecl *decl, const bool isParameter, const TemplateTypeParmType *type)
std::string GetDeclContext(const DeclContext *ctx, WithTemplateParameters withTemplateParameters)
const QualType GetDesugarType(const QualType &QT)
Remove decltype from a QualType, if possible.
static bool TypeContainsSubType(const QualType &t)
static std::string GetUnqualifiedScopelessName(const Type *type, const InsightsSuppressScope supressScope)
bool IsStaticStorageClass(const CXXMethodDecl *md)
APValue * GetEvaluatedValue(const VarDecl &varDecl)
Get the evaluated APValue from a VarDecl
std::string GetNestedName(const NestedNameSpecifier *nns, const IgnoreNamespace ignoreNamespace)
void for_each(T start, T end, TFunc &&func)
std::string GetTypeNameAsParameter(const QualType &t, std::string_view varName, const Unqualified unqualified)
bool IsAnonymousStructOrUnion(const CXXRecordDecl *cxxRecordDecl)
Check whether this is an anonymous struct or union.
static bool IsTrivialStaticClassVarDecl(const DeclRefExpr &declRefExpr)
QualType GetDesugarReturnType(const FunctionDecl &FD)
Base class for StackList.
Definition StackList.h:8
BackupAndRestore(T &value, U &&newVal)
Track the scope we are currently in to build a properly scoped variable.
const size_t mLength
Length of the scope as it was before this declaration was appended.
Handy helper to avoid longish comparisons.
constexpr bool any_of(const auto &... ts) const