diff --git a/validation/core/integration_test/class_design_implementation/BUILD b/validation/core/integration_test/class_design_implementation/BUILD index 5a0c014e..701db643 100644 --- a/validation/core/integration_test/class_design_implementation/BUILD +++ b/validation/core/integration_test/class_design_implementation/BUILD @@ -20,6 +20,7 @@ filegroup( "//validation/core/integration_test/class_design_implementation/negative_class_missing:case_data", "//validation/core/integration_test/class_design_implementation/negative_class_missing_with_suggestion:case_data", "//validation/core/integration_test/class_design_implementation/negative_class_missing_without_suggestion:case_data", + "//validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_implementation_input:case_data", "//validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_input:case_data", "//validation/core/integration_test/class_design_implementation/negative_entity_type_mismatch:case_data", "//validation/core/integration_test/class_design_implementation/negative_enum_literal_mismatch:case_data", @@ -61,6 +62,7 @@ filegroup( "//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", + "//validation/core/integration_test/class_design_implementation/positive_shared_implementation_dependency:case_data", "//validation/core/integration_test/class_design_implementation/positive_unresolved_type_not_int:case_data", "//validation/core/integration_test/class_design_implementation/positive_variable_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 93ec7873..3162cee7 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_free_function_features() { assert_case("positive_free_function_features"); } +#[test] +fn positive_shared_implementation_dependency() { + assert_case("positive_shared_implementation_dependency"); +} + #[test] fn positive_method_features() { assert_case("positive_method_features"); @@ -152,6 +157,11 @@ fn negative_duplicate_class_entity_input() { assert_case("negative_duplicate_class_entity_input"); } +#[test] +fn negative_duplicate_class_entity_implementation_input() { + assert_case("negative_duplicate_class_entity_implementation_input"); +} + #[test] fn negative_entity_type_mismatch() { assert_case("negative_entity_type_mismatch"); diff --git a/validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_implementation_input/BUILD b/validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_implementation_input/BUILD new file mode 100644 index 00000000..b7c33f0e --- /dev/null +++ b/validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_implementation_input/BUILD @@ -0,0 +1,62 @@ +# ******************************************************************************* +# 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 = "implementation_one", + srcs = ["implementation_one.cpp"], +) + +cc_library( + name = "implementation_two", + srcs = ["implementation_two.cpp"], +) + +unit( + name = "unit", + testonly = False, + maturity = "development", + tests = [], + unit_design = [":unit_design"], + visibility = ["//visibility:private"], + implementation = [ + ":implementation_one", + ":implementation_two", + ], +) + +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/negative_duplicate_class_entity_implementation_input/class_diagram.puml b/validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_implementation_input/class_diagram.puml new file mode 100644 index 00000000..c5199ca6 --- /dev/null +++ b/validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_implementation_input/class_diagram.puml @@ -0,0 +1,18 @@ +' ******************************************************************************* +' 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 Engine + +@enduml diff --git a/validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_implementation_input/expected.yaml b/validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_implementation_input/expected.yaml new file mode 100644 index 00000000..2c7478d1 --- /dev/null +++ b/validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_implementation_input/expected.yaml @@ -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 +# ******************************************************************************* +should_pass: false +error_contains: | + [Class] Class "Engine" is defined more than once in the class implementation. + Class : "Engine" + Implementation source file : "validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_implementation_input/implementation_one.cpp" + Implementation source line : 14 + Duplicate source file : "validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_implementation_input/implementation_two.cpp" + Duplicate source line : 14 + Fix : Remove or rename one of the duplicate class "Engine" declarations in the class implementation. diff --git a/validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_implementation_input/implementation_one.cpp b/validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_implementation_input/implementation_one.cpp new file mode 100644 index 00000000..c9b72fd9 --- /dev/null +++ b/validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_implementation_input/implementation_one.cpp @@ -0,0 +1,16 @@ +/******************************************************************************** + * 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 Engine +{ +}; diff --git a/validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_implementation_input/implementation_two.cpp b/validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_implementation_input/implementation_two.cpp new file mode 100644 index 00000000..58994d98 --- /dev/null +++ b/validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_implementation_input/implementation_two.cpp @@ -0,0 +1,16 @@ +/******************************************************************************** + * 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 engine +{ +}; diff --git a/validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_input/expected.yaml b/validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_input/expected.yaml index de0b2fda..fad77385 100644 --- a/validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_input/expected.yaml +++ b/validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_input/expected.yaml @@ -12,10 +12,10 @@ # ******************************************************************************* should_pass: false error_contains: | - [Class] Class "Engine" is defined more than once in the class diagram. + [Class] Class "Engine" is defined more than once in the class design. Class : "Engine" Design source file : "validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_input/class_diagram_part1.puml" Design source line : 18 Duplicate source file : "validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_input/class_diagram_part2.puml" Duplicate source line : 18 - Fix : Remove or rename one of the duplicate class "Engine" declarations in the class diagram. + Fix : Remove or rename one of the duplicate class "Engine" declarations in the class design. diff --git a/validation/core/integration_test/class_design_implementation/positive_shared_implementation_dependency/BUILD b/validation/core/integration_test/class_design_implementation/positive_shared_implementation_dependency/BUILD new file mode 100644 index 00000000..f062b6e1 --- /dev/null +++ b/validation/core/integration_test/class_design_implementation/positive_shared_implementation_dependency/BUILD @@ -0,0 +1,69 @@ +# ******************************************************************************* +# 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 = "shared_types", + hdrs = ["shared.h"], +) + +cc_library( + name = "implementation_one", + srcs = ["implementation_one.cpp"], + deps = [":shared_types"], +) + +cc_library( + name = "implementation_two", + srcs = ["implementation_two.cpp"], + deps = [":shared_types"], +) + +unit( + name = "unit", + testonly = False, + maturity = "development", + tests = [], + unit_design = [":unit_design"], + visibility = ["//visibility:private"], + implementation = [ + ":implementation_one", + ":implementation_two", + ], +) + +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_shared_implementation_dependency/class_diagram.puml b/validation/core/integration_test/class_design_implementation/positive_shared_implementation_dependency/class_diagram.puml new file mode 100644 index 00000000..4b090481 --- /dev/null +++ b/validation/core/integration_test/class_design_implementation/positive_shared_implementation_dependency/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 + +package shared { + class Shared +} + +@enduml diff --git a/validation/core/integration_test/class_design_implementation/positive_shared_implementation_dependency/expected.yaml b/validation/core/integration_test/class_design_implementation/positive_shared_implementation_dependency/expected.yaml new file mode 100644 index 00000000..898ecba3 --- /dev/null +++ b/validation/core/integration_test/class_design_implementation/positive_shared_implementation_dependency/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_shared_implementation_dependency/implementation_one.cpp b/validation/core/integration_test/class_design_implementation/positive_shared_implementation_dependency/implementation_one.cpp new file mode 100644 index 00000000..cfa76b70 --- /dev/null +++ b/validation/core/integration_test/class_design_implementation/positive_shared_implementation_dependency/implementation_one.cpp @@ -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 + ********************************************************************************/ + +#include "validation/core/integration_test/class_design_implementation/positive_shared_implementation_dependency/shared.h" + +shared::Shared MakeSharedOne() +{ + return {}; +} diff --git a/validation/core/integration_test/class_design_implementation/positive_shared_implementation_dependency/implementation_two.cpp b/validation/core/integration_test/class_design_implementation/positive_shared_implementation_dependency/implementation_two.cpp new file mode 100644 index 00000000..4e4c3986 --- /dev/null +++ b/validation/core/integration_test/class_design_implementation/positive_shared_implementation_dependency/implementation_two.cpp @@ -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 + ********************************************************************************/ + +#include "validation/core/integration_test/class_design_implementation/positive_shared_implementation_dependency/shared.h" + +shared::Shared MakeSharedTwo() +{ + return {}; +} diff --git a/validation/core/integration_test/class_design_implementation/positive_shared_implementation_dependency/shared.h b/validation/core/integration_test/class_design_implementation/positive_shared_implementation_dependency/shared.h new file mode 100644 index 00000000..a0ff6d97 --- /dev/null +++ b/validation/core/integration_test/class_design_implementation/positive_shared_implementation_dependency/shared.h @@ -0,0 +1,24 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ + +#ifndef VALIDATION_SHARED_IMPLEMENTATION_DEPENDENCY_SHARED_H_ +#define VALIDATION_SHARED_IMPLEMENTATION_DEPENDENCY_SHARED_H_ + +namespace shared +{ +class Shared +{ +}; +} + +#endif diff --git a/validation/core/src/models/class_diagram_models.rs b/validation/core/src/models/class_diagram_models.rs index 41ba25ce..fc61bceb 100644 --- a/validation/core/src/models/class_diagram_models.rs +++ b/validation/core/src/models/class_diagram_models.rs @@ -25,6 +25,12 @@ use crate::{ErrorBuilder, ErrorCategory, ValidationResult}; /// Collection of class diagrams loaded from one or more FlatBuffer files. pub type ClassDiagramInputs = Vec; +#[derive(Clone, Copy)] +enum ClassInputKind { + Design, + Implementation, +} + /// Class-like entities from one or more class diagrams, keyed by lower-case id. pub struct ClassEntityIndex { entities: BTreeMap, @@ -32,10 +38,33 @@ pub struct ClassEntityIndex { } impl ClassEntityIndex { - /// Build an index from class diagrams for class implementation validation. - pub fn build_index(diagrams: &[ClassDiagramInput], result: &mut ValidationResult) -> Self { + /// Builds an index from class design diagrams. + pub fn build_diagram_index( + diagrams: &[ClassDiagramInput], + result: &mut ValidationResult, + ) -> Self { + Self::build_index(diagrams, result, ClassInputKind::Design) + } + + /// Builds an index from C++ implementation diagrams. + pub fn build_implementation_index( + diagrams: &[ClassDiagramInput], + result: &mut ValidationResult, + ) -> Self { + Self::build_index(diagrams, result, ClassInputKind::Implementation) + } + + fn build_index( + diagrams: &[ClassDiagramInput], + result: &mut ValidationResult, + input_kind: ClassInputKind, + ) -> Self { let mut entities: BTreeMap = BTreeMap::new(); let mut free_functions = Vec::new(); + let (source_kind, ignore_same_source_location) = match input_kind { + ClassInputKind::Design => ("design", false), + ClassInputKind::Implementation => ("implementation", true), + }; for diagram in diagrams { for entity in &diagram.entities { @@ -43,22 +72,29 @@ impl ClassEntityIndex { let key = indexed_entity.id.to_lowercase(); if let Some(prev) = entities.get(&key) { + if ignore_same_source_location + && prev.source_location == indexed_entity.source_location + { + continue; + } + let (first_source_file, _) = prev.source_location.display(); let (second_source_file, _) = indexed_entity.source_location.display(); result.add_failure( ErrorBuilder::new(ErrorCategory::Class) .title(format!( - "class \"{}\" is defined more than once in the class diagram.", + "class \"{}\" is defined more than once in the class {}.", prev.id, + source_kind, )) .field("class", format!("\"{}\"", prev.id)) .field( - "design source file", + format!("{source_kind} source file"), format!("\"{}\"", first_source_file), ) .field( - "design source line", + format!("{source_kind} source line"), prev.source_location.line.to_string(), ) .field( @@ -70,8 +106,9 @@ impl ClassEntityIndex { indexed_entity.source_location.line.to_string(), ) .fix(format!( - "remove or rename one of the duplicate class \"{}\" declarations in the class diagram", + "remove or rename one of the duplicate class \"{}\" declarations in the class {}", prev.id, + source_kind, )) .build(), ); @@ -224,14 +261,32 @@ mod tests { }]; let mut result = ValidationResult::default(); - let _index = ClassEntityIndex::build_index(&diagrams, &mut result); + let _index = ClassEntityIndex::build_diagram_index(&diagrams, &mut result); assert_eq!(result.failures.len(), 1); assert!(result.failures[0].contains( - "[Class] Class \"Unit.Sample\" is defined more than once in the class diagram." + "[Class] Class \"Unit.Sample\" is defined more than once in the class design." )); } + #[test] + fn implementation_class_entity_index_ignores_same_source_location() { + let diagrams = vec![ClassDiagram { + name: "implementation".to_string(), + entities: vec![ + entity("Unit.Sample", "implementation.h", 12), + entity("unit.sample", "implementation.h", 12), + ], + free_functions: Vec::new(), + }]; + + let mut result = ValidationResult::default(); + let index = ClassEntityIndex::build_implementation_index(&diagrams, &mut result); + + assert!(result.failures.is_empty()); + assert_eq!(index.entities().count(), 1); + } + #[test] fn internal_api_index_collects_interfaces_and_methods() { let diagrams = vec![ClassDiagram { diff --git a/validation/core/src/profiles/unit.rs b/validation/core/src/profiles/unit.rs index 49e53ad1..de6e9530 100644 --- a/validation/core/src/profiles/unit.rs +++ b/validation/core/src/profiles/unit.rs @@ -61,12 +61,12 @@ pub fn run(inputs: &UnitInputs) -> Result { let design_classes = read_and_convert::( inputs.design_classes.as_slice(), &mut result, - |raw: ClassDiagramInputs, errs| ClassEntityIndex::build_index(&raw, errs), + |raw: ClassDiagramInputs, errs| ClassEntityIndex::build_diagram_index(&raw, errs), )?; let implementation_classes = read_and_convert::( inputs.implementation_classes.as_slice(), &mut result, - |raw: ClassDiagramInputs, errs| ClassEntityIndex::build_index(&raw, errs), + |raw: ClassDiagramInputs, errs| ClassEntityIndex::build_implementation_index(&raw, errs), )?; let sequence_diagrams = read_and_convert::( inputs.sequence_diagrams.as_slice(), diff --git a/validation/core/src/validators/class_design_implementation_validator.rs b/validation/core/src/validators/class_design_implementation_validator.rs index fe8aabb0..58b71a18 100644 --- a/validation/core/src/validators/class_design_implementation_validator.rs +++ b/validation/core/src/validators/class_design_implementation_validator.rs @@ -1376,7 +1376,7 @@ mod tests { entities, free_functions: Vec::new(), }]; - ClassEntityIndex::build_index(&diagrams, &mut ValidationResult::default()) + ClassEntityIndex::build_diagram_index(&diagrams, &mut ValidationResult::default()) } fn index_with_free_functions(free_functions: Vec) -> ClassEntityIndex { @@ -1385,7 +1385,7 @@ mod tests { entities: Vec::new(), free_functions, }]; - ClassEntityIndex::build_index(&diagrams, &mut ValidationResult::default()) + ClassEntityIndex::build_diagram_index(&diagrams, &mut ValidationResult::default()) } fn free_function(name: &str, parameter_types: &[&str]) -> FreeFunctionDecl { diff --git a/validation/core/src/validators/test/class_design_sequence_validator_test.rs b/validation/core/src/validators/test/class_design_sequence_validator_test.rs index b4f12b2d..d3595562 100644 --- a/validation/core/src/validators/test/class_design_sequence_validator_test.rs +++ b/validation/core/src/validators/test/class_design_sequence_validator_test.rs @@ -22,7 +22,7 @@ fn validate( sequence_diagrams: SequenceDiagramInputs, ) -> ValidationResult { let mut setup_result = ValidationResult::default(); - let design_index = ClassEntityIndex::build_index(&design_classes, &mut setup_result); + let design_index = ClassEntityIndex::build_diagram_index(&design_classes, &mut setup_result); let sequence_index = sequence_diagrams.to_sequence_diagram_index(&mut setup_result); assert!( setup_result.is_empty(),