From ef67d91075df7da50d385d4c8fdbb19cb8d8fc84 Mon Sep 17 00:00:00 2001 From: Simon Cockx Date: Thu, 28 Nov 2024 10:02:17 +0100 Subject: [PATCH 01/10] WIP --- rosetta-ide/rosetta.tmLanguage.yaml | 2 +- rosetta-lang/model/Rosetta.xcore | 2 +- .../java/com/regnosys/rosetta/Rosetta.xtext | 6 +- .../rosetta/RosettaRuntimeModule.xtend | 2 - .../java/expression/ExpressionGenerator.xtend | 4 +- .../java/types/JavaTypeTranslator.java | 10 - .../interpreter/RosettaValueFactory.java | 11 - .../rosetta/types/CardinalityProvider.xtend | 4 + .../regnosys/rosetta/types/RErrorType.java | 71 -- .../com/regnosys/rosetta/types/RListType.java | 1 + .../rosetta/types/RMetaAnnotatedType.java | 4 +- .../rosetta/types/RObjectFactory.java | 2 +- .../regnosys/rosetta/types/Rosetta.xsemantics | 303 +------- .../rosetta/types/RosettaAuxiliary.xsemantics | 16 - .../rosetta/types/RosettaOperators.xtend | 112 --- .../types/RosettaTypeChecking.xsemantics | 554 ------------- .../rosetta/types/RosettaTypeProvider.xtend | 208 +++-- .../rosetta/types/SubtypeRelation.java | 4 +- .../regnosys/rosetta/types/TypeFactory.java | 94 +-- .../regnosys/rosetta/types/TypeSystem.java | 23 +- .../rosetta/types/TypeValidationUtil.java | 228 ------ .../types/builtin/RBuiltinTypeService.java | 20 +- .../rosetta/utils/RosettaTypeSwitch.java | 10 +- .../validation/ExpressionValidator.java | 636 +++++++++++++++ .../validation/RosettaSimpleValidator.xtend | 373 +-------- .../rosetta/validation/RosettaValidator.xtend | 2 +- .../StandaloneRosettaTypingValidator.java | 42 +- .../java/condition/RosettaConditionTest.xtend | 16 +- .../RosettaBinaryOperationTest.xtend | 6 +- .../RosettaExistsExpressionTest.xtend | 30 +- .../function/FunctionGeneratorHelper.xtend | 6 +- .../java/function/FunctionGeneratorTest.xtend | 63 +- .../java/rule/RosettaRuleGeneratorTest.xtend | 5 +- .../tests/RosettaExpressionsTest.xtend | 2 +- .../rosetta/tests/RosettaParsingTest.xtend | 9 +- .../types/RosettaTypeProviderTest.xtend | 630 +++++++++++++++ .../rosetta/types/RosettaTypingTest.xtend | 728 ------------------ .../regnosys/rosetta/types/TypeTestUtil.xtend | 46 -- .../validation/RosettaValidatorTest.xtend | 36 +- 39 files changed, 1570 insertions(+), 2751 deletions(-) delete mode 100644 rosetta-lang/src/main/java/com/regnosys/rosetta/types/RErrorType.java delete mode 100644 rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaOperators.xtend delete mode 100644 rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaTypeChecking.xsemantics delete mode 100644 rosetta-lang/src/main/java/com/regnosys/rosetta/types/TypeValidationUtil.java create mode 100644 rosetta-lang/src/main/java/com/regnosys/rosetta/validation/ExpressionValidator.java delete mode 100644 rosetta-testing/src/test/java/com/regnosys/rosetta/types/RosettaTypingTest.xtend delete mode 100644 rosetta-testing/src/test/java/com/regnosys/rosetta/types/TypeTestUtil.xtend diff --git a/rosetta-ide/rosetta.tmLanguage.yaml b/rosetta-ide/rosetta.tmLanguage.yaml index 81e65de19..9f4b02253 100644 --- a/rosetta-ide/rosetta.tmLanguage.yaml +++ b/rosetta-ide/rosetta.tmLanguage.yaml @@ -36,7 +36,7 @@ variables: unambiguousRootEnd: (?={{unambiguousRootStart}}) sectionStart: '{{wordStart}}((post-)?condition|set|add|inputs|output|alias){{wordEnd}}' sectionEnd: (?={{sectionStart}}|{{rootStart}})(?!\bcondition\b)|(?=\bcondition\b\s*({{identifier}})?:) - functionalOperation: '{{wordStart}}(reduce|filter|map|extract|sort|min|max){{wordEnd}}' + functionalOperation: '{{wordStart}}(reduce|filter|extract|sort|min|max){{wordEnd}}' listOperationWord: '{{functionalOperation}}|{{wordStart}}(single|multiple|exists|is|absent|only-element|count|flatten|distinct|reverse|first|last|sum){{wordEnd}}' listOperation: ->>|->|{{listOperationWord}} synonymAnnotationSimpleSection: '{{wordStart}}(value|meta|definition|pattern|removeHtml|dateFormat|mapper|hint|merge|condition-func|condition-path){{wordEnd}}' diff --git a/rosetta-lang/model/Rosetta.xcore b/rosetta-lang/model/Rosetta.xcore index 90d00c185..c8daf6643 100644 --- a/rosetta-lang/model/Rosetta.xcore +++ b/rosetta-lang/model/Rosetta.xcore @@ -114,7 +114,7 @@ class RosettaExternalFunction extends RosettaRootElement, RosettaTyped, RosettaC } } -class RosettaParameter extends RosettaTyped, RosettaNamed { +class RosettaParameter extends RosettaTyped, RosettaSymbol { boolean isArray } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext b/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext index f5a54d05c..05482f106 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext @@ -682,7 +682,7 @@ UnaryOperation returns RosettaExpression: ({ReduceOperation.argument=current} operator='reduce') |({FilterOperation.argument=current} operator='filter') // @Compat: remove `map` - |({MapOperation.argument=current} operator=('map' | 'extract')) + |({MapOperation.argument=current} operator='extract') ) (function=InlineFunction|=>function=ImplicitInlineFunction)? )* | // Without left parameter: @@ -719,7 +719,7 @@ UnaryOperation returns RosettaExpression: ({ReduceOperation} operator='reduce') |({FilterOperation} operator='filter') // @Compat: remove `map` - |({MapOperation} operator=('map' | 'extract')) + |({MapOperation} operator='extract') ) (function=InlineFunction|=>function=ImplicitInlineFunction)? ) ( @@ -757,7 +757,7 @@ UnaryOperation returns RosettaExpression: ({ReduceOperation.argument=current} operator='reduce') |({FilterOperation.argument=current} operator='filter') // @Compat: remove `map` - |({MapOperation.argument=current} operator=('map' | 'extract')) + |({MapOperation.argument=current} operator='extract') ) (function=InlineFunction|=>function=ImplicitInlineFunction)? )* ; diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/RosettaRuntimeModule.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/RosettaRuntimeModule.xtend index a6de2036a..040bd70e6 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/RosettaRuntimeModule.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/RosettaRuntimeModule.xtend @@ -40,7 +40,6 @@ import org.eclipse.xtext.parsetree.reconstr.ITransientValueService import com.regnosys.rosetta.resource.RosettaResource import com.regnosys.rosetta.typing.RosettaTyping import com.regnosys.rosetta.typing.RosettaTypingAuxiliary -import com.regnosys.rosetta.typing.RosettaTypingChecking import org.eclipse.xtext.validation.IResourceValidator import com.regnosys.rosetta.validation.CachingResourceValidator import com.regnosys.rosetta.config.RosettaConfiguration @@ -60,7 +59,6 @@ class RosettaRuntimeModule extends AbstractRosettaRuntimeModule { // since they will only be instantiated once. binder.bind(RosettaTyping).asEagerSingleton binder.bind(RosettaTypingAuxiliary).asEagerSingleton - binder.bind(RosettaTypingChecking).asEagerSingleton } override Class bindIFragmentProvider() { diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/expression/ExpressionGenerator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/expression/ExpressionGenerator.xtend index 8580199e9..770da3669 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/expression/ExpressionGenerator.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/expression/ExpressionGenerator.xtend @@ -98,7 +98,6 @@ import com.regnosys.rosetta.types.RFunction import com.regnosys.rosetta.types.RMetaAnnotatedType import com.regnosys.rosetta.types.RObjectFactory import com.regnosys.rosetta.types.RShortcut -import com.regnosys.rosetta.types.RosettaOperators import com.regnosys.rosetta.types.RosettaTypeProvider import com.regnosys.rosetta.types.TypeSystem import com.regnosys.rosetta.types.builtin.RBasicType @@ -149,7 +148,6 @@ class ExpressionGenerator extends RosettaExpressionSwitch caseTimeType(RBasicType type, Void context) { return typeUtil.LOCAL_TIME; } @Override - protected JavaType caseMissingType(RBasicType type, Void context) { - throw new IllegalArgumentException("Cannot convert a missing type to a Java type."); - } - @Override protected JavaClass caseNothingType(RBasicType type, Void context) { return typeUtil.VOID; } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/interpreter/RosettaValueFactory.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/interpreter/RosettaValueFactory.java index 3dff8ed78..02013008a 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/interpreter/RosettaValueFactory.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/interpreter/RosettaValueFactory.java @@ -29,7 +29,6 @@ import com.regnosys.rosetta.types.RChoiceType; import com.regnosys.rosetta.types.RDataType; import com.regnosys.rosetta.types.REnumType; -import com.regnosys.rosetta.types.RErrorType; import com.regnosys.rosetta.types.RType; import com.regnosys.rosetta.types.builtin.RBasicType; import com.regnosys.rosetta.types.builtin.RBuiltinTypeService; @@ -61,11 +60,6 @@ public RosettaValue createOfType(RType type, Object item) { return createOfType(type, List.of(item)); } - @Override - protected RosettaValue caseErrorType(RErrorType type, List context) { - throw new UnsupportedOperationException("Cannot create a value of error type " + type); - } - @Override protected RosettaValue caseDataType(RDataType type, List context) { throw new UnsupportedOperationException("Data type is unsupported"); @@ -111,11 +105,6 @@ protected RosettaValue caseTimeType(RBasicType type, List context) { // return new RosettaPatternValue(castList(context, Pattern.class)); // } - @Override - protected RosettaValue caseMissingType(RBasicType type, List context) { - throw new UnsupportedOperationException("Cannot create a value of a missing type"); - } - @Override protected RosettaValue caseNothingType(RBasicType type, List context) { if (!context.isEmpty()) { diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/CardinalityProvider.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/CardinalityProvider.xtend index b801396d0..18a75f701 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/CardinalityProvider.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/CardinalityProvider.xtend @@ -72,6 +72,7 @@ import com.regnosys.rosetta.rosetta.expression.RosettaDeepFeatureCall import com.regnosys.rosetta.rosetta.expression.DefaultOperation import com.regnosys.rosetta.rosetta.expression.SwitchOperation import com.regnosys.rosetta.rosetta.expression.SwitchCase +import com.regnosys.rosetta.rosetta.RosettaParameter class CardinalityProvider extends RosettaExpressionSwitch { static Logger LOGGER = LoggerFactory.getLogger(CardinalityProvider) @@ -100,6 +101,9 @@ class CardinalityProvider extends RosettaExpressionSwitch { RosettaFeature: { isFeatureMulti(symbol as RosettaFeature, breakOnClosureParameter) } + RosettaParameter: { + false + } ClosureParameter: { isClosureParameterMulti(symbol.function) } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RErrorType.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RErrorType.java deleted file mode 100644 index 2d7f0483b..000000000 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RErrorType.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2024 REGnosys - * - * 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. - */ - -package com.regnosys.rosetta.types; - -import java.util.Objects; - -import com.rosetta.model.lib.ModelSymbolId; -import com.rosetta.util.DottedPath; - -// TODO: remove this type -public class RErrorType extends RType { - private final String message; - - public RErrorType(final String message) { - super(); - this.message = message; - } - - @Override - public ModelSymbolId getSymbolId() { - return null; - } - - @Override - public String getName() { - return this.message; - } - - @Override - public DottedPath getNamespace() { - return null; - } - - @Override - public DottedPath getQualifiedName() { - return DottedPath.of(message); - } - - - public String getMessage() { - return this.message; - } - - @Override - public int hashCode() { - return 31 * 1 + ((this.message == null) ? 0 : this.message.hashCode()); - } - - @Override - public boolean equals(final Object object) { - if (object == null) return false; - if (this.getClass() != object.getClass()) return false; - - RErrorType other = (RErrorType) object; - return Objects.equals(message, other.message); - } -} diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RListType.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RListType.java index b69dbee75..249d15882 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RListType.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RListType.java @@ -20,6 +20,7 @@ import com.regnosys.rosetta.rosetta.RosettaCardinality; +// TODO: remove public class RListType { private final RType itemType; private final RosettaCardinality constraint; diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RMetaAnnotatedType.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RMetaAnnotatedType.java index 5899d01c3..8cd30bead 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RMetaAnnotatedType.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RMetaAnnotatedType.java @@ -10,11 +10,11 @@ public class RMetaAnnotatedType { private final RType rType; private final List metaAttributes; - public RMetaAnnotatedType(RType rType, List metaAttributes) { + private RMetaAnnotatedType(RType rType, List metaAttributes) { this.rType = rType; this.metaAttributes = Validate.noNullElements(metaAttributes); } - + // TODO: Rename to withNoMeta public static RMetaAnnotatedType withEmptyMeta(RType rType) { return new RMetaAnnotatedType(rType, List.of()); } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RObjectFactory.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RObjectFactory.java index 657d4fe2f..c27f40c92 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RObjectFactory.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RObjectFactory.java @@ -75,7 +75,7 @@ public RFunction buildRFunction(Function function) { // TODO: should be private public RAttribute createArtificialAttribute(String name, RType type, boolean isMulti) { - RMetaAnnotatedType rAnnotatedType = new RMetaAnnotatedType(type, List.of()); + RMetaAnnotatedType rAnnotatedType = RMetaAnnotatedType.withEmptyMeta(type); return new RAttribute(name, null, Collections.emptyList(), rAnnotatedType, isMulti ? PositiveIntegerInterval.boundedLeft(0) : PositiveIntegerInterval.bounded(0, 1), null, null); } public RFunction buildRFunction(RosettaRule rule) { diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/Rosetta.xsemantics b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/Rosetta.xsemantics index 1cf0e47c4..f218ee187 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/Rosetta.xsemantics +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/Rosetta.xsemantics @@ -1,57 +1,19 @@ system com.regnosys.rosetta.typing.RosettaTyping extends RosettaTypingAuxiliary -import com.regnosys.rosetta.rosetta.RosettaEnumValue -import com.regnosys.rosetta.rosetta.RosettaEnumeration -import com.regnosys.rosetta.rosetta.RosettaExternalFunction -import com.regnosys.rosetta.rosetta.RosettaTypedFeature -import com.regnosys.rosetta.rosetta.expression.ArithmeticOperation -import com.regnosys.rosetta.rosetta.expression.ClosureParameter -import com.regnosys.rosetta.rosetta.expression.ComparisonOperation -import com.regnosys.rosetta.rosetta.expression.EqualityOperation -import com.regnosys.rosetta.rosetta.expression.ListLiteral -import com.regnosys.rosetta.rosetta.expression.LogicalOperation -import com.regnosys.rosetta.rosetta.expression.RosettaAbsentExpression -import com.regnosys.rosetta.rosetta.expression.RosettaNumberLiteral -import com.regnosys.rosetta.rosetta.expression.RosettaBooleanLiteral -import com.regnosys.rosetta.rosetta.expression.RosettaConditionalExpression -import com.regnosys.rosetta.rosetta.expression.RosettaCountOperation -import com.regnosys.rosetta.rosetta.expression.RosettaExistsExpression -import com.regnosys.rosetta.rosetta.expression.RosettaExpression -import com.regnosys.rosetta.rosetta.expression.RosettaFeatureCall -import com.regnosys.rosetta.rosetta.expression.RosettaFunctionalOperation -import com.regnosys.rosetta.rosetta.expression.RosettaIntLiteral -import com.regnosys.rosetta.rosetta.expression.RosettaOnlyElement -import com.regnosys.rosetta.rosetta.expression.RosettaOnlyExistsExpression -import com.regnosys.rosetta.rosetta.expression.RosettaStringLiteral import com.regnosys.rosetta.rosetta.simple.Attribute import com.regnosys.rosetta.rosetta.simple.Data -import com.regnosys.rosetta.rosetta.simple.Function -import com.regnosys.rosetta.rosetta.simple.ShortcutDeclaration import com.regnosys.rosetta.types.RListType import com.regnosys.rosetta.types.RType import com.regnosys.rosetta.types.TypeFactory -import com.regnosys.rosetta.types.TypeValidationUtil import com.regnosys.rosetta.utils.ExpressionHelper -import com.regnosys.rosetta.rosetta.RosettaSymbol -import com.regnosys.rosetta.rosetta.expression.RosettaSymbolReference -import com.regnosys.rosetta.rosetta.expression.RosettaImplicitVariable import com.regnosys.rosetta.utils.ImplicitVariableUtil -import com.regnosys.rosetta.rosetta.expression.AsKeyOperation -import com.regnosys.rosetta.rosetta.expression.OneOfOperation -import com.regnosys.rosetta.rosetta.expression.ChoiceOperation import java.util.Optional -import org.eclipse.emf.ecore.EObject -import com.regnosys.rosetta.rosetta.expression.ThenOperation import com.regnosys.rosetta.types.builtin.RBuiltinTypeService -import com.regnosys.rosetta.types.builtin.RNumberType -import com.regnosys.rosetta.types.builtin.RStringType -import com.regnosys.rosetta.utils.OptionalUtil import com.regnosys.rosetta.interpreter.RosettaInterpreter import com.regnosys.rosetta.interpreter.RosettaInterpreterContext import com.regnosys.rosetta.types.RAliasType import com.regnosys.rosetta.utils.RosettaSimpleSystemSolver -import com.regnosys.rosetta.rosetta.expression.RosettaPatternLiteral import com.regnosys.rosetta.rosetta.RosettaTypeAlias import com.regnosys.rosetta.types.RTypeFunction import com.regnosys.rosetta.rosetta.expression.ExpressionFactory @@ -62,16 +24,13 @@ import com.regnosys.rosetta.rosetta.RosettaMetaType import com.regnosys.rosetta.rosetta.RosettaBuiltinType import com.regnosys.rosetta.interpreter.RosettaValue import com.rosetta.util.DottedPath -import com.regnosys.rosetta.rosetta.RosettaRule -import java.math.BigInteger import com.regnosys.rosetta.utils.ModelIdProvider import com.regnosys.rosetta.types.TypeSystem import com.regnosys.rosetta.types.RObjectFactory import com.regnosys.rosetta.rosetta.simple.Choice -import com.regnosys.rosetta.rosetta.expression.SwitchCase +import com.regnosys.rosetta.rosetta.RosettaEnumeration inject extension TypeFactory typeFactory -inject extension TypeValidationUtil util inject extension ExpressionHelper exprHelper inject extension ImplicitVariableUtil implicitVarUtil inject extension RBuiltinTypeService builtinTypes @@ -86,10 +45,6 @@ auxiliary { comparable(RType t1, RType t2) listComparable(RListType t1, RListType t2) - symbolListType(RosettaSymbol c): RListType - functionalOperationItemType(RosettaFunctionalOperation op): RListType - typeOfImplicitVariable(EObject context): Optional - // The methods below could be moved to RosettaAuxiliary were it not for issue https://github.com/eclipse/xsemantics/issues/178 typeFunctionOfTypeAlias(RosettaTypeAlias typeAlias): RTypeFunction cached typeCallToRType(TypeCall rt, RosettaInterpreterContext context): RType @@ -97,9 +52,6 @@ auxiliary { } judgments { - inferType |- RosettaExpression expression : output RListType cached - error "Cannot type " + stringRep(expression) + "." - source expression subtype |- RType subtype <: RType supertype error subtype + " is not a subtype of " + supertype listSubtype |- RListType subtype <| RListType supertype @@ -119,66 +71,6 @@ auxiliary listComparable(RListType t1, RListType t2) { overlap(t1.constraint, t2.constraint) } -auxiliary symbolListType(Attribute c) { - return attributeListType(c) -} - -auxiliary symbolListType(ClosureParameter c) { - val f = c.function - val op = f.eContainer as RosettaFunctionalOperation - return functionalOperationItemType(op) -} - -auxiliary symbolListType(RosettaEnumeration c) { - return createListType(c.buildREnumType, single) -} - -auxiliary symbolListType(ShortcutDeclaration c) { - empty |- c.expression : var RListType t - return t -} - -auxiliary symbolListType(RosettaExternalFunction c) { - return createListType(typeCallToRType(c.typeCall, new RosettaInterpreterContext), 1, 1) -} - -auxiliary symbolListType(Function c) { - return createListType(typeCallToRType(c.^output.typeCall, new RosettaInterpreterContext), c.^output.card) -} - -auxiliary symbolListType(RosettaRule c) { - empty |- c.expression : var RListType t - return t -} - -auxiliary functionalOperationItemType(RosettaFunctionalOperation op) { - var RListType receiverType - empty |- op.argument : receiverType or receiverType = null - if (receiverType !== null) { - if (op instanceof ThenOperation) { - return receiverType - } else { - return createListType(receiverType.itemType, single) - } - } - return null -} - -auxiliary typeOfImplicitVariable(EObject c) { - val definingContainer = c.findContainerDefiningImplicitVariable - definingContainer.map [ - if (it instanceof Data) { - createListType(it.buildRDataType, single) - } else if (it instanceof RosettaFunctionalOperation) { - functionalOperationItemType - } else if (it instanceof RosettaRule) { - input?.typeCallToRType(new RosettaInterpreterContext)?.createListType(single) - } else if (it instanceof SwitchCase) { - guard.choiceOptionGuard.symbolListType - } - ] -} - auxiliary typeFunctionOfTypeAlias(RosettaTypeAlias typeAlias) { if (typeAlias.name == INT_NAME) { return INT_FUNCTION; @@ -253,11 +145,14 @@ auxiliary typeCallToRType(TypeCall call, RosettaInterpreterContext context) { val refersTo = t.typeCall.typeCallToRType(RosettaInterpreterContext.of(args)) new RAliasType(t.typeFunctionOfTypeAlias, args, refersTo) } + default: { + NOTHING + } } } auxiliary attributeListType(Attribute a) { - return createListType(typeCallToRType(a.typeCall, new RosettaInterpreterContext), a.card) + return new RListType(typeCallToRType(a.typeCall, new RosettaInterpreterContext), a.card) } /*** SUBTYPING **/ @@ -274,191 +169,3 @@ from { G |- s.itemType <: t.itemType s.constraint.isSubconstraintOf(t.constraint) } - -/*** TYPING ***/ -axiom TBoolean - G |- RosettaBooleanLiteral bool : singleBoolean -axiom TString - G |- RosettaStringLiteral str : singleString(str.value.length, str.value.length) -axiom TNumber - G |- RosettaNumberLiteral num : singleNumber(num.value.toPlainString.replaceAll("\\.|\\-", "").length, Math.max(0, num.value.scale), num.value, num.value) -axiom TInt - G |- RosettaIntLiteral i : singleInt(if (i.value.signum >= 0) i.value.toString.length else i.value.toString.length - 1, i.value, i.value) -axiom TPattern - G |- RosettaPatternLiteral p : singlePattern - -rule TVar // TA-Var, TA-Func - G |- RosettaSymbolReference ref : RListType t -from { - t = ref.symbol.symbolListType - - val itType = ref.typeOfImplicitVariable - if (itType.isPresent) { - if (itType.get.itemType.allFeatures(ref.eResource?.resourceSet).contains(ref.symbol)) { - // Case implicit `item -> ...` - t = createListType(t.itemType, t.constraint * itType.get.constraint) - } - } -} -rule TIt - G |- RosettaImplicitVariable c : RListType t -from { - t = c.typeOfImplicitVariable.orElse(null) -} - -rule TArithmetic - G |- ArithmeticOperation op : RListType lt -from { - // TODO: keep type aliases - G |- op.left : var RListType t1 - G |- op.right : var RListType t2 - if (t1 !== null && t2 !== null) { - val item1 = t1.itemType - val item2 = t2.itemType - if (op.operator == '+') { - if ({G |- item1 <: DATE}) { - lt = singleDateTime - } else if ({G |- item1 <: UNCONSTRAINED_STRING} && {G |- item2 <: UNCONSTRAINED_STRING}) { - lt = createListType(keepTypeAliasIfPossible(item1, item2, [l, r| - val s1 = l as RStringType - val s2 = r as RStringType - val newInterval = s1.interval.add(s2.interval) - constrainedString(newInterval, Optional.^empty()) - ]), single) - } else if ({G |- item1 <: UNCONSTRAINED_NUMBER} && {G |- item2 <: UNCONSTRAINED_NUMBER}) { - lt = createListType(keepTypeAliasIfPossible(item1, item2, [l, r| - val n1 = l as RNumberType - val n2 = r as RNumberType - val newFractionalDigits = OptionalUtil.zipWith(n1.fractionalDigits, n2.fractionalDigits, [a,b|Math.max(a,b)]) - val newInterval = n1.interval.add(n2.interval) - constrainedNumber(Optional.^empty(), newFractionalDigits, newInterval, Optional.^empty()) - ]), single) - } - } else if (op.operator == '-') { - if ({G |- item1 <: DATE} || {G |- item2 <: DATE}) { - lt = singleUnconstrainedInt - } else if ({G |- item1 <: UNCONSTRAINED_NUMBER} && {G |- item2 <: UNCONSTRAINED_NUMBER}) { - lt = createListType(keepTypeAliasIfPossible(item1, item2, [l, r| - val n1 = l as RNumberType - val n2 = r as RNumberType - val newFractionalDigits = OptionalUtil.zipWith(n1.fractionalDigits, n2.fractionalDigits, [a,b|Math.max(a,b)]) - val newInterval = n1.interval.subtract(n2.interval) - constrainedNumber(Optional.^empty(), newFractionalDigits, newInterval, Optional.^empty()) - ]), single) - } - } else if (op.operator == '*') { - if ({G |- item1 <: UNCONSTRAINED_NUMBER} && {G |- item2 <: UNCONSTRAINED_NUMBER}) { - lt = createListType(keepTypeAliasIfPossible(item1, item2, [l, r| - val n1 = l as RNumberType - val n2 = r as RNumberType - val newFractionalDigits = OptionalUtil.zipWith(n1.fractionalDigits, n2.fractionalDigits, [a,b|a+b]) - val newInterval = n1.interval.multiply(n2.interval) - constrainedNumber(Optional.^empty(), newFractionalDigits, newInterval, Optional.^empty()) - ]), single) - } - } else if (op.operator == '/') { - if ({G |- item1 <: UNCONSTRAINED_NUMBER} && {G |- item2 <: UNCONSTRAINED_NUMBER}) { - lt = singleUnconstrainedNumber - } - } - } -} - -axiom TEquality - G |- EqualityOperation op : singleBoolean -axiom TLogical - G |- LogicalOperation op : singleBoolean -axiom TComparison - G |- ComparisonOperation op : singleBoolean - -rule TIf - G |- RosettaConditionalExpression e : RListType t -from { - var RListType tthen - var RListType telse - empty |- e.ifthen : tthen or tthen = null - empty |- e.elsethen : telse or telse = null - if (tthen !== null && telse !== null) { - t = listJoin(tthen, telse) - } -} - -rule TList - G |- ListLiteral e : RListType t -from { - val telems = newArrayList - if (e.elements.forall[ - var RListType telem - empty |- it : telem or telem = null - if (telem === null) { - false - } else { - telems.add(telem) - true - } - ]) { - t = telems.fold(emptyNothing, [ RListType acc, RListType telem | - if (acc === null) { - null - } else { - val sup = join(telem.itemType, acc.itemType); - if ({G |- ANY <: sup}) { - null - } else { - createListType(sup, telem.constraint + acc.constraint) - } - } - ]) - } -} - -rule TProject // TA-Project - G |- RosettaFeatureCall e : RListType t -from { // TODO: support metadata feature calls? - G |- e.receiver : var RListType dt - if (dt !== null) { - val a = e.^feature - switch (a) { - Attribute: { // regular projections - val at = a.attributeListType - t = createListType(at.itemType, dt.constraint * at.constraint) - } - RosettaTypedFeature: { // projections on record types or of metadata properties - if (a.typeCall === null) { - fail error "Unsupported by the type system." - } else { - t = createListType(a.typeCall.typeCallToRType(new RosettaInterpreterContext), dt.constraint) - } - } - RosettaEnumValue: { // projections on enums - t = dt - } - } - } -} - -axiom TExists // TA-Exists - G |- RosettaExistsExpression e : singleBoolean -axiom TAbsent - G |- RosettaAbsentExpression e : singleBoolean -axiom TOnlyExists // TA-OnlyExists - G |- RosettaOnlyExistsExpression e : singleBoolean -axiom TCount // TA-Count - G |- RosettaCountOperation e : singleInt(Optional.^empty(), Optional.of(BigInteger.ZERO), Optional.^empty()) -rule TOnlyElement // TA-OnlyElement - G |- RosettaOnlyElement e : RListType t -from { - G |- e.argument : var RListType targ - if (targ !== null) { - t = createListType(targ.itemType, 0, 1) - } -} -rule TAsKey - G |- AsKeyOperation e : RListType t -from { - G |- e.argument : t -} -axiom TOneOf - G |- OneOfOperation e : singleBoolean -axiom TChoice - G |- ChoiceOperation e : singleBoolean diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaAuxiliary.xsemantics b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaAuxiliary.xsemantics index d669a2721..2277eedf4 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaAuxiliary.xsemantics +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaAuxiliary.xsemantics @@ -9,10 +9,8 @@ import com.regnosys.rosetta.rosetta.simple.Data import com.regnosys.rosetta.rosetta.simple.SimplePackage import com.regnosys.rosetta.types.RDataType import com.regnosys.rosetta.types.REnumType -import com.regnosys.rosetta.types.RListType import com.regnosys.rosetta.types.RType import com.regnosys.rosetta.types.TypeFactory -import com.regnosys.rosetta.types.TypeValidationUtil import com.regnosys.rosetta.utils.ExpressionHelper import java.util.List @@ -33,7 +31,6 @@ import com.regnosys.rosetta.types.TypeSystem import com.regnosys.rosetta.types.RChoiceType inject extension TypeFactory typeFactory -inject extension TypeValidationUtil util inject extension ExpressionHelper exprHelper inject extension ImplicitVariableUtil implicitVarUtil inject extension RBuiltinTypeService builtinTypes @@ -48,8 +45,6 @@ auxiliary { ancestorEnums(RosettaEnumeration t) : List overlap(RosettaCardinality c1, RosettaCardinality c2) join(RType t1, RType t2) : RType - union(RosettaCardinality c1, RosettaCardinality c2) : RosettaCardinality - listJoin(RListType t1, RListType t2) : RListType allEnumValues(RosettaEnumeration d) : Iterable mayBeEmpty(RDataType d) @@ -80,17 +75,6 @@ auxiliary overlap(RosettaCardinality c1, RosettaCardinality c2) { auxiliary join(RType t1, RType t2) { return typeSystem.join(t1, t2) } -auxiliary union(RosettaCardinality c1, RosettaCardinality c2) { - if (c1.unbounded || c2.unbounded) { - return createConstraint(Math.min(c1.inf, c2.inf)) - } else { - return createConstraint(Math.min(c1.inf, c2.inf), Math.max(c1.sup, c2.sup)) - } -} -auxiliary listJoin(RListType t1, RListType t2) { - val sup = join(t1.itemType, t2.itemType); - return createListType(sup, union(t1.constraint, t2.constraint)) -} auxiliary allEnumValues(RosettaEnumeration e) { if (e.parent === null) { return e.enumValues; diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaOperators.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaOperators.xtend deleted file mode 100644 index 4eb6fe33c..000000000 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaOperators.xtend +++ /dev/null @@ -1,112 +0,0 @@ -package com.regnosys.rosetta.types - -import com.google.inject.Singleton -import com.regnosys.rosetta.types.builtin.RBuiltinTypeService -import com.regnosys.rosetta.types.builtin.RStringType -import java.util.Optional -import com.regnosys.rosetta.types.builtin.RNumberType -import com.regnosys.rosetta.utils.OptionalUtil -import javax.inject.Inject - -@Singleton -class RosettaOperators { - - public static val ARITHMETIC_OPS = #['+', '-', '*', '/'] - public static val COMPARISON_OPS = #['<', '<=', '>', '>='] - public static val EQUALITY_OPS = #['=', '<>', 'contains', 'disjoint'] - public static val LOGICAL_OPS = #['and', 'or'] - public static val JOIN_OP = 'join' - public static val DEFAULT_OP = 'default' - - @Inject extension RBuiltinTypeService service - @Inject extension TypeSystem - - def RType resultType(String op, RType left, RType right) { - if (left == NOTHING || right === NOTHING) { - return NOTHING - } - val resultType = if (op == '+') { - if (left.isSubtypeOf(DATE) && right.isSubtypeOf(TIME)) { - DATE_TIME - } else if (left.isSubtypeOf(UNCONSTRAINED_STRING) && right.isSubtypeOf(UNCONSTRAINED_STRING)) { - keepTypeAliasIfPossible(left, right, [l,r| - val s1 = l as RStringType - val s2 = r as RStringType - val newInterval = s1.interval.add(s2.interval) - new RStringType(newInterval, Optional.empty()) - ]) - } else if (left.isSubtypeOf(UNCONSTRAINED_NUMBER) && right.isSubtypeOf(UNCONSTRAINED_NUMBER)) { - keepTypeAliasIfPossible(left, right, [l,r| - val n1 = l as RNumberType - val n2 = r as RNumberType - val newFractionalDigits = OptionalUtil.zipWith(n1.fractionalDigits, n2.fractionalDigits, [a,b|Math.max(a,b)]) - val newInterval = n1.interval.add(n2.interval) - new RNumberType(Optional.empty(), newFractionalDigits, newInterval, Optional.empty()) - ]) - } - } else if (op == '-') { - if (left.isSubtypeOf(DATE) && right.isSubtypeOf(DATE)) { - UNCONSTRAINED_INT - } else if (left.isSubtypeOf(UNCONSTRAINED_NUMBER) && right.isSubtypeOf(UNCONSTRAINED_NUMBER)) { - keepTypeAliasIfPossible(left, right, [l,r| - val n1 = l as RNumberType - val n2 = r as RNumberType - val newFractionalDigits = OptionalUtil.zipWith(n1.fractionalDigits, n2.fractionalDigits, [a,b|Math.max(a,b)]) - val newInterval = n1.interval.subtract(n2.interval) - new RNumberType(Optional.empty(), newFractionalDigits, newInterval, Optional.empty()) - ]) - } - } else if (op == '*') { - if (left.isSubtypeOf(UNCONSTRAINED_NUMBER) && right.isSubtypeOf(UNCONSTRAINED_NUMBER)) { - keepTypeAliasIfPossible(left, right, [l,r| - val n1 = l as RNumberType - val n2 = r as RNumberType - val newFractionalDigits = OptionalUtil.zipWith(n1.fractionalDigits, n2.fractionalDigits, [a,b|a+b]) - val newInterval = n1.interval.multiply(n2.interval) - new RNumberType(Optional.empty(), newFractionalDigits, newInterval, Optional.empty()) - ]) - } - } else if (op == '/') { - if (left.isSubtypeOf(UNCONSTRAINED_NUMBER) && right.isSubtypeOf(UNCONSTRAINED_NUMBER)) { - UNCONSTRAINED_NUMBER - } - } else if (COMPARISON_OPS.contains(op) || EQUALITY_OPS.contains(op)) { - if (left === null || right === null || left.isComparable(right)) { - BOOLEAN - } - } else if (op == JOIN_OP) { - return bothString(left, right, op) - } else if (LOGICAL_OPS.contains(op)) { - return bothBoolean(left, right, op) - } else if (op == DEFAULT_OP) { - val result = left.join(right) - if (result != ANY) { - result - } - } - - if (resultType === null) { - return new RErrorType( - "Incompatible types: cannot use operator '" + op + "' with " + left.name + " and " + - right.name + ".") - } - else - return resultType - } - - def private bothBoolean(RType left, RType right, String op) { - if (!left.isSubtypeOf(BOOLEAN)) - return new RErrorType('''Left hand side of '«op»' expression must be boolean''') - if (!right.isSubtypeOf(BOOLEAN)) - return new RErrorType('''Right hand side of '«op»' expression must be boolean''') - return BOOLEAN - } - - def private bothString(RType left, RType right, String op) { - if (!left.isSubtypeOf(UNCONSTRAINED_STRING)) - return new RErrorType('''Left hand side of '«op»' expression must be string''') - if (!right.isSubtypeOf(UNCONSTRAINED_STRING)) - return new RErrorType('''Right hand side of '«op»' expression must be string''') - return UNCONSTRAINED_STRING - } -} diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaTypeChecking.xsemantics b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaTypeChecking.xsemantics deleted file mode 100644 index 02de96f9c..000000000 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaTypeChecking.xsemantics +++ /dev/null @@ -1,554 +0,0 @@ -system com.regnosys.rosetta.typing.RosettaTypingChecking extends RosettaTyping - -import com.regnosys.rosetta.rosetta.RosettaCardinality -import com.regnosys.rosetta.rosetta.RosettaExternalFunction -import com.regnosys.rosetta.rosetta.expression.ArithmeticOperation -import com.regnosys.rosetta.rosetta.expression.CardinalityModifier -import com.regnosys.rosetta.rosetta.expression.ComparisonOperation -import com.regnosys.rosetta.rosetta.expression.EqualityOperation -import com.regnosys.rosetta.rosetta.expression.ExpressionPackage -import com.regnosys.rosetta.rosetta.expression.ListLiteral -import com.regnosys.rosetta.rosetta.expression.LogicalOperation -import com.regnosys.rosetta.rosetta.expression.ModifiableBinaryOperation -import com.regnosys.rosetta.rosetta.expression.RosettaAbsentExpression -import com.regnosys.rosetta.rosetta.expression.RosettaBinaryOperation -import com.regnosys.rosetta.rosetta.expression.RosettaConditionalExpression -import com.regnosys.rosetta.rosetta.expression.RosettaExistsExpression -import com.regnosys.rosetta.rosetta.expression.RosettaExpression -import com.regnosys.rosetta.rosetta.expression.RosettaOnlyExistsExpression -import com.regnosys.rosetta.rosetta.simple.Function -import com.regnosys.rosetta.types.RDataType -import com.regnosys.rosetta.types.RListType -import com.regnosys.rosetta.types.RType -import com.regnosys.rosetta.types.TypeFactory -import com.regnosys.rosetta.types.TypeValidationUtil -import com.regnosys.rosetta.utils.ExpressionHelper - -import com.regnosys.rosetta.rosetta.expression.RosettaSymbolReference -import com.regnosys.rosetta.utils.ImplicitVariableUtil -import org.eclipse.xtext.EcoreUtil2 -import com.regnosys.rosetta.rosetta.expression.OneOfOperation -import com.regnosys.rosetta.rosetta.expression.ChoiceOperation -import static com.regnosys.rosetta.rosetta.expression.ExpressionPackage.Literals.* -import com.regnosys.rosetta.types.builtin.RBuiltinTypeService -import com.regnosys.rosetta.interpreter.RosettaInterpreter -import com.regnosys.rosetta.interpreter.RosettaInterpreterContext -import com.regnosys.rosetta.utils.RosettaSimpleSystemSolver -import com.regnosys.rosetta.rosetta.RosettaRule -import com.regnosys.rosetta.types.RChoiceType - -inject extension TypeFactory typeFactory -inject extension TypeValidationUtil util -inject extension ExpressionHelper exprHelper -inject extension ImplicitVariableUtil implicitVarUtil -inject extension RBuiltinTypeService builtinTypes -inject RosettaInterpreter interpreter -inject RosettaSimpleSystemSolver systemSolver - - -auxiliary { - listSubtypeCheck(RosettaExpression sourceObject, RListType expected) - looseListSubtypeCheck(RosettaExpression sourceObject, RListType expected) - subtypeCheck(RosettaExpression sourceObject, RType expected) - comparableListTypeCheck(RosettaBinaryOperation sourceObject) - comparableTypeCheck(RosettaBinaryOperation sourceObject) - onlyRightIsSingularCheck(ModifiableBinaryOperation sourceObject) - looseOnlyRightIsSingularCheck(ModifiableBinaryOperation sourceObject) - constraintCheck(RosettaExpression sourceObject, RosettaCardinality expected) - looseConstraintCheck(RosettaExpression sourceObject, RosettaCardinality expected) - notConstraintCheck(RosettaExpression sourceObject, RosettaCardinality notExpected) - isLooserConstraintCheck(RosettaExpression sourceObject, RosettaCardinality expected) -} - -/****** TYPE VALIDATION UTILITIES *******/ -auxiliary listSubtypeCheck(RosettaExpression sourceObject, RListType expected) { - var RListType actual - empty |- sourceObject : actual or actual = null - if (expected !== null && actual !== null && !{empty |- actual <| expected}) { - fail error notAListSubtypeMessage(expected, actual) - source sourceObject - } -} -auxiliary looseListSubtypeCheck(RosettaExpression sourceObject, RListType expected) { - var RListType actual - empty |- sourceObject : actual or actual = null - if (expected !== null && actual !== null && !({empty |- actual.itemType <: expected.itemType} && overlap(expected.constraint, actual.constraint))) { - fail error notAListSubtypeMessage(expected, actual) - source sourceObject - } -} -auxiliary subtypeCheck(RosettaExpression sourceObject, RType expected) { - var RListType actual - empty |- sourceObject : actual or actual = null - if (expected !== null && actual !== null && !{empty |- actual.itemType <: expected}) { - fail error notASubtypeMessage(expected, actual.itemType) - source sourceObject - } -} -auxiliary comparableListTypeCheck(RosettaBinaryOperation sourceObject) { - var RListType tl - var RListType tr - empty |- sourceObject.left : tl or tl = null - empty |- sourceObject.right : tr or tr = null - if (tl !== null && tr !== null) { - tl.listComparable(tr) - or - fail error notListComparableMessage(tl, tr) - source sourceObject - } -} -auxiliary comparableTypeCheck(RosettaBinaryOperation sourceObject) { - var RListType tl - var RListType tr - empty |- sourceObject.left : tl or tl = null - empty |- sourceObject.right : tr or tr = null - if (tl !== null && tr !== null) { - tl.itemType.comparable(tr.itemType) - or - fail error notComparableMessage(tl.itemType, tr.itemType) - source sourceObject - } -} -auxiliary onlyRightIsSingularCheck(ModifiableBinaryOperation sourceObject) { - var RListType tl - var RListType tr - empty |- sourceObject.left : tl or tl = null - empty |- sourceObject.right : tr or tr = null - if (tl !== null && tr !== null) { - !tl.constraint.constraintEquals(single) && tr.constraint.constraintEquals(single) - or - { - if (tl.constraint.constraintEquals(single)) { - if (tr.constraint.constraintEquals(single)) { - fail error bothAreSingularMessage(sourceObject) - source sourceObject - feature ExpressionPackage.Literals.MODIFIABLE_BINARY_OPERATION__CARD_MOD - } else { - fail error notRightIsSingularButLeftIsMessage(tr) - source sourceObject.right - } - } else { - fail error notConstraintMessage(single, tr) - source sourceObject.right - } - } - } -} -auxiliary looseOnlyRightIsSingularCheck(ModifiableBinaryOperation sourceObject) { - var RListType tl - var RListType tr - empty |- sourceObject.left : tl or tl = null - empty |- sourceObject.right : tr or tr = null - if (tl !== null && tr !== null) { - !tl.constraint.constraintEquals(single) && single.isSubconstraintOf(tr.constraint) - or - { - if (tl.constraint.constraintEquals(single)) { - if (tr.constraint.constraintEquals(single)) { - fail error bothAreSingularMessage(sourceObject) - source sourceObject - feature ExpressionPackage.Literals.MODIFIABLE_BINARY_OPERATION__CARD_MOD - } else { - fail error notRightIsSingularButLeftIsMessage(tr) - source sourceObject.right - } - } else { - fail error notConstraintMessage(single, tr) - source sourceObject.right - } - } - } -} -auxiliary constraintCheck(RosettaExpression sourceObject, RosettaCardinality expected) { - var RListType actual - empty |- sourceObject : actual or actual = null - if (expected !== null && actual !== null) { - expected.constraintEquals(actual.constraint) - or - fail error notConstraintMessage(expected, actual) - source sourceObject - } -} -auxiliary looseConstraintCheck(RosettaExpression sourceObject, RosettaCardinality expected) { - var RListType actual - empty |- sourceObject : actual or actual = null - if (expected !== null && actual !== null) { - expected.isSubconstraintOf(actual.constraint) - or - fail error notConstraintMessage(expected, actual) - source sourceObject - } -} -auxiliary notConstraintCheck(RosettaExpression sourceObject, RosettaCardinality notExpected) { - var RListType actual - empty |- sourceObject : actual or actual = null - if (notExpected !== null && actual !== null) { - !notExpected.constraintEquals(actual.constraint) - or - fail error wrongConstraintMessage(notExpected, actual) - source sourceObject - } -} -auxiliary isLooserConstraintCheck(RosettaExpression sourceObject, RosettaCardinality expected) { - var RListType actual - empty |- sourceObject : actual or actual = null - if (expected !== null && actual !== null) { - expected.isSubconstraintOf(actual.constraint) - or - fail error notLooserConstraintMessage(expected, actual) - source sourceObject - } -} - -/****** CHECK RULES ********/ -checkrule CheckLeftArithmetic for - ArithmeticOperation op -from { - if (op.operator == '+') { // TODO: improve error messages - { - looseListSubtypeCheck(op.left, singleDate) - } or { - looseListSubtypeCheck(op.left, singleUnconstrainedString) - } or { - looseListSubtypeCheck(op.left, singleUnconstrainedNumber) - } - } else if (op.operator == '-') { - { - looseListSubtypeCheck(op.left, singleDate) - } or { - looseListSubtypeCheck(op.left, singleUnconstrainedNumber) - } - } else { - looseListSubtypeCheck(op.left, singleUnconstrainedNumber) - } -} -checkrule CheckRightArithmetic for - ArithmeticOperation op -from { - if (op.operator == '+') { - { - looseListSubtypeCheck(op.right, singleTime) - } or { - looseListSubtypeCheck(op.right, singleUnconstrainedString) - } or { - looseListSubtypeCheck(op.right, singleUnconstrainedNumber) - } - } else if (op.operator == '-') { - { - looseListSubtypeCheck(op.right, singleDate) - } or { - looseListSubtypeCheck(op.right, singleUnconstrainedNumber) - } - } else { - looseListSubtypeCheck(op.right, singleUnconstrainedNumber) - } -} -checkrule CheckAddition for - ArithmeticOperation op -from { - var RListType tl - var RListType tr - empty |- op.left : tl or tl = null - empty |- op.right : tr or tr = null - if (tl !== null && tr !== null) { - if (op.operator == '+') { - { - empty |- tl.itemType <: DATE - empty |- tr.itemType <: TIME - } - or - { - empty |- tl.itemType <: UNCONSTRAINED_STRING - empty |- tr.itemType <: UNCONSTRAINED_STRING - } - or - { - empty |- tl.itemType <: UNCONSTRAINED_NUMBER - empty |- tr.itemType <: UNCONSTRAINED_NUMBER - } - or - fail error "Expected arguments to be either both a `string` or both a `number`, but got `" + relevantItemTypeDescription(tl, tr) + "` and `" + relevantItemTypeDescription(tr, tl) + "` instead." - source op - } else if (op.operator == '-') { - { - empty |- tl.itemType <: DATE - empty |- tr.itemType <: DATE - } - or - { - empty |- tl.itemType <: UNCONSTRAINED_NUMBER - empty |- tr.itemType <: UNCONSTRAINED_NUMBER - } - or - fail error "Expected arguments to be either both a `date` or both a `number`, but got `" + tl.itemType.toString() + "` and `" + tr.itemType.toString() + "` instead." - source op - } - } -} - -checkrule CheckEqualityOperation for - EqualityOperation op -from { - if (op.cardMod === CardinalityModifier.^NONE) { - comparableListTypeCheck(op) - } else { - looseOnlyRightIsSingularCheck(op) - comparableTypeCheck(op) - } -} - -checkrule CheckLeftLogical for - LogicalOperation op -from { - looseListSubtypeCheck(op.left, singleBoolean) -} -checkrule CheckRightLogical for - LogicalOperation op -from { - looseListSubtypeCheck(op.right, singleBoolean) -} - -checkrule CheckLeftComparison for - ComparisonOperation op -from { - if (op.cardMod === CardinalityModifier.^NONE) { // TODO: improve error messages - { - looseListSubtypeCheck(op.left, singleZonedDateTime) - } or { - looseListSubtypeCheck(op.left, singleDate) - } or { - looseListSubtypeCheck(op.left, singleUnconstrainedNumber) - } - } else { - { - subtypeCheck(op.left, ZONED_DATE_TIME) - } or { - subtypeCheck(op.left, DATE) - } or { - subtypeCheck(op.left, UNCONSTRAINED_NUMBER) - } - } -} -checkrule CheckRightComparison for - ComparisonOperation op -from { - if (op.cardMod === CardinalityModifier.^NONE) { - { - looseListSubtypeCheck(op.left, singleZonedDateTime) - } or { - looseListSubtypeCheck(op.right, singleDate) - } or { - looseListSubtypeCheck(op.right, singleUnconstrainedNumber) - } - } else { - { - subtypeCheck(op.left, ZONED_DATE_TIME) - } or { - subtypeCheck(op.right, DATE) - } or { - subtypeCheck(op.right, UNCONSTRAINED_NUMBER) - } - } -} -checkrule CheckComparison for - ComparisonOperation op -from { - var RListType tl - var RListType tr - empty |- op.left : tl or tl = null - empty |- op.right : tr or tr = null - if (tl !== null && tr !== null) { - { - empty |- tl.itemType <: ZONED_DATE_TIME - empty |- tr.itemType <: ZONED_DATE_TIME - } - or - { - empty |- tl.itemType <: DATE - empty |- tr.itemType <: DATE - } - or - { - empty |- tl.itemType <: UNCONSTRAINED_NUMBER - empty |- tr.itemType <: UNCONSTRAINED_NUMBER - } - or - fail error "Expected arguments to be either both a `date`, both a `number` or both a `zonedDateTime`, but got `" + tl.itemType.toString() + "` and `" + tr.itemType.toString() + "` instead." - source op - } - if (op.cardMod !== CardinalityModifier.^NONE) { - looseOnlyRightIsSingularCheck(op) - } -} - -checkrule CheckIfConditionalExpression for - RosettaConditionalExpression e -from { - looseListSubtypeCheck(e.^if, singleBoolean) -} -checkrule CheckBodyConditionalExpression for - RosettaConditionalExpression e -from { - var RListType tthen - var RListType telse - empty |- e.ifthen : tthen or tthen = null - empty |- e.elsethen : telse or telse = null - if (tthen !== null && telse !== null) { - val joined = listJoin(tthen, telse) - - if ({empty |- ANY <: joined.itemType}) { - fail error "Types `" + tthen.itemType.name + "` and `" + telse.itemType.name + "` do not have a common supertype." - } - } -} - -checkrule CheckListLiteral for - ListLiteral e -from { - val telems = newArrayList - if (e.elements.forall[ - var RListType telem - empty |- it : telem or telem = null - if (telem !== null) { - telems.add(telem) - } - telem !== null - ]) { - telems.fold(emptyNothing, [ RListType acc, RListType telem | - if (acc === null) { - null - } else { - val sup = join(telem.itemType, acc.itemType); - if ({empty |- ANY <: sup}) { - null - } else { - createListType(sup, telem.constraint + acc.constraint) - } - } - ]) !== null - or - fail error "Elements do not have a common supertype: " + telems.join(', ')["`" + it.itemType.name + "`"] + "." - } -} - -checkrule CheckRosettaSymbolReference for RosettaSymbolReference e from { - val f = e.symbol - switch f { - RosettaExternalFunction: { - { - f.parameters.size() == e.args.size() - or - fail error "Expected " + f.parameters.size() + " argument" + (if (f.parameters.size() === 1) "" else "s") + ", but got " + e.args.size() + " instead." - } - (0..> { public static String EXPRESSION_RTYPE_CACHE_KEY = RosettaTypeProvider.canonicalName + ".EXPRESSION_RTYPE" - @Inject extension RosettaOperators - @Inject IQualifiedNameProvider qNames @Inject RosettaEcoreUtil extensions @Inject extension ImplicitVariableUtil @Inject extension TypeSystem @@ -157,20 +156,23 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { + private def RMetaAnnotatedType safeRType(RosettaSymbol symbol, EObject context, Map cycleTracker) { if (!extensions.isResolved(symbol)) { - return NOTHING.withEmptyMeta + return NOTHING_WITH_NO_META } switch symbol { RosettaFeature: { safeRType(symbol as RosettaFeature, context, cycleTracker) } + RosettaParameter: { + symbol.typeCall.typeCallToRType.withEmptyMeta + } ClosureParameter: { val setOp = symbol.function.eContainer as RosettaFunctionalOperation if(setOp !== null) { setOp.argument.safeRType(cycleTracker) } else - MISSING.withEmptyMeta + NOTHING_WITH_NO_META } RosettaEnumeration: { // @Compat: RosettaEnumeration should not be a RosettaSymbol. symbol.buildREnumType.withMeta(symbol.RMetaAttributesOfSymbol) @@ -179,14 +181,14 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { if (!extensions.isResolved(feature)) { - return NOTHING.withEmptyMeta + return NOTHING_WITH_NO_META } switch (feature) { RosettaTypedFeature: { val featureType = if (feature.typeCall === null) { - NOTHING.withEmptyMeta + NOTHING_WITH_NO_META } else { feature.typeCall.typeCallToRType.withMeta(feature.RMetaAttributesOfFeature) } @@ -220,11 +222,11 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { - val left = expr.left - var leftType = left.safeRType(cycleTracker) - if (leftType.RType instanceof RErrorType) { - return NOTHING.withEmptyMeta - } - val right = expr.right - var rightType = right.safeRType(cycleTracker) - if (rightType.RType instanceof RErrorType) { - return NOTHING.withEmptyMeta - } - expr.operator.resultType(leftType.RType, rightType.RType).withEmptyMeta + ].orElse(NOTHING_WITH_NO_META) } override protected caseAbsentOperation(RosettaAbsentExpression expr, Map cycleTracker) { - BOOLEAN.withEmptyMeta + BOOLEAN_WITH_NO_META } override protected caseAddOperation(ArithmeticOperation expr, Map cycleTracker) { - caseBinaryOperation(expr, cycleTracker) + val left = expr.left.safeRType(cycleTracker) + val right = expr.right.safeRType(cycleTracker) + if (left.isSubtypeOf(NOTHING_WITH_NO_META)) { + NOTHING_WITH_NO_META + } else if (left.isSubtypeOf(DATE_WITH_NO_META)) { + DATE_TIME_WITH_NO_META + } else if (left.isSubtypeOf(UNCONSTRAINED_STRING_WITH_NO_META)) { + keepTypeAliasIfPossible(left.RType, right.RType, [l,r| + if (l instanceof RStringType && r instanceof RStringType) { + val s1 = l as RStringType + val s2 = r as RStringType + val newInterval = s1.interval.add(s2.interval) + new RStringType(newInterval, Optional.empty()) + } else { + NOTHING + } + ]).withEmptyMeta + } else if (left.isSubtypeOf(UNCONSTRAINED_NUMBER_WITH_NO_META)) { + keepTypeAliasIfPossible(left.RType, right.RType, [l,r| + if (l instanceof RNumberType && r instanceof RNumberType) { + val n1 = l as RNumberType + val n2 = r as RNumberType + val newFractionalDigits = OptionalUtil.zipWith(n1.fractionalDigits, n2.fractionalDigits, [a,b|Math.max(a,b)]) + val newInterval = n1.interval.add(n2.interval) + new RNumberType(Optional.empty(), newFractionalDigits, newInterval, Optional.empty()) + } else { + NOTHING + } + ]).withEmptyMeta + } else { + NOTHING_WITH_NO_META + } } override protected caseAndOperation(LogicalOperation expr, Map cycleTracker) { - caseBinaryOperation(expr, cycleTracker) + BOOLEAN_WITH_NO_META } override protected caseAsKeyOperation(AsKeyOperation expr, Map cycleTracker) { @@ -302,36 +321,37 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { - BOOLEAN.withEmptyMeta + BOOLEAN_WITH_NO_META } override protected caseChoiceOperation(ChoiceOperation expr, Map cycleTracker) { - BOOLEAN.withEmptyMeta + BOOLEAN_WITH_NO_META } override protected caseConditionalExpression(RosettaConditionalExpression expr, Map cycleTracker) { val ifT = expr.ifthen.safeRType(cycleTracker) - if (ifT.RType instanceof RErrorType) { - return NOTHING.withEmptyMeta - } val elseT = expr.elsethen.safeRType(cycleTracker) - if (elseT.RType instanceof RErrorType) { - return NOTHING.withEmptyMeta - } val joined = joinMetaAnnotatedTypes(ifT, elseT) - if (joined == ANY) { - new RErrorType('''Types `«ifT»` and `«elseT»` do not have a common supertype.''').withEmptyMeta + if (ANY_WITH_NO_META.isSubtypeOf(joined)) { + NOTHING_WITH_NO_META } else { joined } } override protected caseContainsOperation(RosettaContainsExpression expr, Map cycleTracker) { - caseBinaryOperation(expr, cycleTracker) + BOOLEAN_WITH_NO_META } override protected caseDefaultOperation(DefaultOperation expr, Map cycleTracker) { - caseBinaryOperation(expr, cycleTracker) + val left = expr.left.safeRType(cycleTracker) + val right = expr.right.safeRType(cycleTracker) + val result = left.joinMetaAnnotatedTypes(right) + if (ANY_WITH_NO_META.isSubtypeOf(result)) { + NOTHING_WITH_NO_META + } else { + result + } } override protected caseCountOperation(RosettaCountOperation expr, Map cycleTracker) { @@ -339,7 +359,7 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { - caseBinaryOperation(expr, cycleTracker) + BOOLEAN_WITH_NO_META } override protected caseDistinctOperation(DistinctOperation expr, Map cycleTracker) { @@ -347,21 +367,21 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { - caseBinaryOperation(expr, cycleTracker) + UNCONSTRAINED_NUMBER_WITH_NO_META } override protected caseEqualsOperation(EqualityOperation expr, Map cycleTracker) { - caseBinaryOperation(expr, cycleTracker) + BOOLEAN_WITH_NO_META } override protected caseExistsOperation(RosettaExistsExpression expr, Map cycleTracker) { - BOOLEAN.withEmptyMeta + BOOLEAN_WITH_NO_META } override protected caseFeatureCall(RosettaFeatureCall expr, Map cycleTracker) { val feature = expr.feature if (!extensions.isResolved(feature)) { - return NOTHING.withEmptyMeta + return NOTHING_WITH_NO_META } if (feature instanceof RosettaEnumValue) { expr.receiver.safeRType(cycleTracker) @@ -373,7 +393,7 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { val feature = expr.feature if (!extensions.isResolved(feature)) { - return NOTHING.withEmptyMeta + return NOTHING_WITH_NO_META } (feature as RosettaFeature).safeRType(expr, cycleTracker) } @@ -391,11 +411,11 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { - caseBinaryOperation(expr, cycleTracker) + BOOLEAN_WITH_NO_META } override protected caseGreaterThanOrEqualOperation(ComparisonOperation expr, Map cycleTracker) { - caseBinaryOperation(expr, cycleTracker) + BOOLEAN_WITH_NO_META } override protected caseImplicitVariable(RosettaImplicitVariable expr, Map cycleTracker) { @@ -407,7 +427,7 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { - UNCONSTRAINED_STRING.withEmptyMeta + UNCONSTRAINED_STRING_WITH_NO_META } override protected caseLastOperation(LastOperation expr, Map cycleTracker) { @@ -415,27 +435,25 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { - caseBinaryOperation(expr, cycleTracker) + BOOLEAN_WITH_NO_META } override protected caseLessThanOrEqualOperation(ComparisonOperation expr, Map cycleTracker) { - caseBinaryOperation(expr, cycleTracker) + BOOLEAN_WITH_NO_META } override protected caseListLiteral(ListLiteral expr, Map cycleTracker) { val types = expr.elements.map[RMetaAnnotatedType].filter[it !== null] val joined = types.joinMetaAnnotatedTypes - val unique = newLinkedHashSet(types) - val StringConcatenationClient failedList = '''«FOR t: unique.take(unique.size-1) SEPARATOR ", "»`«t»`«ENDFOR» and `«unique.last»`''' - if (joined == ANY) { - new RErrorType('''Types «failedList» do not have a common supertype.''').withEmptyMeta + if (ANY_WITH_NO_META.isSubtypeOf(joined)) { + NOTHING_WITH_NO_META } else { joined } } override protected caseMapOperation(MapOperation expr, Map cycleTracker) { - expr.function?.body?.safeRType(cycleTracker) + expr.function?.body?.safeRType(cycleTracker) ?: NOTHING_WITH_NO_META } override protected caseMaxOperation(MaxOperation expr, Map cycleTracker) { @@ -447,22 +465,34 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { - caseBinaryOperation(expr, cycleTracker) + val left = expr.left.safeRType(cycleTracker) + val right = expr.right.safeRType(cycleTracker) + keepTypeAliasIfPossible(left.RType, right.RType, [l,r| + if (l instanceof RNumberType && r instanceof RNumberType) { + val n1 = l as RNumberType + val n2 = r as RNumberType + val newFractionalDigits = OptionalUtil.zipWith(n1.fractionalDigits, n2.fractionalDigits, [a,b|a+b]) + val newInterval = n1.interval.multiply(n2.interval) + new RNumberType(Optional.empty(), newFractionalDigits, newInterval, Optional.empty()) + } else { + NOTHING + } + ]).withEmptyMeta } override protected caseNotEqualsOperation(EqualityOperation expr, Map cycleTracker) { - caseBinaryOperation(expr, cycleTracker) + BOOLEAN_WITH_NO_META } override protected caseNumberLiteral(RosettaNumberLiteral expr, Map cycleTracker) { if (expr.value === null) { // In case of a parse error - return NOTHING.withEmptyMeta + return NOTHING_WITH_NO_META } constrainedNumber(expr.value.toPlainString.replaceAll("\\.|\\-", "").length, Math.max(0, expr.value.scale), expr.value, expr.value).withEmptyMeta } override protected caseOneOfOperation(OneOfOperation expr, Map cycleTracker) { - BOOLEAN.withEmptyMeta + BOOLEAN_WITH_NO_META } override protected caseOnlyElementOperation(RosettaOnlyElement expr, Map cycleTracker) { @@ -470,15 +500,15 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { - BOOLEAN.withEmptyMeta + BOOLEAN_WITH_NO_META } override protected caseOrOperation(LogicalOperation expr, Map cycleTracker) { - caseBinaryOperation(expr, cycleTracker) + BOOLEAN_WITH_NO_META } override protected caseReduceOperation(ReduceOperation expr, Map cycleTracker) { - expr.function?.body?.safeRType(cycleTracker) + expr.function?.body?.safeRType(cycleTracker) ?: NOTHING_WITH_NO_META } override protected caseReverseOperation(ReverseOperation expr, Map cycleTracker) { @@ -494,7 +524,27 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { - caseBinaryOperation(expr, cycleTracker) + val left = expr.left.safeRType(cycleTracker) + val right = expr.right.safeRType(cycleTracker) + if (left.isSubtypeOf(NOTHING_WITH_NO_META)) { + NOTHING_WITH_NO_META + } else if (left.isSubtypeOf(DATE_WITH_NO_META)) { + UNCONSTRAINED_INT_WITH_NO_META + } else if (left.isSubtypeOf(UNCONSTRAINED_NUMBER_WITH_NO_META)) { + keepTypeAliasIfPossible(left.RType, right.RType, [l,r| + if (l instanceof RNumberType && r instanceof RNumberType) { + val n1 = l as RNumberType + val n2 = r as RNumberType + val newFractionalDigits = OptionalUtil.zipWith(n1.fractionalDigits, n2.fractionalDigits, [a,b|Math.max(a,b)]) + val newInterval = n1.interval.subtract(n2.interval) + new RNumberType(Optional.empty(), newFractionalDigits, newInterval, Optional.empty()) + } else { + NOTHING + } + ]).withEmptyMeta + } else { + NOTHING_WITH_NO_META + } } override protected caseSumOperation(SumOperation expr, Map cycleTracker) { @@ -519,7 +569,7 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { - expr.function?.body?.safeRType(cycleTracker) + expr.function?.body?.safeRType(cycleTracker) ?: NOTHING_WITH_NO_META } override protected caseToEnumOperation(ToEnumOperation expr, Map cycleTracker) { @@ -527,19 +577,19 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { - UNCONSTRAINED_INT.withEmptyMeta + UNCONSTRAINED_INT_WITH_NO_META } override protected caseToNumberOperation(ToNumberOperation expr, Map cycleTracker) { - UNCONSTRAINED_NUMBER.withEmptyMeta + UNCONSTRAINED_NUMBER_WITH_NO_META } override protected caseToStringOperation(ToStringOperation expr, Map cycleTracker) { - UNCONSTRAINED_STRING.withEmptyMeta + UNCONSTRAINED_STRING_WITH_NO_META } override protected caseToTimeOperation(ToTimeOperation expr, Map cycleTracker) { - TIME.withEmptyMeta + TIME_WITH_NO_META } override protected caseConstructorExpression(RosettaConstructorExpression expr, Map cycleTracker) { @@ -547,15 +597,15 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { - DATE.withEmptyMeta + DATE_WITH_NO_META } override protected caseToDateTimeOperation(ToDateTimeOperation expr, Map cycleTracker) { - DATE_TIME.withEmptyMeta + DATE_TIME_WITH_NO_META } override protected caseToZonedDateTimeOperation(ToZonedDateTimeOperation expr, Map cycleTracker) { - ZONED_DATE_TIME.withEmptyMeta + ZONED_DATE_TIME_WITH_NO_META } override protected caseSwitchOperation(SwitchOperation expr, Map context) { diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/SubtypeRelation.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/SubtypeRelation.java index 1889b2def..c1402cf0e 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/SubtypeRelation.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/SubtypeRelation.java @@ -105,9 +105,9 @@ public RMetaAnnotatedType join(RMetaAnnotatedType t1, RMetaAnnotatedType t2) { RType t1RType = t1.getRType(); RType t2RType = t2.getRType(); if (t1RType.equals(t2RType)) { - return new RMetaAnnotatedType(t1RType, intersectMeta(t1, t2)); + return RMetaAnnotatedType.withMeta(t1RType, intersectMeta(t1, t2)); } - return new RMetaAnnotatedType(join(t1RType, t2RType), List.of()); + return RMetaAnnotatedType.withEmptyMeta(join(t1RType, t2RType)); } public RType join(RType t1, RType t2) { diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/TypeFactory.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/TypeFactory.java index 462595e9c..38eee4415 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/TypeFactory.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/TypeFactory.java @@ -25,8 +25,6 @@ import javax.inject.Inject; import com.regnosys.rosetta.interpreter.RosettaValue; -import com.regnosys.rosetta.rosetta.RosettaCardinality; -import com.regnosys.rosetta.rosetta.RosettaFactory; import com.regnosys.rosetta.types.builtin.RBuiltinTypeService; import com.regnosys.rosetta.types.builtin.RNumberType; import com.regnosys.rosetta.types.builtin.RStringType; @@ -36,48 +34,19 @@ public class TypeFactory { private final RBuiltinTypeService builtinTypes; - public final RosettaCardinality single; - public final RosettaCardinality empty; - - public final RListType singleBoolean; - public final RListType singleDate; - public final RListType singleTime; - public final RListType singlePattern; - public final RListType singleUnconstrainedInt; - public final RListType singleUnconstrainedNumber; - public final RListType singleUnconstrainedString; - public final RListType singleDateTime; - public final RListType singleZonedDateTime; - public final RListType emptyNothing; - @Inject public TypeFactory(RBuiltinTypeService builtinTypes) { this.builtinTypes = builtinTypes; - - this.single = createConstraint(1, 1); - - this.empty = createConstraint(0, 0); - - this.singleBoolean = createListType(builtinTypes.BOOLEAN, single); - this.singleDate = createListType(builtinTypes.DATE, single); - this.singleTime = createListType(builtinTypes.TIME, single); - this.singlePattern = createListType(builtinTypes.PATTERN, single); - this.singleUnconstrainedInt = createListType(builtinTypes.UNCONSTRAINED_INT, single); - this.singleUnconstrainedNumber = createListType(builtinTypes.UNCONSTRAINED_NUMBER, single); - this.singleUnconstrainedString = createListType(builtinTypes.UNCONSTRAINED_STRING, single); - this.singleDateTime = createListType(builtinTypes.DATE_TIME, single); - this.singleZonedDateTime = createListType(builtinTypes.ZONED_DATE_TIME, single); - this.emptyNothing = createListType(builtinTypes.NOTHING, empty); } - public RListType singleInt(Optional digits, Optional min, Optional max) { - return createListType(constrainedInt(digits, min, max), single); + public RMetaAnnotatedType intWithNoMeta(Optional digits, Optional min, Optional max) { + return RMetaAnnotatedType.withEmptyMeta(constrainedInt(digits, min, max)); } - public RListType singleInt(int digits, BigInteger min, BigInteger max) { - return createListType(constrainedInt(digits, min, max), single); + public RMetaAnnotatedType intWithNoMeta(int digits, BigInteger min, BigInteger max) { + return RMetaAnnotatedType.withEmptyMeta(constrainedInt(digits, min, max)); } - public RListType singleInt(int digits, String min, String max) { - return createListType(constrainedInt(digits, min, max), single); + public RMetaAnnotatedType intWithNoMeta(int digits, String min, String max) { + return RMetaAnnotatedType.withEmptyMeta(constrainedInt(digits, min, max)); } public RAliasType constrainedInt(Optional digits, Optional min, Optional max) { RNumberType refersTo = constrainedNumber(digits, Optional.of(0), min.map(BigDecimal::new), max.map(BigDecimal::new), Optional.empty()); @@ -93,19 +62,19 @@ public RAliasType constrainedInt(int digits, String min, String max) { return constrainedInt(Optional.of(digits), Optional.of(new BigInteger(min)), Optional.of(new BigInteger(max))); } - public RListType singleNumber(Optional digits, Optional fractionalDigits, + public RMetaAnnotatedType numberWithNoMeta(Optional digits, Optional fractionalDigits, Optional min, Optional max, Optional scale) { - return createListType(constrainedNumber(digits, fractionalDigits, min, max, scale), single); + return RMetaAnnotatedType.withEmptyMeta(constrainedNumber(digits, fractionalDigits, min, max, scale)); } - public RListType singleNumber(Optional digits, Optional fractionalDigits, + public RMetaAnnotatedType numberWithNoMeta(Optional digits, Optional fractionalDigits, BigDecimalInterval interval, Optional scale) { - return createListType(constrainedNumber(digits, fractionalDigits, interval, scale), single); + return RMetaAnnotatedType.withEmptyMeta(constrainedNumber(digits, fractionalDigits, interval, scale)); } - public RListType singleNumber(int digits, int fractionalDigits, BigDecimal min, BigDecimal max) { - return createListType(constrainedNumber(digits, fractionalDigits, min, max), single); + public RMetaAnnotatedType numberWithNoMeta(int digits, int fractionalDigits, BigDecimal min, BigDecimal max) { + return RMetaAnnotatedType.withEmptyMeta(constrainedNumber(digits, fractionalDigits, min, max)); } - public RListType singleNumber(int digits, int fractionalDigits, String min, String max) { - return createListType(constrainedNumber(digits, fractionalDigits, min, max), single); + public RMetaAnnotatedType numberWithNoMeta(int digits, int fractionalDigits, String min, String max) { + return RMetaAnnotatedType.withEmptyMeta(constrainedNumber(digits, fractionalDigits, min, max)); } public RNumberType constrainedNumber(Optional digits, Optional fractionalDigits, Optional min, Optional max, Optional scale) { @@ -122,14 +91,14 @@ public RNumberType constrainedNumber(int digits, int fractionalDigits, String mi return constrainedNumber(Optional.of(digits), Optional.of(fractionalDigits), Optional.of(new BigDecimal(min)), Optional.of(new BigDecimal(max)), Optional.empty()); } - public RListType singleString(Optional minLength, Optional maxLength, Optional pattern) { - return createListType(constrainedString(minLength, maxLength, pattern), single); + public RMetaAnnotatedType stringWithNoMeta(Optional minLength, Optional maxLength, Optional pattern) { + return RMetaAnnotatedType.withEmptyMeta(constrainedString(minLength, maxLength, pattern)); } - public RListType singleString(PositiveIntegerInterval interval, Optional pattern) { - return createListType(constrainedString(interval, pattern), single); + public RMetaAnnotatedType stringWithNoMeta(PositiveIntegerInterval interval, Optional pattern) { + return RMetaAnnotatedType.withEmptyMeta(constrainedString(interval, pattern)); } - public RListType singleString(int minLength, int maxLength) { - return createListType(constrainedString(minLength, maxLength), single); + public RMetaAnnotatedType stringWithNoMeta(int minLength, int maxLength) { + return RMetaAnnotatedType.withEmptyMeta(constrainedString(minLength, maxLength)); } public RStringType constrainedString(Optional minLength, Optional maxLength, Optional pattern) { return new RStringType(minLength, maxLength, pattern); @@ -140,27 +109,4 @@ public RStringType constrainedString(PositiveIntegerInterval interval, Optional< public RStringType constrainedString(int minLength, int maxLength) { return new RStringType(Optional.of(minLength), Optional.of(maxLength), Optional.empty()); } - - public RosettaCardinality createConstraint(int inf, int sup) { - RosettaCardinality c = RosettaFactory.eINSTANCE.createRosettaCardinality(); - c.setInf(inf); - c.setSup(sup); - return c; - } - public RosettaCardinality createConstraint(int inf) { - RosettaCardinality c = RosettaFactory.eINSTANCE.createRosettaCardinality(); - c.setInf(inf); - c.setUnbounded(true); - return c; - } - - public RListType createListType(RType itemType, RosettaCardinality constraint) { - return new RListType(itemType, constraint); - } - public RListType createListType(RType itemType, int inf, int sup) { - return createListType(itemType, createConstraint(inf, sup)); - } - public RListType createListType(RType itemType, int inf) { - return createListType(itemType, createConstraint(inf)); - } } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/TypeSystem.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/TypeSystem.java index 2bbbd4cf3..1609d453c 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/TypeSystem.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/TypeSystem.java @@ -17,7 +17,6 @@ package com.regnosys.rosetta.types; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -34,7 +33,6 @@ import com.regnosys.rosetta.rosetta.RosettaExternalRuleSource; import com.regnosys.rosetta.rosetta.RosettaRule; import com.regnosys.rosetta.rosetta.TypeCall; -import com.regnosys.rosetta.rosetta.expression.RosettaExpression; import com.regnosys.rosetta.types.builtin.RBuiltinTypeService; import com.regnosys.rosetta.typing.RosettaTyping; import com.regnosys.rosetta.utils.ExternalAnnotationUtil; @@ -55,12 +53,6 @@ public class TypeSystem { @Inject private SubtypeRelation subtypeRelation; - public RListType inferType(RosettaExpression expr) { - Objects.requireNonNull(expr); - - return typing.inferType(expr).getValue(); - } - public RType getRulesInputType(RDataType data, Optional source) { return getRulesInputType(data, source, new HashSet<>()); } @@ -108,11 +100,10 @@ public RMetaAnnotatedType joinMetaAnnotatedTypes(Iterable ty Objects.requireNonNull(types); Validate.noNullElements(types); - RMetaAnnotatedType any = new RMetaAnnotatedType(builtins.ANY, List.of()); - RMetaAnnotatedType acc = new RMetaAnnotatedType(builtins.NOTHING, List.of()); + RMetaAnnotatedType acc = builtins.NOTHING_WITH_NO_META; for (RMetaAnnotatedType t: types) { acc = subtypeRelation.join(acc, t); - if (acc.equals(any)) { + if (acc.equals(builtins.ANY_WITH_NO_META)) { return acc; } } @@ -138,12 +129,6 @@ public RType join(Iterable types) { } return acc; } - public RListType listJoin(RListType t1, RListType t2) { - Objects.requireNonNull(t1); - Objects.requireNonNull(t2); - - return Objects.requireNonNull(typing.listJoin(t1, t2)); - } public RType meet(RType t1, RType t2) { Objects.requireNonNull(t1); @@ -196,11 +181,11 @@ public boolean isListSubtypeOf(RListType sub, RListType sup) { return typing.listSubtypeSucceeded(sub, sup); } - public boolean isComparable(RType t1, RType t2) { + public boolean isComparable(RMetaAnnotatedType t1, RMetaAnnotatedType t2) { Objects.requireNonNull(t1); Objects.requireNonNull(t2); - return typing.comparable(t1, t2); + return isSubtypeOf(t1, t2) || isSubtypeOf(t2, t1); } public boolean isListComparable(RListType t1, RListType t2) { Objects.requireNonNull(t1); diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/TypeValidationUtil.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/TypeValidationUtil.java deleted file mode 100644 index c0f7864eb..000000000 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/TypeValidationUtil.java +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright 2024 REGnosys - * - * 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. - */ - -package com.regnosys.rosetta.types; - -import com.regnosys.rosetta.rosetta.expression.ModifiableBinaryOperation; -import com.regnosys.rosetta.types.builtin.RBuiltinTypeService; - -import javax.inject.Inject; - -import com.regnosys.rosetta.rosetta.RosettaCardinality; - -public class TypeValidationUtil { - @Inject - TypeSystem typing; - @Inject - TypeFactory fac; - @Inject - RBuiltinTypeService service; - - public String notAListSubtypeMessage(RListType expected, RListType actual) { - if (!typing.isSubtypeOf(actual.getItemType(), expected.getItemType())) { - if (!actual.getConstraint().isSubconstraintOf(expected.getConstraint()) && !(expected.isPlural() && actual.isPlural())) { - return new StringBuilder() - .append("Expected ") - .append(toCompleteDescription(expected, actual.getItemType())) - .append(", but got ") - .append(toCompleteDescription(actual, expected.getItemType())) - .append(" instead.") - .toString(); - } - return notASubtypeMessage(expected.getItemType(), actual.getItemType()); - } - return notLooserConstraintMessage(expected.getConstraint(), actual); - } - public String notASubtypeMessage(RType expected, RType actual) { - return new StringBuilder() - .append("Expected type `") - .append(relevantItemTypeDescription(expected, actual)) - .append("`, but got `") - .append(relevantItemTypeDescription(actual, expected)) - .append("` instead.") - .toString(); - } - public String notListComparableMessage(RListType left, RListType right) { - if (!typing.isComparable(left.getItemType(), right.getItemType())) { - return notComparableMessage(left.getItemType(), right.getItemType()); - } - StringBuilder b = new StringBuilder() - .append("Cannot compare ") - .append(toConstraintDescription(left.getConstraint())) - .append(" to ") - .append(toConstraintDescription(right.getConstraint())) - .append(", as they cannot be of the same length."); - if (left.isSingular() || right.isSingular()) { - b.append(" Perhaps you forgot to write `all` or `any` in front of the operator?"); - } - return b.toString(); - } - public String notComparableMessage(RType left, RType right) { - return new StringBuilder() - .append("Types `") - .append(relevantItemTypeDescription(left, right)) - .append("` and `") - .append(relevantItemTypeDescription(right, left)) - .append("` are not comparable.") - .toString(); - } - public String bothAreSingularMessage(ModifiableBinaryOperation op) { - return new StringBuilder() - .append("The cardinality operator `") - .append(op.getCardMod()) - .append("` is redundant when comparing two single values.") - .toString(); - } - public String notRightIsSingularButLeftIsMessage(RListType actual) { - return new StringBuilder() - .append("Expected ") - .append(toConstraintDescription(fac.single)) - .append(", but got ") - .append(toConstraintDescription(actual.getConstraint())) - .append(" instead. Perhaps you meant to swap the left and right operands?") - .toString(); - } - public String notConstraintMessage(RosettaCardinality expected, RListType actual) { - return new StringBuilder() - .append("Expected ") - .append(toConstraintDescription(expected)) - .append(", but got ") - .append(toConstraintDescription(actual.getConstraint())) - .append(" instead.") - .toString(); - } - public String wrongConstraintMessage(RosettaCardinality wrong, RListType actual) { - return new StringBuilder() - .append("May not be ") - .append(toConstraintDescription(wrong)) - .append(".") - .toString(); - } - public String notLooserConstraintMessage(RosettaCardinality expected, RListType actual) { - return new StringBuilder() - .append("Expected ") - .append(toConstraintDescription(expected)) - .append(", but got ") - .append(toConstraintDescription(actual.getConstraint())) - .append(" instead.") - .toString(); - } - - public CharSequence relevantItemTypeDescription(RType t, RType context) { - if (t.getName().equals(context.getName())) { - return t.toString(); - } - return t.getName(); - } - public CharSequence relevantItemTypeDescription(RListType t, RType context) { - return relevantItemTypeDescription(t.getItemType(), context); - } - public CharSequence relevantItemTypeDescription(RListType t, RListType context) { - return relevantItemTypeDescription(t.getItemType(), context.getItemType()); - } - public CharSequence toShortDescription(RListType t, RType context) { - StringBuilder b = new StringBuilder(); - if (t.isEmpty()) { - if (t.getItemType().equals(service.NOTHING)) { - return "an empty value"; - } - b.append("an empty value of type"); - } else if (t.isOptional()) { - b.append("an optional"); - } else if (t.isSingular()) { - b.append("a single"); - } else { - return b.append("a list of `") - .append(t.getItemType()) - .append("`s") - .toString(); - } - return b.append(" `") - .append(relevantItemTypeDescription(t, context)) - .append("`") - .toString(); - } - public CharSequence toConstraintDescription(RosettaCardinality c) { - if (c.isEmpty()) { - return "an empty value"; - } else if (c.isOptional()) { - return "an optional value"; - } else if (c.isSingular()) { - return "a single value"; - } else { - StringBuilder b = new StringBuilder(); - if (c.isUnbounded()) { - if (c.getInf() == 0) { - b.append("an unbounded list of any length"); - } else { - b.append("an unbounded list with at least ") - .append(c.getInf()) - .append(" item") - .append(pluralS(c.getInf())); - } - } else { - b.append("a list with "); - if (c.getInf() == c.getSup()) { - b.append(c.getSup()); - } else { - b.append(c.getInf()) - .append(" to ") - .append(c.getSup()); - } - b.append(" item") - .append(pluralS(c.getSup())); - } - return b.toString(); - } - } - public CharSequence toCompleteDescription(RListType t, RType context) { - if (t.isPlural()) { - StringBuilder b = new StringBuilder(); - RosettaCardinality c = t.getConstraint(); - if (c.isUnbounded()) { - b.append("an unbounded list of `") - .append(relevantItemTypeDescription(t, context)) - .append("`s "); - if (c.getInf() == 0) { - b.append("of any length"); - } else { - b.append("with at least ") - .append(c.getInf()) - .append(" item") - .append(pluralS(c.getInf())); - } - } else { - b.append("a list of `") - .append(relevantItemTypeDescription(t, context)) - .append("`s with "); - if (c.getInf() == c.getSup()) { - b.append(c.getSup()); - } else { - b.append(c.getInf()) - .append(" to ") - .append(c.getSup()); - } - b.append(" item") - .append(pluralS(c.getSup())); - } - return b.toString(); - } - return toShortDescription(t, context); - } - private String pluralS(int count) { - return count == 1 ? "" : "s"; - } -} diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/builtin/RBuiltinTypeService.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/builtin/RBuiltinTypeService.java index da03a7828..c6fe1fc03 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/builtin/RBuiltinTypeService.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/builtin/RBuiltinTypeService.java @@ -18,7 +18,6 @@ import java.util.HashMap; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; import java.util.Optional; import java.util.function.Function; @@ -75,25 +74,28 @@ public Optional> reverse(RType type) { }; public final RBasicType BOOLEAN = registerConstantType(new RBasicType("boolean", true)); + public final RMetaAnnotatedType BOOLEAN_WITH_NO_META = RMetaAnnotatedType.withEmptyMeta(BOOLEAN); public final RBasicType TIME = registerConstantType(new RBasicType("time", true)); + public final RMetaAnnotatedType TIME_WITH_NO_META = RMetaAnnotatedType.withEmptyMeta(TIME); public final RBasicType PATTERN = registerConstantType(new RBasicType("pattern", false)); - // TODO: remove the MISSING type - public final RBasicType MISSING = registerConstantType(new RBasicType("missing", true)); + public final RMetaAnnotatedType PATTERN_WITH_NO_META = RMetaAnnotatedType.withEmptyMeta(PATTERN); public final RBasicType NOTHING = registerConstantType(new RBasicType("nothing", true)); + public final RMetaAnnotatedType NOTHING_WITH_NO_META = RMetaAnnotatedType.withEmptyMeta(NOTHING); public final RBasicType ANY = registerConstantType(new RBasicType("any", false)); + public final RMetaAnnotatedType ANY_WITH_NO_META = RMetaAnnotatedType.withEmptyMeta(ANY); public final RAliasType UNCONSTRAINED_INT = new RAliasType(INT_FUNCTION, new LinkedHashMap<>(Map.of(RNumberType.DIGITS_PARAM_NAME, RosettaValue.empty(), RNumberType.MIN_PARAM_NAME, RosettaValue.empty(), RNumberType.MAX_PARAM_NAME, RosettaValue.empty())), new RNumberType(Optional.empty(), Optional.of(0), Optional.empty(), Optional.empty(), Optional.empty())); - public final RMetaAnnotatedType UNCONSTRAINED_INT_WITH_NO_META = new RMetaAnnotatedType(UNCONSTRAINED_INT, List.of()); + public final RMetaAnnotatedType UNCONSTRAINED_INT_WITH_NO_META = RMetaAnnotatedType.withEmptyMeta(UNCONSTRAINED_INT); public final RNumberType UNCONSTRAINED_NUMBER = new RNumberType(Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty()); - public final RMetaAnnotatedType UNCONSTRAINED_NUMBER_WITH_NO_META = new RMetaAnnotatedType(UNCONSTRAINED_NUMBER, List.of()); + public final RMetaAnnotatedType UNCONSTRAINED_NUMBER_WITH_NO_META = RMetaAnnotatedType.withEmptyMeta(UNCONSTRAINED_NUMBER); public final RStringType UNCONSTRAINED_STRING = new RStringType(Optional.empty(), Optional.empty(), Optional.empty()); - public final RMetaAnnotatedType UNCONSTRAINED_STRING_WITH_NO_META = new RMetaAnnotatedType(UNCONSTRAINED_STRING, List.of()); + public final RMetaAnnotatedType UNCONSTRAINED_STRING_WITH_NO_META = RMetaAnnotatedType.withEmptyMeta(UNCONSTRAINED_STRING); public final RDateType DATE = registerConstantType(new RDateType()); - public final RMetaAnnotatedType DATE_WITH_NO_META = new RMetaAnnotatedType(DATE, List.of()); + public final RMetaAnnotatedType DATE_WITH_NO_META = RMetaAnnotatedType.withEmptyMeta(DATE); public final RDateTimeType DATE_TIME = registerConstantType(new RDateTimeType()); - public final RMetaAnnotatedType DATE_TIME_WITH_NO_META = new RMetaAnnotatedType(DATE_TIME, List.of()); + public final RMetaAnnotatedType DATE_TIME_WITH_NO_META = RMetaAnnotatedType.withEmptyMeta(DATE_TIME); public final RZonedDateTimeType ZONED_DATE_TIME = registerConstantType(new RZonedDateTimeType()); - public final RMetaAnnotatedType ZONED_DATE_TIME_NO_META = new RMetaAnnotatedType(ZONED_DATE_TIME, List.of()); + public final RMetaAnnotatedType ZONED_DATE_TIME_WITH_NO_META = RMetaAnnotatedType.withEmptyMeta(ZONED_DATE_TIME); public RBuiltinTypeService() { register("number", (m) -> RNumberType.from(m)); diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/RosettaTypeSwitch.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/RosettaTypeSwitch.java index 6a4d5c030..0d62b9755 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/RosettaTypeSwitch.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/RosettaTypeSwitch.java @@ -20,7 +20,6 @@ import com.regnosys.rosetta.types.RChoiceType; import com.regnosys.rosetta.types.RDataType; import com.regnosys.rosetta.types.REnumType; -import com.regnosys.rosetta.types.RErrorType; import com.regnosys.rosetta.types.RParametrizedType; import com.regnosys.rosetta.types.RType; import com.regnosys.rosetta.types.builtin.RBasicType; @@ -51,8 +50,6 @@ protected Return doSwitch(RType type, Context context) { return caseChoiceType((RChoiceType)type, context); } else if (type instanceof REnumType) { return caseEnumType((REnumType)type, context); - } else if (type instanceof RErrorType) { - return caseErrorType((RErrorType)type, context); } else if (type instanceof RParametrizedType) { return doSwitch((RParametrizedType)type, context); } else if (type instanceof RRecordType) { @@ -78,8 +75,6 @@ protected Return doSwitch(RBasicType type, Context context) { return caseBooleanType(type, context); } else if (type.equals(builtins.TIME)) { return caseTimeType(type, context); - } else if (type.equals(builtins.MISSING)) { - return caseMissingType(type, context); } else if (type.equals(builtins.NOTHING)) { return caseNothingType(type, context); } else if (type.equals(builtins.ANY)) { @@ -97,9 +92,7 @@ protected Return doSwitch(RRecordType type, Context context) { } throw errorMissedCase(type); } - - protected abstract Return caseErrorType(RErrorType type, Context context); - + protected abstract Return caseDataType(RDataType type, Context context); protected abstract Return caseChoiceType(RChoiceType type, Context context); protected abstract Return caseEnumType(REnumType type, Context context); @@ -110,7 +103,6 @@ protected Return doSwitch(RRecordType type, Context context) { protected abstract Return caseStringType(RStringType type, Context context); protected abstract Return caseBooleanType(RBasicType type, Context context); protected abstract Return caseTimeType(RBasicType type, Context context); - protected abstract Return caseMissingType(RBasicType type, Context context); protected abstract Return caseNothingType(RBasicType type, Context context); protected abstract Return caseAnyType(RBasicType type, Context context); diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/ExpressionValidator.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/ExpressionValidator.java new file mode 100644 index 000000000..744aedc6f --- /dev/null +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/ExpressionValidator.java @@ -0,0 +1,636 @@ +package com.regnosys.rosetta.validation; + +import javax.inject.Inject; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.xtext.EcoreUtil2; +import org.eclipse.xtext.validation.Check; + +import com.google.common.collect.Iterables; +import com.regnosys.rosetta.RosettaEcoreUtil; +import com.regnosys.rosetta.generator.util.RosettaFunctionExtensions; +import com.regnosys.rosetta.interpreter.RosettaInterpreter; +import com.regnosys.rosetta.interpreter.RosettaValue; +import com.regnosys.rosetta.rosetta.RosettaCallableWithArgs; +import com.regnosys.rosetta.rosetta.RosettaEnumValue; +import com.regnosys.rosetta.rosetta.RosettaExternalFunction; +import com.regnosys.rosetta.rosetta.RosettaFeature; +import com.regnosys.rosetta.rosetta.RosettaMetaType; +import com.regnosys.rosetta.rosetta.RosettaNamed; +import com.regnosys.rosetta.rosetta.RosettaRule; +import com.regnosys.rosetta.rosetta.RosettaSymbol; +import com.regnosys.rosetta.rosetta.expression.ArithmeticOperation; +import com.regnosys.rosetta.rosetta.expression.CardinalityModifier; +import com.regnosys.rosetta.rosetta.expression.ChoiceOperation; +import com.regnosys.rosetta.rosetta.expression.ComparisonOperation; +import com.regnosys.rosetta.rosetta.expression.DefaultOperation; +import com.regnosys.rosetta.rosetta.expression.EqualityOperation; +import com.regnosys.rosetta.rosetta.expression.ExistsModifier; +import com.regnosys.rosetta.rosetta.expression.JoinOperation; +import com.regnosys.rosetta.rosetta.expression.ListLiteral; +import com.regnosys.rosetta.rosetta.expression.LogicalOperation; +import com.regnosys.rosetta.rosetta.expression.OneOfOperation; +import com.regnosys.rosetta.rosetta.expression.RosettaBinaryOperation; +import com.regnosys.rosetta.rosetta.expression.RosettaConditionalExpression; +import com.regnosys.rosetta.rosetta.expression.RosettaContainsExpression; +import com.regnosys.rosetta.rosetta.expression.RosettaDisjointExpression; +import com.regnosys.rosetta.rosetta.expression.RosettaExistsExpression; +import com.regnosys.rosetta.rosetta.expression.RosettaExpression; +import com.regnosys.rosetta.rosetta.expression.RosettaFeatureCall; +import com.regnosys.rosetta.rosetta.expression.RosettaLiteral; +import com.regnosys.rosetta.rosetta.expression.RosettaOnlyExistsExpression; +import com.regnosys.rosetta.rosetta.expression.RosettaOperation; +import com.regnosys.rosetta.rosetta.expression.RosettaSymbolReference; +import com.regnosys.rosetta.rosetta.expression.SwitchCase; +import com.regnosys.rosetta.rosetta.expression.SwitchOperation; +import com.regnosys.rosetta.rosetta.simple.Attribute; +import com.regnosys.rosetta.rosetta.simple.ChoiceOption; +import com.regnosys.rosetta.rosetta.simple.Function; +import com.regnosys.rosetta.types.CardinalityProvider; +import com.regnosys.rosetta.types.RChoiceType; +import com.regnosys.rosetta.types.RDataType; +import com.regnosys.rosetta.types.REnumType; +import com.regnosys.rosetta.types.RMetaAnnotatedType; +import com.regnosys.rosetta.types.RParametrizedType; +import com.regnosys.rosetta.types.RType; +import com.regnosys.rosetta.types.RosettaTypeProvider; +import com.regnosys.rosetta.types.TypeSystem; +import com.regnosys.rosetta.types.builtin.RBasicType; +import com.regnosys.rosetta.types.builtin.RBuiltinTypeService; +import com.regnosys.rosetta.utils.ExpressionHelper; +import com.regnosys.rosetta.utils.ImplicitVariableUtil; + +import static com.regnosys.rosetta.rosetta.expression.ExpressionPackage.Literals.*; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +// TODO: move over expression validations from RosettaSimpleValidator +public class ExpressionValidator extends AbstractDeclarativeRosettaValidator { + @Inject + private RosettaTypeProvider typeProvider; + @Inject + private TypeSystem typeSystem; + @Inject + private RBuiltinTypeService builtins; + @Inject + private CardinalityProvider cardinalityProvider; + @Inject + private ExpressionHelper exprHelper; + @Inject + private ImplicitVariableUtil implicitVarUtil; + @Inject + private RosettaInterpreter interpreter; + @Inject + private RosettaEcoreUtil ecoreUtil; + @Inject + private RosettaFunctionExtensions functionExtensions; + + private String relevantTypeDescription(RMetaAnnotatedType type, RMetaAnnotatedType context) { + RType valueType = type.getRType(); + RType valueContext = context.getRType(); + if (valueType.equals(valueContext)) { + // Include meta info + return type.toString(); + } + if (valueType.getName().equals(valueContext.getName())) { + // Include type parameters + return valueType.toString(); + } + return valueType.getName(); + } + + private String notASubtypeMessage(RMetaAnnotatedType expected, RMetaAnnotatedType actual) { + return new StringBuilder() + .append("Expected type `") + .append(relevantTypeDescription(expected, actual)) + .append("`, but got `") + .append(relevantTypeDescription(actual, expected)) + .append("` instead") + .toString(); + } + private boolean subtypeCheck(RMetaAnnotatedType expected, RosettaExpression expr, EObject sourceObject, EStructuralFeature feature) { + return subtypeCheck(expected, typeProvider.getRMetaAnnotatedType(expr), sourceObject, feature, INSIGNIFICANT_INDEX); + } + private boolean subtypeCheck(RMetaAnnotatedType expected, RosettaExpression expr, EObject sourceObject, EStructuralFeature feature, int featureIndex) { + return subtypeCheck(expected, typeProvider.getRMetaAnnotatedType(expr), sourceObject, feature, featureIndex); + } + private boolean subtypeCheck(RMetaAnnotatedType expected, RMetaAnnotatedType actual, EObject sourceObject, EStructuralFeature feature) { + return subtypeCheck(expected, actual, sourceObject, feature, INSIGNIFICANT_INDEX); + } + private boolean subtypeCheck(RMetaAnnotatedType expected, RMetaAnnotatedType actual, EObject sourceObject, EStructuralFeature feature, int featureIndex) { + if (!typeSystem.isSubtypeOf(actual, expected)) { + error(notASubtypeMessage(expected, actual), sourceObject, feature, featureIndex); + return false; + } + return true; + } + + private String notComparableMessage(RMetaAnnotatedType left, RMetaAnnotatedType right) { + return new StringBuilder() + .append("Types `") + .append(relevantTypeDescription(left, right)) + .append("` and `") + .append(relevantTypeDescription(right, left)) + .append("` are not comparable") + .toString(); + } + private boolean comparableTypeCheck(RosettaBinaryOperation sourceObject) { + RMetaAnnotatedType tl = typeProvider.getRMetaAnnotatedType(sourceObject.getLeft()); + RMetaAnnotatedType tr = typeProvider.getRMetaAnnotatedType(sourceObject.getRight()); + if (!typeSystem.isComparable(tl, tr)) { + error(notComparableMessage(tl, tr), sourceObject, null); + return false; + } + return true; + } + + private boolean isMultiCheck(RosettaExpression expr, EObject sourceObject, EStructuralFeature feature) { + return isMultiCheck(expr, sourceObject, feature, INSIGNIFICANT_INDEX); + } + private boolean isMultiCheck(RosettaExpression expr, EObject sourceObject, EStructuralFeature feature, int featureIndex) { + if (!cardinalityProvider.isMulti(expr)) { + error("Expecting multi cardinality", sourceObject, feature, featureIndex); + return false; + } + return true; + } + private boolean isSingleCheck(RosettaExpression expr, EObject sourceObject, EStructuralFeature feature) { + return isSingleCheck(expr, sourceObject, feature, INSIGNIFICANT_INDEX); + } + private boolean isSingleCheck(RosettaExpression expr, EObject sourceObject, EStructuralFeature feature, int featureIndex) { + if (cardinalityProvider.isMulti(expr)) { + error("Expecting single cardinality", sourceObject, feature, featureIndex); + return false; + } + return true; + } + + private boolean commonTypeCheck(List expressions, EObject sourceObject, EStructuralFeature feature) { + boolean haveCommonType = true; + if (!expressions.isEmpty()) { + Set types = new LinkedHashSet<>(); + RMetaAnnotatedType firstElemType = typeProvider.getRMetaAnnotatedType(expressions.get(0)); + types.add(firstElemType); + RMetaAnnotatedType commonType = firstElemType; + for (int i=1; i "`" + relevantTypeDescription(t, elemType) + "`").collect(Collectors.joining(", ")) + " and `" + relevantTypeDescription(elemType, newCommonType) + "` do not have a common supertype", + sourceObject, + feature, + feature == null || !feature.isMany() ? INSIGNIFICANT_INDEX : i); + haveCommonType = false; + } else { + types.add(elemType); + commonType = newCommonType; + } + } + } + return haveCommonType; + } + + private void unsupportedTypeError(RMetaAnnotatedType type, RosettaOperation op, EStructuralFeature feature, RType supportedType1, RType supportedType2, RType... moreSupportedTypes) { + StringBuilder supportedTypesMsg = new StringBuilder(); + supportedTypesMsg.append("Supported types are "); + supportedTypesMsg.append(supportedType1); + if (moreSupportedTypes.length > 0) { + supportedTypesMsg.append(", "); + supportedTypesMsg.append(supportedType2); + for (int i=0; i= 1) { + RMetaAnnotatedType paramType = RMetaAnnotatedType.withEmptyMeta(typeSystem.typeCallToRType(f.getInput())); + RosettaExpression arg = expr.getArgs().get(0); + isSingleCheck(arg, expr, ROSETTA_SYMBOL_REFERENCE__RAW_ARGS, 0); + subtypeCheck(paramType, arg, expr, ROSETTA_SYMBOL_REFERENCE__RAW_ARGS, 0); + } + } + } else { + if (s instanceof Attribute) { + if (functionExtensions.isOutput((Attribute) s)) { + RMetaAnnotatedType implicitType = typeProvider.typeOfImplicitVariable(expr); + Iterable implicitFeatures = ecoreUtil.allFeatures(implicitType, expr); + if (Iterables.any(implicitFeatures, f -> f.getName().equals(s.getName()))) { + error( + "Ambiguous reference. `" + s.getName() + "` may either refer to `item -> " + s.getName() + "` or to the output variable.", + expr, + ROSETTA_SYMBOL_REFERENCE__SYMBOL + ); + } + } + } + if (expr.isExplicitArguments()) { + error( + "A variable may not be called", + expr, + ROSETTA_SYMBOL_REFERENCE__EXPLICIT_ARGUMENTS + ); + } + } + } + } + + @Check + public void checkExistsExpression(RosettaExistsExpression expr) { + if (expr.getModifier() == ExistsModifier.MULTIPLE || expr.getModifier() == ExistsModifier.SINGLE) { + isMultiCheck(expr.getArgument(), expr, ROSETTA_UNARY_OPERATION__ARGUMENT); + } + } + + private boolean mayBeEmpty(RType t) { + return t instanceof RDataType && ((RDataType) t).getAllAttributes().stream().allMatch(a -> a.getCardinality().getMinBound() == 0) || t instanceof RChoiceType; + } + @Check + public void checkOnlyExistsExpression(RosettaOnlyExistsExpression expr) { + if (expr.getArgs().size() > 0) { + for (RosettaExpression input : expr.getArgs()) { + RosettaNamed invalidMetaFeature = null; + EStructuralFeature structFeature = null; + if (input instanceof RosettaFeatureCall && ((RosettaFeatureCall)input).getFeature() instanceof RosettaMetaType) { + invalidMetaFeature = ((RosettaFeatureCall)input).getFeature(); + structFeature = ROSETTA_FEATURE_CALL__FEATURE; + } else if (input instanceof RosettaSymbolReference && ((RosettaSymbolReference)input).getSymbol() instanceof RosettaMetaType) { + invalidMetaFeature = ((RosettaSymbolReference)input).getSymbol(); + structFeature = ROSETTA_SYMBOL_REFERENCE__SYMBOL; + } + if (invalidMetaFeature != null) { + error("Invalid use of `only exists` on meta feature " + invalidMetaFeature.getName(), input, structFeature); + } + } + + RosettaExpression first = expr.getArgs().get(0); + RosettaExpression parent = exprHelper.getParentExpression(first); + for (int i=1; i seenValues = new HashSet<>(); + for (SwitchCase caseStatement : op.getCases()) { + RosettaEnumValue guard = caseStatement.getGuard().getEnumGuard(); + if (guard == null) { + error("Case should match an enum value of " + argumentType, caseStatement, SWITCH_CASE__GUARD); + } else { + if (!seenValues.add(guard)) { + error("Duplicate case " + guard.getName(), caseStatement, SWITCH_CASE__GUARD); + } + } + } + + if (op.getDefault() == null) { + List missingEnumValues = new ArrayList<>(argumentType.getAllEnumValues()); + missingEnumValues.removeAll(seenValues); + if (!missingEnumValues.isEmpty()) { + String missingValuesMsg = missingEnumValues.stream().map(v -> v.getName()).collect(Collectors.joining(", ")); + error("Missing the following cases: " + missingValuesMsg + ". Either provide all or add a default.", op, ROSETTA_OPERATION__OPERATOR); + } + } + } + private void checkBasicTypeSwitch(RBasicType argumentType, SwitchOperation op) { + // When the argument is a basic type: + // - all guards should be literal guards, + // - there are no duplicate cases, + // - all guards should be comparable to the input. + Set seenValues = new HashSet<>(); + RMetaAnnotatedType argumentTypeWithoutMeta = RMetaAnnotatedType.withEmptyMeta(argumentType); + for (SwitchCase caseStatement : op.getCases()) { + RosettaLiteral guard = caseStatement.getGuard().getLiteralGuard(); + if (guard == null) { + error("Case should match a literal of type " + argumentType, caseStatement, SWITCH_CASE__GUARD); + } else { + if (!seenValues.add(interpreter.interpret(guard))) { + error("Duplicate case", caseStatement, SWITCH_CASE__GUARD); + } + RMetaAnnotatedType conditionType = typeProvider.getRMetaAnnotatedType(guard); + if (!typeSystem.isComparable(conditionType, argumentTypeWithoutMeta)) { + error("Invalid case: " + notComparableMessage(conditionType, argumentTypeWithoutMeta), caseStatement, SWITCH_CASE__GUARD); + } + } + } + } + private void checkChoiceSwitch(RChoiceType argumentType, SwitchOperation op) { + // When the argument is a choice type: + // - all guards should be choice option guards, + // - all cases should be reachable, + // - all choice options should be covered. + Map includedOptions = new HashMap<>(); + for (SwitchCase caseStatement : op.getCases()) { + ChoiceOption guard = caseStatement.getGuard().getChoiceOptionGuard(); + if (guard == null) { + error("Case should match a choice option of type " + argumentType, caseStatement, SWITCH_CASE__GUARD); + } else { + RMetaAnnotatedType alreadyCovered = includedOptions.get(guard); + if (alreadyCovered != null) { + error("Case already covered by " + alreadyCovered, caseStatement, SWITCH_CASE__GUARD); + } else { + RMetaAnnotatedType guardType = typeProvider.getRTypeOfSymbol(guard); + includedOptions.put(guard, guardType); + RType valueType = guardType.getRType(); + if (valueType instanceof RChoiceType) { + ((RChoiceType)valueType).getAllOptions().forEach(it -> includedOptions.put(it.getEObject(), guardType)); + } + } + } + } + if (op.getDefault() == null) { + List missingOptions = new ArrayList<>(); + argumentType.getOwnOptions().forEach(opt -> missingOptions.add(opt.getType())); + for (RMetaAnnotatedType guard : new LinkedHashSet<>(includedOptions.values())) { + for (var i=0; i missingOptions.add(o.getType())); + } + } + } + } + if (!missingOptions.isEmpty()) { + String missingOptsMsg = missingOptions.stream() + .map(opt -> opt.toString()) + .collect(Collectors.joining(", ")); + error("Missing the following cases: " + missingOptsMsg + ". Either provide all or add a default.", op, ROSETTA_OPERATION__OPERATOR); + } + } + } +} diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaSimpleValidator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaSimpleValidator.xtend index f60a47cc5..749217e00 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaSimpleValidator.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaSimpleValidator.xtend @@ -7,7 +7,6 @@ import com.regnosys.rosetta.generator.util.RosettaFunctionExtensions import com.regnosys.rosetta.rosetta.ExternalAnnotationSource import com.regnosys.rosetta.rosetta.ParametrizedRosettaType import com.regnosys.rosetta.rosetta.RosettaAttributeReference -import com.regnosys.rosetta.rosetta.RosettaCallableWithArgs import com.regnosys.rosetta.rosetta.RosettaDocReference import com.regnosys.rosetta.rosetta.RosettaEnumSynonym import com.regnosys.rosetta.rosetta.RosettaEnumValueReference @@ -27,40 +26,6 @@ import com.regnosys.rosetta.rosetta.RosettaSynonymBody import com.regnosys.rosetta.rosetta.RosettaSynonymValueBase import com.regnosys.rosetta.rosetta.RosettaType import com.regnosys.rosetta.rosetta.RosettaTyped -import com.regnosys.rosetta.rosetta.expression.AsKeyOperation -import com.regnosys.rosetta.rosetta.expression.CanHandleListOfLists -import com.regnosys.rosetta.rosetta.expression.CardinalityModifier -import com.regnosys.rosetta.rosetta.expression.ClosureParameter -import com.regnosys.rosetta.rosetta.expression.ComparingFunctionalOperation -import com.regnosys.rosetta.rosetta.expression.ConstructorKeyValuePair -import com.regnosys.rosetta.rosetta.expression.DefaultOperation -import com.regnosys.rosetta.rosetta.expression.FilterOperation -import com.regnosys.rosetta.rosetta.expression.FlattenOperation -import com.regnosys.rosetta.rosetta.expression.HasGeneratedInput -import com.regnosys.rosetta.rosetta.expression.InlineFunction -import com.regnosys.rosetta.rosetta.expression.ListLiteral -import com.regnosys.rosetta.rosetta.expression.ListOperation -import com.regnosys.rosetta.rosetta.expression.MandatoryFunctionalOperation -import com.regnosys.rosetta.rosetta.expression.MapOperation -import com.regnosys.rosetta.rosetta.expression.ModifiableBinaryOperation -import com.regnosys.rosetta.rosetta.expression.ParseOperation -import com.regnosys.rosetta.rosetta.expression.ReduceOperation -import com.regnosys.rosetta.rosetta.expression.RosettaBinaryOperation -import com.regnosys.rosetta.rosetta.expression.RosettaConstructorExpression -import com.regnosys.rosetta.rosetta.expression.RosettaCountOperation -import com.regnosys.rosetta.rosetta.expression.RosettaExpression -import com.regnosys.rosetta.rosetta.expression.RosettaFeatureCall -import com.regnosys.rosetta.rosetta.expression.RosettaFunctionalOperation -import com.regnosys.rosetta.rosetta.expression.RosettaImplicitVariable -import com.regnosys.rosetta.rosetta.expression.RosettaOnlyExistsExpression -import com.regnosys.rosetta.rosetta.expression.RosettaOperation -import com.regnosys.rosetta.rosetta.expression.RosettaSymbolReference -import com.regnosys.rosetta.rosetta.expression.RosettaUnaryOperation -import com.regnosys.rosetta.rosetta.expression.SumOperation -import com.regnosys.rosetta.rosetta.expression.SwitchOperation -import com.regnosys.rosetta.rosetta.expression.ThenOperation -import com.regnosys.rosetta.rosetta.expression.ToStringOperation -import com.regnosys.rosetta.rosetta.expression.UnaryFunctionalOperation import com.regnosys.rosetta.rosetta.simple.Annotated import com.regnosys.rosetta.rosetta.simple.Annotation import com.regnosys.rosetta.rosetta.simple.AnnotationQualifier @@ -78,12 +43,10 @@ import com.regnosys.rosetta.types.CardinalityProvider import com.regnosys.rosetta.types.RAttribute import com.regnosys.rosetta.types.RDataType import com.regnosys.rosetta.types.REnumType -import com.regnosys.rosetta.types.RErrorType import com.regnosys.rosetta.types.RObjectFactory import com.regnosys.rosetta.types.RType import com.regnosys.rosetta.types.RosettaTypeProvider import com.regnosys.rosetta.types.TypeSystem -import com.regnosys.rosetta.types.TypeValidationUtil import com.regnosys.rosetta.types.builtin.RBasicType import com.regnosys.rosetta.types.builtin.RBuiltinTypeService import com.regnosys.rosetta.types.builtin.RRecordType @@ -117,17 +80,36 @@ import static com.regnosys.rosetta.rosetta.simple.SimplePackage.Literals.* import static com.regnosys.rosetta.validation.RosettaIssueCodes.* import static extension org.eclipse.emf.ecore.util.EcoreUtil.* -import static extension com.regnosys.rosetta.types.RMetaAnnotatedType.* import org.eclipse.emf.ecore.impl.EClassImpl -import com.regnosys.rosetta.rosetta.expression.RosettaConditionalExpression -import com.regnosys.rosetta.rosetta.RosettaExternalFunction import com.regnosys.rosetta.types.RChoiceType -import com.regnosys.rosetta.interpreter.RosettaInterpreter -import com.google.common.collect.Lists import java.util.Collection import org.eclipse.xtext.resource.IResourceDescriptions import com.regnosys.rosetta.types.RMetaAnnotatedType -import com.regnosys.rosetta.rosetta.RosettaMetaType +import com.regnosys.rosetta.rosetta.expression.RosettaFunctionalOperation +import com.regnosys.rosetta.rosetta.expression.ThenOperation +import com.regnosys.rosetta.rosetta.expression.RosettaOperation +import com.regnosys.rosetta.rosetta.expression.MandatoryFunctionalOperation +import com.regnosys.rosetta.rosetta.expression.RosettaUnaryOperation +import com.regnosys.rosetta.rosetta.expression.InlineFunction +import com.regnosys.rosetta.rosetta.expression.HasGeneratedInput +import com.regnosys.rosetta.rosetta.expression.RosettaImplicitVariable +import com.regnosys.rosetta.rosetta.expression.RosettaFeatureCall +import com.regnosys.rosetta.rosetta.expression.RosettaExpression +import com.regnosys.rosetta.rosetta.expression.ClosureParameter +import com.regnosys.rosetta.rosetta.expression.RosettaConstructorExpression +import com.regnosys.rosetta.rosetta.expression.ParseOperation +import com.regnosys.rosetta.rosetta.expression.ToStringOperation +import com.regnosys.rosetta.rosetta.expression.ListOperation +import com.regnosys.rosetta.rosetta.expression.UnaryFunctionalOperation +import com.regnosys.rosetta.rosetta.expression.FilterOperation +import com.regnosys.rosetta.rosetta.expression.MapOperation +import com.regnosys.rosetta.rosetta.expression.FlattenOperation +import com.regnosys.rosetta.rosetta.expression.ReduceOperation +import com.regnosys.rosetta.rosetta.expression.SumOperation +import com.regnosys.rosetta.rosetta.expression.ComparingFunctionalOperation +import com.regnosys.rosetta.rosetta.expression.AsKeyOperation +import com.regnosys.rosetta.rosetta.expression.ConstructorKeyValuePair +import com.regnosys.rosetta.rosetta.expression.CanHandleListOfLists // TODO: split expression validator // TODO: type check type call arguments @@ -147,29 +129,7 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { @Inject extension RBuiltinTypeService @Inject extension TypeSystem @Inject extension RosettaGrammarAccess - @Inject extension TypeValidationUtil @Inject extension RObjectFactory objectFactory - @Inject extension RosettaInterpreter - - @Check - def void checkOnlyExistsNotUsedOnMeta(RosettaOnlyExistsExpression op) { - val message = "Invalid use of `only exists` on meta feature" - - op.args - .filter(RosettaFeatureCall) - .filter[it.feature instanceof RosettaMetaType] - .forEach[ - error('''«message» «it.feature.name»''', it, ROSETTA_FEATURE_CALL__FEATURE) - ] - - op.args - .filter(RosettaSymbolReference) - .filter[it.symbol instanceof RosettaMetaType] - .forEach[ - error('''«message» «it.symbol.name»''', it, ROSETTA_SYMBOL_REFERENCE__SYMBOL) - ] - - } @Check def void deprecatedWarning(EObject object) { @@ -200,114 +160,6 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { warning(msg, owner, ref, index) } } - - @Check - def void checkSwitch(SwitchOperation op) { - if (op.argument.multi) { - error("Input to switch must be single cardinality", op.argument, null) - } - val argumentType = op.argument.RMetaAnnotatedType.RType.stripFromTypeAliases - if (argumentType instanceof REnumType) { - checkEnumSwitch(argumentType, op) - } else if (argumentType instanceof RBasicType) { - checkBasicTypeSwitch(argumentType, op) - } else if (argumentType instanceof RChoiceType) { - checkChoiceSwitch(argumentType, op) - } else { - error('''Type `«argumentType»` is not a valid switch argument type. Supported argument types are basic types, enumerations, and choice types.''', op, ROSETTA_UNARY_OPERATION__ARGUMENT) - } - } - - private def void checkEnumSwitch(REnumType argumentType, SwitchOperation op) { - // When the argument is an enum: - // - all guards should be enum guards, - // - there are no duplicate cases, - // - all enum values must be covered. - val seenValues = newHashSet - for (caseStatement : op.cases) { - if (caseStatement.guard.enumGuard === null) { - error('''Case should match an enum value of «argumentType»''', caseStatement, SWITCH_CASE__GUARD) - } else { - if (!seenValues.add(caseStatement.guard.enumGuard)) { - error('''Duplicate case «caseStatement.guard.enumGuard.name»''', caseStatement, SWITCH_CASE__GUARD) - } - } - } - - if (op.^default === null) { - val missingEnumValues = argumentType.allEnumValues.filter[!seenValues.contains(it)] - if (!missingEnumValues.empty) { - error('''Missing the following cases: «missingEnumValues.map[it.name].join(", ")». Either provide all or add a default.''', op, ROSETTA_OPERATION__OPERATOR) - } - } - } - private def void checkBasicTypeSwitch(RBasicType argumentType, SwitchOperation op) { - // When the argument is a basic type: - // - all guards should be literal guards, - // - there are no duplicate cases, - // - all guards should be comparable to the input. - val seenValues = newHashSet - for (caseStatement : op.cases) { - if (caseStatement.guard.literalGuard === null) { - error('''Case should match a literal of type «argumentType»''', caseStatement, SWITCH_CASE__GUARD) - } else { - if (!seenValues.add(caseStatement.guard.literalGuard.interpret)) { - error('''Duplicate case''', caseStatement, SWITCH_CASE__GUARD) - } - val conditionType = caseStatement.guard.literalGuard.RMetaAnnotatedType.RType - if (!conditionType.isComparable(argumentType)) { - error('''Invalid case: «argumentType.notComparableMessage(conditionType)»''', caseStatement, SWITCH_CASE__GUARD) - } - } - } - } - private def void checkChoiceSwitch(RChoiceType argumentType, SwitchOperation op) { - // When the argument is a choice type: - // - all guards should be choice option guards, - // - all cases should be reachable, - // - all choice options should be covered. - val Map includedOptions = newHashMap - for (caseStatement : op.cases) { - if (caseStatement.guard.choiceOptionGuard === null) { - error('''Case should match a choice option of type «argumentType»''', caseStatement, SWITCH_CASE__GUARD) - } else { - val guard = caseStatement.guard.choiceOptionGuard - val alreadyCovered = includedOptions.get(guard) - if (alreadyCovered !== null) { - error('''Case already covered by «alreadyCovered»''', caseStatement, SWITCH_CASE__GUARD) - } else { - val guardType = guard.RTypeOfSymbol - includedOptions.put(guard, guardType) - val valueType = guardType.RType - if (valueType instanceof RChoiceType) { - valueType.allOptions.forEach[includedOptions.put(it.EObject, guardType)] - } - } - } - } - if (op.^default === null) { - val missingOptions = Lists.newArrayList(argumentType.ownOptions.map[type]) - for (guard : includedOptions.values.toSet) { - for (var i=0; i «callable.name»` or to the output variable.''', - element, - ROSETTA_SYMBOL_REFERENCE__SYMBOL - ) - } - } - } - if (element.explicitArguments) { - error( - '''A variable may not be called.''', - element, - ROSETTA_SYMBOL_REFERENCE__EXPLICIT_ARGUMENTS - ) - } - } - } - } - @Check def void checkMergeSynonymAttributeCardinality(Attribute attribute) { for (syn : attribute.synonyms) { @@ -1025,7 +785,7 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { // check type val ruleType = rule.expression.RMetaAnnotatedType.RType - if (ruleType !== null && ruleType != MISSING && attrType !== null && attrType != MISSING && !ruleType.isSubtypeOf(attrType)) { + if (!ruleType.isSubtypeOf(attrType)) { val typeError = '''Type mismatch - report field «attr.name» has type «attrType.name» ''' + '''whereas the reporting rule «rule.name» has type «ruleType».''' error(typeError, ruleRef, ROSETTA_RULE_REFERENCE__REPORTING_RULE) @@ -1033,40 +793,6 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { } } - @Check - def checkExpressionCardinality(ModifiableBinaryOperation binOp) { - val leftCard = cardinality.isMulti(binOp.left) - val rightCard = cardinality.isMulti(binOp.right) - if (leftCard != rightCard) { - if (binOp.cardMod === CardinalityModifier.NONE) { - warning('''Comparison operator «binOp.operator» should specify 'all' or 'any' when comparing a list to a single value''', - binOp, ROSETTA_OPERATION__OPERATOR) - } - } else if (binOp.cardMod !== CardinalityModifier.NONE) { - warning('''«binOp.cardMod» is only aplicable when the sides have differing cardinality''', binOp, - ROSETTA_OPERATION__OPERATOR) - } - } - - @Check - def checkBinaryParamsRightTypes(RosettaBinaryOperation binOp) { - val resultMetaType = binOp.RMetaAnnotatedType - val resultType = resultMetaType.RType - if (resultType instanceof RErrorType) { - error(resultType.message, binOp, ROSETTA_OPERATION__OPERATOR) - } - } - - @Check - def checkDefaultOperationMatchingCardinality(DefaultOperation defOp) { - val leftCard = cardinality.isMulti(defOp.left) - val rightCard = cardinality.isMulti(defOp.right) - if (leftCard != rightCard) { - val typeError = "Cardinality mismatch - default operator requires both sides to have matching cardinality" - error(typeError, defOp, ROSETTA_OPERATION__OPERATOR) - } - } - @Check def checkFuncDispatchAttr(FunctionDispatch ele) { if (ele.attribute.isResolved && ele.attribute.typeCall.isResolved) { @@ -1206,16 +932,7 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { } @Check - def checkListLiteral(ListLiteral ele) { - val metaType = ele.RMetaAnnotatedType - val type = metaType.RType - if (type instanceof RErrorType) { - error('''All collection elements must have the same super type but types were «type.message»''', ele, null) - } - } - - @Check - def checkSynonyMapPath(RosettaMapPathValue ele) { + def checkSynonymMapPath(RosettaMapPathValue ele) { if (!ele.path.nullOrEmpty) { val invalidChar = checkPathChars(ele.path) if (invalidChar !== null) @@ -1233,14 +950,6 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { ele, ROSETTA_SYNONYM_VALUE_BASE__PATH) } } - - @Check - def checkCountOpArgument(RosettaCountOperation ele) { - if (ele.argument.isResolved) { - if (!cardinality.isMulti(ele.argument)) - error('''Count operation multiple cardinality argument.''', ele, ROSETTA_UNARY_OPERATION__ARGUMENT) - } - } @Check def checkParseOpArgument(ParseOperation ele) { @@ -1453,24 +1162,6 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { } } - @Check - def checkOnlyExistsPathsHaveCommonParent(RosettaOnlyExistsExpression e) { - val first = e.args.head - val parent = exprHelper.getParentExpression(first) - for (var i = 1; i < e.args.size; i++) { - val other = e.args.get(i) - val otherParent = exprHelper.getParentExpression(other) - if ((parent === null) !== (otherParent === null) || - parent !== null && otherParent !== null && !EcoreUtil2.equals(parent, otherParent)) { - if (otherParent !== null) { - error('''Only exists paths must have a common parent.''', otherParent, null) - } else { - error('''Only exists paths must have a common parent.''', other, null) - } - } - } - } - @Check def checkUnaryOperation(RosettaUnaryOperation e) { val receiver = e.argument @@ -1640,7 +1331,7 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { private def void checkBodyType(InlineFunction ref, RType type) { val bodyType = ref?.body?.getRMetaAnnotatedType?.RType - if (ref !== null && bodyType !== null && bodyType != MISSING && bodyType != type) { + if (ref !== null && bodyType !== null && bodyType != type) { error('''Expression must evaluate to a «type.name».''', ref, null) } } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaValidator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaValidator.xtend index 912311578..83ef324cb 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaValidator.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaValidator.xtend @@ -10,7 +10,7 @@ import org.eclipse.xtext.validation.ComposedChecks * * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#validation */ -@ComposedChecks(validators = #[RosettaSimpleValidator, StandaloneRosettaTypingValidator, EnumValidator, ChoiceValidator]) +@ComposedChecks(validators = #[RosettaSimpleValidator, StandaloneRosettaTypingValidator, EnumValidator, ChoiceValidator, ExpressionValidator]) class RosettaValidator extends AbstractRosettaValidator { diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/StandaloneRosettaTypingValidator.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/StandaloneRosettaTypingValidator.java index 5bd867483..49446e6cf 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/StandaloneRosettaTypingValidator.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/StandaloneRosettaTypingValidator.java @@ -16,19 +16,15 @@ package com.regnosys.rosetta.validation; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; import javax.inject.Inject; -import org.eclipse.emf.ecore.EPackage; import org.eclipse.xtext.validation.Check; -import org.eclipse.xtext.validation.EValidatorRegistrar; import com.regnosys.rosetta.rosetta.ExternalValueOperator; -import com.regnosys.rosetta.rosetta.RosettaCardinality; import com.regnosys.rosetta.rosetta.RosettaExternalClass; import com.regnosys.rosetta.rosetta.RosettaExternalRegularAttribute; import com.regnosys.rosetta.rosetta.RosettaExternalRuleSource; @@ -43,29 +39,24 @@ import com.regnosys.rosetta.types.RAttribute; import com.regnosys.rosetta.types.RChoiceType; import com.regnosys.rosetta.types.RDataType; -import com.regnosys.rosetta.types.RListType; import com.regnosys.rosetta.types.RObjectFactory; import com.regnosys.rosetta.types.TypeFactory; import com.regnosys.rosetta.types.TypeSystem; -import com.regnosys.rosetta.types.TypeValidationUtil; import com.regnosys.rosetta.types.builtin.RBuiltinTypeService; -import com.regnosys.rosetta.typing.validation.RosettaTypingCheckingValidator; import com.regnosys.rosetta.utils.ExternalAnnotationUtil; import static com.regnosys.rosetta.rosetta.expression.ExpressionPackage.Literals.*; import static com.regnosys.rosetta.rosetta.RosettaPackage.Literals.*; import static com.regnosys.rosetta.rosetta.simple.SimplePackage.Literals.*; -public class StandaloneRosettaTypingValidator extends RosettaTypingCheckingValidator { +// TODO: remove +public class StandaloneRosettaTypingValidator extends AbstractDeclarativeRosettaValidator { @Inject private TypeSystem ts; @Inject private TypeFactory tf; - @Inject - private TypeValidationUtil tu; - @Inject private RBuiltinTypeService builtins; @@ -75,32 +66,19 @@ public class StandaloneRosettaTypingValidator extends RosettaTypingCheckingValid @Inject private RObjectFactory objectFactory; - @Override - protected List getEPackages() { - List result = new ArrayList(); - result.add(EPackage.Registry.INSTANCE.getEPackage("http://www.rosetta-model.com/Rosetta")); - result.add(EPackage.Registry.INSTANCE.getEPackage("http://www.rosetta-model.com/RosettaSimple")); - result.add(EPackage.Registry.INSTANCE.getEPackage("http://www.rosetta-model.com/RosettaExpression")); - result.add(EPackage.Registry.INSTANCE.getEPackage("http://www.rosetta-model.com/RosettaTranslate")); - return result; - } - - @Override - public void register(EValidatorRegistrar registrar) { - } - /** * Xsemantics does not allow raising warnings. See https://github.com/eclipse/xsemantics/issues/149. */ @Check public void checkOnlyElement(RosettaOnlyElement e) { - RListType t = ts.inferType(e.getArgument()); - if (t != null) { - RosettaCardinality minimalConstraint = tf.createConstraint(1, 2); - if (!minimalConstraint.isSubconstraintOf(t.getConstraint())) { - warning(tu.notLooserConstraintMessage(minimalConstraint, t), e, ROSETTA_UNARY_OPERATION__ARGUMENT); - } - } + // TODO: restore +// RListType t = ts.inferType(e.getArgument()); +// if (t != null) { +// RosettaCardinality minimalConstraint = tf.createConstraint(1, 2); +// if (!minimalConstraint.isSubconstraintOf(t.getConstraint())) { +// warning(tu.notLooserConstraintMessage(minimalConstraint, t), e, ROSETTA_UNARY_OPERATION__ARGUMENT); +// } +// } } /** diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/condition/RosettaConditionTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/condition/RosettaConditionTest.xtend index 8adc2fba4..692610121 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/condition/RosettaConditionTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/condition/RosettaConditionTest.xtend @@ -37,11 +37,11 @@ class RosettaConditionTest { condition FeatureCallComparisonDecreasing: if bar exists - then bar -> before > bar -> after + then bar first -> before > bar first -> after condition BarFeatureCallGreaterThanLiteralZero: if bar exists - then bar -> after > 0 + then bar first -> after > 0 condition BazFeatureCallGreaterThanLiteralFive: if baz exists @@ -71,10 +71,10 @@ class RosettaConditionTest { val fooInstance = RosettaModelObject.cast(classes.createInstanceUsingBuilder('Foo', of('baz', bazInstance), of('bar', ImmutableList.of(barInstance)))) // FeatureCallComparisonDecreasing (success) - assertCondition(fooInstance, 'FooFeatureCallComparisonDecreasing', true, "if bar exists then bar -> before > bar -> after") + assertCondition(fooInstance, 'FooFeatureCallComparisonDecreasing', true, "if bar exists then bar first -> before > bar first -> after") // BarFeatureCallGreaterThanLiteralZero (success) - assertCondition(fooInstance, 'FooBarFeatureCallGreaterThanLiteralZero', true, "if bar exists then bar -> after > 0") + assertCondition(fooInstance, 'FooBarFeatureCallGreaterThanLiteralZero', true, "if bar exists then bar first -> after > 0") // BazFeatureCallGreaterThanLiteralZero (success) assertCondition(fooInstance, 'FooBazFeatureCallGreaterThanLiteralZero', true, "if baz exists then baz -> other > 0") @@ -90,10 +90,10 @@ class RosettaConditionTest { val fooInstance = RosettaModelObject.cast(classes.createInstanceUsingBuilder('Foo', of('baz', bazInstance), of('bar', ImmutableList.of(barInstance)))) // FeatureCallComparisonDecreasing (success) - assertCondition(fooInstance, 'FooFeatureCallComparisonDecreasing', true, "if bar exists then bar -> before > bar -> after") + assertCondition(fooInstance, 'FooFeatureCallComparisonDecreasing', true, "if bar exists then bar first -> before > bar first -> after") // BarFeatureCallGreaterThanLiteralZero (fail) - assertCondition(fooInstance, 'FooBarFeatureCallGreaterThanLiteralZero', false, "if bar exists then bar -> after > 0") + assertCondition(fooInstance, 'FooBarFeatureCallGreaterThanLiteralZero', false, "if bar exists then bar first -> after > 0") // BazFeatureCallGreaterThanLiteralZero (success) assertCondition(fooInstance, 'FooBazFeatureCallGreaterThanLiteralZero', true, "if baz exists then baz -> other > 0") @@ -111,13 +111,13 @@ class RosettaConditionTest { // FeatureCallComparisonDecreasing (fail) val conditionBarDescreasing = ValidationResult.cast(classes.runCondition(fooInstance, 'FooFeatureCallComparisonDecreasing')) assertFalse(conditionBarDescreasing.success) - assertThat(conditionBarDescreasing.definition, is("if bar exists then bar -> before > bar -> after")) + assertThat(conditionBarDescreasing.definition, is("if bar exists then bar first -> before > bar first -> after")) assertThat(conditionBarDescreasing.failureReason.orElse(""), is("all elements of paths [Foo->getBar[0]->getBefore] values [-10] are not > than all elements of paths [Foo->getBar[0]->getAfter] values [0]")) // BarFeatureCallGreaterThanLiteralZero (fail) val conditionBarGreaterThanZero = ValidationResult.cast(classes.runCondition(fooInstance, 'FooBarFeatureCallGreaterThanLiteralZero')) assertFalse(conditionBarGreaterThanZero.success) - assertThat(conditionBarGreaterThanZero.getDefinition(), is("if bar exists then bar -> after > 0")) + assertThat(conditionBarGreaterThanZero.getDefinition(), is("if bar exists then bar first -> after > 0")) assertThat(conditionBarGreaterThanZero.failureReason.orElse(""), is("all elements of paths [Foo->getBar[0]->getAfter] values [0] are not > than all elements of paths [BigDecimal] values [0]")) // BazFeatureCallGreaterThanLiteralZero (fail) diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/RosettaBinaryOperationTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/RosettaBinaryOperationTest.xtend index 91a6970ad..168a19cb3 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/RosettaBinaryOperationTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/RosettaBinaryOperationTest.xtend @@ -205,15 +205,15 @@ class RosettaBinaryOperationTest { inputs: foo Foo (1..1) output: result boolean (1..1) set result: - foo -> bar -> before > 5 - or ( foo -> baz -> other > 10 and foo -> bar -> after > 15 ) + foo -> bar first -> before > 5 + or ( foo -> baz -> other > 10 and foo -> bar first -> after > 15 ) or foo -> baz -> bazValue > 20 func FeatureCallGreatherThan: inputs: foo Foo (1..1) output: result boolean (1..1) set result: - foo -> bar -> before > foo -> bar2 -> before + foo -> bar first -> before > foo -> bar2 first -> before ««« Aliases diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/RosettaExistsExpressionTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/RosettaExistsExpressionTest.xtend index bcbf4c98c..8a260ce7b 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/RosettaExistsExpressionTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/RosettaExistsExpressionTest.xtend @@ -76,94 +76,94 @@ class RosettaExistsExpressionTest { inputs: foo Foo (1..1) output: result boolean (1..1) set result: - foo -> bar -> before only exists + foo -> bar first then before only exists func OnlyExistsMultiplePaths: inputs: foo Foo (1..1) output: result boolean (1..1) set result: - ( foo -> bar -> before, foo -> bar -> after ) only exists + foo -> bar first then ( before, after ) only exists func OnlyExistsPathWithScheme: inputs: foo Foo (1..1) output: result boolean (1..1) set result: - ( foo -> bar -> before, foo -> bar -> afterWithScheme ) only exists + foo -> bar first then ( before, afterWithScheme ) only exists func OnlyExistsBothPathsWithScheme: inputs: foo Foo (1..1) output: result boolean (1..1) set result: - ( foo -> bar -> beforeWithScheme, foo -> bar -> afterWithScheme ) only exists + foo -> bar first then ( beforeWithScheme, afterWithScheme ) only exists func OnlyExistsListMultiplePaths: inputs: foo Foo (1..1) output: result boolean (1..1) set result: - ( foo -> bar -> before, foo -> bar -> afterList ) only exists + foo -> bar first then ( before, afterList ) only exists func OnlyExistsListPathWithScheme: inputs: foo Foo (1..1) output: result boolean (1..1) set result: - ( foo -> bar -> before, foo -> bar -> afterListWithScheme ) only exists + foo -> bar first then ( before, afterListWithScheme ) only exists func OnlyExistsListBothPathsWithScheme: inputs: foo Foo (1..1) output: result boolean (1..1) set result: - ( foo -> bar -> beforeListWithScheme, foo -> bar -> afterListWithScheme ) only exists + foo -> bar first then ( beforeListWithScheme, afterListWithScheme ) only exists -««« TODO tests compilation only, add unit test +«««««« TODO tests compilation only, add unit test func MultipleSeparateOr_NoAliases_Exists: inputs: foo Foo (1..1) output: result boolean (1..1) set result: foo -> bar -> before exists or foo -> bar -> after exists -««« TODO tests compilation only, add unit test +«««««« TODO tests compilation only, add unit test func MultipleOr_NoAliases_Exists: inputs: foo Foo (1..1) output: result boolean (1..1) set result: foo -> bar -> before exists or foo -> bar -> after exists or foo -> baz -> other exists -««« TODO tests compilation only, add unit test +«««««« TODO tests compilation only, add unit test func MultipleOrBranchNode_NoAliases_Exists: inputs: foo Foo (1..1) output: result boolean (1..1) set result: foo -> bar exists or foo -> baz exists -««« TODO tests compilation only, add unit test +«««««« TODO tests compilation only, add unit test func MultipleAnd_NoAliases_Exists: inputs: foo Foo (1..1) output: result boolean (1..1) set result: foo -> bar -> before exists and foo -> bar -> after exists and foo -> baz -> other exists -««« TODO tests compilation only, add unit test +«««««« TODO tests compilation only, add unit test func MultipleOrAnd_NoAliases_Exists: inputs: foo Foo (1..1) output: result boolean (1..1) set result: foo -> bar -> before exists or ( foo -> bar -> after exists and foo -> baz -> other exists ) -««« TODO tests compilation only, add unit test +«««««« TODO tests compilation only, add unit test func MultipleOrAnd_NoAliases_Exists2: inputs: foo Foo (1..1) output: result boolean (1..1) set result: (foo -> bar -> before exists and foo -> bar -> after exists) or foo -> baz -> other exists or foo -> baz -> bazValue exists -««« TODO tests compilation only, add unit test +«««««« TODO tests compilation only, add unit test func MultipleOrAnd_NoAliases_Exists3: inputs: foo Foo (1..1) output: result boolean (1..1) set result: (foo -> bar -> before exists or foo -> bar -> after exists) or (foo -> baz -> other exists and foo -> baz -> bazValue exists) -««« TODO tests compilation only, add unit test +«««««« TODO tests compilation only, add unit test func MultipleExistsWithOrAnd: inputs: foo Foo (1..1) output: result boolean (1..1) diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/function/FunctionGeneratorHelper.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/function/FunctionGeneratorHelper.xtend index f05354d29..2be9dd93e 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/function/FunctionGeneratorHelper.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/function/FunctionGeneratorHelper.xtend @@ -21,6 +21,7 @@ import com.regnosys.rosetta.generator.java.RosettaJavaPackages.RootPackage import java.lang.reflect.InvocationTargetException import javax.inject.Inject import com.regnosys.rosetta.utils.ModelIdProvider +import com.rosetta.util.DottedPath class FunctionGeneratorHelper { @@ -42,7 +43,10 @@ class FunctionGeneratorHelper { } def createFunc(Map> classes, String funcName) { - injector.getInstance(classes.get(rootPackage.functions + '.' + funcName)) as RosettaFunction + createFunc(classes, funcName, rootPackage.functions) + } + def createFunc(Map> classes, String funcName, DottedPath packageName) { + injector.getInstance(classes.get(packageName + '.' + funcName)) as RosettaFunction } def invokeFunc(RosettaFunction func, Class resultClass, Object... inputs) { diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/function/FunctionGeneratorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/function/FunctionGeneratorTest.xtend index 143fa96c0..087382bdd 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/function/FunctionGeneratorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/function/FunctionGeneratorTest.xtend @@ -43,6 +43,23 @@ class FunctionGeneratorTest { @Inject extension ModelHelper @Inject extension ValidationTestHelper + @Test + def void reportingRuleSupportsRecursion() { + val code = ''' + reporting rule Fac from int: + if item = 1 + then 1 + else item * Fac(item - 1) + '''.generateCode + + code.compileToClasses + val classes = code.compileToClasses + + val facRule = classes.createFunc("FacRule", rootPackage.reports) + + assertEquals(120, facRule.invokeFunc(Integer, #[5])) + } + @Test def void testCanPassMetaFromOutputOfFunctionCall() { val code = ''' @@ -1214,42 +1231,6 @@ class FunctionGeneratorTest { assertFalse(testOneOf.invokeFunc(Boolean, #[b2])) } - @Test - def void onlyExistsOnList() { - val code = ''' - type A: - a1 string (0..1) - a2 string (0..1) - a3 boolean (0..1) - - func TestOnlyExists: - inputs: - a A (0..*) - output: - result boolean (1..1) - - set result: - a -> a1 only exists - '''.generateCode - - val classes = code.compileToClasses - - val a1 = classes.createInstanceUsingBuilder("A", #{ - "a1" -> "some value" - }) - val a2 = classes.createInstanceUsingBuilder("A", #{ - "a1" -> "other value" - }) - val a3 = classes.createInstanceUsingBuilder("A", #{ - "a1" -> "some value", - "a2" -> "other value" - }) - - val testOnlyExists = classes.createFunc("TestOnlyExists") - assertTrue(testOnlyExists.invokeFunc(Boolean, #[List.of(a1, a2)])) - assertFalse(testOnlyExists.invokeFunc(Boolean, #[List.of(a1, a2, a3)])) - } - @Test def void testDeepPathOperatorWithMeta() { val code = ''' @@ -3508,7 +3489,7 @@ class FunctionGeneratorTest { top1-> foo disjoint top2 -> bar '''.parseRosetta - model.assertError(ROSETTA_DISJOINT_EXPRESSION, null, "Incompatible types: cannot use operator 'disjoint' with Foo and string.") + model.assertError(ROSETTA_DISJOINT_EXPRESSION, null, "Types `Foo` and `string` are not comparable") } @Test @@ -3528,14 +3509,14 @@ class FunctionGeneratorTest { top1 Top (1..1) top2 Top (1..1) - output: result int (1..1) + output: result boolean (1..1) set result: top1 -> foo and top2 -> foo '''.parseRosetta - model.assertError(SimplePackage.Literals.OPERATION, RosettaIssueCodes.TYPE_ERROR, - "Left hand side of 'and' expression must be boolean") + model.assertError(LOGICAL_OPERATION, null, + "Expected type `boolean`, but got `Foo` instead") } @Test @@ -3977,7 +3958,7 @@ class FunctionGeneratorTest { type Bar: num number (0..1) - zap Zap (1..1) + zap Zap (1..2) enum Zap: A B C diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/rule/RosettaRuleGeneratorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/rule/RosettaRuleGeneratorTest.xtend index 32a113b32..308ddf9c4 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/rule/RosettaRuleGeneratorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/rule/RosettaRuleGeneratorTest.xtend @@ -5,7 +5,6 @@ import com.google.inject.Injector import com.regnosys.rosetta.tests.RosettaInjectorProvider import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper import com.regnosys.rosetta.tests.util.ModelHelper -import com.regnosys.rosetta.validation.RosettaIssueCodes import java.util.Map import org.eclipse.xtext.testing.InjectWith import org.eclipse.xtext.testing.extensions.InjectionExtension @@ -1131,8 +1130,8 @@ class RosettaRuleGeneratorTest { '''.toString .replace('\r', "") .parseRosetta - .assertError(ROSETTA_SYMBOL_REFERENCE, RosettaIssueCodes.TYPE_ERROR, - "Expected type 'Foo' but was 'Bar'") + .assertError(ROSETTA_SYMBOL_REFERENCE, null, + "Expected type `Foo`, but got `Bar` instead") } diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaExpressionsTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaExpressionsTest.xtend index b044a7329..e5d67e6a8 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaExpressionsTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaExpressionsTest.xtend @@ -99,7 +99,7 @@ class RosettaExpressionsTest { output: result boolean (1..1) set result: test -> one + test -> two = 42 - '''.parseRosetta.assertError(ARITHMETIC_OPERATION, null, "Incompatible types: cannot use operator '+' with date and date.") + '''.parseRosetta.assertError(ARITHMETIC_OPERATION, null, "Expected type `time`, but got `date` instead") } /** diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaParsingTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaParsingTest.xtend index 865d6d3d6..89cb66e87 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaParsingTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaParsingTest.xtend @@ -185,7 +185,7 @@ class RosettaParsingTest { def void testDefaultIncompatibleTypesReturnsError() { "a default 2" .parseExpression(#["a string (1..1)"]) - .assertError(DEFAULT_OPERATION, null, "Incompatible types: cannot use operator 'default' with string and int.") + .assertError(DEFAULT_OPERATION, null, "Types `string` and `int` do not have a common supertype") } @Test @@ -195,13 +195,6 @@ class RosettaParsingTest { .assertNoIssues } - @Test - def void testDefaultIncompatibleCardinalityReturnsError() { - "a default b" - .parseExpression(#["a string (1..1)", "b string (1..*)"]) - .assertError(DEFAULT_OPERATION, null, "Cardinality mismatch - default operator requires both sides to have matching cardinality") - } - @Test def void testOnlyExistsInsideFunctionalOperation() { ''' diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/types/RosettaTypeProviderTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/types/RosettaTypeProviderTest.xtend index 4df92fee6..634f98bbb 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/types/RosettaTypeProviderTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/types/RosettaTypeProviderTest.xtend @@ -2,6 +2,7 @@ package com.regnosys.rosetta.types import com.regnosys.rosetta.rosetta.expression.RosettaBinaryOperation import com.regnosys.rosetta.rosetta.expression.RosettaContainsExpression +import com.regnosys.rosetta.rosetta.simple.Data import com.regnosys.rosetta.rosetta.simple.Function import com.regnosys.rosetta.tests.RosettaInjectorProvider import com.regnosys.rosetta.tests.util.ModelHelper @@ -13,14 +14,643 @@ import org.junit.jupiter.api.^extension.ExtendWith import static org.junit.jupiter.api.Assertions.* import org.eclipse.xtext.testing.validation.ValidationTestHelper import javax.inject.Inject +import com.regnosys.rosetta.tests.util.ExpressionParser +import com.regnosys.rosetta.types.builtin.RBuiltinTypeService +import com.regnosys.rosetta.rosetta.RosettaModel +import com.regnosys.rosetta.rosetta.expression.RosettaExpression +import java.util.List +import com.regnosys.rosetta.rosetta.expression.MapOperation +import com.regnosys.rosetta.rosetta.expression.ArithmeticOperation +import static extension com.regnosys.rosetta.types.RMetaAnnotatedType.* +import java.util.Optional +import java.math.BigInteger +import java.math.BigDecimal +import org.eclipse.xtext.serializer.ISerializer +import com.regnosys.rosetta.rosetta.RosettaEnumeration +import com.regnosys.rosetta.rosetta.expression.LogicalOperation +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.api.TestInstance.Lifecycle @ExtendWith(InjectionExtension) @InjectWith(RosettaInjectorProvider) +@TestInstance(Lifecycle.PER_CLASS) class RosettaTypeProviderTest { @Inject extension RosettaTypeProvider + @Inject extension CardinalityProvider @Inject extension ModelHelper modelHelper @Inject extension ValidationTestHelper + @Inject extension ExpressionParser + @Inject extension TypeFactory + @Inject extension ISerializer + @Inject extension RObjectFactory + @Inject extension TypeSystem + @Inject extension RBuiltinTypeService builtins + + RMetaAttribute SCHEME + @BeforeAll + def void setup() { + SCHEME = new RMetaAttribute("scheme", UNCONSTRAINED_STRING, null) + } + + private def void assertIsValidWithType(CharSequence expr, RMetaAnnotatedType expectedType, boolean expectedIsMulti, List context, String... attributes) { + assertIsValidWithType(expr.parseExpression(context, attributes), expectedType, expectedIsMulti) + } + private def void assertIsValidWithType(CharSequence expr, RMetaAnnotatedType expectedType, boolean expectedIsMulti, List context) { + assertIsValidWithType(expr.parseExpression(context), expectedType, expectedIsMulti) + } + private def void assertIsValidWithType(CharSequence expr, RMetaAnnotatedType expectedType, boolean expectedIsMulti, String... attributes) { + assertIsValidWithType(expr.parseExpression(attributes), expectedType, expectedIsMulti) + } + private def void assertIsValidWithType(CharSequence expr, RMetaAnnotatedType expectedType, boolean expectedIsMulti) { + assertIsValidWithType(expr, expectedType, expectedIsMulti) + } + private def void assertIsValidWithType(RosettaExpression expr, RMetaAnnotatedType expectedType, boolean expectedIsMulti) { + expr.assertNoIssues + val actual = expr.RMetaAnnotatedType + + assertEquals(expectedType, actual, "Expression: " + expr.serialize) + if (expectedIsMulti) { + assertTrue(expr.isMulti, "Expected multi cardinality. Expression: " + expr.serialize) + } else { + assertFalse(expr.isMulti, "Expected single cardinality. Expression: " + expr.serialize) + } + } + + @Test + def void testLiteralTypeInference() { + 'False'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false) + '"Some string"'.assertIsValidWithType(stringWithNoMeta(11, 11), false) + '3.14'.assertIsValidWithType(numberWithNoMeta(3, 2, "3.14", "3.14"), false) + '1'.assertIsValidWithType(intWithNoMeta(1, "1", "1"), false) + 'empty'.assertIsValidWithType(NOTHING_WITH_NO_META, false) + } + + @Test + def void testVariableTypeInference() { + val context = ''' + func TestVar: + output: result number (1..4) + alias c: if True then 42 else -1/12 + add result: + c + + func TestImplicitVar: + output: result int (3..3) + add result: + [1, 2, 3] extract item + 1 + '''.parseRosettaWithNoIssues + 'a'.assertIsValidWithType(UNCONSTRAINED_INT.withMeta(#[SCHEME]), true, #["a int (2..4) [metadata scheme]"]) + 'b'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false, #["b boolean (1..1)"]) + context.elements.get(0) as Function => [operations.head.expression.assertIsValidWithType(UNCONSTRAINED_NUMBER_WITH_NO_META, false)] + + context.elements.get(1) as Function => [operations.head.expression as MapOperation => [ + function.body as ArithmeticOperation => [ + left.assertIsValidWithType(intWithNoMeta(1, "1", "3"), false) + ] + ]]; + } + + @Test + def void testLogicalOperationTypeInference() { + 'True or False'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false) + 'True and False'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false) + } + + @Test + def void testLogicalOperationTypeChecking() { + '1 or False' + .parseExpression + .assertError(null, "Expected type `boolean`, but got `int` instead.") + 'True or 3.14' + .parseExpression + .assertError(null, "Expected type `boolean`, but got `number` instead.") + 'a or False' + .parseExpression(#['a boolean (1..2)']) + .assertError(null, "aaaaa") + } + + @Test + def void testEqualityOperationTypeInference() { + '[2, 3] = [6.0, 7, 8]'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false) + '[2, 3] <> [6.0, 7, 8]'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false) + '[1, 3] all = 5.0'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false) + 'empty all <> 5.0'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false) + '[1, 3] any = 5.0'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false) + + 'a = 1'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false, #['a int (0..1)']) + } + + @Test + def void testEqualityOperationTypeChecking() { + '1 = True' + .parseExpression + .assertError(null, "Types `int` and `boolean` are not comparable.") + 'empty = True' + .parseExpression + .assertError(null, "Cannot compare an empty value to a single value, as they cannot be of the same length. Perhaps you forgot to write `all` or `any` in front of the operator?") + '[1, 2] = [3, 4, 5]' + .parseExpression + .assertError(null, "Cannot compare a list with 2 items to a list with 3 items, as they cannot be of the same length.") + '[1, 2] <> [True, False, False]' + .parseExpression + .assertError(null, "Types `int` and `boolean` are not comparable.") + + '1 = True' + .parseExpression + .assertError(null, "Types `int` and `boolean` are not comparable.") + '[1, 3] any <> a' + .parseExpression(#['a int (1..2)']) + .assertError(null, "aaaaaaaaaa") + '[1, 2] all = empty' + .parseExpression + .assertError(null, "Expected a single value, but got an empty value instead.") + 'empty any = empty' + .parseExpression + .assertError(null, "Expected a single value, but got an empty value instead.") + '[1, 2] all = [1, 2]' + .parseExpression + .assertError(null, "Expected a single value, but got a list with 2 items instead.") + '5 any <> [1, 2]' + .parseExpression + .assertError(null, "Expected a single value, but got a list with 2 items instead. Perhaps you meant to swap the left and right operands?") + '[3.0] any <> 5' + .parseExpression + .assertError(null, "The cardinality operator `any` is redundant when comparing two single values.") + } + + // TODO: test arithmetic and comparisons with dates/times/etc + @Test + def void testArithmeticOperationTypeInference() { + '3 + 4'.assertIsValidWithType(intWithNoMeta(Optional.empty, Optional.of(BigInteger.valueOf(7)), Optional.of(BigInteger.valueOf(7))), false) + '3.0 + 4'.assertIsValidWithType(numberWithNoMeta(Optional.empty, Optional.of(1), Optional.of(new BigDecimal("7")), Optional.of(new BigDecimal("7")), Optional.empty), false) + '3 + 4.0'.assertIsValidWithType(numberWithNoMeta(Optional.empty, Optional.of(1), Optional.of(new BigDecimal("7")), Optional.of(new BigDecimal("7")), Optional.empty), false) + '3.0 + 4.0'.assertIsValidWithType(numberWithNoMeta(Optional.empty, Optional.of(1), Optional.of(new BigDecimal("7")), Optional.of(new BigDecimal("7")), Optional.empty), false) + '"ab" + "cd"'.assertIsValidWithType(stringWithNoMeta(4, 4), false) + + '3 - 4'.assertIsValidWithType(intWithNoMeta(Optional.empty, Optional.of(BigInteger.valueOf(-1)), Optional.of(BigInteger.valueOf(-1))), false) + '3 - 4.0'.assertIsValidWithType(numberWithNoMeta(Optional.empty, Optional.of(1), Optional.of(new BigDecimal("-1")), Optional.of(new BigDecimal("-1")), Optional.empty), false) + + '3 * 4'.assertIsValidWithType(intWithNoMeta(Optional.empty, Optional.of(BigInteger.valueOf(12)), Optional.of(BigInteger.valueOf(12))), false) + '3.0 * 4'.assertIsValidWithType(numberWithNoMeta(Optional.empty, Optional.of(1), Optional.of(new BigDecimal("12")), Optional.of(new BigDecimal("12")), Optional.empty), false) + + '3 / 4'.assertIsValidWithType(UNCONSTRAINED_NUMBER_WITH_NO_META, false) + } + + @Test + def void testArithemticOperationTypeChecking() { + '[1, 2] + 3' + .parseExpression + .assertError(null, "Expected a single value, but got a list with 2 items instead.") + 'empty - 3' + .parseExpression + .assertError(null, "Expected a single value, but got an empty value instead.") + '1.5 * False' + .parseExpression + .assertError(null, "Expected type `number`, but got `boolean` instead.") + '"ab" + 3' + .parseExpression + .assertError(null, "Expected arguments to be either both a `string` or both a `number`, but got `string` and `int` instead.") + 'a + 5' + .parseExpression(#['a int (1..2)']) + .assertError(null, "aaaaaaaaaaa") + } + + @Test + def void testComparisonOperationTypeInference() { + '1 < 2'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false) + '3 > 3.14'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false) + '-5.1 <= 42'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false) + '-3.14 >= 3.14'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false) + + '[1, 2] any < 5'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false) + 'empty all > 5'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false) + } + + @Test + def void testComparisonOperationTypeChecking() { + // TODO: support date, zonedDateTime and `time`? + '[1, 2] < 3' + .parseExpression + .assertError(null, "Expected a single value, but got a list with 2 items instead.") + 'empty > 3' + .parseExpression + .assertError(null, "Expected a single value, but got an empty value instead.") + '1.5 <= False' + .parseExpression + .assertError(null, "Expected type `number`, but got `boolean` instead.") + + 'a < 5' + .parseExpression(#['a int (1..2)']) + .assertError(null, "aaaaa") + '[1, 2] any < a' + .parseExpression(#['a int (1..2)']) + .assertError(null, "aaaaa") + '[1, 2] all >= empty' + .parseExpression + .assertError(null, "Expected a single value, but got an empty value instead.") + 'empty any < empty' + .parseExpression + .assertError(null, "Expected a single value, but got an empty value instead.") + '[1, 2] all > [1, 2]' + .parseExpression + .assertError(null, "Expected a single value, but got a list with 2 items instead.") + '5 any <= [1, 2]' + .parseExpression + .assertError(null, "Expected a single value, but got a list with 2 items instead. Perhaps you meant to swap the left and right operands?") + '5 all >= 1' + .parseExpression + .assertError(null, "The cardinality operator `all` is redundant when comparing two single values.") + } + + @Test + def void testConditionalExpressionTypeInference() { + 'if True then [1, 2] else [3.0, 4.0, 5.0, 6.0]'.assertIsValidWithType(numberWithNoMeta(2, 1, "1", "6"), true); + } + + @Test + def void testConditionalExpressionTypeChecking() { + 'if [True, False] then 1 else 2' + .parseExpression + .assertError(null, "Expected a single value, but got a list with 2 items instead.") + 'if empty then 1 else 2' + .parseExpression + .assertError(null, "Expected a single value, but got an empty value instead.") + 'if True then 1 else False' + .parseExpression + .assertError(null, "Types `int` and `boolean` do not have a common supertype.") + 'if True then [1, 2, 3] else [False, True]' + .parseExpression + .assertError(null, "Types `int` and `boolean` do not have a common supertype.") + } + + @Test + def void testListLiteralTypeInference() { + '[]'.assertIsValidWithType(NOTHING_WITH_NO_META, false); + '[2, 4.5, 7, -3.14]'.assertIsValidWithType(numberWithNoMeta(3, 2, "-3.14", "7"), true); + '[2, [1, 2], -3.14]'.assertIsValidWithType(numberWithNoMeta(3, 2, "-3.14", "2"), true); + } + + @Test + def void testListLiteralTypeChecking() { + '[1, True]' + .parseExpression + .assertError(null, "Elements do not have a common supertype: `int`, `boolean`.") + } + + @Test + def void testFunctionCallTypeInference() { + val context = ''' + func SomeFunc: + inputs: + a int (1..1) + b boolean (2..4) + output: result number (3..5) + add result: + [1.0, 2.0, 3.0] + '''.parseRosettaWithNoIssues + 'SomeFunc(42, [True, False, True])'.assertIsValidWithType(UNCONSTRAINED_NUMBER_WITH_NO_META, true, #[context]) + } + + @Test + def void testFunctionCallTypeChecking() { + val context = ''' + namespace test + + func SomeFunc: + inputs: + a int (1..1) + b boolean (2..4) + output: result int (1..1) + set result: + 42 + + func TestParamNumber: + output: result int (1..1) + set result: + SomeFunc(1, [False, True], True) + + func TestParamType: + output: result int (1..1) + set result: + SomeFunc(1, [2, 3]) + + func TestParamCardinality: + output: result int (1..1) + set result: + SomeFunc(1, [False, True, False, False, True]) + '''.parseRosettaWithNoIssues + + 'SomeFunc(1, [False, True], True)' + .parseExpression(#[context]) + .assertError(null, "Expected 2 arguments, but got 3 instead."); + 'SomeFunc(1, [2, 3])' + .parseExpression(#[context]) + .assertError(null, "Expected type `boolean`, but got `int` instead."); + 'SomeFunc(1, [False, True, False, False, True])' + .parseExpression(#[context]) + .assertError(null, "Expected a list with 2 to 4 items, but got a list with 5 items instead."); + } + + @Test + def void testProjectionTypeInference() { + val context = ''' + namespace test + + type A: + x int (1..1) + y number (0..*) + z boolean (3..7) + '''.parseRosettaWithNoIssues + 'a -> x'.assertIsValidWithType(UNCONSTRAINED_INT_WITH_NO_META, false, #[context], #['a A (1..1)']) + 'a -> y'.assertIsValidWithType(UNCONSTRAINED_NUMBER_WITH_NO_META, true, #[context], #['a A (1..1)']) + 'a -> z'.assertIsValidWithType(BOOLEAN_WITH_NO_META, true, #[context], #['a A (1..1)']) + 'a -> x'.assertIsValidWithType(UNCONSTRAINED_INT_WITH_NO_META, true, #[context], #['a A (2..5)']) + 'a -> y'.assertIsValidWithType(UNCONSTRAINED_NUMBER_WITH_NO_META, true, #[context], #['a A (1..1)']) + 'a -> z'.assertIsValidWithType(BOOLEAN_WITH_NO_META, true, #[context], #['a A (1..1)']) + } + + @Test + def void testEnumTypeInference() { + val context = ''' + namespace test + + enum A: + V1 + V2 + + func Test: + output: result A (1..1) + set result: + A -> V1 + '''.parseRosettaWithNoIssues + val A = (context.elements.get(0) as RosettaEnumeration).buildREnumType; + 'A -> V1'.assertIsValidWithType(A.withEmptyMeta, false, #[context]) + } + + @Test + def void testExistsTypeInference() { + 'a exists'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false, #['a int (0..1)']); + 'a exists'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false, #['a int (0..3)']); + } + + @Test + def void testExistsTypeChecking() { + 'empty exists' + .parseExpression + .assertError(null, "Expected an optional value, but got an empty value instead.") + '42 exists' + .parseExpression + .assertError(null, "Expected an optional value, but got a single value instead.") + '(if True then 42 else [1, 2, 3, 4, 5]) exists' + .parseExpression + .assertError(null, "Expected an optional value, but got a list with 1 to 5 items instead.") + } + + @Test + def void testAbsentTypeInference() { + 'a is absent'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false, #['a int (0..1)']); + 'a is absent'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false, #['a int (0..3)']); + } + + @Test + def void testAbsentTypeChecking() { + 'empty is absent' + .parseExpression + .assertError(null, "Expected an optional value, but got an empty value instead.") + '42 is absent' + .parseExpression + .assertError(null, "Expected an optional value, but got a single value instead.") + '(if True then 42 else [1, 2, 3, 4, 5]) is absent' + .parseExpression + .assertError(null, "Expected an optional value, but got a list with 1 to 5 items instead.") + } + + @Test + def void testCountTypeInference() { + val positiveInt = intWithNoMeta(Optional.empty, Optional.of(BigInteger.ZERO), Optional.empty) + 'empty count'.assertIsValidWithType(positiveInt, false); + '42 count'.assertIsValidWithType(positiveInt, false); + '[1, 2, 3] count'.assertIsValidWithType(positiveInt, false); + '(if True then empty else [1, 2, 3]) count'.assertIsValidWithType(positiveInt, false); + } + + @Test + def void testOnlyExistsTypeInference() { + val context = ''' + namespace test + + type A: + x int (0..1) + y number (0..3) + z boolean (0..*) + + condition C: + x only exists and (x, y) only exists + '''.parseRosettaWithNoIssues; + + (context.elements.get(0) as Data).conditions.head.expression as LogicalOperation => [ + left.assertIsValidWithType(BOOLEAN_WITH_NO_META, false) + right.assertIsValidWithType(BOOLEAN_WITH_NO_META, false) + ] + 'a -> x only exists'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false, #[context], #['a A (1..1)']) + '(a -> x, a -> y) only exists'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false, #[context], #['a A (1..1)']) + } + + @Test + def void testOnlyExistsTypeChecking() { + val model = ''' + namespace test + + type Foo: + bar int (1..1) + baz boolean (0..1) + + condition X: + baz only exists + + type A: + x Foo (0..1) + y number (0..3) + z boolean (0..*) + + condition C1: + (x -> baz, y) only exists and (y, x -> baz) only exists + condition C2: + (x, x) only exists and (x -> baz, x -> baz) only exists + + func Test: + inputs: + a A (1..1) + foo Foo (1..1) + b A (2..3) + c A (0..1) + output: result boolean (0..*) + add result: + b -> x only exists + add result: + c only exists + add result: + (a -> x -> baz, a -> x) only exists + add result: + foo -> baz only exists + '''.parseRosetta; + + (model.elements.get(0) as Data).conditions.head.expression + .assertError(null, "The `only exists` operator is not applicable to instances of `Foo`."); + + (model.elements.get(1) as Data).conditions => [ + get(0).expression as LogicalOperation => [ + left.assertError(null, "All parent paths must be equal.") + right.assertError(null, "All parent paths must be equal.") + ] + get(1).expression as LogicalOperation => [ + left.assertError(null, "Duplicate attribute.") + right.assertError(null, "Duplicate attribute.") + ] + ] + + (model.elements.get(2) as Function).operations => [ + get(0).expression.assertError(null, "Expected a single value, but got a list with 2 to 3 items instead.") + get(1).expression.assertError(null, "Object must have a parent object.") + get(2).expression.assertError(null, "All parent paths must be equal.") + get(3).expression.assertError(null, "The `only exists` operator is not applicable to instances of `Foo`.") + ] + } + + @Test + def void testOnlyElementTypeInference() { + '(if True then 0 else [1, 2]) only-element'.assertIsValidWithType(intWithNoMeta(1, "0", "2"), false); + '(if True then empty else [True, False]) only-element'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false); + '(if True then 0 else [1, 2, 3, 42.0]) only-element'.assertIsValidWithType(numberWithNoMeta(3, 1, "0", "42.0"), false); + } + + @Test + def void testOnlyElementTypeChecking() { + 'empty only-element' + .parseExpression + .assertWarning(null, "Expected a list with 1 to 2 items, but got an empty value instead.") + '42 only-element' + .parseExpression + .assertWarning(null, "Expected a list with 1 to 2 items, but got a single value instead.") + '[1, 2] only-element' + .parseExpression + .assertWarning(null, "Expected a list with 1 to 2 items, but got a list with 2 items instead.") + '(if True then empty else 42) only-element' + .parseExpression + .assertWarning(null, "Expected a list with 1 to 2 items, but got an optional value instead.") + } + + @Test + def void testTypeAliasJoin() { + val model = ''' + namespace test + + typeAlias maxNString(n int): string(minLength: 1, maxLength: n) + typeAlias max3String: maxNString(n: 3) + typeAlias max4String: maxNString(n: 4) + + func Test: + inputs: + s1 max3String (1..1) + s2 max4String (1..1) + s3 maxNString(n: 4) (1..1) + output: result string (0..*) + add result: if True then s1 else s2 + add result: if True then s2 else s2 + add result: if True then s2 else s3 + '''.parseRosettaWithNoIssues + model.elements.last as Function => [ + val max4String = inputs.get(1).typeCall.typeCallToRType.withEmptyMeta + val maxNString = inputs.get(2).typeCall.typeCallToRType.withEmptyMeta + + operations => [ + get(0).expression.assertIsValidWithType(maxNString, false) + get(1).expression.assertIsValidWithType(max4String, false) + get(2).expression.assertIsValidWithType(maxNString, false) + ] + ] + } + + @Test + def void shouldCoerceStringToParameterizedString() { + ''' + namespace test + + func Test: + inputs: str string (1..1) + output: max3String string(minLength: 1, maxLength: 3) (1..1) + set max3String: str + '''.parseRosettaWithNoIssues + } + + @Test + def void shouldCoerceParameterizedStringToString() { + ''' + namespace test + + func Test: + inputs: max3String string(minLength: 1, maxLength: 3) (1..1) + output: str string (1..1) + set str: max3String + '''.parseRosettaWithNoIssues + } + + @Test + def void shouldCoerceStringToStringTypeAlias() { + ''' + namespace test + + typeAlias Max3String: string(minLength: 1, maxLength: 3) + + func Test: + inputs: str string (1..1) + output: max3String Max3String (1..1) + set max3String: str + '''.parseRosettaWithNoIssues + } + + @Test + def void shouldCoerceStringTypeAliasToString() { + ''' + namespace test + + typeAlias Max3String: string(minLength: 1, maxLength: 3) + + func Test: + inputs: max3String Max3String (1..1) + output: str string (1..1) + set str: max3String + '''.parseRosettaWithNoIssues + } + + @Test + def void shouldCoerceDifferentParameterizedStrings() { + ''' + namespace test + + func Test: + inputs: max10String string(minLength: 1, maxLength: 10) (1..1) + output: max3String string(minLength: 1, maxLength: 3) (1..1) + set max3String: max10String + '''.parseRosettaWithNoIssues + } + + @Test + def void shouldCoerceDifferentTypeAliases() { + ''' + namespace test + + typeAlias Max10String: string(minLength: 1, maxLength: 10) + typeAlias Max3String: string(minLength: 1, maxLength: 3) + + func Test: + inputs: max10String Max10String (1..1) + output: max3String Max3String (1..1) + set max3String: max10String + '''.parseRosettaWithNoIssues + } @Test def void testAttributeSameNameAsAnnotationTest() { diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/types/RosettaTypingTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/types/RosettaTypingTest.xtend deleted file mode 100644 index 0c2b221a3..000000000 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/types/RosettaTypingTest.xtend +++ /dev/null @@ -1,728 +0,0 @@ -package com.regnosys.rosetta.types - -import com.regnosys.rosetta.tests.RosettaInjectorProvider -import org.eclipse.xtext.testing.InjectWith -import org.eclipse.xtext.testing.extensions.InjectionExtension -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.^extension.ExtendWith - -import com.regnosys.rosetta.tests.util.ModelHelper -import com.regnosys.rosetta.rosetta.simple.Function -import com.regnosys.rosetta.rosetta.expression.RosettaConditionalExpression -import com.regnosys.rosetta.rosetta.simple.Data -import com.regnosys.rosetta.rosetta.expression.ArithmeticOperation -import com.regnosys.rosetta.rosetta.RosettaEnumeration -import com.regnosys.rosetta.rosetta.expression.LogicalOperation -import com.regnosys.rosetta.rosetta.expression.MapOperation -import com.regnosys.rosetta.types.builtin.RBuiltinTypeService -import java.util.Optional -import java.math.BigDecimal -import com.regnosys.rosetta.tests.util.ExpressionValidationHelper -import com.regnosys.rosetta.tests.util.ExpressionParser -import javax.inject.Inject -import java.math.BigInteger - -@ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) -class RosettaTypingTest { - @Inject - extension TypeFactory - - @Inject - extension TypeSystem - - @Inject - extension TypeTestUtil - - @Inject - extension ExpressionParser - - @Inject - extension ExpressionValidationHelper - - @Inject - extension ModelHelper - - @Inject - extension RBuiltinTypeService - - @Inject - extension RObjectFactory - - @Test - def void testLiteralTypeInference() { - 'False'.assertIsValidWithType(singleBoolean) - '"Some string"'.assertIsValidWithType(singleString(11, 11)) - '3.14'.assertIsValidWithType(singleNumber(3, 2, "3.14", "3.14")) - '1'.assertIsValidWithType(singleInt(1, "1", "1")) - 'empty'.assertIsValidWithType(emptyNothing) - } - - @Test - def void testVariableTypeInference() { - val model = ''' - namespace test - - func TestVar: - inputs: - a int (2..4) - b boolean (1..1) - output: result number (1..4) - alias c: if b then 42 else -1/12 - add result: - if b then a else c - - func TestImplicitVar: - output: result int (3..3) - add result: - [1, 2, 3] extract item + 1 - '''.parseRosettaWithNoIssues - model.elements.get(0) as Function => [operations.head.expression as RosettaConditionalExpression => [ - ^if.assertHasType(singleBoolean) - ifthen.assertHasType(createListType(UNCONSTRAINED_INT, 2, 4)) - elsethen.assertHasType(singleUnconstrainedNumber) - ]]; - - model.elements.get(1) as Function => [operations.head.expression as MapOperation => [ - function.body as ArithmeticOperation => [ - left.assertHasType(singleInt(1, "1", "3")) - ] - ]]; - } - - // TODO: test auxiliary functions - @Test - def void testSubtyping() { - val t1 = createListType(UNCONSTRAINED_INT, 1, 3); - val t2 = createListType(UNCONSTRAINED_NUMBER, 1, 5); - t1.assertListSubtype(t2) - - val t3 = createListType(BOOLEAN, 1, 3); - t1.assertNotListSubtype(t3); - - val t4 = createListType(UNCONSTRAINED_INT, 1, 2); - t1.assertNotListSubtype(t4); - } - - @Test - def void testLogicalOperationTypeInference() { - 'True or False'.assertIsValidWithType(singleBoolean) - 'True and False'.assertIsValidWithType(singleBoolean) - - // Test loosened version - '(if False then True else [True, False]) and False'.assertIsValidWithType(singleBoolean) - } - - @Test - def void testLogicalOperationTypeChecking() { - '1 or False' - .parseExpression - .assertError(null, "Expected type `boolean`, but got `int` instead.") - 'True or 3.14' - .parseExpression - .assertError(null, "Expected type `boolean`, but got `number` instead.") - } - - @Test - def void testEqualityOperationTypeInference() { - '(if True then [1] else [2, 3]) = (if False then [4.0, 5] else [6.0, 7, 8])' - .assertIsValidWithType(singleBoolean) - '(if True then [1] else [2, 3]) <> (if False then [4.0, 5] else [6.0, 7, 8])' - .assertIsValidWithType(singleBoolean) - '[1, 3] all = 5.0'.assertIsValidWithType(singleBoolean) - 'empty all <> 5.0'.assertIsValidWithType(singleBoolean) - '[1, 3] any = 5.0'.assertIsValidWithType(singleBoolean) - - 'a = 1' - .parseExpression(#['a int (0..1)']) - .assertNoIssues - - val model = ''' - namespace test - - type Foo: - packageTransactionPriceNotation int (0..1) - - condition C: - packageTransactionPriceNotation = 1 - '''.parseRosettaWithNoIssues - val expression = (model.elements.last as Data).conditions.head.expression; - expression.assertHasType(singleBoolean); - - // Test loosened version - '[1, 3] any = (if False then 1 else [2, 3])'.assertIsValidWithType(singleBoolean) - } - - @Test - def void testEqualityOperationTypeChecking() { - '1 = True' - .parseExpression - .assertError(null, "Types `int` and `boolean` are not comparable.") - 'empty = True' - .parseExpression - .assertError(null, "Cannot compare an empty value to a single value, as they cannot be of the same length. Perhaps you forgot to write `all` or `any` in front of the operator?") - '[1, 2] = [3, 4, 5]' - .parseExpression - .assertError(null, "Cannot compare a list with 2 items to a list with 3 items, as they cannot be of the same length.") - '[1, 2] <> [True, False, False]' - .parseExpression - .assertError(null, "Types `int` and `boolean` are not comparable.") - - '[1, 2] all = empty' - .parseExpression - .assertError(null, "Expected a single value, but got an empty value instead.") - 'empty any = empty' - .parseExpression - .assertError(null, "Expected a single value, but got an empty value instead.") - '[1, 2] all = [1, 2]' - .parseExpression - .assertError(null, "Expected a single value, but got a list with 2 items instead.") - '5 any <> [1, 2]' - .parseExpression - .assertError(null, "Expected a single value, but got a list with 2 items instead. Perhaps you meant to swap the left and right operands?") - '[3.0] any <> 5' - .parseExpression - .assertError(null, "The cardinality operator `any` is redundant when comparing two single values.") - } - - // TODO: test arithmetic and comparisons with dates/times/etc - @Test - def void testArithmeticOperationTypeInference() { - '3 + 4'.assertIsValidWithType(singleInt(Optional.empty, Optional.of(BigInteger.valueOf(7)), Optional.of(BigInteger.valueOf(7)))) - '3.0 + 4'.assertIsValidWithType(singleNumber(Optional.empty, Optional.of(1), Optional.of(new BigDecimal("7")), Optional.of(new BigDecimal("7")), Optional.empty)) - '3 + 4.0'.assertIsValidWithType(singleNumber(Optional.empty, Optional.of(1), Optional.of(new BigDecimal("7")), Optional.of(new BigDecimal("7")), Optional.empty)) - '3.0 + 4.0'.assertIsValidWithType(singleNumber(Optional.empty, Optional.of(1), Optional.of(new BigDecimal("7")), Optional.of(new BigDecimal("7")), Optional.empty)) - '"ab" + "cd"'.assertIsValidWithType(singleString(4, 4)) - - '3 - 4'.assertIsValidWithType(singleInt(Optional.empty, Optional.of(BigInteger.valueOf(-1)), Optional.of(BigInteger.valueOf(-1)))) - '3 - 4.0'.assertIsValidWithType(singleNumber(Optional.empty, Optional.of(1), Optional.of(new BigDecimal("-1")), Optional.of(new BigDecimal("-1")), Optional.empty)) - - '3 * 4'.assertIsValidWithType(singleInt(Optional.empty, Optional.of(BigInteger.valueOf(12)), Optional.of(BigInteger.valueOf(12)))) - '3.0 * 4'.assertIsValidWithType(singleNumber(Optional.empty, Optional.of(1), Optional.of(new BigDecimal("12")), Optional.of(new BigDecimal("12")), Optional.empty)) - - '3 / 4'.assertIsValidWithType(singleUnconstrainedNumber) - - // Test loosened version - '(if False then 2 else [3, 4]) + 5'.assertIsValidWithType(singleInt(Optional.empty, Optional.of(BigInteger.valueOf(7)), Optional.of(BigInteger.valueOf(9)))) - } - - @Test - def void testArithemticOperationTypeChecking() { - '[1, 2] + 3' - .parseExpression - .assertError(null, "Expected a single value, but got a list with 2 items instead.") - 'empty - 3' - .parseExpression - .assertError(null, "Expected a single value, but got an empty value instead.") - '1.5 * False' - .parseExpression - .assertError(null, "Expected type `number`, but got `boolean` instead.") - '"ab" + 3' - .parseExpression - .assertError(null, "Expected arguments to be either both a `string` or both a `number`, but got `string` and `int` instead.") - } - - @Test - def void testComparisonOperationTypeInference() { - '1 < 2'.assertIsValidWithType(singleBoolean) - '3 > 3.14'.assertIsValidWithType(singleBoolean) - '-5.1 <= 42'.assertIsValidWithType(singleBoolean) - '-3.14 >= 3.14'.assertIsValidWithType(singleBoolean) - - '[1, 2] any < 5'.assertIsValidWithType(singleBoolean) - 'empty all > 5'.assertIsValidWithType(singleBoolean) - - // Test loosened version - '(if False then 2 else [3, 4]) < 5'.assertIsValidWithType(singleBoolean) - '[1, 2] any < (if False then 5 else [3, 4])'.assertIsValidWithType(singleBoolean) - } - - @Test - def void testComparisonOperationTypeChecking() { - // TODO: support date, zonedDateTime and `time`? - '[1, 2] < 3' - .parseExpression - .assertError(null, "Expected a single value, but got a list with 2 items instead.") - 'empty > 3' - .parseExpression - .assertError(null, "Expected a single value, but got an empty value instead.") - '1.5 <= False' - .parseExpression - .assertError(null, "Expected type `number`, but got `boolean` instead.") - - '[1, 2] all >= empty' - .parseExpression - .assertError(null, "Expected a single value, but got an empty value instead.") - 'empty any < empty' - .parseExpression - .assertError(null, "Expected a single value, but got an empty value instead.") - '[1, 2] all > [1, 2]' - .parseExpression - .assertError(null, "Expected a single value, but got a list with 2 items instead.") - '5 any <= [1, 2]' - .parseExpression - .assertError(null, "Expected a single value, but got a list with 2 items instead. Perhaps you meant to swap the left and right operands?") - '5 all >= 1' - .parseExpression - .assertError(null, "The cardinality operator `all` is redundant when comparing two single values.") - } - - @Test - def void testConditionalExpressionTypeInference() { - 'if True then [1, 2] else [3.0, 4.0, 5.0, 6.0]'.assertIsValidWithType(createListType(constrainedNumber(2, 1, "1", "6"), 2, 4)); - } - - @Test - def void testConditionalExpressionTypeChecking() { - 'if [True, False] then 1 else 2' - .parseExpression - .assertError(null, "Expected a single value, but got a list with 2 items instead.") - 'if empty then 1 else 2' - .parseExpression - .assertError(null, "Expected a single value, but got an empty value instead.") - 'if True then 1 else False' - .parseExpression - .assertError(null, "Types `int` and `boolean` do not have a common supertype.") - 'if True then [1, 2, 3] else [False, True]' - .parseExpression - .assertError(null, "Types `int` and `boolean` do not have a common supertype.") - } - - @Test - def void testListLiteralTypeInference() { - '[]'.assertIsValidWithType(emptyNothing); - '[2, 4.5, 7, -3.14]'.assertIsValidWithType(createListType(constrainedNumber(3, 2, "-3.14", "7"), 4, 4)); - '[2, [1, 2], -3.14]'.assertIsValidWithType(createListType(constrainedNumber(3, 2, "-3.14", "2"), 4, 4)); - } - - @Test - def void testListLiteralTypeChecking() { - '[1, True]' - .parseExpression - .assertError(null, "Elements do not have a common supertype: `int`, `boolean`.") - } - - @Test - def void testFunctionCallTypeInference() { - val model = ''' - namespace test - - func SomeFunc: - inputs: - a int (1..1) - b boolean (2..4) - output: result number (3..5) - add result: - [1.0, 2.0, 3.0] - - func Test: - output: result number (3..5) - add result: - SomeFunc(42, [True, False, True]) - '''.parseRosettaWithNoIssues - val expression = (model.elements.last as Function).operations.head.expression; - expression.assertHasType(createListType(UNCONSTRAINED_NUMBER, 3, 5)); - } - - @Test - def void testFunctionCallTypeChecking() { - val model = ''' - namespace test - - func SomeFunc: - inputs: - a int (1..1) - b boolean (2..4) - output: result int (1..1) - set result: - 42 - - func TestParamNumber: - output: result int (1..1) - set result: - SomeFunc(1, [False, True], True) - - func TestParamType: - output: result int (1..1) - set result: - SomeFunc(1, [2, 3]) - - func TestParamCardinality: - output: result int (1..1) - set result: - SomeFunc(1, [False, True, False, False, True]) - '''.parseRosetta - - val expr1 = (model.elements.get(1) as Function).operations.head.expression; - expr1.assertError(null, "Expected 2 arguments, but got 3 instead."); - - val expr2 = (model.elements.get(2) as Function).operations.head.expression; - expr2.assertError(null, "Expected type `boolean`, but got `int` instead."); - - val expr3 = (model.elements.get(3) as Function).operations.head.expression; - expr3.assertError(null, "Expected a list with 2 to 4 items, but got a list with 5 items instead."); - } - - @Test - def void testProjectionTypeInference() { - val model = ''' - namespace test - - type A: - x int (1..1) - y number (0..*) - z boolean (3..7) - - func Test1: - inputs: - a A (1..1) - output: result int (1..1) - set result: - a -> x - - func Test2: - inputs: - a A (1..1) - output: result number (0..*) - add result: - a -> y - - func Test3: - inputs: - a A (1..1) - output: result boolean (3..7) - add result: - a -> z - - func Test4: - inputs: - a A (2..5) - output: result int (2..5) - add result: - a -> x - - func Test5: - inputs: - a A (2..5) - output: result number (0..*) - add result: - a -> y - - func Test6: - inputs: - a A (2..5) - output: result boolean (6..35) - add result: - a -> z - '''.parseRosettaWithNoIssues - val expr1 = (model.elements.get(1) as Function).operations.head.expression; - expr1.assertHasType(createListType(UNCONSTRAINED_INT, 1, 1)); - - val expr2 = (model.elements.get(2) as Function).operations.head.expression; - expr2.assertHasType(createListType(UNCONSTRAINED_NUMBER, 0)); - - val expr3 = (model.elements.get(3) as Function).operations.head.expression; - expr3.assertHasType(createListType(BOOLEAN, 3, 7)); - - val expr4 = (model.elements.get(4) as Function).operations.head.expression; - expr4.assertHasType(createListType(UNCONSTRAINED_INT, 2, 5)); - - val expr5 = (model.elements.get(5) as Function).operations.head.expression; - expr5.assertHasType(createListType(UNCONSTRAINED_NUMBER, 0)); - - val expr6 = (model.elements.get(6) as Function).operations.head.expression; - expr6.assertHasType(createListType(BOOLEAN, 6, 35)); - } - - @Test - def void testEnumTypeInference() { - val model = ''' - namespace test - - enum A: - V1 - V2 - - func Test: - output: result A (1..1) - set result: - A -> V1 - '''.parseRosettaWithNoIssues - val A = (model.elements.get(0) as RosettaEnumeration).buildREnumType; - val expr1 = (model.elements.get(1) as Function).operations.head.expression; - expr1.assertHasType(createListType(A, 1, 1)); - } - - @Test - def void testExistsTypeInference() { - '(if True then [] else 5) exists'.assertIsValidWithType(singleBoolean); - '(if True then [] else [1, 2, 3]) exists'.assertIsValidWithType(singleBoolean); - } - - @Test - def void testExistsTypeChecking() { - 'empty exists' - .parseExpression - .assertError(null, "Expected an optional value, but got an empty value instead.") - '42 exists' - .parseExpression - .assertError(null, "Expected an optional value, but got a single value instead.") - '(if True then 42 else [1, 2, 3, 4, 5]) exists' - .parseExpression - .assertError(null, "Expected an optional value, but got a list with 1 to 5 items instead.") - } - - @Test - def void testAbsentTypeInference() { - '(if True then [] else 5) is absent'.assertIsValidWithType(singleBoolean); - '(if True then [] else [1, 2, 3]) is absent'.assertIsValidWithType(singleBoolean); - } - - @Test - def void testAbsentTypeChecking() { - 'empty is absent' - .parseExpression - .assertError(null, "Expected an optional value, but got an empty value instead.") - '42 is absent' - .parseExpression - .assertError(null, "Expected an optional value, but got a single value instead.") - '(if True then 42 else [1, 2, 3, 4, 5]) is absent' - .parseExpression - .assertError(null, "Expected an optional value, but got a list with 1 to 5 items instead.") - } - - @Test - def void testCountTypeInference() { - val singlePositiveInt = singleInt(Optional.empty, Optional.of(BigInteger.ZERO), Optional.empty) - 'empty count'.assertIsValidWithType(singlePositiveInt); - '42 count'.assertIsValidWithType(singlePositiveInt); - '[1, 2, 3] count'.assertIsValidWithType(singlePositiveInt); - '(if True then empty else [1, 2, 3]) count'.assertIsValidWithType(singlePositiveInt); - } - - @Test - def void testOnlyExistsTypeInference() { - val model = ''' - namespace test - - type A: - x int (0..1) - y number (0..3) - z boolean (0..*) - - condition C: - x only exists and (x, y) only exists - - func Test: - inputs: - a A (1..1) - output: result boolean (1..1) - set result: - a -> x only exists and (a -> x, a -> y) only exists - '''.parseRosettaWithNoIssues; - - (model.elements.get(0) as Data).conditions.head.expression as LogicalOperation => [ - left.assertHasType(singleBoolean) - right.assertHasType(singleBoolean) - ] - (model.elements.get(1) as Function).operations.head.expression as LogicalOperation => [ - left.assertHasType(singleBoolean) - right.assertHasType(singleBoolean) - ] - } - - @Test - def void testOnlyExistsTypeChecking() { - val model = ''' - namespace test - - type Foo: - bar int (1..1) - baz boolean (0..1) - - condition X: - baz only exists - - type A: - x Foo (0..1) - y number (0..3) - z boolean (0..*) - - condition C1: - (x -> baz, y) only exists and (y, x -> baz) only exists - condition C2: - (x, x) only exists and (x -> baz, x -> baz) only exists - - func Test: - inputs: - a A (1..1) - foo Foo (1..1) - b A (2..3) - c A (0..1) - output: result boolean (0..*) - add result: - b -> x only exists - add result: - c only exists - add result: - (a -> x -> baz, a -> x) only exists - add result: - foo -> baz only exists - '''.parseRosetta; - - (model.elements.get(0) as Data).conditions.head.expression - .assertError(null, "The `only exists` operator is not applicable to instances of `Foo`."); - - (model.elements.get(1) as Data).conditions => [ - get(0).expression as LogicalOperation => [ - left.assertError(null, "All parent paths must be equal.") - right.assertError(null, "All parent paths must be equal.") - ] - get(1).expression as LogicalOperation => [ - left.assertError(null, "Duplicate attribute.") - right.assertError(null, "Duplicate attribute.") - ] - ] - - (model.elements.get(2) as Function).operations => [ - get(0).expression.assertError(null, "Expected a single value, but got a list with 2 to 3 items instead.") - get(1).expression.assertError(null, "Object must have a parent object.") - get(2).expression.assertError(null, "All parent paths must be equal.") - get(3).expression.assertError(null, "The `only exists` operator is not applicable to instances of `Foo`.") - ] - } - - @Test - def void testOnlyElementTypeInference() { - '(if True then 0 else [1, 2]) only-element'.assertIsValidWithType(createListType(constrainedInt(1, "0", "2"), 0, 1)); - '(if True then empty else [True, False]) only-element'.assertIsValidWithType(createListType(BOOLEAN, 0, 1)); - '(if True then 0 else [1, 2, 3, 42.0]) only-element'.assertIsValidWithType(createListType(constrainedNumber(3, 1, "0", "42.0"), 0, 1)); - } - - @Test - def void testOnlyElementTypeChecking() { - 'empty only-element' - .parseExpression - .assertWarning(null, "Expected a list with 1 to 2 items, but got an empty value instead.") - '42 only-element' - .parseExpression - .assertWarning(null, "Expected a list with 1 to 2 items, but got a single value instead.") - '[1, 2] only-element' - .parseExpression - .assertWarning(null, "Expected a list with 1 to 2 items, but got a list with 2 items instead.") - '(if True then empty else 42) only-element' - .parseExpression - .assertWarning(null, "Expected a list with 1 to 2 items, but got an optional value instead.") - } - - @Test - def void testTypeAliasJoin() { - val model = ''' - namespace test - - typeAlias maxNString(n int): string(minLength: 1, maxLength: n) - typeAlias max3String: maxNString(n: 3) - typeAlias max4String: maxNString(n: 4) - - func Test: - inputs: - s1 max3String (1..1) - s2 max4String (1..1) - s3 maxNString(n: 4) (1..1) - output: result string (0..*) - add result: if True then s1 else s2 - add result: if True then s2 else s2 - add result: if True then s2 else s3 - '''.parseRosettaWithNoIssues - model.elements.last as Function => [ - val max4String = createListType(inputs.get(1).typeCall.typeCallToRType, single) - val maxNString = createListType(inputs.get(2).typeCall.typeCallToRType, single) - - operations => [ - get(0).expression.assertHasType(maxNString) - get(1).expression.assertHasType(max4String) - get(2).expression.assertHasType(maxNString) - ] - ] - } - - @Test - def void shouldCoerceStringToParameterizedString() { - ''' - namespace test - - func Test: - inputs: str string (1..1) - output: max3String string(minLength: 1, maxLength: 3) (1..1) - set max3String: str - '''.parseRosettaWithNoIssues - } - - @Test - def void shouldCoerceParameterizedStringToString() { - ''' - namespace test - - func Test: - inputs: max3String string(minLength: 1, maxLength: 3) (1..1) - output: str string (1..1) - set str: max3String - '''.parseRosettaWithNoIssues - } - - @Test - def void shouldCoerceStringToStringTypeAlias() { - ''' - namespace test - - typeAlias Max3String: string(minLength: 1, maxLength: 3) - - func Test: - inputs: str string (1..1) - output: max3String Max3String (1..1) - set max3String: str - '''.parseRosettaWithNoIssues - } - - @Test - def void shouldCoerceStringTypeAliasToString() { - ''' - namespace test - - typeAlias Max3String: string(minLength: 1, maxLength: 3) - - func Test: - inputs: max3String Max3String (1..1) - output: str string (1..1) - set str: max3String - '''.parseRosettaWithNoIssues - } - - @Test - def void shouldCoerceDifferentParameterizedStrings() { - ''' - namespace test - - func Test: - inputs: max10String string(minLength: 1, maxLength: 10) (1..1) - output: max3String string(minLength: 1, maxLength: 3) (1..1) - set max3String: max10String - '''.parseRosettaWithNoIssues - } - - @Test - def void shouldCoerceDifferentTypeAliases() { - ''' - namespace test - - typeAlias Max10String: string(minLength: 1, maxLength: 10) - typeAlias Max3String: string(minLength: 1, maxLength: 3) - - func Test: - inputs: max10String Max10String (1..1) - output: max3String Max3String (1..1) - set max3String: max10String - '''.parseRosettaWithNoIssues - } - -} diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/types/TypeTestUtil.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/types/TypeTestUtil.xtend deleted file mode 100644 index 0929166e4..000000000 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/types/TypeTestUtil.xtend +++ /dev/null @@ -1,46 +0,0 @@ -package com.regnosys.rosetta.types - -import com.regnosys.rosetta.rosetta.expression.RosettaExpression - -import static org.junit.jupiter.api.Assertions.* -import com.regnosys.rosetta.tests.util.ExpressionValidationHelper -import com.regnosys.rosetta.tests.util.ExpressionParser -import javax.inject.Inject - -class TypeTestUtil { - @Inject - extension TypeSystem - - @Inject - extension ExpressionValidationHelper - - @Inject - extension ExpressionParser - - def RListType getType(CharSequence expr) { - return getType(expr.parseExpression); - } - def RListType getType(RosettaExpression expr) { - val res = expr.inferType - assertNotNull(res); - return res; - } - - def void assertIsValidWithType(CharSequence expr, RListType expected) { - val e = expr.parseExpression - e.assertNoIssues - e.assertHasType(expected) - } - - def void assertHasType(RosettaExpression e, RListType expected) { - val t = e.type - assertEquals(expected, t) - } - - def void assertListSubtype(RListType a, RListType b) { - assertTrue(isListSubtypeOf(a, b)) - } - def void assertNotListSubtype(RListType a, RListType b) { - assertFalse(isListSubtypeOf(a, b)) - } -} \ No newline at end of file diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaValidatorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaValidatorTest.xtend index 5297f87c7..dc46a8928 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaValidatorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaValidatorTest.xtend @@ -260,7 +260,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { def void testSwitchInputRecordTypesAreNotValid() { "someDate switch default \"someResult\"" .parseExpression(#["someDate date (1..1)"]) - .assertError(SWITCH_OPERATION, null, "Type `date` is not a valid switch argument type. Supported argument types are basic types, enumerations, and choice types.") + .assertError(SWITCH_OPERATION, null, "Operator `switch` is not supported for type date. Supported argument types are basic types, enumerations, and choice types") } @Test @@ -281,7 +281,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { default "someOtherValue" ''' .parseExpression(#[model], #["inEnum SomeEnum (1..*)"]) - .assertError(ROSETTA_EXPRESSION, null, "Input to switch must be single cardinality") + .assertError(ROSETTA_EXPRESSION, null, "Expecting single cardinality") } @@ -356,7 +356,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { "inFoo switch default 42" .parseExpression(#[model], #["inFoo Foo (1..1)"]) - .assertError(SWITCH_OPERATION, null, "Type `Foo` is not a valid switch argument type. Supported argument types are basic types, enumerations, and choice types.") + .assertError(SWITCH_OPERATION, null, "Operator `switch` is not supported for type Foo. Supported argument types are basic types, enumerations, and choice types") } @Test @@ -533,7 +533,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { '''.parseRosetta model.assertError(ROSETTA_SYMBOL_REFERENCE, null, - "Invalid number of arguments. Expecting 1 but passed 0." + "Expected 1 argument, but got 0 instead" ) } @@ -1529,10 +1529,10 @@ class RosettaValidatorTest implements RosettaIssueCodes { inputs: a int (0..*) output: b int (0..*) add b: - a extract [Add] + a extract Add '''.parseRosetta model.assertError(ROSETTA_SYMBOL_REFERENCE, null, - "Expected 2 arguments, but got 0 instead.") + "Expected 2 arguments, but got 0 instead") } @Test @@ -1546,7 +1546,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { a() '''.parseRosetta model.assertError(ROSETTA_SYMBOL_REFERENCE, null, - "A variable may not be called.") + "A variable may not be called") } @Test @@ -1586,8 +1586,8 @@ class RosettaValidatorTest implements RosettaIssueCodes { condition A: *42 '''.parseRosetta - model.assertError(ROSETTA_IMPLICIT_VARIABLE, null, - "Expected type `number`, but got `Foo` instead.") + model.assertError(ARITHMETIC_OPERATION, null, + "Expected type `number`, but got `Foo` instead") } @Test @@ -1644,8 +1644,8 @@ class RosettaValidatorTest implements RosettaIssueCodes { if id = True then id < 1 '''.parseRosetta - model.assertError(ROSETTA_CONDITIONAL_EXPRESSION, TYPE_ERROR, - "Incompatible types: cannot use operator '=' with int and boolean.") + model.assertError(EQUALITY_OPERATION, null, + "Types `int` and `boolean` are not comparable") } @Test @@ -1672,7 +1672,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { if id = True then id < 1 '''.parseRosetta - model.assertError(COMPARISON_OPERATION, null, "Incompatible types: cannot use operator '<' with boolean and int.") + model.assertError(COMPARISON_OPERATION, null, "Operator `<` is not supported for type boolean. Supported types are number, date and zonedDateTime") } @Test @@ -2081,8 +2081,8 @@ class RosettaValidatorTest implements RosettaIssueCodes { Foo(timestamp) = timestamp '''.parseRosetta - model.assertError(ROSETTA_SYMBOL_REFERENCE, TYPE_ERROR, - "Expected type 'zonedDateTime' but was 'date'") + model.assertError(ROSETTA_SYMBOL_REFERENCE, null, + "Expected type `zonedDateTime`, but got `date` instead") } @Test @@ -2197,7 +2197,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { attribute1 '''.parseRosetta model.assertError(CHOICE_OPERATION, null, - "At least two attributes must be passed to a choice rule.") + "At least two attributes must be passed to a choice rule") } @@ -2810,7 +2810,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { type Foo: x string (1..1) '''.parseRosetta - model.assertError(ROSETTA_SYMBOL_REFERENCE, null, "Expected 2 arguments, but got 0 instead.") + model.assertError(ROSETTA_SYMBOL_REFERENCE, null, "Expected 2 arguments, but got 0 instead") } @Test @@ -3150,7 +3150,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { x5 int (1..1) x6 string (0..1) '''.parseRosetta - model.assertError(ROSETTA_BINARY_OPERATION, null, "Left hand side of 'and' expression must be boolean") + model.assertError(ROSETTA_BINARY_OPERATION, null, "Expected type `boolean`, but got `number` instead") } @Test @@ -3169,7 +3169,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { x3 number (1..1) x4 number (1..1) '''.parseRosetta - model.assertError(LOGICAL_OPERATION, null, "Left hand side of 'and' expression must be boolean") + model.assertError(LOGICAL_OPERATION, null, "Expected type `boolean`, but got `number` instead") } @Test From 2e2c27523d31af23873896b6a6b5af460bf94d73 Mon Sep 17 00:00:00 2001 From: Simon Cockx Date: Mon, 9 Dec 2024 14:36:52 +0000 Subject: [PATCH 02/10] Fixed rule recursion --- .gitignore | 1 - README.md | 8 - pom.xml | 18 -- rosetta-lang/pom.xml | 24 +- .../rosetta/RosettaRuntimeModule.xtend | 41 --- .../rosetta/formatting2/FormattingUtil.java | 2 +- .../regnosys/rosetta/types/RTypeFunction.java | 14 - .../regnosys/rosetta/types/Rosetta.xsemantics | 171 ---------- .../rosetta/types/RosettaAuxiliary.xsemantics | 145 --------- .../rosetta/types/RosettaTypeProvider.xtend | 148 ++++----- .../regnosys/rosetta/types/TypeSystem.java | 155 ++++++++- ...ingValidator.java => ReportValidator.java} | 41 +-- ...emanticsIssuesOnGeneratedInputsFilter.java | 95 ------ .../rosetta/validation/RosettaValidator.xtend | 3 +- .../AbstractExpressionValidator.java | 162 ++++++++++ .../{ => expression}/ExpressionValidator.java | 298 ++---------------- .../expression/SwitchValidator.java | 150 +++++++++ .../util/ExpressionValidationHelper.xtend | 4 +- .../condition/DataRuleGeneratorTest.xtend | 12 +- .../RosettaBinaryOperationTest.xtend | 29 +- .../java/function/FunctionGeneratorTest.xtend | 6 +- .../rosetta/tests/RosettaParsingTest.xtend | 2 +- .../types/RosettaTypeProviderTest.xtend | 247 ++++++++------- rosetta-xcore-plugin-dependencies/pom.xml | 4 - 24 files changed, 695 insertions(+), 1085 deletions(-) delete mode 100644 rosetta-lang/src/main/java/com/regnosys/rosetta/types/Rosetta.xsemantics delete mode 100644 rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaAuxiliary.xsemantics rename rosetta-lang/src/main/java/com/regnosys/rosetta/validation/{StandaloneRosettaTypingValidator.java => ReportValidator.java} (84%) delete mode 100644 rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RetainXsemanticsIssuesOnGeneratedInputsFilter.java create mode 100644 rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/AbstractExpressionValidator.java rename rosetta-lang/src/main/java/com/regnosys/rosetta/validation/{ => expression}/ExpressionValidator.java (57%) create mode 100644 rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/SwitchValidator.java diff --git a/.gitignore b/.gitignore index e8b7dfc37..5c71c9b05 100644 --- a/.gitignore +++ b/.gitignore @@ -21,7 +21,6 @@ rosetta-profiling/reports/ **/xtend-gen/ **/emf-gen/ **/src/generated/ -**/xsemantics-gen/ .antlr-generator-3.2.0-patch.jar com.regnosys.rosetta.web/ diff --git a/README.md b/README.md index 61d70f8b3..820742253 100644 --- a/README.md +++ b/README.md @@ -82,13 +82,6 @@ Install the latest version of the "Eclipse IDE for Java and DSL Developers" usin #### Install the Checkstyle plugin We use [Checkstyle](https://checkstyle.sourceforge.io/) for enforcing good coding practices. The Eclipse plugin for Checkstyle can be found here: [https://checkstyle.org/eclipse-cs/#!/](https://checkstyle.org/eclipse-cs/#!/). -#### Install the Xsemantics plugin -We use the [Xsemantics DSL](https://github.com/eclipse/xsemantics) to define the type system of Rune. To enable language support for it in Eclipse, follow these steps: -1. Find out which version of Xsemantics you need by looking in the `pom.xml` file of the parent project. There should be a property called `xsemantics.version`. -2. Go to Help > Install New Software... -3. In 'Work with' fill in [https://download.eclipse.org/xsemantics/milestones/](https://download.eclipse.org/xsemantics/milestones/). -4. Install the appropriate version of XSemantics. - #### Setup the project 1. **Open the project in Eclipse**: File > Open Projects from File System..., select the right folder, click Finish. 2. **Update Maven dependencies**: right click on the `com.regnosys.rosetta.parent` project > Maven > Update project... and finish. @@ -106,7 +99,6 @@ If you're seeing 1000+ errors in the "Problems" window of Eclipse, try the follo Support for developing Xtext projects in Intellij is limited. It has no support for - editing `Xtend` files - editing the `Xtext` file -- editing the `Xsemantics` file - running `GenerateRosetta.mwe2`. You can however let Maven take care of that, and still edit regular Java files, run tests, etc. diff --git a/pom.xml b/pom.xml index 0f318bcdb..b334e17d5 100644 --- a/pom.xml +++ b/pom.xml @@ -101,7 +101,6 @@ 2.27.0 3.14.0 1.12.0 - 1.22.0 2.2 1.6.0 3.9.8 @@ -275,22 +274,6 @@ org.eclipse.emf.ecore.xcore.lib ${org.eclipse.emf.ecore.xcore.lib.version} - - org.eclipse.xsemantics - org.eclipse.xsemantics.runtime - ${xsemantics.version} - - - ch.qos.reload4j - reload4j - - - - - org.eclipse.xsemantics - org.eclipse.xsemantics.dsl - ${xsemantics.version} - org.yaml snakeyaml @@ -451,7 +434,6 @@ ${project.basedir}/src-gen/main/java ${project.basedir}/emf-gen/main/java ${project.basedir}/xtend-gen/main/java - ${project.basedir}/xsemantics-gen/main/java diff --git a/rosetta-lang/pom.xml b/rosetta-lang/pom.xml index 8686c0ae5..6c62aadf3 100644 --- a/rosetta-lang/pom.xml +++ b/rosetta-lang/pom.xml @@ -55,10 +55,6 @@ org.eclipse.xtext org.eclipse.xtext - - org.eclipse.xsemantics - org.eclipse.xsemantics.runtime - org.eclipse.emf org.eclipse.emf.ecore.xcore.lib @@ -120,12 +116,6 @@ **/* - - ${basedir}/../rosetta-lang/xsemantics-gen - - **/* - - ${basedir}/../rosetta-lang/xtend-gen @@ -180,7 +170,7 @@ xtext-maven-plugin - xcore-and-xsemantics-generator + xcore-generator generate-sources generate @@ -190,7 +180,7 @@ ${maven.compiler.release} ${maven.compiler.release} - + true @@ -220,16 +210,6 @@ - - - org.eclipse.xsemantics.dsl.XsemanticsStandaloneSetup - - - - ${project.basedir}/xsemantics-gen/main/java - - - diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/RosettaRuntimeModule.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/RosettaRuntimeModule.xtend index 040bd70e6..1abc6ea9a 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/RosettaRuntimeModule.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/RosettaRuntimeModule.xtend @@ -23,23 +23,14 @@ import org.eclipse.xtext.resource.impl.DefaultResourceDescriptionStrategy import org.eclipse.xtext.parser.IEncodingProvider import com.google.inject.Binder import org.eclipse.xtext.service.DispatchingProvider -import com.regnosys.rosetta.utils.ImplicitVariableUtil -import org.eclipse.xsemantics.runtime.validation.XsemanticsValidatorFilter -import com.regnosys.rosetta.validation.RetainXsemanticsIssuesOnGeneratedInputsFilter import org.eclipse.xtext.conversion.IValueConverterService import com.regnosys.rosetta.parsing.RosettaValueConverterService -import com.regnosys.rosetta.parsing.BigDecimalConverter import com.regnosys.rosetta.transgest.ModelLoader import com.regnosys.rosetta.transgest.ModelLoaderImpl -import com.regnosys.rosetta.formatting2.RosettaExpressionFormatter -import com.regnosys.rosetta.formatting2.FormattingUtil import javax.inject.Provider -import com.regnosys.rosetta.generator.java.util.RecordJavaUtil import com.regnosys.rosetta.serialization.RosettaTransientValueService import org.eclipse.xtext.parsetree.reconstr.ITransientValueService import com.regnosys.rosetta.resource.RosettaResource -import com.regnosys.rosetta.typing.RosettaTyping -import com.regnosys.rosetta.typing.RosettaTypingAuxiliary import org.eclipse.xtext.validation.IResourceValidator import com.regnosys.rosetta.validation.CachingResourceValidator import com.regnosys.rosetta.config.RosettaConfiguration @@ -52,15 +43,6 @@ import com.regnosys.rosetta.formatting2.ResourceFormatterService /* Use this class to register components to be used at runtime / without the Equinox extension registry.*/ class RosettaRuntimeModule extends AbstractRosettaRuntimeModule { - def void configureXsemanticsTypeSystem(Binder binder) { - // During a language server build, the following three classes are injected over and over again - // for each Rosetta resource. This means that code generation is spending up to 54% of its time - // just injecting these classes. By binding them as singletons, this time virtually disappears - // since they will only be instantiated once. - binder.bind(RosettaTyping).asEagerSingleton - binder.bind(RosettaTypingAuxiliary).asEagerSingleton - } - override Class bindIFragmentProvider() { RosettaFragmentProvider } @@ -99,19 +81,9 @@ class RosettaRuntimeModule extends AbstractRosettaRuntimeModule { .to(UTF8EncodingProvider); } - def Class bindImplicitVariableUtil() { - ImplicitVariableUtil - } - def Class bindXsemanticsValidatorFilter() { - RetainXsemanticsIssuesOnGeneratedInputsFilter - } - override Class bindIValueConverterService() { RosettaValueConverterService } - def Class bindBigDecimalConverter() { - BigDecimalConverter - } override Class bindXtextResource() { RosettaResource @@ -124,19 +96,6 @@ class RosettaRuntimeModule extends AbstractRosettaRuntimeModule { ModelLoaderImpl } - - def Class bindRosettaExpressionFormatter() { - RosettaExpressionFormatter - } - - def Class bindFormattingUtil() { - FormattingUtil - } - - def Class bindRecordFeatureMap() { - RecordJavaUtil - } - def Class bindIResourceValidator() { CachingResourceValidator } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/formatting2/FormattingUtil.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/formatting2/FormattingUtil.java index 7561cc647..b28c72dcb 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/formatting2/FormattingUtil.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/formatting2/FormattingUtil.java @@ -43,7 +43,7 @@ public void formatInlineOrMultiline(IFormattableDocument document, EObject objec public void formatInlineOrMultiline(IFormattableDocument document, EObject object, FormattingMode mode, int maxLineWidth, Consumer inlineFormatter, Consumer multilineFormatter) { IEObjectRegion objRegion = getTextRegionExt(document).regionForEObject(object); // I need to include the next hidden region in the conditional formatting as well, - // because that's where I might decrease indentation in case of a (long) multi-line operation. + // because that's where I might decrease indentation in case of a (long) multi-line operation. ITextSegment formattableRegion = objRegion.merge(objRegion.getNextHiddenRegion()); formatInlineOrMultiline(document, objRegion, formattableRegion, mode, maxLineWidth, inlineFormatter, multilineFormatter); } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RTypeFunction.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RTypeFunction.java index 0968e1440..064e43256 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RTypeFunction.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RTypeFunction.java @@ -30,20 +30,6 @@ public abstract class RTypeFunction extends AbstractModelSymbol { public RTypeFunction(DottedPath namespace, String name) { super(namespace, name); } - - // TODO: limitation of Xsemantics, which doesn't support anonymous classes. - public static RTypeFunction create(DottedPath namespace, String name, Function, RType> evaluate, Function>> reverse) { - return new RTypeFunction(namespace, name) { - @Override - public RType evaluate(Map arguments) { - return evaluate.apply(arguments); - } - @Override - public Optional> reverse(RType type) { - return reverse.apply(type); - } - }; - } public abstract RType evaluate(Map arguments); diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/Rosetta.xsemantics b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/Rosetta.xsemantics deleted file mode 100644 index f218ee187..000000000 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/Rosetta.xsemantics +++ /dev/null @@ -1,171 +0,0 @@ -system com.regnosys.rosetta.typing.RosettaTyping extends RosettaTypingAuxiliary - -import com.regnosys.rosetta.rosetta.simple.Attribute -import com.regnosys.rosetta.rosetta.simple.Data -import com.regnosys.rosetta.types.RListType -import com.regnosys.rosetta.types.RType -import com.regnosys.rosetta.types.TypeFactory -import com.regnosys.rosetta.utils.ExpressionHelper - -import com.regnosys.rosetta.utils.ImplicitVariableUtil -import java.util.Optional -import com.regnosys.rosetta.types.builtin.RBuiltinTypeService -import com.regnosys.rosetta.interpreter.RosettaInterpreter -import com.regnosys.rosetta.interpreter.RosettaInterpreterContext -import com.regnosys.rosetta.types.RAliasType -import com.regnosys.rosetta.utils.RosettaSimpleSystemSolver -import com.regnosys.rosetta.rosetta.RosettaTypeAlias -import com.regnosys.rosetta.types.RTypeFunction -import com.regnosys.rosetta.rosetta.expression.ExpressionFactory -import com.regnosys.rosetta.utils.RosettaSimpleSystemSolver.Equation -import com.regnosys.rosetta.types.RParametrizedType -import com.regnosys.rosetta.rosetta.TypeCall -import com.regnosys.rosetta.rosetta.RosettaMetaType -import com.regnosys.rosetta.rosetta.RosettaBuiltinType -import com.regnosys.rosetta.interpreter.RosettaValue -import com.rosetta.util.DottedPath -import com.regnosys.rosetta.utils.ModelIdProvider -import com.regnosys.rosetta.types.TypeSystem -import com.regnosys.rosetta.types.RObjectFactory -import com.regnosys.rosetta.rosetta.simple.Choice -import com.regnosys.rosetta.rosetta.RosettaEnumeration - -inject extension TypeFactory typeFactory -inject extension ExpressionHelper exprHelper -inject extension ImplicitVariableUtil implicitVarUtil -inject extension RBuiltinTypeService builtinTypes -inject extension ModelIdProvider modelIdProvider -inject extension RObjectFactory objectFactory -inject RosettaInterpreter interpreter -inject RosettaSimpleSystemSolver systemSolver -inject TypeSystem typeSystem - - -auxiliary { - comparable(RType t1, RType t2) - listComparable(RListType t1, RListType t2) - - // The methods below could be moved to RosettaAuxiliary were it not for issue https://github.com/eclipse/xsemantics/issues/178 - typeFunctionOfTypeAlias(RosettaTypeAlias typeAlias): RTypeFunction cached - typeCallToRType(TypeCall rt, RosettaInterpreterContext context): RType - attributeListType(Attribute a): RListType -} - -judgments { - subtype |- RType subtype <: RType supertype - error subtype + " is not a subtype of " + supertype - listSubtype |- RListType subtype <| RListType supertype - error subtype + " is not a list subtype of " + supertype -} - -/****** AUXILIARY DEFINITIONS *******/ -auxiliary comparable(RType t1, RType t2) { - empty |- t1 <: t2 - or - empty |- t2 <: t1 - or - return false -} -auxiliary listComparable(RListType t1, RListType t2) { - comparable(t1.itemType, t2.itemType) - overlap(t1.constraint, t2.constraint) -} - -auxiliary typeFunctionOfTypeAlias(RosettaTypeAlias typeAlias) { - if (typeAlias.name == INT_NAME) { - return INT_FUNCTION; - } - val parameters = typeAlias.parameters; - val namespace = DottedPath.splitOnDots(typeAlias.model.name) - val equations = - typeAlias.typeCall.arguments.map[arg| - val ref = ExpressionFactory.eINSTANCE.createRosettaSymbolReference(); - ref.generated = true; - ref.symbol = arg.parameter; - return new Equation(ref, arg.value); - ].toList; - return systemSolver.solve(equations, parameters.toSet).map[solutionSet| - RTypeFunction.create(namespace, typeAlias.name, - [arguments| - typeCallToRType(typeAlias.typeCall, RosettaInterpreterContext.of(arguments)) - ], - [type| - if (!(type instanceof RParametrizedType)) { - return Optional.^empty - } - val context = RosettaInterpreterContext.of((type as RParametrizedType).arguments); - return solutionSet.getSolution(context).map[solution| - val newArgs = newLinkedHashMap - parameters.forEach[p| newArgs.put(p.name, solution.get(p))] - return newArgs; - ] - ] - ) - ].orElseGet[ - RTypeFunction.create(namespace, typeAlias.name, - [arguments| - typeCallToRType(typeAlias.typeCall, RosettaInterpreterContext.of(arguments)) - ], - [Optional.^empty] - ) - ] -} - -auxiliary typeCallToRType(TypeCall call, RosettaInterpreterContext context) { - val t = call.type - - switch t { - Choice: t.buildRChoiceType - Data: t.buildRDataType - RosettaBuiltinType: { - val argMap = call.arguments.toMap([parameter.name], [interpreter.interpret(value, context)]) - builtinTypes.getType(t.name, argMap).orElse(null) - } - RosettaEnumeration: t.buildREnumType - RosettaMetaType: { - val argMap = call.arguments.toMap([parameter.name], [interpreter.interpret(value, context)]) - val builtinResult = builtinTypes.getType(t.name, argMap) - if (builtinResult.present) { - builtinResult.get - } else { - t.typeCall.typeCallToRType(context) - } - } - RosettaTypeAlias: { - val args = newLinkedHashMap - val absentParameters = t.parameters.toSet - call.arguments.forEach[arg| - val eval = interpreter.interpret(arg.value, context) - args.put(arg.parameter.name, eval) - absentParameters.remove(arg.parameter) - ] - absentParameters.forEach[ - args.put(name, RosettaValue.^empty) - ] - val refersTo = t.typeCall.typeCallToRType(RosettaInterpreterContext.of(args)) - new RAliasType(t.typeFunctionOfTypeAlias, args, refersTo) - } - default: { - NOTHING - } - } -} - -auxiliary attributeListType(Attribute a) { - return new RListType(typeCallToRType(a.typeCall, new RosettaInterpreterContext), a.card) -} - -/*** SUBTYPING **/ -rule SAll - G |- RType t1 <: RType t2 -from { - typeSystem.isSubtypeOf(t1, t2) -} - -/*** LIST SUBTYPING ***/ -rule SList // SA-List - G |- RListType s <| RListType t -from { - G |- s.itemType <: t.itemType - s.constraint.isSubconstraintOf(t.constraint) -} diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaAuxiliary.xsemantics b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaAuxiliary.xsemantics deleted file mode 100644 index 2277eedf4..000000000 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaAuxiliary.xsemantics +++ /dev/null @@ -1,145 +0,0 @@ -system com.regnosys.rosetta.typing.RosettaTypingAuxiliary - -validatorExtends AbstractDeclarativeValidator - -import com.regnosys.rosetta.rosetta.RosettaCardinality -import com.regnosys.rosetta.rosetta.RosettaEnumeration -import com.regnosys.rosetta.rosetta.RosettaPackage -import com.regnosys.rosetta.rosetta.simple.Data -import com.regnosys.rosetta.rosetta.simple.SimplePackage -import com.regnosys.rosetta.types.RDataType -import com.regnosys.rosetta.types.REnumType -import com.regnosys.rosetta.types.RType -import com.regnosys.rosetta.types.TypeFactory -import com.regnosys.rosetta.utils.ExpressionHelper -import java.util.List - -import com.regnosys.rosetta.utils.ImplicitVariableUtil -import com.regnosys.rosetta.types.builtin.RBuiltinTypeService -import com.regnosys.rosetta.interpreter.RosettaInterpreter -import com.regnosys.rosetta.types.RAliasType -import com.regnosys.rosetta.utils.RosettaSimpleSystemSolver -import java.util.function.BiFunction -import org.eclipse.xtext.validation.AbstractDeclarativeValidator -import org.eclipse.emf.ecore.resource.ResourceSet -import com.regnosys.rosetta.rosetta.RosettaFeature -import com.regnosys.rosetta.types.builtin.RRecordType -import com.regnosys.rosetta.rosetta.RosettaRecordType -import com.regnosys.rosetta.rosetta.RosettaEnumValue -import com.regnosys.rosetta.utils.ModelIdProvider -import com.regnosys.rosetta.types.TypeSystem -import com.regnosys.rosetta.types.RChoiceType - -inject extension TypeFactory typeFactory -inject extension ExpressionHelper exprHelper -inject extension ImplicitVariableUtil implicitVarUtil -inject extension RBuiltinTypeService builtinTypes -inject extension ModelIdProvider modelIdProvider -inject RosettaInterpreter interpreter -inject RosettaSimpleSystemSolver systemSolver -inject TypeSystem typeSystem - - -auxiliary { - ancestors(Data t) : List - ancestorEnums(RosettaEnumeration t) : List - overlap(RosettaCardinality c1, RosettaCardinality c2) - join(RType t1, RType t2) : RType - allEnumValues(RosettaEnumeration d) : Iterable - mayBeEmpty(RDataType d) - - keepTypeAliasIfPossible(RType t1, RType t2, BiFunction combineUnderlyingTypes): RType - allFeatures(RType t, ResourceSet resourceSet) : Iterable -} - -/****** AUXILIARY DEFINITIONS *******/ -auxiliary ancestors(Data t) { - getAll(t, - SimplePackage::eINSTANCE.data_SuperType, - SimplePackage::eINSTANCE.data_SuperType, - typeof(Data) - ) -} -auxiliary ancestorEnums(RosettaEnumeration t) { - getAll(t, - RosettaPackage::eINSTANCE.rosettaEnumeration_Parent, - RosettaPackage::eINSTANCE.rosettaEnumeration_Parent, - typeof(RosettaEnumeration) - ) -} -auxiliary overlap(RosettaCardinality c1, RosettaCardinality c2) { - (c1.unbounded || c1.sup >= c2.inf) && (c2.unbounded || c2.sup >= c1.inf) - or - return false -} -auxiliary join(RType t1, RType t2) { - return typeSystem.join(t1, t2) -} -auxiliary allEnumValues(RosettaEnumeration e) { - if (e.parent === null) { - return e.enumValues; - } else { - return allEnumValues(e.parent) + e.enumValues; - } -} -auxiliary mayBeEmpty(RDataType t) { - t.allAttributes.forall[ - cardinality.minBound === 0 - ] -} - -auxiliary keepTypeAliasIfPossible(RAliasType t1, RAliasType t2, BiFunction combineUnderlyingTypes) { - if (t1.typeFunction == t2.typeFunction) { - val typeFunc = t1.typeFunction - val underlier = keepTypeAliasIfPossible(t1.refersTo, t2.refersTo, combineUnderlyingTypes) - return typeFunc.reverse(underlier) - .map[args| new RAliasType(typeFunc, args, underlier)] - .orElse(underlier) - } else { - val superAliases = newArrayList - var RType curr = t1 - while (curr instanceof RAliasType) { - superAliases.add(curr) - curr = curr.refersTo - } - curr = t2 - while (curr instanceof RAliasType) { - val tf = curr.typeFunction - val match = superAliases.findFirst[tf == typeFunction] - if (match !== null) { - return keepTypeAliasIfPossible(match, curr, combineUnderlyingTypes) - } - curr = curr.refersTo - } - return keepTypeAliasIfPossible(t1.refersTo, t2.refersTo, combineUnderlyingTypes) - } -} -auxiliary keepTypeAliasIfPossible(RAliasType t1, RType t2, BiFunction combineUnderlyingTypes) { - keepTypeAliasIfPossible(t1.refersTo, t2, combineUnderlyingTypes) -} -auxiliary keepTypeAliasIfPossible(RType t1, RAliasType t2, BiFunction combineUnderlyingTypes) { - keepTypeAliasIfPossible(t1, t2.refersTo, combineUnderlyingTypes) -} -auxiliary keepTypeAliasIfPossible(RType t1, RType t2, BiFunction combineUnderlyingTypes) { - combineUnderlyingTypes.apply(t1, t2) -} - -auxiliary allFeatures(RType t, ResourceSet resourceSet) { - switch t { - RDataType: - t.allNonOverridenAttributes.map[EObject] - RChoiceType: - t.asRDataType.allFeatures(resourceSet) - REnumType: - t.EObject.allEnumValues - RRecordType: { - if (resourceSet !== null) { - builtinTypes.toRosettaType(t, RosettaRecordType, resourceSet).features - } else { - #[] - } - } - default: - #[] - } -} diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaTypeProvider.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaTypeProvider.xtend index 281a4ea4c..cb239719c 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaTypeProvider.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaTypeProvider.xtend @@ -77,7 +77,6 @@ import com.regnosys.rosetta.utils.ImplicitVariableUtil import com.regnosys.rosetta.utils.RosettaExpressionSwitch import java.math.BigInteger import java.util.List -import java.util.Map import java.util.Optional import javax.inject.Inject import javax.inject.Provider @@ -89,8 +88,9 @@ import com.regnosys.rosetta.rosetta.RosettaParameter import com.regnosys.rosetta.types.builtin.RStringType import com.regnosys.rosetta.types.builtin.RNumberType import com.regnosys.rosetta.utils.OptionalUtil +import java.util.Set -class RosettaTypeProvider extends RosettaExpressionSwitch> { +class RosettaTypeProvider extends RosettaExpressionSwitch> { public static String EXPRESSION_RTYPE_CACHE_KEY = RosettaTypeProvider.canonicalName + ".EXPRESSION_RTYPE" @@ -104,13 +104,13 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { - if (!extensions.isResolved(symbol)) { + private def RMetaAnnotatedType safeRType(RosettaSymbol symbol, EObject context, Set cycleTracker) { + if (!extensions.isResolved(symbol) || !cycleTracker.add(symbol)) { return NOTHING_WITH_NO_META } switch symbol { @@ -195,9 +195,7 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { + private def RMetaAnnotatedType safeRType(RosettaFeature feature, EObject context, Set cycleTracker) { if (!extensions.isResolved(feature)) { return NOTHING_WITH_NO_META } @@ -230,16 +228,8 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { + private def RMetaAnnotatedType safeRType(RosettaExpression expression, Set cycleTracker) { getRTypeFromCache(EXPRESSION_RTYPE_CACHE_KEY, expression, [ - if (cycleTracker.containsKey(expression)) { - val computed = cycleTracker.get(expression) - if (computed === null) { - return NOTHING_WITH_NO_META - } else { - return computed - } - } if (!extensions.isResolved(expression)) { return NOTHING_WITH_NO_META } @@ -255,10 +245,10 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { + private def RMetaAnnotatedType safeTypeOfImplicitVariable(EObject context, Set cycleTracker) { val definingContainer = context.findContainerDefiningImplicitVariable definingContainer.map [ if (it instanceof Data) { @@ -273,11 +263,11 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { + override protected caseAbsentOperation(RosettaAbsentExpression expr, Set cycleTracker) { BOOLEAN_WITH_NO_META } - override protected caseAddOperation(ArithmeticOperation expr, Map cycleTracker) { + override protected caseAddOperation(ArithmeticOperation expr, Set cycleTracker) { val left = expr.left.safeRType(cycleTracker) val right = expr.right.safeRType(cycleTracker) if (left.isSubtypeOf(NOTHING_WITH_NO_META)) { @@ -312,23 +302,23 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { + override protected caseAndOperation(LogicalOperation expr, Set cycleTracker) { BOOLEAN_WITH_NO_META } - override protected caseAsKeyOperation(AsKeyOperation expr, Map cycleTracker) { + override protected caseAsKeyOperation(AsKeyOperation expr, Set cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseBooleanLiteral(RosettaBooleanLiteral expr, Map cycleTracker) { + override protected caseBooleanLiteral(RosettaBooleanLiteral expr, Set cycleTracker) { BOOLEAN_WITH_NO_META } - override protected caseChoiceOperation(ChoiceOperation expr, Map cycleTracker) { + override protected caseChoiceOperation(ChoiceOperation expr, Set cycleTracker) { BOOLEAN_WITH_NO_META } - override protected caseConditionalExpression(RosettaConditionalExpression expr, Map cycleTracker) { + override protected caseConditionalExpression(RosettaConditionalExpression expr, Set cycleTracker) { val ifT = expr.ifthen.safeRType(cycleTracker) val elseT = expr.elsethen.safeRType(cycleTracker) val joined = joinMetaAnnotatedTypes(ifT, elseT) @@ -339,11 +329,11 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { + override protected caseContainsOperation(RosettaContainsExpression expr, Set cycleTracker) { BOOLEAN_WITH_NO_META } - override protected caseDefaultOperation(DefaultOperation expr, Map cycleTracker) { + override protected caseDefaultOperation(DefaultOperation expr, Set cycleTracker) { val left = expr.left.safeRType(cycleTracker) val right = expr.right.safeRType(cycleTracker) val result = left.joinMetaAnnotatedTypes(right) @@ -354,31 +344,31 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { + override protected caseCountOperation(RosettaCountOperation expr, Set cycleTracker) { constrainedInt(Optional.empty(), Optional.of(BigInteger.ZERO), Optional.empty()).withEmptyMeta } - override protected caseDisjointOperation(RosettaDisjointExpression expr, Map cycleTracker) { + override protected caseDisjointOperation(RosettaDisjointExpression expr, Set cycleTracker) { BOOLEAN_WITH_NO_META } - override protected caseDistinctOperation(DistinctOperation expr, Map cycleTracker) { + override protected caseDistinctOperation(DistinctOperation expr, Set cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseDivideOperation(ArithmeticOperation expr, Map cycleTracker) { + override protected caseDivideOperation(ArithmeticOperation expr, Set cycleTracker) { UNCONSTRAINED_NUMBER_WITH_NO_META } - override protected caseEqualsOperation(EqualityOperation expr, Map cycleTracker) { + override protected caseEqualsOperation(EqualityOperation expr, Set cycleTracker) { BOOLEAN_WITH_NO_META } - override protected caseExistsOperation(RosettaExistsExpression expr, Map cycleTracker) { + override protected caseExistsOperation(RosettaExistsExpression expr, Set cycleTracker) { BOOLEAN_WITH_NO_META } - override protected caseFeatureCall(RosettaFeatureCall expr, Map cycleTracker) { + override protected caseFeatureCall(RosettaFeatureCall expr, Set cycleTracker) { val feature = expr.feature if (!extensions.isResolved(feature)) { return NOTHING_WITH_NO_META @@ -390,7 +380,7 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { + override protected caseDeepFeatureCall(RosettaDeepFeatureCall expr, Set cycleTracker) { val feature = expr.feature if (!extensions.isResolved(feature)) { return NOTHING_WITH_NO_META @@ -398,51 +388,51 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { + override protected caseFilterOperation(FilterOperation expr, Set cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseFirstOperation(FirstOperation expr, Map cycleTracker) { + override protected caseFirstOperation(FirstOperation expr, Set cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseFlattenOperation(FlattenOperation expr, Map cycleTracker) { + override protected caseFlattenOperation(FlattenOperation expr, Set cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseGreaterThanOperation(ComparisonOperation expr, Map cycleTracker) { + override protected caseGreaterThanOperation(ComparisonOperation expr, Set cycleTracker) { BOOLEAN_WITH_NO_META } - override protected caseGreaterThanOrEqualOperation(ComparisonOperation expr, Map cycleTracker) { + override protected caseGreaterThanOrEqualOperation(ComparisonOperation expr, Set cycleTracker) { BOOLEAN_WITH_NO_META } - override protected caseImplicitVariable(RosettaImplicitVariable expr, Map cycleTracker) { + override protected caseImplicitVariable(RosettaImplicitVariable expr, Set cycleTracker) { safeTypeOfImplicitVariable(expr, cycleTracker) } - override protected caseIntLiteral(RosettaIntLiteral expr, Map cycleTracker) { + override protected caseIntLiteral(RosettaIntLiteral expr, Set cycleTracker) { constrainedInt(if (expr.value.signum >= 0) expr.value.toString.length else expr.value.toString.length - 1, expr.value, expr.value).withEmptyMeta } - override protected caseJoinOperation(JoinOperation expr, Map cycleTracker) { + override protected caseJoinOperation(JoinOperation expr, Set cycleTracker) { UNCONSTRAINED_STRING_WITH_NO_META } - override protected caseLastOperation(LastOperation expr, Map cycleTracker) { + override protected caseLastOperation(LastOperation expr, Set cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseLessThanOperation(ComparisonOperation expr, Map cycleTracker) { + override protected caseLessThanOperation(ComparisonOperation expr, Set cycleTracker) { BOOLEAN_WITH_NO_META } - override protected caseLessThanOrEqualOperation(ComparisonOperation expr, Map cycleTracker) { + override protected caseLessThanOrEqualOperation(ComparisonOperation expr, Set cycleTracker) { BOOLEAN_WITH_NO_META } - override protected caseListLiteral(ListLiteral expr, Map cycleTracker) { + override protected caseListLiteral(ListLiteral expr, Set cycleTracker) { val types = expr.elements.map[RMetaAnnotatedType].filter[it !== null] val joined = types.joinMetaAnnotatedTypes if (ANY_WITH_NO_META.isSubtypeOf(joined)) { @@ -452,19 +442,19 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { + override protected caseMapOperation(MapOperation expr, Set cycleTracker) { expr.function?.body?.safeRType(cycleTracker) ?: NOTHING_WITH_NO_META } - override protected caseMaxOperation(MaxOperation expr, Map cycleTracker) { + override protected caseMaxOperation(MaxOperation expr, Set cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseMinOperation(MinOperation expr, Map cycleTracker) { + override protected caseMinOperation(MinOperation expr, Set cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseMultiplyOperation(ArithmeticOperation expr, Map cycleTracker) { + override protected caseMultiplyOperation(ArithmeticOperation expr, Set cycleTracker) { val left = expr.left.safeRType(cycleTracker) val right = expr.right.safeRType(cycleTracker) keepTypeAliasIfPossible(left.RType, right.RType, [l,r| @@ -480,50 +470,50 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { + override protected caseNotEqualsOperation(EqualityOperation expr, Set cycleTracker) { BOOLEAN_WITH_NO_META } - override protected caseNumberLiteral(RosettaNumberLiteral expr, Map cycleTracker) { + override protected caseNumberLiteral(RosettaNumberLiteral expr, Set cycleTracker) { if (expr.value === null) { // In case of a parse error return NOTHING_WITH_NO_META } constrainedNumber(expr.value.toPlainString.replaceAll("\\.|\\-", "").length, Math.max(0, expr.value.scale), expr.value, expr.value).withEmptyMeta } - override protected caseOneOfOperation(OneOfOperation expr, Map cycleTracker) { + override protected caseOneOfOperation(OneOfOperation expr, Set cycleTracker) { BOOLEAN_WITH_NO_META } - override protected caseOnlyElementOperation(RosettaOnlyElement expr, Map cycleTracker) { + override protected caseOnlyElementOperation(RosettaOnlyElement expr, Set cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseOnlyExists(RosettaOnlyExistsExpression expr, Map cycleTracker) { + override protected caseOnlyExists(RosettaOnlyExistsExpression expr, Set cycleTracker) { BOOLEAN_WITH_NO_META } - override protected caseOrOperation(LogicalOperation expr, Map cycleTracker) { + override protected caseOrOperation(LogicalOperation expr, Set cycleTracker) { BOOLEAN_WITH_NO_META } - override protected caseReduceOperation(ReduceOperation expr, Map cycleTracker) { + override protected caseReduceOperation(ReduceOperation expr, Set cycleTracker) { expr.function?.body?.safeRType(cycleTracker) ?: NOTHING_WITH_NO_META } - override protected caseReverseOperation(ReverseOperation expr, Map cycleTracker) { + override protected caseReverseOperation(ReverseOperation expr, Set cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseSortOperation(SortOperation expr, Map cycleTracker) { + override protected caseSortOperation(SortOperation expr, Set cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseStringLiteral(RosettaStringLiteral expr, Map cycleTracker) { + override protected caseStringLiteral(RosettaStringLiteral expr, Set cycleTracker) { constrainedString(expr.value.length, expr.value.length).withEmptyMeta } - override protected caseSubtractOperation(ArithmeticOperation expr, Map cycleTracker) { + override protected caseSubtractOperation(ArithmeticOperation expr, Set cycleTracker) { val left = expr.left.safeRType(cycleTracker) val right = expr.right.safeRType(cycleTracker) if (left.isSubtypeOf(NOTHING_WITH_NO_META)) { @@ -547,11 +537,11 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { + override protected caseSumOperation(SumOperation expr, Set cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseSymbolReference(RosettaSymbolReference expr, Map cycleTracker) { + override protected caseSymbolReference(RosettaSymbolReference expr, Set cycleTracker) { if (expr.symbol instanceof RosettaExternalFunction) { val fun = expr.symbol as RosettaExternalFunction val returnType = fun.safeRType(expr, cycleTracker) @@ -568,49 +558,49 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { + override protected caseThenOperation(ThenOperation expr, Set cycleTracker) { expr.function?.body?.safeRType(cycleTracker) ?: NOTHING_WITH_NO_META } - override protected caseToEnumOperation(ToEnumOperation expr, Map cycleTracker) { + override protected caseToEnumOperation(ToEnumOperation expr, Set cycleTracker) { expr.enumeration.buildREnumType.withEmptyMeta } - override protected caseToIntOperation(ToIntOperation expr, Map cycleTracker) { + override protected caseToIntOperation(ToIntOperation expr, Set cycleTracker) { UNCONSTRAINED_INT_WITH_NO_META } - override protected caseToNumberOperation(ToNumberOperation expr, Map cycleTracker) { + override protected caseToNumberOperation(ToNumberOperation expr, Set cycleTracker) { UNCONSTRAINED_NUMBER_WITH_NO_META } - override protected caseToStringOperation(ToStringOperation expr, Map cycleTracker) { + override protected caseToStringOperation(ToStringOperation expr, Set cycleTracker) { UNCONSTRAINED_STRING_WITH_NO_META } - override protected caseToTimeOperation(ToTimeOperation expr, Map cycleTracker) { + override protected caseToTimeOperation(ToTimeOperation expr, Set cycleTracker) { TIME_WITH_NO_META } - override protected caseConstructorExpression(RosettaConstructorExpression expr, Map cycleTracker) { + override protected caseConstructorExpression(RosettaConstructorExpression expr, Set cycleTracker) { expr.typeCall.typeCallToRType.withEmptyMeta } - override protected caseToDateOperation(ToDateOperation expr, Map cycleTracker) { + override protected caseToDateOperation(ToDateOperation expr, Set cycleTracker) { DATE_WITH_NO_META } - override protected caseToDateTimeOperation(ToDateTimeOperation expr, Map cycleTracker) { + override protected caseToDateTimeOperation(ToDateTimeOperation expr, Set cycleTracker) { DATE_TIME_WITH_NO_META } - override protected caseToZonedDateTimeOperation(ToZonedDateTimeOperation expr, Map cycleTracker) { + override protected caseToZonedDateTimeOperation(ToZonedDateTimeOperation expr, Set cycleTracker) { ZONED_DATE_TIME_WITH_NO_META } - override protected caseSwitchOperation(SwitchOperation expr, Map context) { + override protected caseSwitchOperation(SwitchOperation expr, Set cycleTracker) { expr.cases - .map[it.expression.RMetaAnnotatedType] + .map[it.expression.safeRType(cycleTracker)] .joinMetaAnnotatedTypes } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/TypeSystem.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/TypeSystem.java index 1609d453c..2b66a6120 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/TypeSystem.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/TypeSystem.java @@ -16,12 +16,16 @@ package com.regnosys.rosetta.types; +import java.util.ArrayList; import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.function.BiFunction; +import java.util.stream.Collectors; import javax.inject.Inject; import javax.inject.Provider; @@ -29,21 +33,36 @@ import org.apache.commons.lang3.Validate; import com.regnosys.rosetta.cache.IRequestScopedCache; +import com.regnosys.rosetta.interpreter.RosettaInterpreter; import com.regnosys.rosetta.interpreter.RosettaInterpreterContext; +import com.regnosys.rosetta.interpreter.RosettaValue; +import com.regnosys.rosetta.rosetta.RosettaBuiltinType; +import com.regnosys.rosetta.rosetta.RosettaEnumeration; import com.regnosys.rosetta.rosetta.RosettaExternalRuleSource; +import com.regnosys.rosetta.rosetta.RosettaMetaType; import com.regnosys.rosetta.rosetta.RosettaRule; +import com.regnosys.rosetta.rosetta.RosettaType; +import com.regnosys.rosetta.rosetta.RosettaTypeAlias; import com.regnosys.rosetta.rosetta.TypeCall; +import com.regnosys.rosetta.rosetta.TypeParameter; +import com.regnosys.rosetta.rosetta.expression.ExpressionFactory; +import com.regnosys.rosetta.rosetta.expression.RosettaSymbolReference; +import com.regnosys.rosetta.rosetta.simple.Choice; +import com.regnosys.rosetta.rosetta.simple.Data; import com.regnosys.rosetta.types.builtin.RBuiltinTypeService; -import com.regnosys.rosetta.typing.RosettaTyping; import com.regnosys.rosetta.utils.ExternalAnnotationUtil; +import com.regnosys.rosetta.utils.ModelIdProvider; +import com.regnosys.rosetta.utils.RosettaSimpleSystemSolver; +import com.regnosys.rosetta.utils.RosettaSimpleSystemSolver.Equation; +import com.rosetta.model.lib.ModelSymbolId; import org.eclipse.xtext.xbase.lib.Pair; public class TypeSystem { public static String RULE_INPUT_TYPE_CACHE_KEY = TypeSystem.class.getCanonicalName() + ".RULE_INPUT_TYPE"; - + @Inject - private RosettaTyping typing; + private RObjectFactory factory; @Inject private RBuiltinTypeService builtins; @Inject @@ -52,6 +71,12 @@ public class TypeSystem { private IRequestScopedCache cache; @Inject private SubtypeRelation subtypeRelation; + @Inject + private RosettaInterpreter interpreter; + @Inject + private RosettaSimpleSystemSolver systemSolver; + @Inject + private ModelIdProvider modelIdProvider; public RType getRulesInputType(RDataType data, Optional source) { return getRulesInputType(data, source, new HashSet<>()); @@ -174,12 +199,6 @@ public boolean isSubtypeOf(RType sub, RType sup, boolean treatChoiceTypeAsData) return subtypeRelation.isSubtypeOf(sub, sup, treatChoiceTypeAsData); } - public boolean isListSubtypeOf(RListType sub, RListType sup) { - Objects.requireNonNull(sub); - Objects.requireNonNull(sup); - - return typing.listSubtypeSucceeded(sub, sup); - } public boolean isComparable(RMetaAnnotatedType t1, RMetaAnnotatedType t2) { Objects.requireNonNull(t1); @@ -187,12 +206,6 @@ public boolean isComparable(RMetaAnnotatedType t1, RMetaAnnotatedType t2) { return isSubtypeOf(t1, t2) || isSubtypeOf(t2, t1); } - public boolean isListComparable(RListType t1, RListType t2) { - Objects.requireNonNull(t1); - Objects.requireNonNull(t2); - - return typing.listComparable(t1, t2); - } public RType typeCallToRType(TypeCall typeCall) { return typeCallToRType(typeCall, new RosettaInterpreterContext()); @@ -202,7 +215,77 @@ public RType typeCallToRType(TypeCall typeCall, RosettaInterpreterContext contex Objects.requireNonNull(typeCall); Objects.requireNonNull(context); - return typing.typeCallToRType(typeCall, context); + RosettaType t = typeCall.getType(); + if (t instanceof Choice) { + return factory.buildRChoiceType((Choice) t); + } else if (t instanceof Data) { + return factory.buildRDataType((Data) t); + } else if (t instanceof RosettaEnumeration) { + return factory.buildREnumType((RosettaEnumeration) t); + } else if (t instanceof RosettaBuiltinType) { + Map argMap = typeCall.getArguments().stream() + .collect(Collectors.toMap(arg -> arg.getParameter().getName(), arg -> interpreter.interpret(arg.getValue(), context))); + return builtins.getType(t, argMap).orElse(builtins.NOTHING); + } else if (t instanceof RosettaMetaType) { + Map argMap = typeCall.getArguments().stream() + .collect(Collectors.toMap(arg -> arg.getParameter().getName(), arg -> interpreter.interpret(arg.getValue(), context))); + return builtins.getType(t, argMap) + .orElseGet(() -> typeCallToRType(((RosettaMetaType) t).getTypeCall(), context)); + } else if (t instanceof RosettaTypeAlias) { + RosettaTypeAlias alias = (RosettaTypeAlias) t; + LinkedHashMap args = new LinkedHashMap<>(); + Set absentParameters = new HashSet<>(((RosettaTypeAlias) t).getParameters()); + typeCall.getArguments().forEach(arg -> { + RosettaValue eval = interpreter.interpret(arg.getValue(), context); + args.put(arg.getParameter().getName(), eval); + absentParameters.remove(arg.getParameter()); + }); + absentParameters.forEach(p -> args.put(p.getName(), RosettaValue.empty())); + RType refersTo = typeCallToRType(alias.getTypeCall(), RosettaInterpreterContext.of(args)); + return new RAliasType(typeFunctionOfTypeAlias(alias), args, refersTo); + } + return builtins.NOTHING; + } + + private RTypeFunction typeFunctionOfTypeAlias(RosettaTypeAlias typeAlias) { + if (typeAlias.getName().equals(builtins.INT_NAME)) { + return builtins.INT_FUNCTION; + } + ModelSymbolId symbolId = modelIdProvider.getSymbolId(typeAlias); + List equations = + typeAlias.getTypeCall().getArguments().stream().map(arg -> { + RosettaSymbolReference ref = ExpressionFactory.eINSTANCE.createRosettaSymbolReference(); + ref.setGenerated(true); + ref.setSymbol(arg.getParameter()); + return new Equation(ref, arg.getValue()); + }).collect(Collectors.toList()); + return systemSolver.solve(equations, new HashSet<>(typeAlias.getParameters())).map(solutionSet -> + new RTypeFunction(symbolId.getNamespace(), symbolId.getName()) { + @Override + public RType evaluate(Map arguments) { + return typeCallToRType(typeAlias.getTypeCall(), RosettaInterpreterContext.of(arguments)); + } + @Override + public Optional> reverse(RType type) { + if (!(type instanceof RParametrizedType)) { + return Optional.empty(); + } + RosettaInterpreterContext context = RosettaInterpreterContext.of(((RParametrizedType)type).getArguments()); + return solutionSet.getSolution(context).map(solution -> { + LinkedHashMap newArgs = new LinkedHashMap<>(); + typeAlias.getParameters().forEach(p -> newArgs.put(p.getName(), solution.get(p))); + return newArgs; + }); + } + } + ).orElseGet(() -> + new RTypeFunction(symbolId.getNamespace(), symbolId.getName()) { + @Override + public RType evaluate(Map arguments) { + return typeCallToRType(typeAlias.getTypeCall(), RosettaInterpreterContext.of(arguments)); + } + } + ); } public RType keepTypeAliasIfPossible(RType t1, RType t2, BiFunction combineUnderlyingTypes) { @@ -210,7 +293,45 @@ public RType keepTypeAliasIfPossible(RType t1, RType t2, BiFunctionmap(args -> new RAliasType(typeFunc, args, underlier)) + .orElse(underlier); + } else { + List superAliases = new ArrayList<>(); + RAliasType curr = alias1; + superAliases.add(curr); + while (curr.getRefersTo() instanceof RAliasType) { + curr = (RAliasType) curr.getRefersTo(); + superAliases.add(curr); + } + curr = alias2; + RTypeFunction tf1 = curr.getTypeFunction(); + Optional match = superAliases.stream().filter(a -> tf1.equals(a.getTypeFunction())).findFirst(); + if (match.isPresent()) { + return keepTypeAliasIfPossible(match.get(), curr, combineUnderlyingTypes); + } + while (curr.getRefersTo() instanceof RAliasType) { + curr = (RAliasType) curr.getRefersTo(); + RTypeFunction tf2 = curr.getTypeFunction(); + match = superAliases.stream().filter(a -> tf2.equals(a.getTypeFunction())).findFirst(); + if (match.isPresent()) { + return keepTypeAliasIfPossible(match.get(), curr, combineUnderlyingTypes); + } + } + return keepTypeAliasIfPossible(alias1.getRefersTo(), alias2.getRefersTo(), combineUnderlyingTypes); + } + } else if (t1 instanceof RAliasType) { + return keepTypeAliasIfPossible(((RAliasType) t1).getRefersTo(), t2, combineUnderlyingTypes); + } else if (t2 instanceof RAliasType) { + return keepTypeAliasIfPossible(t1, ((RAliasType) t2).getRefersTo(), combineUnderlyingTypes); + } + return combineUnderlyingTypes.apply(t1, t2); } public RType stripFromTypeAliases(RType t) { diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/StandaloneRosettaTypingValidator.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/ReportValidator.java similarity index 84% rename from rosetta-lang/src/main/java/com/regnosys/rosetta/validation/StandaloneRosettaTypingValidator.java rename to rosetta-lang/src/main/java/com/regnosys/rosetta/validation/ReportValidator.java index 49446e6cf..ac670f4ff 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/StandaloneRosettaTypingValidator.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/ReportValidator.java @@ -30,8 +30,6 @@ import com.regnosys.rosetta.rosetta.RosettaExternalRuleSource; import com.regnosys.rosetta.rosetta.RosettaReport; import com.regnosys.rosetta.rosetta.RosettaRule; -import com.regnosys.rosetta.rosetta.expression.ChoiceOperation; -import com.regnosys.rosetta.rosetta.expression.RosettaOnlyElement; import com.regnosys.rosetta.rosetta.simple.Attribute; import com.regnosys.rosetta.rosetta.simple.Data; import com.regnosys.rosetta.rosetta.simple.RosettaRuleReference; @@ -40,23 +38,17 @@ import com.regnosys.rosetta.types.RChoiceType; import com.regnosys.rosetta.types.RDataType; import com.regnosys.rosetta.types.RObjectFactory; -import com.regnosys.rosetta.types.TypeFactory; import com.regnosys.rosetta.types.TypeSystem; import com.regnosys.rosetta.types.builtin.RBuiltinTypeService; import com.regnosys.rosetta.utils.ExternalAnnotationUtil; -import static com.regnosys.rosetta.rosetta.expression.ExpressionPackage.Literals.*; import static com.regnosys.rosetta.rosetta.RosettaPackage.Literals.*; import static com.regnosys.rosetta.rosetta.simple.SimplePackage.Literals.*; -// TODO: remove -public class StandaloneRosettaTypingValidator extends AbstractDeclarativeRosettaValidator { +public class ReportValidator extends AbstractDeclarativeRosettaValidator { @Inject private TypeSystem ts; - @Inject - private TypeFactory tf; - @Inject private RBuiltinTypeService builtins; @@ -66,37 +58,6 @@ public class StandaloneRosettaTypingValidator extends AbstractDeclarativeRosetta @Inject private RObjectFactory objectFactory; - /** - * Xsemantics does not allow raising warnings. See https://github.com/eclipse/xsemantics/issues/149. - */ - @Check - public void checkOnlyElement(RosettaOnlyElement e) { - // TODO: restore -// RListType t = ts.inferType(e.getArgument()); -// if (t != null) { -// RosettaCardinality minimalConstraint = tf.createConstraint(1, 2); -// if (!minimalConstraint.isSubconstraintOf(t.getConstraint())) { -// warning(tu.notLooserConstraintMessage(minimalConstraint, t), e, ROSETTA_UNARY_OPERATION__ARGUMENT); -// } -// } - } - - /** - * Xsemantics does not allow raising errors on a specific index of a multi-valued feature. - * See https://github.com/eclipse/xsemantics/issues/64. - */ - @Check - public void checkChoiceOperationHasNoDuplicateAttributes(ChoiceOperation e) { - for (var i = 1; i < e.getAttributes().size(); i++) { - Attribute attr = e.getAttributes().get(i); - for (var j = 0; j < i; j++) { - if (attr.equals(e.getAttributes().get(j))) { - error("Duplicate attribute.", e, CHOICE_OPERATION__ATTRIBUTES, i); - } - } - } - } - @Check public void checkReport(RosettaReport report) { RType inputType = ts.typeCallToRType(report.getInputType()); diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RetainXsemanticsIssuesOnGeneratedInputsFilter.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RetainXsemanticsIssuesOnGeneratedInputsFilter.java deleted file mode 100644 index cd23ce2c0..000000000 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RetainXsemanticsIssuesOnGeneratedInputsFilter.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2024 REGnosys - * - * 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. - */ - -package com.regnosys.rosetta.validation; - -import java.util.LinkedList; -import java.util.List; -import java.util.stream.Collectors; - -import org.eclipse.emf.ecore.EObject; -import org.eclipse.xsemantics.runtime.ErrorInformation; -import org.eclipse.xsemantics.runtime.RuleFailedException; -import org.eclipse.xsemantics.runtime.validation.XsemanticsValidatorFilter; -import org.eclipse.xtext.nodemodel.util.NodeModelUtils; - -import com.google.common.collect.Lists; -import com.regnosys.rosetta.rosetta.expression.RosettaExpression; - -/** - * By default, Xsemantics removes validation issues when they point to a - * source that isn't represented by a node, e.g., when the source is generated - * by our {@code RosettaDerivedStateComputer}. This class fixes that. - */ -public class RetainXsemanticsIssuesOnGeneratedInputsFilter extends XsemanticsValidatorFilter { - @Override - public Iterable filterRuleFailedExceptions( - RuleFailedException e) { - final RuleFailedException inner = innermostRuleFailedExceptionWithNodeModelSourcesOrGenerated(e); - if (inner != null) { - return Lists.newArrayList(inner); - } - // we must return at least a failure, so we default to the passed one - return Lists.newArrayList(e); - } - - @Override - public Iterable filterErrorInformation( - RuleFailedException e) { - return filteredErrorInformation(e); - } - - /** - * Adjustment of {@code TraceUtils::innermostRuleFailedExceptionWithNodeModelSources} - */ - private RuleFailedException innermostRuleFailedExceptionWithNodeModelSourcesOrGenerated(RuleFailedException e) { - List failures = traceUtils.failureAsList(e); - for (int i=failures.size()-1; i>=0; i--) { - RuleFailedException failure = failures.get(i); - if (!filteredErrorInformation(failure).isEmpty()) { - return failure; - } - } - return null; - } - - /** - * Adjustment of {@code TraceUtils::filteredErrorInformation} - */ - private List filteredErrorInformation(RuleFailedException e) { - return removeNonNodeModelSourcesIfNotGenerated( - traceUtils.removeDuplicateErrorInformation( - traceUtils.allErrorInformation( - e))); - } - - /** - * Adjustment of {@code TraceUtils::removeNonNodeModelSources} - */ - private List removeNonNodeModelSourcesIfNotGenerated(List errorInformations) { - return errorInformations.stream() - .filter(errorInfo -> { - EObject source = errorInfo.getSource(); - if (source instanceof RosettaExpression) { - if (((RosettaExpression)source).isGenerated()) { - return true; - } - } - return NodeModelUtils.getNode(source) != null; - }) - .collect(Collectors.toCollection(LinkedList::new)); - } -} diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaValidator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaValidator.xtend index 83ef324cb..2cf2dca01 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaValidator.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaValidator.xtend @@ -4,13 +4,14 @@ package com.regnosys.rosetta.validation import org.eclipse.xtext.validation.ComposedChecks +import com.regnosys.rosetta.validation.expression.ExpressionValidator /** * This class contains custom validation rules. * * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#validation */ -@ComposedChecks(validators = #[RosettaSimpleValidator, StandaloneRosettaTypingValidator, EnumValidator, ChoiceValidator, ExpressionValidator]) +@ComposedChecks(validators = #[RosettaSimpleValidator, ReportValidator, EnumValidator, ChoiceValidator, ExpressionValidator]) class RosettaValidator extends AbstractRosettaValidator { diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/AbstractExpressionValidator.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/AbstractExpressionValidator.java new file mode 100644 index 000000000..b3825b6ea --- /dev/null +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/AbstractExpressionValidator.java @@ -0,0 +1,162 @@ +package com.regnosys.rosetta.validation.expression; + +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.inject.Inject; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EStructuralFeature; + +import com.regnosys.rosetta.rosetta.expression.RosettaBinaryOperation; +import com.regnosys.rosetta.rosetta.expression.RosettaExpression; +import com.regnosys.rosetta.rosetta.expression.RosettaOperation; +import com.regnosys.rosetta.types.CardinalityProvider; +import com.regnosys.rosetta.types.RMetaAnnotatedType; +import com.regnosys.rosetta.types.RType; +import com.regnosys.rosetta.types.RosettaTypeProvider; +import com.regnosys.rosetta.types.TypeSystem; +import com.regnosys.rosetta.types.builtin.RBuiltinTypeService; +import com.regnosys.rosetta.validation.AbstractDeclarativeRosettaValidator; + +public class AbstractExpressionValidator extends AbstractDeclarativeRosettaValidator { + @Inject + protected RosettaTypeProvider typeProvider; + @Inject + protected TypeSystem typeSystem; + @Inject + protected RBuiltinTypeService builtins; + @Inject + protected CardinalityProvider cardinalityProvider; + + protected String relevantTypeDescription(RMetaAnnotatedType type, RMetaAnnotatedType context) { + RType valueType = type.getRType(); + RType valueContext = context.getRType(); + if (valueType.equals(valueContext)) { + // Include meta info + return type.toString(); + } + if (valueType.getName().equals(valueContext.getName())) { + // Include type parameters + return valueType.toString(); + } + return valueType.getName(); + } + + protected String notASubtypeMessage(RMetaAnnotatedType expected, RMetaAnnotatedType actual) { + return new StringBuilder() + .append("Expected type `") + .append(relevantTypeDescription(expected, actual)) + .append("`, but got `") + .append(relevantTypeDescription(actual, expected)) + .append("` instead") + .toString(); + } + protected boolean subtypeCheck(RMetaAnnotatedType expected, RosettaExpression expr, EObject sourceObject, EStructuralFeature feature) { + return subtypeCheck(expected, typeProvider.getRMetaAnnotatedType(expr), sourceObject, feature, INSIGNIFICANT_INDEX); + } + protected boolean subtypeCheck(RMetaAnnotatedType expected, RosettaExpression expr, EObject sourceObject, EStructuralFeature feature, int featureIndex) { + return subtypeCheck(expected, typeProvider.getRMetaAnnotatedType(expr), sourceObject, feature, featureIndex); + } + protected boolean subtypeCheck(RMetaAnnotatedType expected, RMetaAnnotatedType actual, EObject sourceObject, EStructuralFeature feature) { + return subtypeCheck(expected, actual, sourceObject, feature, INSIGNIFICANT_INDEX); + } + protected boolean subtypeCheck(RMetaAnnotatedType expected, RMetaAnnotatedType actual, EObject sourceObject, EStructuralFeature feature, int featureIndex) { + if (!typeSystem.isSubtypeOf(actual, expected)) { + error(notASubtypeMessage(expected, actual), sourceObject, feature, featureIndex); + return false; + } + return true; + } + + protected String notComparableMessage(RMetaAnnotatedType left, RMetaAnnotatedType right) { + return new StringBuilder() + .append("Types `") + .append(relevantTypeDescription(left, right)) + .append("` and `") + .append(relevantTypeDescription(right, left)) + .append("` are not comparable") + .toString(); + } + protected boolean comparableTypeCheck(RosettaBinaryOperation sourceObject) { + RMetaAnnotatedType tl = typeProvider.getRMetaAnnotatedType(sourceObject.getLeft()); + RMetaAnnotatedType tr = typeProvider.getRMetaAnnotatedType(sourceObject.getRight()); + if (!typeSystem.isComparable(tl, tr)) { + error(notComparableMessage(tl, tr), sourceObject, null); + return false; + } + return true; + } + + protected boolean isMultiCheck(RosettaExpression expr, EObject sourceObject, EStructuralFeature feature) { + return isMultiCheck(expr, sourceObject, feature, INSIGNIFICANT_INDEX); + } + protected boolean isMultiCheck(RosettaExpression expr, EObject sourceObject, EStructuralFeature feature, int featureIndex) { + if (!cardinalityProvider.isMulti(expr)) { + error("Expecting multi cardinality", sourceObject, feature, featureIndex); + return false; + } + return true; + } + protected boolean isSingleCheck(RosettaExpression expr, EObject sourceObject, EStructuralFeature feature) { + return isSingleCheck(expr, sourceObject, feature, INSIGNIFICANT_INDEX); + } + protected boolean isSingleCheck(RosettaExpression expr, EObject sourceObject, EStructuralFeature feature, int featureIndex) { + if (cardinalityProvider.isMulti(expr)) { + error("Expecting single cardinality", sourceObject, feature, featureIndex); + return false; + } + return true; + } + + protected boolean commonTypeCheck(List expressions, EObject sourceObject, EStructuralFeature feature) { + boolean haveCommonType = true; + if (!expressions.isEmpty()) { + Set types = new LinkedHashSet<>(); + RMetaAnnotatedType firstElemType = typeProvider.getRMetaAnnotatedType(expressions.get(0)); + types.add(firstElemType); + RMetaAnnotatedType commonType = firstElemType; + for (int i=1; i "`" + relevantTypeDescription(t, elemType) + "`").collect(Collectors.joining(", ")) + " and `" + relevantTypeDescription(elemType, newCommonType) + "` do not have a common supertype", + sourceObject, + feature, + feature == null || !feature.isMany() ? INSIGNIFICANT_INDEX : i); + haveCommonType = false; + } else { + types.add(elemType); + commonType = newCommonType; + } + } + } + return haveCommonType; + } + + protected void unsupportedTypeError(RMetaAnnotatedType type, RosettaOperation op, EStructuralFeature feature, RType supportedType1, RType supportedType2, RType... moreSupportedTypes) { + StringBuilder supportedTypesMsg = new StringBuilder(); + supportedTypesMsg.append("Supported types are "); + supportedTypesMsg.append(supportedType1); + if (moreSupportedTypes.length > 0) { + supportedTypesMsg.append(", "); + supportedTypesMsg.append(supportedType2); + for (int i=0; i expressions, EObject sourceObject, EStructuralFeature feature) { - boolean haveCommonType = true; - if (!expressions.isEmpty()) { - Set types = new LinkedHashSet<>(); - RMetaAnnotatedType firstElemType = typeProvider.getRMetaAnnotatedType(expressions.get(0)); - types.add(firstElemType); - RMetaAnnotatedType commonType = firstElemType; - for (int i=1; i "`" + relevantTypeDescription(t, elemType) + "`").collect(Collectors.joining(", ")) + " and `" + relevantTypeDescription(elemType, newCommonType) + "` do not have a common supertype", - sourceObject, - feature, - feature == null || !feature.isMany() ? INSIGNIFICANT_INDEX : i); - haveCommonType = false; - } else { - types.add(elemType); - commonType = newCommonType; - } - } - } - return haveCommonType; - } - - private void unsupportedTypeError(RMetaAnnotatedType type, RosettaOperation op, EStructuralFeature feature, RType supportedType1, RType supportedType2, RType... moreSupportedTypes) { - StringBuilder supportedTypesMsg = new StringBuilder(); - supportedTypesMsg.append("Supported types are "); - supportedTypesMsg.append(supportedType1); - if (moreSupportedTypes.length > 0) { - supportedTypesMsg.append(", "); - supportedTypesMsg.append(supportedType2); - for (int i=0; i seen = new HashSet<>(); + for (var i = 0; i < op.getAttributes().size(); i++) { + Attribute attr = op.getAttributes().get(i); + if (!seen.add(attr)) { + error("Duplicate attribute.", op, CHOICE_OPERATION__ATTRIBUTES, i); + } + } } @Check @@ -519,118 +369,14 @@ public void checkJoinOperation(JoinOperation op) { } @Check - public void checkSwitch(SwitchOperation op) { - isSingleCheck(op.getArgument(), op, ROSETTA_UNARY_OPERATION__ARGUMENT); - RMetaAnnotatedType argumentType = typeProvider.getRMetaAnnotatedType(op.getArgument()); - RType rType = typeSystem.stripFromTypeAliases(argumentType.getRType()); - if (rType instanceof REnumType) { - checkEnumSwitch((REnumType) rType, op); - } else if (rType instanceof RBasicType) { - checkBasicTypeSwitch((RBasicType) rType, op); - } else if (rType instanceof RChoiceType) { - checkChoiceSwitch((RChoiceType) rType, op); - } else { - unsupportedTypeError(argumentType, op.getOperator(), op, ROSETTA_UNARY_OPERATION__ARGUMENT, "Supported argument types are basic types, enumerations, and choice types"); - } - } - private void checkEnumSwitch(REnumType argumentType, SwitchOperation op) { - // When the argument is an enum: - // - all guards should be enum guards, - // - there are no duplicate cases, - // - all enum values must be covered. - Set seenValues = new HashSet<>(); - for (SwitchCase caseStatement : op.getCases()) { - RosettaEnumValue guard = caseStatement.getGuard().getEnumGuard(); - if (guard == null) { - error("Case should match an enum value of " + argumentType, caseStatement, SWITCH_CASE__GUARD); - } else { - if (!seenValues.add(guard)) { - error("Duplicate case " + guard.getName(), caseStatement, SWITCH_CASE__GUARD); - } - } - } - - if (op.getDefault() == null) { - List missingEnumValues = new ArrayList<>(argumentType.getAllEnumValues()); - missingEnumValues.removeAll(seenValues); - if (!missingEnumValues.isEmpty()) { - String missingValuesMsg = missingEnumValues.stream().map(v -> v.getName()).collect(Collectors.joining(", ")); - error("Missing the following cases: " + missingValuesMsg + ". Either provide all or add a default.", op, ROSETTA_OPERATION__OPERATOR); - } - } - } - private void checkBasicTypeSwitch(RBasicType argumentType, SwitchOperation op) { - // When the argument is a basic type: - // - all guards should be literal guards, - // - there are no duplicate cases, - // - all guards should be comparable to the input. - Set seenValues = new HashSet<>(); - RMetaAnnotatedType argumentTypeWithoutMeta = RMetaAnnotatedType.withEmptyMeta(argumentType); - for (SwitchCase caseStatement : op.getCases()) { - RosettaLiteral guard = caseStatement.getGuard().getLiteralGuard(); - if (guard == null) { - error("Case should match a literal of type " + argumentType, caseStatement, SWITCH_CASE__GUARD); - } else { - if (!seenValues.add(interpreter.interpret(guard))) { - error("Duplicate case", caseStatement, SWITCH_CASE__GUARD); - } - RMetaAnnotatedType conditionType = typeProvider.getRMetaAnnotatedType(guard); - if (!typeSystem.isComparable(conditionType, argumentTypeWithoutMeta)) { - error("Invalid case: " + notComparableMessage(conditionType, argumentTypeWithoutMeta), caseStatement, SWITCH_CASE__GUARD); - } - } - } - } - private void checkChoiceSwitch(RChoiceType argumentType, SwitchOperation op) { - // When the argument is a choice type: - // - all guards should be choice option guards, - // - all cases should be reachable, - // - all choice options should be covered. - Map includedOptions = new HashMap<>(); - for (SwitchCase caseStatement : op.getCases()) { - ChoiceOption guard = caseStatement.getGuard().getChoiceOptionGuard(); - if (guard == null) { - error("Case should match a choice option of type " + argumentType, caseStatement, SWITCH_CASE__GUARD); - } else { - RMetaAnnotatedType alreadyCovered = includedOptions.get(guard); - if (alreadyCovered != null) { - error("Case already covered by " + alreadyCovered, caseStatement, SWITCH_CASE__GUARD); - } else { - RMetaAnnotatedType guardType = typeProvider.getRTypeOfSymbol(guard); - includedOptions.put(guard, guardType); - RType valueType = guardType.getRType(); - if (valueType instanceof RChoiceType) { - ((RChoiceType)valueType).getAllOptions().forEach(it -> includedOptions.put(it.getEObject(), guardType)); - } - } - } - } - if (op.getDefault() == null) { - List missingOptions = new ArrayList<>(); - argumentType.getOwnOptions().forEach(opt -> missingOptions.add(opt.getType())); - for (RMetaAnnotatedType guard : new LinkedHashSet<>(includedOptions.values())) { - for (var i=0; i missingOptions.add(o.getType())); - } - } - } - } - if (!missingOptions.isEmpty()) { - String missingOptsMsg = missingOptions.stream() - .map(opt -> opt.toString()) - .collect(Collectors.joining(", ")); - error("Missing the following cases: " + missingOptsMsg + ". Either provide all or add a default.", op, ROSETTA_OPERATION__OPERATOR); - } - } + public void checkOnlyElement(RosettaOnlyElement e) { + // TODO: restore +// RListType t = ts.inferType(e.getArgument()); +// if (t != null) { +// RosettaCardinality minimalConstraint = tf.createConstraint(1, 2); +// if (!minimalConstraint.isSubconstraintOf(t.getConstraint())) { +// warning(tu.notLooserConstraintMessage(minimalConstraint, t), e, ROSETTA_UNARY_OPERATION__ARGUMENT); +// } +// } } } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/SwitchValidator.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/SwitchValidator.java new file mode 100644 index 000000000..e4c0cfb65 --- /dev/null +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/SwitchValidator.java @@ -0,0 +1,150 @@ +package com.regnosys.rosetta.validation.expression; + +import static com.regnosys.rosetta.rosetta.expression.ExpressionPackage.Literals.*; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.inject.Inject; + +import org.eclipse.xtext.validation.Check; + +import com.regnosys.rosetta.interpreter.RosettaInterpreter; +import com.regnosys.rosetta.interpreter.RosettaValue; +import com.regnosys.rosetta.rosetta.RosettaEnumValue; +import com.regnosys.rosetta.rosetta.expression.RosettaLiteral; +import com.regnosys.rosetta.rosetta.expression.SwitchCase; +import com.regnosys.rosetta.rosetta.expression.SwitchOperation; +import com.regnosys.rosetta.rosetta.simple.ChoiceOption; +import com.regnosys.rosetta.types.RChoiceType; +import com.regnosys.rosetta.types.REnumType; +import com.regnosys.rosetta.types.RMetaAnnotatedType; +import com.regnosys.rosetta.types.RType; +import com.regnosys.rosetta.types.builtin.RBasicType; + +public class SwitchValidator extends ExpressionValidator { + @Inject + private RosettaInterpreter interpreter; + + @Check + public void checkSwitch(SwitchOperation op) { + isSingleCheck(op.getArgument(), op, ROSETTA_UNARY_OPERATION__ARGUMENT); + RMetaAnnotatedType argumentType = typeProvider.getRMetaAnnotatedType(op.getArgument()); + RType rType = typeSystem.stripFromTypeAliases(argumentType.getRType()); + if (rType instanceof REnumType) { + checkEnumSwitch((REnumType) rType, op); + } else if (rType instanceof RBasicType) { + checkBasicTypeSwitch((RBasicType) rType, op); + } else if (rType instanceof RChoiceType) { + checkChoiceSwitch((RChoiceType) rType, op); + } else { + unsupportedTypeError(argumentType, op.getOperator(), op, ROSETTA_UNARY_OPERATION__ARGUMENT, "Supported argument types are basic types, enumerations, and choice types"); + } + } + private void checkEnumSwitch(REnumType argumentType, SwitchOperation op) { + // When the argument is an enum: + // - all guards should be enum guards, + // - there are no duplicate cases, + // - all enum values must be covered. + Set seenValues = new HashSet<>(); + for (SwitchCase caseStatement : op.getCases()) { + RosettaEnumValue guard = caseStatement.getGuard().getEnumGuard(); + if (guard == null) { + error("Case should match an enum value of " + argumentType, caseStatement, SWITCH_CASE__GUARD); + } else { + if (!seenValues.add(guard)) { + error("Duplicate case " + guard.getName(), caseStatement, SWITCH_CASE__GUARD); + } + } + } + + if (op.getDefault() == null) { + List missingEnumValues = new ArrayList<>(argumentType.getAllEnumValues()); + missingEnumValues.removeAll(seenValues); + if (!missingEnumValues.isEmpty()) { + String missingValuesMsg = missingEnumValues.stream().map(v -> v.getName()).collect(Collectors.joining(", ")); + error("Missing the following cases: " + missingValuesMsg + ". Either provide all or add a default.", op, ROSETTA_OPERATION__OPERATOR); + } + } + } + private void checkBasicTypeSwitch(RBasicType argumentType, SwitchOperation op) { + // When the argument is a basic type: + // - all guards should be literal guards, + // - there are no duplicate cases, + // - all guards should be comparable to the input. + Set seenValues = new HashSet<>(); + RMetaAnnotatedType argumentTypeWithoutMeta = RMetaAnnotatedType.withEmptyMeta(argumentType); + for (SwitchCase caseStatement : op.getCases()) { + RosettaLiteral guard = caseStatement.getGuard().getLiteralGuard(); + if (guard == null) { + error("Case should match a literal of type " + argumentType, caseStatement, SWITCH_CASE__GUARD); + } else { + if (!seenValues.add(interpreter.interpret(guard))) { + error("Duplicate case", caseStatement, SWITCH_CASE__GUARD); + } + RMetaAnnotatedType conditionType = typeProvider.getRMetaAnnotatedType(guard); + if (!typeSystem.isComparable(conditionType, argumentTypeWithoutMeta)) { + error("Invalid case: " + notComparableMessage(conditionType, argumentTypeWithoutMeta), caseStatement, SWITCH_CASE__GUARD); + } + } + } + } + private void checkChoiceSwitch(RChoiceType argumentType, SwitchOperation op) { + // When the argument is a choice type: + // - all guards should be choice option guards, + // - all cases should be reachable, + // - all choice options should be covered. + Map includedOptions = new HashMap<>(); + for (SwitchCase caseStatement : op.getCases()) { + ChoiceOption guard = caseStatement.getGuard().getChoiceOptionGuard(); + if (guard == null) { + error("Case should match a choice option of type " + argumentType, caseStatement, SWITCH_CASE__GUARD); + } else { + RMetaAnnotatedType alreadyCovered = includedOptions.get(guard); + if (alreadyCovered != null) { + error("Case already covered by " + alreadyCovered, caseStatement, SWITCH_CASE__GUARD); + } else { + RMetaAnnotatedType guardType = typeProvider.getRTypeOfSymbol(guard); + includedOptions.put(guard, guardType); + RType valueType = guardType.getRType(); + if (valueType instanceof RChoiceType) { + ((RChoiceType)valueType).getAllOptions().forEach(it -> includedOptions.put(it.getEObject(), guardType)); + } + } + } + } + if (op.getDefault() == null) { + List missingOptions = new ArrayList<>(); + argumentType.getOwnOptions().forEach(opt -> missingOptions.add(opt.getType())); + for (RMetaAnnotatedType guard : new LinkedHashSet<>(includedOptions.values())) { + for (var i=0; i missingOptions.add(o.getType())); + } + } + } + } + if (!missingOptions.isEmpty()) { + String missingOptsMsg = missingOptions.stream() + .map(opt -> opt.toString()) + .collect(Collectors.joining(", ")); + error("Missing the following cases: " + missingOptsMsg + ". Either provide all or add a default.", op, ROSETTA_OPERATION__OPERATOR); + } + } + } +} diff --git a/rosetta-testing/src/main/java/com/regnosys/rosetta/tests/util/ExpressionValidationHelper.xtend b/rosetta-testing/src/main/java/com/regnosys/rosetta/tests/util/ExpressionValidationHelper.xtend index 06eceee69..6fbfd274b 100644 --- a/rosetta-testing/src/main/java/com/regnosys/rosetta/tests/util/ExpressionValidationHelper.xtend +++ b/rosetta-testing/src/main/java/com/regnosys/rosetta/tests/util/ExpressionValidationHelper.xtend @@ -2,7 +2,6 @@ package com.regnosys.rosetta.tests.util import com.regnosys.rosetta.rosetta.expression.RosettaExpression import com.regnosys.rosetta.validation.AbstractRosettaValidator -import com.regnosys.rosetta.validation.StandaloneRosettaTypingValidator import java.util.List import org.eclipse.emf.common.util.BasicDiagnostic import org.eclipse.emf.common.util.Diagnostic @@ -16,10 +15,11 @@ import static com.google.common.collect.Iterables.isEmpty import static org.junit.jupiter.api.Assertions.* import javax.inject.Inject import javax.inject.Named +import com.regnosys.rosetta.validation.ReportValidator class ExpressionValidationHelper { @Inject - extension StandaloneRosettaTypingValidator // TODO: replace this with RosettaValidator once old type system has been removed + extension ReportValidator // TODO: replace this with RosettaValidator once old type system has been removed @Inject@Named(Constants.LANGUAGE_NAME) String languageName; diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/condition/DataRuleGeneratorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/condition/DataRuleGeneratorTest.xtend index ee1037259..5b5e31a36 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/condition/DataRuleGeneratorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/condition/DataRuleGeneratorTest.xtend @@ -552,7 +552,7 @@ class DataRuleGeneratorTest { val validationResult = classes.runCondition(fooInstance, 'FooListDataRule') assertTrue(validationResult.isSuccess) - assertThat(validationResult.definition, is("if bar -> baz exists then bar -> baz -> bazValue = 1.0")) + assertThat(validationResult.definition, is("if bar -> baz exists then bar -> baz -> bazValue all = 1.0")) } @Test @@ -568,7 +568,7 @@ class DataRuleGeneratorTest { val validationResult = classes.runCondition(fooInstance, 'FooListDataRule') assertFalse(validationResult.isSuccess) - assertThat(validationResult.definition, is("if bar -> baz exists then bar -> baz -> bazValue = 1.0")) + assertThat(validationResult.definition, is("if bar -> baz exists then bar -> baz -> bazValue all = 1.0")) assertThat(validationResult.failureReason.orElse(""), is("[Foo->getBar[0]->getBaz[0]->getBazValue] [2.0] does not equal [BigDecimal] [1.0]")) } @@ -589,7 +589,7 @@ class DataRuleGeneratorTest { val validationResult = classes.runCondition(fooInstance, 'FooListDataRule') assertTrue(validationResult.isSuccess) - assertThat(validationResult.definition, is("if bar -> baz exists then bar -> baz -> bazValue = 1.0")) + assertThat(validationResult.definition, is("if bar -> baz exists then bar -> baz -> bazValue all = 1.0")) } @Test @@ -606,7 +606,7 @@ class DataRuleGeneratorTest { val validationResult = classes.runCondition(fooInstance, 'FooListDataRule') assertFalse(validationResult.isSuccess) - assertThat(validationResult.definition, is("if bar -> baz exists then bar -> baz -> bazValue = 1.0")) + assertThat(validationResult.definition, is("if bar -> baz exists then bar -> baz -> bazValue all = 1.0")) assertThat(validationResult.failureReason.orElse(""), is("[Foo->getBar[0]->getBaz[0]->getBazValue, Foo->getBar[0]->getBaz[1]->getBazValue] [1.0, 2.0] does not equal [BigDecimal] [1.0]")) } @@ -625,7 +625,7 @@ class DataRuleGeneratorTest { val validationResult = classes.runCondition(fooInstance, 'FooListDataRule') assertFalse(validationResult.isSuccess) - assertThat(validationResult.definition, is("if bar -> baz exists then bar -> baz -> bazValue = 1.0")) + assertThat(validationResult.definition, is("if bar -> baz exists then bar -> baz -> bazValue all = 1.0")) assertThat(validationResult.failureReason.orElse(""), is("[Foo->getBar[0]->getBaz[0]->getBazValue, Foo->getBar[1]->getBaz[0]->getBazValue] [1.0, 2.0] does not equal [BigDecimal] [1.0]")) } @@ -636,7 +636,7 @@ class DataRuleGeneratorTest { condition ListDataRule: if bar -> baz exists - then bar -> baz -> bazValue = 1.0 + then bar -> baz -> bazValue all = 1.0 type Bar: baz Baz (0..*) diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/RosettaBinaryOperationTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/RosettaBinaryOperationTest.xtend index 168a19cb3..0fdc87cb8 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/RosettaBinaryOperationTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/RosettaBinaryOperationTest.xtend @@ -52,13 +52,13 @@ class RosettaBinaryOperationTest { inputs: foo Foo (1..1) output: result boolean (1..1) set result: - foo -> bar -> before = 5 + foo -> bar -> before any = 5 func FeatureCallNotEqualToLiteral: inputs: foo Foo (1..1) output: result boolean (1..1) set result: - foo -> bar -> before <> 5 + foo -> bar -> before all <> 5 func FeatureCallEqualToFeatureCall: inputs: foo Foo (1..1) @@ -70,7 +70,7 @@ class RosettaBinaryOperationTest { inputs: foo Foo (1..1) output: result boolean (1..1) set result: - foo -> bar -> before = foo -> baz -> other + foo -> bar -> before any = foo -> baz -> other func FeatureCallNotEqualToFeatureCall: inputs: foo Foo (1..1) @@ -82,19 +82,19 @@ class RosettaBinaryOperationTest { inputs: foo Foo (1..1) output: result boolean (1..1) set result: - foo -> bar -> before <> foo -> baz -> other + foo -> bar -> before all <> foo -> baz -> other func FeatureCallsEqualToLiteralOr: inputs: foo Foo (1..1) output: result boolean (1..1) set result: - foo -> bar -> before = 5 or foo -> baz -> other = 5 + foo -> bar -> before any = 5 or foo -> baz -> other = 5 func FeatureCallsEqualToLiteralAnd: inputs: foo Foo (1..1) output: result boolean (1..1) set result: - foo -> bar -> before = 5 and foo -> bar -> after = 5 + foo -> bar -> before any = 5 and foo -> bar -> after any = 5 ««« TODO tests compilation only, add unit test @@ -112,8 +112,7 @@ class RosettaBinaryOperationTest { inputs: foo Foo (1..1) output: result boolean (1..1) set result: - // (foo -> bar -> before and foo -> baz -> other) = (foo -> bar -> after and foo -> baz -> bazValue) - [foo -> bar -> before, foo -> baz -> other] = [foo -> bar -> after, foo -> baz -> bazValue] + [foo -> bar -> before, foo -> baz -> other] = [foo -> bar -> after, foo -> baz -> bazValue] ««« TODO tests compilation only, add unit test @@ -121,7 +120,7 @@ class RosettaBinaryOperationTest { inputs: foo Foo(1..1) output: result boolean (1..1) set result: - (foo -> bar -> before = foo -> baz -> other) or (foo -> bar -> after = foo -> baz -> bazValue) + (foo -> bar -> before any = foo -> baz -> other) or (foo -> bar -> after any = foo -> baz -> bazValue) ««« TODO tests compilation only, add unit test @@ -129,7 +128,7 @@ class RosettaBinaryOperationTest { inputs: foo Foo(1..1) output: result boolean (1..1) set result: - (foo -> bar -> before = foo -> baz -> other) and (foo -> bar -> after = foo -> baz -> bazValue) + (foo -> bar -> before any = foo -> baz -> other) and (foo -> bar -> after any = foo -> baz -> bazValue) ««« TODO tests compilation only, add unit test @@ -137,7 +136,6 @@ class RosettaBinaryOperationTest { inputs: foo Foo (1..1) output: result boolean (1..1) set result: - // (foo -> bar -> before or foo -> bar -> after or foo -> baz -> other) = 5.0 [foo -> bar -> before, foo -> bar -> after, foo -> baz -> other] contains 5.0 ««« TODO tests compilation only, add unit test @@ -146,8 +144,7 @@ class RosettaBinaryOperationTest { inputs: foo Foo (1..1) output: result boolean (1..1) set result: - // (foo -> bar -> before and foo -> bar -> after and foo -> baz -> other) = 5.0 - [foo -> bar -> before, foo -> bar -> after, foo -> baz -> other] = 5.0 + [foo -> bar -> before, foo -> bar -> after, foo -> baz -> other] any = 5.0 ««« TODO tests compilation only, add unit test @@ -155,7 +152,7 @@ class RosettaBinaryOperationTest { inputs: foo Foo (1..1) output: result boolean (1..1) set result: - AliasBefore(foo) -> numbers = 5 + AliasBefore(foo) -> numbers any = 5 ««« TODO tests compilation only, add unit test @@ -171,7 +168,7 @@ class RosettaBinaryOperationTest { inputs: foo Foo (1..1) output: result boolean (1..1) set result: - AliasBefore(foo) -> numbers = 5 or AliasOther(foo) -> numbers = 5 + AliasBefore(foo) -> numbers any = 5 or AliasOther(foo) -> numbers any = 5 ««« TODO tests compilation only, add unit test @@ -179,7 +176,7 @@ class RosettaBinaryOperationTest { inputs: foo Foo (1..1) output: result boolean (1..1) set result: - AliasBefore(foo) -> numbers = 5 and AliasOther(foo) -> numbers = 5 + AliasBefore(foo) -> numbers any = 5 and AliasOther(foo) -> numbers any = 5 ««« TODO tests compilation only, add unit test diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/function/FunctionGeneratorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/function/FunctionGeneratorTest.xtend index 087382bdd..150473c1f 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/function/FunctionGeneratorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/function/FunctionGeneratorTest.xtend @@ -3129,7 +3129,7 @@ class FunctionGeneratorTest { result string (0..1) condition: - [ m1 -> currency , m2 -> currency ] = currency + [ m1 -> currency , m2 -> currency ] any = currency '''.generateCode code.compileToClasses } @@ -3984,8 +3984,8 @@ class FunctionGeneratorTest { set res: t1->num = t2->nums '''.parseRosetta - model.assertWarning(ROSETTA_BINARY_OPERATION, null, - "Comparison operator = should specify 'all' or 'any' when comparing a list to a single value") + model.assertError(EQUALITY_OPERATION, null, + "Operator `=` should specify 'all' or 'any' when comparing a list to a single value") } @Test diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaParsingTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaParsingTest.xtend index 89cb66e87..44941b9b5 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaParsingTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaParsingTest.xtend @@ -721,7 +721,7 @@ class RosettaParsingTest { condition Foo_Bar: if foo then - if bar = BarEnum -> abc + if bar any = BarEnum -> abc then foobar exists else foobar is absent enum BarEnum: diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/types/RosettaTypeProviderTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/types/RosettaTypeProviderTest.xtend index 634f98bbb..e1694dc26 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/types/RosettaTypeProviderTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/types/RosettaTypeProviderTest.xtend @@ -25,13 +25,15 @@ import static extension com.regnosys.rosetta.types.RMetaAnnotatedType.* import java.util.Optional import java.math.BigInteger import java.math.BigDecimal -import org.eclipse.xtext.serializer.ISerializer import com.regnosys.rosetta.rosetta.RosettaEnumeration import com.regnosys.rosetta.rosetta.expression.LogicalOperation import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.TestInstance import org.junit.jupiter.api.TestInstance.Lifecycle +import static com.regnosys.rosetta.rosetta.expression.ExpressionPackage.Literals.* +import org.eclipse.xtext.nodemodel.util.NodeModelUtils + @ExtendWith(InjectionExtension) @InjectWith(RosettaInjectorProvider) @TestInstance(Lifecycle.PER_CLASS) @@ -43,7 +45,6 @@ class RosettaTypeProviderTest { @Inject extension ValidationTestHelper @Inject extension ExpressionParser @Inject extension TypeFactory - @Inject extension ISerializer @Inject extension RObjectFactory @Inject extension TypeSystem @Inject extension RBuiltinTypeService builtins @@ -55,26 +56,29 @@ class RosettaTypeProviderTest { } private def void assertIsValidWithType(CharSequence expr, RMetaAnnotatedType expectedType, boolean expectedIsMulti, List context, String... attributes) { - assertIsValidWithType(expr.parseExpression(context, attributes), expectedType, expectedIsMulti) + assertIsValidWithType(expr.parseExpression(context, attributes), expr, expectedType, expectedIsMulti) } private def void assertIsValidWithType(CharSequence expr, RMetaAnnotatedType expectedType, boolean expectedIsMulti, List context) { - assertIsValidWithType(expr.parseExpression(context), expectedType, expectedIsMulti) + assertIsValidWithType(expr.parseExpression(context), expr, expectedType, expectedIsMulti) } private def void assertIsValidWithType(CharSequence expr, RMetaAnnotatedType expectedType, boolean expectedIsMulti, String... attributes) { - assertIsValidWithType(expr.parseExpression(attributes), expectedType, expectedIsMulti) + assertIsValidWithType(expr.parseExpression(attributes), expr, expectedType, expectedIsMulti) } private def void assertIsValidWithType(CharSequence expr, RMetaAnnotatedType expectedType, boolean expectedIsMulti) { - assertIsValidWithType(expr, expectedType, expectedIsMulti) + assertIsValidWithType(expr.parseExpression, expr, expectedType, expectedIsMulti) } private def void assertIsValidWithType(RosettaExpression expr, RMetaAnnotatedType expectedType, boolean expectedIsMulti) { + assertIsValidWithType(expr, NodeModelUtils.findActualNodeFor(expr).text, expectedType, expectedIsMulti) + } + private def void assertIsValidWithType(RosettaExpression expr, CharSequence originalExpression, RMetaAnnotatedType expectedType, boolean expectedIsMulti) { expr.assertNoIssues val actual = expr.RMetaAnnotatedType - assertEquals(expectedType, actual, "Expression: " + expr.serialize) + assertEquals(expectedType, actual, "Expression: " + originalExpression) if (expectedIsMulti) { - assertTrue(expr.isMulti, "Expected multi cardinality. Expression: " + expr.serialize) + assertTrue(expr.isMulti, "Expected multi cardinality. Expression: " + originalExpression) } else { - assertFalse(expr.isMulti, "Expected single cardinality. Expression: " + expr.serialize) + assertFalse(expr.isMulti, "Expected single cardinality. Expression: " + originalExpression) } } @@ -122,13 +126,13 @@ class RosettaTypeProviderTest { def void testLogicalOperationTypeChecking() { '1 or False' .parseExpression - .assertError(null, "Expected type `boolean`, but got `int` instead.") + .assertError(LOGICAL_OPERATION, null, "Expected type `boolean`, but got `int` instead") 'True or 3.14' .parseExpression - .assertError(null, "Expected type `boolean`, but got `number` instead.") + .assertError(LOGICAL_OPERATION, null, "Expected type `boolean`, but got `number` instead") 'a or False' .parseExpression(#['a boolean (1..2)']) - .assertError(null, "aaaaa") + .assertError(LOGICAL_OPERATION, null, "Expecting single cardinality") } @Test @@ -136,7 +140,8 @@ class RosettaTypeProviderTest { '[2, 3] = [6.0, 7, 8]'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false) '[2, 3] <> [6.0, 7, 8]'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false) '[1, 3] all = 5.0'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false) - 'empty all <> 5.0'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false) + // TODO? +// 'empty all <> 5.0'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false) '[1, 3] any = 5.0'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false) 'a = 1'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false, #['a int (0..1)']) @@ -146,38 +151,39 @@ class RosettaTypeProviderTest { def void testEqualityOperationTypeChecking() { '1 = True' .parseExpression - .assertError(null, "Types `int` and `boolean` are not comparable.") - 'empty = True' - .parseExpression - .assertError(null, "Cannot compare an empty value to a single value, as they cannot be of the same length. Perhaps you forgot to write `all` or `any` in front of the operator?") - '[1, 2] = [3, 4, 5]' - .parseExpression - .assertError(null, "Cannot compare a list with 2 items to a list with 3 items, as they cannot be of the same length.") + .assertError(EQUALITY_OPERATION, null, "Types `int` and `boolean` are not comparable") + // TODO? +// 'empty = True' +// .parseExpression +// .assertError(EQUALITY_OPERATION, null, "Cannot compare an empty value to a single value, as they cannot be of the same length. Perhaps you forgot to write `all` or `any` in front of the operator?") +// '[1, 2] = [3, 4, 5]' +// .parseExpression +// .assertError(EQUALITY_OPERATION, null, "Cannot compare a list with 2 items to a list with 3 items, as they cannot be of the same length.") '[1, 2] <> [True, False, False]' .parseExpression - .assertError(null, "Types `int` and `boolean` are not comparable.") + .assertError(EQUALITY_OPERATION, null, "Types `int` and `boolean` are not comparable") '1 = True' .parseExpression - .assertError(null, "Types `int` and `boolean` are not comparable.") + .assertError(EQUALITY_OPERATION, null, "Types `int` and `boolean` are not comparable") '[1, 3] any <> a' .parseExpression(#['a int (1..2)']) - .assertError(null, "aaaaaaaaaa") - '[1, 2] all = empty' - .parseExpression - .assertError(null, "Expected a single value, but got an empty value instead.") - 'empty any = empty' - .parseExpression - .assertError(null, "Expected a single value, but got an empty value instead.") + .assertError(EQUALITY_OPERATION, null, "Expecting single cardinality") +// '[1, 2] all = empty' +// .parseExpression +// .assertError(EQUALITY_OPERATION, null, "Expected a single value, but got an empty value instead") +// 'empty any = empty' +// .parseExpression +// .assertError(EQUALITY_OPERATION, null, "Expected a single value, but got an empty value instead") '[1, 2] all = [1, 2]' .parseExpression - .assertError(null, "Expected a single value, but got a list with 2 items instead.") + .assertError(EQUALITY_OPERATION, null, "Expecting single cardinality") '5 any <> [1, 2]' .parseExpression - .assertError(null, "Expected a single value, but got a list with 2 items instead. Perhaps you meant to swap the left and right operands?") - '[3.0] any <> 5' - .parseExpression - .assertError(null, "The cardinality operator `any` is redundant when comparing two single values.") + .assertError(EQUALITY_OPERATION, null, "Expecting multi cardinality") +// '[3.0] any <> 5' +// .parseExpression +// .assertError(EQUALITY_OPERATION, null, "The cardinality operator `any` is redundant when comparing two single values") } // TODO: test arithmetic and comparisons with dates/times/etc @@ -199,22 +205,23 @@ class RosettaTypeProviderTest { } @Test - def void testArithemticOperationTypeChecking() { + def void testArithmeticOperationTypeChecking() { '[1, 2] + 3' .parseExpression - .assertError(null, "Expected a single value, but got a list with 2 items instead.") - 'empty - 3' - .parseExpression - .assertError(null, "Expected a single value, but got an empty value instead.") + .assertError(ARITHMETIC_OPERATION, null, "Expecting single cardinality") + // TODO +// 'empty - 3' +// .parseExpression +// .assertError(ARITHMETIC_OPERATION, null, "Expected a single value, but got an empty value instead.") '1.5 * False' .parseExpression - .assertError(null, "Expected type `number`, but got `boolean` instead.") + .assertError(ARITHMETIC_OPERATION, null, "Expected type `number`, but got `boolean` instead") '"ab" + 3' .parseExpression - .assertError(null, "Expected arguments to be either both a `string` or both a `number`, but got `string` and `int` instead.") + .assertError(ARITHMETIC_OPERATION, null, "Expected type `string`, but got `int` instead") 'a + 5' .parseExpression(#['a int (1..2)']) - .assertError(null, "aaaaaaaaaaa") + .assertError(ARITHMETIC_OPERATION, null, "Expecting single cardinality") } @Test @@ -225,7 +232,8 @@ class RosettaTypeProviderTest { '-3.14 >= 3.14'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false) '[1, 2] any < 5'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false) - 'empty all > 5'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false) + // TODO? +// 'empty all > 5'.assertIsValidWithType(BOOLEAN_WITH_NO_META, false) } @Test @@ -233,35 +241,36 @@ class RosettaTypeProviderTest { // TODO: support date, zonedDateTime and `time`? '[1, 2] < 3' .parseExpression - .assertError(null, "Expected a single value, but got a list with 2 items instead.") - 'empty > 3' - .parseExpression - .assertError(null, "Expected a single value, but got an empty value instead.") + .assertError(COMPARISON_OPERATION, null, "Expecting single cardinality") + // TODO +// 'empty > 3' +// .parseExpression +// .assertError(COMPARISON_OPERATION, null, "Expected a single value, but got an empty value instead.") '1.5 <= False' .parseExpression - .assertError(null, "Expected type `number`, but got `boolean` instead.") + .assertError(COMPARISON_OPERATION, null, "Expected type `number`, but got `boolean` instead") 'a < 5' .parseExpression(#['a int (1..2)']) - .assertError(null, "aaaaa") + .assertError(COMPARISON_OPERATION, null, "Expecting single cardinality") '[1, 2] any < a' .parseExpression(#['a int (1..2)']) - .assertError(null, "aaaaa") - '[1, 2] all >= empty' - .parseExpression - .assertError(null, "Expected a single value, but got an empty value instead.") + .assertError(COMPARISON_OPERATION, null, "Expecting single cardinality") +// '[1, 2] all >= empty' +// .parseExpression +// .assertError(COMPARISON_OPERATION, null, "Expected a single value, but got an empty value instead") 'empty any < empty' .parseExpression - .assertError(null, "Expected a single value, but got an empty value instead.") + .assertError(COMPARISON_OPERATION, null, "Expecting multi cardinality") '[1, 2] all > [1, 2]' .parseExpression - .assertError(null, "Expected a single value, but got a list with 2 items instead.") + .assertError(COMPARISON_OPERATION, null, "Expecting single cardinality") '5 any <= [1, 2]' .parseExpression - .assertError(null, "Expected a single value, but got a list with 2 items instead. Perhaps you meant to swap the left and right operands?") + .assertError(COMPARISON_OPERATION, null, "Expecting single cardinality") '5 all >= 1' .parseExpression - .assertError(null, "The cardinality operator `all` is redundant when comparing two single values.") + .assertError(COMPARISON_OPERATION, null, "Expecting multi cardinality") } @Test @@ -273,16 +282,17 @@ class RosettaTypeProviderTest { def void testConditionalExpressionTypeChecking() { 'if [True, False] then 1 else 2' .parseExpression - .assertError(null, "Expected a single value, but got a list with 2 items instead.") - 'if empty then 1 else 2' - .parseExpression - .assertError(null, "Expected a single value, but got an empty value instead.") + .assertError(ROSETTA_CONDITIONAL_EXPRESSION, null, "Expecting single cardinality") + // TODO +// 'if empty then 1 else 2' +// .parseExpression +// .assertError(ROSETTA_CONDITIONAL_EXPRESSION, null, "Expected a single value, but got an empty value instead.") 'if True then 1 else False' .parseExpression - .assertError(null, "Types `int` and `boolean` do not have a common supertype.") + .assertError(ROSETTA_CONDITIONAL_EXPRESSION, null, "Types `int` and `boolean` do not have a common supertype") 'if True then [1, 2, 3] else [False, True]' .parseExpression - .assertError(null, "Types `int` and `boolean` do not have a common supertype.") + .assertError(ROSETTA_CONDITIONAL_EXPRESSION, null, "Types `int` and `boolean` do not have a common supertype") } @Test @@ -296,7 +306,7 @@ class RosettaTypeProviderTest { def void testListLiteralTypeChecking() { '[1, True]' .parseExpression - .assertError(null, "Elements do not have a common supertype: `int`, `boolean`.") + .assertError(LIST_LITERAL, null, "Types `int` and `boolean` do not have a common supertype") } @Test @@ -325,32 +335,18 @@ class RosettaTypeProviderTest { output: result int (1..1) set result: 42 - - func TestParamNumber: - output: result int (1..1) - set result: - SomeFunc(1, [False, True], True) - - func TestParamType: - output: result int (1..1) - set result: - SomeFunc(1, [2, 3]) - - func TestParamCardinality: - output: result int (1..1) - set result: - SomeFunc(1, [False, True, False, False, True]) '''.parseRosettaWithNoIssues 'SomeFunc(1, [False, True], True)' .parseExpression(#[context]) - .assertError(null, "Expected 2 arguments, but got 3 instead."); + .assertError(ROSETTA_SYMBOL_REFERENCE, null, "Expected 2 arguments, but got 3 instead"); 'SomeFunc(1, [2, 3])' .parseExpression(#[context]) - .assertError(null, "Expected type `boolean`, but got `int` instead."); - 'SomeFunc(1, [False, True, False, False, True])' - .parseExpression(#[context]) - .assertError(null, "Expected a list with 2 to 4 items, but got a list with 5 items instead."); + .assertError(ROSETTA_SYMBOL_REFERENCE, null, "Expected type `boolean`, but got `int` instead"); + // TODO +// 'SomeFunc(1, [False, True, False, False, True])' +// .parseExpression(#[context]) +// .assertError(ROSETTA_SYMBOL_REFERENCE, null, "Expected a list with 2 to 4 items, but got a list with 5 items instead"); } @Test @@ -397,15 +393,16 @@ class RosettaTypeProviderTest { @Test def void testExistsTypeChecking() { - 'empty exists' - .parseExpression - .assertError(null, "Expected an optional value, but got an empty value instead.") - '42 exists' - .parseExpression - .assertError(null, "Expected an optional value, but got a single value instead.") - '(if True then 42 else [1, 2, 3, 4, 5]) exists' - .parseExpression - .assertError(null, "Expected an optional value, but got a list with 1 to 5 items instead.") + // TODO +// 'empty exists' +// .parseExpression +// .assertError(ROSETTA_EXISTS_EXPRESSION, null, "Expected an optional value, but got an empty value instead.") +// '42 exists' +// .parseExpression +// .assertError(ROSETTA_EXISTS_EXPRESSION, null, "Expected an optional value, but got a single value instead.") +// '(if True then 42 else [1, 2, 3, 4, 5]) exists' +// .parseExpression +// .assertError(ROSETTA_EXISTS_EXPRESSION, null, "Expected an optional value, but got a list with 1 to 5 items instead.") } @Test @@ -416,15 +413,16 @@ class RosettaTypeProviderTest { @Test def void testAbsentTypeChecking() { - 'empty is absent' - .parseExpression - .assertError(null, "Expected an optional value, but got an empty value instead.") - '42 is absent' - .parseExpression - .assertError(null, "Expected an optional value, but got a single value instead.") - '(if True then 42 else [1, 2, 3, 4, 5]) is absent' - .parseExpression - .assertError(null, "Expected an optional value, but got a list with 1 to 5 items instead.") + // TODO +// 'empty is absent' +// .parseExpression +// .assertError(ROSETTA_ABSENT_EXPRESSION, null, "Expected an optional value, but got an empty value instead.") +// '42 is absent' +// .parseExpression +// .assertError(ROSETTA_ABSENT_EXPRESSION, null, "Expected an optional value, but got a single value instead.") +// '(if True then 42 else [1, 2, 3, 4, 5]) is absent' +// .parseExpression +// .assertError(ROSETTA_ABSENT_EXPRESSION, null, "Expected an optional value, but got a list with 1 to 5 items instead.") } @Test @@ -498,24 +496,24 @@ class RosettaTypeProviderTest { '''.parseRosetta; (model.elements.get(0) as Data).conditions.head.expression - .assertError(null, "The `only exists` operator is not applicable to instances of `Foo`."); + .assertError(ROSETTA_SYMBOL_REFERENCE, null, "Operator `only exists` is not supported for type Foo. All attributes of input type should be optional"); (model.elements.get(1) as Data).conditions => [ get(0).expression as LogicalOperation => [ - left.assertError(null, "All parent paths must be equal.") - right.assertError(null, "All parent paths must be equal.") + left.assertError(ROSETTA_SYMBOL_REFERENCE, null, "All parent paths must be equal") + right.assertError(ROSETTA_SYMBOL_REFERENCE, null, "All parent paths must be equal") ] get(1).expression as LogicalOperation => [ - left.assertError(null, "Duplicate attribute.") - right.assertError(null, "Duplicate attribute.") + left.assertError(ROSETTA_ONLY_EXISTS_EXPRESSION, null, "Duplicate attribute") + right.assertError(ROSETTA_ONLY_EXISTS_EXPRESSION, null, "Duplicate attribute") ] ] (model.elements.get(2) as Function).operations => [ - get(0).expression.assertError(null, "Expected a single value, but got a list with 2 to 3 items instead.") - get(1).expression.assertError(null, "Object must have a parent object.") - get(2).expression.assertError(null, "All parent paths must be equal.") - get(3).expression.assertError(null, "The `only exists` operator is not applicable to instances of `Foo`.") + get(0).expression.assertError(ROSETTA_SYMBOL_REFERENCE, null, "Expecting single cardinality") + get(1).expression.assertError(ROSETTA_ONLY_EXISTS_EXPRESSION, null, "Object must have a parent object") + get(2).expression.assertError(ROSETTA_SYMBOL_REFERENCE, null, "All parent paths must be equal") + get(3).expression.assertError(ROSETTA_SYMBOL_REFERENCE, null, "Operator `only exists` is not supported for type Foo. All attributes of input type should be optional") ] } @@ -528,18 +526,19 @@ class RosettaTypeProviderTest { @Test def void testOnlyElementTypeChecking() { - 'empty only-element' - .parseExpression - .assertWarning(null, "Expected a list with 1 to 2 items, but got an empty value instead.") - '42 only-element' - .parseExpression - .assertWarning(null, "Expected a list with 1 to 2 items, but got a single value instead.") - '[1, 2] only-element' - .parseExpression - .assertWarning(null, "Expected a list with 1 to 2 items, but got a list with 2 items instead.") - '(if True then empty else 42) only-element' - .parseExpression - .assertWarning(null, "Expected a list with 1 to 2 items, but got an optional value instead.") + // TODO +// 'empty only-element' +// .parseExpression +// .assertWarning(ROSETTA_ONLY_ELEMENT, null, "Expected a list with 1 to 2 items, but got an empty value instead.") +// '42 only-element' +// .parseExpression +// .assertWarning(ROSETTA_ONLY_ELEMENT, null, "Expected a list with 1 to 2 items, but got a single value instead.") +// '[1, 2] only-element' +// .parseExpression +// .assertWarning(ROSETTA_ONLY_ELEMENT, null, "Expected a list with 1 to 2 items, but got a list with 2 items instead.") +// '(if True then empty else 42) only-element' +// .parseExpression +// .assertWarning(ROSETTA_ONLY_ELEMENT, null, "Expected a list with 1 to 2 items, but got an optional value instead.") } @Test @@ -696,7 +695,7 @@ class RosettaTypeProviderTest { inputs: foo Foo (1..1) output: is_event boolean (1..1) set is_event: - foo -> iBar = 4.0 + foo -> iBar any = 4.0 '''.parseRosettaWithNoErrors.elements.filter(Function) val allNumber = funcs.filter[name == "Qualify_AllNumber"].head diff --git a/rosetta-xcore-plugin-dependencies/pom.xml b/rosetta-xcore-plugin-dependencies/pom.xml index 2cb1b3be0..53ab6cd00 100644 --- a/rosetta-xcore-plugin-dependencies/pom.xml +++ b/rosetta-xcore-plugin-dependencies/pom.xml @@ -63,10 +63,6 @@ org.eclipse.xtext org.eclipse.xtext.generator - - org.eclipse.xsemantics - org.eclipse.xsemantics.dsl - com.google.inject guice From 1a92374110eff774b596446729a4a382290cd5e3 Mon Sep 17 00:00:00 2001 From: Simon Cockx Date: Mon, 9 Dec 2024 15:26:27 +0000 Subject: [PATCH 03/10] Fixed tests --- .../rosetta/types/RosettaTypeProvider.xtend | 232 +++++++++--------- .../java/expression/ListOperationTest.xtend | 104 ++++---- .../validation/RosettaValidatorTest.xtend | 44 ++-- 3 files changed, 193 insertions(+), 187 deletions(-) diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaTypeProvider.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaTypeProvider.xtend index cb239719c..1dfe9af02 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaTypeProvider.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaTypeProvider.xtend @@ -88,9 +88,9 @@ import com.regnosys.rosetta.rosetta.RosettaParameter import com.regnosys.rosetta.types.builtin.RStringType import com.regnosys.rosetta.types.builtin.RNumberType import com.regnosys.rosetta.utils.OptionalUtil -import java.util.Set +import java.util.Map -class RosettaTypeProvider extends RosettaExpressionSwitch> { +class RosettaTypeProvider extends RosettaExpressionSwitch> { public static String EXPRESSION_RTYPE_CACHE_KEY = RosettaTypeProvider.canonicalName + ".EXPRESSION_RTYPE" @@ -104,13 +104,13 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { - if (!extensions.isResolved(symbol) || !cycleTracker.add(symbol)) { + private def RMetaAnnotatedType safeRType(RosettaSymbol symbol, EObject context, Map cycleTracker) { + if (!extensions.isResolved(symbol)) { return NOTHING_WITH_NO_META } - switch symbol { - RosettaFeature: { - safeRType(symbol as RosettaFeature, context, cycleTracker) - } - RosettaParameter: { - symbol.typeCall.typeCallToRType.withEmptyMeta - } - ClosureParameter: { - val setOp = symbol.function.eContainer as RosettaFunctionalOperation - if(setOp !== null) { - setOp.argument.safeRType(cycleTracker) - } else - NOTHING_WITH_NO_META - } - RosettaEnumeration: { // @Compat: RosettaEnumeration should not be a RosettaSymbol. - symbol.buildREnumType.withMeta(symbol.RMetaAttributesOfSymbol) - } - Function: { - if (symbol.output !== null) { - safeRType(symbol.output as RosettaFeature, context, cycleTracker) - } else { - NOTHING_WITH_NO_META + val existing = cycleTracker.get(symbol) + if (existing !== null) { + return existing + } + cycleTracker.put(symbol, NOTHING_WITH_NO_META) + val result = + switch symbol { + RosettaFeature: { + safeRType(symbol as RosettaFeature, context, cycleTracker) } - } - RosettaRule: { - if (symbol.expression !== null) { - safeRType(symbol.expression, cycleTracker) - } else { - NOTHING_WITH_NO_META + RosettaParameter: { + symbol.typeCall.typeCallToRType.withEmptyMeta + } + ClosureParameter: { + val setOp = symbol.function.eContainer as RosettaFunctionalOperation + if(setOp !== null) { + setOp.argument.safeRType(cycleTracker) + } else + NOTHING_WITH_NO_META + } + RosettaEnumeration: { // @Compat: RosettaEnumeration should not be a RosettaSymbol. + symbol.buildREnumType.withMeta(symbol.RMetaAttributesOfSymbol) + } + Function: { + if (symbol.output !== null) { + safeRType(symbol.output as RosettaFeature, context, cycleTracker) + } else { + NOTHING_WITH_NO_META + } + } + RosettaRule: { + if (symbol.expression !== null) { + safeRType(symbol.expression, cycleTracker) + } else { + NOTHING_WITH_NO_META + } + } + RosettaExternalFunction: { + symbol.typeCall.typeCallToRType.withEmptyMeta + } + ShortcutDeclaration: { + val type = symbol.expression.safeRType(cycleTracker) + type + } + TypeParameter: { + symbol.typeCall.typeCallToRType.withEmptyMeta } } - RosettaExternalFunction: { - symbol.typeCall.typeCallToRType.withEmptyMeta - } - ShortcutDeclaration: { - val type = symbol.expression.safeRType(cycleTracker) - type - } - TypeParameter: { - symbol.typeCall.typeCallToRType.withEmptyMeta - } - } + cycleTracker.put(symbol, result) + return result } - private def RMetaAnnotatedType safeRType(RosettaFeature feature, EObject context, Set cycleTracker) { + private def RMetaAnnotatedType safeRType(RosettaFeature feature, EObject context, Map cycleTracker) { if (!extensions.isResolved(feature)) { return NOTHING_WITH_NO_META } @@ -228,13 +236,11 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { - getRTypeFromCache(EXPRESSION_RTYPE_CACHE_KEY, expression, [ - if (!extensions.isResolved(expression)) { - return NOTHING_WITH_NO_META - } - doSwitch(expression, cycleTracker) - ]) + private def RMetaAnnotatedType safeRType(RosettaExpression expression, Map cycleTracker) { + if (expression === null) { + return NOTHING_WITH_NO_META + } + getRTypeFromCache(EXPRESSION_RTYPE_CACHE_KEY, expression, [doSwitch(expression, cycleTracker)]) } private def RMetaAnnotatedType getRTypeFromCache(String cacheKey, EObject object, Provider typeProvider) { @@ -245,10 +251,10 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { + private def RMetaAnnotatedType safeTypeOfImplicitVariable(EObject context, Map cycleTracker) { val definingContainer = context.findContainerDefiningImplicitVariable definingContainer.map [ if (it instanceof Data) { @@ -263,11 +269,11 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { + override protected caseAbsentOperation(RosettaAbsentExpression expr, Map cycleTracker) { BOOLEAN_WITH_NO_META } - override protected caseAddOperation(ArithmeticOperation expr, Set cycleTracker) { + override protected caseAddOperation(ArithmeticOperation expr, Map cycleTracker) { val left = expr.left.safeRType(cycleTracker) val right = expr.right.safeRType(cycleTracker) if (left.isSubtypeOf(NOTHING_WITH_NO_META)) { @@ -302,23 +308,23 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { + override protected caseAndOperation(LogicalOperation expr, Map cycleTracker) { BOOLEAN_WITH_NO_META } - override protected caseAsKeyOperation(AsKeyOperation expr, Set cycleTracker) { + override protected caseAsKeyOperation(AsKeyOperation expr, Map cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseBooleanLiteral(RosettaBooleanLiteral expr, Set cycleTracker) { + override protected caseBooleanLiteral(RosettaBooleanLiteral expr, Map cycleTracker) { BOOLEAN_WITH_NO_META } - override protected caseChoiceOperation(ChoiceOperation expr, Set cycleTracker) { + override protected caseChoiceOperation(ChoiceOperation expr, Map cycleTracker) { BOOLEAN_WITH_NO_META } - override protected caseConditionalExpression(RosettaConditionalExpression expr, Set cycleTracker) { + override protected caseConditionalExpression(RosettaConditionalExpression expr, Map cycleTracker) { val ifT = expr.ifthen.safeRType(cycleTracker) val elseT = expr.elsethen.safeRType(cycleTracker) val joined = joinMetaAnnotatedTypes(ifT, elseT) @@ -329,11 +335,11 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { + override protected caseContainsOperation(RosettaContainsExpression expr, Map cycleTracker) { BOOLEAN_WITH_NO_META } - override protected caseDefaultOperation(DefaultOperation expr, Set cycleTracker) { + override protected caseDefaultOperation(DefaultOperation expr, Map cycleTracker) { val left = expr.left.safeRType(cycleTracker) val right = expr.right.safeRType(cycleTracker) val result = left.joinMetaAnnotatedTypes(right) @@ -344,31 +350,31 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { + override protected caseCountOperation(RosettaCountOperation expr, Map cycleTracker) { constrainedInt(Optional.empty(), Optional.of(BigInteger.ZERO), Optional.empty()).withEmptyMeta } - override protected caseDisjointOperation(RosettaDisjointExpression expr, Set cycleTracker) { + override protected caseDisjointOperation(RosettaDisjointExpression expr, Map cycleTracker) { BOOLEAN_WITH_NO_META } - override protected caseDistinctOperation(DistinctOperation expr, Set cycleTracker) { + override protected caseDistinctOperation(DistinctOperation expr, Map cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseDivideOperation(ArithmeticOperation expr, Set cycleTracker) { + override protected caseDivideOperation(ArithmeticOperation expr, Map cycleTracker) { UNCONSTRAINED_NUMBER_WITH_NO_META } - override protected caseEqualsOperation(EqualityOperation expr, Set cycleTracker) { + override protected caseEqualsOperation(EqualityOperation expr, Map cycleTracker) { BOOLEAN_WITH_NO_META } - override protected caseExistsOperation(RosettaExistsExpression expr, Set cycleTracker) { + override protected caseExistsOperation(RosettaExistsExpression expr, Map cycleTracker) { BOOLEAN_WITH_NO_META } - override protected caseFeatureCall(RosettaFeatureCall expr, Set cycleTracker) { + override protected caseFeatureCall(RosettaFeatureCall expr, Map cycleTracker) { val feature = expr.feature if (!extensions.isResolved(feature)) { return NOTHING_WITH_NO_META @@ -380,7 +386,7 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { + override protected caseDeepFeatureCall(RosettaDeepFeatureCall expr, Map cycleTracker) { val feature = expr.feature if (!extensions.isResolved(feature)) { return NOTHING_WITH_NO_META @@ -388,51 +394,51 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { + override protected caseFilterOperation(FilterOperation expr, Map cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseFirstOperation(FirstOperation expr, Set cycleTracker) { + override protected caseFirstOperation(FirstOperation expr, Map cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseFlattenOperation(FlattenOperation expr, Set cycleTracker) { + override protected caseFlattenOperation(FlattenOperation expr, Map cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseGreaterThanOperation(ComparisonOperation expr, Set cycleTracker) { + override protected caseGreaterThanOperation(ComparisonOperation expr, Map cycleTracker) { BOOLEAN_WITH_NO_META } - override protected caseGreaterThanOrEqualOperation(ComparisonOperation expr, Set cycleTracker) { + override protected caseGreaterThanOrEqualOperation(ComparisonOperation expr, Map cycleTracker) { BOOLEAN_WITH_NO_META } - override protected caseImplicitVariable(RosettaImplicitVariable expr, Set cycleTracker) { + override protected caseImplicitVariable(RosettaImplicitVariable expr, Map cycleTracker) { safeTypeOfImplicitVariable(expr, cycleTracker) } - override protected caseIntLiteral(RosettaIntLiteral expr, Set cycleTracker) { + override protected caseIntLiteral(RosettaIntLiteral expr, Map cycleTracker) { constrainedInt(if (expr.value.signum >= 0) expr.value.toString.length else expr.value.toString.length - 1, expr.value, expr.value).withEmptyMeta } - override protected caseJoinOperation(JoinOperation expr, Set cycleTracker) { + override protected caseJoinOperation(JoinOperation expr, Map cycleTracker) { UNCONSTRAINED_STRING_WITH_NO_META } - override protected caseLastOperation(LastOperation expr, Set cycleTracker) { + override protected caseLastOperation(LastOperation expr, Map cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseLessThanOperation(ComparisonOperation expr, Set cycleTracker) { + override protected caseLessThanOperation(ComparisonOperation expr, Map cycleTracker) { BOOLEAN_WITH_NO_META } - override protected caseLessThanOrEqualOperation(ComparisonOperation expr, Set cycleTracker) { + override protected caseLessThanOrEqualOperation(ComparisonOperation expr, Map cycleTracker) { BOOLEAN_WITH_NO_META } - override protected caseListLiteral(ListLiteral expr, Set cycleTracker) { + override protected caseListLiteral(ListLiteral expr, Map cycleTracker) { val types = expr.elements.map[RMetaAnnotatedType].filter[it !== null] val joined = types.joinMetaAnnotatedTypes if (ANY_WITH_NO_META.isSubtypeOf(joined)) { @@ -442,19 +448,19 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { + override protected caseMapOperation(MapOperation expr, Map cycleTracker) { expr.function?.body?.safeRType(cycleTracker) ?: NOTHING_WITH_NO_META } - override protected caseMaxOperation(MaxOperation expr, Set cycleTracker) { + override protected caseMaxOperation(MaxOperation expr, Map cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseMinOperation(MinOperation expr, Set cycleTracker) { + override protected caseMinOperation(MinOperation expr, Map cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseMultiplyOperation(ArithmeticOperation expr, Set cycleTracker) { + override protected caseMultiplyOperation(ArithmeticOperation expr, Map cycleTracker) { val left = expr.left.safeRType(cycleTracker) val right = expr.right.safeRType(cycleTracker) keepTypeAliasIfPossible(left.RType, right.RType, [l,r| @@ -470,50 +476,50 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { + override protected caseNotEqualsOperation(EqualityOperation expr, Map cycleTracker) { BOOLEAN_WITH_NO_META } - override protected caseNumberLiteral(RosettaNumberLiteral expr, Set cycleTracker) { + override protected caseNumberLiteral(RosettaNumberLiteral expr, Map cycleTracker) { if (expr.value === null) { // In case of a parse error return NOTHING_WITH_NO_META } constrainedNumber(expr.value.toPlainString.replaceAll("\\.|\\-", "").length, Math.max(0, expr.value.scale), expr.value, expr.value).withEmptyMeta } - override protected caseOneOfOperation(OneOfOperation expr, Set cycleTracker) { + override protected caseOneOfOperation(OneOfOperation expr, Map cycleTracker) { BOOLEAN_WITH_NO_META } - override protected caseOnlyElementOperation(RosettaOnlyElement expr, Set cycleTracker) { + override protected caseOnlyElementOperation(RosettaOnlyElement expr, Map cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseOnlyExists(RosettaOnlyExistsExpression expr, Set cycleTracker) { + override protected caseOnlyExists(RosettaOnlyExistsExpression expr, Map cycleTracker) { BOOLEAN_WITH_NO_META } - override protected caseOrOperation(LogicalOperation expr, Set cycleTracker) { + override protected caseOrOperation(LogicalOperation expr, Map cycleTracker) { BOOLEAN_WITH_NO_META } - override protected caseReduceOperation(ReduceOperation expr, Set cycleTracker) { + override protected caseReduceOperation(ReduceOperation expr, Map cycleTracker) { expr.function?.body?.safeRType(cycleTracker) ?: NOTHING_WITH_NO_META } - override protected caseReverseOperation(ReverseOperation expr, Set cycleTracker) { + override protected caseReverseOperation(ReverseOperation expr, Map cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseSortOperation(SortOperation expr, Set cycleTracker) { + override protected caseSortOperation(SortOperation expr, Map cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseStringLiteral(RosettaStringLiteral expr, Set cycleTracker) { + override protected caseStringLiteral(RosettaStringLiteral expr, Map cycleTracker) { constrainedString(expr.value.length, expr.value.length).withEmptyMeta } - override protected caseSubtractOperation(ArithmeticOperation expr, Set cycleTracker) { + override protected caseSubtractOperation(ArithmeticOperation expr, Map cycleTracker) { val left = expr.left.safeRType(cycleTracker) val right = expr.right.safeRType(cycleTracker) if (left.isSubtypeOf(NOTHING_WITH_NO_META)) { @@ -537,11 +543,11 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { + override protected caseSumOperation(SumOperation expr, Map cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseSymbolReference(RosettaSymbolReference expr, Set cycleTracker) { + override protected caseSymbolReference(RosettaSymbolReference expr, Map cycleTracker) { if (expr.symbol instanceof RosettaExternalFunction) { val fun = expr.symbol as RosettaExternalFunction val returnType = fun.safeRType(expr, cycleTracker) @@ -558,47 +564,47 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { + override protected caseThenOperation(ThenOperation expr, Map cycleTracker) { expr.function?.body?.safeRType(cycleTracker) ?: NOTHING_WITH_NO_META } - override protected caseToEnumOperation(ToEnumOperation expr, Set cycleTracker) { + override protected caseToEnumOperation(ToEnumOperation expr, Map cycleTracker) { expr.enumeration.buildREnumType.withEmptyMeta } - override protected caseToIntOperation(ToIntOperation expr, Set cycleTracker) { + override protected caseToIntOperation(ToIntOperation expr, Map cycleTracker) { UNCONSTRAINED_INT_WITH_NO_META } - override protected caseToNumberOperation(ToNumberOperation expr, Set cycleTracker) { + override protected caseToNumberOperation(ToNumberOperation expr, Map cycleTracker) { UNCONSTRAINED_NUMBER_WITH_NO_META } - override protected caseToStringOperation(ToStringOperation expr, Set cycleTracker) { + override protected caseToStringOperation(ToStringOperation expr, Map cycleTracker) { UNCONSTRAINED_STRING_WITH_NO_META } - override protected caseToTimeOperation(ToTimeOperation expr, Set cycleTracker) { + override protected caseToTimeOperation(ToTimeOperation expr, Map cycleTracker) { TIME_WITH_NO_META } - override protected caseConstructorExpression(RosettaConstructorExpression expr, Set cycleTracker) { + override protected caseConstructorExpression(RosettaConstructorExpression expr, Map cycleTracker) { expr.typeCall.typeCallToRType.withEmptyMeta } - override protected caseToDateOperation(ToDateOperation expr, Set cycleTracker) { + override protected caseToDateOperation(ToDateOperation expr, Map cycleTracker) { DATE_WITH_NO_META } - override protected caseToDateTimeOperation(ToDateTimeOperation expr, Set cycleTracker) { + override protected caseToDateTimeOperation(ToDateTimeOperation expr, Map cycleTracker) { DATE_TIME_WITH_NO_META } - override protected caseToZonedDateTimeOperation(ToZonedDateTimeOperation expr, Set cycleTracker) { + override protected caseToZonedDateTimeOperation(ToZonedDateTimeOperation expr, Map cycleTracker) { ZONED_DATE_TIME_WITH_NO_META } - override protected caseSwitchOperation(SwitchOperation expr, Set cycleTracker) { + override protected caseSwitchOperation(SwitchOperation expr, Map cycleTracker) { expr.cases .map[it.expression.safeRType(cycleTracker)] .joinMetaAnnotatedTypes diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/ListOperationTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/ListOperationTest.xtend index 23958d63c..86493b4e3 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/ListOperationTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/ListOperationTest.xtend @@ -961,7 +961,7 @@ class ListOperationTest { set foos: bar -> foos - map [ if item -> include = True then Foo { include: include, attr: attr + "_bar" } else item ] + extract [ if item -> include = True then Foo { include: include, attr: attr + "_bar" } else item ] ''' val code = model.generateCode val f = code.get("com.rosetta.test.model.functions.FuncFoo") @@ -1073,7 +1073,7 @@ class ListOperationTest { add updatedBar -> foos: bar -> foos - map [ if item -> include = True then Create_Foo( item -> include, Create_Attr( item -> attr, "_bar" ) ) else item ] + extract [ if item -> include = True then Create_Foo( item -> include, Create_Attr( item -> attr, "_bar" ) ) else item ] func Create_Foo: inputs: @@ -1266,7 +1266,7 @@ class ListOperationTest { } @Test - def void shouldGenerateFunctionWithMapList() { + def void shouldGenerateFunctionWithExtractList() { val model = ''' type Foo: attr string (1..1) @@ -1279,7 +1279,7 @@ class ListOperationTest { set strings: foos - map [ item -> attr ] + extract [ item -> attr ] ''' val code = model.generateCode val f = code.get("com.rosetta.test.model.functions.FuncFoo") @@ -1350,7 +1350,7 @@ class ListOperationTest { } @Test - def void shouldGenerateFunctionWithMapList2() { + def void shouldGenerateFunctionWithExtractList2() { val model = ''' type Foo: attr string (1..1) @@ -1363,7 +1363,7 @@ class ListOperationTest { set strings: foos - map foo [ foo -> attr ] + extract foo [ foo -> attr ] ''' val code = model.generateCode val classes = code.compileToClasses @@ -1384,7 +1384,7 @@ class ListOperationTest { } @Test - def void shouldGenerateFunctionWithMapListOfListThenMapToListOfCounts() { + def void shouldGenerateFunctionWithExtractListOfListThenExtractToListOfCounts() { val model = ''' type Bar: foos Foo (0..*) @@ -1400,8 +1400,8 @@ class ListOperationTest { set fooCounts: bars - map bar [ bar -> foos ] - then map fooListItem [ fooListItem count ] + extract bar [ bar -> foos ] + then extract fooListItem [ fooListItem count ] ''' val code = model.generateCode val f = code.get("com.rosetta.test.model.functions.FuncFoo") @@ -1476,7 +1476,7 @@ class ListOperationTest { } @Test - def void shouldGenerateFunctionWithMapListOfListThenMapToListOfCounts2() { + def void shouldGenerateFunctionWithExtractListOfListThenExtractToListOfCounts2() { val model = ''' type Bar: foos Foo (0..*) @@ -1492,8 +1492,8 @@ class ListOperationTest { set fooCounts: bars - map [ item -> foos ] - then map [ item count ] + extract [ item -> foos ] + then extract [ item count ] ''' val code = model.generateCode val classes = code.compileToClasses @@ -1513,7 +1513,7 @@ class ListOperationTest { } @Test - def void shouldGenerateFunctionWithMapListOfListThenFilterOnCount() { + def void shouldGenerateFunctionWithExtractListOfListThenFilterOnCount() { val model = ''' type Bar: foos Foo (0..*) @@ -1529,9 +1529,9 @@ class ListOperationTest { set fooCounts: bars - map [ item -> foos ] + extract [ item -> foos ] then filter [ item count > 1 ] - then map [ item count ] + then extract [ item count ] ''' val code = model.generateCode val classes = code.compileToClasses @@ -1567,9 +1567,9 @@ class ListOperationTest { set fooCounts: bars - map a [ a -> foos ] + extract a [ a -> foos ] then filter b [ b count > 1 ] - then map c [ c count ] + then extract c [ c count ] ''' val code = model.generateCode val classes = code.compileToClasses @@ -1589,7 +1589,7 @@ class ListOperationTest { } @Test - def void shouldGenerateFunctionWithMapListOfListsThenFlatten() { + def void shouldGenerateFunctionWithExtractListOfListsThenFlatten() { val model = ''' type Bar: foos Foo (0..*) @@ -1605,7 +1605,7 @@ class ListOperationTest { set foos: bars - map bar [ bar -> foos ] + extract bar [ bar -> foos ] then flatten ''' val code = model.generateCode @@ -1700,7 +1700,7 @@ class ListOperationTest { } @Test - def void shouldGenerateFunctionWithMapListOfListsThenFlatten2() { + def void shouldGenerateFunctionWithExtractListOfListsThenFlatten2() { val model = ''' type Bar: foos Foo (0..*) @@ -1716,7 +1716,7 @@ class ListOperationTest { set foos: bars - map [ item -> foos ] + extract [ item -> foos ] then flatten ''' val code = model.generateCode @@ -1740,7 +1740,7 @@ class ListOperationTest { } @Test - def void shouldGenerateFunctionWithMapListOfListsThenFlatten3() { + def void shouldGenerateFunctionWithExtractListOfListsThenFlatten3() { val model = ''' type Bar: foos Foo (0..*) @@ -1756,9 +1756,9 @@ class ListOperationTest { set attrs: bars - map [ item -> foos ] + extract [ item -> foos ] then flatten - then map [ item -> attr ] + then extract [ item -> attr ] ''' val code = model.generateCode val f = code.get("com.rosetta.test.model.functions.FuncFoo") @@ -1837,7 +1837,7 @@ class ListOperationTest { } @Test - def void shouldGenerateFunctionWithMapListCount() { + def void shouldGenerateFunctionWithExtractListCount() { val model = ''' type Bar: foos Foo (0..*) @@ -1853,7 +1853,7 @@ class ListOperationTest { set fooCounts: bars - map [ item -> foos count ] + extract [ item -> foos count ] ''' val code = model.generateCode val classes = code.compileToClasses @@ -1889,7 +1889,7 @@ class ListOperationTest { set fooCounts: bars - map bar [ bar -> foos count ] + extract bar [ bar -> foos count ] ''' val code = model.generateCode val classes = code.compileToClasses @@ -1909,7 +1909,7 @@ class ListOperationTest { } @Test - def void shouldGenerateFunctionWithNestedMaps() { + def void shouldGenerateFunctionWithNestedExtracts() { val model = ''' type Bar: foos Foo (0..*) @@ -1925,10 +1925,10 @@ class ListOperationTest { set updatedBars: bars - map bar [ bar -> foos - map foo [ NewFoo( foo -> attr + "_bar" ) ] + extract bar [ bar -> foos + extract foo [ NewFoo( foo -> attr + "_bar" ) ] ] - then map updatedFoos [ NewBar( updatedFoos ) ] + then extract updatedFoos [ NewBar( updatedFoos ) ] func NewBar: inputs: @@ -2070,9 +2070,9 @@ class ListOperationTest { set updatedBars: bars - map bar [ + extract bar [ NewBar( bar -> foos - map foo [ NewFoo( foo -> attr + "_bar" ) ] ) + extract foo [ NewFoo( foo -> attr + "_bar" ) ] ) ] func NewBar: @@ -2196,7 +2196,7 @@ class ListOperationTest { } @Test - def void shouldGenerateFunctionWithMapListModifyItemFunc() { + def void shouldGenerateFunctionWithExtractListModifyItemFunc() { val model = ''' type Foo: attr string (1..1) @@ -2209,7 +2209,7 @@ class ListOperationTest { set updatedFoos: foos - map [ NewFoo( item -> attr + "_1" ) ] + extract [ NewFoo( item -> attr + "_1" ) ] func NewFoo: inputs: @@ -2244,7 +2244,7 @@ class ListOperationTest { } @Test - def void shouldGenerateFunctionWithFilterThenMap() { + def void shouldGenerateFunctionWithFilterThenExtract() { val model = ''' type Foo: include boolean (1..1) @@ -2259,7 +2259,7 @@ class ListOperationTest { set newFoos: foos filter [ item -> include = True ] - then map [ item -> attr ] + then extract [ item -> attr ] ''' val code = model.generateCode @@ -2360,8 +2360,8 @@ class ListOperationTest { set strings: bars - map [ GetFoo( item -> barAttr ) ] - then map [ item -> fooAttr ] + extract [ GetFoo( item -> barAttr ) ] + then extract [ item -> fooAttr ] ''' val code = model.generateCode val f = code.get("ns1.functions.FuncFoo") @@ -2449,9 +2449,9 @@ class ListOperationTest { set strings: bars - map [ item -> foos ] + extract [ item -> foos ] then flatten - then map [ item -> attr ] + then extract [ item -> attr ] '''] val code = model.generateCode val f = code.get("ns2.functions.FuncFoo") @@ -2542,8 +2542,8 @@ class ListOperationTest { set strings: bars - map [ GetFoo( item -> barAttr ) ] - then map [ item -> fooAttr ] + extract [ GetFoo( item -> barAttr ) ] + then extract [ item -> fooAttr ] '''] val code = model.generateCode val f = code.get("ns2.functions.FuncFoo") @@ -2647,8 +2647,8 @@ class ListOperationTest { set strings: bars - map [ GetFoo( GetBaz( item -> barAttr ) ) ] - then map [ item -> fooAttr ] + extract [ GetFoo( GetBaz( item -> barAttr ) ) ] + then extract [ item -> fooAttr ] '''] val code = model.generateCode val f = code.get("ns2.functions.FuncFoo") @@ -2731,11 +2731,11 @@ class ListOperationTest { set strings: if test = "a" - then foos map [ item -> attr + "_a" ] + then foos extract [ item -> attr + "_a" ] else if test = "b" - then foos map [ item -> attr + "_b" ] + then foos extract [ item -> attr + "_b" ] else if test = "c" - then foos map [ item -> attr + "_c" ] + then foos extract [ item -> attr + "_c" ] // default else ''' val code = model.generateCode @@ -3475,7 +3475,7 @@ class ListOperationTest { set fooCount: bars reduce bar1, bar2 [ if bar1 -> foos count > bar2 -> foos count then bar1 else bar2 ] - then map [ item -> foos count ] + then extract [ item -> foos count ] ''' val code = model.generateCode val classes = code.compileToClasses @@ -3502,7 +3502,7 @@ class ListOperationTest { } @Test - def void shouldGenerateListReduceThenMapList() { + def void shouldGenerateListReduceThenExtractList() { val model = ''' type Bar: foos Foo (0..*) @@ -3519,8 +3519,8 @@ class ListOperationTest { set attrs: bars reduce bar1, bar2 [ if bar1 -> foos count > bar2 -> foos count then bar1 else bar2 ] // max by foo count - then map [ item -> foos ] - then map [ item -> attr ] + then extract [ item -> foos ] + then extract [ item -> attr ] ''' val code = model.generateCode val classes = code.compileToClasses diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaValidatorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaValidatorTest.xtend index dc46a8928..02dbf113a 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaValidatorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaValidatorTest.xtend @@ -2442,7 +2442,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { } @Test - def shouldNotGenerateCountCardinalityErrorForMap() { + def shouldNotGenerateCountCardinalityErrorForExtract() { val model = ''' type Bar: foos Foo (0..*) @@ -2466,7 +2466,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { } @Test - def shouldNotGenerateCountCardinalityErrorDefaultParameterForMap() { + def shouldNotGenerateCountCardinalityErrorDefaultParameterForExtract() { val model = ''' type Bar: foos Foo (0..*) @@ -2490,7 +2490,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { } @Test - def shouldNotGenerateCountCardinalityErrorForNestedMap() { + def shouldNotGenerateCountCardinalityErrorForNestedExtract() { val model = ''' type Bar: foos Foo (0..*) @@ -2516,7 +2516,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { } @Test - def shouldNotGenerateCountCardinalityErrorDefaultParameterForNestedMap() { + def shouldNotGenerateCountCardinalityErrorDefaultParameterForNestedExtract() { val model = ''' type Bar: foos Foo (0..*) @@ -2542,7 +2542,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { } @Test - def shouldNotGenerateErrorForMapListOperation() { + def shouldNotGenerateErrorForExtractListOperation() { val model = ''' type Bar: foo Foo (1..1) @@ -2609,7 +2609,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { set result: if bars exists - then bars map [ item -> foo ] distinct only-element -> amount + then bars extract [ item -> foo ] distinct only-element -> amount '''.parseRosetta // then clause should generate syntax error (see test above shouldGenerateErrorForFeatureCallAfterListOperation) model.assertError(ROSETTA_MODEL, Diagnostic.SYNTAX_DIAGNOSTIC, "missing EOF at '->'") @@ -2719,7 +2719,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { } @Test - def void shouldGenerateListMapNoExpressionError() { + def void shouldGenerateListExtractNoExpressionError() { val model = ''' func FuncFoo: inputs: @@ -2729,7 +2729,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { add strings: foos - map + extract type Foo: x string (1..1) @@ -2738,7 +2738,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { } @Test - def void shouldGenerateListMapParametersError() { + def void shouldGenerateListExtractParametersError() { val model = ''' func FuncFoo: inputs: @@ -2748,7 +2748,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { add strings: foos - map a, b [ a -> x ] + extract a, b [ a -> x ] type Foo: x string (1..1) @@ -2757,7 +2757,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { } @Test - def void mapWithNamedFunctionReferenceShouldGenerateNoError() { + def void extractWithNamedFunctionReferenceShouldGenerateNoError() { val model = ''' func DoSomething: inputs: @@ -2785,7 +2785,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { } @Test - def void shouldGenerateListMapParametersErrorNamedFunctionReference() { + def void shouldGenerateListExtractParametersErrorNamedFunctionReference() { val model = ''' func DoSomething: inputs: @@ -2805,7 +2805,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { add strings: foos - map DoSomething + extract DoSomething type Foo: x string (1..1) @@ -2814,7 +2814,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { } @Test - def void shouldNotGenerateListMapExpressionCardinalityError() { + def void shouldNotGenerateListExtractExpressionCardinalityError() { val model = ''' func FuncFoo: inputs: @@ -2835,7 +2835,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { } @Test - def void shouldNotGenerateListMapExpressionCardinalityError2() { + def void shouldNotGenerateListExtractExpressionCardinalityError2() { val model = ''' func FuncFoo: inputs: @@ -2859,7 +2859,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { } @Test - def void shouldNotGenerateListMapExpressionCardinalityError3() { + def void shouldNotGenerateListExtractExpressionCardinalityError3() { val model = ''' func FuncFoo: inputs: @@ -2897,7 +2897,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { add strings: foos - map a [ a -> x ] // not a list of lists + extract a [ a -> x ] // not a list of lists flatten type Foo: @@ -3013,7 +3013,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { add strings: foos - map a [ a -> xs ] // list of lists + extract a [ a -> xs ] // list of lists type Foo: xs string (0..*) @@ -3032,7 +3032,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { add strings: foos - map a [ a -> xs ] // list of lists + extract a [ a -> xs ] // list of lists type Foo: xs string (0..*) @@ -3051,7 +3051,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { alias stringsAlias: foos - map a [ a -> xs ] // list of lists + extract a [ a -> xs ] // list of lists add strings: stringsAlias @@ -3073,7 +3073,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { set res: foos - map a [ a -> xs ] // list of lists + extract a [ a -> xs ] // list of lists only-element type Foo: @@ -3093,7 +3093,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { add res: foos - map a [ a -> xs ] // list of lists + extract a [ a -> xs ] // list of lists distinct type Foo: From 9dc849e6366e3924fb9dc0f4875e6fee7acab369 Mon Sep 17 00:00:00 2001 From: Simon Cockx Date: Mon, 9 Dec 2024 15:51:35 +0000 Subject: [PATCH 04/10] Fixed tests --- .../ide/quickfix/RosettaQuickFixProvider.java | 11 --- .../ide/contentassist/ContentAssistTest.xtend | 4 - .../rosetta/ide/quickfix/QuickFixTest.xtend | 33 -------- .../ide/server/ChangeDetectionTest.xtend | 4 +- .../expression/DeepPathUtilGenerator.xtend | 2 +- .../java/expression/ExpressionGenerator.xtend | 12 +-- .../java/function/FunctionGenerator.xtend | 4 +- .../java/object/MetaFieldGenerator.xtend | 4 +- .../scoping/RosettaScopeProvider.xtend | 6 +- .../rosetta/types/ExpectedTypeProvider.java | 34 ++++----- .../com/regnosys/rosetta/types/RListType.java | 75 ------------------- .../rosetta/types/RMetaAnnotatedType.java | 4 +- .../rosetta/types/RObjectFactory.java | 2 +- .../rosetta/types/RosettaTypeProvider.xtend | 32 ++++---- .../rosetta/types/SubtypeRelation.java | 2 +- .../regnosys/rosetta/types/TypeFactory.java | 20 ++--- .../types/builtin/RBuiltinTypeService.java | 22 +++--- .../rosetta/validation/RosettaIssueCodes.java | 1 - .../AbstractExpressionValidator.java | 6 ++ .../expression/ExpressionValidator.java | 7 +- .../expression/SwitchValidator.java | 2 +- .../RosettaExistsExpressionTest.xtend | 16 ++-- .../types/RosettaTypeProviderTest.xtend | 6 +- .../rosetta/types/SubtypeRelationTest.xtend | 4 +- 24 files changed, 97 insertions(+), 216 deletions(-) delete mode 100644 rosetta-lang/src/main/java/com/regnosys/rosetta/types/RListType.java diff --git a/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/quickfix/RosettaQuickFixProvider.java b/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/quickfix/RosettaQuickFixProvider.java index 78d6dd70f..775d325f3 100644 --- a/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/quickfix/RosettaQuickFixProvider.java +++ b/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/quickfix/RosettaQuickFixProvider.java @@ -69,15 +69,4 @@ public void fixMandatoryThen(DiagnosticResolutionAcceptor acceptor) { return List.of(edit); }); } - - @QuickFix(RosettaIssueCodes.DEPRECATED_MAP) - public void fixDeprecatedMap(DiagnosticResolutionAcceptor acceptor) { - acceptor.accept("Replace with `extract`.", (Diagnostic diagnostic, EObject object, Document document) -> { - RosettaUnaryOperation op = (RosettaUnaryOperation)object; - Range range = rangeUtils.getRange(op, ROSETTA_OPERATION__OPERATOR); - String edited = "extract"; - TextEdit edit = new TextEdit(range, edited); - return List.of(edit); - }); - } } diff --git a/rosetta-ide/src/test/java/com/regnosys/rosetta/ide/contentassist/ContentAssistTest.xtend b/rosetta-ide/src/test/java/com/regnosys/rosetta/ide/contentassist/ContentAssistTest.xtend index 5467dd7aa..4c18c8575 100644 --- a/rosetta-ide/src/test/java/com/regnosys/rosetta/ide/contentassist/ContentAssistTest.xtend +++ b/rosetta-ide/src/test/java/com/regnosys/rosetta/ide/contentassist/ContentAssistTest.xtend @@ -45,7 +45,6 @@ class ContentAssistTest extends AbstractRosettaLanguageServerTest { is -> is [[9, 14] .. [9, 14]] join -> join [[9, 14] .. [9, 14]] last -> last [[9, 14] .. [9, 14]] - map -> map [[9, 14] .. [9, 14]] max -> max [[9, 14] .. [9, 14]] min -> min [[9, 14] .. [9, 14]] multiple -> multiple [[9, 14] .. [9, 14]] @@ -134,7 +133,6 @@ class ContentAssistTest extends AbstractRosettaLanguageServerTest { join -> join [[7, 27] .. [7, 27]] last -> last [[7, 27] .. [7, 27]] library -> library [[7, 27] .. [7, 27]] - map -> map [[7, 27] .. [7, 27]] max -> max [[7, 27] .. [7, 27]] metaType -> metaType [[7, 27] .. [7, 27]] min -> min [[7, 27] .. [7, 27]] @@ -219,7 +217,6 @@ class ContentAssistTest extends AbstractRosettaLanguageServerTest { is -> is [[6, 25] .. [6, 25]] join -> join [[6, 25] .. [6, 25]] last -> last [[6, 25] .. [6, 25]] - map -> map [[6, 25] .. [6, 25]] max -> max [[6, 25] .. [6, 25]] min -> min [[6, 25] .. [6, 25]] multiple -> multiple [[6, 25] .. [6, 25]] @@ -414,7 +411,6 @@ class ContentAssistTest extends AbstractRosettaLanguageServerTest { item -> item [[19, 2] .. [19, 2]] join -> join [[19, 2] .. [19, 2]] last -> last [[19, 2] .. [19, 2]] - map -> map [[19, 2] .. [19, 2]] max -> max [[19, 2] .. [19, 2]] min -> min [[19, 2] .. [19, 2]] multiple -> multiple [[19, 2] .. [19, 2]] diff --git a/rosetta-ide/src/test/java/com/regnosys/rosetta/ide/quickfix/QuickFixTest.xtend b/rosetta-ide/src/test/java/com/regnosys/rosetta/ide/quickfix/QuickFixTest.xtend index 5af1e88c8..32a1cd20b 100644 --- a/rosetta-ide/src/test/java/com/regnosys/rosetta/ide/quickfix/QuickFixTest.xtend +++ b/rosetta-ide/src/test/java/com/regnosys/rosetta/ide/quickfix/QuickFixTest.xtend @@ -54,37 +54,4 @@ class QuickFixTest extends AbstractRosettaLanguageServerTest { ] ] } - - @Test - def testQuickFixDeprecatedMap() { - val model = ''' - namespace foo.bar - - type Foo: - a int (1..1) - - func Bar: - inputs: foo Foo (1..1) - output: result int (1..1) - - set result: foo map a - ''' - testCodeAction[ - it.model = model - assertCodeActions = [ - assertEquals(1, size) - - val sorted = it.sortWith[a,b| ru.comparePositions(a.getRight.diagnostics.head.range.start, b.getRight.diagnostics.head.range.start)] - - sorted.get(0).getRight => [ - assertEquals("Replace with `extract`.", title) - edit.changes.values.head.head => [ - assertEquals("extract", newText) - assertEquals(new Position(9, 17), range.start) - assertEquals(new Position(9, 20), range.end) - ] - ] - ] - ] - } } \ No newline at end of file diff --git a/rosetta-ide/src/test/java/com/regnosys/rosetta/ide/server/ChangeDetectionTest.xtend b/rosetta-ide/src/test/java/com/regnosys/rosetta/ide/server/ChangeDetectionTest.xtend index b3a10c781..5435b194d 100644 --- a/rosetta-ide/src/test/java/com/regnosys/rosetta/ide/server/ChangeDetectionTest.xtend +++ b/rosetta-ide/src/test/java/com/regnosys/rosetta/ide/server/ChangeDetectionTest.xtend @@ -133,8 +133,8 @@ class ChangeDetectionTest extends AbstractRosettaLanguageServerValidationTest { // There should be a type error in rule B val issues = diagnostics.get(ruleBURI) - assertEquals(2, issues.size) - assertEquals("Expected type 'int' but was 'string'", issues.head.message) + assertEquals(1, issues.size) + assertEquals("Expected type `int`, but got `string` instead", issues.head.message) } @Test diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/expression/DeepPathUtilGenerator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/expression/DeepPathUtilGenerator.xtend index 3c4037bdf..374a39826 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/expression/DeepPathUtilGenerator.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/expression/DeepPathUtilGenerator.xtend @@ -104,7 +104,7 @@ class DeepPathUtilGenerator { for (a : attrs.reverseView) { val currAcc = acc acc = inputParameter - .attributeCall(choiceType.withEmptyMeta, a, false, scope) + .attributeCall(choiceType.withNoMeta, a, false, scope) .declareAsVariable(true, a.name.toFirstLower, scope) .mapExpression[attrVar| attrVar.exists(ExistsModifier.NONE, scope) diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/expression/ExpressionGenerator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/expression/ExpressionGenerator.xtend index 770da3669..7668fe2b2 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/expression/ExpressionGenerator.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/expression/ExpressionGenerator.xtend @@ -136,9 +136,9 @@ import org.eclipse.xtext.EcoreUtil2 import org.eclipse.xtext.xbase.lib.Functions.Function3 import static extension com.regnosys.rosetta.generator.java.enums.EnumHelper.convertValue -import static extension com.regnosys.rosetta.types.RMetaAnnotatedType.withEmptyMeta import com.regnosys.rosetta.generator.java.types.RJavaFieldWithMeta import com.regnosys.rosetta.generator.java.types.RJavaWithMetaValue +import static extension com.regnosys.rosetta.types.RMetaAnnotatedType.withNoMeta class ExpressionGenerator extends RosettaExpressionSwitch { @@ -224,10 +224,10 @@ class ExpressionGenerator extends RosettaExpressionSwitch metaAttributes) { this.rType = rType; this.metaAttributes = Validate.noNullElements(metaAttributes); } - // TODO: Rename to withNoMeta - public static RMetaAnnotatedType withEmptyMeta(RType rType) { + + public static RMetaAnnotatedType withNoMeta(RType rType) { return new RMetaAnnotatedType(rType, List.of()); } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RObjectFactory.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RObjectFactory.java index c27f40c92..29789a335 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RObjectFactory.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RObjectFactory.java @@ -75,7 +75,7 @@ public RFunction buildRFunction(Function function) { // TODO: should be private public RAttribute createArtificialAttribute(String name, RType type, boolean isMulti) { - RMetaAnnotatedType rAnnotatedType = RMetaAnnotatedType.withEmptyMeta(type); + RMetaAnnotatedType rAnnotatedType = RMetaAnnotatedType.withNoMeta(type); return new RAttribute(name, null, Collections.emptyList(), rAnnotatedType, isMulti ? PositiveIntegerInterval.boundedLeft(0) : PositiveIntegerInterval.bounded(0, 1), null, null); } public RFunction buildRFunction(RosettaRule rule) { diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaTypeProvider.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaTypeProvider.xtend index 1dfe9af02..9454846d3 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaTypeProvider.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaTypeProvider.xtend @@ -82,13 +82,13 @@ import javax.inject.Inject import javax.inject.Provider import org.eclipse.emf.ecore.EObject import com.regnosys.rosetta.rosetta.expression.SwitchCase -import static extension com.regnosys.rosetta.types.RMetaAnnotatedType.withEmptyMeta import static extension com.regnosys.rosetta.types.RMetaAnnotatedType.withMeta import com.regnosys.rosetta.rosetta.RosettaParameter import com.regnosys.rosetta.types.builtin.RStringType import com.regnosys.rosetta.types.builtin.RNumberType import com.regnosys.rosetta.utils.OptionalUtil import java.util.Map +import static extension com.regnosys.rosetta.types.RMetaAnnotatedType.withNoMeta class RosettaTypeProvider extends RosettaExpressionSwitch> { public static String EXPRESSION_RTYPE_CACHE_KEY = RosettaTypeProvider.canonicalName + ".EXPRESSION_RTYPE" @@ -171,7 +171,7 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { - constrainedInt(Optional.empty(), Optional.of(BigInteger.ZERO), Optional.empty()).withEmptyMeta + constrainedInt(Optional.empty(), Optional.of(BigInteger.ZERO), Optional.empty()).withNoMeta } override protected caseDisjointOperation(RosettaDisjointExpression expr, Map cycleTracker) { @@ -419,7 +419,7 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { - constrainedInt(if (expr.value.signum >= 0) expr.value.toString.length else expr.value.toString.length - 1, expr.value, expr.value).withEmptyMeta + constrainedInt(if (expr.value.signum >= 0) expr.value.toString.length else expr.value.toString.length - 1, expr.value, expr.value).withNoMeta } override protected caseJoinOperation(JoinOperation expr, Map cycleTracker) { @@ -473,7 +473,7 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { @@ -484,7 +484,7 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { @@ -516,7 +516,7 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { - constrainedString(expr.value.length, expr.value.length).withEmptyMeta + constrainedString(expr.value.length, expr.value.length).withNoMeta } override protected caseSubtractOperation(ArithmeticOperation expr, Map cycleTracker) { @@ -537,7 +537,7 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { - expr.enumeration.buildREnumType.withEmptyMeta + expr.enumeration.buildREnumType.withNoMeta } override protected caseToIntOperation(ToIntOperation expr, Map cycleTracker) { @@ -589,7 +589,7 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { - expr.typeCall.typeCallToRType.withEmptyMeta + expr.typeCall.typeCallToRType.withNoMeta } override protected caseToDateOperation(ToDateOperation expr, Map cycleTracker) { diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/SubtypeRelation.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/SubtypeRelation.java index c1402cf0e..581b84f5a 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/SubtypeRelation.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/SubtypeRelation.java @@ -107,7 +107,7 @@ public RMetaAnnotatedType join(RMetaAnnotatedType t1, RMetaAnnotatedType t2) { if (t1RType.equals(t2RType)) { return RMetaAnnotatedType.withMeta(t1RType, intersectMeta(t1, t2)); } - return RMetaAnnotatedType.withEmptyMeta(join(t1RType, t2RType)); + return RMetaAnnotatedType.withNoMeta(join(t1RType, t2RType)); } public RType join(RType t1, RType t2) { diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/TypeFactory.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/TypeFactory.java index 38eee4415..a0f8df801 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/TypeFactory.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/TypeFactory.java @@ -40,13 +40,13 @@ public TypeFactory(RBuiltinTypeService builtinTypes) { } public RMetaAnnotatedType intWithNoMeta(Optional digits, Optional min, Optional max) { - return RMetaAnnotatedType.withEmptyMeta(constrainedInt(digits, min, max)); + return RMetaAnnotatedType.withNoMeta(constrainedInt(digits, min, max)); } public RMetaAnnotatedType intWithNoMeta(int digits, BigInteger min, BigInteger max) { - return RMetaAnnotatedType.withEmptyMeta(constrainedInt(digits, min, max)); + return RMetaAnnotatedType.withNoMeta(constrainedInt(digits, min, max)); } public RMetaAnnotatedType intWithNoMeta(int digits, String min, String max) { - return RMetaAnnotatedType.withEmptyMeta(constrainedInt(digits, min, max)); + return RMetaAnnotatedType.withNoMeta(constrainedInt(digits, min, max)); } public RAliasType constrainedInt(Optional digits, Optional min, Optional max) { RNumberType refersTo = constrainedNumber(digits, Optional.of(0), min.map(BigDecimal::new), max.map(BigDecimal::new), Optional.empty()); @@ -64,17 +64,17 @@ public RAliasType constrainedInt(int digits, String min, String max) { public RMetaAnnotatedType numberWithNoMeta(Optional digits, Optional fractionalDigits, Optional min, Optional max, Optional scale) { - return RMetaAnnotatedType.withEmptyMeta(constrainedNumber(digits, fractionalDigits, min, max, scale)); + return RMetaAnnotatedType.withNoMeta(constrainedNumber(digits, fractionalDigits, min, max, scale)); } public RMetaAnnotatedType numberWithNoMeta(Optional digits, Optional fractionalDigits, BigDecimalInterval interval, Optional scale) { - return RMetaAnnotatedType.withEmptyMeta(constrainedNumber(digits, fractionalDigits, interval, scale)); + return RMetaAnnotatedType.withNoMeta(constrainedNumber(digits, fractionalDigits, interval, scale)); } public RMetaAnnotatedType numberWithNoMeta(int digits, int fractionalDigits, BigDecimal min, BigDecimal max) { - return RMetaAnnotatedType.withEmptyMeta(constrainedNumber(digits, fractionalDigits, min, max)); + return RMetaAnnotatedType.withNoMeta(constrainedNumber(digits, fractionalDigits, min, max)); } public RMetaAnnotatedType numberWithNoMeta(int digits, int fractionalDigits, String min, String max) { - return RMetaAnnotatedType.withEmptyMeta(constrainedNumber(digits, fractionalDigits, min, max)); + return RMetaAnnotatedType.withNoMeta(constrainedNumber(digits, fractionalDigits, min, max)); } public RNumberType constrainedNumber(Optional digits, Optional fractionalDigits, Optional min, Optional max, Optional scale) { @@ -92,13 +92,13 @@ public RNumberType constrainedNumber(int digits, int fractionalDigits, String mi } public RMetaAnnotatedType stringWithNoMeta(Optional minLength, Optional maxLength, Optional pattern) { - return RMetaAnnotatedType.withEmptyMeta(constrainedString(minLength, maxLength, pattern)); + return RMetaAnnotatedType.withNoMeta(constrainedString(minLength, maxLength, pattern)); } public RMetaAnnotatedType stringWithNoMeta(PositiveIntegerInterval interval, Optional pattern) { - return RMetaAnnotatedType.withEmptyMeta(constrainedString(interval, pattern)); + return RMetaAnnotatedType.withNoMeta(constrainedString(interval, pattern)); } public RMetaAnnotatedType stringWithNoMeta(int minLength, int maxLength) { - return RMetaAnnotatedType.withEmptyMeta(constrainedString(minLength, maxLength)); + return RMetaAnnotatedType.withNoMeta(constrainedString(minLength, maxLength)); } public RStringType constrainedString(Optional minLength, Optional maxLength, Optional pattern) { return new RStringType(minLength, maxLength, pattern); diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/builtin/RBuiltinTypeService.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/builtin/RBuiltinTypeService.java index c6fe1fc03..668eca3e7 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/builtin/RBuiltinTypeService.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/builtin/RBuiltinTypeService.java @@ -74,28 +74,28 @@ public Optional> reverse(RType type) { }; public final RBasicType BOOLEAN = registerConstantType(new RBasicType("boolean", true)); - public final RMetaAnnotatedType BOOLEAN_WITH_NO_META = RMetaAnnotatedType.withEmptyMeta(BOOLEAN); + public final RMetaAnnotatedType BOOLEAN_WITH_NO_META = RMetaAnnotatedType.withNoMeta(BOOLEAN); public final RBasicType TIME = registerConstantType(new RBasicType("time", true)); - public final RMetaAnnotatedType TIME_WITH_NO_META = RMetaAnnotatedType.withEmptyMeta(TIME); + public final RMetaAnnotatedType TIME_WITH_NO_META = RMetaAnnotatedType.withNoMeta(TIME); public final RBasicType PATTERN = registerConstantType(new RBasicType("pattern", false)); - public final RMetaAnnotatedType PATTERN_WITH_NO_META = RMetaAnnotatedType.withEmptyMeta(PATTERN); + public final RMetaAnnotatedType PATTERN_WITH_NO_META = RMetaAnnotatedType.withNoMeta(PATTERN); public final RBasicType NOTHING = registerConstantType(new RBasicType("nothing", true)); - public final RMetaAnnotatedType NOTHING_WITH_NO_META = RMetaAnnotatedType.withEmptyMeta(NOTHING); + public final RMetaAnnotatedType NOTHING_WITH_NO_META = RMetaAnnotatedType.withNoMeta(NOTHING); public final RBasicType ANY = registerConstantType(new RBasicType("any", false)); - public final RMetaAnnotatedType ANY_WITH_NO_META = RMetaAnnotatedType.withEmptyMeta(ANY); + public final RMetaAnnotatedType ANY_WITH_NO_META = RMetaAnnotatedType.withNoMeta(ANY); public final RAliasType UNCONSTRAINED_INT = new RAliasType(INT_FUNCTION, new LinkedHashMap<>(Map.of(RNumberType.DIGITS_PARAM_NAME, RosettaValue.empty(), RNumberType.MIN_PARAM_NAME, RosettaValue.empty(), RNumberType.MAX_PARAM_NAME, RosettaValue.empty())), new RNumberType(Optional.empty(), Optional.of(0), Optional.empty(), Optional.empty(), Optional.empty())); - public final RMetaAnnotatedType UNCONSTRAINED_INT_WITH_NO_META = RMetaAnnotatedType.withEmptyMeta(UNCONSTRAINED_INT); + public final RMetaAnnotatedType UNCONSTRAINED_INT_WITH_NO_META = RMetaAnnotatedType.withNoMeta(UNCONSTRAINED_INT); public final RNumberType UNCONSTRAINED_NUMBER = new RNumberType(Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty()); - public final RMetaAnnotatedType UNCONSTRAINED_NUMBER_WITH_NO_META = RMetaAnnotatedType.withEmptyMeta(UNCONSTRAINED_NUMBER); + public final RMetaAnnotatedType UNCONSTRAINED_NUMBER_WITH_NO_META = RMetaAnnotatedType.withNoMeta(UNCONSTRAINED_NUMBER); public final RStringType UNCONSTRAINED_STRING = new RStringType(Optional.empty(), Optional.empty(), Optional.empty()); - public final RMetaAnnotatedType UNCONSTRAINED_STRING_WITH_NO_META = RMetaAnnotatedType.withEmptyMeta(UNCONSTRAINED_STRING); + public final RMetaAnnotatedType UNCONSTRAINED_STRING_WITH_NO_META = RMetaAnnotatedType.withNoMeta(UNCONSTRAINED_STRING); public final RDateType DATE = registerConstantType(new RDateType()); - public final RMetaAnnotatedType DATE_WITH_NO_META = RMetaAnnotatedType.withEmptyMeta(DATE); + public final RMetaAnnotatedType DATE_WITH_NO_META = RMetaAnnotatedType.withNoMeta(DATE); public final RDateTimeType DATE_TIME = registerConstantType(new RDateTimeType()); - public final RMetaAnnotatedType DATE_TIME_WITH_NO_META = RMetaAnnotatedType.withEmptyMeta(DATE_TIME); + public final RMetaAnnotatedType DATE_TIME_WITH_NO_META = RMetaAnnotatedType.withNoMeta(DATE_TIME); public final RZonedDateTimeType ZONED_DATE_TIME = registerConstantType(new RZonedDateTimeType()); - public final RMetaAnnotatedType ZONED_DATE_TIME_WITH_NO_META = RMetaAnnotatedType.withEmptyMeta(ZONED_DATE_TIME); + public final RMetaAnnotatedType ZONED_DATE_TIME_WITH_NO_META = RMetaAnnotatedType.withNoMeta(ZONED_DATE_TIME); public RBuiltinTypeService() { register("number", (m) -> RNumberType.from(m)); diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaIssueCodes.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaIssueCodes.java index ab1f6144b..f5b603e35 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaIssueCodes.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaIssueCodes.java @@ -41,5 +41,4 @@ public interface RosettaIssueCodes { static final String MANDATORY_SQUARE_BRACKETS = PREFIX + "mandatorySquareBrackets"; static final String REDUNDANT_SQUARE_BRACKETS = PREFIX + "redundantSquareBrackets"; static final String MANDATORY_THEN = PREFIX + "mandatoryThen"; - static final String DEPRECATED_MAP = PREFIX + "deprecatedMap"; // @Compat } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/AbstractExpressionValidator.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/AbstractExpressionValidator.java index b3825b6ea..4ff5b4644 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/AbstractExpressionValidator.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/AbstractExpressionValidator.java @@ -111,6 +111,12 @@ protected boolean isSingleCheck(RosettaExpression expr, EObject sourceObject, ES return true; } + protected boolean commonTypeCheck(RosettaExpression expr1, RosettaExpression expr2, EObject sourceObject, EStructuralFeature feature) { + if (expr1 == null || expr2 == null) { + return true; + } + return commonTypeCheck(List.of(expr1, expr2), sourceObject, feature); + } protected boolean commonTypeCheck(List expressions, EObject sourceObject, EStructuralFeature feature) { boolean haveCommonType = true; if (!expressions.isEmpty()) { diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/ExpressionValidator.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/ExpressionValidator.java index 67864f2ab..fe01ca8ba 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/ExpressionValidator.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/ExpressionValidator.java @@ -49,7 +49,6 @@ import static com.regnosys.rosetta.rosetta.expression.ExpressionPackage.Literals.*; import java.util.HashSet; -import java.util.List; import java.util.Set; // TODO: move over expression validations from RosettaSimpleValidator @@ -186,7 +185,7 @@ public void checkDisjointExpression(RosettaDisjointExpression expr) { public void checkConditionalExpression(RosettaConditionalExpression expr) { isSingleCheck(expr.getIf(), expr, ROSETTA_CONDITIONAL_EXPRESSION__IF); subtypeCheck(builtins.BOOLEAN_WITH_NO_META, expr.getIf(), expr, ROSETTA_CONDITIONAL_EXPRESSION__IF); - commonTypeCheck(List.of(expr.getIfthen(), expr.getElsethen()), expr, ROSETTA_CONDITIONAL_EXPRESSION__ELSETHEN); + commonTypeCheck(expr.getIfthen(), expr.getElsethen(), expr, ROSETTA_CONDITIONAL_EXPRESSION__ELSETHEN); } @Check @@ -196,7 +195,7 @@ public void checkListLiteral(ListLiteral expr) { @Check public void checkDefaultOperation(DefaultOperation op) { - commonTypeCheck(List.of(op.getLeft(), op.getRight()), op, ROSETTA_BINARY_OPERATION__RIGHT); + commonTypeCheck(op.getLeft(), op.getRight(), op, ROSETTA_BINARY_OPERATION__RIGHT); } @Check @@ -233,7 +232,7 @@ public void checkSymbolReference(RosettaSymbolReference expr) { } else if (callable instanceof RosettaRule) { RosettaRule f = (RosettaRule) callable; if (minCount >= 1) { - RMetaAnnotatedType paramType = RMetaAnnotatedType.withEmptyMeta(typeSystem.typeCallToRType(f.getInput())); + RMetaAnnotatedType paramType = RMetaAnnotatedType.withNoMeta(typeSystem.typeCallToRType(f.getInput())); RosettaExpression arg = expr.getArgs().get(0); isSingleCheck(arg, expr, ROSETTA_SYMBOL_REFERENCE__RAW_ARGS, 0); subtypeCheck(paramType, arg, expr, ROSETTA_SYMBOL_REFERENCE__RAW_ARGS, 0); diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/SwitchValidator.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/SwitchValidator.java index e4c0cfb65..557e10506 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/SwitchValidator.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/SwitchValidator.java @@ -79,7 +79,7 @@ private void checkBasicTypeSwitch(RBasicType argumentType, SwitchOperation op) { // - there are no duplicate cases, // - all guards should be comparable to the input. Set seenValues = new HashSet<>(); - RMetaAnnotatedType argumentTypeWithoutMeta = RMetaAnnotatedType.withEmptyMeta(argumentType); + RMetaAnnotatedType argumentTypeWithoutMeta = RMetaAnnotatedType.withNoMeta(argumentType); for (SwitchCase caseStatement : op.getCases()) { RosettaLiteral guard = caseStatement.getGuard().getLiteralGuard(); if (guard == null) { diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/RosettaExistsExpressionTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/RosettaExistsExpressionTest.xtend index 8a260ce7b..e08d5e4be 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/RosettaExistsExpressionTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/RosettaExistsExpressionTest.xtend @@ -114,56 +114,56 @@ class RosettaExistsExpressionTest { set result: foo -> bar first then ( beforeListWithScheme, afterListWithScheme ) only exists -«««««« TODO tests compilation only, add unit test +««« TODO tests compilation only, add unit test func MultipleSeparateOr_NoAliases_Exists: inputs: foo Foo (1..1) output: result boolean (1..1) set result: foo -> bar -> before exists or foo -> bar -> after exists -«««««« TODO tests compilation only, add unit test +««« TODO tests compilation only, add unit test func MultipleOr_NoAliases_Exists: inputs: foo Foo (1..1) output: result boolean (1..1) set result: foo -> bar -> before exists or foo -> bar -> after exists or foo -> baz -> other exists -«««««« TODO tests compilation only, add unit test +««« TODO tests compilation only, add unit test func MultipleOrBranchNode_NoAliases_Exists: inputs: foo Foo (1..1) output: result boolean (1..1) set result: foo -> bar exists or foo -> baz exists -«««««« TODO tests compilation only, add unit test +««« TODO tests compilation only, add unit test func MultipleAnd_NoAliases_Exists: inputs: foo Foo (1..1) output: result boolean (1..1) set result: foo -> bar -> before exists and foo -> bar -> after exists and foo -> baz -> other exists -«««««« TODO tests compilation only, add unit test +««« TODO tests compilation only, add unit test func MultipleOrAnd_NoAliases_Exists: inputs: foo Foo (1..1) output: result boolean (1..1) set result: foo -> bar -> before exists or ( foo -> bar -> after exists and foo -> baz -> other exists ) -«««««« TODO tests compilation only, add unit test +««« TODO tests compilation only, add unit test func MultipleOrAnd_NoAliases_Exists2: inputs: foo Foo (1..1) output: result boolean (1..1) set result: (foo -> bar -> before exists and foo -> bar -> after exists) or foo -> baz -> other exists or foo -> baz -> bazValue exists -«««««« TODO tests compilation only, add unit test +««« TODO tests compilation only, add unit test func MultipleOrAnd_NoAliases_Exists3: inputs: foo Foo (1..1) output: result boolean (1..1) set result: (foo -> bar -> before exists or foo -> bar -> after exists) or (foo -> baz -> other exists and foo -> baz -> bazValue exists) -«««««« TODO tests compilation only, add unit test +««« TODO tests compilation only, add unit test func MultipleExistsWithOrAnd: inputs: foo Foo (1..1) output: result boolean (1..1) diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/types/RosettaTypeProviderTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/types/RosettaTypeProviderTest.xtend index e1694dc26..505931043 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/types/RosettaTypeProviderTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/types/RosettaTypeProviderTest.xtend @@ -382,7 +382,7 @@ class RosettaTypeProviderTest { A -> V1 '''.parseRosettaWithNoIssues val A = (context.elements.get(0) as RosettaEnumeration).buildREnumType; - 'A -> V1'.assertIsValidWithType(A.withEmptyMeta, false, #[context]) + 'A -> V1'.assertIsValidWithType(A.withNoMeta, false, #[context]) } @Test @@ -561,8 +561,8 @@ class RosettaTypeProviderTest { add result: if True then s2 else s3 '''.parseRosettaWithNoIssues model.elements.last as Function => [ - val max4String = inputs.get(1).typeCall.typeCallToRType.withEmptyMeta - val maxNString = inputs.get(2).typeCall.typeCallToRType.withEmptyMeta + val max4String = inputs.get(1).typeCall.typeCallToRType.withNoMeta + val maxNString = inputs.get(2).typeCall.typeCallToRType.withNoMeta operations => [ get(0).expression.assertIsValidWithType(maxNString, false) diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/types/SubtypeRelationTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/types/SubtypeRelationTest.xtend index fdc5b2bbf..579e9fb6c 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/types/SubtypeRelationTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/types/SubtypeRelationTest.xtend @@ -16,7 +16,7 @@ import com.regnosys.rosetta.rosetta.RosettaEnumeration import com.regnosys.rosetta.tests.util.ExpressionParser import com.regnosys.rosetta.types.builtin.RBuiltinTypeService import com.regnosys.rosetta.types.builtin.RStringType -import static extension com.regnosys.rosetta.types.RMetaAnnotatedType.withEmptyMeta +import static extension com.regnosys.rosetta.types.RMetaAnnotatedType.withNoMeta @ExtendWith(InjectionExtension) @InjectWith(RosettaInjectorProvider) @@ -62,7 +62,7 @@ class SubtypeRelationTest { val joined = fieldBType.join(fieldCType) - assertEquals(fieldA.withEmptyMeta, joined) + assertEquals(fieldA.withNoMeta, joined) } @Test From bbbbd14adf9ab9aeb9db4de13b8a279318e7ecd7 Mon Sep 17 00:00:00 2001 From: Simon Cockx Date: Tue, 10 Dec 2024 09:23:01 +0000 Subject: [PATCH 05/10] Fixed tests --- .../tools/modelimport/XsdTypeImport.java | 130 ++++++++++++------ .../multi/expected/confirmation-type.rosetta | 20 +-- 2 files changed, 95 insertions(+), 55 deletions(-) diff --git a/rosetta-tools/src/main/java/com/regnosys/rosetta/tools/modelimport/XsdTypeImport.java b/rosetta-tools/src/main/java/com/regnosys/rosetta/tools/modelimport/XsdTypeImport.java index 936b4abdf..6e7613641 100644 --- a/rosetta-tools/src/main/java/com/regnosys/rosetta/tools/modelimport/XsdTypeImport.java +++ b/rosetta-tools/src/main/java/com/regnosys/rosetta/tools/modelimport/XsdTypeImport.java @@ -28,7 +28,9 @@ import org.xmlet.xsdparser.xsdelements.enums.UsageEnum; import org.xmlet.xsdparser.xsdelements.visitors.AttributesVisitor; +import com.google.common.collect.Iterables; import com.google.common.collect.Streams; +import com.regnosys.rosetta.RosettaEcoreUtil; import com.regnosys.rosetta.rosetta.RosettaCardinality; import com.regnosys.rosetta.rosetta.RosettaFactory; import com.regnosys.rosetta.rosetta.expression.ExpressionFactory; @@ -50,11 +52,13 @@ private record ChoiceGroup(List attributes, boolean required) {} public final String SIMPLE_EXTENSION_ATTRIBUTE_NAME = "value"; private final XsdUtil util; + private final RosettaEcoreUtil ecoreUtil; @Inject - public XsdTypeImport(XsdUtil util) { + public XsdTypeImport(XsdUtil util, RosettaEcoreUtil ecoreUtil) { super(XsdNamedElements.class); this.util = util; + this.ecoreUtil = ecoreUtil; } @Override @@ -126,7 +130,7 @@ public List registerType(XsdNamedElements xsdType, RosettaXsdMapping xsdMa if (xsdType instanceof XsdGroup group) { xsdMapping.registerGroup(group, data); - completeData(data, Stream.of(group.getChildElement()), null, xsdMapping, result, targetConfig); + initialCompleteData(data, Stream.of(group.getChildElement()), null, xsdMapping, result, targetConfig); } else { XsdComplexType ct = (XsdComplexType)xsdType; xsdMapping.registerComplexType(ct, data); @@ -139,14 +143,13 @@ public List registerType(XsdNamedElements xsdType, RosettaXsdMapping xsdMa xsdMapping.registerAttribute(ct, valueAttr); } - completeData(data, Streams.concat(getChildElement(ct).stream(), getAttributes(ct)), null, xsdMapping, result, targetConfig); + initialCompleteData(data, Streams.concat(getChildElement(ct).stream(), getAttributes(ct)), null, xsdMapping, result, targetConfig); } // Post process: make sure all names are unique: util.makeNamesUnique(result); result.forEach(d -> { util.makeNamesUnique(d.getAttributes()); - util.makeNamesUnique(d.getConditions()); }); elementType.ifPresent(d -> util.makeNamesUnique(d.getAttributes())); @@ -212,46 +215,24 @@ private Data createData(String name, Stream abstractElements data.setName(name); result.add(data); - completeData(data, abstractElements, initialChoiceGroup, xsdMapping, result, config); + initialCompleteData(data, abstractElements, initialChoiceGroup, xsdMapping, result, config); return data; } - private void completeData(Data data, Stream abstractElements, ChoiceGroup initialChoiceGroup, RosettaXsdMapping xsdMapping, List result, ImportTargetConfig config) { + private void initialCompleteData(Data data, Stream abstractElements, ChoiceGroup initialChoiceGroup, RosettaXsdMapping xsdMapping, List result, ImportTargetConfig config) { // Add attributes List choiceGroups = new ArrayList<>(); if (initialChoiceGroup != null) { choiceGroups.add(initialChoiceGroup); } abstractElements.forEach(elem -> registerXsdElementsRecursively(data, elem, initialChoiceGroup, choiceGroups, xsdMapping, result, config)); - - // Add conditions - choiceGroups.forEach(choiceGroup -> { - if (choiceGroup.attributes.size() > 1) { - Condition choice = SimpleFactory.eINSTANCE.createCondition(); - choice.setName("Choice"); - if (choiceGroup.attributes.size() == data.getAttributes().size() && choiceGroup.required) { - OneOfOperation oneOf = ExpressionFactory.eINSTANCE.createOneOfOperation(); - oneOf.setOperator("one-of"); - choice.setExpression(oneOf); - } else { - ChoiceOperation op = ExpressionFactory.eINSTANCE.createChoiceOperation(); - op.setOperator("choice"); - op.setNecessity(choiceGroup.required ? Necessity.REQUIRED : Necessity.OPTIONAL); - op.getAttributes().addAll(choiceGroup.attributes); - choice.setExpression(op); - } - data.getConditions().add(choice); - } else if (choiceGroup.attributes.size() == 1 && choiceGroup.required) { - Attribute attr = choiceGroup.attributes.get(0); - attr.getCard().setInf(1); - } - }); } @Override public void completeType(XsdNamedElements xsdType, RosettaXsdMapping xsdMapping) { if (xsdType instanceof XsdGroup group) { - completeXsdElementsRecursively(group.getChildElement(), false, xsdMapping); + Data data = xsdMapping.getRosettaTypeFromGroup(group); + completeData(data, Stream.of(group.getChildElement()), null, xsdMapping); } else { XsdComplexType ct = (XsdComplexType)xsdType; Data data = xsdMapping.getRosettaTypeFromComplex(ct); @@ -271,11 +252,10 @@ public void completeType(XsdNamedElements xsdType, RosettaXsdMapping xsdMapping) attr.setTypeCall(xsdMapping.getRosettaTypeCall(base)); }); - Stream.concat(getChildElement(ct).stream(), getAttributes(ct)) - .forEach(content -> completeXsdElementsRecursively(content, false, xsdMapping)); + completeData(data, Stream.concat(getChildElement(ct).stream(), getAttributes(ct)), null, xsdMapping); } } - private void completeXsdElementsRecursively(XsdAbstractElement abstractElement, boolean isChoiceGroup, RosettaXsdMapping xsdMapping) { + private void completeXsdElementsRecursively(Data currentData, XsdAbstractElement abstractElement, ChoiceGroup currentChoiceGroup, List currentChoiceGroups, RosettaXsdMapping xsdMapping) { if (abstractElement instanceof XsdElement elem) { Attribute attr = xsdMapping.getAttribute(elem); if (elem.getTypeAsXsd() != null) { @@ -284,9 +264,15 @@ private void completeXsdElementsRecursively(XsdAbstractElement abstractElement, // TODO attr.setTypeCall(xsdMapping.getRosettaTypeCallFromBuiltin("string")); } + if (currentChoiceGroup != null) { + currentChoiceGroup.attributes.add(attr); + } } else if (abstractElement instanceof XsdGroup group) { Attribute attr = xsdMapping.getAttribute(group); attr.setTypeCall(xsdMapping.getRosettaTypeCall(group)); + if (currentChoiceGroup != null) { + currentChoiceGroup.attributes.add(attr); + } } else if (abstractElement instanceof XsdAttribute xsdAttr) { Attribute attr = xsdMapping.getAttribute(xsdAttr); if (xsdAttr.getXsdSimpleType() != null) { @@ -297,34 +283,88 @@ private void completeXsdElementsRecursively(XsdAbstractElement abstractElement, // TODO attr.setTypeCall(xsdMapping.getRosettaTypeCallFromBuiltin("string")); } + if (currentChoiceGroup != null) { + currentChoiceGroup.attributes.add(attr); + } } else if (abstractElement instanceof XsdSequence seq) { - if (isChoiceGroup || isMulti(seq.getMaxOccurs()) || seq.getMinOccurs() == 0) { - seq.getXsdElements().forEach(child -> completeXsdElementsRecursively(child, false, xsdMapping)); + if (currentChoiceGroup != null || isMulti(seq.getMaxOccurs()) || seq.getMinOccurs() == 0) { + Data newData = xsdMapping.getRosettaTypeFromComplex(seq); + completeData(newData, seq.getXsdElements(), null, xsdMapping); + Attribute attr = xsdMapping.getAttribute(seq); attr.setTypeCall(xsdMapping.getRosettaTypeCall(seq)); + if (currentChoiceGroup != null) { + currentChoiceGroup.attributes.add(attr); + } } else { - seq.getXsdElements().forEach(child -> completeXsdElementsRecursively(child, isChoiceGroup, xsdMapping)); + seq.getXsdElements().forEach(elem -> completeXsdElementsRecursively(currentData, elem, null, currentChoiceGroups, xsdMapping)); } } else if (abstractElement instanceof XsdAll all) { - if (isChoiceGroup || all.getMinOccurs() == 0) { - all.getXsdElements().forEach(child -> completeXsdElementsRecursively(child, false, xsdMapping)); + if (currentChoiceGroup != null || all.getMinOccurs() == 0) { + Data newData = xsdMapping.getRosettaTypeFromComplex(all); + completeData(newData, all.getXsdElements(), null, xsdMapping); + Attribute attr = xsdMapping.getAttribute(all); attr.setTypeCall(xsdMapping.getRosettaTypeCall(all)); + if (currentChoiceGroup != null) { + currentChoiceGroup.attributes.add(attr); + } } else { - all.getXsdElements().forEach(child -> completeXsdElementsRecursively(child, isChoiceGroup, xsdMapping)); + all.getXsdElements().forEach(elem -> completeXsdElementsRecursively(currentData, elem, null, currentChoiceGroups, xsdMapping)); } } else if (abstractElement instanceof XsdChoice choice) { - if (isChoiceGroup || isMulti(choice.getMaxOccurs())) { - choice.getXsdElements().forEach(child -> completeXsdElementsRecursively(child, true, xsdMapping)); - Attribute attr = xsdMapping.getAttribute(choice); + if (currentChoiceGroup != null || isMulti(choice.getMaxOccurs())) { + boolean required = choice.getMinOccurs() > 0 && choice.getXsdElements().allMatch(elem -> Integer.parseInt(elem.getAttributesMap().getOrDefault(MIN_OCCURS_TAG, "1")) > 0); + ChoiceGroup initialChoiceGroup = new ChoiceGroup(new ArrayList<>(), required); + Data newData = xsdMapping.getRosettaTypeFromComplex(choice); + completeData(newData, choice.getXsdElements(), initialChoiceGroup, xsdMapping); + + Attribute attr = xsdMapping.getAttribute(choice); attr.setTypeCall(xsdMapping.getRosettaTypeCall(choice)); + if (currentChoiceGroup != null) { + currentChoiceGroup.attributes.add(attr); + } } else { - choice.getXsdElements().forEach(child -> completeXsdElementsRecursively(child, true, xsdMapping)); + ChoiceGroup newChoiceGroup = new ChoiceGroup(new ArrayList<>(), choice.getMinOccurs() > 0); + currentChoiceGroups.add(newChoiceGroup); + choice.getXsdElements().forEach(child -> completeXsdElementsRecursively(currentData, child, newChoiceGroup, currentChoiceGroups, xsdMapping)); } - } else { - return; } } + private void completeData(Data data, Stream abstractElements, ChoiceGroup initialChoiceGroup, RosettaXsdMapping xsdMapping) { + List choiceGroups = new ArrayList<>(); + if (initialChoiceGroup != null) { + choiceGroups.add(initialChoiceGroup); + } + + // Complete attributes + abstractElements.forEach(elem -> completeXsdElementsRecursively(data, elem, initialChoiceGroup, choiceGroups, xsdMapping)); + + // Add conditions + choiceGroups.forEach(choiceGroup -> { + if (choiceGroup.attributes.size() > 1) { + Condition choice = SimpleFactory.eINSTANCE.createCondition(); + choice.setName("Choice"); + if (choiceGroup.attributes.size() == Iterables.size(ecoreUtil.getAllAttributes(data)) && choiceGroup.required) { + OneOfOperation oneOf = ExpressionFactory.eINSTANCE.createOneOfOperation(); + oneOf.setOperator("one-of"); + choice.setExpression(oneOf); + } else { + ChoiceOperation op = ExpressionFactory.eINSTANCE.createChoiceOperation(); + op.setOperator("choice"); + op.setNecessity(choiceGroup.required ? Necessity.REQUIRED : Necessity.OPTIONAL); + op.getAttributes().addAll(choiceGroup.attributes); + choice.setExpression(op); + } + data.getConditions().add(choice); + } else if (choiceGroup.attributes.size() == 1 && choiceGroup.required) { + Attribute attr = choiceGroup.attributes.get(0); + attr.getCard().setInf(1); + } + }); + + util.makeNamesUnique(data.getConditions()); + } public Map getXMLConfiguration(XsdNamedElements xsdType, RosettaXsdMapping xsdMapping, String schemaTargetNamespace) { Data data; diff --git a/rosetta-tools/src/test/resources/model-import/multi/expected/confirmation-type.rosetta b/rosetta-tools/src/test/resources/model-import/multi/expected/confirmation-type.rosetta index 0dfaaaca7..9c7903aa2 100644 --- a/rosetta-tools/src/test/resources/model-import/multi/expected/confirmation-type.rosetta +++ b/rosetta-tools/src/test/resources/model-import/multi/expected/confirmation-type.rosetta @@ -486,10 +486,10 @@ type CommodityBasketOption extends Option: <"Defines a commodity basket option p premium CommodityPremium (1..*) <"The option premium payable by the buyer to the seller."> commodityContentModel CommodityContentModel (0..1) - condition Choice: + condition Choice0: required choice strikePriceUnderlyingReference, strikePriceBasketReference - condition Choice: + condition Choice1: required choice commodityStrikePriceModel, commodityFloatingStrikePriceModel type CommodityDigitalOption extends Option: <"Defines a commodity digital option product. Defines the digital commodity option product type. Digital options exercise when a barrier is breached and are financially settled. The \'commodityDigitalOption\' type is an extension of the \'commodityOption\' product."> @@ -589,10 +589,10 @@ type CommodityVarianceLeg extends CommodityPerformanceSwapLeg: <"Specifies the v volatilityStrikePrice number (0..1) <"Specifies the volatility strike price when this strike is expressed in standard deviation units. Payments on the variance leg are equal to the national amount multiplied by the realized volatility squared minus the volatility strike price squared. Notional amount * (realized volatility^2 - volatility strike^2). Squaring the volatility strike price converts the volatility strike price into a variance strike price. Squaring the realized volatility converts realized volatility to realized variance."> varianceCalculation CommodityVarianceCalculation (1..1) <"Specifies, in relation to each Payment Date, the variance percentage which, when multiplied times the notional amount is the amount to which the Payment Date relates. For purposes of this representation the realized variance is: (annualizationFactor / N) * signma from i = 1 to N (ln (S sub (i+1)) / (S sub i)), where: ln is the natural logarithm, N is the number of pricing dates, S sub i is the relevant price on the observation date i. If nAdjustment is \'true\' then the denominator of the annualization factor is (N - 1) rather than N. If realized volatility is the performance metric in a variance swap rather than realized variance then the square root of the formula above will appear in the confirmation."> - condition Choice: + condition Choice0: required choice notionalAmount, notionalAmountReference - condition Choice: + condition Choice1: required choice varianceStrikePrice, volatilityStrikePrice type ElectricityPhysicalLeg extends PhysicalSwapLeg: <"Physically settled electricity leg. Physically settled leg of a physically settled electricity transaction."> @@ -655,13 +655,13 @@ type Repo extends Product: <"Global element representing a Repo. A Repo, modeled bondEquityModel BondEquityModel (0..*) triParty TriParty (0..1) - condition Choice: + condition Choice0: required choice fixedRateSchedule, floatingRateCalculation - condition Choice: + condition Choice1: required choice duration, repoSequence - condition Choice: + condition Choice2: required choice bondEquityModel, triParty type RequestClearingEligibility extends CorrectableRequestMessage: @@ -5097,7 +5097,7 @@ type GasPhysicalQuantity extends CommodityPhysicalQuantityBase: <"The quantity o gasPhysicalQuantitySequence GasPhysicalQuantitySequence (0..1) condition Choice: - one-of + required choice commodityFixedPhysicalQuantityModel, gasPhysicalQuantitySequence type GasPhysicalQuantitySequence: @@ -8562,7 +8562,7 @@ type UnderlyerInterestLeg extends DirectionalLeg: <"A type describing interest p spreadSchedule SpreadSchedule (0..*) <"The ISDA Spread or a Spread schedule expressed as explicit spreads and dates. In the case of a schedule, the step dates may be subject to adjustment in accordance with any adjustments specified in calculationPeriodDatesAdjustments. The spread is a per annum rate, expressed as a decimal. For purposes of determining a calculation period amount, if positive the spread will be added to the floating rate and if negative the spread will be subtracted from the floating rate. A positive 10 basis point (0.1%) spread would be represented as 0.001."> condition Choice: - one-of + required choice fixedRate, spreadSchedule type Unit: <"A type used to record information about a unit, subdivision, desk, or other similar business entity."> @@ -10450,7 +10450,7 @@ type TouchRateObservation extends TriggerRateObservation: isExercisable boolean (0..1) <"If the touch or no touch event hasn\'t generated an exercise, then we specify whether the option is exercisable or not."> condition Choice: - one-of + required choice touchRateObservationSequence, isExercisable type TouchRateObservationSequence: From 7f893e2c16d0764e880e0ca153df81493d4b76dd Mon Sep 17 00:00:00 2001 From: Simon Cockx Date: Tue, 10 Dec 2024 11:57:49 +0000 Subject: [PATCH 06/10] Improved messages --- .../validation/RosettaSimpleValidator.xtend | 28 +---- .../AbstractExpressionValidator.java | 32 ++++-- .../expression/ExpressionValidator.java | 102 +++++++++++++----- .../expression/SwitchValidator.java | 2 +- .../java/function/FunctionGeneratorTest.xtend | 2 +- .../validation/RosettaValidatorTest.xtend | 25 +++-- 6 files changed, 122 insertions(+), 69 deletions(-) diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaSimpleValidator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaSimpleValidator.xtend index 749217e00..c3980ed00 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaSimpleValidator.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaSimpleValidator.xtend @@ -443,8 +443,8 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { } @Check - def void checkFunctionNameStartsWithCapital(Function enumeration) { - if (Character.isLowerCase(enumeration.name.charAt(0))) { + def void checkFunctionNameStartsWithCapital(Function func) { + if (Character.isLowerCase(func.name.charAt(0))) { warning("Function name should start with a capital", ROSETTA_NAMED__NAME, INVALID_CASE) } } @@ -1346,30 +1346,6 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { } } - @Check - def checkOutputOperation(Operation o) { - val expr = o?.expression - if (expr !== null && expr.isOutputListOfLists) { - error('''Assign expression contains a list of lists, use flatten to create a list.''', o, - OPERATION__EXPRESSION) - } - val attr = o.path !== null - ? o.pathAsSegmentList.last.attribute - : o.assignRoot - checkType(attr.RTypeOfSymbol, expr, o, OPERATION__EXPRESSION, INSIGNIFICANT_INDEX) - val isList = cardinality.isSymbolMulti(attr) - if (o.add && !isList) { - error('''Add must be used with a list.''', o, OPERATION__ASSIGN_ROOT) - } - if (!o.add && isList) { - info('''Set used with a list. Any existing list items will be overwritten. Use Add to append items to existing list.''', - o, OPERATION__ASSIGN_ROOT) - } - if (!isList && cardinality.isMulti(o.expression)) { - error('''Cardinality mismatch - cannot assign list to a single value.''', o, OPERATION__ASSIGN_ROOT) - } - } - @Check def checkAlias(ShortcutDeclaration o) { val expr = o?.expression diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/AbstractExpressionValidator.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/AbstractExpressionValidator.java index 4ff5b4644..67c30e3e5 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/AbstractExpressionValidator.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/AbstractExpressionValidator.java @@ -90,22 +90,38 @@ protected boolean comparableTypeCheck(RosettaBinaryOperation sourceObject) { return true; } - protected boolean isMultiCheck(RosettaExpression expr, EObject sourceObject, EStructuralFeature feature) { - return isMultiCheck(expr, sourceObject, feature, INSIGNIFICANT_INDEX); + protected boolean isMultiCheck(RosettaExpression expr, EObject sourceObject, EStructuralFeature feature, RosettaOperation op) { + String suggestion = "The `" + op.getOperator() + "` operator requires a multi cardinality input"; + return isMultiCheck(expr, sourceObject, feature, INSIGNIFICANT_INDEX, suggestion); } - protected boolean isMultiCheck(RosettaExpression expr, EObject sourceObject, EStructuralFeature feature, int featureIndex) { + protected boolean isMultiCheck(RosettaExpression expr, EObject sourceObject, EStructuralFeature feature, String suggestion) { + return isMultiCheck(expr, sourceObject, feature, INSIGNIFICANT_INDEX, suggestion); + } + protected boolean isMultiCheck(RosettaExpression expr, EObject sourceObject, EStructuralFeature feature, int featureIndex, String suggestion) { if (!cardinalityProvider.isMulti(expr)) { - error("Expecting multi cardinality", sourceObject, feature, featureIndex); + String msg = "Expecting multi cardinality"; + if (suggestion != null) { + msg += ". " + suggestion; + } + error(msg, sourceObject, feature, featureIndex); return false; } return true; } - protected boolean isSingleCheck(RosettaExpression expr, EObject sourceObject, EStructuralFeature feature) { - return isSingleCheck(expr, sourceObject, feature, INSIGNIFICANT_INDEX); + protected boolean isSingleCheck(RosettaExpression expr, EObject sourceObject, EStructuralFeature feature, RosettaOperation op) { + String suggestion = "The `" + op.getOperator() + "` operator requires a single cardinality input"; + return isSingleCheck(expr, sourceObject, feature, INSIGNIFICANT_INDEX, suggestion); } - protected boolean isSingleCheck(RosettaExpression expr, EObject sourceObject, EStructuralFeature feature, int featureIndex) { + protected boolean isSingleCheck(RosettaExpression expr, EObject sourceObject, EStructuralFeature feature, String suggestion) { + return isSingleCheck(expr, sourceObject, feature, INSIGNIFICANT_INDEX, suggestion); + } + protected boolean isSingleCheck(RosettaExpression expr, EObject sourceObject, EStructuralFeature feature, int featureIndex, String suggestion) { if (cardinalityProvider.isMulti(expr)) { - error("Expecting single cardinality", sourceObject, feature, featureIndex); + String msg = "Expecting single cardinality"; + if (suggestion != null) { + msg += ". " + suggestion; + } + error(msg, sourceObject, feature, featureIndex); return false; } return true; diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/ExpressionValidator.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/ExpressionValidator.java index fe01ca8ba..60dbb98bf 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/ExpressionValidator.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/ExpressionValidator.java @@ -27,6 +27,7 @@ import com.regnosys.rosetta.rosetta.expression.JoinOperation; import com.regnosys.rosetta.rosetta.expression.ListLiteral; import com.regnosys.rosetta.rosetta.expression.LogicalOperation; +import com.regnosys.rosetta.rosetta.expression.ModifiableBinaryOperation; import com.regnosys.rosetta.rosetta.expression.OneOfOperation; import com.regnosys.rosetta.rosetta.expression.RosettaConditionalExpression; import com.regnosys.rosetta.rosetta.expression.RosettaContainsExpression; @@ -37,8 +38,12 @@ import com.regnosys.rosetta.rosetta.expression.RosettaOnlyElement; import com.regnosys.rosetta.rosetta.expression.RosettaOnlyExistsExpression; import com.regnosys.rosetta.rosetta.expression.RosettaSymbolReference; +import com.regnosys.rosetta.rosetta.simple.AssignPathRoot; import com.regnosys.rosetta.rosetta.simple.Attribute; +import com.regnosys.rosetta.rosetta.simple.Condition; import com.regnosys.rosetta.rosetta.simple.Function; +import com.regnosys.rosetta.rosetta.simple.Operation; +import com.regnosys.rosetta.rosetta.simple.Segment; import com.regnosys.rosetta.types.RChoiceType; import com.regnosys.rosetta.types.RDataType; import com.regnosys.rosetta.types.RMetaAnnotatedType; @@ -46,9 +51,11 @@ import com.regnosys.rosetta.utils.ExpressionHelper; import com.regnosys.rosetta.utils.ImplicitVariableUtil; +import static com.regnosys.rosetta.rosetta.simple.SimplePackage.Literals.*; import static com.regnosys.rosetta.rosetta.expression.ExpressionPackage.Literals.*; import java.util.HashSet; +import java.util.List; import java.util.Set; // TODO: move over expression validations from RosettaSimpleValidator @@ -63,6 +70,36 @@ public class ExpressionValidator extends AbstractExpressionValidator { @Inject private RosettaFunctionExtensions functionExtensions; + @Check + public void checkCondition(Condition c) { + isSingleCheck(c.getExpression(), c, CONDITION__EXPRESSION, "A condition should be single cardinality"); + subtypeCheck(builtins.BOOLEAN_WITH_NO_META, c.getExpression(), c, CONDITION__EXPRESSION); + } + + @Check + public void checkFunctionOperation(Operation op) { + RosettaExpression expr = op.getExpression(); + if (expr != null && cardinalityProvider.isOutputListOfLists(expr)) { + error("Assign expression contains a list of lists, use flatten to create a list", op, OPERATION__EXPRESSION); + } + List segments = op.pathAsSegmentList(); + AssignPathRoot attr = op.getPath() != null + ? segments.get(segments.size() - 1).getAttribute() + : op.getAssignRoot(); + subtypeCheck(typeProvider.getRTypeOfSymbol(attr), expr, op, OPERATION__EXPRESSION); + boolean isList = cardinalityProvider.isSymbolMulti(attr); + if (op.isAdd() && !isList) { + error("`add` must be used with a list", op, OPERATION__ASSIGN_ROOT); + } + if (!op.isAdd() && isList) { + info("`set` used with a list. Any existing list items will be overwritten. Use `add` to append items to existing list", + op, OPERATION__ASSIGN_ROOT); + } + if (!isList) { + isSingleCheck(expr, op, OPERATION__EXPRESSION, "Cannot assign a list to a single value"); + } + } + @Check public void checkArithmeticOperation(ArithmeticOperation op) { RosettaExpression left = op.getLeft(); @@ -70,8 +107,8 @@ public void checkArithmeticOperation(ArithmeticOperation op) { String operator = op.getOperator(); RMetaAnnotatedType leftType = typeProvider.getRMetaAnnotatedType(left); RMetaAnnotatedType rightType = typeProvider.getRMetaAnnotatedType(right); - isSingleCheck(left, op, ROSETTA_BINARY_OPERATION__LEFT); - isSingleCheck(right, op, ROSETTA_BINARY_OPERATION__RIGHT); + isSingleCheck(left, op, ROSETTA_BINARY_OPERATION__LEFT, op); + isSingleCheck(right, op, ROSETTA_BINARY_OPERATION__RIGHT, op); if (operator.equals("+")) { if (typeSystem.isSubtypeOf(leftType, builtins.NOTHING_WITH_NO_META)) { // Do not check right type @@ -109,19 +146,32 @@ public void checkArithmeticOperation(ArithmeticOperation op) { } } - @Check - public void checkEqualityOperation(EqualityOperation op) { + private void checkModifiedBinaryOperation(ModifiableBinaryOperation op) { RosettaExpression left = op.getLeft(); RosettaExpression right = op.getRight(); + String removeModifierSuggestion = "Did you mean to remove the `" + op.getCardMod().getLiteral() + "` modifier on the `" + op.getOperator() + "` operator?"; + String suggestion; + if (cardinalityProvider.isMulti(right)) { + // Multi and single are flipped + suggestion = "Did you mean to flip around the operands of the `" + op.getOperator() + "` operator?"; + } else { + // Both are single + suggestion = removeModifierSuggestion; + } + isMultiCheck(left, op, ROSETTA_BINARY_OPERATION__LEFT, suggestion); + isSingleCheck(right, op, ROSETTA_BINARY_OPERATION__RIGHT, removeModifierSuggestion); + } + + @Check + public void checkEqualityOperation(EqualityOperation op) { comparableTypeCheck(op); if (op.getCardMod() != CardinalityModifier.NONE) { - isMultiCheck(left, op, ROSETTA_BINARY_OPERATION__LEFT); - isSingleCheck(right, op, ROSETTA_BINARY_OPERATION__RIGHT); + checkModifiedBinaryOperation(op); } else { boolean leftIsMulti = cardinalityProvider.isMulti(op.getLeft()); boolean rightIsMulti = cardinalityProvider.isMulti(op.getRight()); if (leftIsMulti != rightIsMulti) { - error("Operator `" + op.getOperator() + "` should specify 'all' or 'any' when comparing a list to a single value", op, null); + error("Operator `" + op.getOperator() + "` should specify `all` or `any` when comparing a list to a single value", op, null); } } } @@ -132,8 +182,8 @@ public void checkLogicalOperation(LogicalOperation op) { RosettaExpression right = op.getRight(); RMetaAnnotatedType leftType = typeProvider.getRMetaAnnotatedType(left); RMetaAnnotatedType rightType = typeProvider.getRMetaAnnotatedType(right); - isSingleCheck(left, op, ROSETTA_BINARY_OPERATION__LEFT); - isSingleCheck(right, op, ROSETTA_BINARY_OPERATION__RIGHT); + isSingleCheck(left, op, ROSETTA_BINARY_OPERATION__LEFT, op); + isSingleCheck(right, op, ROSETTA_BINARY_OPERATION__RIGHT, op); subtypeCheck(builtins.BOOLEAN_WITH_NO_META, leftType, op, ROSETTA_BINARY_OPERATION__LEFT); subtypeCheck(builtins.BOOLEAN_WITH_NO_META, rightType, op, ROSETTA_BINARY_OPERATION__RIGHT); } @@ -145,11 +195,10 @@ public void checkComparisonOperation(ComparisonOperation op) { RMetaAnnotatedType leftType = typeProvider.getRMetaAnnotatedType(left); RMetaAnnotatedType rightType = typeProvider.getRMetaAnnotatedType(right); if (op.getCardMod() != CardinalityModifier.NONE) { - isMultiCheck(left, op, ROSETTA_BINARY_OPERATION__LEFT); - isSingleCheck(right, op, ROSETTA_BINARY_OPERATION__RIGHT); + checkModifiedBinaryOperation(op); } else { - isSingleCheck(left, op, ROSETTA_BINARY_OPERATION__LEFT); - isSingleCheck(right, op, ROSETTA_BINARY_OPERATION__RIGHT); + isSingleCheck(left, op, ROSETTA_BINARY_OPERATION__LEFT, "Did you mean to use `all` or `any` in front of the `" + op.getOperator() + "` operator?"); + isSingleCheck(right, op, ROSETTA_BINARY_OPERATION__RIGHT, "Did you mean to use `all` or `any` in front of the `" + op.getOperator() + "` operator?"); } if (typeSystem.isSubtypeOf(leftType, builtins.NOTHING_WITH_NO_META)) { // Do not check right type @@ -171,19 +220,20 @@ public void checkComparisonOperation(ComparisonOperation op) { @Check public void checkContainsExpression(RosettaContainsExpression expr) { - isMultiCheck(expr.getLeft(), expr, ROSETTA_BINARY_OPERATION__LEFT); + isMultiCheck(expr.getLeft(), expr, ROSETTA_BINARY_OPERATION__LEFT, "Did you mean to use the `=` operator instead?"); comparableTypeCheck(expr); } @Check public void checkDisjointExpression(RosettaDisjointExpression expr) { - isMultiCheck(expr.getLeft(), expr, ROSETTA_BINARY_OPERATION__LEFT); + isMultiCheck(expr.getLeft(), expr, ROSETTA_BINARY_OPERATION__LEFT, expr); + isMultiCheck(expr.getRight(), expr, ROSETTA_BINARY_OPERATION__LEFT, expr); comparableTypeCheck(expr); } @Check public void checkConditionalExpression(RosettaConditionalExpression expr) { - isSingleCheck(expr.getIf(), expr, ROSETTA_CONDITIONAL_EXPRESSION__IF); + isSingleCheck(expr.getIf(), expr, ROSETTA_CONDITIONAL_EXPRESSION__IF, "The condition of an if-then-else expression should be single cardinality"); subtypeCheck(builtins.BOOLEAN_WITH_NO_META, expr.getIf(), expr, ROSETTA_CONDITIONAL_EXPRESSION__IF); commonTypeCheck(expr.getIfthen(), expr.getElsethen(), expr, ROSETTA_CONDITIONAL_EXPRESSION__ELSETHEN); } @@ -216,7 +266,7 @@ public void checkSymbolReference(RosettaSymbolReference expr) { for (int i=0; i= 1) { RMetaAnnotatedType paramType = RMetaAnnotatedType.withNoMeta(typeSystem.typeCallToRType(f.getInput())); RosettaExpression arg = expr.getArgs().get(0); - isSingleCheck(arg, expr, ROSETTA_SYMBOL_REFERENCE__RAW_ARGS, 0); + isSingleCheck(arg, expr, ROSETTA_SYMBOL_REFERENCE__RAW_ARGS, 0, null); subtypeCheck(paramType, arg, expr, ROSETTA_SYMBOL_REFERENCE__RAW_ARGS, 0); } } @@ -266,7 +316,7 @@ public void checkSymbolReference(RosettaSymbolReference expr) { @Check public void checkExistsExpression(RosettaExistsExpression expr) { if (expr.getModifier() == ExistsModifier.MULTIPLE || expr.getModifier() == ExistsModifier.SINGLE) { - isMultiCheck(expr.getArgument(), expr, ROSETTA_UNARY_OPERATION__ARGUMENT); + isMultiCheck(expr.getArgument(), expr, ROSETTA_UNARY_OPERATION__ARGUMENT, expr); } } @@ -315,11 +365,11 @@ public void checkOnlyExistsExpression(RosettaOnlyExistsExpression expr) { RMetaAnnotatedType parentType = null; if (parent != null) { - isSingleCheck(parent, parent, null); + isSingleCheck(parent, parent, null, "The `only exists` operation requires a single cardinality input"); parentType = typeProvider.getRMetaAnnotatedType(parent); } else { if (cardinalityProvider.isImplicitVariableMulti(expr)) { - error("Expecting single cardinality parent", expr, ROSETTA_ONLY_EXISTS_EXPRESSION__ARGS, 0); + error("Expecting single cardinality input", expr, ROSETTA_ONLY_EXISTS_EXPRESSION__ARGS, 0); } parentType = typeProvider.typeOfImplicitVariable(expr); } @@ -332,7 +382,7 @@ public void checkOnlyExistsExpression(RosettaOnlyExistsExpression expr) { @Check public void checkOneOfOperation(OneOfOperation op) { - isSingleCheck(op.getArgument(), op, ROSETTA_UNARY_OPERATION__ARGUMENT); + isSingleCheck(op.getArgument(), op, ROSETTA_UNARY_OPERATION__ARGUMENT, op); RMetaAnnotatedType argType = typeProvider.getRMetaAnnotatedType(op.getArgument()); if (!mayBeEmpty(argType.getRType())) { unsupportedTypeError(argType, op.getOperator(), op, ROSETTA_UNARY_OPERATION__ARGUMENT, "All attributes of input type should be optional"); @@ -341,7 +391,7 @@ public void checkOneOfOperation(OneOfOperation op) { @Check public void checkChoiceOperation(ChoiceOperation op) { - isSingleCheck(op.getArgument(), op, ROSETTA_UNARY_OPERATION__ARGUMENT); + isSingleCheck(op.getArgument(), op, ROSETTA_UNARY_OPERATION__ARGUMENT, op); RMetaAnnotatedType argType = typeProvider.getRMetaAnnotatedType(op.getArgument()); if (!(argType.getRType() instanceof RDataType)) { unsupportedTypeError(argType, op.getOperator(), op, ROSETTA_UNARY_OPERATION__ARGUMENT, "Input should be a complex type"); @@ -361,8 +411,8 @@ public void checkChoiceOperation(ChoiceOperation op) { @Check public void checkJoinOperation(JoinOperation op) { - isMultiCheck(op.getLeft(), op, ROSETTA_BINARY_OPERATION__LEFT); - isSingleCheck(op.getRight(), op, ROSETTA_BINARY_OPERATION__RIGHT); + isMultiCheck(op.getLeft(), op, ROSETTA_BINARY_OPERATION__LEFT, op); + isSingleCheck(op.getRight(), op, ROSETTA_BINARY_OPERATION__RIGHT, op); subtypeCheck(builtins.UNCONSTRAINED_STRING_WITH_NO_META, op.getLeft(), op, ROSETTA_BINARY_OPERATION__LEFT); subtypeCheck(builtins.UNCONSTRAINED_STRING_WITH_NO_META, op.getRight(), op, ROSETTA_BINARY_OPERATION__RIGHT); } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/SwitchValidator.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/SwitchValidator.java index 557e10506..f31e6c731 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/SwitchValidator.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/SwitchValidator.java @@ -34,7 +34,7 @@ public class SwitchValidator extends ExpressionValidator { @Check public void checkSwitch(SwitchOperation op) { - isSingleCheck(op.getArgument(), op, ROSETTA_UNARY_OPERATION__ARGUMENT); + isSingleCheck(op.getArgument(), op, ROSETTA_UNARY_OPERATION__ARGUMENT, op); RMetaAnnotatedType argumentType = typeProvider.getRMetaAnnotatedType(op.getArgument()); RType rType = typeSystem.stripFromTypeAliases(argumentType.getRType()); if (rType instanceof REnumType) { diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/function/FunctionGeneratorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/function/FunctionGeneratorTest.xtend index 150473c1f..ca49bdc8a 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/function/FunctionGeneratorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/function/FunctionGeneratorTest.xtend @@ -3985,7 +3985,7 @@ class FunctionGeneratorTest { '''.parseRosetta model.assertError(EQUALITY_OPERATION, null, - "Operator `=` should specify 'all' or 'any' when comparing a list to a single value") + "Operator `=` should specify `all` or `any` when comparing a list to a single value") } @Test diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaValidatorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaValidatorTest.xtend index 02dbf113a..cf7553cf5 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaValidatorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaValidatorTest.xtend @@ -31,6 +31,17 @@ class RosettaValidatorTest implements RosettaIssueCodes { @Inject extension ModelHelper @Inject extension ExpressionParser + @Test + def void testConditionShouldBeSingleCardinality() { + val model = ''' + type Foo: + condition C: + [True, False] + '''.parseRosetta + + model.assertError(CONDITION, null, "Expecting single cardinality. A condition should be single cardinality") + } + @Test def void testOnlyExistsOnMetaIsNotValidOnSymbolReferences() { val model = ''' @@ -728,7 +739,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { '''.parseRosetta model.assertError(OPERATION, null, - "Cardinality mismatch - cannot assign list to a single value.") + "Expecting single cardinality. Cannot assign a list to a single value") } @Test @@ -1691,7 +1702,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { set out: "not a Foo" '''.parseRosetta - model.assertError(OPERATION, TYPE_ERROR, "Expected type 'Foo' but was 'string'") + model.assertError(OPERATION, null, "Expected type `Foo`, but got `string` instead") } @@ -1708,7 +1719,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { set out -> id: "not a boolean" '''.parseRosetta - model.assertError(OPERATION, TYPE_ERROR, "Expected type 'boolean' but was 'string'") + model.assertError(OPERATION, null, "Expected type `boolean`, but got `string` instead") } @Test @@ -1729,7 +1740,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { set result -> attr: in1 as-key '''.parseRosetta - model.assertError(OPERATION, TYPE_ERROR, "Expected type 'WithKey' but was 'TypeToUse'") + model.assertError(OPERATION, null, "Expected type `WithKey`, but got `TypeToUse` instead") } @Test @@ -1761,7 +1772,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { output: out string (0..1) set out: in0->other '''.parseRosetta - model.assertError(OPERATION, TYPE_ERROR, "Expected type 'string' but was 'int'") + model.assertError(OPERATION, null, "Expected type `string`, but got `int` instead") } @Test @@ -3018,7 +3029,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { type Foo: xs string (0..*) '''.parseRosetta - model.assertError(OPERATION, null, "Assign expression contains a list of lists, use flatten to create a list.") + model.assertError(OPERATION, null, "Assign expression contains a list of lists, use flatten to create a list") } @Test @@ -3037,7 +3048,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { type Foo: xs string (0..*) '''.parseRosetta - model.assertError(OPERATION, null, "Assign expression contains a list of lists, use flatten to create a list.") + model.assertError(OPERATION, null, "Assign expression contains a list of lists, use flatten to create a list") } @Test From 7ee5111e8832ac75275c3b838b794924197cd346 Mon Sep 17 00:00:00 2001 From: Simon Cockx Date: Tue, 10 Dec 2024 14:12:26 +0000 Subject: [PATCH 07/10] Improved messages --- .../ide/server/ChangeDetectionTest.xtend | 10 ++-- .../AbstractExpressionValidator.java | 48 ++++++++++++------- .../expression/ExpressionValidator.java | 47 +++++++++--------- 3 files changed, 62 insertions(+), 43 deletions(-) diff --git a/rosetta-ide/src/test/java/com/regnosys/rosetta/ide/server/ChangeDetectionTest.xtend b/rosetta-ide/src/test/java/com/regnosys/rosetta/ide/server/ChangeDetectionTest.xtend index 5435b194d..2f2d3809f 100644 --- a/rosetta-ide/src/test/java/com/regnosys/rosetta/ide/server/ChangeDetectionTest.xtend +++ b/rosetta-ide/src/test/java/com/regnosys/rosetta/ide/server/ChangeDetectionTest.xtend @@ -32,7 +32,7 @@ class ChangeDetectionTest extends AbstractRosettaLanguageServerValidationTest { // There should be a type error in func `Foo` val issues = diagnostics.get(funcsURI) assertEquals(1, issues.size) - assertEquals("Expected type 'int' but was 'string'", issues.head.message) + assertEquals("Expected type `int`, but got `string` instead. Cannot assign `string` to output `result`", issues.head.message) } @Test @@ -62,7 +62,7 @@ class ChangeDetectionTest extends AbstractRosettaLanguageServerValidationTest { // There should be a cardinality error in func `Foo` val issues = diagnostics.get(funcsURI) assertEquals(1, issues.size) - assertEquals("Cardinality mismatch - cannot assign list to a single value.", issues.head.message) + assertEquals("Expecting single cardinality. Cannot assign a list to a single value", issues.head.message) } @Test @@ -107,7 +107,7 @@ class ChangeDetectionTest extends AbstractRosettaLanguageServerValidationTest { // There should be a type error in func `Foo` val issues = diagnostics.get(funcsURI) assertEquals(1, issues.size) - assertEquals("Expected type 'MyType' but was 'MyType'", issues.head.message) + assertEquals("Expected type `foo.MyType`, but got `bar.MyType` instead. Cannot assign `bar.MyType` to output `result`", issues.head.message) } @Test @@ -134,7 +134,7 @@ class ChangeDetectionTest extends AbstractRosettaLanguageServerValidationTest { // There should be a type error in rule B val issues = diagnostics.get(ruleBURI) assertEquals(1, issues.size) - assertEquals("Expected type `int`, but got `string` instead", issues.head.message) + assertEquals("Expected type `int`, but got `string` instead. Rule `A` cannot be called with type `string`", issues.head.message) } @Test @@ -164,6 +164,6 @@ class ChangeDetectionTest extends AbstractRosettaLanguageServerValidationTest { // There should be a type error in func Foo val issues = diagnostics.get(funcURI) assertEquals(1, issues.size) - assertEquals("Expected type 'int' but was 'string'", issues.head.message) + assertEquals("Expected type `int`, but got `string` instead. Cannot assign `string` to output `result`", issues.head.message) } } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/AbstractExpressionValidator.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/AbstractExpressionValidator.java index 67c30e3e5..56da5d14a 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/AbstractExpressionValidator.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/AbstractExpressionValidator.java @@ -3,6 +3,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Set; +import java.util.function.Function; import java.util.stream.Collectors; import javax.inject.Inject; @@ -34,38 +35,53 @@ public class AbstractExpressionValidator extends AbstractDeclarativeRosettaValid protected String relevantTypeDescription(RMetaAnnotatedType type, RMetaAnnotatedType context) { RType valueType = type.getRType(); RType valueContext = context.getRType(); + String prepend = valueType.getName().equals(valueContext.getName()) ? valueType.getNamespace() + "." : ""; if (valueType.equals(valueContext)) { // Include meta info - return type.toString(); + return prepend + type.toString(); } if (valueType.getName().equals(valueContext.getName())) { // Include type parameters - return valueType.toString(); + return prepend + valueType.toString(); } - return valueType.getName(); + return prepend + valueType.getName(); } - protected String notASubtypeMessage(RMetaAnnotatedType expected, RMetaAnnotatedType actual) { - return new StringBuilder() + protected String notASubtypeMessage(RMetaAnnotatedType expected, RMetaAnnotatedType actual, Function suggestion) { + String actualDescr = relevantTypeDescription(actual, expected); + StringBuilder msg = new StringBuilder() .append("Expected type `") .append(relevantTypeDescription(expected, actual)) .append("`, but got `") - .append(relevantTypeDescription(actual, expected)) - .append("` instead") - .toString(); + .append(actualDescr) + .append("` instead"); + if (suggestion != null) { + msg.append(". "); + msg.append(suggestion.apply(actualDescr)); + } + return msg.toString(); + } + private Function defaultSubtypeSuggestion(RosettaOperation op) { + return actual -> "Cannot use `" + actual + "` with operator `" + op.getOperator() + "`"; + } + protected boolean subtypeCheck(RMetaAnnotatedType expected, RosettaExpression expr, EObject sourceObject, EStructuralFeature feature, RosettaOperation op) { + return subtypeCheck(expected, typeProvider.getRMetaAnnotatedType(expr), sourceObject, feature, INSIGNIFICANT_INDEX, defaultSubtypeSuggestion(op)); + } + protected boolean subtypeCheck(RMetaAnnotatedType expected, RosettaExpression expr, EObject sourceObject, EStructuralFeature feature, Function suggestion) { + return subtypeCheck(expected, typeProvider.getRMetaAnnotatedType(expr), sourceObject, feature, INSIGNIFICANT_INDEX, suggestion); } - protected boolean subtypeCheck(RMetaAnnotatedType expected, RosettaExpression expr, EObject sourceObject, EStructuralFeature feature) { - return subtypeCheck(expected, typeProvider.getRMetaAnnotatedType(expr), sourceObject, feature, INSIGNIFICANT_INDEX); + protected boolean subtypeCheck(RMetaAnnotatedType expected, RosettaExpression expr, EObject sourceObject, EStructuralFeature feature, int featureIndex, Function suggestion) { + return subtypeCheck(expected, typeProvider.getRMetaAnnotatedType(expr), sourceObject, feature, featureIndex, suggestion); } - protected boolean subtypeCheck(RMetaAnnotatedType expected, RosettaExpression expr, EObject sourceObject, EStructuralFeature feature, int featureIndex) { - return subtypeCheck(expected, typeProvider.getRMetaAnnotatedType(expr), sourceObject, feature, featureIndex); + protected boolean subtypeCheck(RMetaAnnotatedType expected, RMetaAnnotatedType actual, EObject sourceObject, EStructuralFeature feature, RosettaOperation op) { + return subtypeCheck(expected, actual, sourceObject, feature, INSIGNIFICANT_INDEX, defaultSubtypeSuggestion(op)); } - protected boolean subtypeCheck(RMetaAnnotatedType expected, RMetaAnnotatedType actual, EObject sourceObject, EStructuralFeature feature) { - return subtypeCheck(expected, actual, sourceObject, feature, INSIGNIFICANT_INDEX); + protected boolean subtypeCheck(RMetaAnnotatedType expected, RMetaAnnotatedType actual, EObject sourceObject, EStructuralFeature feature, Function suggestion) { + return subtypeCheck(expected, actual, sourceObject, feature, INSIGNIFICANT_INDEX, suggestion); } - protected boolean subtypeCheck(RMetaAnnotatedType expected, RMetaAnnotatedType actual, EObject sourceObject, EStructuralFeature feature, int featureIndex) { + protected boolean subtypeCheck(RMetaAnnotatedType expected, RMetaAnnotatedType actual, EObject sourceObject, EStructuralFeature feature, int featureIndex, Function suggestion) { if (!typeSystem.isSubtypeOf(actual, expected)) { - error(notASubtypeMessage(expected, actual), sourceObject, feature, featureIndex); + error(notASubtypeMessage(expected, actual, suggestion), sourceObject, feature, featureIndex); return false; } return true; diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/ExpressionValidator.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/ExpressionValidator.java index 60dbb98bf..0faad05ca 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/ExpressionValidator.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/ExpressionValidator.java @@ -15,6 +15,7 @@ import com.regnosys.rosetta.rosetta.RosettaFeature; import com.regnosys.rosetta.rosetta.RosettaMetaType; import com.regnosys.rosetta.rosetta.RosettaNamed; +import com.regnosys.rosetta.rosetta.RosettaParameter; import com.regnosys.rosetta.rosetta.RosettaRule; import com.regnosys.rosetta.rosetta.RosettaSymbol; import com.regnosys.rosetta.rosetta.expression.ArithmeticOperation; @@ -73,7 +74,7 @@ public class ExpressionValidator extends AbstractExpressionValidator { @Check public void checkCondition(Condition c) { isSingleCheck(c.getExpression(), c, CONDITION__EXPRESSION, "A condition should be single cardinality"); - subtypeCheck(builtins.BOOLEAN_WITH_NO_META, c.getExpression(), c, CONDITION__EXPRESSION); + subtypeCheck(builtins.BOOLEAN_WITH_NO_META, c.getExpression(), c, CONDITION__EXPRESSION, actual -> "A condition must be a boolean"); } @Check @@ -86,7 +87,7 @@ public void checkFunctionOperation(Operation op) { AssignPathRoot attr = op.getPath() != null ? segments.get(segments.size() - 1).getAttribute() : op.getAssignRoot(); - subtypeCheck(typeProvider.getRTypeOfSymbol(attr), expr, op, OPERATION__EXPRESSION); + subtypeCheck(typeProvider.getRTypeOfSymbol(attr), expr, op, OPERATION__EXPRESSION, actual -> "Cannot assign `" + actual + "` to output `" + attr.getName() + "`"); boolean isList = cardinalityProvider.isSymbolMulti(attr); if (op.isAdd() && !isList) { error("`add` must be used with a list", op, OPERATION__ASSIGN_ROOT); @@ -113,11 +114,11 @@ public void checkArithmeticOperation(ArithmeticOperation op) { if (typeSystem.isSubtypeOf(leftType, builtins.NOTHING_WITH_NO_META)) { // Do not check right type } else if (typeSystem.isSubtypeOf(leftType, builtins.DATE_WITH_NO_META)) { - subtypeCheck(builtins.TIME_WITH_NO_META, rightType, op, ROSETTA_BINARY_OPERATION__RIGHT); + subtypeCheck(builtins.TIME_WITH_NO_META, rightType, op, ROSETTA_BINARY_OPERATION__RIGHT, actual -> "Cannot add `" + actual + "` to a `date`"); } else if (typeSystem.isSubtypeOf(leftType, builtins.UNCONSTRAINED_STRING_WITH_NO_META)) { - subtypeCheck(builtins.UNCONSTRAINED_STRING_WITH_NO_META, rightType, op, ROSETTA_BINARY_OPERATION__RIGHT); + subtypeCheck(builtins.UNCONSTRAINED_STRING_WITH_NO_META, rightType, op, ROSETTA_BINARY_OPERATION__RIGHT, actual -> "Cannot add `" + actual + "` to a `string`"); } else if (typeSystem.isSubtypeOf(leftType, builtins.UNCONSTRAINED_NUMBER_WITH_NO_META)) { - subtypeCheck(builtins.UNCONSTRAINED_NUMBER_WITH_NO_META, rightType, op, ROSETTA_BINARY_OPERATION__RIGHT); + subtypeCheck(builtins.UNCONSTRAINED_NUMBER_WITH_NO_META, rightType, op, ROSETTA_BINARY_OPERATION__RIGHT, actual -> "Cannot add `" + actual + "` to a `number`"); } else { unsupportedTypeError(leftType, op, ROSETTA_BINARY_OPERATION__LEFT, builtins.UNCONSTRAINED_NUMBER, builtins.UNCONSTRAINED_STRING, builtins.DATE); if (!typeSystem.isSubtypeOf(rightType, builtins.TIME_WITH_NO_META) @@ -130,9 +131,9 @@ public void checkArithmeticOperation(ArithmeticOperation op) { if (typeSystem.isSubtypeOf(leftType, builtins.NOTHING_WITH_NO_META)) { // Do not check right type } else if (typeSystem.isSubtypeOf(leftType, builtins.DATE_WITH_NO_META)) { - subtypeCheck(builtins.DATE_WITH_NO_META, rightType, op, ROSETTA_BINARY_OPERATION__RIGHT); + subtypeCheck(builtins.DATE_WITH_NO_META, rightType, op, ROSETTA_BINARY_OPERATION__RIGHT, actual -> "Cannot subtract `" + actual + "` from a `date`"); } else if (typeSystem.isSubtypeOf(leftType, builtins.UNCONSTRAINED_NUMBER_WITH_NO_META)) { - subtypeCheck(builtins.UNCONSTRAINED_NUMBER_WITH_NO_META, rightType, op, ROSETTA_BINARY_OPERATION__RIGHT); + subtypeCheck(builtins.UNCONSTRAINED_NUMBER_WITH_NO_META, rightType, op, ROSETTA_BINARY_OPERATION__RIGHT, actual -> "Cannot subtract `" + actual + "` from a `number`"); } else { unsupportedTypeError(leftType, op, ROSETTA_BINARY_OPERATION__LEFT, builtins.UNCONSTRAINED_NUMBER, builtins.DATE); if (!typeSystem.isSubtypeOf(rightType, builtins.DATE_WITH_NO_META) @@ -141,8 +142,8 @@ public void checkArithmeticOperation(ArithmeticOperation op) { } } } else { - subtypeCheck(builtins.UNCONSTRAINED_NUMBER_WITH_NO_META, leftType, op, ROSETTA_BINARY_OPERATION__LEFT); - subtypeCheck(builtins.UNCONSTRAINED_NUMBER_WITH_NO_META, rightType, op, ROSETTA_BINARY_OPERATION__RIGHT); + subtypeCheck(builtins.UNCONSTRAINED_NUMBER_WITH_NO_META, leftType, op, ROSETTA_BINARY_OPERATION__LEFT, op); + subtypeCheck(builtins.UNCONSTRAINED_NUMBER_WITH_NO_META, rightType, op, ROSETTA_BINARY_OPERATION__RIGHT, op); } } @@ -184,8 +185,8 @@ public void checkLogicalOperation(LogicalOperation op) { RMetaAnnotatedType rightType = typeProvider.getRMetaAnnotatedType(right); isSingleCheck(left, op, ROSETTA_BINARY_OPERATION__LEFT, op); isSingleCheck(right, op, ROSETTA_BINARY_OPERATION__RIGHT, op); - subtypeCheck(builtins.BOOLEAN_WITH_NO_META, leftType, op, ROSETTA_BINARY_OPERATION__LEFT); - subtypeCheck(builtins.BOOLEAN_WITH_NO_META, rightType, op, ROSETTA_BINARY_OPERATION__RIGHT); + subtypeCheck(builtins.BOOLEAN_WITH_NO_META, leftType, op, ROSETTA_BINARY_OPERATION__LEFT, op); + subtypeCheck(builtins.BOOLEAN_WITH_NO_META, rightType, op, ROSETTA_BINARY_OPERATION__RIGHT, op); } @Check @@ -203,11 +204,11 @@ public void checkComparisonOperation(ComparisonOperation op) { if (typeSystem.isSubtypeOf(leftType, builtins.NOTHING_WITH_NO_META)) { // Do not check right type } else if (typeSystem.isSubtypeOf(leftType, builtins.ZONED_DATE_TIME_WITH_NO_META)) { - subtypeCheck(builtins.ZONED_DATE_TIME_WITH_NO_META, rightType, op, ROSETTA_BINARY_OPERATION__RIGHT); + subtypeCheck(builtins.ZONED_DATE_TIME_WITH_NO_META, rightType, op, ROSETTA_BINARY_OPERATION__RIGHT, actual -> "Cannot compare a `" + actual + "` to a `zonedDateTime`"); } else if (typeSystem.isSubtypeOf(leftType, builtins.DATE_WITH_NO_META)) { - subtypeCheck(builtins.DATE_WITH_NO_META, rightType, op, ROSETTA_BINARY_OPERATION__RIGHT); + subtypeCheck(builtins.DATE_WITH_NO_META, rightType, op, ROSETTA_BINARY_OPERATION__RIGHT, actual -> "Cannot compare a `" + actual + "` to a `date`"); } else if (typeSystem.isSubtypeOf(leftType, builtins.UNCONSTRAINED_NUMBER_WITH_NO_META)) { - subtypeCheck(builtins.UNCONSTRAINED_NUMBER_WITH_NO_META, rightType, op, ROSETTA_BINARY_OPERATION__RIGHT); + subtypeCheck(builtins.UNCONSTRAINED_NUMBER_WITH_NO_META, rightType, op, ROSETTA_BINARY_OPERATION__RIGHT, actual -> "Cannot compare a `" + actual + "` to a `number`"); } else { unsupportedTypeError(leftType, op, ROSETTA_BINARY_OPERATION__LEFT, builtins.UNCONSTRAINED_NUMBER, builtins.DATE, builtins.ZONED_DATE_TIME); if (!typeSystem.isSubtypeOf(rightType, builtins.ZONED_DATE_TIME_WITH_NO_META) @@ -234,7 +235,7 @@ public void checkDisjointExpression(RosettaDisjointExpression expr) { @Check public void checkConditionalExpression(RosettaConditionalExpression expr) { isSingleCheck(expr.getIf(), expr, ROSETTA_CONDITIONAL_EXPRESSION__IF, "The condition of an if-then-else expression should be single cardinality"); - subtypeCheck(builtins.BOOLEAN_WITH_NO_META, expr.getIf(), expr, ROSETTA_CONDITIONAL_EXPRESSION__IF); + subtypeCheck(builtins.BOOLEAN_WITH_NO_META, expr.getIf(), expr, ROSETTA_CONDITIONAL_EXPRESSION__IF, actual -> "The condition of an if-then-else expression must be a boolean"); commonTypeCheck(expr.getIfthen(), expr.getElsethen(), expr, ROSETTA_CONDITIONAL_EXPRESSION__ELSETHEN); } @@ -264,20 +265,22 @@ public void checkSymbolReference(RosettaSymbolReference expr) { if (callable instanceof RosettaExternalFunction) { RosettaExternalFunction f = (RosettaExternalFunction) callable; for (int i=0; i "Cannot assign `" + actual + "` to parameter `" + param.getName() + "`"); } } else if (callable instanceof Function) { Function f = (Function) callable; for (int i=0; i "Cannot assign `" + actual + "` to input `" + param.getName() + "`"); } } else if (callable instanceof RosettaRule) { RosettaRule f = (RosettaRule) callable; @@ -285,7 +288,7 @@ public void checkSymbolReference(RosettaSymbolReference expr) { RMetaAnnotatedType paramType = RMetaAnnotatedType.withNoMeta(typeSystem.typeCallToRType(f.getInput())); RosettaExpression arg = expr.getArgs().get(0); isSingleCheck(arg, expr, ROSETTA_SYMBOL_REFERENCE__RAW_ARGS, 0, null); - subtypeCheck(paramType, arg, expr, ROSETTA_SYMBOL_REFERENCE__RAW_ARGS, 0); + subtypeCheck(paramType, arg, expr, ROSETTA_SYMBOL_REFERENCE__RAW_ARGS, 0, actual -> "Rule `" + f.getName() + "` cannot be called with type `" + actual + "`"); } } } else { @@ -413,8 +416,8 @@ public void checkChoiceOperation(ChoiceOperation op) { public void checkJoinOperation(JoinOperation op) { isMultiCheck(op.getLeft(), op, ROSETTA_BINARY_OPERATION__LEFT, op); isSingleCheck(op.getRight(), op, ROSETTA_BINARY_OPERATION__RIGHT, op); - subtypeCheck(builtins.UNCONSTRAINED_STRING_WITH_NO_META, op.getLeft(), op, ROSETTA_BINARY_OPERATION__LEFT); - subtypeCheck(builtins.UNCONSTRAINED_STRING_WITH_NO_META, op.getRight(), op, ROSETTA_BINARY_OPERATION__RIGHT); + subtypeCheck(builtins.UNCONSTRAINED_STRING_WITH_NO_META, op.getLeft(), op, ROSETTA_BINARY_OPERATION__LEFT, op); + subtypeCheck(builtins.UNCONSTRAINED_STRING_WITH_NO_META, op.getRight(), op, ROSETTA_BINARY_OPERATION__RIGHT, op); } @Check From e6439feab3ff40821612f87d2a0070fd4e41de94 Mon Sep 17 00:00:00 2001 From: Simon Cockx Date: Tue, 10 Dec 2024 14:30:04 +0000 Subject: [PATCH 08/10] Improved messages --- .../rosetta/validation/expression/ExpressionValidator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/ExpressionValidator.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/ExpressionValidator.java index 0faad05ca..c76afcf0f 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/ExpressionValidator.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/ExpressionValidator.java @@ -368,7 +368,7 @@ public void checkOnlyExistsExpression(RosettaOnlyExistsExpression expr) { RMetaAnnotatedType parentType = null; if (parent != null) { - isSingleCheck(parent, parent, null, "The `only exists` operation requires a single cardinality input"); + isSingleCheck(parent, parent, null, "The `only exists` operator requires a single cardinality input"); parentType = typeProvider.getRMetaAnnotatedType(parent); } else { if (cardinalityProvider.isImplicitVariableMulti(expr)) { From f4feeb4976ce4def354bd872b6e0ee99afcf3292 Mon Sep 17 00:00:00 2001 From: Simon Cockx Date: Tue, 17 Dec 2024 18:54:10 +0100 Subject: [PATCH 09/10] Fixed tests --- .../AbstractExpressionValidator.java | 4 +- .../expression/ExpressionValidator.java | 19 ++++++-- .../tests/RosettaTestInjectorProvider.java | 26 +++++++++++ .../tests/util/CustomConfigTestHelper.xtend | 4 +- .../RosettaCustomConfigInjectorProvider.java | 4 +- .../rosetta/docrefs/DocReferenceTest.xtend | 4 +- .../rosetta/docs/DocumentationSamples.xtend | 4 +- .../ResourceFormatterServiceTest.java | 4 +- .../RosettaExpressionFormattingTest.xtend | 4 +- .../formatting2/RosettaFormattingTest.xtend | 4 +- .../condition/ChoiceRuleGeneratorTest.xtend | 4 +- .../condition/ConditionGeneratorTest.xtend | 4 +- .../condition/DataRuleGeneratorTest.xtend | 4 +- .../condition/OneOfRuleGeneratorTest.xtend | 4 +- .../java/condition/RosettaConditionTest.xtend | 4 +- .../expression/ExpressionGeneratorTest.xtend | 4 +- .../java/expression/ListOperationTest.xtend | 4 +- .../RosettaBinaryOperationTest.xtend | 4 +- .../RosettaCountOperationTest.xtend | 4 +- .../RosettaExistsExpressionTest.xtend | 4 +- .../java/expression/TypeCoercionTest.xtend | 4 +- .../CalculationFunctionGeneratorTest.xtend | 4 +- .../java/function/FunctionGeneratorTest.xtend | 6 +-- .../java/object/EnumGeneratorTest.xtend | 4 +- .../ExternalHashcodeGeneratorTest.xtend | 4 +- .../java/object/GlobalKeyGeneratorTest.xtend | 4 +- .../java/object/ModelMetaGeneratorTest.xtend | 4 +- .../object/ModelObjectBoilerPlateTest.xtend | 4 +- .../ModelObjectBuilderGeneratorTest.xtend | 4 +- .../object/ModelObjectGeneratorTest.xtend | 4 +- .../java/object/PojoRegressionTest.xtend | 4 +- .../java/object/RosettaExtensionsTest.xtend | 4 +- .../java/object/RosettaModelTest.xtend | 4 +- ...osettaObjectInheritanceGeneratorTest.xtend | 4 +- .../java/object/RosettaProcessorTest.xtend | 4 +- .../qualify/RosettaQualifyEventTest.xtend | 4 +- .../qualify/RosettaQualifyProductTest.xtend | 4 +- .../java/reports/ReportingTest.xtend | 4 +- .../java/reports/TabulatorTest.xtend | 4 +- .../java/rule/RosettaRuleGeneratorTest.xtend | 6 +-- .../java/util/ModelGeneratorUtilTest.xtend | 4 +- .../validator/ValidatorGeneratorTest.xtend | 4 +- .../interpreter/RosettaInterpreterTest.java | 4 +- .../regnosys/rosetta/issues/Issue844.xtend | 4 +- .../RosettaFragmentProviderTest.xtend | 4 +- .../tests/RosettaExpressionsTest.xtend | 4 +- .../rosetta/tests/RosettaParsingTest.xtend | 2 +- .../tests/util/ExpressionParserTest.xtend | 4 +- .../types/RosettaTypeProviderTest.xtend | 46 +++++++++---------- .../rosetta/types/SubtypeRelationTest.xtend | 4 +- .../utils/RosettaSimpleSystemSolverTest.java | 4 +- .../validation/ChoiceValidatorTest.xtend | 2 +- .../validation/EnumValidatorTest.xtend | 2 +- ...ttaNamespaceDescriptionValidatorTest.xtend | 4 +- .../validation/RosettaValidatorTest.xtend | 14 +++--- .../UnnecessaryElementsRemoverTest.xtend | 4 +- .../tools/modelimport/XsdImportTest.java | 4 +- 57 files changed, 175 insertions(+), 140 deletions(-) create mode 100644 rosetta-testing/src/main/java/com/regnosys/rosetta/tests/RosettaTestInjectorProvider.java diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/AbstractExpressionValidator.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/AbstractExpressionValidator.java index 56da5d14a..c70bbbe97 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/AbstractExpressionValidator.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/AbstractExpressionValidator.java @@ -119,7 +119,7 @@ protected boolean isMultiCheck(RosettaExpression expr, EObject sourceObject, ESt if (suggestion != null) { msg += ". " + suggestion; } - error(msg, sourceObject, feature, featureIndex); + warning(msg, sourceObject, feature, featureIndex); return false; } return true; @@ -137,7 +137,7 @@ protected boolean isSingleCheck(RosettaExpression expr, EObject sourceObject, ES if (suggestion != null) { msg += ". " + suggestion; } - error(msg, sourceObject, feature, featureIndex); + warning(msg, sourceObject, feature, featureIndex); return false; } return true; diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/ExpressionValidator.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/ExpressionValidator.java index c76afcf0f..012165258 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/ExpressionValidator.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/ExpressionValidator.java @@ -151,16 +151,25 @@ private void checkModifiedBinaryOperation(ModifiableBinaryOperation op) { RosettaExpression left = op.getLeft(); RosettaExpression right = op.getRight(); String removeModifierSuggestion = "Did you mean to remove the `" + op.getCardMod().getLiteral() + "` modifier on the `" + op.getOperator() + "` operator?"; - String suggestion; + String flipOperandsSuggestion = "Did you mean to flip around the operands of the `" + op.getOperator() + "` operator?"; + String leftSuggestion; if (cardinalityProvider.isMulti(right)) { // Multi and single are flipped - suggestion = "Did you mean to flip around the operands of the `" + op.getOperator() + "` operator?"; + leftSuggestion = flipOperandsSuggestion; } else { // Both are single - suggestion = removeModifierSuggestion; + leftSuggestion = removeModifierSuggestion; } - isMultiCheck(left, op, ROSETTA_BINARY_OPERATION__LEFT, suggestion); - isSingleCheck(right, op, ROSETTA_BINARY_OPERATION__RIGHT, removeModifierSuggestion); + String rightSuggestion; + if (cardinalityProvider.isMulti(left)) { + // Both are multi + rightSuggestion = null; + } else { + // Multi and single are flipped + rightSuggestion = flipOperandsSuggestion; + } + isMultiCheck(left, op, ROSETTA_BINARY_OPERATION__LEFT, leftSuggestion); + isSingleCheck(right, op, ROSETTA_BINARY_OPERATION__RIGHT, rightSuggestion); } @Check diff --git a/rosetta-testing/src/main/java/com/regnosys/rosetta/tests/RosettaTestInjectorProvider.java b/rosetta-testing/src/main/java/com/regnosys/rosetta/tests/RosettaTestInjectorProvider.java new file mode 100644 index 000000000..0103156f8 --- /dev/null +++ b/rosetta-testing/src/main/java/com/regnosys/rosetta/tests/RosettaTestInjectorProvider.java @@ -0,0 +1,26 @@ +package com.regnosys.rosetta.tests; + +import org.eclipse.xtext.testing.validation.ValidationTestHelper; +import org.eclipse.xtext.testing.validation.ValidationTestHelper.Mode; + +import com.google.inject.Binder; +import com.regnosys.rosetta.RosettaRuntimeModule; + +public class RosettaTestInjectorProvider extends RosettaInjectorProvider { + protected RosettaRuntimeModule createRuntimeModule() { + // make it work also with Maven/Tycho and OSGI + // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=493672 + return new RosettaRuntimeModule() { + @Override + public ClassLoader bindClassLoaderToInstance() { + return RosettaTestInjectorProvider.class + .getClassLoader(); + } + + // Make sure validation tests properly check the message + public void configureValidationTestHelper(Binder binder) { + binder.bind(ValidationTestHelper.class).toInstance(new ValidationTestHelper(Mode.EXACT)); + } + }; + } +} diff --git a/rosetta-testing/src/main/java/com/regnosys/rosetta/tests/util/CustomConfigTestHelper.xtend b/rosetta-testing/src/main/java/com/regnosys/rosetta/tests/util/CustomConfigTestHelper.xtend index 562c07f54..6d036b6c9 100644 --- a/rosetta-testing/src/main/java/com/regnosys/rosetta/tests/util/CustomConfigTestHelper.xtend +++ b/rosetta-testing/src/main/java/com/regnosys/rosetta/tests/util/CustomConfigTestHelper.xtend @@ -5,7 +5,7 @@ import java.util.List import java.util.HashMap import com.google.inject.Injector import com.regnosys.rosetta.RosettaRuntimeModule -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider class CustomConfigTestHelper { static def compileToClassesForModel(List> code, @@ -48,7 +48,7 @@ class CustomConfigTestHelper { new RosettaRuntimeModule() { override ClassLoader bindClassLoaderToInstance() { - RosettaInjectorProvider.getClassLoader() + RosettaTestInjectorProvider.getClassLoader() } def Class bindRosettaConfigurationFileProvider() { diff --git a/rosetta-testing/src/main/java/com/regnosys/rosetta/tests/util/RosettaCustomConfigInjectorProvider.java b/rosetta-testing/src/main/java/com/regnosys/rosetta/tests/util/RosettaCustomConfigInjectorProvider.java index ce00fc4af..a09de71e6 100644 --- a/rosetta-testing/src/main/java/com/regnosys/rosetta/tests/util/RosettaCustomConfigInjectorProvider.java +++ b/rosetta-testing/src/main/java/com/regnosys/rosetta/tests/util/RosettaCustomConfigInjectorProvider.java @@ -12,7 +12,7 @@ import com.regnosys.rosetta.RosettaRuntimeModule; import com.regnosys.rosetta.RosettaStandaloneSetup; import com.regnosys.rosetta.config.file.RosettaConfigurationFileProvider; -import com.regnosys.rosetta.tests.RosettaInjectorProvider; +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider; public class RosettaCustomConfigInjectorProvider implements IInjectorProvider, IRegistryConfigurator { protected GlobalStateMemento stateBeforeInjectorCreation; @@ -47,7 +47,7 @@ protected RosettaRuntimeModule createRuntimeModule() { return new RosettaRuntimeModule() { @Override public ClassLoader bindClassLoaderToInstance() { - return RosettaInjectorProvider.class + return RosettaTestInjectorProvider.class .getClassLoader(); } diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/docrefs/DocReferenceTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/docrefs/DocReferenceTest.xtend index 41e2350c6..521300466 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/docrefs/DocReferenceTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/docrefs/DocReferenceTest.xtend @@ -1,6 +1,6 @@ package com.regnosys.rosetta.docrefs -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import com.regnosys.rosetta.tests.util.ModelHelper import org.eclipse.xtext.testing.InjectWith import org.eclipse.xtext.testing.extensions.InjectionExtension @@ -8,7 +8,7 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.^extension.ExtendWith import javax.inject.Inject -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) @ExtendWith(InjectionExtension) class DocReferenceTest { diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/docs/DocumentationSamples.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/docs/DocumentationSamples.xtend index 5e3f37bed..613927bc4 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/docs/DocumentationSamples.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/docs/DocumentationSamples.xtend @@ -1,7 +1,7 @@ package com.regnosys.rosetta.docs import org.eclipse.xtext.testing.InjectWith -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import org.junit.jupiter.api.^extension.ExtendWith import org.eclipse.xtext.testing.extensions.InjectionExtension import javax.inject.Inject @@ -22,7 +22,7 @@ import com.rosetta.util.types.generated.GeneratedJavaClass * If one of these tests fail, then the documentation should be reviewed as well, * as this probably means a code sample is out-dated. */ -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) @ExtendWith(InjectionExtension) class DocumentationSamples { diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/formatting2/ResourceFormatterServiceTest.java b/rosetta-testing/src/test/java/com/regnosys/rosetta/formatting2/ResourceFormatterServiceTest.java index 9c38fcdea..512009da2 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/formatting2/ResourceFormatterServiceTest.java +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/formatting2/ResourceFormatterServiceTest.java @@ -23,10 +23,10 @@ import java.util.stream.Collectors; import com.google.common.io.Resources; -import com.regnosys.rosetta.tests.RosettaInjectorProvider; +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider; @ExtendWith(InjectionExtension.class) -@InjectWith(RosettaInjectorProvider.class) +@InjectWith(RosettaTestInjectorProvider.class) public class ResourceFormatterServiceTest { @Inject ResourceFormatterService formatterService; diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/formatting2/RosettaExpressionFormattingTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/formatting2/RosettaExpressionFormattingTest.xtend index 32fa3fb31..703f07b89 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/formatting2/RosettaExpressionFormattingTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/formatting2/RosettaExpressionFormattingTest.xtend @@ -6,10 +6,10 @@ import javax.inject.Inject import org.junit.jupiter.api.^extension.ExtendWith import org.eclipse.xtext.testing.extensions.InjectionExtension import org.eclipse.xtext.testing.InjectWith -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class RosettaExpressionFormattingTest { @Inject extension ExpressionFormatterTestHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/formatting2/RosettaFormattingTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/formatting2/RosettaFormattingTest.xtend index c38bc8902..6136e5dad 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/formatting2/RosettaFormattingTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/formatting2/RosettaFormattingTest.xtend @@ -1,6 +1,6 @@ package com.regnosys.rosetta.formatting2 -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import org.eclipse.xtext.testing.InjectWith import org.eclipse.xtext.testing.extensions.InjectionExtension import org.junit.jupiter.api.Test @@ -12,7 +12,7 @@ import org.eclipse.xtext.testing.formatter.FormatterTestRequest import javax.inject.Inject @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class RosettaFormattingTest { @Inject extension FormatterTestHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/condition/ChoiceRuleGeneratorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/condition/ChoiceRuleGeneratorTest.xtend index 67e5b35da..07330a389 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/condition/ChoiceRuleGeneratorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/condition/ChoiceRuleGeneratorTest.xtend @@ -1,6 +1,6 @@ package com.regnosys.rosetta.generator.java.condition -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper import java.util.List import java.util.Map @@ -15,7 +15,7 @@ import javax.inject.Inject import com.regnosys.rosetta.RosettaEcoreUtil @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class ChoiceRuleGeneratorTest { @Inject extension CodeGeneratorTestHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/condition/ConditionGeneratorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/condition/ConditionGeneratorTest.xtend index 2439e46fc..44ae1e368 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/condition/ConditionGeneratorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/condition/ConditionGeneratorTest.xtend @@ -3,7 +3,7 @@ package com.regnosys.rosetta.generator.java.condition import org.junit.jupiter.api.^extension.ExtendWith import org.eclipse.xtext.testing.InjectWith import org.eclipse.xtext.testing.extensions.InjectionExtension -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import org.junit.jupiter.api.Test import javax.inject.Inject import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper @@ -13,7 +13,7 @@ import static org.junit.jupiter.api.Assertions.* import com.rosetta.model.lib.records.Date @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class ConditionGeneratorTest { @Inject extension CodeGeneratorTestHelper @Inject extension ConditionTestHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/condition/DataRuleGeneratorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/condition/DataRuleGeneratorTest.xtend index 5b5e31a36..fd1b7a3a8 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/condition/DataRuleGeneratorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/condition/DataRuleGeneratorTest.xtend @@ -2,7 +2,7 @@ package com.regnosys.rosetta.generator.java.condition import com.google.common.collect.ImmutableList import com.regnosys.rosetta.generator.java.RosettaJavaPackages.RootPackage -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper import java.io.File import java.math.BigDecimal @@ -22,7 +22,7 @@ import static org.junit.jupiter.api.Assertions.* import javax.inject.Inject @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class DataRuleGeneratorTest { @Inject extension CodeGeneratorTestHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/condition/OneOfRuleGeneratorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/condition/OneOfRuleGeneratorTest.xtend index 0da6a943d..cd14549dd 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/condition/OneOfRuleGeneratorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/condition/OneOfRuleGeneratorTest.xtend @@ -1,6 +1,6 @@ package com.regnosys.rosetta.generator.java.condition -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper import com.rosetta.model.lib.RosettaModelObject import com.rosetta.model.lib.path.RosettaPath @@ -20,7 +20,7 @@ import com.rosetta.model.lib.validation.ValidationResult import javax.inject.Inject @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class OneOfRuleGeneratorTest { @Inject extension CodeGeneratorTestHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/condition/RosettaConditionTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/condition/RosettaConditionTest.xtend index 692610121..1fc4b64cd 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/condition/RosettaConditionTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/condition/RosettaConditionTest.xtend @@ -1,7 +1,7 @@ package com.regnosys.rosetta.generator.java.condition import com.google.common.collect.ImmutableList -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper import com.rosetta.model.lib.RosettaModelObject import com.rosetta.model.lib.validation.ValidationResult @@ -20,7 +20,7 @@ import static org.junit.jupiter.api.Assertions.* import javax.inject.Inject @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class RosettaConditionTest { @Inject extension CodeGeneratorTestHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/ExpressionGeneratorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/ExpressionGeneratorTest.xtend index 2b161f5c8..a191f06df 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/ExpressionGeneratorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/ExpressionGeneratorTest.xtend @@ -10,7 +10,7 @@ import com.regnosys.rosetta.rosetta.expression.RosettaIntLiteral import com.regnosys.rosetta.rosetta.RosettaModel import com.regnosys.rosetta.rosetta.simple.Attribute import com.regnosys.rosetta.rosetta.simple.Data -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import org.eclipse.emf.common.util.ECollections import org.eclipse.xtext.testing.InjectWith import org.eclipse.xtext.testing.extensions.InjectionExtension @@ -39,7 +39,7 @@ import java.math.BigInteger import com.regnosys.rosetta.generator.java.types.JavaTypeUtil @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class ExpressionGeneratorTest { @Inject extension ExpressionGenerator expressionGenerator @Inject extension ExpressionParser diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/ListOperationTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/ListOperationTest.xtend index 86493b4e3..dd73d9b4b 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/ListOperationTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/ListOperationTest.xtend @@ -2,7 +2,7 @@ package com.regnosys.rosetta.generator.java.expression import com.google.common.collect.ImmutableList import com.regnosys.rosetta.generator.java.function.FunctionGeneratorHelper -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper import com.rosetta.model.lib.RosettaModelObject import com.rosetta.model.lib.records.Date @@ -22,7 +22,7 @@ import static org.junit.jupiter.api.Assertions.* import javax.inject.Inject @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class ListOperationTest { @Inject extension FunctionGeneratorHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/RosettaBinaryOperationTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/RosettaBinaryOperationTest.xtend index 0fdc87cb8..4782c92bd 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/RosettaBinaryOperationTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/RosettaBinaryOperationTest.xtend @@ -1,7 +1,7 @@ package com.regnosys.rosetta.generator.java.expression import com.google.common.collect.ImmutableList -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper import com.rosetta.model.lib.RosettaModelObject import java.math.BigDecimal @@ -21,7 +21,7 @@ import org.junit.jupiter.api.Disabled import javax.inject.Inject @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class RosettaBinaryOperationTest { @Inject extension CodeGeneratorTestHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/RosettaCountOperationTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/RosettaCountOperationTest.xtend index 7da4d4ec4..eb4a8f8e1 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/RosettaCountOperationTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/RosettaCountOperationTest.xtend @@ -1,7 +1,7 @@ package com.regnosys.rosetta.generator.java.expression import com.google.common.collect.ImmutableList -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper import com.rosetta.model.lib.RosettaModelObject import java.math.BigDecimal @@ -20,7 +20,7 @@ import com.regnosys.rosetta.generator.java.function.FunctionGeneratorHelper import javax.inject.Inject @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class RosettaCountOperationTest { @Inject extension CodeGeneratorTestHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/RosettaExistsExpressionTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/RosettaExistsExpressionTest.xtend index e08d5e4be..94825525a 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/RosettaExistsExpressionTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/RosettaExistsExpressionTest.xtend @@ -1,7 +1,7 @@ package com.regnosys.rosetta.generator.java.expression import com.google.common.collect.ImmutableList -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper import com.rosetta.model.lib.RosettaModelObject import java.math.BigDecimal @@ -20,7 +20,7 @@ import com.regnosys.rosetta.generator.java.function.FunctionGeneratorHelper import javax.inject.Inject @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class RosettaExistsExpressionTest { @Inject extension CodeGeneratorTestHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/TypeCoercionTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/TypeCoercionTest.xtend index 2a1d82c32..352ffc8f2 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/TypeCoercionTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/TypeCoercionTest.xtend @@ -3,7 +3,7 @@ package com.regnosys.rosetta.generator.java.expression import org.junit.jupiter.api.^extension.ExtendWith import org.eclipse.xtext.testing.extensions.InjectionExtension import org.eclipse.xtext.testing.InjectWith -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import javax.inject.Inject import org.junit.jupiter.api.Test import com.regnosys.rosetta.generator.java.statement.builder.JavaExpression @@ -25,7 +25,7 @@ import com.regnosys.rosetta.generator.java.types.RJavaFieldWithMeta import com.regnosys.rosetta.generator.java.types.RJavaReferenceWithMeta @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class TypeCoercionTest { @Inject TypeCoercionService coercionService @Inject extension ImportManagerExtension diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/function/CalculationFunctionGeneratorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/function/CalculationFunctionGeneratorTest.xtend index 849eca60c..87c1ab698 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/function/CalculationFunctionGeneratorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/function/CalculationFunctionGeneratorTest.xtend @@ -1,6 +1,6 @@ package com.regnosys.rosetta.generator.java.function -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper import org.eclipse.xtext.testing.InjectWith import org.eclipse.xtext.testing.extensions.InjectionExtension @@ -11,7 +11,7 @@ import static org.junit.jupiter.api.Assertions.* import javax.inject.Inject @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class CalculationFunctionGeneratorTest { @Inject extension FunctionGeneratorHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/function/FunctionGeneratorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/function/FunctionGeneratorTest.xtend index ca49bdc8a..567f3e971 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/function/FunctionGeneratorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/function/FunctionGeneratorTest.xtend @@ -2,7 +2,7 @@ package com.regnosys.rosetta.generator.java.function import com.google.common.collect.ImmutableList import com.regnosys.rosetta.rosetta.simple.SimplePackage -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper import com.regnosys.rosetta.tests.util.ModelHelper import com.regnosys.rosetta.validation.RosettaIssueCodes @@ -35,7 +35,7 @@ import com.rosetta.model.lib.meta.Key import com.rosetta.model.lib.meta.Reference @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class FunctionGeneratorTest { @Inject extension FunctionGeneratorHelper @@ -3516,7 +3516,7 @@ class FunctionGeneratorTest { '''.parseRosetta model.assertError(LOGICAL_OPERATION, null, - "Expected type `boolean`, but got `Foo` instead") + "Expected type `boolean`, but got `Foo` instead. Cannot use `Foo` with operator `and`") } @Test diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/EnumGeneratorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/EnumGeneratorTest.xtend index 8180f22ba..067878f6c 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/EnumGeneratorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/EnumGeneratorTest.xtend @@ -1,7 +1,7 @@ package com.regnosys.rosetta.generator.java.object import com.regnosys.rosetta.generator.java.enums.EnumHelper -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper import com.regnosys.rosetta.tests.util.ModelHelper import org.eclipse.xtext.testing.InjectWith @@ -15,7 +15,7 @@ import org.junit.jupiter.api.Disabled import javax.inject.Inject @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class EnumGeneratorTest { @Inject extension CodeGeneratorTestHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/ExternalHashcodeGeneratorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/ExternalHashcodeGeneratorTest.xtend index db05bd55a..42a48c797 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/ExternalHashcodeGeneratorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/ExternalHashcodeGeneratorTest.xtend @@ -1,6 +1,6 @@ package com.regnosys.rosetta.generator.java.object -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper import com.regnosys.rosetta.tests.util.ModelHelper import org.eclipse.xtext.testing.InjectWith @@ -13,7 +13,7 @@ import static org.hamcrest.MatcherAssert.* import javax.inject.Inject @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class ExternalHashcodeGeneratorTest { @Inject extension CodeGeneratorTestHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/GlobalKeyGeneratorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/GlobalKeyGeneratorTest.xtend index e6a4a9c82..62916d0b2 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/GlobalKeyGeneratorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/GlobalKeyGeneratorTest.xtend @@ -1,6 +1,6 @@ package com.regnosys.rosetta.generator.java.object -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper import com.regnosys.rosetta.tests.util.ModelHelper import org.eclipse.xtext.testing.InjectWith @@ -14,7 +14,7 @@ import static org.hamcrest.MatcherAssert.* import javax.inject.Inject @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class GlobalKeyGeneratorTest { @Inject extension CodeGeneratorTestHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/ModelMetaGeneratorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/ModelMetaGeneratorTest.xtend index 09f1c1f46..222b76dbe 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/ModelMetaGeneratorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/ModelMetaGeneratorTest.xtend @@ -2,7 +2,7 @@ package com.regnosys.rosetta.generator.java.object import com.google.inject.AbstractModule import com.google.inject.Guice -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper import com.regnosys.rosetta.tests.util.ModelHelper import com.rosetta.model.lib.functions.ConditionValidator @@ -27,7 +27,7 @@ import static org.junit.jupiter.api.Assertions.* import javax.inject.Inject @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class ModelMetaGeneratorTest { @Inject extension ModelHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/ModelObjectBoilerPlateTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/ModelObjectBoilerPlateTest.xtend index 3c5539249..36d93a2cc 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/ModelObjectBoilerPlateTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/ModelObjectBoilerPlateTest.xtend @@ -1,6 +1,6 @@ package com.regnosys.rosetta.generator.java.object -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper import com.regnosys.rosetta.tests.util.ModelHelper import org.eclipse.xtext.testing.InjectWith @@ -13,7 +13,7 @@ import static org.hamcrest.MatcherAssert.* import javax.inject.Inject @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class ModelObjectBoilerPlateTest { @Inject extension CodeGeneratorTestHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/ModelObjectBuilderGeneratorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/ModelObjectBuilderGeneratorTest.xtend index bfe503b81..0285e0612 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/ModelObjectBuilderGeneratorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/ModelObjectBuilderGeneratorTest.xtend @@ -1,7 +1,7 @@ package com.regnosys.rosetta.generator.java.object import com.google.common.collect.Lists -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper import com.regnosys.rosetta.tests.util.ModelHelper import java.util.List @@ -16,7 +16,7 @@ import static org.junit.jupiter.api.Assertions.* import javax.inject.Inject @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class ModelObjectBuilderGeneratorTest { @Inject extension CodeGeneratorTestHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/ModelObjectGeneratorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/ModelObjectGeneratorTest.xtend index c334ce15b..98f10ecdc 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/ModelObjectGeneratorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/ModelObjectGeneratorTest.xtend @@ -3,7 +3,7 @@ package com.regnosys.rosetta.generator.java.object import com.google.common.collect.ImmutableList import com.google.common.collect.Lists import com.regnosys.rosetta.generator.java.RosettaJavaPackages.RootPackage -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper import com.regnosys.rosetta.tests.util.ModelHelper import com.rosetta.model.lib.RosettaModelObject @@ -28,7 +28,7 @@ import static org.junit.jupiter.api.Assertions.* import javax.inject.Inject @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class ModelObjectGeneratorTest { @Inject extension ReflectExtensions diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/PojoRegressionTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/PojoRegressionTest.xtend index cc24c3cea..b0ab41f0b 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/PojoRegressionTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/PojoRegressionTest.xtend @@ -3,7 +3,7 @@ package com.regnosys.rosetta.generator.java.object import org.eclipse.xtext.testing.extensions.InjectionExtension import org.junit.jupiter.api.^extension.ExtendWith import org.eclipse.xtext.testing.InjectWith -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import javax.inject.Inject import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper import org.junit.jupiter.api.Test @@ -20,7 +20,7 @@ import org.junit.jupiter.api.TestInstance.Lifecycle * through, please add to this test so it does not happen again. */ @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) @TestInstance(Lifecycle.PER_CLASS) class PojoRegressionTest { @Inject extension CodeGeneratorTestHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/RosettaExtensionsTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/RosettaExtensionsTest.xtend index 40f0ebefe..4246dc590 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/RosettaExtensionsTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/RosettaExtensionsTest.xtend @@ -3,7 +3,7 @@ package com.regnosys.rosetta.generator.java.object import com.regnosys.rosetta.rosetta.RosettaEnumeration import com.regnosys.rosetta.rosetta.RosettaModel import com.regnosys.rosetta.rosetta.simple.Data -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import org.eclipse.xtext.testing.InjectWith import org.eclipse.xtext.testing.extensions.InjectionExtension import org.eclipse.xtext.testing.util.ParseHelper @@ -15,7 +15,7 @@ import javax.inject.Inject import com.regnosys.rosetta.types.RObjectFactory @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class RosettaExtensionsTest { @Inject extension ParseHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/RosettaModelTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/RosettaModelTest.xtend index de5357010..40f4ebfe6 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/RosettaModelTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/RosettaModelTest.xtend @@ -4,7 +4,7 @@ package com.regnosys.rosetta.generator.java.object import com.regnosys.rosetta.rosetta.RosettaEnumeration -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import com.regnosys.rosetta.tests.util.ModelHelper import org.eclipse.xtext.testing.InjectWith import org.eclipse.xtext.testing.extensions.InjectionExtension @@ -15,7 +15,7 @@ import static org.junit.jupiter.api.Assertions.* import javax.inject.Inject @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class RosettaModelTest{ @Inject extension ModelHelper modelHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/RosettaObjectInheritanceGeneratorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/RosettaObjectInheritanceGeneratorTest.xtend index 8425d664d..84ca205a5 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/RosettaObjectInheritanceGeneratorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/RosettaObjectInheritanceGeneratorTest.xtend @@ -1,6 +1,6 @@ package com.regnosys.rosetta.generator.java.object -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper import com.regnosys.rosetta.tests.util.ModelHelper import org.eclipse.xtext.testing.InjectWith @@ -13,7 +13,7 @@ import org.junit.jupiter.api.Disabled import javax.inject.Inject @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class RosettaObjectInheritanceGeneratorTest { @Inject extension CodeGeneratorTestHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/RosettaProcessorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/RosettaProcessorTest.xtend index 2a633fe2b..8c422e566 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/RosettaProcessorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/object/RosettaProcessorTest.xtend @@ -8,12 +8,12 @@ import com.rosetta.model.lib.RosettaModelObject import org.junit.jupiter.api.^extension.ExtendWith import org.eclipse.xtext.testing.extensions.InjectionExtension import org.eclipse.xtext.testing.InjectWith -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import static org.junit.jupiter.api.Assertions.* @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class RosettaProcessorTest { @Inject extension CodeGeneratorTestHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/qualify/RosettaQualifyEventTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/qualify/RosettaQualifyEventTest.xtend index 2807a16a7..743ad4637 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/qualify/RosettaQualifyEventTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/qualify/RosettaQualifyEventTest.xtend @@ -1,6 +1,6 @@ package com.regnosys.rosetta.generator.java.qualify -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper import com.rosetta.model.lib.RosettaModelObject import java.math.BigDecimal @@ -18,7 +18,7 @@ import static org.junit.jupiter.api.Assertions.* import javax.inject.Inject @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class RosettaQualifyEventTest { @Inject extension CodeGeneratorTestHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/qualify/RosettaQualifyProductTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/qualify/RosettaQualifyProductTest.xtend index 78007c689..26b65e213 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/qualify/RosettaQualifyProductTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/qualify/RosettaQualifyProductTest.xtend @@ -1,7 +1,7 @@ package com.regnosys.rosetta.generator.java.qualify import com.google.common.collect.ImmutableList -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper import com.rosetta.model.lib.RosettaModelObject import com.rosetta.model.lib.qualify.QualifyResult @@ -20,7 +20,7 @@ import static org.hamcrest.core.Is.is import javax.inject.Inject @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class RosettaQualifyProductTest { @Inject extension CodeGeneratorTestHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/reports/ReportingTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/reports/ReportingTest.xtend index 882800502..0345e1f32 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/reports/ReportingTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/reports/ReportingTest.xtend @@ -1,7 +1,7 @@ package com.regnosys.rosetta.generator.java.reports import org.eclipse.xtext.testing.InjectWith -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import org.junit.jupiter.api.^extension.ExtendWith import org.eclipse.xtext.testing.extensions.InjectionExtension import javax.inject.Inject @@ -19,7 +19,7 @@ import org.junit.jupiter.api.Test import com.rosetta.model.lib.ModelReportId import com.rosetta.util.types.generated.GeneratedJavaClassService -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) @ExtendWith(InjectionExtension) class ReportingTest { diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/reports/TabulatorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/reports/TabulatorTest.xtend index f6d367838..4a60af678 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/reports/TabulatorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/reports/TabulatorTest.xtend @@ -2,7 +2,7 @@ package com.regnosys.rosetta.generator.java.reports import com.regnosys.rosetta.config.file.RosettaConfigurationFileProvider import com.regnosys.rosetta.generator.java.RosettaJavaPackages.RootPackage -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper import com.rosetta.model.lib.ModelReportId import com.rosetta.model.lib.RosettaModelObject @@ -26,7 +26,7 @@ import static org.junit.jupiter.api.Assertions.* import static extension com.regnosys.rosetta.tests.util.CustomConfigTestHelper.* -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) @ExtendWith(InjectionExtension) class TabulatorTest { diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/rule/RosettaRuleGeneratorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/rule/RosettaRuleGeneratorTest.xtend index 308ddf9c4..958bfcf12 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/rule/RosettaRuleGeneratorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/rule/RosettaRuleGeneratorTest.xtend @@ -2,7 +2,7 @@ package com.regnosys.rosetta.generator.java.rule import com.google.inject.Guice import com.google.inject.Injector -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper import com.regnosys.rosetta.tests.util.ModelHelper import java.util.Map @@ -18,7 +18,7 @@ import static org.hamcrest.MatcherAssert.* import static org.junit.jupiter.api.Assertions.* import javax.inject.Inject -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) @ExtendWith(InjectionExtension) class RosettaRuleGeneratorTest { @@ -1131,7 +1131,7 @@ class RosettaRuleGeneratorTest { .replace('\r', "") .parseRosetta .assertError(ROSETTA_SYMBOL_REFERENCE, null, - "Expected type `Foo`, but got `Bar` instead") + "Expected type `Foo`, but got `Bar` instead. Rule `Rule2` cannot be called with type `Bar`") } diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/util/ModelGeneratorUtilTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/util/ModelGeneratorUtilTest.xtend index 8dddda3a3..57874e071 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/util/ModelGeneratorUtilTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/util/ModelGeneratorUtilTest.xtend @@ -1,6 +1,6 @@ package com.regnosys.rosetta.generator.java.util -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import org.eclipse.xtext.testing.InjectWith import org.eclipse.xtext.testing.extensions.InjectionExtension import org.junit.jupiter.api.Test @@ -12,7 +12,7 @@ import static org.junit.jupiter.api.Assertions.* import javax.inject.Inject @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class ModelGeneratorUtilTest { @Inject extension ModelHelper modelHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/validator/ValidatorGeneratorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/validator/ValidatorGeneratorTest.xtend index 411ca8190..f50904a3b 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/validator/ValidatorGeneratorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/validator/ValidatorGeneratorTest.xtend @@ -1,6 +1,6 @@ package com.regnosys.rosetta.generator.java.validator -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper import javax.inject.Inject import org.eclipse.xtext.testing.InjectWith @@ -12,7 +12,7 @@ import org.junit.jupiter.api.^extension.ExtendWith import static org.junit.jupiter.api.Assertions.* @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class ValidatorGeneratorTest { @Inject extension CodeGeneratorTestHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/interpreter/RosettaInterpreterTest.java b/rosetta-testing/src/test/java/com/regnosys/rosetta/interpreter/RosettaInterpreterTest.java index 9f61a1b2d..cf4ff2ff4 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/interpreter/RosettaInterpreterTest.java +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/interpreter/RosettaInterpreterTest.java @@ -23,7 +23,7 @@ import javax.inject.Inject; import com.regnosys.rosetta.rosetta.expression.RosettaExpression; -import com.regnosys.rosetta.tests.RosettaInjectorProvider; +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider; import org.eclipse.xtext.testing.extensions.InjectionExtension; import org.eclipse.xtext.testing.InjectWith; @@ -37,7 +37,7 @@ import com.rosetta.model.lib.RosettaNumber; @ExtendWith(InjectionExtension.class) -@InjectWith(RosettaInjectorProvider.class) +@InjectWith(RosettaTestInjectorProvider.class) public class RosettaInterpreterTest { @Inject private ExpressionParser parser; diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/issues/Issue844.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/issues/Issue844.xtend index 73a1e9b2b..792f01bfd 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/issues/Issue844.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/issues/Issue844.xtend @@ -1,6 +1,6 @@ package com.regnosys.rosetta.issues -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper import org.eclipse.xtext.testing.InjectWith import org.eclipse.xtext.testing.extensions.InjectionExtension @@ -17,7 +17,7 @@ import org.junit.jupiter.api.TestInstance.Lifecycle // Regression test for https://github.com/finos/rune-dsl/issues/844 @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) @TestInstance(Lifecycle.PER_CLASS) class Issue844 { diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/resource/RosettaFragmentProviderTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/resource/RosettaFragmentProviderTest.xtend index 4041cfb95..036855b7a 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/resource/RosettaFragmentProviderTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/resource/RosettaFragmentProviderTest.xtend @@ -2,7 +2,7 @@ package com.regnosys.rosetta.resource import com.regnosys.rosetta.rosetta.RosettaModel import com.regnosys.rosetta.rosetta.simple.Data -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import org.eclipse.emf.ecore.util.EcoreUtil import org.eclipse.xtext.testing.InjectWith import org.eclipse.xtext.testing.extensions.InjectionExtension @@ -14,7 +14,7 @@ import static org.junit.jupiter.api.Assertions.* import javax.inject.Inject @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class RosettaFragmentProviderTest { @Inject extension ParseHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaExpressionsTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaExpressionsTest.xtend index e5d67e6a8..1734cc9da 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaExpressionsTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaExpressionsTest.xtend @@ -26,7 +26,7 @@ import javax.inject.Inject * A set of tests for all instances of RosettaExpression i.e. RosettaAdditiveExpression */ @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class RosettaExpressionsTest { @Inject extension CodeGeneratorTestHelper @@ -99,7 +99,7 @@ class RosettaExpressionsTest { output: result boolean (1..1) set result: test -> one + test -> two = 42 - '''.parseRosetta.assertError(ARITHMETIC_OPERATION, null, "Expected type `time`, but got `date` instead") + '''.parseRosetta.assertError(ARITHMETIC_OPERATION, null, "Expected type `time`, but got `date` instead. Cannot add `date` to a `date`") } /** diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaParsingTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaParsingTest.xtend index 44941b9b5..c3e57d172 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaParsingTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaParsingTest.xtend @@ -27,7 +27,7 @@ import static com.regnosys.rosetta.rosetta.expression.ExpressionPackage.Literals import static org.junit.jupiter.api.Assertions.* @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class RosettaParsingTest { @Inject extension ModelHelper modelHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/util/ExpressionParserTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/util/ExpressionParserTest.xtend index 133d58442..f2009b486 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/util/ExpressionParserTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/util/ExpressionParserTest.xtend @@ -7,10 +7,10 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.^extension.ExtendWith import javax.inject.Inject -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class ExpressionParserTest { @Inject extension ExpressionParser diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/types/RosettaTypeProviderTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/types/RosettaTypeProviderTest.xtend index 505931043..434eacb49 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/types/RosettaTypeProviderTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/types/RosettaTypeProviderTest.xtend @@ -4,7 +4,7 @@ import com.regnosys.rosetta.rosetta.expression.RosettaBinaryOperation import com.regnosys.rosetta.rosetta.expression.RosettaContainsExpression import com.regnosys.rosetta.rosetta.simple.Data import com.regnosys.rosetta.rosetta.simple.Function -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import com.regnosys.rosetta.tests.util.ModelHelper import org.eclipse.xtext.testing.InjectWith import org.eclipse.xtext.testing.extensions.InjectionExtension @@ -35,7 +35,7 @@ import static com.regnosys.rosetta.rosetta.expression.ExpressionPackage.Literals import org.eclipse.xtext.nodemodel.util.NodeModelUtils @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) @TestInstance(Lifecycle.PER_CLASS) class RosettaTypeProviderTest { @@ -126,13 +126,13 @@ class RosettaTypeProviderTest { def void testLogicalOperationTypeChecking() { '1 or False' .parseExpression - .assertError(LOGICAL_OPERATION, null, "Expected type `boolean`, but got `int` instead") + .assertError(LOGICAL_OPERATION, null, "Expected type `boolean`, but got `int` instead. Cannot use `int` with operator `or`") 'True or 3.14' .parseExpression - .assertError(LOGICAL_OPERATION, null, "Expected type `boolean`, but got `number` instead") + .assertError(LOGICAL_OPERATION, null, "Expected type `boolean`, but got `number` instead. Cannot use `number` with operator `or`") 'a or False' .parseExpression(#['a boolean (1..2)']) - .assertError(LOGICAL_OPERATION, null, "Expecting single cardinality") + .assertWarning(LOGICAL_OPERATION, null, "Expecting single cardinality. The `or` operator requires a single cardinality input") } @Test @@ -168,7 +168,7 @@ class RosettaTypeProviderTest { .assertError(EQUALITY_OPERATION, null, "Types `int` and `boolean` are not comparable") '[1, 3] any <> a' .parseExpression(#['a int (1..2)']) - .assertError(EQUALITY_OPERATION, null, "Expecting single cardinality") + .assertWarning(EQUALITY_OPERATION, null, "Expecting single cardinality") // '[1, 2] all = empty' // .parseExpression // .assertError(EQUALITY_OPERATION, null, "Expected a single value, but got an empty value instead") @@ -177,10 +177,10 @@ class RosettaTypeProviderTest { // .assertError(EQUALITY_OPERATION, null, "Expected a single value, but got an empty value instead") '[1, 2] all = [1, 2]' .parseExpression - .assertError(EQUALITY_OPERATION, null, "Expecting single cardinality") + .assertWarning(EQUALITY_OPERATION, null, "Expecting single cardinality") '5 any <> [1, 2]' .parseExpression - .assertError(EQUALITY_OPERATION, null, "Expecting multi cardinality") + .assertWarning(EQUALITY_OPERATION, null, "Expecting multi cardinality. Did you mean to flip around the operands of the `<>` operator?") // '[3.0] any <> 5' // .parseExpression // .assertError(EQUALITY_OPERATION, null, "The cardinality operator `any` is redundant when comparing two single values") @@ -208,20 +208,20 @@ class RosettaTypeProviderTest { def void testArithmeticOperationTypeChecking() { '[1, 2] + 3' .parseExpression - .assertError(ARITHMETIC_OPERATION, null, "Expecting single cardinality") + .assertWarning(ARITHMETIC_OPERATION, null, "Expecting single cardinality. The `+` operator requires a single cardinality input") // TODO // 'empty - 3' // .parseExpression // .assertError(ARITHMETIC_OPERATION, null, "Expected a single value, but got an empty value instead.") '1.5 * False' .parseExpression - .assertError(ARITHMETIC_OPERATION, null, "Expected type `number`, but got `boolean` instead") + .assertError(ARITHMETIC_OPERATION, null, "Expected type `number`, but got `boolean` instead. Cannot use `boolean` with operator `*`") '"ab" + 3' .parseExpression - .assertError(ARITHMETIC_OPERATION, null, "Expected type `string`, but got `int` instead") + .assertError(ARITHMETIC_OPERATION, null, "Expected type `string`, but got `int` instead. Cannot add `int` to a `string`") 'a + 5' .parseExpression(#['a int (1..2)']) - .assertError(ARITHMETIC_OPERATION, null, "Expecting single cardinality") + .assertWarning(ARITHMETIC_OPERATION, null, "Expecting single cardinality. The `+` operator requires a single cardinality input") } @Test @@ -241,36 +241,36 @@ class RosettaTypeProviderTest { // TODO: support date, zonedDateTime and `time`? '[1, 2] < 3' .parseExpression - .assertError(COMPARISON_OPERATION, null, "Expecting single cardinality") + .assertWarning(COMPARISON_OPERATION, null, "Expecting single cardinality. Did you mean to use `all` or `any` in front of the `<` operator?") // TODO // 'empty > 3' // .parseExpression // .assertError(COMPARISON_OPERATION, null, "Expected a single value, but got an empty value instead.") '1.5 <= False' .parseExpression - .assertError(COMPARISON_OPERATION, null, "Expected type `number`, but got `boolean` instead") + .assertError(COMPARISON_OPERATION, null, "Expected type `number`, but got `boolean` instead. Cannot compare a `boolean` to a `number`") 'a < 5' .parseExpression(#['a int (1..2)']) - .assertError(COMPARISON_OPERATION, null, "Expecting single cardinality") + .assertWarning(COMPARISON_OPERATION, null, "Expecting single cardinality. Did you mean to use `all` or `any` in front of the `<` operator?") '[1, 2] any < a' .parseExpression(#['a int (1..2)']) - .assertError(COMPARISON_OPERATION, null, "Expecting single cardinality") + .assertWarning(COMPARISON_OPERATION, null, "Expecting single cardinality") // '[1, 2] all >= empty' // .parseExpression // .assertError(COMPARISON_OPERATION, null, "Expected a single value, but got an empty value instead") 'empty any < empty' .parseExpression - .assertError(COMPARISON_OPERATION, null, "Expecting multi cardinality") + .assertWarning(COMPARISON_OPERATION, null, "Expecting multi cardinality. Did you mean to remove the `any` modifier on the `<` operator?") '[1, 2] all > [1, 2]' .parseExpression - .assertError(COMPARISON_OPERATION, null, "Expecting single cardinality") + .assertWarning(COMPARISON_OPERATION, null, "Expecting single cardinality") '5 any <= [1, 2]' .parseExpression - .assertError(COMPARISON_OPERATION, null, "Expecting single cardinality") + .assertWarning(COMPARISON_OPERATION, null, "Expecting single cardinality. Did you mean to flip around the operands of the `<=` operator?") '5 all >= 1' .parseExpression - .assertError(COMPARISON_OPERATION, null, "Expecting multi cardinality") + .assertWarning(COMPARISON_OPERATION, null, "Expecting multi cardinality. Did you mean to remove the `all` modifier on the `>=` operator?") } @Test @@ -282,7 +282,7 @@ class RosettaTypeProviderTest { def void testConditionalExpressionTypeChecking() { 'if [True, False] then 1 else 2' .parseExpression - .assertError(ROSETTA_CONDITIONAL_EXPRESSION, null, "Expecting single cardinality") + .assertWarning(ROSETTA_CONDITIONAL_EXPRESSION, null, "Expecting single cardinality. The condition of an if-then-else expression should be single cardinality") // TODO // 'if empty then 1 else 2' // .parseExpression @@ -342,7 +342,7 @@ class RosettaTypeProviderTest { .assertError(ROSETTA_SYMBOL_REFERENCE, null, "Expected 2 arguments, but got 3 instead"); 'SomeFunc(1, [2, 3])' .parseExpression(#[context]) - .assertError(ROSETTA_SYMBOL_REFERENCE, null, "Expected type `boolean`, but got `int` instead"); + .assertError(ROSETTA_SYMBOL_REFERENCE, null, "Expected type `boolean`, but got `int` instead. Cannot assign `int` to input `b`"); // TODO // 'SomeFunc(1, [False, True, False, False, True])' // .parseExpression(#[context]) @@ -510,7 +510,7 @@ class RosettaTypeProviderTest { ] (model.elements.get(2) as Function).operations => [ - get(0).expression.assertError(ROSETTA_SYMBOL_REFERENCE, null, "Expecting single cardinality") + get(0).expression.assertWarning(ROSETTA_SYMBOL_REFERENCE, null, "Expecting single cardinality. The `only exists` operator requires a single cardinality input") get(1).expression.assertError(ROSETTA_ONLY_EXISTS_EXPRESSION, null, "Object must have a parent object") get(2).expression.assertError(ROSETTA_SYMBOL_REFERENCE, null, "All parent paths must be equal") get(3).expression.assertError(ROSETTA_SYMBOL_REFERENCE, null, "Operator `only exists` is not supported for type Foo. All attributes of input type should be optional") diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/types/SubtypeRelationTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/types/SubtypeRelationTest.xtend index 579e9fb6c..bfeb811c5 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/types/SubtypeRelationTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/types/SubtypeRelationTest.xtend @@ -3,7 +3,7 @@ package com.regnosys.rosetta.types import org.eclipse.xtext.testing.extensions.InjectionExtension import org.junit.jupiter.api.^extension.ExtendWith import com.regnosys.rosetta.rosetta.simple.Data -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import org.eclipse.xtext.testing.InjectWith import javax.inject.Inject import com.regnosys.rosetta.tests.util.ModelHelper @@ -19,7 +19,7 @@ import com.regnosys.rosetta.types.builtin.RStringType import static extension com.regnosys.rosetta.types.RMetaAnnotatedType.withNoMeta @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class SubtypeRelationTest { @Inject extension SubtypeRelation @Inject extension ModelHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/utils/RosettaSimpleSystemSolverTest.java b/rosetta-testing/src/test/java/com/regnosys/rosetta/utils/RosettaSimpleSystemSolverTest.java index 61e79f902..f1d875078 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/utils/RosettaSimpleSystemSolverTest.java +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/utils/RosettaSimpleSystemSolverTest.java @@ -37,14 +37,14 @@ import com.regnosys.rosetta.interpreter.RosettaInterpreterContext; import com.regnosys.rosetta.interpreter.RosettaValue; import com.regnosys.rosetta.rosetta.expression.RosettaExpression; -import com.regnosys.rosetta.tests.RosettaInjectorProvider; +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider; import com.regnosys.rosetta.tests.util.ExpressionParser; import com.regnosys.rosetta.tests.util.RosettaValueHelper; import com.regnosys.rosetta.rosetta.simple.Attribute; import com.regnosys.rosetta.rosetta.RosettaSymbol; @ExtendWith(InjectionExtension.class) -@InjectWith(RosettaInjectorProvider.class) +@InjectWith(RosettaTestInjectorProvider.class) public class RosettaSimpleSystemSolverTest { @Inject private RosettaSimpleSystemSolver solver; diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/ChoiceValidatorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/ChoiceValidatorTest.xtend index 2679f9ed1..7fbd66b0f 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/ChoiceValidatorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/ChoiceValidatorTest.xtend @@ -12,7 +12,7 @@ import static com.regnosys.rosetta.rosetta.simple.SimplePackage.Literals.* import javax.inject.Inject @ExtendWith(InjectionExtension) -@InjectWith(MyRosettaInjectorProvider) +@InjectWith(MyRosettaTestInjectorProvider) class ChoiceValidatorTest implements RosettaIssueCodes { @Inject extension ValidationTestHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/EnumValidatorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/EnumValidatorTest.xtend index 09b417d3d..2dbcb25c6 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/EnumValidatorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/EnumValidatorTest.xtend @@ -12,7 +12,7 @@ import static com.regnosys.rosetta.rosetta.expression.ExpressionPackage.Literals import javax.inject.Inject @ExtendWith(InjectionExtension) -@InjectWith(MyRosettaInjectorProvider) +@InjectWith(MyRosettaTestInjectorProvider) class EnumValidatorTest implements RosettaIssueCodes { @Inject extension ValidationTestHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaNamespaceDescriptionValidatorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaNamespaceDescriptionValidatorTest.xtend index 2b9e266b0..9589dc1b6 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaNamespaceDescriptionValidatorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaNamespaceDescriptionValidatorTest.xtend @@ -3,7 +3,7 @@ */ package com.regnosys.rosetta.validation -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import com.regnosys.rosetta.tests.util.ModelHelper import org.eclipse.xtext.testing.InjectWith import org.eclipse.xtext.testing.extensions.InjectionExtension @@ -13,7 +13,7 @@ import org.junit.jupiter.api.^extension.ExtendWith import javax.inject.Inject @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class RosettaNamespaceDescriptionValidatorTest { @Inject extension ValidationTestHelper diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaValidatorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaValidatorTest.xtend index cf7553cf5..b3f1707a0 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaValidatorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaValidatorTest.xtend @@ -5,7 +5,7 @@ package com.regnosys.rosetta.validation import com.regnosys.rosetta.RosettaRuntimeModule import com.regnosys.rosetta.rosetta.simple.Data -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import com.regnosys.rosetta.tests.util.ModelHelper import org.eclipse.xtext.diagnostics.Diagnostic import org.eclipse.xtext.service.SingletonBinding @@ -24,7 +24,7 @@ import javax.inject.Inject import com.regnosys.rosetta.tests.util.ExpressionParser @ExtendWith(InjectionExtension) -@InjectWith(MyRosettaInjectorProvider) +@InjectWith(MyRosettaTestInjectorProvider) class RosettaValidatorTest implements RosettaIssueCodes { @Inject extension ValidationTestHelper @@ -39,7 +39,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { [True, False] '''.parseRosetta - model.assertError(CONDITION, null, "Expecting single cardinality. A condition should be single cardinality") + model.assertWarning(CONDITION, null, "Expecting single cardinality. A condition should be single cardinality") } @Test @@ -292,7 +292,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { default "someOtherValue" ''' .parseExpression(#[model], #["inEnum SomeEnum (1..*)"]) - .assertError(ROSETTA_EXPRESSION, null, "Expecting single cardinality") + .assertWarning(ROSETTA_EXPRESSION, null, "Expecting single cardinality") } @@ -738,7 +738,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { ins then val '''.parseRosetta - model.assertError(OPERATION, null, + model.assertWarning(OPERATION, null, "Expecting single cardinality. Cannot assign a list to a single value") } @@ -3520,11 +3520,11 @@ class RosettaValidatorTest implements RosettaIssueCodes { } } -class MyRosettaInjectorProvider extends RosettaInjectorProvider { +class MyRosettaTestInjectorProvider extends RosettaTestInjectorProvider { override createRuntimeModule() { return new RosettaRuntimeModule(){ override bindClassLoaderToInstance() { - return MyRosettaInjectorProvider + return MyRosettaTestInjectorProvider .getClassLoader(); } diff --git a/rosetta-tools/src/test/java/com/regnosys/rosetta/tools/minimalexampleproducer/UnnecessaryElementsRemoverTest.xtend b/rosetta-tools/src/test/java/com/regnosys/rosetta/tools/minimalexampleproducer/UnnecessaryElementsRemoverTest.xtend index d33cc3705..533314331 100644 --- a/rosetta-tools/src/test/java/com/regnosys/rosetta/tools/minimalexampleproducer/UnnecessaryElementsRemoverTest.xtend +++ b/rosetta-tools/src/test/java/com/regnosys/rosetta/tools/minimalexampleproducer/UnnecessaryElementsRemoverTest.xtend @@ -1,6 +1,6 @@ package com.regnosys.rosetta.tools.minimalexampleproducer -import com.regnosys.rosetta.tests.RosettaInjectorProvider +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import org.eclipse.xtext.testing.InjectWith import org.eclipse.xtext.testing.extensions.InjectionExtension import org.junit.jupiter.api.^extension.ExtendWith @@ -18,7 +18,7 @@ import org.eclipse.xtext.serializer.impl.Serializer import com.regnosys.rosetta.rosetta.RosettaRule @ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) +@InjectWith(RosettaTestInjectorProvider) class UnnecessaryElementsRemoverTest { @Inject UnnecessaryElementsRemover service @Inject Serializer serializer diff --git a/rosetta-tools/src/test/java/com/regnosys/rosetta/tools/modelimport/XsdImportTest.java b/rosetta-tools/src/test/java/com/regnosys/rosetta/tools/modelimport/XsdImportTest.java index 571edc44d..31e84329f 100644 --- a/rosetta-tools/src/test/java/com/regnosys/rosetta/tools/modelimport/XsdImportTest.java +++ b/rosetta-tools/src/test/java/com/regnosys/rosetta/tools/modelimport/XsdImportTest.java @@ -48,12 +48,12 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.regnosys.rosetta.builtin.RosettaBuiltinsService; -import com.regnosys.rosetta.tests.RosettaInjectorProvider; +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider; import com.regnosys.rosetta.types.builtin.RBuiltinTypeService; import com.rosetta.util.serialisation.RosettaXMLConfiguration; @ExtendWith(InjectionExtension.class) -@InjectWith(RosettaInjectorProvider.class) +@InjectWith(RosettaTestInjectorProvider.class) public class XsdImportTest { private static final String NAMESPACE = "test.ns"; From 2ae3d686180a4f44274548e39510c2b16c210ee8 Mon Sep 17 00:00:00 2001 From: Simon Cockx Date: Wed, 18 Dec 2024 17:22:23 +0100 Subject: [PATCH 10/10] Fixed merge issues --- .../tools/modelimport/XsdTypeImport.java | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/rosetta-tools/src/main/java/com/regnosys/rosetta/tools/modelimport/XsdTypeImport.java b/rosetta-tools/src/main/java/com/regnosys/rosetta/tools/modelimport/XsdTypeImport.java index e26dd3487..6e7613641 100644 --- a/rosetta-tools/src/main/java/com/regnosys/rosetta/tools/modelimport/XsdTypeImport.java +++ b/rosetta-tools/src/main/java/com/regnosys/rosetta/tools/modelimport/XsdTypeImport.java @@ -226,33 +226,6 @@ private void initialCompleteData(Data data, Stream abstractE choiceGroups.add(initialChoiceGroup); } abstractElements.forEach(elem -> registerXsdElementsRecursively(data, elem, initialChoiceGroup, choiceGroups, xsdMapping, result, config)); -<<<<<<< HEAD -======= - - // Add conditions - choiceGroups.forEach(choiceGroup -> { - if (choiceGroup.attributes.size() > 1) { - Condition choice = SimpleFactory.eINSTANCE.createCondition(); - choice.setName("Choice"); - // TODO: shouldn't the count of parent attributes also be taken into account? - if (choiceGroup.attributes.size() == data.getAttributes().size() && choiceGroup.required) { - OneOfOperation oneOf = ExpressionFactory.eINSTANCE.createOneOfOperation(); - oneOf.setOperator("one-of"); - choice.setExpression(oneOf); - } else { - ChoiceOperation op = ExpressionFactory.eINSTANCE.createChoiceOperation(); - op.setOperator("choice"); - op.setNecessity(choiceGroup.required ? Necessity.REQUIRED : Necessity.OPTIONAL); - op.getAttributes().addAll(choiceGroup.attributes); - choice.setExpression(op); - } - data.getConditions().add(choice); - } else if (choiceGroup.attributes.size() == 1 && choiceGroup.required) { - Attribute attr = choiceGroup.attributes.get(0); - attr.getCard().setInf(1); - } - }); ->>>>>>> 0ce292e904fba5f063ba8c1d7bc65c53d2db8a9a } @Override