0.6.0
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)
 
virtual bool VisitObjCCategoryDecl (clang::ObjCCategoryDecl *decl)
 
virtual bool VisitObjCProtocolDecl (clang::ObjCProtocolDecl *decl)
 
virtual bool VisitObjCInterfaceDecl (clang::ObjCInterfaceDecl *decl)
 
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_objc_container_children (const clang::ObjCContainerDecl &cls, found_relationships_t &relationships)
 Process ObjC container 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_objc_method (const clang::ObjCMethodDecl &mf, found_relationships_t &relationships)
 Process ObjC 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_objc_property (const clang::ObjCPropertyDecl &property_declaration, found_relationships_t &relationships)
 Process ObjC property.
 
void process_interface_protocol (const clang::ObjCProtocolDecl &protocol_declaration, found_relationships_t &relationships)
 Process ObjC protocol implemented by certain interface.
 
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::Decl *decl, 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 ()
 
std::string get_file_path (const std::string &file_location) const
 

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 38 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 285 of file translation_unit_visitor.cc.

287{
288 // If this diagram has directory packages, first make sure that the
289 // package for current directory is already in the model
290 if (config().package_type() == config::package_type_t::kDirectory) {
291 auto file = source_manager().getFilename(cls->getLocation()).str();
292
293 if (file.empty())
294 return;
295
296 auto relative_file = config().make_path_relative(file);
297 relative_file.make_preferred();
298
299 common::model::path parent_path{
300 relative_file.string(), common::model::path_type::kFilesystem};
301 parent_path.pop_back();
302 auto pkg_name = parent_path.name();
303 parent_path.pop_back();
304
305 auto pkg = std::make_unique<common::model::package>(
306 config().using_namespace(), common::model::path_type::kFilesystem);
307
308 pkg->set_name(pkg_name);
309 pkg->set_namespace(parent_path);
310 pkg->set_id(common::to_id(pkg->full_name(false)));
311 set_source_location(*cls, *pkg);
312
313 if (diagram().should_include(*pkg))
314 diagram().add(parent_path, std::move(pkg));
315 }
316 else if (config().package_type() == config::package_type_t::kModule) {
317 const auto *module = cls->getOwningModule();
318
319 if (module == nullptr) {
320 return;
321 }
322
323 std::string module_path_str = module->Name;
324#if LLVM_VERSION_MAJOR < 15
325 if (module->Kind == clang::Module::ModuleKind::PrivateModuleFragment) {
326#else
327 if (module->isPrivateModule()) {
328#endif
329 module_path_str = module->getTopLevelModule()->Name;
330 }
331
332 common::model::path module_path{
333 module_path_str, common::model::path_type::kModule};
334 module_path.pop_back();
335
336 auto relative_module =
337 config().make_module_relative(std::optional{module_path_str});
338
339 common::model::path parent_path{
340 relative_module, common::model::path_type::kModule};
341 auto pkg_name = parent_path.name();
342 parent_path.pop_back();
343
344 auto pkg = std::make_unique<common::model::package>(
345 config().using_module(), common::model::path_type::kModule);
346
347 pkg->set_name(pkg_name);
348 pkg->set_id(get_package_id(cls));
349 // This is for diagram filters
350 pkg->set_module(module_path.to_string());
351 // This is for rendering nested package structure
352 pkg->set_namespace(module_path);
353 set_source_location(*cls, *pkg);
354
355 if (diagram().should_include(*pkg))
356 diagram().add(parent_path, std::move(pkg));
357 }
358
359 auto current_package_id = get_package_id(cls);
360
361 if (current_package_id.value() == 0)
362 // These are relationships to a global namespace, and we don't care
363 // about those
364 return;
365
366 auto current_package = diagram().get(current_package_id);
367
368 if (current_package) {
369 std::vector<eid_t> parent_ids =
370 get_parent_package_ids(current_package_id);
371
372 for (auto &dependency : relationships) {
373 const auto destination_id = std::get<0>(dependency);
374
375 // Skip dependency relationships to parent packages
376 if (util::contains(parent_ids, destination_id))
377 continue;
378
379 // Skip dependency relationship to child packages
380 if (util::contains(
381 get_parent_package_ids(destination_id), current_package_id))
382 continue;
383
384 relationship r{relationship_t::kDependency, destination_id,
386
387 if (std::get<2>(dependency) != nullptr)
388 set_source_location(*std::get<2>(dependency), r);
389
390 if (destination_id != current_package_id)
391 current_package.value().add_relationship(std::move(r));
392 }
393 }
394}

◆ finalize()

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

Finalize diagram model.

Definition at line 850 of file translation_unit_visitor.cc.

850{ }

◆ find_relationships()

bool clanguml::package_diagram::visitor::translation_unit_visitor::find_relationships ( const clang::Decl *  decl,
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 396 of file translation_unit_visitor.cc.

397{
398 if (config().package_type() == config::package_type_t::kNamespace) {
399 const auto *namespace_context =
400 cls->getDeclContext()->getEnclosingNamespaceContext();
401 if (namespace_context != nullptr && namespace_context->isNamespace()) {
402 return common::to_id(
403 *llvm::cast<clang::NamespaceDecl>(namespace_context));
404 }
405
406 return {};
407 }
408
409 if (config().package_type() == config::package_type_t::kModule) {
410 const auto *module = cls->getOwningModule();
411 if (module != nullptr) {
412 std::string module_path = module->Name;
413#if LLVM_VERSION_MAJOR < 15
414 if (module->Kind ==
415 clang::Module::ModuleKind::PrivateModuleFragment) {
416#else
417 if (module->isPrivateModule()) {
418#endif
419 module_path = module->getTopLevelModule()->Name;
420 }
421 return common::to_id(module_path);
422 }
423
424 return {};
425 }
426
427 auto file =
428 source_manager().getFilename(cls->getSourceRange().getBegin()).str();
429 auto relative_file = config().make_path_relative(file);
430 relative_file.make_preferred();
431 common::model::path parent_path{
432 relative_file.string(), common::model::path_type::kFilesystem};
433 parent_path.pop_back();
434
435 return common::to_id(parent_path.to_string());
436}

◆ 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 852 of file translation_unit_visitor.cc.

853{
854 std::vector<eid_t> parent_ids;
855 std::optional<eid_t> parent_id = id;
856
857 while (parent_id.has_value()) {
858 const auto pid = parent_id.value(); // NOLINT
859 parent_ids.push_back(pid);
860 auto parent = this->diagram().get(pid);
861 if (parent)
862 parent_id = parent.value().parent_element_id();
863 else
864 break;
865 }
866
867 return parent_ids;
868}

◆ 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 499 of file translation_unit_visitor.cc.

501{
502 for (const auto &base : cls.bases()) {
503 find_relationships(&cls, base.getType(), relationships);
504 }
505}

◆ 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 448 of file translation_unit_visitor.cc.

450{
451 // Iterate over class methods (both regular and static)
452 for (const auto *method : cls.methods()) {
453 if (method != nullptr) {
454 process_method(*method, relationships);
455 }
456 }
457
458 if (const auto *decl_context =
459 clang::dyn_cast_or_null<clang::DeclContext>(&cls);
460 decl_context != nullptr) {
461 // Iterate over class template methods
462 for (auto const *decl_iterator : decl_context->decls()) {
463 auto const *method_template =
464 llvm::dyn_cast_or_null<clang::FunctionTemplateDecl>(
465 decl_iterator);
466 if (method_template == nullptr)
467 continue;
468
469 process_template_method(*method_template, relationships);
470 }
471 }
472
473 // Iterate over regular class fields
474 for (const auto *field : cls.fields()) {
475 if (field != nullptr)
476 process_field(*field, relationships);
477 }
478
479 // Static fields have to be processed by iterating over variable
480 // declarations
481 for (const auto *decl : cls.decls()) {
482 if (decl->getKind() == clang::Decl::Var) {
483 const clang::VarDecl *variable_declaration{
484 clang::dyn_cast_or_null<clang::VarDecl>(decl)};
485 if ((variable_declaration != nullptr) &&
486 variable_declaration->isStaticDataMember()) {
487 process_static_field(*variable_declaration, relationships);
488 }
489 }
490 }
491
492 if (cls.isCompleteDefinition())
493 for (const auto *friend_declaration : cls.friends()) {
494 if (friend_declaration != nullptr)
495 process_friend(*friend_declaration, relationships);
496 }
497}

◆ 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 438 of file translation_unit_visitor.cc.

440{
441 // Look for dependency relationships in class children (fields, methods)
442 process_class_children(cls, relationships);
443
444 // Look for dependency relationships in class bases
445 process_class_bases(cls, relationships);
446}

◆ 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 625 of file translation_unit_visitor.cc.

628{
629 find_relationships(&field_declaration, field_declaration.getType(),
630 relationships, relationship_t::kDependency);
631}

◆ 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 670 of file translation_unit_visitor.cc.

673{
674 if (const auto *friend_type_declaration =
675 friend_declaration.getFriendDecl()) {
676 if (friend_type_declaration->isTemplateDecl()) {
677 // TODO
678 }
679 }
680 else if (const auto *friend_type = friend_declaration.getFriendType()) {
682 &friend_declaration, friend_type->getType(), relationships);
683 }
684}

◆ process_interface_protocol()

void clanguml::package_diagram::visitor::translation_unit_visitor::process_interface_protocol ( const clang::ObjCProtocolDecl &  protocol_declaration,
found_relationships_t relationships 
)
private

Process ObjC protocol implemented by certain interface.

Parameters
protocol_declarationProtocol declaration
relationshipsList of relationships discovered from this protocol

Definition at line 633 of file translation_unit_visitor.cc.

636{
637 const auto target_id = get_package_id(&protocol_declaration);
638 relationships.emplace_back(
639 target_id, relationship_t::kDependency, &protocol_declaration);
640}

◆ 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 507 of file translation_unit_visitor.cc.

509{
510 find_relationships(&method, method.getReturnType(), relationships);
511
512 for (const auto *param : method.parameters()) {
513 if (param != nullptr)
514 find_relationships(&method, param->getType(), relationships);
515 }
516}

◆ process_objc_container_children()

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

Process ObjC container children.

ObjC container in Clang is a base class for any object (e.g. interface, protocol or category) that can contain methods.

Parameters
clsObjC container declaration
relationshipsList of relationships discovered from this objc container

Definition at line 567 of file translation_unit_visitor.cc.

570{
571 for (const auto *property : cls.properties()) {
572 if (property != nullptr)
573 process_objc_property(*property, relationships);
574 }
575
576 for (const auto *property : cls.class_properties()) {
577 if (property != nullptr)
578 process_objc_property(*property, relationships);
579 }
580
581 for (const auto *method : cls.methods()) {
582 if (method != nullptr)
583 process_objc_method(*method, relationships);
584 }
585
586 for (const auto *method : cls.class_methods()) {
587 if (method != nullptr)
588 process_objc_method(*method, relationships);
589 }
590
591 // Static fields have to be processed by iterating over variable
592 // declarations
593 for (const auto *decl : cls.decls()) {
594 if (decl->getKind() == clang::Decl::Var) {
595 const clang::VarDecl *variable_declaration{
596 clang::dyn_cast_or_null<clang::VarDecl>(decl)};
597 if ((variable_declaration != nullptr) &&
598 variable_declaration->isStaticDataMember()) {
599 process_static_field(*variable_declaration, relationships);
600 }
601 }
602 }
603}

◆ process_objc_method()

void clanguml::package_diagram::visitor::translation_unit_visitor::process_objc_method ( const clang::ObjCMethodDecl &  mf,
found_relationships_t relationships 
)
private

Process ObjC method declaration.

Parameters
methodMethod declaration
relationshipsList of relationships discovered from this method

Definition at line 518 of file translation_unit_visitor.cc.

520{
521 find_relationships(&mf, mf.getReturnType(), relationships);
522
523 for (const auto *param : mf.parameters()) {
524 if (param != nullptr)
525 find_relationships(&mf, param->getType(), relationships);
526 }
527}

◆ process_objc_property()

void clanguml::package_diagram::visitor::translation_unit_visitor::process_objc_property ( const clang::ObjCPropertyDecl &  property_declaration,
found_relationships_t relationships 
)
private

Process ObjC property.

Parameters
property_declarationProperty declaration
relationshipsList of relationships discovered from this property

Definition at line 642 of file translation_unit_visitor.cc.

645{
646 const auto *property_objc_id_type =
647 property_declaration.getType()->getAsObjCQualifiedIdType();
648 if (property_objc_id_type != nullptr) {
649 const auto protocols_count = property_objc_id_type->getNumProtocols();
650 for (auto i = 0U; i < protocols_count; i++) {
651 const auto target_id =
652 get_package_id(property_objc_id_type->getProtocol(i));
653 relationships.emplace_back(
654 target_id, relationship_t::kDependency, &property_declaration);
655 }
656 }
657
658 find_relationships(&property_declaration, property_declaration.getType(),
659 relationships, relationship_t::kDependency);
660}

◆ 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 529 of file translation_unit_visitor.cc.

531{
532 if (const auto *decl_context =
533 clang::dyn_cast_or_null<clang::DeclContext>(&cls);
534 decl_context != nullptr) {
535 // Iterate over class template methods
536 for (auto const *decl_iterator : decl_context->decls()) {
537 auto const *method_template =
538 llvm::dyn_cast_or_null<clang::FunctionTemplateDecl>(
539 decl_iterator);
540 if (method_template == nullptr)
541 continue;
542
543 process_template_method(*method_template, relationships);
544 }
545 }
546
547 // Iterate over regular class fields
548 for (const auto *field : cls.fields()) {
549 if (field != nullptr)
550 process_field(*field, relationships);
551 }
552
553 // Static fields have to be processed by iterating over variable
554 // declarations
555 for (const auto *decl : cls.decls()) {
556 if (decl->getKind() == clang::Decl::Var) {
557 const clang::VarDecl *variable_declaration{
558 clang::dyn_cast_or_null<clang::VarDecl>(decl)};
559 if ((variable_declaration != nullptr) &&
560 variable_declaration->isStaticDataMember()) {
561 process_static_field(*variable_declaration, relationships);
562 }
563 }
564 }
565}

◆ 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 662 of file translation_unit_visitor.cc.

665{
666 find_relationships(&field_declaration, field_declaration.getType(),
667 relationships, relationship_t::kDependency);
668}

◆ 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 605 of file translation_unit_visitor.cc.

608{
609 // TODO: For now skip implicitly default methods
610 // in the future, add config option to choose
611 if (method.getTemplatedDecl()->isDefaulted() &&
612 !method.getTemplatedDecl()->isExplicitlyDefaulted())
613 return;
614
616 &method, method.getTemplatedDecl()->getReturnType(), relationships);
617
618 for (const auto *param : method.getTemplatedDecl()->parameters()) {
619 if (param != nullptr) {
620 find_relationships(&method, param->getType(), relationships);
621 }
622 }
623}

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