فهرست منبع

Run type validation for all operation call arguments (#1483) (#1484)

Since parameters can be optional it is valid to have less arguments than parameters, hence we need to validate all given arguments.
Thomas Kutz 8 سال پیش
والد
کامیت
03b588eff3

+ 10 - 10
plugins/org.yakindu.base.expressions/src/org/yakindu/base/expressions/inferrer/ExpressionsTypeInferrer.java

@@ -298,17 +298,17 @@ public class ExpressionsTypeInferrer extends AbstractTypeSystemInferrer implemen
 			Map<TypeParameter, InferenceResult> typeParameterMapping, Operation operation, List<Expression> args,
 			IValidationIssueAcceptor acceptor) {
 		List<Parameter> parameters = operation.getParameters();
-		if (parameters.size() <= args.size()) {
 			for (int i = 0; i < parameters.size(); i++) {
-				Parameter parameter = parameters.get(i);
-				Expression argument = args.get(i);
-				InferenceResult parameterType = inferTypeDispatch(parameter);
-				InferenceResult argumentType = inferTypeDispatch(argument);
-				parameterType = typeParameterInferrer.buildInferenceResult(parameterType, typeParameterMapping,
-						acceptor);
-				assertAssignable(parameterType, argumentType,
-						String.format(INCOMPATIBLE_TYPES, argumentType, parameterType));
-			}
+				if (args.size() > i) {
+					Parameter parameter = parameters.get(i);
+					Expression argument = args.get(i);
+					InferenceResult parameterType = inferTypeDispatch(parameter);
+					InferenceResult argumentType = inferTypeDispatch(argument);
+					parameterType = typeParameterInferrer.buildInferenceResult(parameterType, typeParameterMapping,
+							acceptor);
+					assertAssignable(parameterType, argumentType,
+							String.format(INCOMPATIBLE_TYPES, argumentType, parameterType));
+				}
 		}
 		if (operation.isVariadic() && args.size() - 1 >= operation.getVarArgIndex()) {
 			Parameter parameter = operation.getParameters().get(operation.getVarArgIndex());

+ 39 - 0
test-plugins/org.yakindu.sct.model.stext.test/src/org/yakindu/sct/model/stext/test/TypeInferrerTest.java

@@ -16,13 +16,23 @@ import org.eclipse.xtext.junit4.InjectWith;
 import org.eclipse.xtext.junit4.XtextRunner;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.yakindu.base.expressions.expressions.Argument;
+import org.yakindu.base.expressions.expressions.ElementReferenceExpression;
 import org.yakindu.base.expressions.expressions.Expression;
+import org.yakindu.base.types.Parameter;
 import org.yakindu.base.types.inferrer.ITypeSystemInferrer;
+import org.yakindu.base.types.typesystem.ITypeSystem;
 import org.yakindu.sct.model.stext.stext.EventRaisingExpression;
+import org.yakindu.sct.model.stext.stext.OperationDefinition;
+import org.yakindu.sct.model.stext.stext.StextFactory;
 import org.yakindu.sct.model.stext.stext.VariableDefinition;
 import org.yakindu.sct.model.stext.test.util.AbstractTypeInferrerTest;
 import org.yakindu.sct.model.stext.test.util.STextInjectorProvider;
 import org.yakindu.sct.model.stext.test.util.STextTestScopeProvider;
+import org.yakindu.sct.model.stext.test.util.StextTestFactory;
+import org.yakindu.sct.model.stext.test.util.TypesTestFactory;
+
+import com.google.inject.Inject;
 
 /**
  * @author andreas muelder - Initial contribution and API
@@ -34,6 +44,9 @@ import org.yakindu.sct.model.stext.test.util.STextTestScopeProvider;
 @InjectWith(STextInjectorProvider.class)
 public class TypeInferrerTest extends AbstractTypeInferrerTest {
 
+	@Inject
+	protected TypesTestFactory typesFactory;
+	
 	// Unary
 	@Test
 	public void testNumericalUnaryExpressionSuccess() {
@@ -842,6 +855,32 @@ public class TypeInferrerTest extends AbstractTypeInferrerTest {
 		
 	}
 
+	@Test
+	public void testOperationCallWithOptionalParameter() {
+		OperationDefinition opDef = StextTestFactory._createOperation("opWithOptionals",
+				StextFactory.eINSTANCE.createInternalScope());
+
+		Parameter pReq = typesFactory.createParameter("pReq", ITypeSystem.INTEGER, false);
+		Parameter pOpt = typesFactory.createParameter("pOpt", ITypeSystem.INTEGER, true);
+		opDef.getParameters().add(pReq);
+		opDef.getParameters().add(pOpt);
+
+		Argument boolArg = (Argument) parseExpression("true", Argument.class.getSimpleName());
+		Argument intArg = (Argument) parseExpression("17", Argument.class.getSimpleName());
+
+		// opWithOptionals(17, 17) => valid
+		ElementReferenceExpression opCall1 = StextTestFactory._createOperationCall(opDef, intArg, intArg);
+		expectNoErrors(opCall1);
+
+		// opWithOptionals(17) => valid, because of optional parameter
+		ElementReferenceExpression opCall2 = StextTestFactory._createOperationCall(opDef, intArg);
+		expectNoErrors(opCall2);
+
+		// opWithOptionals(true) => invalid
+		ElementReferenceExpression opCall3 = StextTestFactory._createOperationCall(opDef, boolArg);
+		expectError(opCall3, ITypeSystemInferrer.NOT_COMPATIBLE_CODE);
+	}
+
 	@Test
 	public void testParenthesizedExpression() {
 		assertTrue(isBooleanType(inferType("( true || false )")));

+ 48 - 5
test-plugins/org.yakindu.sct.model.stext.test/src/org/yakindu/sct/model/stext/test/util/AbstractTypeInferrerTest.java

@@ -109,31 +109,70 @@ public abstract class AbstractTypeInferrerTest extends AbstractSTextTest {
 						.iterator().next().getMessage());
 	}
 	
+	protected void expectNoErrors(EObject element) {
+		ListBasedValidationIssueAcceptor diagnostics = validate(element);
+		assertNoErrors(diagnostics);
+	}
+	
 	protected void expectNoErrors(String expression, String scope) {
 		ListBasedValidationIssueAcceptor diagnostics = validate(expression, scope);
+		assertNoErrors(diagnostics);
+	}
+	
+	protected void assertNoErrors(ListBasedValidationIssueAcceptor diagnostics) {
 		List<ValidationIssue> errors = diagnostics.getTraces(Severity.ERROR);
 		assertEquals(errors.toString(), 0, errors.size());
 	}
 	
+	protected void expectWarning(EObject element, String code) {
+		ListBasedValidationIssueAcceptor diagnostics = validate(element);
+		assertWarning(diagnostics, code);
+	}
+	
 	protected void expectWarning(String expression, String scope, String code) {
-		List<ValidationIssue> traces = validate(expression, scope).getTraces(Severity.WARNING);
+		ListBasedValidationIssueAcceptor diagnostics = validate(expression, scope);
+		assertWarning(diagnostics, code);
+	}
+
+	protected void assertWarning(ListBasedValidationIssueAcceptor diagnostics, String code) {
+		List<ValidationIssue> traces = diagnostics.getTraces(Severity.WARNING);
 		List<ValidationIssue> issues = filterForIssueCode(traces, code);
 		assertEquals(Arrays.toString(traces.toArray()), 1, issues.size());
 	}
+
+	protected void expectError(EObject element, String code) {
+		ListBasedValidationIssueAcceptor diagnostics = validate(element);
+		assertError(diagnostics, code);
+	}
 	
 	protected void expectError(String expression, String scope, String code) {
-		List<ValidationIssue> traces = validate(expression, scope).getTraces(Severity.ERROR);
+		ListBasedValidationIssueAcceptor diagnostics = validate(expression, scope);
+		assertError(diagnostics, code);
+	}
+
+	protected void assertError(ListBasedValidationIssueAcceptor diagnostics, String code) {
+		List<ValidationIssue> traces = diagnostics.getTraces(Severity.ERROR);
 		List<ValidationIssue> issues = filterForIssueCode(traces, code);
 		assertEquals(Arrays.toString(traces.toArray()), 1, issues.size());
 	}
 	
+	protected void expectErrors(EObject element, String code, int noOfErrors) {
+		ListBasedValidationIssueAcceptor diagnostics = validate(element);
+		assertErrors(diagnostics, code, noOfErrors);
+	}
+	
 	protected void expectErrors(String expression, String scope, String code, int noOfErrors) {
-		List<ValidationIssue> traces = validate(expression, scope).getTraces(Severity.ERROR);
+		ListBasedValidationIssueAcceptor diagnostics = validate(expression, scope);
+		assertErrors(diagnostics, code, noOfErrors);
+	}
+
+	protected void assertErrors(ListBasedValidationIssueAcceptor diagnostics, String code, int noOfErrors) {
+		List<ValidationIssue> traces = diagnostics.getTraces(Severity.ERROR);
 		List<ValidationIssue> issues = filterForIssueCode(traces, code);
 		assertEquals(Arrays.toString(traces.toArray()), noOfErrors, issues.size());
 	}
 	
-	private List<ValidationIssue> filterForIssueCode(List<ValidationIssue> traces, final String code) {
+	protected List<ValidationIssue> filterForIssueCode(List<ValidationIssue> traces, final String code) {
 		return Lists.newArrayList(Iterables.filter(traces, new Predicate<ValidationIssue>() {
 			@Override
 			public boolean apply(ValidationIssue input) {
@@ -144,8 +183,12 @@ public abstract class AbstractTypeInferrerTest extends AbstractSTextTest {
 	
 	protected ListBasedValidationIssueAcceptor validate(String expression, String scope) {
 		EObject exp = parseExpression(expression, Expression.class.getSimpleName(), scope);
+		return validate(exp);
+	}
+	
+	protected ListBasedValidationIssueAcceptor validate(EObject element) {
 		ListBasedValidationIssueAcceptor diagnostics = new ListBasedValidationIssueAcceptor();
-		typeInferrer.infer(exp, diagnostics);
+		typeInferrer.infer(element, diagnostics);
 		return diagnostics;
 	}
 }

+ 11 - 0
test-plugins/org.yakindu.sct.model.stext.test/src/org/yakindu/sct/model/stext/test/util/StextTestFactory.java

@@ -12,7 +12,10 @@
 package org.yakindu.sct.model.stext.test.util;
 
 
+import java.util.Arrays;
+
 import org.yakindu.base.base.NamedElement;
+import org.yakindu.base.expressions.expressions.Argument;
 import org.yakindu.base.expressions.expressions.AssignmentExpression;
 import org.yakindu.base.expressions.expressions.AssignmentOperator;
 import org.yakindu.base.expressions.expressions.BoolLiteral;
@@ -323,6 +326,14 @@ public class StextTestFactory extends StextFactoryImpl {
 		oc.setOperationCall(true);
 		return oc;
 	}
+	
+	public static ElementReferenceExpression _createOperationCall(OperationDefinition o, Argument... arguments) {
+		ElementReferenceExpression oc = ExpressionsFactory.eINSTANCE.createElementReferenceExpression();
+		oc.setReference(o);
+		oc.setOperationCall(true);
+		oc.getArguments().addAll(Arrays.asList(arguments));
+		return oc;
+	}
 
 	public static PrimitiveValueExpression _createValue(int i) {
 		PrimitiveValueExpression assignment = ExpressionsFactory.eINSTANCE

+ 15 - 2
test-plugins/org.yakindu.sct.model.stext.test/src/org/yakindu/sct/model/stext/test/util/TypesTestFactory.xtend

@@ -33,17 +33,30 @@ class TypesTestFactory {
 	}
 
 	def createParameter(String name, String typeName) {
-		createParameter(name, toTypeSpecifier(ts.getType(typeName)));
+		createParameter(name, typeName, false);
+	}
+	
+	def createParameter(String name, String typeName, boolean isOptional) {
+		createParameter(name, toTypeSpecifier(ts.getType(typeName)), isOptional);
 	}
 
 	def createParameter(String name, Type type) {
-		createParameter(name, type.toTypeSpecifier);
+		createParameter(name, type.toTypeSpecifier, false);
+	}
+	
+	def createParameter(String name, Type type, boolean isOptional) {
+		createParameter(name, type.toTypeSpecifier, isOptional);
 	}
 
 	def createParameter(String name, TypeSpecifier typeSpec) {
+		createParameter(name, typeSpec, false)
+	}
+	
+	def createParameter(String name, TypeSpecifier typeSpec, boolean isOptional) {
 		factory.createParameter => [
 			it.name = name
 			it.typeSpecifier = typeSpec
+			it.optional = isOptional
 		]
 	}