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