diff --git a/emv2/org.osate.aadl2.errormodel.tests/models/issue2858/.gitignore b/emv2/org.osate.aadl2.errormodel.tests/models/issue2858/.gitignore
new file mode 100644
index 0000000000..afce51184c
--- /dev/null
+++ b/emv2/org.osate.aadl2.errormodel.tests/models/issue2858/.gitignore
@@ -0,0 +1,2 @@
+/.aadlbin-gen/
+/instances/
diff --git a/emv2/org.osate.aadl2.errormodel.tests/models/issue2858/.project b/emv2/org.osate.aadl2.errormodel.tests/models/issue2858/.project
new file mode 100644
index 0000000000..60a485cf5f
--- /dev/null
+++ b/emv2/org.osate.aadl2.errormodel.tests/models/issue2858/.project
@@ -0,0 +1,18 @@
+
+
+ issue2858
+
+
+
+
+
+ org.eclipse.xtext.ui.shared.xtextBuilder
+
+
+
+
+
+ org.osate.core.aadlnature
+ org.eclipse.xtext.ui.shared.xtextNature
+
+
diff --git a/emv2/org.osate.aadl2.errormodel.tests/models/issue2858/CompoundCondition.aadl b/emv2/org.osate.aadl2.errormodel.tests/models/issue2858/CompoundCondition.aadl
new file mode 100644
index 0000000000..697da7581b
--- /dev/null
+++ b/emv2/org.osate.aadl2.errormodel.tests/models/issue2858/CompoundCondition.aadl
@@ -0,0 +1,33 @@
+package CompoundCondition
+public
+ system leaf
+ annex EMV2 {**
+ use behavior CompoundCondition::machine;
+ **};
+ end leaf;
+
+ system parent
+ end parent;
+
+ system implementation parent.i
+ subcomponents
+ first: system leaf;
+ second: system leaf;
+ annex EMV2 {**
+ use types ErrorLibrary;
+ use behavior CompoundCondition::machine;
+ composite error behavior
+ states
+ composite1: [first.state1 and second.state1]-> state1 {CommonErrors};
+ end composite;
+ **};
+ end parent.i;
+
+ annex EMV2 {**
+ error behavior machine
+ use types ErrorLibrary;
+ states
+ state1: initial state {CommonErrors};
+ end behavior;
+ **};
+end CompoundCondition;
diff --git a/emv2/org.osate.aadl2.errormodel.tests/models/issue2858/Issue2858.aadl b/emv2/org.osate.aadl2.errormodel.tests/models/issue2858/Issue2858.aadl
new file mode 100644
index 0000000000..1836b73dc6
--- /dev/null
+++ b/emv2/org.osate.aadl2.errormodel.tests/models/issue2858/Issue2858.aadl
@@ -0,0 +1,31 @@
+package Issue2858
+public
+ system s
+ features
+ f1: in feature;
+ f2: in feature;
+ annex EMV2 {**
+ use types ErrorLibrary;
+ use behavior Issue2858::machine;
+ error propagations
+ f1: in propagation {CommonErrors};
+ f2: in propagation {CommonErrors};
+ end propagations;
+ composite error behavior
+ states
+ composite1: [in f1]-> state1 {ServiceError, ItemTimingError};
+ composite2: [in f2]-> state2 {CommonErrors};
+ fallback: [others]-> state1 {CommonErrors};
+ end composite;
+ **};
+ end s;
+
+ annex EMV2 {**
+ error behavior machine
+ use types ErrorLibrary;
+ states
+ state1: initial state {CommonErrors};
+ state2: state {CommonErrors};
+ end behavior;
+ **};
+end Issue2858;
diff --git a/emv2/org.osate.aadl2.errormodel.tests/models/issue2858/ValidCompositeTargets.aadl b/emv2/org.osate.aadl2.errormodel.tests/models/issue2858/ValidCompositeTargets.aadl
new file mode 100644
index 0000000000..b4a0c820de
--- /dev/null
+++ b/emv2/org.osate.aadl2.errormodel.tests/models/issue2858/ValidCompositeTargets.aadl
@@ -0,0 +1,48 @@
+package ValidCompositeTargets
+public
+ system leaf
+ annex EMV2 {**
+ use behavior ValidCompositeTargets::machine;
+ **};
+ end leaf;
+
+ system parent
+ features
+ f1: in feature;
+ end parent;
+
+ system implementation parent.i
+ subcomponents
+ first: system leaf;
+ second: system leaf;
+ annex EMV2 {**
+ use types ValidCompositeTargets;
+ use behavior ValidCompositeTargets::machine;
+ error propagations
+ f1: in propagation {FirstType, SecondType};
+ end propagations;
+ composite error behavior
+ states
+ single: [in f1]-> singleState {FirstType};
+ product: [in f1]-> productState {FirstType * SecondType};
+ compound: [first.start and second.start]-> productState {FirstType * SecondType};
+ implicit: [first.start]-> start;
+ fallback: [others]-> start;
+ end composite;
+ **};
+ end parent.i;
+
+ annex EMV2 {**
+ error types
+ FirstType: type;
+ SecondType: type;
+ end types;
+ error behavior machine
+ use types ValidCompositeTargets;
+ states
+ start: initial state;
+ singleState: state {FirstType};
+ productState: state {FirstType * SecondType};
+ end behavior;
+ **};
+end ValidCompositeTargets;
diff --git a/emv2/org.osate.aadl2.errormodel.tests/src/org/osate/aadl2/errormodel/tests/issues/Issue2858Test.java b/emv2/org.osate.aadl2.errormodel.tests/src/org/osate/aadl2/errormodel/tests/issues/Issue2858Test.java
new file mode 100644
index 0000000000..9f75e9a1c4
--- /dev/null
+++ b/emv2/org.osate.aadl2.errormodel.tests/src/org/osate/aadl2/errormodel/tests/issues/Issue2858Test.java
@@ -0,0 +1,90 @@
+/**
+ * Copyright (c) 2004-2026 Carnegie Mellon University and others. (see Contributors file).
+ * All Rights Reserved.
+ *
+ * NO WARRANTY. ALL MATERIAL IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY
+ * KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE
+ * OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT
+ * MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Created, in part, with funding and support from the United States Government. (see Acknowledgments file).
+ *
+ * This program includes and/or can make use of certain third party source code, object code, documentation and other
+ * files ("Third Party Software"). The Third Party Software that is used by this program is dependent upon your system
+ * configuration. By using this program, You agree to comply with any and all relevant Third Party Software terms and
+ * conditions contained in any such Third Party Software or separate license file distributed with such Third Party
+ * Software. The parties who own the Third Party Software ("Third Party Licensors") are intended third party benefici-
+ * aries to this license with respect to the terms applicable to their Third Party Software. Third Party Software li-
+ * censes only apply to the Third Party Software and not any other portion of this program or this program as a whole.
+ */
+package org.osate.aadl2.errormodel.tests.issues;
+
+import java.util.ArrayList;
+
+import org.eclipse.xtext.testing.InjectWith;
+import org.eclipse.xtext.testing.XtextRunner;
+import org.eclipse.xtext.testing.validation.ValidationTestHelper;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.osate.aadl2.AadlPackage;
+import org.osate.aadl2.Classifier;
+import org.osate.aadl2.DefaultAnnexSubclause;
+import org.osate.aadl2.errormodel.tests.ErrorModelInjectorProvider;
+import org.osate.testsupport.AssertHelper;
+import org.osate.testsupport.TestHelper;
+import org.osate.xtext.aadl2.errormodel.errorModel.ErrorModelSubclause;
+
+import com.google.inject.Inject;
+import com.itemis.xtext.testing.FluentIssueCollection;
+import com.itemis.xtext.testing.XtextTest;
+
+@RunWith(XtextRunner.class)
+@InjectWith(ErrorModelInjectorProvider.class)
+public class Issue2858Test extends XtextTest {
+ private static final String PROJECT_LOCATION = "org.osate.aadl2.errormodel.tests/models/issue2858/";
+
+ @Inject
+ private TestHelper testHelper;
+
+ @Inject
+ private ValidationTestHelper validationHelper;
+
+ @Test
+ public void rejectMultipleTypesAndTypeSetsForPropagationAndOthersConditions() throws Exception {
+ var result = issues = testHelper.testFile(PROJECT_LOCATION + "Issue2858.aadl");
+ var expected = new FluentIssueCollection(result.getResource(), new ArrayList<>(), new ArrayList<>());
+ var pkg = (AadlPackage) result.getResource().getContents().getFirst();
+ for (var composite : annex(pkg.getPublicSection().getOwnedClassifiers().getFirst()).getStates()) {
+ AssertHelper.assertError(composite, result.getIssues(), expected,
+ "Target error type may only have a single error type");
+ }
+ expected.sizeIs(3);
+ assertConstraints(expected);
+ }
+
+ @Test
+ public void reportOneErrorForACompoundSubcomponentCondition() throws Exception {
+ var result = issues = testHelper.testFile(PROJECT_LOCATION + "CompoundCondition.aadl");
+ var expected = new FluentIssueCollection(result.getResource(), new ArrayList<>(), new ArrayList<>());
+ var pkg = (AadlPackage) result.getResource().getContents().getFirst();
+ var composite = annex(pkg.getPublicSection().getOwnedClassifiers().get(2)).getStates().getFirst();
+ AssertHelper.assertError(composite, result.getIssues(), expected,
+ "Target error type may only have a single error type");
+ expected.sizeIs(1);
+ assertConstraints(expected);
+ }
+
+ @Test
+ public void acceptSingleTypesProductsAndOmittedTokens() {
+ validationHelper.assertNoIssues(testHelper.parseFile(PROJECT_LOCATION + "ValidCompositeTargets.aadl"));
+ }
+
+ private ErrorModelSubclause annex(Classifier classifier) {
+ return (ErrorModelSubclause) ((DefaultAnnexSubclause) classifier.getOwnedAnnexSubclauses().getFirst())
+ .getParsedAnnexSubclause();
+ }
+}
diff --git a/emv2/org.osate.xtext.aadl2.errormodel/src/org/osate/xtext/aadl2/errormodel/validation/ErrorModelValidator.java b/emv2/org.osate.xtext.aadl2.errormodel/src/org/osate/xtext/aadl2/errormodel/validation/ErrorModelValidator.java
index 462249cf4f..79c5a0ec50 100644
--- a/emv2/org.osate.xtext.aadl2.errormodel/src/org/osate/xtext/aadl2/errormodel/validation/ErrorModelValidator.java
+++ b/emv2/org.osate.xtext.aadl2.errormodel/src/org/osate/xtext/aadl2/errormodel/validation/ErrorModelValidator.java
@@ -304,6 +304,22 @@ public void caseSConditionElement(SConditionElement conditionElement) {
checkSConditionElementType(conditionElement);
}
+ /**
+ * @since 8.0
+ */
+ @Check(CheckType.FAST)
+ public void caseCompositeState(CompositeState composite) {
+ var target = composite.getTypedToken();
+ if (target == null || target.getTypeTokens().isEmpty()) {
+ return;
+ }
+ var tokens = target.getTypeTokens();
+ var types = tokens.getFirst().getType();
+ if (tokens.size() > 1 || (!types.isEmpty() && !(types.getFirst() instanceof ErrorType))) {
+ error(composite, "Target error type may only have a single error type");
+ }
+ }
+
@Check(CheckType.FAST)
public void caseErrorModelSubclause(ErrorModelSubclause subclause) {
if (EcoreUtil2.getContainerOfType(subclause, FeatureGroupType.class) != null) {
@@ -469,25 +485,6 @@ private void checkSConditionElementType(SConditionElement conditionElement) {
return;
}
- CompositeState compState;
- EObject eo = conditionElement;
- while (eo.eContainer() != null) {
- eo = eo.eContainer();
- if (eo instanceof CompositeState) {
- break;
- }
- }
- if (eo instanceof CompositeState) {
- compState = (CompositeState) eo;
- EList targetTKs = compState.getTypedToken().getTypeTokens();
- // marks error if target state has a typeset or multiple errors associated with it
- if ((targetTKs != null && targetTKs.size() > 1) || (targetTKs.get(0).getType() != null
- && !(targetTKs.get(0).getType().get(0) instanceof ErrorType))) {
- error(compState, "Target error type may only have a single error type");
- }
- }
-
-
TypeSet triggerTS = null;
String triggerName = "";
triggerTS = es.getTypeSet();