فهرست منبع

Type inference for generic type parameters (#1334)

Commit messages from squashed merge:

* Type Inferrer for template functions

* WIP on generic Type Inferrer

* Refactor test model factories

* Move root package creation to test model factory

* Work in progress on generic type inference

* Moved packages to superclass

* Add Xtend to pom.xml

* Add xtend-gen to build properties

* WIP on TypeInferrer for feature call

* Add TypeInferrer for feature calls

* Move error messages to constants

* Fix commit 807e928

* Extract type parameter lookup messages into TypeParameterResolver

* Change visibility of non-API methods

* Replace dispatch with good oldschool instanceof

* It worked, then we refactored.

* Add copyright header

* Fixed Operation Type Inferrer

* Extracted Type Validation to new class for reuse

* Change Binding Validation to use new TypeValidator

* Consistent usage of names 'inferrer' and 'infer' instead of 'resolver' and 'resolve'

* Fix inference for properties with complex generic types

* Remove redundant methods

* Consolidate error messages

* Extract method to retrieve operation arguments for subclassing

This is handy in scenarios where operations are handled as extension methods on their first argument type. In such a case the argument list needs also to contain the operation caller and thus has to be extended.

* Avoid NullPointerException when writing new operation that does not have a name yet

* Refactor TypeValidator

* Add comment

* Fix missing `!` operator

* Fix parameter compatibility validation

I found out that the TypeInferrer would only throw a warning when the user
tried to assign something like an integer to a parameter of type
ComplexType<T>, saying that T could not be inferred. This is of cause not the
real problem, as giving an integer to a ComplexType Parameter should not be
possible at all.
Rearranging some checks and exceptions fixed the problem.

* Remove Exceptions from TypeParameterInferrer

* Fix SoftValidator, prevent inferral of TypeParameters for not parameterized types

* Remove sysouts
Rene Beckmann 8 سال پیش
والد
کامیت
7c4dffd748
19فایلهای تغییر یافته به همراه1236 افزوده شده و 339 حذف شده
  1. 94 27
      plugins/org.yakindu.base.expressions/src/org/yakindu/base/expressions/inferrer/ExpressionsTypeInferrer.java
  2. 3 0
      plugins/org.yakindu.base.expressions/src/org/yakindu/base/expressions/inferrer/ExpressionsTypeInferrerMessages.java
  3. 278 0
      plugins/org.yakindu.base.expressions/src/org/yakindu/base/expressions/inferrer/TypeParameterInferrer.xtend
  4. 3 0
      plugins/org.yakindu.base.types.edit/src/org/yakindu/base/types/provider/OperationItemProvider.java
  5. 25 57
      plugins/org.yakindu.base.types/src/org/yakindu/base/types/inferrer/AbstractTypeSystemInferrer.java
  6. 2 1
      plugins/org.yakindu.base.types/src/org/yakindu/base/types/inferrer/ITypeSystemInferrer.java
  7. 20 0
      plugins/org.yakindu.base.types/src/org/yakindu/base/types/validation/TypeValidationError.java
  8. 111 0
      plugins/org.yakindu.base.types/src/org/yakindu/base/types/validation/TypeValidator.java
  9. 46 46
      plugins/org.yakindu.sct.ui.editor/src/org/yakindu/sct/ui/editor/validation/DefaultValidationIssueStore.java
  10. 1 0
      test-plugins/org.yakindu.sct.model.stext.test/.classpath
  11. 2 1
      test-plugins/org.yakindu.sct.model.stext.test/.gitignore
  12. 6 0
      test-plugins/org.yakindu.sct.model.stext.test/.project
  13. 2 1
      test-plugins/org.yakindu.sct.model.stext.test/build.properties
  14. 29 0
      test-plugins/org.yakindu.sct.model.stext.test/pom.xml
  15. 105 1
      test-plugins/org.yakindu.sct.model.stext.test/src/org/yakindu/sct/model/stext/test/TypeInferrerTest.java
  16. 6 0
      test-plugins/org.yakindu.sct.model.stext.test/src/org/yakindu/sct/model/stext/test/util/AbstractTypeInferrerTest.java
  17. 0 205
      test-plugins/org.yakindu.sct.model.stext.test/src/org/yakindu/sct/model/stext/test/util/STextTestScopeProvider.java
  18. 402 0
      test-plugins/org.yakindu.sct.model.stext.test/src/org/yakindu/sct/model/stext/test/util/STextTestScopeProvider.xtend
  19. 101 0
      test-plugins/org.yakindu.sct.model.stext.test/src/org/yakindu/sct/model/stext/test/util/TypesTestFactory.xtend

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

@@ -20,9 +20,11 @@ import static org.yakindu.base.types.typesystem.ITypeSystem.VOID;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 import org.eclipse.emf.common.util.EList;
 import org.eclipse.xtext.EcoreUtil2;
+import org.yakindu.base.expressions.expressions.ArgumentExpression;
 import org.yakindu.base.expressions.expressions.AssignmentExpression;
 import org.yakindu.base.expressions.expressions.BitwiseAndExpression;
 import org.yakindu.base.expressions.expressions.BitwiseOrExpression;
@@ -61,12 +63,18 @@ import org.yakindu.base.types.TypeAlias;
 import org.yakindu.base.types.TypeParameter;
 import org.yakindu.base.types.TypeSpecifier;
 import org.yakindu.base.types.inferrer.AbstractTypeSystemInferrer;
+import org.yakindu.base.types.validation.IValidationIssueAcceptor;
+
+import com.google.common.collect.Maps;
+import com.google.inject.Inject;
 
 /**
  * @author andreas muelder - Initial contribution and API
  * 
  */
 public class ExpressionsTypeInferrer extends AbstractTypeSystemInferrer implements ExpressionsTypeInferrerMessages {
+	@Inject
+	protected TypeParameterInferrer typeParameterInferrer;
 
 	public InferenceResult doInfer(AssignmentExpression e) {
 		InferenceResult result1 = inferTypeDispatch(e.getVarRef());
@@ -203,62 +211,122 @@ public class ExpressionsTypeInferrer extends AbstractTypeSystemInferrer implemen
 	}
 
 	public InferenceResult doInfer(FeatureCall e) {
+		// map to hold inference results for type parameters
+		Map<TypeParameter, InferenceResult> inferredTypeParameterTypes = Maps.newHashMap();
+		typeParameterInferrer.inferTypeParametersFromOwner(inferTypeDispatch(e.getOwner()), inferredTypeParameterTypes);
+
 		if (e.isOperationCall()) {
-			Operation operation = (Operation) e.getFeature();
-			EList<Expression> args = e.getArgs();
-			inferParameter(operation, args, e.getOwner());
+			if (!e.getFeature().eIsProxy()) {
+				return inferOperation(e, (Operation) e.getFeature(), inferredTypeParameterTypes);
+			} else {
+				return getAnyType();
+			}
 		}
 		InferenceResult result = inferTypeDispatch(e.getFeature());
-		if (result != null && result.getType() instanceof TypeParameter) {
-			result = inferTypeParameter((TypeParameter) result.getType(), inferTypeDispatch(e.getOwner()));
+		if (result != null) {
+			result = typeParameterInferrer.buildInferenceResult(result, inferredTypeParameterTypes, acceptor);
+		}
+		if (result == null) {
+			return getAnyType();
 		}
 		return result;
 	}
 
 	public InferenceResult doInfer(ElementReferenceExpression e) {
 		if (e.isOperationCall()) {
-			Operation operation = (Operation) e.getReference();
-			EList<Expression> args = e.getArgs();
-			inferParameter(operation, args, null);
+			if (!e.getReference().eIsProxy()) {
+				return inferOperation(e, (Operation) e.getReference(),
+						Maps.<TypeParameter, InferenceResult> newHashMap());
+			} else {
+				return getAnyType();
+			}
 		}
 		return inferTypeDispatch(e.getReference());
 	}
 
-	public void inferParameter(Operation operation, EList<Expression> args, Expression operationOwner) {
-		EList<Parameter> parameters = operation.getParameters();
+	protected InferenceResult inferOperation(ArgumentExpression e, Operation op,
+			Map<TypeParameter, InferenceResult> typeParameterMapping) {
+		// resolve type parameter from operation call
+		List<InferenceResult> argumentTypes = getArgumentTypes(getOperationArguments(e));
+		List<Parameter> parameters = op.getParameters();
+		typeParameterInferrer.inferTypeParametersFromOperationArguments(parameters, argumentTypes, typeParameterMapping,
+				acceptor);
+		validateParameters(typeParameterMapping, op, getOperationArguments(e), acceptor);
+		return inferReturnType(op, typeParameterMapping);
+	}
+
+	/**
+	 * Can be extended to e.g. add operation caller to argument list for
+	 * extension methods
+	 */
+	protected List<Expression> getOperationArguments(ArgumentExpression e) {
+		return e.getArgs();
+	}
+
+	protected List<InferenceResult> getArgumentTypes(List<Expression> args) {
+		List<InferenceResult> argumentTypes = new ArrayList<>();
+		for (Expression arg : args) {
+			argumentTypes.add(inferTypeDispatch(arg));
+		}
+		return argumentTypes;
+	}
+
+	protected InferenceResult inferReturnType(Operation operation,
+			Map<TypeParameter, InferenceResult> inferredTypeParameterTypes) {
+		InferenceResult returnType = inferTypeDispatch(operation);
+			returnType = typeParameterInferrer.buildInferenceResult(returnType, inferredTypeParameterTypes, acceptor);
+			if(returnType == null) {
+				return getAnyType();
+			}
+		return returnType;
+	}
+
+	private InferenceResult getAnyType() {
+		return InferenceResult.from(registry.getType(ANY));
+	}
+
+	/**
+	 * Takes the operation parameter type and performs a lookup for all
+	 * contained type parameters by using the given type parameter inference
+	 * map.<br>
+	 * The parameter types are validated against the operation call's argument
+	 * types.
+	 * 
+	 * @throws TypeParameterInferrenceException
+	 */
+	protected Map<TypeParameter, InferenceResult> validateParameters(
+			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++) {
-				assertArgumentIsCompatible(operationOwner, parameters.get(i), args.get(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);
+				assertCompatible(argumentType, parameterType,
+						String.format(INCOMPATIBLE_TYPES, argumentType, parameterType));
 			}
 		}
 		if (operation.isVariadic() && args.size() - 1 >= operation.getVarArgIndex()) {
 			Parameter parameter = operation.getParameters().get(operation.getVarArgIndex());
 			List<Expression> varArgs = args.subList(operation.getVarArgIndex(), args.size() - 1);
 			for (Expression expression : varArgs) {
-				assertArgumentIsCompatible(operationOwner, parameter, expression);
+				// TODO: handle op(T...)
+				assertArgumentIsCompatible(parameter, expression);
 			}
 		}
+		return typeParameterMapping;
 	}
 
-	protected void assertArgumentIsCompatible(Expression operationOwner, Parameter parameter, Expression argument) {
+	protected void assertArgumentIsCompatible(Parameter parameter, Expression argument) {
 		InferenceResult result1 = inferTypeDispatch(parameter);
-		if (operationOwner != null && result1 != null && result1.getType() instanceof TypeParameter) {
-			result1 = inferTypeParameter((TypeParameter) result1.getType(), inferTypeDispatch(operationOwner));
-		}
 		InferenceResult result2 = inferTypeDispatch(argument);
 		assertCompatible(result2, result1, String.format(INCOMPATIBLE_TYPES, result2, result1));
 	}
 
-	protected InferenceResult inferTypeParameter(TypeParameter typeParameter, InferenceResult ownerResult) {
-		if (ownerResult.getBindings().isEmpty() || !(ownerResult.getType() instanceof GenericElement)) {
-			return getResultFor(ANY);
-		} else {
-			int index = ((GenericElement) ownerResult.getType()).getTypeParameters().indexOf(typeParameter);
-			return InferenceResult.from(ownerResult.getBindings().get(index).getType(),
-					ownerResult.getBindings().get(index).getBindings());
-		}
-	}
-
 	public InferenceResult doInfer(ParenthesizedExpression e) {
 		return inferTypeDispatch(e.getExpression());
 	}
@@ -324,6 +392,5 @@ public class ExpressionsTypeInferrer extends AbstractTypeSystemInferrer implemen
 			return InferenceResult.from(type, bindings);
 		}
 		return inferTypeDispatch(specifier.getType());
-
 	}
 }

+ 3 - 0
plugins/org.yakindu.base.expressions/src/org/yakindu/base/expressions/inferrer/ExpressionsTypeInferrerMessages.java

@@ -30,5 +30,8 @@ public interface ExpressionsTypeInferrerMessages {
 	public static final String CAN_NOT_CONVERT = "%s cannot be converted to '%s'.";
 	public static final String INCOMPATIBLE_TYPES = "Incompatible types %s and %s.";
 	public static final String VARIABLE_VOID_TYPE = "'void' is an invalid type for variables";
+	public static final String INFER_COMMON_TYPE = "Could not infer common type for type parameter %s from argument types %s.";
+	public static final String INFER_TYPE_PARAMETER = "Could not infer type for type parameter %s.";
+	public static final String INFER_RETURN_TYPE_PARAMETER = "Could not infer type for return type parameter %s, returning ANY instead.";
 
 }

+ 278 - 0
plugins/org.yakindu.base.expressions/src/org/yakindu/base/expressions/inferrer/TypeParameterInferrer.xtend

@@ -0,0 +1,278 @@
+/**
+ * Copyright (c) 2017 committers of YAKINDU and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * Contributors:
+ * 	Thomas Kutz, René Beckmann - initial API and implementation
+ * 
+ */
+package org.yakindu.base.expressions.inferrer
+
+import com.google.inject.Inject
+import java.util.List
+import java.util.Map
+import org.yakindu.base.types.GenericElement
+import org.yakindu.base.types.Parameter
+import org.yakindu.base.types.Type
+import org.yakindu.base.types.TypeParameter
+import org.yakindu.base.types.TypeSpecifier
+import org.yakindu.base.types.Property
+import org.yakindu.base.types.inferrer.ITypeSystemInferrer.InferenceResult
+import org.yakindu.base.types.typesystem.ITypeSystem
+import org.yakindu.base.types.validation.IValidationIssueAcceptor
+import org.yakindu.base.types.validation.IValidationIssueAcceptor.ValidationIssue
+import org.yakindu.base.types.validation.IValidationIssueAcceptor.ValidationIssue.Severity
+import org.yakindu.base.types.validation.TypeValidator
+
+import static org.yakindu.base.expressions.inferrer.ExpressionsTypeInferrerMessages.*
+import static org.yakindu.base.types.inferrer.ITypeSystemInferrer.NOT_INFERRABLE_TYPE_PARAMETER_CODE
+import static org.yakindu.base.types.inferrer.ITypeSystemInferrer.NOT_COMPATIBLE_CODE
+import org.yakindu.base.types.validation.TypeValidationError
+import org.yakindu.base.types.PrimitiveType
+import org.yakindu.base.types.ComplexType
+
+/**
+ * Infers the actual type for a type parameter used in generic elements like operations or complex types.
+ * <p>
+ * For a generic operation, the actual type is inferred from the arguments used in the operation call:
+ * <ul>
+ * <li><code>op(p: T)</code> called as <code>op(10)</code> implies that <code>T</code> is <code>integer</code></li>
+ * <li><code>op(p1: T, p2: T)</code> called as <code>op(10, 1.5)</code> implies that <code>T</code> is <code>real</code> (common type of <code>integer</code> and <code>real</code>)</li>
+ * </ul>
+ * 
+ * For a generic type, the actual type of its type parameter is inferred from the type instantiation:
+ * <ul>
+ * <li><code>CPT&lt;T&gt;</code> instantiated as <code>var x : CPT&lt;integer&gt;</code> implies that <code>T</code> is <code>integer</code></li>
+ * <li><code>CPT&lt;T&gt;</code> instantiated as <code>var x : CPT&lt;List&lt;integer&gt;&gt;</code> implies that <code>T</code> is <code>List&lt;integer&gt;</code></li>
+ * </ul>
+ */
+class TypeParameterInferrer {
+
+	@Inject ITypeSystem registry;
+	@Inject TypeValidator typeValidator;
+
+	/**
+	 * Goes through the parameters and the arguments of the operation and the operation call
+	 * and tries to infer the type of the TypeParameters for this operation call.
+	 * 
+	 * @param parameters the parameters of the operation
+	 * @param arguments the inferred types of operation call's arguments
+	 * @param inferredTypeParameterTypes map of type parameters to their inference result which will be filled by this method
+	 */
+	def void inferTypeParametersFromOperationArguments(List<Parameter> parameters, List<InferenceResult> arguments,
+		Map<TypeParameter, InferenceResult> inferredTypeParameterTypes, IValidationIssueAcceptor acceptor) {
+		if (parameters.size() <= arguments.size()) {
+			for (var i = 0; i < parameters.size(); i++) {
+				val parameter = parameters.get(i).typeSpecifier;
+				val argument = arguments.get(i);
+				if (parameterContainsTypeParameter(parameter) && assertArgumentAndParameterSoftCompatible(argument, parameter, acceptor)) {
+					inferTypeParameterFromOperationArgument(parameter, argument, inferredTypeParameterTypes, acceptor);
+				}
+			}
+		}
+	}
+	
+	def boolean parameterContainsTypeParameter(TypeSpecifier specifier) {
+		val type = specifier.type
+		if (type instanceof PrimitiveType) {
+			return false
+		} 
+		if(type instanceof TypeParameter) {
+			return true
+		} 
+		if(type instanceof ComplexType) {
+			val complexType = type as ComplexType
+			if(complexType.typeParameters != null) {
+				return true;
+			} else {
+				for(prop : complexType.features.filter(Property)) {
+					if(prop.typeSpecifier.parameterContainsTypeParameter) {
+						return true
+					}
+				}
+			}
+		}
+		return false
+	}
+
+	def protected void inferTypeParameterFromOperationArgument(TypeSpecifier parameterTypeSpecifier,
+		InferenceResult argumentType, Map<TypeParameter, InferenceResult> inferredTypeParameterTypes,
+		IValidationIssueAcceptor acceptor) {
+		val parameterType = parameterTypeSpecifier.getType()
+
+		if (parameterType instanceof TypeParameter) {
+			doInferTypeParameterFromOperationArgument(parameterType, argumentType, inferredTypeParameterTypes, acceptor)
+		} else if (parameterType instanceof GenericElement) {
+			doInferGenericTypeFromOperationArgument(parameterTypeSpecifier, argumentType, inferredTypeParameterTypes,
+				acceptor)
+		}
+	}
+
+	def protected doInferGenericTypeFromOperationArgument(TypeSpecifier parameterTypeSpecifier,
+		InferenceResult argumentType, Map<TypeParameter, InferenceResult> inferredTypeParameterTypes,
+		IValidationIssueAcceptor acceptor) {
+		for (var i = 0; i < parameterTypeSpecifier.getTypeArguments().size(); i++) {
+			val typeParameter = parameterTypeSpecifier.getTypeArguments().get(i);
+			if (argumentType.getBindings().size() <= i) {
+				acceptor.error(typeParameter, NOT_INFERRABLE_TYPE_PARAMETER_CODE)
+			} else {
+				val typeArgument = argumentType.getBindings().get(i);
+				inferTypeParameterFromOperationArgument(typeParameter, typeArgument, inferredTypeParameterTypes,
+					acceptor);
+			}
+		}
+	}
+
+	def protected doInferTypeParameterFromOperationArgument(TypeParameter typeParameter, InferenceResult argumentType,
+		Map<TypeParameter, InferenceResult> inferredTypeParameterTypes, IValidationIssueAcceptor acceptor) {
+
+		val newMappedType = argumentType.getType();
+		val typeInMap = inferredTypeParameterTypes.get(typeParameter);
+		if (typeInMap == null) {
+			inferredTypeParameterTypes.put(typeParameter,
+				InferenceResult.from(newMappedType, argumentType.getBindings()));
+		} else {
+			val commonType = getCommonType(argumentType, typeInMap);
+			val errorMsg = String.format(INCOMPATIBLE_TYPES, argumentType.toString, typeInMap.toString)
+			val errors = typeValidator.assertTypeBindingsSame(argumentType, typeInMap, errorMsg)
+			if (commonType == null || !errors.empty) {
+				inferredTypeParameterTypes.put(typeParameter, null);
+				if (!errors.empty) {
+					errors.forEach [
+						acceptor.error(message, errorCode)
+					]
+				}
+				acceptor.error(
+					String.format(INFER_COMMON_TYPE, typeParameter.name,
+						newArrayList(argumentType.type.name, typeInMap.type.name)), NOT_INFERRABLE_TYPE_PARAMETER_CODE)
+			} else {
+				inferredTypeParameterTypes.put(typeParameter,
+					InferenceResult.from(commonType, argumentType.getBindings()));
+			}
+		}
+	}
+
+	def protected Type getCommonType(InferenceResult type1, InferenceResult type2) {
+		val result = registry.getCommonType(type1.getType(), type2.getType())
+		return result
+	}
+
+	/**
+	 * For a given inference result with potentially unresolved type parameters a new inference result is built by 
+	 * resolving type parameters based on the given type parameter inference map.
+	 * For generic types this method calls itself recursively to fill all nested type parameters.
+	 */
+	def protected InferenceResult buildInferenceResult(InferenceResult oldInferenceResult,
+		Map<TypeParameter, InferenceResult> inferredTypeParameterTypes, IValidationIssueAcceptor acceptor) {
+		if (oldInferenceResult.getType() instanceof TypeParameter) {
+			// get already inferred type from type parameter map
+			val typeParameter = oldInferenceResult.getType() as TypeParameter
+			val mappedType = inferredTypeParameterTypes.get(typeParameter);
+			if (mappedType == null) {
+				acceptor.warning(typeParameter, NOT_INFERRABLE_TYPE_PARAMETER_CODE)
+				return null
+			} else {
+				return mappedType;
+			}
+		} else if (oldInferenceResult.getType() instanceof GenericElement) {
+			val List<InferenceResult> newBindings = newArrayList()
+			for (InferenceResult oldBinding : oldInferenceResult.getBindings()) {
+				newBindings.add(buildInferenceResult(oldBinding, inferredTypeParameterTypes, acceptor))
+			}
+			val result = InferenceResult.from(oldInferenceResult.getType(), newBindings);
+			return result;
+		}
+		return oldInferenceResult;
+	}
+
+	def void inferTypeParametersFromOwner(InferenceResult operationOwnerResult,
+		Map<TypeParameter, InferenceResult> inferredTypeParameterTypes) {
+		var operationOwnerType = operationOwnerResult.getType()
+		if (operationOwnerType instanceof GenericElement) {
+			for (var i = 0; i < operationOwnerType.typeParameters.size() &&
+				i < operationOwnerResult.bindings.size(); i++) { // T1, T2...
+				val typeParameter = operationOwnerType.typeParameters.get(i)
+				val binding = operationOwnerResult.bindings.get(i) // integer, boolean...
+				inferredTypeParameterTypes.put(typeParameter, binding)
+			}
+		}
+	}
+
+	/**
+	 * Asserts that two types are <i>roughly</i> compatible.<br>
+	 * These are compatible:
+	 * <ul>
+	 * <li><code>ComplexType&lt;integer&gt;</code></li>
+	 * <li> <code>ComplexType&lt;T&gt;</code></li>
+	 * </ul>
+	 * These are as well:
+	 * <ul>
+	 * <li><code>T</code></li>
+	 * <li><code>integer</code></li>
+	 * </ul>
+	 * These are not:
+	 * <ul>
+	 * <li><code>ComplexType&lt;T&gt;</code></li>
+	 * <li><code>integer</code></li>
+	 * </ul>
+	 */
+	def assertArgumentAndParameterSoftCompatible(InferenceResult argumentResult, TypeSpecifier parameter,
+		IValidationIssueAcceptor acceptor) {
+		// I can't think of anything that's not compatible to a TypeParameter, so...
+		if (parameter.type instanceof TypeParameter) {
+			return true
+		}
+		var result1 = InferenceResult.from(argumentResult.type) // ignore bindings
+		val result2 = InferenceResult.from(parameter.type)
+		val errors = typeValidator.assertCompatible(result1, result2, null)
+		// check for correct number of TypeParameters / Argument's type parameters
+		if (errors.empty && parameter.typeArguments != null &&
+			parameter.typeArguments.size != argumentResult.bindings.size) {
+			// build temporary binding list for error message
+			val bindings = parameter.typeArguments.map [
+				InferenceResult.from(type)
+			]
+			errors.add(
+				new TypeValidationError(
+					String.format(INCOMPATIBLE_TYPES, argumentResult, InferenceResult.from(parameter.type, bindings)),
+					NOT_COMPATIBLE_CODE))
+		}
+		if (!errors.empty) {
+			errors.forEach [
+				acceptor.error(message, errorCode)
+			]
+			return false
+		}
+		return true
+	}
+
+	def error(IValidationIssueAcceptor acceptor, String msg, String issueCode) {
+		acceptor.accept(new ValidationIssue(Severity.ERROR, msg, issueCode));
+	}
+
+	def error(IValidationIssueAcceptor acceptor, TypeSpecifier typeSpecifier, String issueCode) {
+		acceptor.accept(
+			new ValidationIssue(Severity.ERROR, String.format(INFER_TYPE_PARAMETER, typeSpecifier.type.name),
+				issueCode));
+	}
+
+	def error(IValidationIssueAcceptor acceptor, TypeParameter typeParameter, String issueCode) {
+		acceptor.accept(
+			new ValidationIssue(Severity.ERROR, String.format(INFER_TYPE_PARAMETER, typeParameter.name),
+				issueCode));
+	}
+
+	def warning(IValidationIssueAcceptor acceptor, String msg, String issueCode) {
+		acceptor.accept(new ValidationIssue(Severity.WARNING, msg, issueCode))
+	}
+
+	def warning(IValidationIssueAcceptor acceptor, TypeParameter typeParameter, String issueCode) {
+		acceptor.accept(
+			new ValidationIssue(Severity.WARNING, String.format(INFER_TYPE_PARAMETER, typeParameter.name),
+				issueCode));
+	}
+
+}
+		

+ 3 - 0
plugins/org.yakindu.base.types.edit/src/org/yakindu/base/types/provider/OperationItemProvider.java

@@ -131,6 +131,9 @@ public class OperationItemProvider extends DeclarationItemProvider {
 	@Override
 	public String getText(Object object) {
 		Operation operation = (Operation) object;
+		if (operation.getName() == null) {
+			return "null";
+		}
 		StringBuilder builder = new StringBuilder(operation.getName());
 		builder.append("(");
 		EList<Parameter> parameters = operation.getParameters();

+ 25 - 57
plugins/org.yakindu.base.types/src/org/yakindu/base/types/inferrer/AbstractTypeSystemInferrer.java

@@ -13,11 +13,9 @@ package org.yakindu.base.types.inferrer;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.List;
 
 import org.eclipse.emf.ecore.EObject;
 import org.eclipse.xtext.util.PolymorphicDispatcher;
-import org.yakindu.base.types.ComplexType;
 import org.yakindu.base.types.Type;
 import org.yakindu.base.types.TypeAlias;
 import org.yakindu.base.types.typesystem.ITypeSystem;
@@ -25,6 +23,8 @@ import org.yakindu.base.types.validation.IValidationIssueAcceptor;
 import org.yakindu.base.types.validation.IValidationIssueAcceptor.ListBasedValidationIssueAcceptor;
 import org.yakindu.base.types.validation.IValidationIssueAcceptor.ValidationIssue;
 import org.yakindu.base.types.validation.IValidationIssueAcceptor.ValidationIssue.Severity;
+import org.yakindu.base.types.validation.TypeValidationError;
+import org.yakindu.base.types.validation.TypeValidator;
 
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
@@ -39,16 +39,18 @@ public abstract class AbstractTypeSystemInferrer implements ITypeSystemInferrer
 
 	protected static final String NO_INFER_METHOD = "No infer method for type(s) %s";
 	protected static final String ASSERT_IS_TYPE = "Expected one of %s, but was %s.";
-	protected static final String ASSERT_NOT_TYPE = "Expected type is not %s.";
-	protected static final String ASSERT_SAME = "Expected types %s and %s are same.";
-	protected static final String ASSERT_COMPATIBLE = "Incompatible types %s and %s.";
+	public static final String ASSERT_NOT_TYPE = "Expected type is not %s.";
+	public static final String ASSERT_SAME = "Expected types %s and %s are same.";
+	public static final String ASSERT_COMPATIBLE = "Incompatible types %s and %s.";
 
 	private static final String METHOD_NAME = "doInfer";
 
 	@Inject
 	protected ITypeSystem registry;
+	
+	@Inject TypeValidator typeValidator;
 
-	private IValidationIssueAcceptor acceptor;
+	protected IValidationIssueAcceptor acceptor;
 
 	private PolymorphicDispatcher<Object> dispatcher;
 
@@ -129,81 +131,43 @@ public abstract class AbstractTypeSystemInferrer implements ITypeSystemInferrer
 	}
 
 	protected void assertNotType(InferenceResult currentResult, String msg, InferenceResult... candidates) {
-		if (currentResult == null)
-			return;
-		for (InferenceResult type : candidates) {
-			if (registry.isSame(currentResult.getType(), type.getType())) {
-				error(msg != null ? msg : String.format(ASSERT_NOT_TYPE, currentResult), NOT_TYPE_CODE);
-				return;
-			}
+		for(TypeValidationError e: typeValidator.assertNotType(currentResult, msg, candidates)) {
+			error(e);
 		}
 	}
 
 	protected void assertSame(InferenceResult result1, InferenceResult result2, String msg) {
-		if (result1 == null || result2 == null)
-			return;
-		String errorMsg = msg != null ? msg : String.format(ASSERT_SAME, result1, result2);
-		if (!registry.isSame(result1.getType(), result2.getType())) {
-			error(errorMsg, NOT_SAME_CODE);
-			return;
+		for(TypeValidationError e: typeValidator.assertSame(result1, result2, msg)) {
+			error(e);
 		}
-
-		assertTypeBindingsSame(result1, result2, errorMsg);
 	}
 
 	protected void assertCompatible(InferenceResult result1, InferenceResult result2, String msg) {
-		if (result1 == null || result2 == null)
-			return;
-		if (isNullOnComplexType(result1, result2) || isNullOnComplexType(result2, result1)) {
-			return;
+		for(TypeValidationError e: typeValidator.assertCompatible(result1, result2, msg)) {
+			error(e);
 		}
-		String errorMsg = msg != null ? msg : String.format(ASSERT_COMPATIBLE, result1, result2);
-		if (!registry.haveCommonType(result1.getType(), result2.getType())) {
-			error(errorMsg, NOT_COMPATIBLE_CODE);
-			return;
-		}
-		assertTypeBindingsSame(result1, result2, errorMsg);
-
 	}
 
 	protected void assertAssignable(InferenceResult varResult, InferenceResult valueResult, String msg) {
-		if (varResult == null || valueResult == null)
-			return;
-		if (isNullOnComplexType(varResult, valueResult)) {
-			return;
+		for(TypeValidationError e: typeValidator.assertAssignable(varResult, valueResult, msg)) {
+			error(e);
 		}
-		if (!registry.isSuperType(valueResult.getType(), varResult.getType())) {
-			error(msg != null ? msg : String.format(ASSERT_COMPATIBLE, varResult, valueResult), NOT_COMPATIBLE_CODE);
-			return;
-		}
-		assertTypeBindingsSame(varResult, valueResult, msg);
 	}
 
 	protected void assertTypeBindingsSame(InferenceResult result1, InferenceResult result2, String msg) {
-		List<InferenceResult> bindings1 = result1.getBindings();
-		List<InferenceResult> bindings2 = result2.getBindings();
-		String errorMsg = msg != null ? msg : String.format(ASSERT_COMPATIBLE, result1, result2);
-		if (bindings1.size() != bindings2.size()) {
-			error(errorMsg, NOT_COMPATIBLE_CODE);
-			return;
-		}
-		for (int i = 0; i < bindings1.size(); i++) {
-			assertSame(bindings1.get(i), bindings2.get(i), errorMsg);
+		for(TypeValidationError e: typeValidator.assertTypeBindingsSame(result1, result2, msg)) {
+			error(e);
 		}
 	}
 
 	protected void assertIsSubType(InferenceResult subResult, InferenceResult superResult, String msg) {
-		if (subResult == null || superResult == null)
-			return;
-		if (!registry.isSuperType(subResult.getType(), superResult.getType())) {
-			String msg2 = msg != null ? msg : String.format(ASSERT_COMPATIBLE, subResult, superResult);
-			error(msg2, NOT_COMPATIBLE_CODE);
+		for(TypeValidationError e: typeValidator.assertIsSubType(subResult, superResult, msg)) {
+			error(e);
 		}
 	}
 	
 	protected boolean isNullOnComplexType(InferenceResult result1, InferenceResult result2) {
-		return result1.getType() instanceof ComplexType
-				&& registry.isSame(result2.getType(), registry.getType(ITypeSystem.NULL));
+		return typeValidator.isNullOnComplexType(result1, result2);
 	}
 
 	protected void info(String msg, String issueCode) {
@@ -217,4 +181,8 @@ public abstract class AbstractTypeSystemInferrer implements ITypeSystemInferrer
 	protected void error(String msg, String issueCode) {
 		acceptor.accept(new ValidationIssue(Severity.ERROR, msg, issueCode));
 	}
+	
+	protected void error(TypeValidationError e) {
+		error(e.getMessage(), e.getErrorCode());
+	}
 }

+ 2 - 1
plugins/org.yakindu.base.types/src/org/yakindu/base/types/inferrer/ITypeSystemInferrer.java

@@ -24,12 +24,13 @@ import org.yakindu.base.types.validation.IValidationIssueAcceptor;
  */
 public interface ITypeSystemInferrer {
 
-	public static final String EXCEPTION_CODE = "RuntimeExcpetion";
+	public static final String EXCEPTION_CODE = "RuntimeException";
 	public static final String NO_INFER_METHOD_CODE = "NoInferMethod";
 	public static final String IS_TYPE_CODE = "NotExpectedType.";
 	public static final String NOT_TYPE_CODE = "NotType";
 	public static final String NOT_SAME_CODE = "NotSame";
 	public static final String NOT_COMPATIBLE_CODE = "IncompatibleTypes";
+	public static final String NOT_INFERRABLE_TYPE_PARAMETER_CODE = "NotInferrableTypeParameter";
 
 	public static class InferenceResult {
 

+ 20 - 0
plugins/org.yakindu.base.types/src/org/yakindu/base/types/validation/TypeValidationError.java

@@ -0,0 +1,20 @@
+package org.yakindu.base.types.validation;
+
+@SuppressWarnings("serial")
+public class TypeValidationError {
+	private String errorCode;
+	private String message;
+	
+	public TypeValidationError(String msg, String errorCode) {
+		this.message = msg;
+		this.errorCode = errorCode;
+	}
+
+	public String getErrorCode() {
+		return errorCode;
+	}
+	
+	public String getMessage() {
+		return message;
+	}
+}

+ 111 - 0
plugins/org.yakindu.base.types/src/org/yakindu/base/types/validation/TypeValidator.java

@@ -0,0 +1,111 @@
+package org.yakindu.base.types.validation;
+
+import static org.yakindu.base.types.inferrer.AbstractTypeSystemInferrer.ASSERT_COMPATIBLE;
+import static org.yakindu.base.types.inferrer.AbstractTypeSystemInferrer.ASSERT_NOT_TYPE;
+import static org.yakindu.base.types.inferrer.AbstractTypeSystemInferrer.ASSERT_SAME;
+import static org.yakindu.base.types.inferrer.ITypeSystemInferrer.NOT_COMPATIBLE_CODE;
+import static org.yakindu.base.types.inferrer.ITypeSystemInferrer.NOT_SAME_CODE;
+import static org.yakindu.base.types.inferrer.ITypeSystemInferrer.NOT_TYPE_CODE;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.yakindu.base.types.ComplexType;
+import org.yakindu.base.types.inferrer.ITypeSystemInferrer.InferenceResult;
+import org.yakindu.base.types.typesystem.ITypeSystem;
+
+import com.google.inject.Inject;
+
+public class TypeValidator {
+	@Inject
+	protected ITypeSystem registry;
+	
+	public List<TypeValidationError> assertNotType(InferenceResult currentResult, String msg, InferenceResult... candidates) {
+		List<TypeValidationError> errors = new ArrayList<>();
+		if (currentResult == null)
+			return errors;
+		for (InferenceResult type : candidates) {
+			if (registry.isSame(currentResult.getType(), type.getType())) {
+				msg = msg != null ? msg : String.format(ASSERT_NOT_TYPE, currentResult);
+				errors.add(new TypeValidationError(msg, NOT_TYPE_CODE));
+			}
+		}
+		return errors;
+	}
+
+	public List<TypeValidationError> assertSame(InferenceResult result1, InferenceResult result2, String msg) {
+		List<TypeValidationError> errors = new ArrayList<>();
+		if (result1 == null || result2 == null)
+			return errors;
+		if (!registry.isSame(result1.getType(), result2.getType())) {
+			msg = msg != null ? msg : String.format(ASSERT_SAME, result1, result2);
+			errors.add(new TypeValidationError(msg, NOT_SAME_CODE));
+			return errors;
+		}
+
+		return assertTypeBindingsSame(result1, result2, msg);
+	}
+
+	public List<TypeValidationError> assertCompatible(InferenceResult result1, InferenceResult result2, String msg) {
+		List<TypeValidationError> errors = new ArrayList<>();
+		if (result1 == null || result2 == null)
+			return errors;
+		if (isNullOnComplexType(result1, result2) || isNullOnComplexType(result2, result1)) {
+			return errors;
+		}
+		if (!registry.haveCommonType(result1.getType(), result2.getType())) {
+			msg = msg != null ? msg : String.format(ASSERT_COMPATIBLE, result1, result2);
+			errors.add(new TypeValidationError(msg, NOT_COMPATIBLE_CODE));
+			return errors;
+		}
+		return assertTypeBindingsSame(result1, result2, msg);
+
+	}
+
+	public List<TypeValidationError> assertAssignable(InferenceResult varResult, InferenceResult valueResult, String msg) {
+		List<TypeValidationError> errors = new ArrayList<>();
+		if (varResult == null || valueResult == null)
+			return errors;
+		if (isNullOnComplexType(varResult, valueResult)) {
+			return errors;
+		}
+		if (!registry.isSuperType(valueResult.getType(), varResult.getType())) {
+			msg = msg != null ? msg : String.format(ASSERT_COMPATIBLE, varResult, valueResult);
+			errors.add(new TypeValidationError(msg, NOT_COMPATIBLE_CODE));
+			return errors;
+		}
+		return assertTypeBindingsSame(varResult, valueResult, msg);
+	}
+
+	public List<TypeValidationError> assertTypeBindingsSame(InferenceResult result1, InferenceResult result2, String msg) {
+		List<TypeValidationError> errors = new ArrayList<>();
+		List<InferenceResult> bindings1 = result1.getBindings();
+		List<InferenceResult> bindings2 = result2.getBindings();
+		msg = msg != null ? msg : String.format(ASSERT_COMPATIBLE, result1, result2);
+		if (bindings1.size() != bindings2.size()) {
+			errors.add(new TypeValidationError(msg, NOT_COMPATIBLE_CODE));
+			return errors;
+		}
+		for (int i = 0; i < bindings1.size(); i++) {
+			errors.addAll(assertSame(bindings1.get(i), bindings2.get(i), msg));
+		}
+		return errors;
+	}
+
+	public List<TypeValidationError> assertIsSubType(InferenceResult subResult, InferenceResult superResult, String msg) {
+		List<TypeValidationError> errors = new ArrayList<>();
+		if (subResult == null || superResult == null)
+			return errors;
+		if (!registry.isSuperType(subResult.getType(), superResult.getType())) {
+			msg = msg != null ? msg : String.format(ASSERT_COMPATIBLE, subResult, superResult);
+			errors.add(new TypeValidationError(msg, NOT_COMPATIBLE_CODE));
+			return errors;
+		}
+		return errors;
+	}
+	
+	public boolean isNullOnComplexType(InferenceResult result1, InferenceResult result2) {
+		return result1.getType() instanceof ComplexType
+				&& registry.isSame(result2.getType(), registry.getType(ITypeSystem.NULL));
+	}
+}

+ 46 - 46
plugins/org.yakindu.sct.ui.editor/src/org/yakindu/sct/ui/editor/validation/DefaultValidationIssueStore.java

@@ -68,34 +68,34 @@ public class DefaultValidationIssueStore implements IValidationIssueStore, IReso
 
 	@Override
 	public void addIssueStoreListener(IValidationIssueStoreListener newListener) {
-		synchronized (this.listener) {
-			this.listener.add(newListener);
-		}
+//		synchronized (this.listener) {
+//			this.listener.add(newListener);
+//		}
 	}
 
 	@Override
 	public void removeIssueStoreListener(IValidationIssueStoreListener oldListener) {
-		synchronized (listener) {
-			listener.remove(oldListener);
-		}
+//		synchronized (listener) {
+//			listener.remove(oldListener);
+//		}
 	}
 
 	protected void notifyListeners() {
-		synchronized (listener) {
-			for (IValidationIssueStoreListener iResourceIssueStoreListener : listener) {
-				iResourceIssueStoreListener.issuesChanged();
-			}
-		}
+//		synchronized (listener) {
+//			for (IValidationIssueStoreListener iResourceIssueStoreListener : listener) {
+//				iResourceIssueStoreListener.issuesChanged();
+//			}
+//		}
 	}
 
 	protected void notifyListeners(String semanticURI) {
-		synchronized (listener) {
-			for (IValidationIssueStoreListener iResourceIssueStoreListener : listener) {
-				if (semanticURI.equals(iResourceIssueStoreListener.getSemanticURI())) {
-					iResourceIssueStoreListener.issuesChanged();
-				}
-			}
-		}
+//		synchronized (listener) {
+//			for (IValidationIssueStoreListener iResourceIssueStoreListener : listener) {
+//				if (semanticURI.equals(iResourceIssueStoreListener.getSemanticURI())) {
+//					iResourceIssueStoreListener.issuesChanged();
+//				}
+//			}
+//		}
 	}
 
 	@Override
@@ -120,10 +120,10 @@ public class DefaultValidationIssueStore implements IValidationIssueStore, IReso
 					iMarker.getAttribute(SCTMarkerType.SEMANTIC_ELEMENT_ID, ""));
 			newVisibleIssues.put(issue.getSemanticURI(), issue);
 		}
-		synchronized (visibleIssues) {
-			visibleIssues.clear();
-			visibleIssues.putAll(newVisibleIssues);
-		}
+//		synchronized (visibleIssues) {
+//			visibleIssues.clear();
+//			visibleIssues.putAll(newVisibleIssues);
+//		}
 		notifyListeners();
 	}
 
@@ -148,9 +148,9 @@ public class DefaultValidationIssueStore implements IValidationIssueStore, IReso
 			ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
 			connected = false;
 			connectedResource = null;
-			synchronized (listener) {
-				listener.clear();
-			}
+//			synchronized (listener) {
+//				listener.clear();
+//			}
 		}
 	}
 
@@ -165,22 +165,22 @@ public class DefaultValidationIssueStore implements IValidationIssueStore, IReso
 		}
 
 		final Multimap<String, SCTIssue> oldVisibleIssues = ArrayListMultimap.create();
-		synchronized (visibleIssues) {
-			oldVisibleIssues.putAll(visibleIssues);
-			// normal and expensive checks will not be executed by the live
-			// validation, so persistent markers have to be copied
-			Iterable<SCTIssue> persistentIssues = Iterables.filter(visibleIssues.values(), new Predicate<SCTIssue>() {
-				public boolean apply(SCTIssue input) {
-					return input.getType() == CheckType.NORMAL || input.getType() == CheckType.EXPENSIVE;
-				}
-			});
-			for (SCTIssue sctIssue : persistentIssues) {
-				newVisibleIssues.put(sctIssue.getSemanticURI(), sctIssue);
-			}
-			visibleIssues.clear();
-			visibleIssues.putAll(newVisibleIssues);
-		}
-		
+//		synchronized (visibleIssues) {
+//			oldVisibleIssues.putAll(visibleIssues);
+//			// normal and expensive checks will not be executed by the live
+//			// validation, so persistent markers have to be copied
+//			Iterable<SCTIssue> persistentIssues = Iterables.filter(visibleIssues.values(), new Predicate<SCTIssue>() {
+//				public boolean apply(SCTIssue input) {
+//					return input.getType() == CheckType.NORMAL || input.getType() == CheckType.EXPENSIVE;
+//				}
+//			});
+//			for (SCTIssue sctIssue : persistentIssues) {
+//				newVisibleIssues.put(sctIssue.getSemanticURI(), sctIssue);
+//			}
+//			visibleIssues.clear();
+//			visibleIssues.putAll(newVisibleIssues);
+//		}
+//		
 		SetView<String> changes = Sets.symmetricDifference(oldVisibleIssues.keySet(), newVisibleIssues.keySet());
 		for (String semanticElementID : changes) {
 			notifyListeners(semanticElementID);
@@ -203,11 +203,11 @@ public class DefaultValidationIssueStore implements IValidationIssueStore, IReso
 		}
 
 		ResourceDeltaToIssueResult markerChangeResult = null;
-		synchronized (visibleIssues) {
-			markerChangeResult = resourceChangeToIssues.process(event, connectedResource, visibleIssues);
-			if (markerChangeResult != null)
-				visibleIssues = markerChangeResult.getIssues();
-		}
+//		synchronized (visibleIssues) {
+//			markerChangeResult = resourceChangeToIssues.process(event, connectedResource, visibleIssues);
+//			if (markerChangeResult != null)
+//				visibleIssues = markerChangeResult.getIssues();
+//		}
 
 		if (markerChangeResult != null)
 			for (String elementID : markerChangeResult.getChangedElementIDs()) {

+ 1 - 0
test-plugins/org.yakindu.sct.model.stext.test/.classpath

@@ -3,5 +3,6 @@
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
 	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
 	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="src" path="xtend-gen"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>

+ 2 - 1
test-plugins/org.yakindu.sct.model.stext.test/.gitignore

@@ -1,2 +1,3 @@
 /bin/
-/target/
+/target/
+/xtend-gen/

+ 6 - 0
test-plugins/org.yakindu.sct.model.stext.test/.project

@@ -5,6 +5,11 @@
 	<projects>
 	</projects>
 	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.xtext.ui.shared.xtextBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
 		<buildCommand>
 			<name>org.eclipse.jdt.core.javabuilder</name>
 			<arguments>
@@ -24,6 +29,7 @@
 	<natures>
 		<nature>org.eclipse.pde.PluginNature</nature>
 		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.xtext.ui.shared.xtextNature</nature>
 	</natures>
 	<filteredResources>
 		<filter>

+ 2 - 1
test-plugins/org.yakindu.sct.model.stext.test/build.properties

@@ -1,4 +1,5 @@
-source.. = src/
+source.. = src/,\
+           xtend-gen/
 output.. = bin/
 bin.includes = META-INF/,\
                .

+ 29 - 0
test-plugins/org.yakindu.sct.model.stext.test/pom.xml

@@ -25,6 +25,35 @@
 					<argLine>${test.vmargs}</argLine>
 				</configuration>
 			</plugin>
+			<plugin>
+				<artifactId>maven-clean-plugin</artifactId>
+				<configuration>
+					<filesets>
+						<fileset>
+							<directory>xtend-gen</directory>
+							<includes>
+								<include>**</include>
+							</includes>
+							<excludes>
+								<exclude>.gitignore</exclude>
+								<exclude>.svn/</exclude>
+							</excludes>
+						</fileset>
+					</filesets>
+				</configuration>
+			</plugin>
+			
+			<plugin>
+				<groupId>org.eclipse.xtend</groupId>
+				<artifactId>xtend-maven-plugin</artifactId>
+				<executions>
+					<execution>
+						<goals>
+							<goal>compile</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
 		</plugins>
 	</build>
 </project>

+ 105 - 1
test-plugins/org.yakindu.sct.model.stext.test/src/org/yakindu/sct/model/stext/test/TypeInferrerTest.java

@@ -17,6 +17,7 @@ import org.eclipse.xtext.junit4.XtextRunner;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.yakindu.base.expressions.expressions.Expression;
+import org.yakindu.base.types.inferrer.ITypeSystemInferrer;
 import org.yakindu.sct.model.stext.stext.EventRaisingExpression;
 import org.yakindu.sct.model.stext.stext.VariableDefinition;
 import org.yakindu.sct.model.stext.test.util.AbstractTypeInferrerTest;
@@ -770,10 +771,34 @@ public class TypeInferrerTest extends AbstractTypeInferrerTest {
 
 		assertTrue(isAnyType(inferTypeResultForExpression("t.prop1", "internal var t:ComplexParameterizedType").getType()));
 		
-		assertTrue(isAnyType(inferTypeResultForExpression("t.op(1, 2)", "internal var t:ComplexParameterizedType").getType()));
+		assertTrue(isIntegerType(inferTypeResultForExpression("t.op(1, 2)", "internal var t:ComplexParameterizedType").getType()));
 		
+		expectNoErrors("b = t.op2()", "internal: var t:ComplexParameterizedType<integer, boolean> var b: boolean");
+
 		assertTrue(isAnyType(inferTypeResultForExpression("t.prop1.prop1",
 				"internal var t:ComplexParameterizedType<ComplexParameterizedType<>, integer>").getType()));
+		
+		expectNoErrors("t2 = t.prop3",
+				"internal "
+				+ "var t:ComplexParameterizedType<boolean, integer> "
+				+ "var t2:ComplexParameterizedType<integer, boolean>");
+		
+		expectErrors("t = t.prop3",
+				"internal "
+				+ "var t:ComplexParameterizedType<boolean, integer> "
+				+ "var t2:ComplexParameterizedType<integer, boolean>", 
+				ITypeSystemInferrer.NOT_SAME_CODE, 2);
+	}
+	
+	@Test
+	public void testOperationWithParameterizedType() {
+		String scopes = ""
+				+ "internal "
+				+ "var t1:ComplexParameterizedType<boolean, integer>"
+				+ "var t2:ComplexParameterizedType<integer, boolean>"
+				+ "operation op(p:ComplexParameterizedType<boolean, integer>) : void";
+		expectNoErrors("op(t1)", scopes);
+		expectErrors("op(t2)", scopes, ITypeSystemInferrer.NOT_SAME_CODE, 2);
 	}
 	
 	@Test
@@ -791,4 +816,83 @@ public class TypeInferrerTest extends AbstractTypeInferrerTest {
 		expectIssue(inferTypeResult("i = null", Expression.class.getSimpleName(), scope).getType(),
 				"Assignment operator '=' may only be applied on compatible types, not on integer and null.");
 	}
+	
+	/**
+	 * Uses a model of the following function:
+	 * 
+	 * template <typename T>
+	 * T genericOp(T a, T b) {
+	 * 		return a > b ? a : b;
+	 * }
+	 */
+	@Test
+	public void testOperationWithTypeParameters() {
+		String scopes = ""
+				+ "internal "
+				+ "var myI : integer "
+				+ "var myF : real "
+				+ "var myB: boolean "
+				+ "var myCPT: ComplexParameterizedType<boolean, integer> "
+				+ "var myCPT2: ComplexParameterizedType<integer, boolean> ";
+		
+		expectNoErrors("myI = genericOp(myI, myI)", scopes);
+		assertTrue(isIntegerType(inferTypeResultForExpression("myI = genericOp(myI, myI)", scopes).getType()));
+		
+		expectNoErrors("myF = genericOp(3+5, 2.3)", scopes);
+		assertTrue(isRealType(inferTypeResultForExpression("myF = genericOp(3+5, 2.3)", scopes).getType()));
+		
+		expectNoErrors("myCPT = genericOp(myCPT, myCPT)", scopes);
+		
+		expectError("myB = genericOp(myI, myI)", scopes, ITypeSystemInferrer.NOT_COMPATIBLE_CODE);
+		expectError("myB = genericOp(3+5, boolean)", scopes, ITypeSystemInferrer.NOT_INFERRABLE_TYPE_PARAMETER_CODE);
+		
+		expectErrors("myCPT2 = genericOp(myCPT, myCPT)", scopes, ITypeSystemInferrer.NOT_SAME_CODE, 2);
+		expectErrors("myCPT = genericOp(myCPT, myCPT2)", scopes, ITypeSystemInferrer.NOT_SAME_CODE, 2);
+	}
+	
+	@Test
+	public void testNestedGenericElementInferrer() {
+		String scope = ""
+				+ "import nestedTemplate "
+				+ "internal: "
+				+ "var nestedCPT: ComplexParameterizedType<boolean, integer> "
+				+ "var myI: integer "
+				+ "var myB: boolean ";
+		
+		expectError("myI = nestedOp(3)", scope, ITypeSystemInferrer.NOT_COMPATIBLE_CODE);
+		expectNoErrors("myI = nestedOp(nestedCPT)", scope);
+		expectError("myB = nestedOp(nestedCPT)", scope, ITypeSystemInferrer.NOT_COMPATIBLE_CODE);
+	}
+	
+	@Test
+	public void testNestedNestedGenericElementInferrer() {
+		String scope = ""
+				+ "import nestedNestedTemplate "
+				+ "internal: "
+				+ "var nestedCPT: ComplexParameterizedType<ComplexParameterizedType<boolean, string>, integer> "
+				+ "var myS: string "
+				+ "var b: boolean ";
+		
+		expectNoErrors("myS = nestedNestedOp(nestedCPT)", scope);
+		expectError("b = nestedNestedOp(nestedCPT)", scope, ITypeSystemInferrer.NOT_COMPATIBLE_CODE);
+	}
+	
+	@Test
+	public void testGenericFeatureCall() {
+		String scope = ""
+				+ "internal: "
+				+ "var cmo: ParameterizedMethodOwner "
+				+ "var i: integer "
+				+ "var r: real "
+				+ "var b: boolean ";
+		
+		expectNoErrors("i = cmo.genericOp(1, 2)", scope);
+		expectNoErrors("r = cmo.genericOp(1.3, 2)", scope);
+		expectNoErrors("r = cmo.genericOp(1, 2)", scope);
+		expectNoErrors("b = cmo.genericOp(true, 2)", scope);
+		expectNoErrors("b = cmo.genericOp(true, 2)", scope);
+		expectError("i = cmo.genericOp(1.3, 2)", scope, ITypeSystemInferrer.NOT_COMPATIBLE_CODE);
+		expectError("r = cmo.genericOp(false, 2)", scope, ITypeSystemInferrer.NOT_COMPATIBLE_CODE);
+		expectError("b = cmo.genericOp(1.3, 2)", scope, ITypeSystemInferrer.NOT_COMPATIBLE_CODE);
+	}
 }

+ 6 - 0
test-plugins/org.yakindu.sct.model.stext.test/src/org/yakindu/sct/model/stext/test/util/AbstractTypeInferrerTest.java

@@ -137,6 +137,12 @@ public abstract class AbstractTypeInferrerTest extends AbstractSTextTest {
 		assertEquals(Arrays.toString(traces.toArray()), 1, issues.size());
 	}
 	
+	protected void expectErrors(String expression, String scope, String code, int noOfErrors) {
+		List<ValidationIssue> traces = validate(expression, scope).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) {
 		return Lists.newArrayList(Iterables.filter(traces, new Predicate<ValidationIssue>() {
 			@Override

+ 0 - 205
test-plugins/org.yakindu.sct.model.stext.test/src/org/yakindu/sct/model/stext/test/util/STextTestScopeProvider.java

@@ -1,205 +0,0 @@
-/**
- * Copyright (c) 2012 committers of YAKINDU and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- * Contributors:
- * 	committers of YAKINDU - initial API and implementation
- * 
- */
-package org.yakindu.sct.model.stext.test.util;
-
-import java.util.HashMap;
-import java.util.List;
-
-import org.eclipse.emf.common.util.URI;
-import org.eclipse.emf.ecore.EObject;
-import org.eclipse.emf.ecore.EReference;
-import org.eclipse.emf.ecore.resource.Resource;
-import org.eclipse.emf.ecore.resource.impl.ResourceImpl;
-import org.eclipse.xtext.naming.IQualifiedNameProvider;
-import org.eclipse.xtext.resource.EObjectDescription;
-import org.eclipse.xtext.resource.IEObjectDescription;
-import org.eclipse.xtext.scoping.IScope;
-import org.eclipse.xtext.scoping.impl.SimpleScope;
-import org.yakindu.base.types.ComplexType;
-import org.yakindu.base.types.Declaration;
-import org.yakindu.base.types.EnumerationType;
-import org.yakindu.base.types.Enumerator;
-import org.yakindu.base.types.Operation;
-import org.yakindu.base.types.Parameter;
-import org.yakindu.base.types.Property;
-import org.yakindu.base.types.Type;
-import org.yakindu.base.types.TypeParameter;
-import org.yakindu.base.types.TypeSpecifier;
-import org.yakindu.base.types.TypesFactory;
-import org.yakindu.base.types.typesystem.GenericTypeSystem;
-import org.yakindu.base.types.typesystem.ITypeSystem;
-import org.yakindu.sct.model.sgraph.Region;
-import org.yakindu.sct.model.sgraph.SGraphFactory;
-import org.yakindu.sct.model.sgraph.State;
-import org.yakindu.sct.model.sgraph.Statechart;
-import org.yakindu.sct.model.stext.scoping.STextScopeProvider;
-
-import com.google.common.collect.Lists;
-import com.google.inject.Inject;
-
-/**
- * 
- * @author andreas muelder - Initial contribution and API
- * 
- */
-public class STextTestScopeProvider extends STextScopeProvider {
-
-	private static final SGraphFactory factory = SGraphFactory.eINSTANCE;
-
-	@Inject
-	private IQualifiedNameProvider qfnProvider;
-
-	@Inject
-	private ITypeSystem typeSystem;
-
-	public IScope getScope(EObject context, EReference reference) {
-		IScope parentScope = super.getScope(context, reference);
-
-		List<IEObjectDescription> descriptions = Lists.newArrayList(parentScope.getAllElements());
-
-		State dummyState = createDummyModel();
-		descriptions.add(createEObjectDesc(dummyState));
-
-		ComplexType complexType = createComplexType();
-		descriptions.add(createEObjectDesc(complexType));
-		
-		Type enumType = createEnumType();
-		descriptions.add(createEObjectDesc(enumType));
-
-		for (Declaration feature : complexType.getFeatures()) {
-			descriptions.add(createEObjectDesc(feature));
-		}
-		
-		ComplexType cmplxParamType = createComplexParameterizedType();
-		descriptions.add(createEObjectDesc(cmplxParamType));
-		
-		for (Declaration feature : cmplxParamType.getFeatures()) {
-			descriptions.add(createEObjectDesc(feature));
-		}
-		
-		return new SimpleScope(descriptions);
-	}
-
-	protected IEObjectDescription createEObjectDesc(EObject object) {
-		return new EObjectDescription(qfnProvider.getFullyQualifiedName(object), object, new HashMap<String, String>());
-	}
-
-	protected State createDummyModel() {
-		Statechart statechart = factory.createStatechart();
-		statechart.setName("chart");
-		Region region = factory.createRegion();
-		region.setName("r1");
-		statechart.getRegions().add(region);
-		State state = factory.createState();
-		state.setName("A");
-		region.getVertices().add(state);
-		return state;
-	}
-
-	protected ComplexType createComplexType() {
-		ComplexType complexType = TypesFactory.eINSTANCE.createComplexType();
-		complexType.setName("ComplexType");
-
-		Property featureX = TypesFactory.eINSTANCE.createProperty();
-		featureX.setName("x");
-		TypeSpecifier typeSpec = TypesFactory.eINSTANCE.createTypeSpecifier();
-		typeSpec.setType(typeSystem.getType(GenericTypeSystem.INTEGER));
-		featureX.setTypeSpecifier(typeSpec);
-		complexType.getFeatures().add(featureX);
-
-		Resource resource = new ResourceImpl(URI.createURI("types2"));
-		resource.getContents().add(complexType);
-
-		return complexType;
-	}
-	
-	protected EnumerationType createEnumType() {
-		EnumerationType enumType = TypesFactory.eINSTANCE.createEnumerationType();
-		enumType.setName("EnumType");
-		
-		enumType.getEnumerator().add(createEnumerator("A"));
-		enumType.getEnumerator().add(createEnumerator("B"));
-		enumType.getEnumerator().add(createEnumerator("C"));
-		
-		Resource resource = new ResourceImpl(URI.createURI("types2"));
-		resource.getContents().add(enumType);
-		
-		return enumType;
-	}
-	
-	protected Enumerator createEnumerator(String name) {
-		Enumerator enumerator = TypesFactory.eINSTANCE.createEnumerator();
-		enumerator.setName(name);
-		return enumerator;
-	}
-	
-	/**
-	 * ComplexParameterizedType<T1,T2> {
-	 * 		T1 prop1;
-	 * 		T2 prop2;
-	 * 		T1 op(T1 param1, T2 param2);
-	 * }
-	 * @return
-	 */
-	protected ComplexType createComplexParameterizedType() {
-		ComplexType complexType = TypesFactory.eINSTANCE.createComplexType();
-		complexType.setName("ComplexParameterizedType");
-		
-		TypeParameter typeParam1 = TypesFactory.eINSTANCE.createTypeParameter();
-		typeParam1.setName("T1");
-		complexType.getTypeParameters().add(typeParam1);
-		TypeParameter typeParam2 = TypesFactory.eINSTANCE.createTypeParameter();
-		typeParam2.setName("T2");
-		complexType.getTypeParameters().add(typeParam2);
-
-		Property prop1 = TypesFactory.eINSTANCE.createProperty();
-		prop1.setName("prop1");
-		TypeSpecifier prop1typeSpec = TypesFactory.eINSTANCE.createTypeSpecifier();
-		prop1typeSpec.setType(typeParam1);
-		prop1.setTypeSpecifier(prop1typeSpec);
-		complexType.getFeatures().add(prop1);
-		
-		Property prop2 = TypesFactory.eINSTANCE.createProperty();
-		prop2.setName("prop2");
-		TypeSpecifier prop2typeSpec = TypesFactory.eINSTANCE.createTypeSpecifier();
-		prop2typeSpec.setType(typeParam2);
-		prop2.setTypeSpecifier(prop2typeSpec);
-		complexType.getFeatures().add(prop2);
-		
-		Operation operation = TypesFactory.eINSTANCE.createOperation();
-		operation.setName("op");
-		TypeSpecifier returnTypeSpec = TypesFactory.eINSTANCE.createTypeSpecifier();
-		returnTypeSpec.setType(typeParam1);
-		operation.setTypeSpecifier(returnTypeSpec);
-
-		Parameter opParam1 = TypesFactory.eINSTANCE.createParameter();
-		opParam1.setName("param1");
-		TypeSpecifier param1TypeSpec = TypesFactory.eINSTANCE.createTypeSpecifier();
-		param1TypeSpec.setType(typeParam1);
-		opParam1.setTypeSpecifier(param1TypeSpec);
-		operation.getParameters().add(opParam1);
-		
-		Parameter opParam2 = TypesFactory.eINSTANCE.createParameter();
-		opParam2.setName("param2");
-		TypeSpecifier param2TypeSpec = TypesFactory.eINSTANCE.createTypeSpecifier();
-		param2TypeSpec.setType(typeParam2);
-		opParam2.setTypeSpecifier(param2TypeSpec);
-		operation.getParameters().add(opParam2);
-		
-		complexType.getFeatures().add(operation);
-
-		Resource resource = new ResourceImpl(URI.createURI("types2"));
-		resource.getContents().add(complexType);
-
-		return complexType;
-	}
-
-}

+ 402 - 0
test-plugins/org.yakindu.sct.model.stext.test/src/org/yakindu/sct/model/stext/test/util/STextTestScopeProvider.xtend

@@ -0,0 +1,402 @@
+/** 
+ * Copyright (c) 2012 committers of YAKINDU and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * Contributors:
+ * committers of YAKINDU - initial API and implementation
+ */
+package org.yakindu.sct.model.stext.test.util
+
+import com.google.common.collect.Lists
+import com.google.inject.Inject
+import java.util.List
+import org.eclipse.emf.common.util.URI
+import org.eclipse.emf.ecore.EObject
+import org.eclipse.emf.ecore.EReference
+import org.eclipse.emf.ecore.resource.impl.ResourceImpl
+import org.eclipse.xtext.naming.IQualifiedNameProvider
+import org.eclipse.xtext.resource.IEObjectDescription
+import org.eclipse.xtext.resource.impl.DefaultResourceDescriptionStrategy
+import org.eclipse.xtext.scoping.IScope
+import org.eclipse.xtext.scoping.impl.SimpleScope
+import org.yakindu.base.types.ComplexType
+import org.yakindu.base.types.Declaration
+import org.yakindu.base.types.EnumerationType
+import org.yakindu.base.types.Package
+import org.yakindu.base.types.TypesFactory
+import org.yakindu.sct.model.sgraph.SGraphFactory
+import org.yakindu.sct.model.sgraph.State
+import org.yakindu.sct.model.stext.scoping.STextScopeProvider
+
+import static org.yakindu.base.types.typesystem.ITypeSystem.*
+
+/** 
+ * @author andreas muelder - Initial contribution and API
+ */
+class STextTestScopeProvider extends STextScopeProvider {
+	
+	@Inject 
+	protected IQualifiedNameProvider qfnProvider
+	@Inject
+	protected DefaultResourceDescriptionStrategy descriptionStrategy
+	@Inject
+	protected extension TypesTestFactory = TypesTestFactory.INSTANCE
+	protected extension SGraphFactory sgraphfactory = SGraphFactory.eINSTANCE
+	protected extension TypesFactory factory = TypesFactory.eINSTANCE;
+	
+	protected ComplexType cmplxParamType
+
+	override IScope getScope(EObject context, EReference reference) {
+		var IScope parentScope = super.getScope(context, reference)
+		var List<IEObjectDescription> descriptions = Lists.newArrayList(parentScope.getAllElements())
+		
+		addToIndex(descriptions, createDummyModel)
+		addToIndex(descriptions, createComplexType)
+		addToIndex(descriptions, createEnumType)
+		cmplxParamType = createComplexParameterizedType()
+		addToIndex(descriptions, cmplxParamType)
+		addToIndex(descriptions, createParameterizedMethodOwner)
+		
+		val simpleTemplate = createPackageWithTemplateFunction()
+		addToIndex(descriptions, simpleTemplate)
+		addToIndex(descriptions, simpleTemplate.member.head)
+		
+		val intTemplate = createPackageWithTemplateFunctionInt()
+		addToIndex(descriptions, intTemplate)
+		addToIndex(descriptions, intTemplate.member.head)
+
+		val boolTemplate = createPackageWithTemplateFunctionBool()
+		addToIndex(descriptions, boolTemplate)
+		addToIndex(descriptions, boolTemplate.member.head)
+		
+		val mixedTemplate = createPackageWithTemplateFunctionTwoParams()
+		addToIndex(descriptions, mixedTemplate)
+		addToIndex(descriptions, mixedTemplate.member.head)
+		
+		val cptTemplate = createComplexParameterizedTypeTemplateTest()
+		addToIndex(descriptions, cptTemplate)
+		addToIndex(descriptions, cptTemplate.member.head)
+		
+		
+		val nestedTemplate = createPackageWithNestedComplexTypeFunction()
+		addToIndex(descriptions, nestedTemplate)
+		addToIndex(descriptions, nestedTemplate.member.head)
+		
+		val nestedNestedTemplate = createPackageWithNestedNestedComplexTypeFunction()
+		addToIndex(descriptions, nestedNestedTemplate)
+		addToIndex(descriptions, nestedNestedTemplate.member.head)
+		
+		return new SimpleScope(descriptions)
+	}
+	
+	def protected void addToIndex(List<IEObjectDescription> descriptions, EObject element) {
+		descriptionStrategy.createEObjectDescriptions(element, [descriptions+=it])
+		if (element instanceof ComplexType) {
+			for (Declaration feature : element.features) {
+				descriptionStrategy.createEObjectDescriptions(element, [descriptions+=it])
+			}
+		}
+	}
+
+	def protected State createDummyModel() {
+		val stateA = createState => [
+			name = "A"
+		]
+		createStatechart => [
+			name = "chart"
+			regions += createRegion => [
+				name = "r1"
+				vertices += stateA
+			]
+		]
+		stateA
+	}
+
+	def protected ComplexType createComplexType() {
+		val complexType = factory.createComplexType => [
+			name = "ComplexType"
+			features += createProperty("x", INTEGER)
+		]
+		complexType.addToResource
+		complexType
+	}
+
+	def protected EnumerationType createEnumType() {
+		val enumType = createEnumerationType => [
+			name = "EnumType"
+			enumerator += createEnumerator("A")
+			enumerator += createEnumerator("B")
+			enumerator += createEnumerator("C")
+		]
+		enumType.addToResource
+		enumType
+	}
+	
+	// TODO: check if needed to add into resource
+	def protected addToResource(EObject element) {
+		val resource = new ResourceImpl(URI.createURI("types2"))
+		resource.contents += element
+	}
+
+	/** 
+	 * ComplexParameterizedType&lt;T1,T2&gt; {
+	 * <ul>
+	 * <li>T1 prop1;
+	 * <li>T2 prop2;
+	 * <li>ComplexParameterizedType&lt;T2,T1&gt; prop3;
+	 * <li>T1 op(T1 param1, T2 param2);
+	 * <li>T2 op2();
+	 * </ul>
+	 * }
+	 * @return
+	 */
+	def protected ComplexType createComplexParameterizedType() {
+		val complexType = createComplexType => [ct |
+			ct.name = "ComplexParameterizedType"
+			ct.typeParameters += createTypeParameter("T1")
+			ct.typeParameters += createTypeParameter("T2")
+			ct.features += createProperty("prop1", ct.typeParameters.get(0))
+			ct.features += createProperty("prop2", ct.typeParameters.get(1))
+			ct.features += createProperty("prop3", ct.toTypeSpecifier => [
+				typeArguments+=ct.typeParameters.get(1).toTypeSpecifier
+				typeArguments+=ct.typeParameters.get(0).toTypeSpecifier
+			])
+			ct.features += createOperation => [op |
+				op.name = "op"
+				op.typeSpecifier = ct.typeParameters.get(0).toTypeSpecifier
+				op.parameters += createParameter("param1", ct.typeParameters.get(0).toTypeSpecifier)
+				op.parameters += createParameter("param2", ct.typeParameters.get(1).toTypeSpecifier)
+			]
+			ct.features += createOperation => [op |
+				op.name = "op2"
+				op.typeSpecifier = ct.typeParameters.get(1).toTypeSpecifier
+			]
+		]
+		complexType.addToResource
+		complexType
+	}
+	
+	/**
+	 * ParameterizedMethodOwner {
+	 * 	T1 genericOp<T1, T2>(T1 p1, T2 p1);
+	 * }
+	 */
+	def protected ComplexType createParameterizedMethodOwner() {
+		val complexType = createComplexType => [ct |
+	 		ct.name = "ParameterizedMethodOwner"
+	 		ct.features += createOperation => [op |
+	 			op.name = "genericOp"
+	 			op.typeParameters += createTypeParameter("T1")
+	 			op.typeParameters += createTypeParameter("T2")
+	 			op.typeSpecifier = op.typeParameters.get(0).toTypeSpecifier
+	 			op.parameters += createParameter("p1", op.typeParameters.get(0).toTypeSpecifier)
+	 			op.parameters += createParameter("p2", op.typeParameters.get(1).toTypeSpecifier)
+	 		]
+	 	]
+		complexType
+	}
+	
+	/**
+	 * Returns a model of the following function:
+	 * 
+	 * template <typename T>
+	 * T genericOp(T a, T b) {
+	 * 		return a > b ? a : b;
+	 * }
+	 */
+	def Package createPackageWithTemplateFunction() {
+		createRootPackage("simpleTemplate") => [ types |
+			types.member += factory.createOperation => [ op |
+				op.name = "genericOp"
+				op.typeParameters += createTypeParameter("T")
+				op.parameters += factory.createParameter => [
+					name = "a"
+					typeSpecifier = op.typeParameters.head.toTypeSpecifier
+				]
+				op.parameters += factory.createParameter => [
+					name = "b"
+					typeSpecifier = op.typeParameters.head.toTypeSpecifier
+				]
+				op.typeSpecifier = op.typeParameters.head.toTypeSpecifier
+			]
+		]
+	}
+	
+	/**
+	 * Returns a model of the following function:
+	 * 
+	 * template <typename T>
+	 * int intGenericOp(T a, T b) {
+	 * 		return a > b ? a : b;
+	 * }
+	 */
+	def Package createPackageWithTemplateFunctionInt() {
+		createRootPackage("intTemplate") => [ types |
+			types.member += factory.createOperation => [ op |
+				op.name = "intGenericOp"
+				op.typeParameters += createTypeParameter("T")
+				op.parameters += factory.createParameter => [
+					name = "a"
+					typeSpecifier = op.typeParameters.head.toTypeSpecifier
+				]
+				op.parameters += factory.createParameter => [
+					name = "b"
+					typeSpecifier = op.typeParameters.head.toTypeSpecifier
+				]
+				op.typeSpecifier = factory.createTypeSpecifier => [
+					type = ts.getType(INTEGER)
+				]
+			]
+		]
+	}
+	
+	/**
+	 * Returns a model of the following function:
+	 * 
+	 * template <typename T>
+	 * T boolGenericOp(T a, T b, bool c) {
+	 * 		if(c) return a > b ? a : b;
+	 * 		return a < b ? a : b;
+	 * }
+	 */
+	def Package createPackageWithTemplateFunctionBool() {
+		createRootPackage("boolTemplate") => [ types |
+			types.member += factory.createOperation => [ op |
+				op.name = "boolGenericOp"
+				op.typeParameters += createTypeParameter("T")
+				op.parameters += factory.createParameter => [
+					name = "a"
+					typeSpecifier = op.typeParameters.head.toTypeSpecifier
+				]
+				op.parameters += factory.createParameter => [
+					name = "b"
+					typeSpecifier = op.typeParameters.head.toTypeSpecifier
+				]
+				op.parameters += factory.createParameter => [
+					name = "c"
+					typeSpecifier = BOOLEAN.toTypeSpecifier
+				]
+				op.typeSpecifier = op.typeParameters.head.toTypeSpecifier
+			]
+		]
+	}
+	
+	/**
+	 * Returns a model of the following function:
+	 * 
+	 * template <typename T, typename T2>
+	 * T2 mixedGenericOp(T a, T2 b, bool c) {
+	 * 		if(c) return a > b ? a : b;
+	 * 		return a < b ? a : b;
+	 * }
+	 */
+	def Package createPackageWithTemplateFunctionTwoParams() {
+		createRootPackage("mixedTemplate") => [ types |
+			types.member += factory.createOperation => [ op |
+				op.name = "mixedGenericOp"
+				op.typeParameters += createTypeParameter("T")
+				op.typeParameters += createTypeParameter("T2")
+				op.parameters += factory.createParameter => [
+					name = "a"
+					typeSpecifier = op.typeParameters.get(0).toTypeSpecifier
+				]
+				op.parameters += factory.createParameter => [
+					name = "b"
+					typeSpecifier = op.typeParameters.get(1).toTypeSpecifier
+				]
+				op.parameters += factory.createParameter => [
+					name = "c"
+					typeSpecifier = BOOLEAN.toTypeSpecifier
+				]
+				op.typeSpecifier = op.typeParameters.get(1).toTypeSpecifier
+			]
+		]
+	}
+	
+	/**
+	 * Returns a model of the following function:
+	 * 
+	 * template <typename T, typename T2>
+	 * T2 nestedOp(ComplexParameterizedType<T, T2> a) {
+	 * 		return a.prop1.prop2;
+	 * }
+	 */
+	def Package createPackageWithNestedComplexTypeFunction() {
+		createRootPackage("nestedTemplate") => [ types |
+			types.member += factory.createOperation => [ op |
+				op.name = "nestedOp"
+				op.typeParameters += createTypeParameter("T")
+				op.typeParameters += createTypeParameter("T2")
+				op.parameters += factory.createParameter => [
+					name = "a"
+					typeSpecifier = cmplxParamType.toTypeSpecifier => [
+						typeArguments += op.typeParameters.get(0).toTypeSpecifier
+						typeArguments += op.typeParameters.get(1).toTypeSpecifier
+					]
+				]
+				op.typeSpecifier = op.typeParameters.get(1).toTypeSpecifier
+			]
+		]
+	}
+	
+	/**
+	 * Returns a model of the following function:
+	 * 
+	 * template <typename T>
+	 * T nestedNestedReturn(ComplexParameterizedType<ComplexParameterizedType<boolean, T>, integer> a) {
+	 * 		return a.prop1.prop2;
+	 * }
+	 */
+	def Package createPackageWithNestedNestedComplexTypeFunction() {
+		createRootPackage("nestedNestedTemplate") => [ types |
+			types.member += factory.createOperation => [ op |
+				op.name = "nestedNestedOp"
+				op.typeParameters += createTypeParameter("T")
+				op.parameters += factory.createParameter => [
+					name = "a"
+					typeSpecifier = cmplxParamType.toTypeSpecifier => [
+						typeArguments += cmplxParamType.toTypeSpecifier => [
+							typeArguments += BOOLEAN.toTypeSpecifier
+							typeArguments += op.typeParameters.head.toTypeSpecifier
+						]
+						typeArguments += INTEGER.toTypeSpecifier
+					]
+				]
+				op.typeSpecifier = op.typeParameters.head.toTypeSpecifier
+			]
+		]
+	}
+	
+
+
+	/**
+	 * Returns a model of the following function:
+	 * 
+	 * template<typename T, typename T2>
+	 * T getProp(ComplexParameterizedType<T,T2> cpt) {
+	 * 	   return ctp.prop1;
+	 * }
+	 */
+	def createComplexParameterizedTypeTemplateTest() { 
+		createRootPackage("cptTemplate") => [ types |
+			types.member += factory.createOperation => [ op |
+				op.name = "getProp"
+				op.typeParameters += createTypeParameter("T")
+				op.typeParameters += createTypeParameter("T2")
+				
+				op.parameters += factory.createParameter => [
+					name = "cpt"
+					typeSpecifier = factory.createTypeSpecifier => [
+						type = cmplxParamType
+						typeArguments += op.typeParameters.get(0).toTypeSpecifier
+						typeArguments += op.typeParameters.get(1).toTypeSpecifier
+					]
+				]
+				op.typeSpecifier = op.typeParameters.head.toTypeSpecifier
+			]
+		]
+	}
+
+}

+ 101 - 0
test-plugins/org.yakindu.sct.model.stext.test/src/org/yakindu/sct/model/stext/test/util/TypesTestFactory.xtend

@@ -0,0 +1,101 @@
+/**
+* Copyright (c) 2016 itemis AG - All rights Reserved
+* Unauthorized copying of this file, via any medium is strictly prohibited
+* 
+* Contributors:
+* 	Thomas Kutz - itemis AG
+*
+*/
+package org.yakindu.sct.model.stext.test.util
+
+import com.google.inject.Inject
+import org.yakindu.base.types.Type
+import org.yakindu.base.types.TypeSpecifier
+import org.yakindu.base.types.TypesFactory
+import org.yakindu.base.types.typesystem.ITypeSystem
+import org.yakindu.base.types.Enumerator
+import org.yakindu.base.types.Package
+
+class TypesTestFactory {
+	
+	public static final TypesTestFactory INSTANCE = new TypesTestFactory();
+	
+	@Inject 
+	protected ITypeSystem ts;
+	
+	protected TypesFactory factory = TypesFactory.eINSTANCE
+	
+	def Package createRootPackage(String filename) {
+		factory.createPackage => [
+			it.name = filename
+		]
+	}
+	
+	def createParameter(String name, String typeName) {
+		createParameter(name, toTypeSpecifier(ts.getType(typeName)));
+	}
+	
+	def createParameter(String name, Type type) {
+		createParameter(name, type.toTypeSpecifier);
+	}
+	
+	def createParameter(String name, TypeSpecifier typeSpec) {
+		factory.createParameter => [
+			it.name = name
+			it.typeSpecifier = typeSpec
+		]
+	}
+	
+	def createProperty(String name, String typeName) {
+		createProperty(name, typeName, false);
+	}
+
+	def createProperty(String name, String typeName, boolean isConst) {
+		createProperty(name, ts.getType(typeName)) => [const = isConst];
+	}
+	
+	def createProperty(String name, Type type) {
+		createProperty(name, type.toTypeSpecifier);
+	}
+	
+	def createProperty(String name, TypeSpecifier typeSpec) {
+		factory.createProperty => [
+			it.name = name
+			it.typeSpecifier = typeSpec
+		]
+	}
+	
+	def createOperation(String name, String returnType) {
+		createOperation(name, ts.getType(returnType))
+	}
+	
+	def createOperation(String name, Type returnType) {
+		factory.createOperation => [
+			it.name = name
+			it.typeSpecifier = returnType.toTypeSpecifier
+		]
+	}
+	
+	def toTypeSpecifier(String typeName) {
+		toTypeSpecifier(ts.getType(typeName));
+	}
+	
+	def toTypeSpecifier(Type type) {
+		factory.createTypeSpecifier => [
+			it.type = type
+		]
+	}
+	
+	def createTypeParameter(String name) {
+		factory.createTypeParameter => [
+			it.name = name
+		]
+	}
+	
+	def Enumerator createEnumerator(String name) {
+		factory.createEnumerator => [
+			it.name = name 
+		]
+	}
+	
+}