Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions cpp/libclang/src/visitor/src/class_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,7 @@ fn parse_method(entity: &Entity, parsed_method_type: &ParsedMethodType) -> Optio

let args = method_arguments(entity);

let arg_count = args.len();
for (idx, arg) in args.into_iter().enumerate() {
for arg in args {
let raw_param_type = arg
.get_type()
.map(|ty| ty.get_display_name())
Expand All @@ -276,11 +275,20 @@ fn parse_method(entity: &Entity, parsed_method_type: &ParsedMethodType) -> Optio
parameters.push(FunctionArgument {
name: arg.get_name().unwrap_or_default(),
param_type: Some(param_type),
is_variadic: method_is_variadic && idx + 1 == arg_count,
is_variadic: false,
is_pack_expansion,
});
}

if method_is_variadic {
parameters.push(FunctionArgument {
name: String::new(),
param_type: None,
is_variadic: true,
is_pack_expansion: false,
});
}

Some(Method {
name,
return_type,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
' *******************************************************************************
' Copyright (c) 2026 Contributors to the Eclipse Foundation
'
' See the NOTICE file(s) distributed with this work for additional
' information regarding copyright ownership.
'
' This program and the accompanying materials are made available under the
' terms of the Apache License Version 2.0 which is available at
' https://www.apache.org/licenses/LICENSE-2.0
'
' SPDX-License-Identifier: Apache-2.0
' *******************************************************************************
@startuml c_variadic_method

class Writer {
+ AllocAndWrite(...): void
}

@enduml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"c_variadic_method.puml": {
"name": "c_variadic_method",
"entities": [
{
"id": "Writer",
"name": "Writer",
"enclosing_namespace_id": null,
"entity_type": "Class",
"type_aliases": [],
"methods": [
{
"name": "AllocAndWrite",
"return_type": "void",
"visibility": "public",
"parameters": [
{
"name": "",
"param_type": null,
"is_variadic": true,
"is_pack_expansion": false
}
],
"template_parameters": null,
"modifiers": [],
"source_location": {
"file": "",
"line": 16
}
}
],
"template_parameters": null,
"enum_literals": [],
"variables": [],
"relationships": [],
"source_location": {
"file": "plantuml/parser/integration_test/class_diagram/c_variadic_method/c_variadic_method.puml",
"line": 15
}
}
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ package modifier_case {

class ServiceImpl {
{abstract} + Run() : ResultBlank
{static} + Finalize()
+ {static} Finalize()
}

class AllowModeChangeService {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ package sample::newline #Lavender
class ServiceImpl implements ServiceContract {
' Priority group 3: member-level syntax
{abstract} + Run() : ResultBlank
{static} + Finalize()
+ {static} Finalize()
+ Submit(callable) : auto <<const>>
+ Build()
--
Expand Down
15 changes: 14 additions & 1 deletion plantuml/parser/puml_parser/src/class_diagram/src/class_ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,12 @@ pub struct Relationship {
pub struct Param {
pub name: Option<String>,
pub param_type: Option<String>,
pub varargs: bool,
/// C-style variadic parameter represented by a standalone `...`.
#[serde(default, skip_serializing_if = "is_false")]
pub is_c_variadic: bool,
/// Typed template parameter pack represented by a type followed by `...`.
#[serde(default, skip_serializing_if = "is_false")]
pub is_pack_expansion: bool,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
Expand Down Expand Up @@ -130,8 +135,15 @@ pub struct Method {
pub params: Vec<Param>,
pub r#type: Option<String>,
pub modifiers: Vec<String>,
#[serde(default, skip_serializing_if = "is_false")]
pub is_friend: bool,
pub source_location: SourceLocation,
}

fn is_false(value: &bool) -> bool {
!*value
}

impl Default for Method {
fn default() -> Self {
Method {
Expand All @@ -141,6 +153,7 @@ impl Default for Method {
params: Vec::new(),
r#type: None,
modifiers: Vec::new(),
is_friend: false,
source_location: SourceLocation::default(),
}
}
Expand Down
63 changes: 35 additions & 28 deletions plantuml/parser/puml_parser/src/class_diagram/src/class_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,9 @@ impl ClassParseSession<'_> {
match inner.as_rule() {
Rule::identifier => name = Some(inner.as_str().to_string()),
Rule::type_name => typ = Some(inner.as_str().trim().to_string()),
Rule::static_modifier | Rule::constexpr_modifier => {
attr.modifiers.push(inner.as_str().to_string())
}
_ => {}
}
}
Expand Down Expand Up @@ -341,10 +344,16 @@ impl ClassParseSession<'_> {

let mut name: Option<String> = None;
let mut ty: Option<String> = None;
let mut varargs = false;
let is_pack_expansion = pair.as_rule() == Rule::typed_param_pack;

// param -> param_named | param_cpp_named | param_unnamed
// typed_param_pack -> param ~ varargs
let inner = pair.into_inner().next().unwrap();
let inner = if inner.as_rule() == Rule::param {
inner.into_inner().next().unwrap()
} else {
inner
};

match inner.as_rule() {
Rule::param_named => {
Expand All @@ -356,9 +365,6 @@ impl ClassParseSession<'_> {
Rule::type_name => {
ty = Some(p.as_str().trim().to_string());
}
Rule::varargs => {
varargs = true;
}
_ => {}
}
}
Expand All @@ -373,30 +379,21 @@ impl ClassParseSession<'_> {
Rule::identifier => {
name = Some(p.as_str().to_string());
}
Rule::varargs => {
varargs = true;
}
_ => {}
}
}
}

Rule::param_unnamed => {
for p in inner.into_inner() {
match p.as_rule() {
Rule::type_name => {
let raw = p.as_str().trim().to_string();
if p.as_rule() == Rule::type_name {
let raw = p.as_str().trim().to_string();

if is_likely_type_only_param(&raw) {
ty = Some(raw);
} else {
name = Some(raw);
}
}
Rule::varargs => {
varargs = true;
if is_likely_type_only_param(&raw) {
ty = Some(raw);
} else {
name = Some(raw);
}
_ => {}
}
}
}
Expand All @@ -407,7 +404,8 @@ impl ClassParseSession<'_> {
Param {
name,
param_type: ty,
varargs,
is_c_variadic: false,
is_pack_expansion,
}
}

Expand Down Expand Up @@ -440,14 +438,23 @@ impl ClassParseSession<'_> {
| Rule::abstract_modifier
| Rule::const_method_qualifier
| Rule::noexcept_method_qualifier => method.modifiers.push(p.as_str().to_string()),
Rule::friend_specifier => method.is_friend = true,
Rule::pure_virtual_suffix => ensure_abstract_modifier(&mut method),
Rule::class_visibility => vis = Some(p),
Rule::method_name | Rule::identifier => name = Some(p.as_str().to_string()),
Rule::param_list => {
for param_pair in p.into_inner() {
if param_pair.as_rule() == Rule::param {
let param = Self::parse_param(param_pair);
method.params.push(param);
match param_pair.as_rule() {
Rule::param | Rule::typed_param_pack => {
method.params.push(Self::parse_param(param_pair));
}
Rule::c_variadic_param => method.params.push(Param {
name: None,
param_type: None,
is_c_variadic: true,
is_pack_expansion: false,
}),
_ => {}
}
}
}
Expand Down Expand Up @@ -1174,9 +1181,9 @@ mod tests {
}

#[test]
fn test_parse_param_unnamed_varargs() {
fn test_parse_typed_param_pack() {
let input = "int...";
let pair = PlantUmlCommonParser::parse(Rule::param, input)
let pair = PlantUmlCommonParser::parse(Rule::typed_param_pack, input)
.unwrap()
.next()
.unwrap();
Expand All @@ -1185,7 +1192,7 @@ mod tests {

assert_eq!(param.name, None);
assert_eq!(param.param_type.as_deref(), Some("int"));
assert!(param.varargs);
assert!(param.is_pack_expansion);
}

#[test]
Expand All @@ -1200,7 +1207,7 @@ mod tests {

assert_eq!(param.name.as_deref(), Some("callable"));
assert_eq!(param.param_type, None);
assert!(!param.varargs);
assert!(!param.is_pack_expansion);
}

#[test]
Expand All @@ -1215,7 +1222,7 @@ mod tests {

assert_eq!(param.name, None);
assert_eq!(param.param_type.as_deref(), Some("InfrastructureContext"));
assert!(!param.varargs);
assert!(!param.is_pack_expansion);
}

#[test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ fn test_class_merge() {
run_class_diagram_parser_case("class_merge");
}

#[test]
fn test_constexpr_attribute() {
run_class_diagram_parser_case("constexpr_attribute");
}

#[test]
fn test_color() {
run_class_diagram_parser_case("color");
Expand All @@ -90,6 +95,31 @@ fn test_cpp_style() {
run_class_diagram_parser_case("cpp_style");
}

#[test]
fn test_friend_method() {
run_class_diagram_parser_case("friend_method");
}

#[test]
fn test_method_modifier_placement() {
run_class_diagram_parser_case("method_modifier_placement");
}

#[test]
fn test_multiline_note() {
run_class_diagram_parser_case("multiline_note");
}

#[test]
fn test_qualified_method_name() {
run_class_diagram_parser_case("qualified_method_name");
}

#[test]
fn test_varargs_method() {
run_class_diagram_parser_case("varargs_method");
}

#[test]
fn test_ctrl_instruct() {
run_class_diagram_parser_case("ctrl_instruct");
Expand Down
Loading
Loading