Skip generation of empty or invalid relationships in PlantUML output

This commit is contained in:
Bartek Kryza
2021-05-22 14:09:39 +02:00
parent 6cc49c2660
commit 08f00889e2
6 changed files with 145 additions and 63 deletions

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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,

View File

@@ -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
View 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)
{
}
};
}

View File

@@ -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__)