diff --git a/cpp/libclang/integration_test/cases/method_modifier_final/BUILD b/cpp/libclang/integration_test/cases/method_modifier_final/BUILD new file mode 100644 index 00000000..52631ecf --- /dev/null +++ b/cpp/libclang/integration_test/cases/method_modifier_final/BUILD @@ -0,0 +1,25 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +load("//cpp/libclang/integration_test:test_rules.bzl", "cpp_parser_integration_test") + +cc_library( + name = "method_modifier_final", + srcs = ["final_sample.cpp"], + visibility = ["//cpp/libclang:__subpackages__"], +) + +cpp_parser_integration_test( + name = "test_method_modifier_final", + expected_output = ["expected.json"], + target = ":method_modifier_final", +) diff --git a/cpp/libclang/integration_test/cases/method_modifier_final/expected.json b/cpp/libclang/integration_test/cases/method_modifier_final/expected.json new file mode 100644 index 00000000..8b564143 --- /dev/null +++ b/cpp/libclang/integration_test/cases/method_modifier_final/expected.json @@ -0,0 +1,98 @@ +{ + "types": { + "FinalMethodBase": { + "id": "FinalMethodBase", + "name": "FinalMethodBase", + "enclosing_namespace_id": null, + "stereotypes": [], + "entity_type": "Class", + "type_aliases": [], + "variables": [], + "methods": [ + { + "name": "sealedHere", + "return_type": "void", + "visibility": "public", + "parameters": [], + "template_parameters": null, + "modifiers": [ + "Virtual", + "Final" + ], + "source_location": { + "file": "cpp/libclang/integration_test/cases/method_modifier_final/final_sample.cpp", + "line": 16 + } + }, + { + "name": "overridable", + "return_type": "void", + "visibility": "public", + "parameters": [], + "template_parameters": null, + "modifiers": [ + "Virtual" + ], + "source_location": { + "file": "cpp/libclang/integration_test/cases/method_modifier_final/final_sample.cpp", + "line": 17 + } + } + ], + "template_parameters": null, + "enum_literals": [], + "relationships": [], + "source_location": { + "file": "cpp/libclang/integration_test/cases/method_modifier_final/final_sample.cpp", + "line": 14 + } + }, + "FinalMethodDerived": { + "id": "FinalMethodDerived", + "name": "FinalMethodDerived", + "enclosing_namespace_id": null, + "stereotypes": [], + "entity_type": "Class", + "type_aliases": [], + "variables": [], + "methods": [ + { + "name": "overridable", + "return_type": "void", + "visibility": "public", + "parameters": [], + "template_parameters": null, + "modifiers": [ + "Virtual", + "Override", + "Final" + ], + "source_location": { + "file": "cpp/libclang/integration_test/cases/method_modifier_final/final_sample.cpp", + "line": 22 + } + } + ], + "template_parameters": null, + "enum_literals": [], + "relationships": [ + { + "source": "FinalMethodDerived", + "target": "FinalMethodBase", + "relation_type": "Inheritance", + "source_multiplicity": null, + "target_multiplicity": null, + "source_location": { + "file": "cpp/libclang/integration_test/cases/method_modifier_final/final_sample.cpp", + "line": 20 + } + } + ], + "source_location": { + "file": "cpp/libclang/integration_test/cases/method_modifier_final/final_sample.cpp", + "line": 20 + } + } + }, + "functions": [] +} diff --git a/cpp/libclang/integration_test/cases/method_modifier_final/final_sample.cpp b/cpp/libclang/integration_test/cases/method_modifier_final/final_sample.cpp new file mode 100644 index 00000000..0eb3a1fc --- /dev/null +++ b/cpp/libclang/integration_test/cases/method_modifier_final/final_sample.cpp @@ -0,0 +1,23 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ + +class FinalMethodBase { +public: + virtual void sealedHere() final; + virtual void overridable(); +}; + +class FinalMethodDerived : public FinalMethodBase { +public: + void overridable() override final; +}; diff --git a/cpp/libclang/integration_test/cases/method_modifier_final/run_test.rs b/cpp/libclang/integration_test/cases/method_modifier_final/run_test.rs new file mode 100644 index 00000000..11cedf0f --- /dev/null +++ b/cpp/libclang/integration_test/cases/method_modifier_final/run_test.rs @@ -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 +// +// +// SPDX-License-Identifier: Apache-2.0 +// ******************************************************************************* + +use test_framework::run_parser_case; + +#[test] +fn test_method_modifier_final() { + run_parser_case(); +} diff --git a/cpp/libclang/src/visitor/src/class_visitor.rs b/cpp/libclang/src/visitor/src/class_visitor.rs index 75e7b8e3..ba3c4225 100644 --- a/cpp/libclang/src/visitor/src/class_visitor.rs +++ b/cpp/libclang/src/visitor/src/class_visitor.rs @@ -250,6 +250,10 @@ fn parse_method(entity: &Entity, parsed_method_type: &ParsedMethodType) -> Optio .get_overridden_methods() .map(|methods| !methods.is_empty()) .unwrap_or(false); + let is_final_method = entity + .get_children() + .into_iter() + .any(|child| child.get_kind() == EntityKind::FinalAttr); // Only the bare `noexcept` specifier is modeled (mirrors the PlantUML grammar, which has // no support for the conditional `noexcept(expr)` form). Requiring `BasicNoexcept` filters @@ -326,6 +330,7 @@ fn parse_method(entity: &Entity, parsed_method_type: &ParsedMethodType) -> Optio (is_noexcept_method, MethodModifier::Noexcept), (kind == EntityKind::Constructor, MethodModifier::Constructor), (kind == EntityKind::Destructor, MethodModifier::Destructor), + (is_final_method, MethodModifier::Final), ]), source_location: parse_source_location(entity), }) diff --git a/plantuml/parser/puml_parser/src/class_diagram/src/class_parser.rs b/plantuml/parser/puml_parser/src/class_diagram/src/class_parser.rs index 46fb7c49..48645e76 100644 --- a/plantuml/parser/puml_parser/src/class_diagram/src/class_parser.rs +++ b/plantuml/parser/puml_parser/src/class_diagram/src/class_parser.rs @@ -456,9 +456,8 @@ impl ClassParseSession<'_> { | Rule::override_modifier | Rule::const_method_qualifier | Rule::noexcept_method_qualifier - | Rule::trailing_override_qualifier => { - method.modifiers.push(p.as_str().to_string()) - } + | Rule::trailing_override_qualifier + | Rule::final_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), @@ -524,6 +523,8 @@ impl ClassParseSession<'_> { } Rule::class_body => { for inner in pair.into_inner() { + // Intentionally visit only Rule::class_member; mock_method_invocation pairs + // are modeled as absent from the class diagram, not as real methods. if let Rule::class_member = inner.as_rule() { for member in inner.into_inner() { self.parse_class_member_into(member, def)?; diff --git a/plantuml/parser/puml_parser/src/class_diagram/test/integration_test.rs b/plantuml/parser/puml_parser/src/class_diagram/test/integration_test.rs index 4ce8d01a..b4b1f718 100644 --- a/plantuml/parser/puml_parser/src/class_diagram/test/integration_test.rs +++ b/plantuml/parser/puml_parser/src/class_diagram/test/integration_test.rs @@ -105,6 +105,16 @@ fn test_method_modifier_placement() { run_class_diagram_parser_case("method_modifier_placement"); } +#[test] +fn test_mock_method_variants_ignored() { + run_class_diagram_parser_case("mock_method_variants_ignored"); +} + +#[test] +fn test_mock_method_interleaved_members_survive() { + run_class_diagram_parser_case("mock_method_interleaved_members_survive"); +} + #[test] fn test_multiline_note() { run_class_diagram_parser_case("multiline_note"); diff --git a/plantuml/parser/puml_parser/src/grammar/class.pest b/plantuml/parser/puml_parser/src/grammar/class.pest index d536e55f..80931a2f 100644 --- a/plantuml/parser/puml_parser/src/grammar/class.pest +++ b/plantuml/parser/puml_parser/src/grammar/class.pest @@ -161,16 +161,157 @@ type_kind = { | "interface" } -class_body = { "{" ~ EOL* ~ (class_member | class_body_separator)* ~ EOL* ~ "}" } +class_body = { "{" ~ EOL* ~ (ignored_mock_method_stmt | class_member | class_body_separator)* ~ EOL* ~ "}" } class_member = { (method | attribute) ~ EOL+ } class_body_separator = { "--" ~ EOL+ } +ignored_mock_method_stmt = _{ + method_modifier* + ~ class_visibility? + ~ method_modifier* + ~ mock_method_invocation + ~ ";"? + ~ EOL+ +} + +mock_method_invocation = { + mock_method_modern_invocation + | mock_method_legacy_invocation +} + +mock_method_modern_invocation = { + mock_method_modern_macro + ~ "(" + ~ EOL* + ~ mock_method_return_type + ~ EOL* + ~ "," + ~ EOL* + ~ mock_method_name + ~ EOL* + ~ "," + ~ EOL* + ~ mock_method_param_group + ~ (EOL* ~ "," ~ EOL* ~ mock_method_modifiers)? + ~ EOL* + ~ ")" +} + +mock_method_legacy_invocation = { + mock_method_legacy_with_calltype_invocation + | mock_method_legacy_without_calltype_invocation +} + +mock_method_legacy_with_calltype_invocation = { + mock_method_legacy_macro_with_calltype + ~ "(" + ~ EOL* + ~ mock_method_calltype_value + ~ EOL* + ~ "," + ~ EOL* + ~ mock_method_name + ~ EOL* + ~ "," + ~ EOL* + ~ mock_method_legacy_signature + ~ EOL* + ~ ")" +} + +mock_method_legacy_without_calltype_invocation = { + mock_method_legacy_macro_without_calltype + ~ "(" + ~ EOL* + ~ mock_method_name + ~ EOL* + ~ "," + ~ EOL* + ~ mock_method_legacy_signature + ~ EOL* + ~ ")" +} + +mock_method_modern_macro = @{ "MOCK_METHOD" } +mock_method_legacy_macro_with_calltype = @{ ("MOCK_METHOD" | "MOCK_CONST_METHOD") ~ ASCII_DIGIT+ ~ ("_WITH_CALLTYPE" | "_T_WITH_CALLTYPE") } +mock_method_legacy_macro_without_calltype = @{ ("MOCK_METHOD" | "MOCK_CONST_METHOD") ~ ASCII_DIGIT+ ~ "_T"? } + +mock_method_calltype_value = @{ (!("," | ")" | EOL) ~ ANY)+ } + +mock_method_legacy_signature = { + mock_method_legacy_return_type + ~ mock_method_param_group +} + +mock_method_legacy_return_type = { + mock_method_parenthesized_return_type + | mock_method_legacy_simple_return_type +} + +mock_method_legacy_simple_return_type = { + const_prefix* + ~ prefix_array_prefix* + ~ qualified_type + ~ generic_type_args? + ~ type_suffix* +} + +mock_method_name = { method_name } + +mock_method_return_type = { + mock_method_parenthesized_return_type + | type_name +} + +mock_method_parenthesized_return_type = { + "(" + ~ EOL* + ~ type_name + ~ EOL* + ~ ")" +} + +mock_method_param_group = { + "(" + ~ EOL* + ~ param_list? + ~ EOL* + ~ ")" +} + +mock_method_modifiers = { + "(" + ~ EOL* + ~ mock_method_modifier_list? + ~ EOL* + ~ ")" +} + +mock_method_modifier_list = { + mock_method_modifier + ~ (EOL* ~ mock_method_modifier_separator ~ EOL* ~ mock_method_modifier)* +} + +mock_method_modifier_separator = _{ + "," +} + +mock_method_modifier = { + method_qualifier + | mock_method_ref_qualifier + | mock_method_calltype_qualifier +} + +mock_method_ref_qualifier = @{ "ref(" ~ ("&" | "&&") ~ ")" } +mock_method_calltype_qualifier = @{ "Calltype(" ~ class_qualified_name ~ ")" } + method = { method_modifier* ~ class_visibility? ~ friend_specifier? ~ method_modifier* + ~ !mock_method_invocation ~ method_name ~ generic_param_list? ~ "(" @@ -192,10 +333,16 @@ static_keyword = { "static" } abstract_modifier = { "{" ~ "abstract" ~ "}" | ("<<" ~ "abstract" ~ ">>") } virtual_modifier = { "{" ~ "virtual" ~ "}" | ("<<" ~ "virtual" ~ ">>") } override_modifier = { "{" ~ "override" ~ "}" | ("<<" ~ "override" ~ ">>") } -method_qualifier = _{ const_method_qualifier | noexcept_method_qualifier | trailing_override_qualifier } +method_qualifier = _{ + const_method_qualifier + | noexcept_method_qualifier + | trailing_override_qualifier + | final_method_qualifier +} const_method_qualifier = { ("<<" ~ "const" ~ ">>") | "const" } noexcept_method_qualifier = { "noexcept" } trailing_override_qualifier = { "override" } +final_method_qualifier = { ("<<" ~ "final" ~ ">>") | "final" } pure_virtual_suffix = { "=" ~ "0" } method_name = { diff --git a/plantuml/parser/puml_parser/tests/class_diagram/mock_method_interleaved_members_survive/mock_method_interleaved_members_survive.puml b/plantuml/parser/puml_parser/tests/class_diagram/mock_method_interleaved_members_survive/mock_method_interleaved_members_survive.puml new file mode 100644 index 00000000..311294d2 --- /dev/null +++ b/plantuml/parser/puml_parser/tests/class_diagram/mock_method_interleaved_members_survive/mock_method_interleaved_members_survive.puml @@ -0,0 +1,21 @@ +' ******************************************************************************* +' 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 mock_method_interleaved_members_survive +class Mixed { + + RealOne(): int + + MOCK_METHOD(void, Write, (int value), (override, final)); + -- + # RealTwo() noexcept final + + MOCK_METHOD1(Foo, bool(int value)); +} +@enduml diff --git a/plantuml/parser/puml_parser/tests/class_diagram/mock_method_interleaved_members_survive/output.json b/plantuml/parser/puml_parser/tests/class_diagram/mock_method_interleaved_members_survive/output.json new file mode 100644 index 00000000..3ef0b7ab --- /dev/null +++ b/plantuml/parser/puml_parser/tests/class_diagram/mock_method_interleaved_members_survive/output.json @@ -0,0 +1,59 @@ +{ + "mock_method_interleaved_members_survive.puml": { + "name": "mock_method_interleaved_members_survive", + "elements": [ + { + "Types": { + "ClassDef": { + "name": { + "internal": "Mixed", + "display": null + }, + "namespace": "", + "package": "", + "source_location": { + "file": "", + "line": 14 + }, + "is_abstract": false, + "type_aliases": [], + "attributes": [], + "methods": [ + { + "visibility": "Public", + "name": "RealOne", + "params": [], + "type": "int", + "template_parameters": null, + "modifiers": [], + "source_location": { + "file": "", + "line": 15 + } + }, + { + "visibility": "Protected", + "name": "RealTwo", + "params": [], + "type": null, + "template_parameters": null, + "modifiers": [ + "noexcept", + "final" + ], + "source_location": { + "file": "", + "line": 18 + } + } + ], + "template_parameters": null, + "extends": [], + "implements": [] + } + } + } + ], + "relationships": [] + } +} diff --git a/plantuml/parser/puml_parser/tests/class_diagram/mock_method_variants_ignored/mock_method_variants_ignored.puml b/plantuml/parser/puml_parser/tests/class_diagram/mock_method_variants_ignored/mock_method_variants_ignored.puml new file mode 100644 index 00000000..8dff80c8 --- /dev/null +++ b/plantuml/parser/puml_parser/tests/class_diagram/mock_method_variants_ignored/mock_method_variants_ignored.puml @@ -0,0 +1,41 @@ +' ******************************************************************************* +' 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 mock_method_variants_ignored +class MockOutput { + + MOCK_METHOD1(Foo, bool(int value)); + + MOCK_METHOD0(Foo, void()); + + MOCK_CONST_METHOD2_T(Bar, void(int, float)); + + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, Foo, bool(int value)); + + + MOCK_METHOD(void, Write, (int value)); + + MOCK_METHOD(void, Write, (int, float), (override)); + + MOCK_METHOD(void, Write, (int value), (override, final)); + + MOCK_METHOD(std::vector, Get, (), (const)); + + MOCK_METHOD((std::pair), Get, (), (const, override)); + + MOCK_METHOD(void, Flush, (), (Calltype(STDMETHODCALLTYPE), ref(&), noexcept, final)); + + + MOCK_METHOD(void, Write, + (int value), (override)); + + MOCK_METHOD(( + std::pair + ), GetWrapped, (), (const, override)); + + MOCK_METHOD( + std::vector>, + SplitEverywhere, + (int first, + float second), + (const, + noexcept, + final)); +} +@enduml diff --git a/plantuml/parser/puml_parser/tests/class_diagram/mock_method_variants_ignored/output.json b/plantuml/parser/puml_parser/tests/class_diagram/mock_method_variants_ignored/output.json new file mode 100644 index 00000000..9991631a --- /dev/null +++ b/plantuml/parser/puml_parser/tests/class_diagram/mock_method_variants_ignored/output.json @@ -0,0 +1,31 @@ +{ + "mock_method_variants_ignored.puml": { + "name": "mock_method_variants_ignored", + "elements": [ + { + "Types": { + "ClassDef": { + "name": { + "internal": "MockOutput", + "display": null + }, + "namespace": "", + "package": "", + "source_location": { + "file": "", + "line": 14 + }, + "is_abstract": false, + "type_aliases": [], + "attributes": [], + "methods": [], + "template_parameters": null, + "extends": [], + "implements": [] + } + } + } + ], + "relationships": [] + } +} diff --git a/plantuml/parser/puml_resolver/src/class_diagram/src/class_resolver.rs b/plantuml/parser/puml_resolver/src/class_diagram/src/class_resolver.rs index 96996814..268d1c42 100644 --- a/plantuml/parser/puml_resolver/src/class_diagram/src/class_resolver.rs +++ b/plantuml/parser/puml_resolver/src/class_diagram/src/class_resolver.rs @@ -512,6 +512,7 @@ impl ClassResolver { let is_abstract = has_modifier(&m.modifiers, "abstract"); let is_override = has_modifier(&m.modifiers, "override"); + let is_final = has_modifier(&m.modifiers, "final"); // A method that is abstract or overriding a base method is implicitly virtual in C++. let is_virtual = has_modifier(&m.modifiers, "virtual") || is_abstract || is_override; @@ -535,6 +536,7 @@ impl ClassResolver { ), (is_constructor, MethodModifier::Constructor), (is_destructor, MethodModifier::Destructor), + (is_final, MethodModifier::Final), ]), source_location: m.source_location.clone(), } diff --git a/tools/metamodel/class/class_logic.rs b/tools/metamodel/class/class_logic.rs index 1a804d7e..aed9d3bb 100644 --- a/tools/metamodel/class/class_logic.rs +++ b/tools/metamodel/class/class_logic.rs @@ -177,6 +177,7 @@ pub enum MethodModifier { Constructor, Destructor, Noexcept, + Final, } impl MethodModifier { diff --git a/tools/serialization/flatbuffers/class/class_diagram.fbs b/tools/serialization/flatbuffers/class/class_diagram.fbs index 297527b6..4c9a4334 100644 --- a/tools/serialization/flatbuffers/class/class_diagram.fbs +++ b/tools/serialization/flatbuffers/class/class_diagram.fbs @@ -48,7 +48,8 @@ enum MethodModifier : byte { Override = 3, Constructor = 4, Destructor = 5, - Noexcept = 6 + Noexcept = 6, + Final = 7 } enum TemplateParameterKind : byte { diff --git a/tools/serialization/flatbuffers/class/class_serializer.rs b/tools/serialization/flatbuffers/class/class_serializer.rs index 853163ef..138b86ac 100644 --- a/tools/serialization/flatbuffers/class/class_serializer.rs +++ b/tools/serialization/flatbuffers/class/class_serializer.rs @@ -387,6 +387,7 @@ impl ClassSerializer { MethodModifier::Constructor => fb::MethodModifier::Constructor, MethodModifier::Destructor => fb::MethodModifier::Destructor, MethodModifier::Noexcept => fb::MethodModifier::Noexcept, + MethodModifier::Final => fb::MethodModifier::Final, } } diff --git a/validation/core/integration_test/class_design_implementation/BUILD b/validation/core/integration_test/class_design_implementation/BUILD index a6ea925c..fedf85a4 100644 --- a/validation/core/integration_test/class_design_implementation/BUILD +++ b/validation/core/integration_test/class_design_implementation/BUILD @@ -52,6 +52,7 @@ filegroup( "//validation/core/integration_test/class_design_implementation/negative_variable_visibility_mismatch:case_data", "//validation/core/integration_test/class_design_implementation/positive_class_features:case_data", "//validation/core/integration_test/class_design_implementation/positive_method_features:case_data", + "//validation/core/integration_test/class_design_implementation/positive_method_final_features:case_data", "//validation/core/integration_test/class_design_implementation/positive_method_inheritance_features:case_data", "//validation/core/integration_test/class_design_implementation/positive_method_template_pack_features:case_data", "//validation/core/integration_test/class_design_implementation/positive_relationship_features:case_data", diff --git a/validation/core/integration_test/class_design_implementation/class_design_implementation_suite.rs b/validation/core/integration_test/class_design_implementation/class_design_implementation_suite.rs index 91c018fc..18cbf7fb 100644 --- a/validation/core/integration_test/class_design_implementation/class_design_implementation_suite.rs +++ b/validation/core/integration_test/class_design_implementation/class_design_implementation_suite.rs @@ -67,6 +67,11 @@ fn positive_method_features() { assert_case("positive_method_features"); } +#[test] +fn positive_method_final_features() { + assert_case("positive_method_final_features"); +} + // #[test] // fn positive_method_template_pack_features() { // assert_case("positive_method_template_pack_features"); diff --git a/validation/core/integration_test/class_design_implementation/positive_method_final_features/BUILD b/validation/core/integration_test/class_design_implementation/positive_method_final_features/BUILD new file mode 100644 index 00000000..c3206a03 --- /dev/null +++ b/validation/core/integration_test/class_design_implementation/positive_method_final_features/BUILD @@ -0,0 +1,54 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* + +load("//bazel/rules/rules_score:rules_score.bzl", "unit", "unit_design") +load("//validation/core/integration_test:puml_fixture.bzl", "provider_fbs_fixture_bundle") + +unit_design( + name = "unit_design", + static = ["class_diagram.puml"], + visibility = ["//visibility:private"], +) + +cc_library( + name = "transport", + srcs = ["transport.cpp"], +) + +unit( + name = "unit", + testonly = False, + maturity = "development", + tests = [], + unit_design = [":unit_design"], + visibility = ["//visibility:private"], + implementation = [":transport"], +) + +provider_fbs_fixture_bundle( + name = "fbs", + visibility = ["//visibility:private"], + deps = [ + ":unit", + ":unit_design", + ], +) + +filegroup( + name = "case_data", + srcs = [ + "expected.yaml", + ":fbs", + ], + visibility = ["//validation/core/integration_test:__subpackages__"], +) diff --git a/validation/core/integration_test/class_design_implementation/positive_method_final_features/class_diagram.puml b/validation/core/integration_test/class_design_implementation/positive_method_final_features/class_diagram.puml new file mode 100644 index 00000000..c444683a --- /dev/null +++ b/validation/core/integration_test/class_design_implementation/positive_method_final_features/class_diagram.puml @@ -0,0 +1,20 @@ +' ******************************************************************************* +' 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 + +class FinalMethod { + + {virtual} run() final : void +} + +@enduml diff --git a/validation/core/integration_test/class_design_implementation/positive_method_final_features/expected.yaml b/validation/core/integration_test/class_design_implementation/positive_method_final_features/expected.yaml new file mode 100644 index 00000000..898ecba3 --- /dev/null +++ b/validation/core/integration_test/class_design_implementation/positive_method_final_features/expected.yaml @@ -0,0 +1,13 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: true diff --git a/validation/core/integration_test/class_design_implementation/positive_method_final_features/transport.cpp b/validation/core/integration_test/class_design_implementation/positive_method_final_features/transport.cpp new file mode 100644 index 00000000..f6ad739b --- /dev/null +++ b/validation/core/integration_test/class_design_implementation/positive_method_final_features/transport.cpp @@ -0,0 +1,17 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ + +class FinalMethod { +public: + virtual void run() final; +}; diff --git a/validation/core/src/readers/class_diagram_reader.rs b/validation/core/src/readers/class_diagram_reader.rs index 171e23fe..0eca0f00 100644 --- a/validation/core/src/readers/class_diagram_reader.rs +++ b/validation/core/src/readers/class_diagram_reader.rs @@ -379,6 +379,7 @@ fn map_method_modifier( fb_class::MethodModifier::Constructor => Ok(MethodModifier::Constructor), fb_class::MethodModifier::Destructor => Ok(MethodModifier::Destructor), fb_class::MethodModifier::Noexcept => Ok(MethodModifier::Noexcept), + fb_class::MethodModifier::Final => Ok(MethodModifier::Final), _ => Err(unsupported_enum(context, "method_modifier", value)), } }