0.5.4
C++ to UML diagram generator based on Clang
Loading...
Searching...
No Matches
clang_utils.h
Go to the documentation of this file.
1/**
2 * @file src/common/clang_utils.h
3 *
4 * Copyright (c) 2021-2024 Bartek Kryza <bkryza@gmail.com>
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18#pragma once
19
20#include "common/model/enums.h"
23#include "config/config.h"
24#include "types.h"
25#include "util/util.h"
26
27#include <clang/AST/Expr.h>
28#include <clang/AST/RecursiveASTVisitor.h>
29
30#include <deque>
31#include <filesystem>
32#include <string>
33
34namespace clang {
35class NamespaceDecl;
36}
37
38namespace clanguml::common {
39/**
40 * @brief Convert `clang::AccessSpecifier` to @see clanguml::model::access_t
41 *
42 * @param access_specifier Clang member access specifier
43 * @return Enum value of @see clanguml::model::access_t
44 */
46 clang::AccessSpecifier access_specifier);
47
48/**
49 * @brief Generate full qualified name for
50 * [clang::TagDecl](https://clang.llvm.org/doxygen/classclang_1_1TagDecl.html)
51 * instance
52 *
53 * @param declaration Input declaration
54 * @return String representation including any templates, parameters and
55 * attribtues
56 */
57std::string get_tag_name(const clang::TagDecl &declaration);
58
59/**
60 * @brief Get qualified name of some Clang declaration
61 *
62 * This template is convenient for getting qualified name of various types of
63 * clang declarations.
64 *
65 * @tparam T Type of Clang's declaration, e.g. `clang::TagDecl`
66 * @param declaration Reference to a clang declaration
67 * @return Fully qualified name
68 */
69template <typename T> std::string get_qualified_name(const T &declaration)
70{
71 auto qualified_name = declaration.getQualifiedNameAsString();
72 util::replace_all(qualified_name, "(anonymous namespace)", "");
73 util::replace_all(qualified_name, "::::", "::");
74
75 if constexpr (std::is_base_of_v<clang::TagDecl, T>) {
76 auto base_name = get_tag_name(declaration);
77 model::namespace_ ns{qualified_name};
78 ns.pop_back();
79 ns = ns | base_name;
80
81 return ns.to_string();
82 }
83
84 return qualified_name;
85}
86
87/**
88 * Get namespace of a specific `clang::TagDecl`
89 *
90 * @param declaration Reference to clang::TagDecl
91 * @return Namespace instance
92 */
93model::namespace_ get_tag_namespace(const clang::TagDecl &declaration);
94
95/**
96 * Get namespace of a specific `clang::TemplateDecl`
97 *
98 * @param declaration Reference to clang::TemplateDecl
99 * @return Namespace instance
100 */
102 const clang::TemplateDecl &declaration);
103
104std::string to_string(const clang::QualType &type, const clang::ASTContext &ctx,
105 bool try_canonical = true);
106
107std::string to_string(const clang::RecordType &type,
108 const clang::ASTContext &ctx, bool try_canonical = true);
109
110std::string to_string(
111 const clang::TemplateArgument &arg, const clang::ASTContext *ctx = nullptr);
112
113std::string to_string(const clang::Expr *expr);
114
115std::string to_string(const clang::ValueDecl *val);
116
117std::string to_string(const clang::Stmt *stmt);
118
119std::string to_string(const clang::FunctionTemplateDecl *decl);
120
121std::string to_string(const clang::TypeConstraint *tc);
122
123std::string to_string(const clang::TemplateName &templ);
124
125/**
126 * @brief Get raw text of specific source range
127 *
128 * @param range Source range
129 * @param sm Source manager reference
130 * @return Raw source text
131 */
132std::string get_source_text_raw(
133 clang::SourceRange range, const clang::SourceManager &sm);
134
135/**
136 * @brief Get printable range of text of specific source range
137 *
138 * @param range Source range
139 * @param sm Source manager reference
140 * @return Printable source text
141 */
142std::string get_source_text(
143 clang::SourceRange range, const clang::SourceManager &sm);
144
145/**
146 * @brief Extract template depth and index
147 *
148 * This function extracts template depth and index values from Clang's
149 * `type-parameter-` names.
150 *
151 * @param type_parameter Clang's type parameter string
152 * @return (depth, index, qualifier)
153 */
154std::tuple<unsigned int, unsigned int, std::string>
155extract_template_parameter_index(const std::string &type_parameter);
156
158 const config::diagram &config, std::string &parameter_type);
159
160/**
161 * @brief Check if an expression is contained in another expression
162 *
163 * This method returns true if `sub_stmt` is equal to or is contained in the
164 * AST subtree of `parent_stmt`
165 *
166 * @param parent_stmt Parent statement
167 * @param sub_stmt Sub statement
168 * @return
169 */
170bool is_subexpr_of(const clang::Stmt *parent_stmt, const clang::Stmt *sub_stmt);
171
172/** @defgroup to_id Forward template for convertions to ID from various entities
173 *
174 * These methods provide the main mechanism for generating globally unique
175 * identifiers for all elements in the diagrams. The identifiers must be unique
176 * between different translation units in order for element relationships to
177 * be properly rendered in diagrams.
178 *
179 * @{
180 */
181template <typename T> eid_t to_id(const T &declaration);
182
183template <> eid_t to_id(const std::string &full_name);
184
185eid_t to_id(const clang::QualType &type, const clang::ASTContext &ctx);
186
187template <> eid_t to_id(const clang::NamespaceDecl &declaration);
188
189template <> eid_t to_id(const clang::CXXRecordDecl &declaration);
190
191template <> eid_t to_id(const clang::RecordDecl &declaration);
192
193template <> eid_t to_id(const clang::EnumDecl &declaration);
194
195template <> eid_t to_id(const clang::TagDecl &declaration);
196
197template <> eid_t to_id(const clang::EnumType &type);
198
199template <> eid_t to_id(const clang::TemplateSpecializationType &type);
200
201template <> eid_t to_id(const std::filesystem::path &type);
202/** @} */ // end of to_id
203
204/**
205 * @brief Split qualified name to namespace and name
206 *
207 * @param full_name Fully qualified element name
208 * @return (namespace, name)
209 */
210std::pair<common::model::namespace_, std::string> split_ns(
211 const std::string &full_name);
212
213/**
214 * @brief Parse unexposed (available as string) template params
215 *
216 * @param params String parameters as provided by Clang
217 * @param ns_resolve Namespace resolver function
218 * @param depth Current depth in the template specification
219 * @return Parsed template parameter
220 */
221std::vector<common::model::template_parameter> parse_unexposed_template_params(
222 const std::string &params,
223 const std::function<std::string(const std::string &)> &ns_resolve,
224 int depth = 0);
225
226std::vector<std::string> tokenize_unexposed_template_parameter(
227 const std::string &t);
228
229template <typename T, typename P, typename F>
230void if_dyn_cast(P pointer, F &&func)
231{
232 if (pointer == nullptr)
233 return;
234
235 if (const auto *dyn_cast_value = clang::dyn_cast<T>(pointer);
236 dyn_cast_value) {
237 std::forward<F>(func)(dyn_cast_value);
238 }
239}
240
241bool parse_source_location(const std::string &location_str, std::string &file,
242 unsigned &line, unsigned &column);
243
244bool is_type_parameter(const std::string &t);
245
246bool is_qualifier(const std::string &q);
247
248bool is_bracket(const std::string &b);
249
250bool is_identifier_character(char c);
251
252bool is_identifier(const std::string &t);
253
254bool is_qualified_identifier(const std::string &t);
255
256bool is_type_token(const std::string &t);
257
258std::string format_condition_text(const std::string &condition_text);
259
260std::string get_condition_text(clang::SourceManager &sm, clang::IfStmt *stmt);
261
262std::string get_condition_text(
263 clang::SourceManager &sm, clang::WhileStmt *stmt);
264
265std::string get_condition_text(
266 clang::SourceManager &sm, clang::CXXForRangeStmt *stmt);
267
268std::string get_condition_text(clang::SourceManager &sm, clang::ForStmt *stmt);
269
270std::string get_condition_text(clang::SourceManager &sm, clang::DoStmt *stmt);
271
272std::string get_condition_text(
273 clang::SourceManager &sm, clang::ConditionalOperator *stmt);
274
275clang::QualType dereference(clang::QualType type);
276
277/**
278 * @brief Extract type context and return raw type
279 *
280 * This function removes the context for a type, for example for:
281 * `std::string const&`
282 * it will return
283 * `(std::string, [const&])`
284 *
285 * @param type Type to process
286 * @return (type, [qualifiers])
287 */
288std::pair<clang::QualType, std::deque<common::model::context>>
289consume_type_context(clang::QualType type);
290
291/**
292 * @brief Extract a comment before or next to a statement
293 *
294 * @param sm clang::SourceManager reference
295 * @param context clang::ASTContext reference
296 * @param stmt Pointer to the current clang::Stmt
297 * @return Pointer to a clang::RawComment* or nullptr
298 */
299clang::RawComment *get_expression_raw_comment(const clang::SourceManager &sm,
300 const clang::ASTContext &context, const clang::Stmt *stmt);
301
302clang::RawComment *get_declaration_raw_comment(const clang::SourceManager &sm,
303 const clang::ASTContext &context, const clang::Decl *decl);
304
305clang::RawComment *get_raw_comment(const clang::SourceManager &sm,
306 const clang::ASTContext &context, const clang::SourceRange &source_range);
307
308/**
309 * Check if function or method declaration is a C++20 coroutine.
310 *
311 * @param decl Function declaration
312 * @return True, if the function is a C++20 coroutine.
313 */
314bool is_coroutine(const clang::FunctionDecl &decl);
315
316/**
317 * Check if named declaration is a C++ struct.
318 *
319 * @param decl Declaration to check
320 * @return True, if declaration represents a struct.
321 */
322bool is_struct(const clang::NamedDecl *decl);
323
324/**
325 * Check if function declaration contains specified attributed
326 *
327 * @param decl Function declaration
328 * @param function_attr Clang function attribute
329 * @return True, if decl contains specified function attribute
330 */
331bool has_attr(const clang::FunctionDecl *decl, clang::attr::Kind function_attr);
332
333/**
334 * If `type` is a constant array, return it's number of elements. Otherwise
335 * nothing.
336 *
337 * @param type
338 * @return Number of elements in the array.
339 */
340std::optional<size_t> get_array_size(const clang::ArrayType &type);
341} // namespace clanguml::common