Skip generation of empty or invalid relationships in PlantUML output
This commit is contained in:
@@ -13,6 +13,15 @@ TODO
|
||||
|
||||
## Usage
|
||||
|
||||
### Generating compile commands database
|
||||
`clang-uml` requires an up-to-date
|
||||
[compile-commands.json](https://clang.llvm.org/docs/JSONCompilationDatabase.html)
|
||||
file, containing the list of commands used for compiling the source code.
|
||||
Nowadays, this file can be generated rather easily using multiple methods:
|
||||
* For [CMake](https://cmake.org/) projects, simply invoke the `cmake` command
|
||||
as `cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ...`
|
||||
* For Make projects checkout [compiledb](https://github.com/nickdiego/compiledb) or [Bear](https://github.com/rizsotto/Bear)
|
||||
|
||||
### Invocation
|
||||
|
||||
### Configuration file format and examples
|
||||
|
||||
@@ -199,47 +199,68 @@ public:
|
||||
|
||||
if (m_config.should_include_relationship("inheritance"))
|
||||
for (const auto &b : c.bases) {
|
||||
ostr << m_model.to_alias(m_config.using_namespace,
|
||||
ns_relative(m_config.using_namespace, b.name))
|
||||
<< " <|-- "
|
||||
<< m_model.to_alias(m_config.using_namespace,
|
||||
ns_relative(m_config.using_namespace, c.name))
|
||||
<< std::endl;
|
||||
std::stringstream relstr;
|
||||
try {
|
||||
relstr << m_model.to_alias(m_config.using_namespace,
|
||||
ns_relative(m_config.using_namespace, b.name))
|
||||
<< " <|-- "
|
||||
<< m_model.to_alias(m_config.using_namespace,
|
||||
ns_relative(m_config.using_namespace, c.name))
|
||||
<< std::endl;
|
||||
ostr << relstr.str();
|
||||
}
|
||||
catch (error::uml_alias_missing &e) {
|
||||
LOG_ERROR("Skipping inheritance relation from {} to {} due "
|
||||
"to: {}",
|
||||
b.name, c.name, e.what());
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &r : c.relationships) {
|
||||
if (!m_config.should_include_relationship(name(r.type)))
|
||||
continue;
|
||||
|
||||
std::string destination;
|
||||
if (r.destination.find("#") != std::string::npos ||
|
||||
r.destination.find("@") != std::string::npos) {
|
||||
destination = m_model.usr_to_name(
|
||||
m_config.using_namespace, r.destination);
|
||||
std::stringstream relstr;
|
||||
|
||||
// If something went wrong and we have an empty destination
|
||||
// generate the relationship but comment it out for
|
||||
// debugging
|
||||
if (destination.empty()) {
|
||||
ostr << "' ";
|
||||
std::string destination;
|
||||
try {
|
||||
if (r.destination.find("#") != std::string::npos ||
|
||||
r.destination.find("@") != std::string::npos) {
|
||||
destination = m_model.usr_to_name(
|
||||
m_config.using_namespace, r.destination);
|
||||
|
||||
// If something went wrong and we have an empty destination
|
||||
// generate the relationship but comment it out for
|
||||
// debugging
|
||||
if (destination.empty()) {
|
||||
relstr << "' ";
|
||||
destination = r.destination;
|
||||
}
|
||||
}
|
||||
else {
|
||||
destination = r.destination;
|
||||
}
|
||||
|
||||
relstr << m_model.to_alias(m_config.using_namespace,
|
||||
ns_relative(m_config.using_namespace,
|
||||
c.full_name(m_config.using_namespace)))
|
||||
<< " " << to_string(r.type) << " "
|
||||
<< m_model.to_alias(m_config.using_namespace,
|
||||
ns_relative(
|
||||
m_config.using_namespace, destination));
|
||||
|
||||
if (!r.label.empty())
|
||||
relstr << " : " << r.label;
|
||||
|
||||
relstr << std::endl;
|
||||
ostr << relstr.str();
|
||||
}
|
||||
else {
|
||||
destination = r.destination;
|
||||
catch (error::uml_alias_missing &e) {
|
||||
LOG_ERROR("Skipping {} relation from {} to {} due "
|
||||
"to: {}",
|
||||
to_string(r.type), c.full_name(m_config.using_namespace),
|
||||
destination, e.what());
|
||||
}
|
||||
|
||||
ostr << m_model.to_alias(m_config.using_namespace,
|
||||
ns_relative(m_config.using_namespace,
|
||||
c.full_name(m_config.using_namespace)))
|
||||
<< " " << to_string(r.type) << " "
|
||||
<< m_model.to_alias(m_config.using_namespace,
|
||||
ns_relative(m_config.using_namespace, destination));
|
||||
|
||||
if (!r.label.empty())
|
||||
ostr << " : " << r.label;
|
||||
|
||||
ostr << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,29 +280,39 @@ public:
|
||||
continue;
|
||||
|
||||
std::string destination;
|
||||
if (r.destination.find("#") != std::string::npos ||
|
||||
r.destination.find("@") != std::string::npos) {
|
||||
destination = m_model.usr_to_name(
|
||||
m_config.using_namespace, r.destination);
|
||||
if (destination.empty()) {
|
||||
ostr << "' ";
|
||||
std::stringstream relstr;
|
||||
try {
|
||||
if (r.destination.find("#") != std::string::npos ||
|
||||
r.destination.find("@") != std::string::npos) {
|
||||
destination = m_model.usr_to_name(
|
||||
m_config.using_namespace, r.destination);
|
||||
if (destination.empty()) {
|
||||
relstr << "' ";
|
||||
destination = r.destination;
|
||||
}
|
||||
}
|
||||
else {
|
||||
destination = r.destination;
|
||||
}
|
||||
|
||||
relstr << m_model.to_alias(m_config.using_namespace,
|
||||
ns_relative(m_config.using_namespace, e.name))
|
||||
<< " " << to_string(r.type) << " "
|
||||
<< m_model.to_alias(m_config.using_namespace,
|
||||
ns_relative(
|
||||
m_config.using_namespace, destination));
|
||||
|
||||
if (!r.label.empty())
|
||||
relstr << " : " << r.label;
|
||||
|
||||
relstr << std::endl;
|
||||
ostr << relstr.str();
|
||||
}
|
||||
else {
|
||||
destination = r.destination;
|
||||
catch (error::uml_alias_missing &ex) {
|
||||
LOG_ERROR("Skipping {} relation from {} to {} due "
|
||||
"to: {}",
|
||||
to_string(r.type), e.name, destination, ex.what());
|
||||
}
|
||||
|
||||
ostr << m_model.to_alias(m_config.using_namespace,
|
||||
ns_relative(m_config.using_namespace, e.name))
|
||||
<< " " << to_string(r.type) << " "
|
||||
<< m_model.to_alias(m_config.using_namespace,
|
||||
ns_relative(m_config.using_namespace, destination));
|
||||
|
||||
if (!r.label.empty())
|
||||
ostr << " : " << r.label;
|
||||
|
||||
ostr << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "util/error.h"
|
||||
#include "util/util.h"
|
||||
|
||||
#include <clang-c/CXCompilationDatabase.h>
|
||||
@@ -174,6 +175,13 @@ public:
|
||||
|
||||
void add_relationship(class_relationship &&cr)
|
||||
{
|
||||
if (cr.destination.empty() || type_aliases.count(cr.destination) == 0) {
|
||||
LOG_WARN(
|
||||
"Skipping relationship '{}' - {} - '{}' due to missing alias",
|
||||
cr.destination, to_string(cr.type), usr);
|
||||
return;
|
||||
}
|
||||
|
||||
auto it = std::find(relationships.begin(), relationships.end(), cr);
|
||||
if (it == relationships.end())
|
||||
relationships.emplace_back(std::move(cr));
|
||||
@@ -281,7 +289,8 @@ struct diagram {
|
||||
}
|
||||
}
|
||||
|
||||
return full_name;
|
||||
throw error::uml_alias_missing(
|
||||
fmt::format("Missing alias for {}", full_name));
|
||||
}
|
||||
|
||||
std::string usr_to_name(const std::vector<std::string> &using_namespaces,
|
||||
|
||||
@@ -659,9 +659,12 @@ void tu_visitor::process_template_method(
|
||||
{
|
||||
class_method m;
|
||||
m.name = util::trim(mf.name());
|
||||
m.type = cppast::to_string(
|
||||
static_cast<const cppast::cpp_member_function &>(mf.function())
|
||||
.return_type());
|
||||
if (mf.function().kind() == cppast::cpp_entity_kind::constructor_t)
|
||||
m.type = "void";
|
||||
else
|
||||
m.type = cppast::to_string(
|
||||
static_cast<const cppast::cpp_member_function &>(mf.function())
|
||||
.return_type());
|
||||
m.is_pure_virtual = false;
|
||||
m.is_virtual = false;
|
||||
m.is_const = cppast::is_const(
|
||||
@@ -747,16 +750,6 @@ void tu_visitor::process_function_parameter(
|
||||
// so we have to deduce the correct namespace prefix of the
|
||||
// template which is being instantiated
|
||||
mp.type = cppast::to_string(param.type());
|
||||
|
||||
auto &template_instantiation_type =
|
||||
static_cast<const cppast::cpp_template_instantiation_type &>(
|
||||
param_type);
|
||||
auto &primary_template_entity =
|
||||
template_instantiation_type.primary_template();
|
||||
|
||||
auto trawname = cppast::to_string(template_instantiation_type);
|
||||
auto pte = cx::util::fully_prefixed(ctx.namespace_,
|
||||
primary_template_entity.get(ctx.entity_index)[0].get());
|
||||
}
|
||||
else {
|
||||
mp.type = cppast::to_string(param.type());
|
||||
@@ -885,6 +878,13 @@ void tu_visitor::process_template_template_parameter(
|
||||
|
||||
void tu_visitor::process_friend(const cppast::cpp_friend &f, class_ &parent)
|
||||
{
|
||||
// Only process friends to other classes or class templates
|
||||
if (!f.entity() ||
|
||||
(f.entity().value().kind() != cppast::cpp_entity_kind::class_t) &&
|
||||
(f.entity().value().kind() !=
|
||||
cppast::cpp_entity_kind::class_template_t))
|
||||
return;
|
||||
|
||||
class_relationship r;
|
||||
r.type = relationship_t::kFriendship;
|
||||
r.label = "<<friend>>";
|
||||
|
||||
29
src/util/error.h
Normal file
29
src/util/error.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* src/util/error.h
|
||||
*
|
||||
* Copyright (c) 2021 Bartek Kryza <bkryza@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace clanguml::error {
|
||||
struct uml_alias_missing : public virtual std::runtime_error {
|
||||
uml_alias_missing(const std::string &message)
|
||||
: std::runtime_error(message)
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -34,6 +34,10 @@ std::string trim(const std::string &s);
|
||||
#define __FILENAME__ \
|
||||
(strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
|
||||
|
||||
#define LOG_ERROR(fmt__, ...) \
|
||||
spdlog::error(std::string("[{}:{}] ") + fmt__, __FILENAME__, __LINE__, \
|
||||
##__VA_ARGS__)
|
||||
|
||||
#define LOG_WARN(fmt__, ...) \
|
||||
spdlog::warn(std::string("[{}:{}] ") + fmt__, __FILENAME__, __LINE__, \
|
||||
##__VA_ARGS__)
|
||||
|
||||
Reference in New Issue
Block a user