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 
26 namespace clang::insights {
27 
28 std::string BuildTemplateParamObjectName(std::string name);
29 //-----------------------------------------------------------------------------
30 
31 std::string BuildInternalVarName(const std::string_view& varName);
32 //-----------------------------------------------------------------------------
33 
34 std::string MakeLineColumnName(const SourceManager& sm, const SourceLocation& loc, const std::string_view& prefix);
35 //-----------------------------------------------------------------------------
36 
37 inline bool IsStaticStorageClass(const CXXMethodDecl* md)
38 {
39  return SC_Static == md->getStorageClass();
40 }
41 //-----------------------------------------------------------------------------
42 
43 inline bool IsReferenceType(const ValueDecl* decl)
44 {
45  return decl and decl->getType()->isReferenceType();
46 }
47 //-----------------------------------------------------------------------------
48 
49 inline bool IsReferenceType(const DeclRefExpr* decl)
50 {
51  return decl and IsReferenceType(decl->getDecl());
52 }
53 //-----------------------------------------------------------------------------
54 
55 std::string BuildRetTypeName(const Decl& decl);
56 //-----------------------------------------------------------------------------
57 
58 inline 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 
64 template<typename K, typename V, typename U>
65 inline bool Contains(const llvm::DenseMap<K, V>& map, const U& key)
66 {
67  return map.find(key) != map.end();
68 }
69 //-----------------------------------------------------------------------------
70 
71 void ReplaceAll(std::string& str, std::string_view from, std::string_view to);
72 //-----------------------------------------------------------------------------
73 
74 void InsertBefore(std::string& source, const std::string_view& find, const std::string_view& replace);
75 //-----------------------------------------------------------------------------
76 
77 inline const SourceManager& GetSM(const Decl& decl)
78 {
79  return decl.getASTContext().getSourceManager();
80 }
81 //-----------------------------------------------------------------------------
82 
83 inline 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.
92 APValue* 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.
98 bool IsEvaluatable(const VarDecl& varDecl);
99 //-----------------------------------------------------------------------------
100 
101 bool IsTrivialStaticClassVarDecl(const VarDecl& varDecl);
102 //-----------------------------------------------------------------------------
103 
104 /*
105  * Get the name of a DeclRefExpr without the namespace
106  */
107 std::string GetPlainName(const DeclRefExpr& DRE);
108 
109 std::string GetName(const DeclRefExpr& declRefExpr);
110 std::string GetName(const VarDecl& VD);
111 std::string GetName(const TemplateParamObjectDecl& decl);
112 //-----------------------------------------------------------------------------
113 
114 STRONG_BOOL(QualifiedName);
115 std::string GetName(const NamedDecl& nd, const QualifiedName qualifiedName = QualifiedName::No);
116 //-----------------------------------------------------------------------------
117 
118 std::string GetNameAsFunctionPointer(const QualType& t);
119 //-----------------------------------------------------------------------------
120 
121 std::string GetLambdaName(const CXXRecordDecl& lambda);
122 
123 inline std::string GetLambdaName(const LambdaExpr& lambda)
124 {
125  return GetLambdaName(*lambda.getLambdaClass());
126 }
127 //-----------------------------------------------------------------------------
128 
129 std::string GetName(const CXXRecordDecl& RD);
130 //-----------------------------------------------------------------------------
131 
132 std::string GetName(const CXXTemporaryObjectExpr& tmp);
133 //-----------------------------------------------------------------------------
134 
135 std::string GetTemporaryName(const Expr& tmp);
136 //-----------------------------------------------------------------------------
137 
138 /// \brief In Cfront mode we transform references to pointers
139 QualType 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.
146 inline bool IsAnonymousStructOrUnion(const CXXRecordDecl* cxxRecordDecl)
147 {
148  if(cxxRecordDecl) {
149  return cxxRecordDecl->getName().empty();
150  }
151 
152  return false;
153 }
154 //-----------------------------------------------------------------------------
155 
156 void 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.
163 const QualType GetDesugarType(const QualType& QT);
164 // -----------------------------------------------------------------------------
165 
166 inline QualType GetDesugarReturnType(const FunctionDecl& FD)
167 {
168  return GetDesugarType(FD.getReturnType());
169 }
170 //-----------------------------------------------------------------------------
171 
172 STRONG_BOOL(Unqualified);
173 
174 std::string GetName(const QualType& t, const Unqualified unqualified = Unqualified::No);
175 //-----------------------------------------------------------------------------
176 
177 std::string GetUnqualifiedScopelessName(const Type* type);
178 //-----------------------------------------------------------------------------
179 
180 std::string
181 GetTypeNameAsParameter(const QualType& t, std::string_view varName, const Unqualified unqualified = Unqualified::No);
182 //-----------------------------------------------------------------------------
183 
184 STRONG_BOOL(WithTemplateParameters);
185 STRONG_BOOL(IgnoreNamespace);
186 
187 std::string GetNestedName(const NestedNameSpecifier* nns, const IgnoreNamespace ignoreNamespace = IgnoreNamespace::No);
188 std::string GetDeclContext(const DeclContext* ctx,
189  WithTemplateParameters withTemplateParameters = WithTemplateParameters::No);
190 //-----------------------------------------------------------------------------
191 
192 const std::string GetNoExcept(const FunctionDecl& decl);
193 const std::string_view GetConst(const FunctionDecl& decl);
194 //-----------------------------------------------------------------------------
195 
196 std::string GetElaboratedTypeKeyword(const ElaboratedTypeKeyword keyword);
197 //-----------------------------------------------------------------------------
198 
199 uint64_t GetSize(const ConstantArrayType*);
200 //-----------------------------------------------------------------------------
201 
202 template<typename QT, typename SUB_T>
203 static 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 
217 template<typename T, typename TFunc>
218 void 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.
247 struct 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 {
264 public:
265  ScopeHandler(const Decl* d);
266 
267  ~ScopeHandler();
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 
276 private:
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.
297 class StringStream : public ::llvm::raw_string_ostream
298 {
299 private:
300  std::string mData{};
301 
302 public:
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.
318 template<typename T>
320 {
321 public:
322  explicit FinalAction(T&& action)
323  : mAction{std::forward<T>(action)}
324  {
325  }
326 
327  ~FinalAction() { mAction(); }
328 
329 private:
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
341 template<typename T>
342 struct is
343 {
344  T t;
345 
346  constexpr bool any_of(const auto&... ts) const { return ((t == ts) or ...); }
347 };
348 //-----------------------------------------------------------------------------
349 
350 template<typename T>
351 is(T) -> is<T>;
352 //-----------------------------------------------------------------------------
353 
354 /// Go deep in a Stmt if necessary and look to all childs for a DeclRefExpr.
355 const DeclRefExpr* FindDeclRef(const Stmt* stmt);
356 //-----------------------------------------------------------------------------
357 
358 ///! Find a LambdaExpr inside a Decltype
359 class 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 
365 public:
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 
382 template<typename T, typename U>
384 {
385  T& mValue;
386  T mBackup{};
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 
399 template<class T>
400 class MyOptional : public std::optional<T>
401 {
402 
403 public:
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 */
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.
Definition: CodeGenerator.h:91
A helper which invokes a lambda when the scope is destroyed.
MyOptional< Return > and_not(std::function< MyOptional< Return >(T)> func)
MyOptional< Return > and_then(std::function< MyOptional< Return >(T)> func) requires(not std
MyOptional< Return > and_then(std::function< MyOptional< Return >(std::remove_pointer_t< T > &)> func) requires(std
The C++ Insights formatter.
! 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)
bool Contains(const std::string_view source, const std::string_view search)
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 SourceManager & GetSM(const Decl &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)
STRONG_BOOL(InsightsSuppressScope)
const LangOptions & GetLangOpts(const Decl &decl)
is(T) -> is< T >
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