0.5.4
C++ to UML diagram generator based on Clang
Loading...
Searching...
No Matches
Public Member Functions | Private Member Functions | List of all members
clanguml::package_diagram::visitor::translation_unit_visitor Class Reference

Package diagram translation unit visitor. More...

Detailed Description

Package diagram translation unit visitor.

This class implements the clang::RecursiveASTVisitor interface for selected visitors relevant to generating package diagrams.

Definition at line 51 of file translation_unit_visitor.h.

#include <translation_unit_visitor.h>

Public Member Functions

 translation_unit_visitor (clang::SourceManager &sm, clanguml::package_diagram::model::diagram &diagram, const clanguml::config::package_diagram &config)
 Constructor.
 
 ~translation_unit_visitor () override=default
 
virtual bool VisitNamespaceDecl (clang::NamespaceDecl *ns)
 
virtual bool VisitEnumDecl (clang::EnumDecl *decl)
 
virtual bool VisitCXXRecordDecl (clang::CXXRecordDecl *cls)
 
virtual bool VisitRecordDecl (clang::RecordDecl *cls)
 
virtual bool VisitClassTemplateDecl (clang::ClassTemplateDecl *decl)
 
virtual bool VisitFunctionDecl (clang::FunctionDecl *function_declaration)
 
void finalize ()
 Finalize diagram model.
 
- Public Member Functions inherited from clanguml::common::visitor::translation_unit_visitor< ConfigT, DiagramT >
 translation_unit_visitor (clang::SourceManager &sm, DiagramT &diagram, const ConfigT &config)
 Constructor.
 
virtual ~translation_unit_visitor ()=default
 
void set_tu_path (const std::string &translation_unit_path)
 
const std::filesystem::path & tu_path () const
 Return relative path to current translation unit.
 
common::visitor::ast_id_mapperid_mapper () const
 Get reference to Clang AST to clang-uml id mapper.
 
clang::SourceManager & source_manager () const
 Get clang::SourceManager.
 
void set_source_location (const clang::Decl &decl, clanguml::common::model::source_location &element)
 Set source location in diagram element.
 
void set_source_location (const clang::Expr &expr, clanguml::common::model::source_location &element)
 Set source location in diagram element.
 
void set_source_location (const clang::Stmt &stmt, clanguml::common::model::source_location &element)
 
void set_qualified_name (const clang::NamedDecl &decl, clanguml::common::model::element &element)
 
void set_source_location (const clang::SourceLocation &location, clanguml::common::model::source_location &element)
 Set source location in diagram element.
 
void set_owning_module (const clang::Decl &decl, clanguml::common::model::element &element)
 
virtual void add_diagram_element (std::unique_ptr< common::model::template_element > element)
 
void process_comment (const clang::NamedDecl &decl, clanguml::common::model::decorated_element &e)
 Process comment directives in comment attached to a declaration.
 
std::string process_comment (const clang::RawComment *comment, clang::DiagnosticsEngine &de, clanguml::common::model::decorated_element &e)
 Process comment directives in raw comment.
 
bool skip_system_header_decl (const clang::NamedDecl *decl) const
 
bool should_include (const clang::NamedDecl *decl) const
 Check if the diagram should include a declaration.
 
DiagramT & diagram ()
 Get diagram model reference.
 
const DiagramT & diagram () const
 Get diagram model reference.
 
const ConfigT & config () const
 Get diagram config instance.
 

Private Member Functions

eid_t get_package_id (const clang::Decl *cls)
 Get global package id for declaration.
 
void process_class_declaration (const clang::CXXRecordDecl &cls, found_relationships_t &relationships)
 Process class declaration.
 
void process_class_children (const clang::CXXRecordDecl &cls, found_relationships_t &relationships)
 Process class children.
 
void process_record_children (const clang::RecordDecl &cls, found_relationships_t &relationships)
 Process record children.
 
void process_class_bases (const clang::CXXRecordDecl &cls, found_relationships_t &relationships)
 Process record bases.
 
void process_method (const clang::CXXMethodDecl &method, found_relationships_t &relationships)
 Process method declaration.
 
