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