Skip to content

Commit

Permalink
Added support for local classes defined in function body in sequence …
Browse files Browse the repository at this point in the history
…diagrams.
  • Loading branch information
bkryza committed Feb 4, 2025
1 parent 8be79b5 commit 32ab85a
Show file tree
Hide file tree
Showing 8 changed files with 160 additions and 8 deletions.
15 changes: 13 additions & 2 deletions src/common/visitor/translation_unit_visitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,19 @@ template <typename ConfigT, typename DiagramT> class translation_unit_visitor {
if (config().filter_mode() == config::filter_mode_t::advanced)
return true;

auto should_include_namespace = diagram().should_include(
common::model::namespace_{decl->getQualifiedNameAsString()});
auto should_include_namespace{false};

if (const auto *record = clang::dyn_cast<clang::CXXRecordDecl>(decl);
record != nullptr && record->isLocalClass() != nullptr &&
diagram().type() == common::model::diagram_t::kSequence) {
should_include_namespace =
diagram().should_include(common::model::namespace_{
record->isLocalClass()->getQualifiedNameAsString()});
}
else {
should_include_namespace = diagram().should_include(
common::model::namespace_{decl->getQualifiedNameAsString()});
}

const auto decl_file =
decl->getLocation().printToString(source_manager());
Expand Down
44 changes: 38 additions & 6 deletions src/sequence_diagram/visitor/translation_unit_visitor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,19 @@ bool translation_unit_visitor::VisitObjCInterfaceDecl(
return true;
}

bool translation_unit_visitor::TraverseCXXRecordDecl(
clang::CXXRecordDecl *declaration)
{
auto context_backup = context();

RecursiveASTVisitor<translation_unit_visitor>::TraverseCXXRecordDecl(
declaration);

call_expression_context_ = context_backup;

return true;
}

bool translation_unit_visitor::VisitCXXRecordDecl(
clang::CXXRecordDecl *declaration)
{
Expand All @@ -166,10 +179,6 @@ bool translation_unit_visitor::VisitCXXRecordDecl(
return true;
}

// TODO: Add support for classes defined in function/method bodies
if (declaration->isLocalClass() != nullptr)
return true;

LOG_TRACE("Visiting class declaration at {}",
declaration->getBeginLoc().printToString(source_manager()));

Expand Down Expand Up @@ -923,8 +932,6 @@ bool translation_unit_visitor::TraverseReturnStmt(clang::ReturnStmt *stmt)

RecursiveASTVisitor<translation_unit_visitor>::TraverseReturnStmt(stmt);

// translation_unit_visitor::VisitReturnStmt(stmt);

LOG_TRACE("Leaving return statement at {}",
stmt->getBeginLoc().printToString(source_manager()));

Expand Down Expand Up @@ -2505,6 +2512,31 @@ translation_unit_visitor::create_class_model(clang::CXXRecordDecl *cls)
return {};
}
}
else if (cls->isLocalClass() != nullptr) {
const auto *func_declaration = cls->isLocalClass();

eid_t func_model_id =
get_unique_id(eid_t{func_declaration->getID()}).has_value()
? *get_unique_id(eid_t{func_declaration->getID()})
: eid_t{func_declaration->getID()};

const auto &func_model =
diagram().get_participant<model::participant>(func_model_id);

if (!func_model.has_value())
return {};

LOG_DBG("Visiting local class declaration: {} in {}",
cls->getQualifiedNameAsString(),
func_model.value().full_name(false));

auto local_cls_ns = func_model.value().get_namespace();

c.set_name(func_model.value().full_name_no_ns() + "##" +
common::get_tag_name(*cls));
c.set_namespace(local_cls_ns);
c.set_id(common::to_id(c.full_name(false)));
}
else {
c.set_name(common::get_tag_name(*cls));
c.set_namespace(ns);
Expand Down
2 changes: 2 additions & 0 deletions src/sequence_diagram/visitor/translation_unit_visitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ class translation_unit_visitor

bool VisitCXXMethodDecl(clang::CXXMethodDecl *declaration);

bool TraverseCXXRecordDecl(clang::CXXRecordDecl *declaration);

bool VisitCXXRecordDecl(clang::CXXRecordDecl *declaration);

bool VisitClassTemplateDecl(clang::ClassTemplateDecl *declaration);
Expand Down
12 changes: 12 additions & 0 deletions tests/t20072/.clang-uml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
diagrams:
t20072_sequence:
type: sequence
glob:
- t20072.cc
include:
namespaces:
- clanguml::t20072
using_namespace: clanguml::t20072
generate_return_types: true
from:
- function: "clanguml::t20072::tmain()"
48 changes: 48 additions & 0 deletions tests/t20072/t20072.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#include <string>

namespace clanguml::t20072 {

bool validate(int v) { return v != 0; }

auto foo(int f)
{
class Foo {
public:
void set(int value)
{
if (validate(value))
value_ = value;
}

int get() const { return value_; }

private:
int value_;
};

Foo result;
result.set(f);

return result;
}

template <typename T> auto bar(const T &b, std::string msg = "two")
{
struct Foo {
int result(int c) const { return value / c; }

int value;
};

Foo foo{b.get() + 10};

return foo.result(msg.size());
}

int tmain()
{
auto v = bar(foo(1));

return 0;
}
} // namespace clanguml::t20072
42 changes: 42 additions & 0 deletions tests/t20072/test_case.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* tests/t20072/test_case.h
*
* Copyright (c) 2021-2025 Bartek Kryza <[email protected]>
*
* 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.
*/

TEST_CASE("t20072")
{
using namespace clanguml::test;
using namespace std::string_literals;

auto [config, db, diagram, model] =
CHECK_SEQUENCE_MODEL("t20072", "t20072_sequence");

CHECK_SEQUENCE_DIAGRAM(*config, diagram, *model, [](const auto &src) {
REQUIRE(MessageOrder(src,
{
{"tmain()", "foo(int)", ""}, //
{"foo(int)", "foo(int)::Foo", "set(int)"}, //
{"foo(int)::Foo", "validate(int)", ""}, //

{"tmain()", "bar<Foo>(const Foo &,std::string)", ""}, //
{"bar<Foo>(const Foo &,std::string)", "foo(int)::Foo",
"get() const"}, //
{"bar<Foo>(const Foo &,std::string)",
"bar<Foo>(const Foo &,std::string)::Foo",
"result(int) const"}, //
}));
});
}
2 changes: 2 additions & 0 deletions tests/test_cases.cc
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,8 @@ void CHECK_INCLUDE_DIAGRAM(const clanguml::config::config &config,
#include "t20071/test_case.h"
#endif

#include "t20072/test_case.h"

///
/// Package diagram tests
///
Expand Down
3 changes: 3 additions & 0 deletions tests/test_cases.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,9 @@ test_cases:
- name: t20071
title: Test case for sequence diagram with coroutines and combined participants
description:
- name: t20072
title: Test case for sequence diagram with local classes defined in function bodies
description:
Package diagrams:
- name: t30001
title: Basic package diagram test case
Expand Down

0 comments on commit 32ab85a

Please sign in to comment.