void process_template_method (const clang::FunctionTemplateDecl &method, found_relationships_t &relationships)
 Process template method declaration.
 
void process_field (const clang::FieldDecl &field_declaration, found_relationships_t &relationships)
 Process member field.
 
void process_static_field (const clang::VarDecl &field_declaration, found_relationships_t &relationships)
 Process static member field.
 
void process_friend (const clang::FriendDecl &friend_declaration, found_relationships_t &relationships)
 Process friend declaration.
 
bool find_relationships (const clang::QualType &type, found_relationships_t &relationships, common::model::relationship_t relationship_hint=common::model::relationship_t::kDependency)
 Process type.
 
void add_relationships (clang::Decl *cls, found_relationships_t &relationships)
 Add discovered relationships for cls to the diagram.
 
std::vector< eid_tget_parent_package_ids (eid_t id)
 

Additional Inherited Members

- Public Types inherited from clanguml::common::visitor::translation_unit_visitor< ConfigT, DiagramT >
using config_t = ConfigT
 
using diagram_t = DiagramT
 
- Protected Member Functions inherited from clanguml::common::visitor::translation_unit_visitor< ConfigT, DiagramT >
std::set< const clang::RawComment * > & processed_comments ()
 

Constructor & Destructor Documentation

◆ translation_unit_visitor()

clanguml::package_diagram::visitor::translation_unit_visitor::translation_unit_visitor ( clang::SourceManager &  sm,
clanguml::package_diagram::model::diagram diagram,
const clanguml::config::package_diagram config 
)

Constructor.

Parameters
smCurrent source manager reference
diagramDiagram model
configDiagram configuration

Definition at line 37 of file translation_unit_visitor.cc.

◆ ~translation_unit_visitor()

clanguml::package_diagram::visitor::translation_unit_visitor::~translation_unit_visitor ( )
overridevirtualdefault

Member Function Documentation

◆ add_relationships()

void clanguml::package_diagram::visitor::translation_unit_visitor::add_relationships ( clang::Decl *  cls,
found_relationships_t relationships 
)
private

Add discovered relationships for cls to the diagram.

Parameters
clsC/C++ entity declaration
relationshipsList of discovered relationships

Definition at line 210 of file translation_unit_visitor.cc.

