diff --git a/CHANGELOG.md b/CHANGELOG.md index 785324e9..1318bb94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # CHANGELOG + * Fixed building with LLVM 18 (#251) + ### 0.5.1 * Fixed elements filter in sequence diagrams (#248) diff --git a/README.md b/README.md index 9254ddce..c6b697ae 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![Build status](https://github.com/bkryza/clang-uml/actions/workflows/build.yml/badge.svg)](https://github.com/bkryza/clang-uml/actions) [![Coverage](https://codecov.io/gh/bkryza/clang-uml/branch/master/graph/badge.svg)](https://codecov.io/gh/bkryza/clang-uml) [![Version](https://img.shields.io/badge/version-0.5.1-blue)](https://github.com/bkryza/clang-uml/releases) -[![Version](https://img.shields.io/badge/LLVM-12,13,14,15,16,17-orange)](https://github.com/bkryza/clang-uml/releases) +[![Version](https://img.shields.io/badge/LLVM-12,13,14,15,16,17,18-orange)](https://github.com/bkryza/clang-uml/releases) [![Doxygen](https://img.shields.io/badge/Docs-Doxygen-gainsboro)](https://clang-uml.github.io) `clang-uml` is an automatic C++ to UML class, sequence, package and include diagram generator, driven by diff --git a/cmake/LLVMSetup.cmake b/cmake/LLVMSetup.cmake index a5b27081..26a96025 100644 --- a/cmake/LLVMSetup.cmake +++ b/cmake/LLVMSetup.cmake @@ -1,5 +1,9 @@ message(STATUS "Checking for LLVM and Clang...") +if(LLVM_VERSION STREQUAL "18") + set(LLVM_VERSION "18.1") +endif() + # If user provided a path to llvm-config executable use it to detect # LLVM Version and appropriate CMake module path if(NOT "${LLVM_CONFIG_PATH}" STREQUAL "") diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 1b265f14..014c6c38 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -7,6 +7,7 @@ * [Diagram generation is very slow](#diagram-generation-is-very-slow) * [Diagram generated with PlantUML is cropped](#diagram-generated-with-plantuml-is-cropped) * [Clang produces several warnings during diagram generation](#clang-produces-several-warnings-during-diagram-generation) + * [Errors with C++20 modules and LLVM 18](#errors-with-c20-modules-and-llvm-18) * [Cannot generate diagrams from header-only projects](#cannot-generate-diagrams-from-header-only-projects) * [YAML anchors and aliases are not fully supported](#yaml-anchors-and-aliases-are-not-fully-supported) * [Schema validation error is thrown, but the configuration file is correct](#schema-validation-error-is-thrown-but-the-configuration-file-is-correct) @@ -143,6 +144,21 @@ remove_compile_flags: - -Wshadow ``` +### Errors with C++20 modules and LLVM 18 + +When running `clang-uml` on code using C++20 modules, the LLVM version used to +build the project must be compatible with the LLVM version linked to +`clang-uml`, otherwise you'll get error like this: +``` +fatal error: malformed or corrupted AST file: 'malformed block record in AST file' +``` +or like this: +``` +error: PCH file uses an older PCH format that is no longer supported +``` + +In particular versions 17 and 18 of LLVM are not compatible in this regard. + ### Cannot generate diagrams from header-only projects Currently, in order to generate UML diagrams using `clang-uml` it is necessary diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index 23900ac8..fef69cd3 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -1280,7 +1280,11 @@ void translation_unit_visitor::process_method_properties( const bool is_constructor = c.name() == method_name; const bool is_destructor = fmt::format("~{}", c.name()) == method_name; +#if LLVM_VERSION_MAJOR > 17 + method.is_pure_virtual(mf.isPureVirtual()); +#else method.is_pure_virtual(mf.isPure()); +#endif method.is_virtual(mf.isVirtual()); method.is_const(mf.isConst()); method.is_defaulted(mf.isDefaulted()); diff --git a/src/common/visitor/comment/clang_visitor.cc b/src/common/visitor/comment/clang_visitor.cc index 1c80b09e..7fd5a226 100644 --- a/src/common/visitor/comment/clang_visitor.cc +++ b/src/common/visitor/comment/clang_visitor.cc @@ -18,6 +18,14 @@ #include "clang_visitor.h" +#if LLVM_VERSION_MAJOR > 17 +#define CLANG_UML_LLVM_COMMENT_KIND(COMMENT_KIND) \ + clang::comments::CommentKind::COMMENT_KIND +#else +#define CLANG_UML_LLVM_COMMENT_KIND(COMMENT_KIND) \ + clang::comments::Comment::COMMENT_KIND##Kind +#endif + namespace clanguml::common::visitor::comment { clang_visitor::clang_visitor(clang::SourceManager &source_manager) @@ -45,7 +53,6 @@ void clang_visitor::visit( cmt["formatted"] = formatted_comment; using clang::comments::BlockCommandComment; - using clang::comments::Comment; using clang::comments::FullComment; using clang::comments::ParagraphComment; using clang::comments::ParamCommandComment; @@ -59,7 +66,7 @@ void clang_visitor::visit( for (const auto *block : full_comment->getBlocks()) { const auto block_kind = block->getCommentKind(); - if (block_kind == Comment::ParagraphCommentKind) { + if (block_kind == CLANG_UML_LLVM_COMMENT_KIND(ParagraphComment)) { std::string paragraph_text; visit_paragraph(clang::dyn_cast(block), traits, @@ -75,18 +82,21 @@ void clang_visitor::visit( cmt["paragraph"].push_back(paragraph_text); } - else if (block_kind == Comment::TextCommentKind) { + else if (block_kind == CLANG_UML_LLVM_COMMENT_KIND(TextComment)) { // TODO } - else if (block_kind == Comment::ParamCommandCommentKind) { + else if (block_kind == + CLANG_UML_LLVM_COMMENT_KIND(ParamCommandComment)) { visit_param_command( clang::dyn_cast(block), traits, cmt); } - else if (block_kind == Comment::TParamCommandCommentKind) { + else if (block_kind == + CLANG_UML_LLVM_COMMENT_KIND(TParamCommandComment)) { visit_tparam_command( clang::dyn_cast(block), traits, cmt); } - else if (block_kind == Comment::BlockCommandCommentKind) { + else if (block_kind == + CLANG_UML_LLVM_COMMENT_KIND(BlockCommandComment)) { if (const auto *command = clang::dyn_cast(block); command != nullptr) { @@ -135,7 +145,7 @@ void clang_visitor::visit_block_command( paragraph_it != command->child_end(); ++paragraph_it) { if ((*paragraph_it)->getCommentKind() == - Comment::ParagraphCommentKind) { + CLANG_UML_LLVM_COMMENT_KIND(ParagraphComment)) { visit_paragraph(clang::dyn_cast(*paragraph_it), traits, command_text); } @@ -168,7 +178,8 @@ void clang_visitor::visit_param_command( for (const auto *it = command->child_begin(); it != command->child_end(); ++it) { - if ((*it)->getCommentKind() == Comment::ParagraphCommentKind) { + if ((*it)->getCommentKind() == + CLANG_UML_LLVM_COMMENT_KIND(ParagraphComment)) { visit_paragraph( clang::dyn_cast(*it), traits, description); } @@ -202,7 +213,8 @@ void clang_visitor::visit_tparam_command( for (const auto *it = command->child_begin(); it != command->child_end(); ++it) { - if ((*it)->getCommentKind() == Comment::ParagraphCommentKind) { + if ((*it)->getCommentKind() == + CLANG_UML_LLVM_COMMENT_KIND(ParagraphComment)) { visit_paragraph( clang::dyn_cast(*it), traits, description); } @@ -232,7 +244,8 @@ void clang_visitor::visit_paragraph( for (const auto *text_it = paragraph->child_begin(); text_it != paragraph->child_end(); ++text_it) { - if ((*text_it)->getCommentKind() == Comment::TextCommentKind && + if ((*text_it)->getCommentKind() == + CLANG_UML_LLVM_COMMENT_KIND(TextComment) && clang::dyn_cast(*text_it) != nullptr) { // Merge paragraph lines into a single string text += clang::dyn_cast(*text_it)->getText(); diff --git a/src/common/visitor/template_builder.h b/src/common/visitor/template_builder.h index ebae7ecc..7a446e23 100644 --- a/src/common/visitor/template_builder.h +++ b/src/common/visitor/template_builder.h @@ -217,6 +217,21 @@ public: template_parameter process_integral_argument( const clang::TemplateArgument &arg); +#if LLVM_VERSION_MAJOR > 17 + /** + * @brief Process `clang::TemplateArgument::StructuralValue` + * + * @note The template argument is a non-type template argument that can't be + * represented by the special-case Declaration, NullPtr, or Integral + * forms. + * + * @param arg Template argument + * @return Return template argument model + */ + template_parameter process_structural_argument( + const clang::TemplateArgument &arg); +#endif + /** * @brief Process `clang::TemplateArgument::NullPtr` * @@ -1041,6 +1056,10 @@ void template_builder::argument_process_dispatch( argument.push_back(a); } break; +#if LLVM_VERSION_MAJOR > 17 + case clang::TemplateArgument::StructuralValue: + break; +#endif } } @@ -1213,6 +1232,21 @@ template_parameter template_builder::process_integral_argument( return template_parameter::make_argument(result); } +#if LLVM_VERSION_MAJOR > 17 +template +template_parameter template_builder::process_structural_argument( + const clang::TemplateArgument &arg) +{ + assert(arg.getKind() == clang::TemplateArgument::StructuralValue); + + std::string result; + llvm::raw_string_ostream ostream(result); + arg.dump(ostream); + + return template_parameter::make_argument(result); +} +#endif + template template_parameter template_builder::process_null_argument( const clang::TemplateArgument &arg)