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