212{
213 // If this diagram has directory packages, first make sure that the
214 // package for current directory is already in the model
215 if (config().package_type() == config::package_type_t::kDirectory) {
216 auto file = source_manager().getFilename(cls->getLocation()).str();
217
218 if (file.empty())
219 return;
220
221 auto relative_file = config().make_path_relative(file);
222 relative_file.make_preferred();
223
224 common::model::path parent_path{
225 relative_file.string(), common::model::path_type::kFilesystem};
226 parent_path.pop_back();
227 auto pkg_name = parent_path.name();
228 parent_path.pop_back();
229
230 auto pkg = std::make_unique<common::model::package>(
231 config().using_namespace());
232
233 pkg->set_name(pkg_name);
234 pkg->set_id(get_package_id(cls));
235 set_source_location(*cls, *pkg);
236
237 if (diagram().should_include(*pkg))
238 diagram().add(parent_path, std::move(pkg));
239 }
240 else if (config().package_type() == config::package_type_t::kModule) {
241 const auto *module = cls->getOwningModule();
242
243 if (module == nullptr) {
244 return;
245 }
246
247 std::string module_path_str = module->Name;
248#if LLVM_VERSION_MAJOR < 15
249 if (module->Kind == clang::Module::ModuleKind::PrivateModuleFragment) {
250#else
251 if (module->isPrivateModule()) {
252#endif
253 module_path_str = module->getTopLevelModule()->Name;
254 }
255
256 common::model::path module_path{
257 module_path_str, common::model::path_type::kModule};
258 module_path.pop_back();
259
260 auto relative_module =
261 config().make_module_relative(std::optional{module_path_str});
262
263 common::model::path parent_path{
264 relative_module, common::model::path_type::kModule};
265 auto pkg_name = parent_path.name();
266 parent_path.pop_back();
267
268 auto pkg = std::make_unique<common::model::package>(
269 config().using_module(), common::model::path_type::kModule);
270
271 pkg->set_name(pkg_name);
272 pkg->set_id(get_package_id(cls));
273 // This is for diagram filters
274 pkg->set_module(module_path.to_string());
275 // This is for rendering nested package structure
276 pkg->set_namespace(module_path);
277 set_source_location(*cls, *pkg);
278
279 if (diagram().should_include(*pkg))
280 diagram().add(parent_path, std::move(pkg));
281 }
282
283 auto current_package_id = get_package_id(cls);
284
285 if (current_package_id.value() == 0)
286 // These are relationships to a global namespace, and we don't care
287 // about those
288 return;
289
290 auto current_package = diagram().get(current_package_id);
291
292 if (current_package) {
293 std::vector<eid_t> parent_ids =
294 get_parent_package_ids(current_package_id);
295
296 for (const auto &dependency : relationships) {
297 const auto destination_id = std::get<0>(dependency);
298
299 // Skip dependency relationships to parent packages
300 if (util::contains(parent_ids, destination_id))
301 continue;
302
303 // Skip dependency relationship to child packages
304 if (util::contains(
305 get_parent_package_ids(destination_id), current_package_id))
306 continue;
307
308 relationship r{relationship_t::kDependency, destination_id,
310 if (destination_id != current_package_id)
311 current_package.value().add_relationship(std::move(r));
312 }
313 }
314}

◆ finalize()

void clanguml::package_diagram::visitor::translation_unit_visitor::finalize ( )

Finalize diagram model.

Definition at line 678 of file translation_unit_visitor.cc.

678{ }

◆ find_relationships()

bool clanguml::package_diagram::visitor::translation_unit_visitor::find_relationships ( const clang::QualType &  type,
found_relationships_t relationships,
common::model::relationship_t  relationship_hint = common::model::relationship_t::kDependency 
)
private

Process type.

Parameters
typeReference to some C++ type
relationshipsList of relationships discovered from this friend
relationship_hintDefault relationship type for discovered relationships

◆ get_package_id()

eid_t clanguml::package_diagram::visitor::translation_unit_visitor::get_package_id ( const clang::Decl *  cls)
private

Get global package id for declaration.

This method calculates package diagram id based on either the namespace of the provided declaration or filesystem path of the source file where it was declared - depending on the diagram configuration.

This is necessary to infer dependency relationships between packages.

Parameters
clsC++ entity declaration
Returns
Id of the package containing that declaration

Definition at line 316 of file translation_unit_visitor.cc.

317{
318 if (config().package_type() == config::package_type_t::kNamespace) {
319 const auto *namespace_context =
320 cls->getDeclContext()->getEnclosingNamespaceContext();
321 if (namespace_context != nullptr && namespace_context->isNamespace()) {
322 return common::to_id(
323 *llvm::cast<clang::NamespaceDecl>(namespace_context));
324 }
325
326 return {};
327 }
328
329 if (config().package_type() == config::package_type_t::kModule) {
330 const auto *module = cls->getOwningModule();
331 if (module != nullptr) {
332 std::string module_path = module->Name;
333#if LLVM_VERSION_MAJOR < 15
334 if (module->Kind ==
335 clang::Module::ModuleKind::PrivateModuleFragment) {
336#else
337 if (module->isPrivateModule()) {
338#endif
339 module_path = module->getTopLevelModule()->Name;
340 }
341 return common::to_id(module_path);
342 }
343
344 return {};
345 }
346
347 auto file =
348 source_manager().getFilename(cls->getSourceRange().getBegin()).str();
349 auto relative_file = config().make_path_relative(file);
350 relative_file.make_preferred();
351 common::model::path parent_path{
352 relative_file.string(), common::model::path_type::kFilesystem};
353 parent_path.pop_back();
354
355 return common::to_id(parent_path.to_string());
356}

◆ get_parent_package_ids()

std::vector< eid_t > clanguml::package_diagram::visitor::translation_unit_visitor::get_parent_package_ids ( eid_t  id)
private

Definition at line 680 of file translation_unit_visitor.cc.

681{
682 std::vector<eid_t> parent_ids;
683 std::optional<eid_t> parent_id = id;
684
685 while (parent_id.has_value()) {
686 const auto pid = parent_id.value(); // NOLINT
687 parent_ids.push_back(pid);
688 auto parent = this->diagram().get(pid);
689 if (parent)
690 parent_id = parent.value().parent_element_id();
691 else
692 break;
693 }
694
695 return parent_ids;
696}

◆ process_class_bases()

void clanguml::package_diagram::visitor::translation_unit_visitor::process_class_bases ( const clang::CXXRecordDecl &  cls,
found_relationships_t relationships 
)
private

Process record bases.

Parameters
clsClass declaration
relationshipsList of relationships discovered from this class

Definition at line 419 of file translation_unit_visitor.cc.

421{
422 for (const auto &base : cls.bases()) {
423 find_relationships(base.getType(), relationships);
424 }
425}

◆ process_class_children()

void clanguml::package_diagram::visitor::translation_unit_visitor::process_class_children ( const clang::CXXRecordDecl &  cls,
found_relationships_t relationships 
)
private

Process class children.

Parameters
clsClass declaration
relationshipsList of relationships discovered from this class

Definition at line 368 of file translation_unit_visitor.cc.

370{
371 // Iterate over class methods (both regular and static)
372 for (const auto *method : cls.methods()) {
373 if (method != nullptr) {
374 process_method(*method, relationships);
375 }
376 }
377
378 if (const auto *decl_context =
379 clang::dyn_cast_or_null<clang::DeclContext>(&cls);
380 decl_context != nullptr) {
381 // Iterate over class template methods
382 for (auto const *decl_iterator : decl_context->decls()) {
383 auto const *method_template =
384 llvm::dyn_cast_or_null<clang::FunctionTemplateDecl>(
385 decl_iterator);
386 if (method_template == nullptr)
387 continue;
388
389 process_template_method(*method_template, relationships);
390 }
391 }
392
393 // Iterate over regular class fields
394 for (const auto *field : cls.fields()) {
395 if (field != nullptr)
396 process_field(*field, relationships);
397 }
398
399 // Static fields have to be processed by iterating over variable
400 // declarations
401 for (const auto *decl : cls.decls()) {
402 if (decl->getKind() == clang::Decl::Var) {
403 const clang::VarDecl *variable_declaration{
404 clang::dyn_cast_or_null<clang::VarDecl>(decl)};
405 if ((variable_declaration != nullptr) &&
406 variable_declaration->isStaticDataMember()) {
407 process_static_field(*variable_declaration, relationships);
408 }
409 }
410 }
411
412 if (cls.isCompleteDefinition())
413 for (const auto *friend_declaration : cls.friends()) {
414 if (friend_declaration != nullptr)
415 process_friend(*friend_declaration, relationships);
416 }
417}

◆ process_class_declaration()

void clanguml::package_diagram::visitor::translation_unit_visitor::process_class_declaration ( const clang::CXXRecordDecl &  cls,
found_relationships_t relationships 
)
private

Process class declaration.

Parameters
clsClass declaration
relationshipsList of relationships discovered from this class

Definition at line 358 of file translation_unit_visitor.cc.

360{
361 // Look for dependency relationships in class children (fields, methods)
362 process_class_children(cls, relationships);
363
364 // Look for dependency relationships in class bases
365 process_class_bases(cls, relationships);
366}

◆ process_field()

void clanguml::package_diagram::visitor::translation_unit_visitor::process_field ( const clang::FieldDecl &  field_declaration,
found_relationships_t relationships 
)
private

Process member field.

Parameters
field_declarationField declaration
relationshipsList of relationships discovered from this field

Definition at line 496 of file translation_unit_visitor.cc.

499{
500 find_relationships(field_declaration.getType(), relationships,
501 relationship_t::kDependency);
502}

◆ process_friend()

void clanguml::package_diagram::visitor::translation_unit_visitor::process_friend ( const clang::FriendDecl &  friend_declaration,
found_relationships_t relationships 
)
private

Process friend declaration.

Parameters
friend_declarationField declaration
relationshipsList of relationships discovered from this friend

Definition at line 512 of file translation_unit_visitor.cc.

515{
516 if (const auto *friend_type_declaration =
517 friend_declaration.getFriendDecl()) {
518 if (friend_type_declaration->isTemplateDecl()) {
519 // TODO
520 }
521 }
522 else if (const auto *friend_type = friend_declaration.getFriendType()) {
523 find_relationships(friend_type->getType(), relationships);
524 }
525}

◆ process_method()

void clanguml::package_diagram::visitor::translation_unit_visitor::process_method ( const clang::CXXMethodDecl &  method,
found_relationships_t relationships 
)
private

Process method declaration.

Parameters
methodMethod declaration
relationshipsList of relationships discovered from this method

Definition at line 427 of file translation_unit_visitor.cc.

429{
430 find_relationships(method.getReturnType(), relationships);
431
432 for (const auto *param : method.parameters()) {
433 if (param != nullptr)
434 find_relationships(param->getType(), relationships);
435 }
436}

◆ process_record_children()

void clanguml::package_diagram::visitor::translation_unit_visitor::process_record_children ( const clang::RecordDecl &  cls,
found_relationships_t relationships 
)
private

Process record children.

Parameters
clsRecord declaration
relationshipsList of relationships discovered from this class

Definition at line 438 of file translation_unit_visitor.cc.

440{
441 if (const auto *decl_context =
442 clang::dyn_cast_or_null<clang::DeclContext>(&cls);
443 decl_context != nullptr) {
444 // Iterate over class template methods
445 for (auto const *decl_iterator : decl_context->decls()) {
446 auto const *method_template =
447 llvm::dyn_cast_or_null<clang::FunctionTemplateDecl>(
448 decl_iterator);
449 if (method_template == nullptr)
450 continue;
451
452 process_template_method(*method_template, relationships);
453 }
454 }
455
456 // Iterate over regular class fields
457 for (const auto *field : cls.fields()) {
458 if (field != nullptr)
459 process_field(*field, relationships);
460 }
461
462 // Static fields have to be processed by iterating over variable
463 // declarations
464 for (const auto *decl : cls.decls()) {
465 if (decl->getKind() == clang::Decl::Var) {
466 const clang::VarDecl *variable_declaration{
467 clang::dyn_cast_or_null<clang::VarDecl>(decl)};
468 if ((variable_declaration != nullptr) &&
469 variable_declaration->isStaticDataMember()) {
470 process_static_field(*variable_declaration, relationships);
471 }
472 }
473 }
474}

◆ process_static_field()

void clanguml::package_diagram::visitor::translation_unit_visitor::process_static_field ( const clang::VarDecl &  field_declaration,
found_relationships_t relationships 
)
private

Process static member field.

Parameters
field_declarationField declaration
relationshipsList of relationships discovered from this field

Definition at line 504 of file translation_unit_visitor.cc.

507{
508 find_relationships(field_declaration.getType(), relationships,
509 relationship_t::kDependency);
510}

◆ process_template_method()

void clanguml::package_diagram::visitor::translation_unit_visitor::process_template_method ( const clang::FunctionTemplateDecl &  method,
found_relationships_t relationships 
)
private

Process template method declaration.

Parameters
methodMethod declaration
relationshipsList of relationships discovered from this method

Definition at line 476 of file translation_unit_visitor.cc.

479{
480 // TODO: For now skip implicitly default methods
481 // in the future, add config option to choose
482 if (method.getTemplatedDecl()->isDefaulted() &&
483 !method.getTemplatedDecl()->isExplicitlyDefaulted())
484 return;
485
487 method.getTemplatedDecl()->getReturnType(), relationships);
488
489 for (const auto *param : method.getTemplatedDecl()->parameters()) {
490 if (param != nullptr) {
491 find_relationships(param->getType(), relationships);
492 }
493 }
494}

The documentation for this class was generated from the following files: