/******************************************************************************* * Copyright (c) 2007, 2016 Borland Software Corporation 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: * Borland Software Corporation - initial API and implementation * Alex Paperno - bugs 414616, 424584 * Christopher Gerking - bugs 427237, 486810 *******************************************************************************/ package org.eclipse.m2m.internal.qvt.oml.ast.parser; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EClassifier; import org.eclipse.emf.ecore.EDataType; import org.eclipse.emf.ecore.EEnumLiteral; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EOperation; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.EParameter; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.ETypedElement; import org.eclipse.m2m.internal.qvt.oml.NLS; import org.eclipse.m2m.internal.qvt.oml.ast.binding.ASTBindingHelper; import org.eclipse.m2m.internal.qvt.oml.ast.env.QvtOperationalEnv; import org.eclipse.m2m.internal.qvt.oml.ast.env.QvtOperationalStdLibrary; import org.eclipse.m2m.internal.qvt.oml.expressions.Constructor; import org.eclipse.m2m.internal.qvt.oml.expressions.ConstructorBody; import org.eclipse.m2m.internal.qvt.oml.expressions.ContextualProperty; import org.eclipse.m2m.internal.qvt.oml.expressions.DirectionKind; import org.eclipse.m2m.internal.qvt.oml.expressions.EntryOperation; import org.eclipse.m2m.internal.qvt.oml.expressions.Helper; import org.eclipse.m2m.internal.qvt.oml.expressions.ImperativeOperation; import org.eclipse.m2m.internal.qvt.oml.expressions.Library; import org.eclipse.m2m.internal.qvt.oml.expressions.MappingBody; import org.eclipse.m2m.internal.qvt.oml.expressions.MappingCallExp; import org.eclipse.m2m.internal.qvt.oml.expressions.MappingOperation; import org.eclipse.m2m.internal.qvt.oml.expressions.MappingParameter; import org.eclipse.m2m.internal.qvt.oml.expressions.ModelParameter; import org.eclipse.m2m.internal.qvt.oml.expressions.ModelType; import org.eclipse.m2m.internal.qvt.oml.expressions.Module; import org.eclipse.m2m.internal.qvt.oml.expressions.ModuleImport; import org.eclipse.m2m.internal.qvt.oml.expressions.ObjectExp; import org.eclipse.m2m.internal.qvt.oml.expressions.OperationBody; import org.eclipse.m2m.internal.qvt.oml.expressions.OperationalTransformation; import org.eclipse.m2m.internal.qvt.oml.expressions.ResolveExp; import org.eclipse.m2m.internal.qvt.oml.expressions.ResolveInExp; import org.eclipse.m2m.internal.qvt.oml.expressions.VarParameter; import org.eclipse.m2m.internal.qvt.oml.expressions.util.QVTOperationalVisitor; import org.eclipse.m2m.internal.qvt.oml.stdlib.ConversionUtils; import org.eclipse.m2m.internal.qvt.oml.stdlib.QVTUMLReflection; import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.AltExp; import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.AssertExp; import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.AssignExp; import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.BlockExp; import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.BreakExp; import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.CatchExp; import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.ComputeExp; import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.ContinueExp; import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.DictLiteralExp; import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.DictLiteralPart; import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.ForExp; import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.ImperativeExpression; import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.ImperativeIterateExp; import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.ImperativeLoopExp; import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.InstantiationExp; import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.ListType; import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.LogExp; import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.OrderedTupleLiteralExp; import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.OrderedTupleLiteralPart; import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.RaiseExp; import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.ReturnExp; import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.SwitchExp; import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.TryExp; import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.UnlinkExp; import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.UnpackExp; import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.VariableInitExp; import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.WhileExp; import org.eclipse.ocl.Environment; import org.eclipse.ocl.ecore.CallOperationAction; import org.eclipse.ocl.ecore.CollectionType; import org.eclipse.ocl.ecore.Constraint; import org.eclipse.ocl.ecore.IfExp; import org.eclipse.ocl.ecore.IterateExp; import org.eclipse.ocl.ecore.IteratorExp; import org.eclipse.ocl.ecore.SendSignalAction; import org.eclipse.ocl.ecore.TupleType; import org.eclipse.ocl.expressions.CollectionLiteralExp; import org.eclipse.ocl.expressions.FeatureCallExp; import org.eclipse.ocl.expressions.OCLExpression; import org.eclipse.ocl.expressions.OperationCallExp; import org.eclipse.ocl.expressions.PropertyCallExp; import org.eclipse.ocl.expressions.Variable; import org.eclipse.ocl.expressions.VariableExp; import org.eclipse.ocl.internal.l10n.OCLMessages; import org.eclipse.ocl.lpg.BasicEnvironment; import org.eclipse.ocl.lpg.FormattingHelper; import org.eclipse.ocl.lpg.ProblemHandler; import org.eclipse.ocl.options.ProblemOption; import org.eclipse.ocl.parser.ValidationVisitor; import org.eclipse.ocl.types.BagType; import org.eclipse.ocl.types.OrderedSetType; import org.eclipse.ocl.types.SequenceType; import org.eclipse.ocl.types.SetType; import org.eclipse.ocl.types.TypeType; import org.eclipse.ocl.util.OCLStandardLibraryUtil; import org.eclipse.ocl.util.OCLUtil; import org.eclipse.ocl.util.TypeUtil; import org.eclipse.ocl.utilities.ASTNode; import org.eclipse.ocl.utilities.PredefinedType; import org.eclipse.ocl.utilities.TypedElement; import org.eclipse.ocl.utilities.UMLReflection; import org.eclipse.ocl.utilities.Visitable; /** * * @author sboyko * * Ill-based validation visitor for OCL expressions only. * Presented till 'https://bugs.eclipse.org/bugs/show_bug.cgi?id=215544' * is not resolved. * */ public class QvtOperationalValidationVisitor extends QvtOperationalAstWalker { private QvtOperationalEnv fEnv; public QvtOperationalValidationVisitor(QvtOperationalEnv environment) { super (new ValidationNodeProcessor(environment)); ((ValidationNodeProcessor) getNodeProcessor()).myOclValidationVisitor.setVisitor(this); fEnv = environment; } private static class ValidationNodeProcessor implements NodeProcessor { ValidationNodeProcessor(QvtOperationalEnv environment) { myOclValidationVisitor = new CustomOclValidationVisitor(environment); } public void process(Visitable e, Visitable parent) { if (e.eClass().eContainer() == org.eclipse.ocl.ecore.EcorePackage.eINSTANCE) { try { e.accept(myOclValidationVisitor); } catch (UnsupportedOperationException ex) { } } } final CustomOclValidationVisitor myOclValidationVisitor; } @Override public Object visitIfExp(org.eclipse.ocl.expressions.IfExp<EClassifier> ifExp) { // QVT language also defines an imperative "if-then-else" construct that is less constrained // as the corresponding OCL construct if (ifExp.getCondition() == null || ifExp.getThenExpression() == null) { QvtOperationalUtil.reportError(fEnv, NLS.bind(ValidationMessages.QvtOperationalVisitorCS_ifExpIncomplete, new Object[] { }), ifExp.getStartPosition(), ifExp.getEndPosition()); } else if (ifExp.getCondition().getType() != fEnv.getOCLStandardLibrary().getBoolean()) { QvtOperationalUtil.reportError(fEnv, NLS.bind(ValidationMessages.QvtOperationalVisitorCS_ifExpNonBooleanCond, new Object[] { }), ifExp.getStartPosition(), ifExp.getEndPosition()); } EObject container = ifExp.eContainer(); while (container != null) { if (container instanceof VariableInitExp) { if (ifExp.getElseExpression() == null) { QvtOperationalUtil.reportWarning(fEnv, NLS.bind(ValidationMessages.QvtOperationalVisitorCS_ifExpWithoutElseAssignment, new Object[] { }), ifExp.getStartPosition(), ifExp.getEndPosition()); break; } } container = container.eContainer(); } return super.visitIfExp(ifExp); } @Override public Object visitContinueExp(ContinueExp astNode) { validateBreakContinue(astNode); return super.visitContinueExp(astNode); } @Override public Object visitBreakExp(BreakExp astNode) { validateBreakContinue(astNode); return super.visitBreakExp(astNode); } private void validateBreakContinue(ImperativeExpression breakContinueExp) { boolean isLoopFound = false; EObject container = breakContinueExp.eContainer(); while (container != null) { if (container instanceof ImperativeLoopExp || container instanceof WhileExp) { isLoopFound = true; break; } if (container instanceof IterateExp || container instanceof IteratorExp) { break; } container = container.eContainer(); } if (!isLoopFound) { QvtOperationalUtil.reportError(fEnv, NLS.bind(ValidationMessages.BreakContinue_InvalidExpressionUsage, breakContinueExp.eClass().getName()), breakContinueExp.getStartPosition(), breakContinueExp.getEndPosition()); } else if (false == breakContinueExp.eContainer() instanceof ImperativeExpression && false == breakContinueExp.eContainer() instanceof IfExp) { // QVT language also defines an imperative �if-then-else� construct that is less constrained // as the corresponding OCL construct QvtOperationalUtil.reportError(fEnv, NLS.bind(ValidationMessages.BreakContinue_InvalidExpressionOwner, breakContinueExp.eClass().getName()), breakContinueExp.getStartPosition(), breakContinueExp.getEndPosition()); } } @Override public Object visitOperationCallExp(OperationCallExp<EClassifier, EOperation> callExp) { if (callExp.getReferredOperation() instanceof Constructor) { QvtOperationalUtil.reportError(fEnv, NLS.bind(ValidationMessages.OperationIsUndefined, operationString(fEnv, callExp.getReferredOperation().getName(), callExp.getArgument()), callExp.getSource() == null ? null : fEnv.getUMLReflection().getName(callExp.getSource().getType())), callExp.getStartPosition(), callExp.getEndPosition()); } return super.visitOperationCallExp(callExp); } protected String operationString(QvtOperationalEnv env, String operName, List<? extends TypedElement<EClassifier>> args) { StringBuffer result = new StringBuffer(); result.append(operName); result.append('('); for (Iterator<? extends TypedElement<EClassifier>> iter = args.iterator(); iter.hasNext();) { TypedElement<EClassifier> arg = iter.next(); EClassifier type = arg.getType(); result.append((type == null) ? (String) null : env.getUMLReflection().getName(type)); if (iter.hasNext()) { result.append(", "); //$NON-NLS-1$ } } result.append(')'); return result.toString(); } @SuppressWarnings("unchecked") @Override public Object visitInstantiationExp(InstantiationExp instantiationExp) { Boolean result = Boolean.TRUE; EClass instantiatedClass = instantiationExp.getInstantiatedClass(); Variable<EClassifier, EParameter> referredObject = null; if (instantiationExp.eContainer() instanceof Variable<?, ?>) { referredObject = (Variable<EClassifier, EParameter>) instantiationExp.eContainer(); } if(instantiatedClass == null && (referredObject != null && QVTUMLReflection.isUserModelElement(referredObject.getType()))) { instantiatedClass = (EClass)referredObject.getType(); } // declared constructors Constructor constructorOp = ASTBindingHelper.getConstructorOperation(instantiationExp); if (constructorOp != null) { if (instantiatedClass != null && (instantiatedClass.isAbstract() || instantiatedClass.isInterface())) { QvtOperationalUtil.reportError(fEnv, NLS.bind(ValidationMessages.QvtOperationalVisitorCS_canNotInstantiateAbstractType, QvtOperationalParserUtil.safeGetQualifiedName(fEnv, instantiatedClass)), instantiationExp.getStartPosition(), instantiationExp.getEndPosition()); result = Boolean.FALSE; } return result; } // default constructor if (instantiatedClass != null && !QvtOperationalStdLibrary.INSTANCE.getTransformationClass().isSuperTypeOf(instantiatedClass) && instantiationExp.getArgument().isEmpty()) { if (instantiatedClass.isAbstract() || instantiatedClass.isInterface()) { QvtOperationalUtil.reportError(fEnv, NLS.bind(ValidationMessages.QvtOperationalVisitorCS_canNotInstantiateAbstractType, QvtOperationalParserUtil.safeGetQualifiedName(fEnv, instantiatedClass)), instantiationExp.getStartPosition(), instantiationExp.getEndPosition()); result = Boolean.FALSE; } return result; } // transformation instantiation if(instantiatedClass == null || QvtOperationalStdLibrary.INSTANCE.getTransformationClass().isSuperTypeOf(instantiatedClass) == false) { EClassifier type = referredObject != null ? referredObject.getType() : instantiationExp.getType(); if(type != null) { if(type instanceof org.eclipse.ocl.types.CollectionType) { if (type.getInstanceClass() == null || type.getInstanceClass() == java.util.Collection.class) { QvtOperationalUtil.reportError(fEnv, NLS.bind(ValidationMessages.QvtOperationalVisitorCS_canNotInstantiateAbstractType, QvtOperationalParserUtil.safeGetQualifiedName(fEnv, type)), instantiationExp.getStartPosition(), instantiationExp.getEndPosition()); result = Boolean.FALSE; } } else if (type instanceof EDataType) { if (instantiationExp.getArgument().size() != 1 || !ConversionUtils.isStringType(instantiationExp.getArgument().get(0).getType())) { fEnv.reportError(NLS.bind( ValidationMessages.QvtOperationalValidationVisitor_invalidParamsForDatatypeInstantiation, instantiationExp.getArgument(), QvtOperationalParserUtil.safeGetQualifiedName(fEnv, type)), instantiationExp.getStartPosition(), instantiationExp.getEndPosition()); result = Boolean.FALSE; } } else { fEnv.reportError(NLS.bind( ValidationMessages.QvtOperationalValidationVisitor_invalidInstantiatedType, QvtOperationalParserUtil.safeGetQualifiedName(fEnv, type)), instantiationExp.getStartPosition(), instantiationExp.getEndPosition()); result = Boolean.FALSE; } } } else { Module instantiatedModule = (Module) instantiatedClass; if (!instantiatedModule.isIsBlackbox()) { ImperativeOperation mainOperation = QvtOperationalParserUtil.getMainOperation(instantiatedModule); if(mainOperation instanceof EntryOperation == false || mainOperation.getEParameters().isEmpty() == false) { String message = NLS.bind(ValidationMessages.QvtOperationalValidationVisitor_ParameterlessMainExpected, instantiatedModule.getName()); fEnv.reportError(message, instantiationExp.getStartPosition(), instantiationExp.getEndPosition()); } } } if(instantiatedClass instanceof OperationalTransformation) { OperationalTransformation transf = (OperationalTransformation) instantiatedClass; List<org.eclipse.ocl.ecore.OCLExpression> actualArgs = instantiationExp.getArgument(); EList<ModelParameter> formalArgs = transf.getModelParameter(); if(actualArgs.size() == formalArgs.size()) { int i = 0; for (ModelParameter modelParameter : formalArgs) { EClassifier paramType = modelParameter.getType(); org.eclipse.ocl.ecore.OCLExpression nextActualArg = actualArgs.get(i++); EClassifier expectedType = nextActualArg.getType(); boolean compatible = paramType instanceof ModelType && expectedType instanceof ModelType && checkCompatibleModelType((ModelType)paramType, (ModelType)expectedType); if(!compatible) { fEnv.reportError(NLS.bind( ValidationMessages.QvtOperationalValidationVisitor_incompatibleArgumentModelType, fEnv.getFormatter().formatType(nextActualArg.getType()), fEnv.getFormatter().formatType(modelParameter.getEType())), nextActualArg.getStartPosition(), nextActualArg.getEndPosition()); result = Boolean.FALSE; } // check for compatible direction kind } } else { fEnv.reportError(NLS.bind( ValidationMessages.QvtOperationalValidationVisitor_unresolvedTransformationSignature, new Object [] { fEnv.getFormatter().formatName(transf), formatArgumentList(actualArgs, fEnv.getFormatter()), formatArgumentList(formalArgs, fEnv.getFormatter()) }), instantiationExp.getStartPosition(), instantiationExp.getEndPosition()); result = Boolean.FALSE; } } return result; } private String formatArgumentList(List<?> args, FormattingHelper helper) { StringBuilder buf = new StringBuilder(); buf.append('('); int i = 0; for (Object nextArg : args) { if(i++ > 0) { buf.append(',').append(' '); } if(nextArg instanceof TypedElement<?>) { @SuppressWarnings("unchecked") TypedElement<EClassifier> typedElement = (TypedElement<EClassifier>) nextArg; buf.append(helper.formatType(typedElement.getType())); } else if(nextArg instanceof ETypedElement) { buf.append(helper.formatType(((ETypedElement) nextArg).getEType())); } else if(nextArg instanceof EClassifier) { buf.append(helper.formatType(nextArg)); } } buf.append(')'); return buf.toString(); } private boolean checkCompatibleModelType(ModelType modelType1, ModelType modelType2) { EList<EPackage> metamodel1 = modelType1.getMetamodel(); EList<EPackage> metamodel2 = modelType2.getMetamodel(); if(metamodel1.size() < metamodel2.size()) { return false; } LinkedList<String> uris1 = new LinkedList<String>(); for (EPackage ePackage : metamodel1) { if(ePackage.getNsURI() != null) { uris1.add(ePackage.getNsURI()); } else { return false; } }; LinkedList<String> uris2 = new LinkedList<String>(); for (EPackage ePackage : metamodel1) { if(ePackage.getNsURI() != null) { uris2.add(ePackage.getNsURI()); } else { return false; } }; return uris1.containsAll(uris2); } @Override public Object visitMappingCallExp(MappingCallExp mappingCallExp) { if(mappingCallExp.getReferredOperation() instanceof MappingOperation) { MappingOperation mappingOperation = (MappingOperation) mappingCallExp.getReferredOperation(); if(QvtOperationalUtil.hasAbstractOutputParameter(mappingOperation) && QvtOperationalParserUtil.isAbstractOperation(mappingOperation) && mappingOperation.getDisjunct().isEmpty()) { String errMessage = NLS.bind(ValidationMessages.directCallToAbstractMappingDisallowed, QvtOperationalParserUtil.safeGetMappingQualifiedName(fEnv, mappingOperation)); fEnv.reportError(errMessage, mappingCallExp.getStartPosition(), mappingCallExp.getEndPosition()); } Iterator<OCLExpression<EClassifier>> itArgument = mappingCallExp.getArgument().iterator(); Iterator<EParameter> itParams = mappingOperation.getEParameters().iterator(); while (itArgument.hasNext()) { OCLExpression<EClassifier> arg = itArgument.next(); MappingParameter mappingParam = (MappingParameter) itParams.next(); if (mappingParam.getKind() != DirectionKind.OUT) { continue; } if (!(arg instanceof VariableExp<?, ?> || arg instanceof PropertyCallExp<?, ?>)) { fEnv.reportError(ValidationMessages.outParamNotAnLValueError, arg.getStartPosition(), arg.getEndPosition()); } if (arg.getType() != mappingParam.getEType()) { fEnv.reportError(ValidationMessages.outParamNotSameTypeError, arg.getStartPosition(), arg.getEndPosition()); } } } return super.visitMappingCallExp(mappingCallExp); } @Override public Object visitReturnExp(ReturnExp returnExp) { OperationBody body = QvtOperationalParserUtil.findParentElement(returnExp, OperationBody.class); if(body != null && body.getOperation() != null) { EClassifier actualType = returnExp.getType(); EClassifier declaredType = body.getOperation().getEType(); if(actualType != null && declaredType != null) { int rel = TypeUtil.getRelationship(fEnv, actualType, declaredType); if((rel & UMLReflection.SUBTYPE) == 0) { String typeName = QvtOperationalParserUtil.safeGetQualifiedName(fEnv, declaredType); fEnv.reportError(NLS.bind(ValidationMessages.typeMismatchError, typeName), returnExp.getStartPosition(), returnExp.getEndPosition()); } if(returnExp.getValue() == null && !body.getOperation().getResult().isEmpty()) { fEnv.reportError(ValidationMessages.missingReturnValueError, returnExp.getStartPosition(), returnExp.getEndPosition()); } } else { if(actualType != null && returnExp.getValue() == null && declaredType == fEnv.getOCLStandardLibrary().getOclVoid()) { return Boolean.TRUE; } String typeName = QvtOperationalParserUtil.safeGetQualifiedName(fEnv, declaredType); fEnv.reportError(NLS.bind(ValidationMessages.typeMismatchError, typeName), returnExp.getStartPosition(), returnExp.getEndPosition()); } if(body instanceof MappingBody) { // do not support explicit return from mapping operation yet fEnv.reportError(ValidationMessages.returnNotAllowedInMappingYet, returnExp.getStartPosition(), returnExp.getEndPosition()); } } else { fEnv.reportError(ValidationMessages.returnUsedOutsideOperationBody, returnExp.getStartPosition(), returnExp.getEndPosition()); } return super.visitReturnExp(returnExp); } @Override public Object visitMappingOperation(MappingOperation operation) { boolean result = MappingExtensionHelper.validate(operation, fEnv); for (VarParameter resultParam : operation.getResult()) { result &= validateOutParamType(resultParam); } for (EParameter nextEParam : operation.getEParameters()) { if(nextEParam instanceof VarParameter) { VarParameter varParameter = (VarParameter) nextEParam; if(varParameter.getKind() == DirectionKind.OUT) { result &= validateOutParamType(varParameter); } } } return Boolean.TRUE.equals(super.visitMappingOperation(operation)) && result; } @Override public Object visitEntryOperation(EntryOperation entry) { EObject eContainer = entry.eContainer(); if (eContainer instanceof Library) { fEnv.reportError(ValidationMessages.QvtOperationalValidationVisitor_MainInLibraryError, entry.getStartPosition(), entry.getEndPosition()); } for (EParameter param : entry.getEParameters()) { if (param instanceof ASTNode) { QvtOperationalUtil.reportWarning(fEnv, NLS.bind(ValidationMessages.EntryOp_DepricatedParamDecl, null), ((ASTNode) param).getStartPosition(), ((ASTNode) param).getEndPosition()); } } return super.visitEntryOperation(entry); } @Override public Object visitImperativeOperation(ImperativeOperation imperativeOperation) { boolean result = true; VarParameter context = imperativeOperation.getContext(); if(context != null) { EClassifier eType = context.getEType(); if(!isValidContextualType(eType)) { result = false; String errMessage = NLS.bind(ValidationMessages.QvtOperationalValidationVisitor_invalidContextualType, QvtOperationalParserUtil.safeGetQualifiedName(fEnv, eType)); fEnv.reportError(errMessage, context.getStartPosition(), context.getEndPosition()); } } validateUniqueParamNames(imperativeOperation); // TODO - 1. validate no param name for single result param, for no explicit but default out direction kind for (EParameter nextEParam : imperativeOperation.getEParameters()) { VarParameter varParameter = (VarParameter) nextEParam; validateParamNameRequired(varParameter); } for (VarParameter nextResultParam : imperativeOperation.getResult()) { validateParamNameRequired(nextResultParam); if(nextResultParam.getKind() != DirectionKind.OUT) { fEnv.reportError(ValidationMessages.QvtOperationalValidationVisitor_resultParamDirectionMustBeOut, nextResultParam.getStartPosition(), nextResultParam.getEndPosition()); } } return Boolean.TRUE.equals(super.visitImperativeOperation(imperativeOperation)) && result; } @Override public Object visitOperationBody(OperationBody operationBody) { if(operationBody instanceof MappingBody == false) { ImperativeOperation operation = operationBody.getOperation(); if(operation.getEType() == null || operation.getEType() == fEnv.getOCLStandardLibrary().getOclVoid()) { //return Boolean.TRUE; // continue to super type visit } else { EList<org.eclipse.ocl.ecore.OCLExpression> content = operationBody.getContent(); if(operation.getResult().size() == 1) { // in case of multiple result, a tuple composed from the result variables is is always returned // so we do not care about the return statement int bodySize = content.size(); if((bodySize == 0 && !operation.isIsBlackbox()) || (bodySize > 1 && !QvtOperationalVisitorCS.isOclExpDeterministic(content.get(bodySize-1)))) { // Note: every single expression is OK from the AST point of view as // it corresponds to a single expression query String message = ValidationMessages.useReturnExpForOperationResult; int endPos = operation.getResult().isEmpty() ? operationBody.getStartPosition() : operation.getResult().get(operation.getResult().size()-1).getEndPosition(); fEnv.reportWarning(message, operation.getStartPosition(), endPos); } } } } return super.visitOperationBody(operationBody); } @Override public Object visitVariableInitExp(VariableInitExp variableInitExp) { EObject parentExp = variableInitExp.eContainer(); if (!( (parentExp instanceof OperationBody) || (parentExp instanceof BlockExp) || (parentExp instanceof AssignExp) || ((parentExp instanceof Variable<?, ?>) && (parentExp.eContainer() != null) && (parentExp.eContainer() instanceof VariableInitExp)) )) { fEnv.reportError(ValidationMessages.QvtOperationalValidationVisitor_CannotDeclareVariables, variableInitExp.getStartPosition(), variableInitExp.getEndPosition()); } return super.visitVariableInitExp(variableInitExp); } private static boolean isValidContextualType(EClassifier type) { return type != null && !QVTUMLReflection.isModuleInstance(type); } private boolean validateOutParamType(VarParameter resultParam) { EClassifier paramType = resultParam.getEType(); if(paramType != null) { if(QVTUMLReflection.isModelTypeInstance(paramType) || QVTUMLReflection.isModuleInstance(paramType) || (!QVTUMLReflection.isUserModelElement(paramType) && !(paramType instanceof CollectionType) && !(paramType == QvtOperationalStdLibrary.INSTANCE.getElementType())) ) { fEnv.reportError(NLS.bind(ValidationMessages.nonModelTypeError, QvtOperationalParserUtil.safeGetQualifiedName(fEnv, paramType)), resultParam.getStartPosition(), resultParam.getEndPosition()); return false; } } return true; } private void validateUniqueParamNames(ImperativeOperation operation) { List<? extends VarParameter> modelParams = getModelParamsInScope(operation); @SuppressWarnings("unchecked") List<? extends VarParameter> regularParams = (List<? extends VarParameter>)operation.getEParameters(); List<? extends VarParameter> resultParams = operation.getResult(); validateUniqueParamNames(regularParams, modelParams); validateUniqueParamNames(regularParams, regularParams); validateUniqueParamNames(resultParams, modelParams); validateUniqueParamNames(resultParams, regularParams); validateUniqueParamNames(resultParams, resultParams); } private static List<ModelParameter> getModelParamsInScope(ImperativeOperation mappingOperation) { Module module = QvtOperationalParserUtil.getOwningModule(mappingOperation); return (module instanceof OperationalTransformation) ? ((OperationalTransformation)module).getModelParameter() : Collections.<ModelParameter>emptyList(); } private boolean validateParamNameRequired(VarParameter param) { boolean result = true; String name = param.getName(); if(name == null || name.trim().length() == 0) { result = false; fEnv.reportError(ValidationMessages.QvtOperationalValidationVisitor_parameterNamedRequired, ((VarParameter) param).getStartPosition(), ((VarParameter) param).getEndPosition()); } return result; } private <T extends VarParameter> boolean validateUniqueParamNames(List<? extends T> params, List<? extends T> scopeParameters) { boolean result = true; for (T nextParam : params) { if(nextParam.getName() == null) { // this case is handled by continue; } T sameNameParam = findParamByName(nextParam.getName(), scopeParameters); if(sameNameParam != null && sameNameParam != nextParam) { result = false; fEnv.reportError(NLS.bind(ValidationMessages.NameAlreadyDefinedError, new Object[] { nextParam.getName() }), ((VarParameter) nextParam).getStartPosition(), ((VarParameter) nextParam).getEndPosition()); } } return result; } private static <T extends EParameter> T findParamByName(String name, List<T> parameters) { for (T nextParam : parameters) { String paramName = nextParam.getName(); if((name != null) ? name.equals(paramName) : name == paramName) { return nextParam; } } return null; } } final class CustomOclValidationVisitor extends ValidationVisitor<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint, EClass, EObject> implements QVTOperationalVisitor<Boolean>{ private QvtOperationalValidationVisitor myDelegateVisitor = null; private QvtOperationalEnv myEnv; private UMLReflection<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint> myUml; protected CustomOclValidationVisitor(QvtOperationalEnv environment) { super(environment); myEnv = environment; myUml = environment.getUMLReflection(); } void setVisitor(QvtOperationalValidationVisitor visitor) { myDelegateVisitor = visitor; } @Override public Boolean visitIfExp(org.eclipse.ocl.expressions.IfExp<EClassifier> i) { if (myDelegateVisitor != null) { myDelegateVisitor.visitIfExp(i); return Boolean.TRUE; } return super.visitIfExp(i); } public Boolean visitConstructor(Constructor constructor) { return Boolean.TRUE; } public Boolean visitConstructorBody(ConstructorBody constructorBody) { return Boolean.TRUE; } public Boolean visitContextualProperty(ContextualProperty contextualProperty) { return Boolean.TRUE; } public Boolean visitEntryOperation(EntryOperation entryOperation) { return Boolean.TRUE; } public Boolean visitHelper(Helper helper) { return Boolean.TRUE; } public Boolean visitImperativeOperation(ImperativeOperation imperativeOperation) { return Boolean.TRUE; } public Boolean visitLibrary(Library library) { return Boolean.TRUE; } public Boolean visitMappingBody(MappingBody mappingBody) { return Boolean.TRUE; } public Boolean visitMappingCallExp(MappingCallExp mappingCallExp) { return Boolean.TRUE; } public Boolean visitMappingOperation(MappingOperation mappingOperation) { return Boolean.TRUE; } public Boolean visitModelType(ModelType modelType) { return Boolean.TRUE; } public Boolean visitModule(Module module) { return Boolean.TRUE; } public Boolean visitModuleImport(ModuleImport moduleImport) { return Boolean.TRUE; } public Boolean visitObjectExp(ObjectExp objectExp) { return Boolean.TRUE; } public Boolean visitOperationBody(OperationBody operationBody) { return Boolean.TRUE; } public Boolean visitResolveExp(ResolveExp resolveExp) { return Boolean.TRUE; } public Boolean visitResolveInExp(ResolveInExp resolveInExp) { return Boolean.TRUE; } public Boolean visitVarParameter(VarParameter varParameter) { return Boolean.TRUE; } public Boolean visitAltExp(AltExp astNode) { return Boolean.TRUE; } public Boolean visitAssertExp(AssertExp astNode) { return Boolean.TRUE; } public Boolean visitAssignExp(AssignExp astNode) { return Boolean.TRUE; } public Boolean visitBlockExp(BlockExp astNode) { return Boolean.TRUE; } public Boolean visitBreakExp(BreakExp astNode) { return Boolean.TRUE; } public Boolean visitCatchtExp(CatchExp astNode) { return Boolean.TRUE; } public Boolean visitComputeExp(ComputeExp astNode) { return Boolean.TRUE; } public Boolean visitContinueExp(ContinueExp astNode) { return Boolean.TRUE; } public Boolean visitDictLiteralExp(DictLiteralExp astNode) { return Boolean.TRUE; } public Boolean visitDictLiteralPart(DictLiteralPart astNode) { return Boolean.TRUE; } public Boolean visitForExp(ForExp astNode) { return Boolean.TRUE; } public Boolean visitImperativeIterateExp(ImperativeIterateExp astNode) { return Boolean.TRUE; } public Boolean visitInstantiationExp(InstantiationExp astNode) { return Boolean.TRUE; } public Boolean visitLogExp(LogExp astNode) { return Boolean.TRUE; } public Boolean visitOrderedTupleLiteralExp(OrderedTupleLiteralExp astNode) { return Boolean.TRUE; } public Boolean visitOrderedTupleLiteralPart(OrderedTupleLiteralPart astNode) { return Boolean.TRUE; } public Boolean visitRaiseExp(RaiseExp astNode) { return Boolean.TRUE; } public Boolean visitReturnExp(ReturnExp astNode) { return Boolean.TRUE; } public Boolean visitSwitchExp(SwitchExp astNode) { return Boolean.TRUE; } public Boolean visitTryExp(TryExp astNode) { return Boolean.TRUE; } public Boolean visitUnlinkExp(UnlinkExp astNode) { return Boolean.TRUE; } public Boolean visitUnpackExp(UnpackExp astNode) { return Boolean.TRUE; } public Boolean visitVariableInitExp(VariableInitExp astNode) { return Boolean.TRUE; } public Boolean visitWhileExp(WhileExp astNode) { return Boolean.TRUE; } @Override public Boolean visitCollectionLiteralExp(CollectionLiteralExp<EClassifier> cl) { if (cl.getType() instanceof ListType) { return Boolean.TRUE; } return super.visitCollectionLiteralExp(cl); } private String getElementName(Object element) { return (element == null)? null : myUml.getName(element); } private boolean isStatic(Object feature) { return (myUml != null) && myUml.isStatic(feature); } public Boolean visitFeatureCallExp(FeatureCallExp<EClassifier> exp) { if (exp.isMarkedPre()) { // check for a postcondition constraint if (!myEnv.isInPostcondition(exp)) { String message = ValidationMessages.AtPreInPostcondition_ERROR_; return validatorError(exp, message, "visitFeatureCallExp");//$NON-NLS-1$ } } // check for static access to non-static features if (exp.getSource() != null) { OCLExpression<EClassifier> source = exp.getSource(); if (source.getType() instanceof TypeType<?, ?>) { @SuppressWarnings("unchecked") TypeType<EClassifier, ?> typeType = (TypeType<EClassifier, ?>) source.getType(); Object feature = null; if (exp instanceof OperationCallExp<?, ?>) { feature = ((OperationCallExp<?, ?>) exp).getReferredOperation(); // operation must either be defined by the TypeType // (e.g., allInstances()) or be a static operation of // the referred classifier if (!(typeType.oclOperations().contains(feature) || isStatic(feature))) { String message = ValidationMessages.bind( ValidationMessages.NonStaticOperation_ERROR_, getElementName(feature)); return validatorError(exp, message, "visitFeatureCallExp");//$NON-NLS-1$ } } else if (exp instanceof PropertyCallExp<?, ?>) { feature = ((PropertyCallExp<?, ?>) exp).getReferredProperty(); // property must be a static attribute of // the referred classifier if (!isStatic(feature)) { String message = ValidationMessages.bind( ValidationMessages.NonStaticAttribute_ERROR_, getElementName(feature)); return validatorError(exp, message, "visitFeatureCallExp");//$NON-NLS-1$ } } } } return Boolean.FALSE; } // Overridden due to bug 271987. FQN operations validation in MDT OCL // cannot be applied here. See comment below. @Override public Boolean visitOperationCallExp( OperationCallExp<EClassifier, EOperation> oc) { OCLExpression<EClassifier> source = oc.getSource(); EOperation oper = oc.getReferredOperation(); int opcode = oc.getOperationCode(); List<OCLExpression<EClassifier>> args = oc.getArgument(); if (oper == null) { String message = ValidationMessages.bind( ValidationMessages.NullOperation_ERROR_, oc.toString()); return validatorError(oc, message, "visitOperationCallExp");//$NON-NLS-1$ } if (source == null) { String message = ValidationMessages.bind( ValidationMessages.NullSourceOperation_ERROR_, oc.toString()); return validatorError(oc, message, "visitOperationCallExp");//$NON-NLS-1$ } EClassifier sourceType = source.getType(); String operName = getElementName(oper); for (OCLExpression<EClassifier> expr : args) { expr.accept(this); } if (visitFeatureCallExp(oc)) { return Boolean.TRUE; } if (opcode == PredefinedType.OCL_IS_NEW) { // oclIsNew() may only be used in postcondition constraints if (!myEnv.isInPostcondition(oc)) { return validatorError(oc, ValidationMessages.OCLIsNewInPostcondition_ERROR_, "visitOperationCallExp");//$NON-NLS-1$ } } source.accept(this); // NB: This check is incorrect for FQN operation calls // // Check argument conformance. // O oper1 = env.lookupOperation(sourceType, // operName, args); // if (oper1 != oper) { // String message = ValidationMessages.bind( // ValidationMessages.IllegalOperation_ERROR_, // oc.toString()); // return validatorError(oc, message, "visitOperationCallExp");//$NON-NLS-1$ // } if (!myUml.isQuery(oper)) { String message = ValidationMessages.bind( ValidationMessages.NonQueryOperation_ERROR_, getElementName(oper)); return validatorError(oc, message, "visitOperationCallExp");//$NON-NLS-1$ } EClassifier resultType; if (TypeUtil.isStandardLibraryFeature(myEnv, sourceType, oper)) { if (opcode != OCLStandardLibraryUtil.getOperationCode(operName)) { String message = ValidationMessages.bind( ValidationMessages.IllegalOpcode_ERROR_, operName); return validatorError(oc, message, "visitOperationCallExp");//$NON-NLS-1$ } resultType = TypeUtil .getResultType(oc, myEnv, sourceType, oper, args); if (resultType == null) { // maybe this operation was an "extra" contribution by a // custom environment implementation resultType = getOCLType(oper); } } else if (TypeUtil.isOclAnyOperation(myEnv, oper)) { // source is an EClass, an enumeration, or a user data type and // operation is defined by OclAny (not the source, itself) if (opcode != OCLStandardLibraryUtil.getOclAnyOperationCode(operName)) { String message = ValidationMessages.bind( ValidationMessages.IllegalOpcode_ERROR_, operName); return validatorError(oc, message, "visitOperationCallExp");//$NON-NLS-1$ } resultType = TypeUtil .getResultType(oc, myEnv, sourceType, oper, args); if (resultType == null) { resultType = getOCLType(oper); } } else { // user-defined operation resultType = TypeUtil .getResultType(oc, myEnv, sourceType, oper, args); } if (!TypeUtil.exactTypeMatch(myEnv, resultType, oc.getType())) { String message = ValidationMessages.bind( ValidationMessages.TypeConformanceOperation_ERROR_, oc.getType().toString()); return validatorError(oc, message, "visitOperationCallExp");//$NON-NLS-1$ } if ((opcode == PredefinedType.TO_LOWER) || (opcode == PredefinedType.TO_UPPER)) { // check settings for using non-standard closure iterator ProblemHandler.Severity sev = ProblemHandler.Severity.OK; BasicEnvironment benv = OCLUtil.getAdapter(myEnv, BasicEnvironment.class); if (benv != null) { sev = benv.getValue(ProblemOption.STRING_CASE_CONVERSION); } if ((sev != null) && (sev != ProblemHandler.Severity.OK)) { benv.problem( sev, ProblemHandler.Phase.VALIDATOR, ValidationMessages.bind( ValidationMessages.NonStd_Operation_, (opcode == PredefinedType.TO_LOWER) ? "String::toLower()" //$NON-NLS-1$ : "String::toUpper()"), "operationCallExp", //$NON-NLS-1$ //$NON-NLS-2$ oc); } } return Boolean.TRUE; } String getUMLName(Object element) { return (element == null)? null : uml.getName(element); } //@Override public Boolean visitIteratorExp( org.eclipse.ocl.expressions.IteratorExp<EClassifier, EParameter> ie) { EClassifier type = ie.getType(); OCLExpression<EClassifier> body = ie.getBody(); OCLExpression<EClassifier> source = ie.getSource(); List<Variable<EClassifier, EParameter>> iterators = ie.getIterator(); String name = ie.getName(); if (type == null || name == null || source == null || body == null || iterators.isEmpty()) { QvtOperationalUtil.reportError(myEnv, NLS.bind(ValidationMessages.QvtOperationalVisitorCS_iterateExpIncomplete, new Object[] { }), ie.getStartPosition(), ie.getEndPosition()); return Boolean.TRUE; } int opcode = 0; if (source.getType() instanceof PredefinedType<?>) { opcode = OCLStandardLibraryUtil.getOperationCode(name); } // Validate all of the iterate parts source.accept(this); body.accept(this); switch (opcode) { case PredefinedType.FOR_ALL: case PredefinedType.EXISTS: case PredefinedType.IS_UNIQUE: if (type != env.getOCLStandardLibrary().getBoolean()) { QvtOperationalUtil.reportError(myEnv, NLS.bind(ValidationMessages.QvtOperationalVisitorCS_TypeConformanceIteratorResult_ERROR_, new Object[] { }), ie.getStartPosition(), ie.getEndPosition()); return Boolean.TRUE; } } if (opcode == PredefinedType.COLLECT) { if (source.getType() instanceof SequenceType<?, ?> || source.getType() instanceof OrderedSetType<?, ?> || source.getType() instanceof ListType) { if (!(type instanceof SequenceType<?, ?>)) { QvtOperationalUtil.reportError(myEnv, NLS.bind(ValidationMessages.QvtOperationalVisitorCS_TypeConformanceCollectSequence_ERROR_, new Object[] { }), ie.getStartPosition(), ie.getEndPosition()); return Boolean.TRUE; } } else if (!(type instanceof BagType<?, ?>)) { QvtOperationalUtil.reportError(myEnv, NLS.bind(ValidationMessages.QvtOperationalVisitorCS_TypeConformanceCollectBag_ERROR_, new Object[] { }), ie.getStartPosition(), ie.getEndPosition()); return Boolean.TRUE; } } switch (opcode) { case PredefinedType.SELECT: case PredefinedType.REJECT: if (source.getType() instanceof ListType && type instanceof SequenceType<?, ?>) { // OK } else if (!TypeUtil.exactTypeMatch(env, type, source.getType())) { QvtOperationalUtil.reportError(myEnv, NLS.bind(ValidationMessages.QvtOperationalVisitorCS_TypeConformanceSelectReject_ERROR_, new Object[] { }), ie.getStartPosition(), ie.getEndPosition()); return Boolean.TRUE; } } switch (opcode) { case PredefinedType.SELECT: case PredefinedType.REJECT: case PredefinedType.FOR_ALL: case PredefinedType.ANY: case PredefinedType.EXISTS: case PredefinedType.ONE: if (body.getType() != env.getOCLStandardLibrary().getBoolean()) { QvtOperationalUtil.reportError(myEnv, NLS.bind(ValidationMessages.QvtOperationalVisitorCS_TypeConformanceIteratorBodyBoolean_ERROR_, new Object[] { }), ie.getStartPosition(), ie.getEndPosition()); return Boolean.TRUE; } } EClassifier sourceType = source.getType(); if (!(sourceType instanceof org.eclipse.ocl.types.CollectionType<?, ?>)) { QvtOperationalUtil.reportError(myEnv, NLS.bind(ValidationMessages.QvtOperationalVisitorCS_IteratorSource_ERROR_, new Object[] { }), ie.getStartPosition(), ie.getEndPosition()); return Boolean.TRUE; } if (opcode == PredefinedType.CLOSURE) { // check settings for using non-standard closure iterator ProblemHandler.Severity sev = ProblemHandler.Severity.OK; BasicEnvironment benv = OCLUtil.getAdapter(env, BasicEnvironment.class); if (benv != null) { sev = benv.getValue(ProblemOption.CLOSURE_ITERATOR); if ((sev != null) && (sev != ProblemHandler.Severity.OK)) { @SuppressWarnings("restriction") String message = OCLMessages.bind(OCLMessages.NonStd_Iterator_, PredefinedType.CLOSURE_NAME); benv.problem(sev, ProblemHandler.Phase.VALIDATOR, message, "iteratorExp", ie); //$NON-NLS-1$ } } if (!(type instanceof SetType<?, ?>) && !(type instanceof OrderedSetType<?, ?>)) { QvtOperationalUtil.reportError(myEnv, NLS.bind(ValidationMessages.QvtOperationalVisitorCS_TypeConformanceClosure_ERROR_, new Object[] { }), ie.getStartPosition(), ie.getEndPosition()); return Boolean.TRUE; } // recursive reference must be to a type conforming // to the source, otherwise it isn't recursive // checked above that the source is a collection type @SuppressWarnings("unchecked") org.eclipse.ocl.types.CollectionType<EClassifier, EOperation> sourceCT = (org.eclipse.ocl.types.CollectionType<EClassifier, EOperation>) source.getType(); @SuppressWarnings("unchecked") org.eclipse.ocl.types.CollectionType<EClassifier, EOperation> bodyCT = (org.eclipse.ocl.types.CollectionType<EClassifier, EOperation>) type; EClassifier sourceElementType = sourceCT.getElementType(); EClassifier bodyType = bodyCT.getElementType(); if (!TypeUtil.compatibleTypeMatch(env, bodyType, sourceElementType)) { @SuppressWarnings("restriction") String message = OCLMessages.bind( OCLMessages.ElementTypeConformanceClosure_ERROR_, getUMLName(bodyType), getUMLName(sourceElementType)); return validatorError(ie, message, "visitIteratorExp");//$NON-NLS-1$ } } if (opcode == PredefinedType.SORTED_BY) { // the body type must be comparable (in OCL terms, it must // define the '<' operation) if (!uml.isComparable(body.getType())) { // FIXME: Should be more specifically about the sortedBy iterator @SuppressWarnings("restriction") String message = OCLMessages.bind( OCLMessages.OperationNotFound_ERROR_, PredefinedType.LESS_THAN_NAME, getUMLName(body.getType())); return validatorError(ie, message, "visitIteratorExp");//$NON-NLS-1$ } } // validate the number of iterators switch (opcode) { case PredefinedType.FOR_ALL: case PredefinedType.EXISTS: if (iterators.size() > 2) { @SuppressWarnings("restriction") String message = OCLMessages.bind( OCLMessages.TooManyIteratorVariables_ERROR_, ie.getName()); return validatorError(ie, message, "visitIteratorExp");//$NON-NLS-1$ } break; default: if (iterators.size() > 1) { @SuppressWarnings("restriction") String message = OCLMessages.bind( OCLMessages.TooManyIteratorVariables_ERROR_, ie.getName()); return validatorError(ie, message, "visitIteratorExp");//$NON-NLS-1$ } } for (Variable<EClassifier, EParameter> loopiter : iterators) { // Validate the iterator expressions loopiter.accept(this); if (loopiter.getInitExpression() != null) { QvtOperationalUtil.reportError(myEnv, NLS.bind(ValidationMessages.QvtOperationalVisitorCS_IterateExpLoopVarInit_ERROR_, new Object[] { }), ie.getStartPosition(), ie.getEndPosition()); return Boolean.TRUE; } @SuppressWarnings("unchecked") org.eclipse.ocl.types.CollectionType<EClassifier, EOperation> ct = (org.eclipse.ocl.types.CollectionType<EClassifier, EOperation>) sourceType; if (!TypeUtil.exactTypeMatch(env, loopiter.getType(), ct.getElementType())) { QvtOperationalUtil.reportError(myEnv, NLS.bind(ValidationMessages.QvtOperationalVisitorCS_TypeConformanceIteratorExpLoopVar_ERROR_, new Object[] { }), ie.getStartPosition(), ie.getEndPosition()); return Boolean.TRUE; } } return Boolean.TRUE; } public Boolean visitVariableExp(VariableExp<EClassifier, EParameter> variableExp) { if (variableExp.getType() instanceof TupleType && variableExp.getReferredVariable().getName().equals(Environment.RESULT_VARIABLE_NAME)) { EParameter param = variableExp.getReferredVariable().getRepresentedParameter(); if (param == null) { // tuple result variable doesn't represent a parameter => deprecated synthetic result tuple // https://bugs.eclipse.org/bugs/show_bug.cgi?id=432112 QvtOperationalUtil.reportWarning(myEnv, NLS.bind(ValidationMessages.QvtOperationalVisitorCS_deprecatedResultTupleAccess, new Object[] {}), variableExp.getStartPosition(), variableExp.getEndPosition()); } } return super.visitVariableExp(variableExp); } }