diff --git a/CMakeLists.txt b/CMakeLists.txt index 5cd3aa10..7c207d03 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,11 +25,6 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) -if(APPLE) - set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES - ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}) -endif(APPLE) - # # clang-uml custom defines # @@ -124,6 +119,11 @@ else() message(STATUS "Found LibTooling libraries: ${LIBTOOLING_LIBS}") endif() +if(APPLE OR (LLVM_VERSION_MAJOR GREATER_EQUAL 16)) + set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES + ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}) +endif() + # # Setup threads library # diff --git a/src/class_diagram/model/class_element.cc b/src/class_diagram/model/class_element.cc index f449e49e..0d1ab1a3 100644 --- a/src/class_diagram/model/class_element.cc +++ b/src/class_diagram/model/class_element.cc @@ -34,8 +34,12 @@ common::model::access_t class_element::access() const { return access_; } std::string class_element::name() const { return name_; } +void class_element::set_name(const std::string &name) { name_ = name; } + std::string class_element::type() const { return type_; } +void class_element::set_type(const std::string &type) { type_ = type; } + inja::json class_element::context() const { inja::json ctx; diff --git a/src/class_diagram/model/class_element.h b/src/class_diagram/model/class_element.h index d4161278..897158e8 100644 --- a/src/class_diagram/model/class_element.h +++ b/src/class_diagram/model/class_element.h @@ -36,7 +36,10 @@ public: common::model::access_t access() const; std::string name() const; + void set_name(const std::string &name); + std::string type() const; + void set_type(const std::string &type); virtual inja::json context() const; diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index 549bc9aa..7a583572 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -986,10 +986,13 @@ bool translation_unit_visitor::process_template_parameters( const auto *template_nontype_parameter = clang::dyn_cast_or_null( parameter); + std::optional default_arg; + if (template_nontype_parameter->hasDefaultArgument()) default_arg = common::to_string( template_nontype_parameter->getDefaultArgument()); + auto ct = template_parameter::make_non_type_template( template_nontype_parameter->getType().getAsString(), template_nontype_parameter->getNameAsString(), default_arg, @@ -1082,6 +1085,7 @@ void translation_unit_visitor::process_class_bases( if (const auto *record_type = base.getType()->getAs(); record_type != nullptr) { + cp.set_name(record_type->getDecl()->getQualifiedNameAsString()); cp.set_id(common::to_id(*record_type->getDecl())); } else if (const auto *tsp = @@ -1488,7 +1492,8 @@ bool translation_unit_visitor::find_relationships(const clang::QualType &type, ->getID(), relationship_hint); } - for (const auto &template_argument : *type_instantiation_decl) { + for (const auto &template_argument : + type_instantiation_decl->template_arguments()) { const auto template_argument_kind = template_argument.getKind(); if (template_argument_kind == clang::TemplateArgument::ArgKind::Integral) { @@ -2520,12 +2525,31 @@ void translation_unit_visitor:: argument.set_type( common::to_string(arg.getAsType(), template_decl->getASTContext())); - if (const auto *record_type = arg.getAsType()->getAs(); - record_type != nullptr) { + if (const auto *tsp = + arg.getAsType()->getAs(); + tsp != nullptr) { + if (const auto *record_type_decl = tsp->getAsRecordDecl(); + record_type_decl != nullptr) { + + argument.set_id(common::to_id(arg)); + if (diagram().should_include(full_template_specialization_name)) { + // Add dependency relationship to the parent + // template + template_instantiation.add_relationship( + {relationship_t::kDependency, common::to_id(arg)}); + } + } + } + else if (const auto *record_type = + arg.getAsType()->getAs(); + record_type != nullptr) { if (const auto *record_type_decl = record_type->getAsRecordDecl(); record_type_decl != nullptr) { - argument.set_id(common::to_id(arg)); + argument.set_id(common::to_id(arg)); +#if LLVM_VERSION_MAJOR >= 16 + argument.set_type(record_type_decl->getQualifiedNameAsString()); +#endif if (diagram().should_include(full_template_specialization_name)) { // Add dependency relationship to the parent // template @@ -2635,11 +2659,10 @@ void translation_unit_visitor::process_field( if (type_name.find("std::weak_ptr") == 0) relationship_hint = relationship_t::kAssociation; - const auto *template_field_type = - field_type->getAs(); - found_relationships_t relationships; + const auto *template_field_type = + field_type->getAs(); // TODO: Refactor to an unalias_type() method if (template_field_type != nullptr) if (template_field_type->isTypeAlias()) @@ -2803,11 +2826,14 @@ void translation_unit_visitor::finalize() bool translation_unit_visitor::simplify_system_template( template_parameter &ct, const std::string &full_name) const { - if (config().type_aliases().count(full_name) > 0) { - ct.set_type(config().type_aliases().at(full_name)); + auto simplified = config().simplify_template_type(full_name); + + if (simplified != full_name) { + ct.set_type(simplified); ct.clear_params(); return true; } + return false; } diff --git a/src/cli/cli_handler.cc b/src/cli/cli_handler.cc index 7bad2977..44385153 100644 --- a/src/cli/cli_handler.cc +++ b/src/cli/cli_handler.cc @@ -268,13 +268,10 @@ cli_flow_t cli_handler::handle_post_config_options() cli_flow_t cli_handler::print_version() { - constexpr auto kLLVMBackendPackageStringLength{5}; ostr_ << "clang-uml " << clanguml::version::CLANG_UML_VERSION << '\n'; ostr_ << "Copyright (C) 2021-2023 Bartek Kryza " << '\n'; ostr_ << "Built against LLVM/Clang libraries version: " - << std::string{BACKEND_PACKAGE_STRING}.substr( - kLLVMBackendPackageStringLength) - << std::endl; + << LLVM_VERSION_STRING << std::endl; ostr_ << "Using LLVM/Clang libraries version: " << clang::getClangFullVersion() << std::endl; diff --git a/src/common/clang_utils.cc b/src/common/clang_utils.cc index f9c42fcf..07eb980b 100644 --- a/src/common/clang_utils.cc +++ b/src/common/clang_utils.cc @@ -115,9 +115,13 @@ std::string get_tag_name(const clang::TagDecl &declaration) std::string to_string(const clang::QualType &type, const clang::ASTContext &ctx, bool try_canonical) { - const clang::PrintingPolicy print_policy(ctx.getLangOpts()); + clang::PrintingPolicy print_policy(ctx.getLangOpts()); + print_policy.SuppressScope = false; + print_policy.PrintCanonicalTypes = false; - auto result{type.getAsString(print_policy)}; + std::string result; + + result = type.getAsString(print_policy); if (try_canonical && result.find('<') != std::string::npos) { auto canonical_type_name = diff --git a/src/config/config.cc b/src/config/config.cc index 7e2190ca..d00a3333 100644 --- a/src/config/config.cc +++ b/src/config/config.cc @@ -121,7 +121,8 @@ void inheritable_diagram_options::inherit( std::string inheritable_diagram_options::simplify_template_type( std::string full_name) const { - const auto &aliases = type_aliases(); + type_aliases_longer_first_t aliases; + aliases.insert(type_aliases().begin(), type_aliases().end()); for (const auto &[pattern, replacement] : aliases) { util::replace_all(full_name, pattern, replacement); @@ -204,6 +205,11 @@ void diagram::initialize_type_aliases() type_aliases().insert( {"std::integral_constant", "std::false_type"}); } +#if LLVM_VERSION_MAJOR >= 16 + if (type_aliases().count("std::basic_string") == 0U) { + type_aliases().insert({"std::basic_string", "std::string"}); + } +#endif } common::model::diagram_t class_diagram::type() const diff --git a/src/config/config.h b/src/config/config.h index 1a5c1172..20f2cc09 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -133,6 +133,8 @@ struct relationship_hint_t { using relationship_hints_t = std::map; using type_aliases_t = std::map; +using type_aliases_longer_first_t = + std::map>; enum class location_t { marker, fileline, function }; diff --git a/src/include_diagram/visitor/translation_unit_visitor.cc b/src/include_diagram/visitor/translation_unit_visitor.cc index 6f47e1ea..40d411d6 100644 --- a/src/include_diagram/visitor/translation_unit_visitor.cc +++ b/src/include_diagram/visitor/translation_unit_visitor.cc @@ -43,7 +43,15 @@ translation_unit_visitor::include_visitor::include_visitor( { } -#if LLVM_VERSION_MAJOR > 14 +#if LLVM_VERSION_MAJOR >= 16 +void translation_unit_visitor::include_visitor::InclusionDirective( + clang::SourceLocation hash_loc, const clang::Token & /*include_tok*/, + clang::StringRef /*file_name*/, bool is_angled, + clang::CharSourceRange /*filename_range*/, clang::OptionalFileEntryRef file, + clang::StringRef /*search_path*/, clang::StringRef relative_path, + const clang::Module * /*imported*/, + clang::SrcMgr::CharacteristicKind file_type) +#elif LLVM_VERSION_MAJOR > 14 void translation_unit_visitor::include_visitor::InclusionDirective( clang::SourceLocation hash_loc, const clang::Token & /*include_tok*/, clang::StringRef /*file_name*/, bool is_angled, diff --git a/src/include_diagram/visitor/translation_unit_visitor.h b/src/include_diagram/visitor/translation_unit_visitor.h index 942db8f8..acc895af 100644 --- a/src/include_diagram/visitor/translation_unit_visitor.h +++ b/src/include_diagram/visitor/translation_unit_visitor.h @@ -49,7 +49,14 @@ public: ~include_visitor() override = default; -#if LLVM_VERSION_MAJOR > 14 +#if LLVM_VERSION_MAJOR >= 16 + void InclusionDirective(clang::SourceLocation HashLoc, + const clang::Token &IncludeTok, clang::StringRef FileName, + bool IsAngled, clang::CharSourceRange FilenameRange, + clang::OptionalFileEntryRef File, clang::StringRef SearchPath, + clang::StringRef RelativePath, const clang::Module *Imported, + clang::SrcMgr::CharacteristicKind FileType) override; +#elif LLVM_VERSION_MAJOR > 14 void InclusionDirective(clang::SourceLocation hash_loc, const clang::Token &include_tok, clang::StringRef file_name, bool is_angled, clang::CharSourceRange filename_range, diff --git a/src/package_diagram/visitor/translation_unit_visitor.cc b/src/package_diagram/visitor/translation_unit_visitor.cc index d12f4a3f..01deefba 100644 --- a/src/package_diagram/visitor/translation_unit_visitor.cc +++ b/src/package_diagram/visitor/translation_unit_visitor.cc @@ -344,7 +344,7 @@ bool translation_unit_visitor::find_relationships(const clang::QualType &type, type->getAs()) { if (template_specialization_type != nullptr) { for (const auto &template_argument : - *template_specialization_type) { + template_specialization_type->template_arguments()) { const auto template_argument_kind = template_argument.getKind(); if (template_argument_kind == clang::TemplateArgument::ArgKind::Integral) { diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.cc b/src/sequence_diagram/visitor/translation_unit_visitor.cc index c9f9e3ee..c2b9f11a 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.cc +++ b/src/sequence_diagram/visitor/translation_unit_visitor.cc @@ -2064,12 +2064,7 @@ bool translation_unit_visitor::simplify_system_template( std::string translation_unit_visitor::simplify_system_template( const std::string &full_name) const { - std::string result{full_name}; - for (const auto &[k, v] : config().type_aliases()) { - util::replace_all(result, k, v); - } - - return result; + return config().simplify_template_type(full_name); } std::string translation_unit_visitor::make_lambda_name( diff --git a/tests/t00008/test_case.h b/tests/t00008/test_case.h index 8687106e..f3288622 100644 --- a/tests/t00008/test_case.h +++ b/tests/t00008/test_case.h @@ -64,8 +64,12 @@ TEST_CASE("t00008", "[test-case][class]") using namespace json; +#if LLVM_VERSION_MAJOR >= 16 + REQUIRE(IsClassTemplate(j, "A")); +#else REQUIRE(IsClassTemplate( j, "A")); +#endif REQUIRE(IsClassTemplate(j, "E::nested_template")); REQUIRE(IsClass(j, "E::nested_template")); diff --git a/tests/t00014/test_case.h b/tests/t00014/test_case.h index f336750d..d1868352 100644 --- a/tests/t00014/test_case.h +++ b/tests/t00014/test_case.h @@ -70,8 +70,13 @@ TEST_CASE("t00014", "[test-case][class]") REQUIRE_THAT( puml, IsField("cb", "SimpleCallback")); +#if LLVM_VERSION_MAJOR >= 16 + REQUIRE_THAT( + puml, IsField("gcb", "GenericCallback")); +#else REQUIRE_THAT( puml, IsField("gcb", "GenericCallback")); +#endif REQUIRE_THAT(puml, IsField("vcb", "VoidCallback")); REQUIRE_THAT(puml, diff --git a/tests/test_cases.h b/tests/test_cases.h index 944911b0..4b70a55f 100644 --- a/tests/test_cases.h +++ b/tests/test_cases.h @@ -620,6 +620,26 @@ struct File { const std::string file; }; +std::optional get_element_by_id( + const nlohmann::json &j, const std::string &id) +{ + if (!j.contains("elements")) + return {}; + + for (const nlohmann::json &e : j["elements"]) { + if (e["id"] == id) + return {e}; + + if (e["type"] == "namespace" || e["type"] == "folder") { + auto maybe_e = get_element_by_id(e, id); + if (maybe_e) + return maybe_e; + } + } + + return {}; +} + std::optional get_element( const nlohmann::json &j, const std::string &name) { @@ -744,15 +764,16 @@ bool IsDeprecated(const nlohmann::json &j, const std::string &name) bool IsBaseClass(const nlohmann::json &j, const std::string &base, const std::string &subclass) { - auto sc = get_element(j, expand_name(j, subclass)); + auto base_el = get_element(j, expand_name(j, base)); + auto subclass_el = get_element(j, expand_name(j, subclass)); - if (!sc) + if (!base_el || !subclass_el) return false; - const nlohmann::json &bases = (*sc)["bases"]; + const nlohmann::json &bases = (*subclass_el)["bases"]; return std::find_if(bases.begin(), bases.end(), [&](const auto &it) { - return it["name"] == expand_name(j, base); + return it["id"] == base_el.value()["id"]; }) != bases.end(); }