-- @atlcompiler emftvm -- @path ATL=/emftvm.compiler/metamodels/ATL.ecore -- @path Problem=/org.eclipse.m2m.atl.common/bin/org/eclipse/m2m/atl/common/resources/Problem.ecore -- $Id$ module ATLWFR; create OUT : Problem from IN : ATL; ------------------------------------------------------------------------------- -- HELPERS -------------------------------------------------------------------- ------------------------------------------------------------------------------- -- This helper provides a set containing the name of the IteratorExp elements -- that accepts a single Iterator. -- CONTEXT: thisModule -- RETURN: Set(String) helper def: singleIteratorExps : Set(String) = Set{ 'isUnique', 'any', 'one', 'collect', 'select', 'reject', 'collectNested', 'sortedBy' }; -- This helper provides a set containing the name of the IteratorExp elements -- for which several Iterators may be declared according to the OCL spec. -- CONTEXT: thisModule -- RETURN: Set(String) helper def: multiIteratorExps : Set(String) = Set{'exists', 'forAll'}; -- This helper computes the set of existing CollectionType elements within the -- input ATL Unit. -- CONTEXT: thisModule -- RETURN: Set(ATL!CollectionType) helper def: collectionTypes : Set(ATL!CollectionType) = ATL!CollectionType.allInstances(); -- This helper computes a sequence containing all the OclModel elements that -- are used in the input ATL Unit. -- CONTEXT: thisModule -- RETURN: Sequence(ATL!OclModel) helper def: allModels : Set(ATL!OclModel) = let atlModules : Sequence(ATL!Module) = ATL!Module.allInstances() in if atlModules->notEmpty() then atlModules->iterate(m; acc : Set(ATL!OclModel) = Set{} | acc->union(m.inModels->asSet())->union(m.outModels->asSet())) else Set{} endif; -- This helper computes the Query element that corresponds to the input ATL -- Unit. If the input ATL Unit corresponds to a Module (eg a transformation), -- the computed value is OclUndefined. -- CONTEXT: thisModule -- RETURN: ATL!Query helper def: queryElt : ATL!Query = let atlQueries : Sequence(ATL!Query) = ATL!Query.allInstances() in if atlQueries->isEmpty() then OclUndefined else atlQueries->first() endif; -- This helper computes a sequence containing all the Binding elements that -- are defined in the input ATL Unit. -- CONTEXT: thisModule -- RETURN: Sequence(ATL!Binding) helper def: allBindings : Sequence(ATL!Binding) = ATL!Binding.allInstances()->asSequence(); -- This helper computes a sequence containing all the Pattern elements that -- are defined in the input ATL Unit. -- CONTEXT: thisModule -- RETURN: Sequence(ATL!InPattern) helper def: allInPatterns : Sequence(ATL!InPattern) = ATL!InPattern.allInstances()->asSequence(); -- This helper computes a sequence containing all the InPatternElement elements -- that are defined in the input ATL Unit. -- CONTEXT: thisModule -- RETURN: Sequence(ATL!InPatternElement) helper def: allInPatternElts : Sequence(ATL!InPatternElement) = ATL!InPatternElement.allInstances()->asSequence(); -- This helper computes a sequence containing all the OutPatternElement -- elements that are defined in the input ATL Unit. -- CONTEXT: thisModule -- RETURN: Sequence(ATL!OutPatternElement) helper def: allOutPatternElts : Sequence(ATL!OutPatternElement) = ATL!OutPatternElement.allInstances()->asSequence(); -- This helper computes a sequence containing all the Rule elements that are -- defined in the input ATL Unit. If the input Unit is a query, the computed -- sequence is empty. -- CONTEXT: thisModule -- RETURN: Sequence(ATL!Rule) helper def: allRules : Sequence(ATL!Rule) = ATL!Rule.allInstances()->asSequence(); -- This helper computes a sequence containing all the Helper elements that are -- defined in the input ATL Unit. -- CONTEXT: thisModule -- RETURN: Sequence(ATL!Helper) helper def: allHelpers : Sequence(ATL!Helper) = ATL!Helper.allInstances()->asSequence(); -- This helper computes a sequence containing all the LoopExp elements that are -- defined in the input ATL Unit. -- CONTEXT: thisModule -- RETURN: Sequence(ATL!LoopExp) helper def: allLoopExps : Sequence(ATL!LoopExp) = ATL!LoopExp.allInstances()->asSequence(); -- This helper computes a sequence containing all the IterateExp elements that -- are defined in the input ATL Unit. -- CONTEXT: thisModule -- RETURN: Sequence(ATL!IterateExp) helper def: allIterateExps : Sequence(ATL!IterateExp) = ATL!IterateExp.allInstances()->asSequence(); -- This helper computes a sequence containing all the VariableDeclaration -- elements that are associated with the contextual Rule. These declarations -- can be of 3 different kinds: -- * the variables declared for the rule; -- * the OutPatternElements of the rule; -- * the InPatternElements of the rule if this last is a MatchedRule. -- CONTEXT: ATL!Rule -- RETURN: Sequence(ATL!VariableDeclaration) helper context ATL!Rule def: namedElements : Sequence(ATL!VariableDeclaration) = Sequence{ if self.oclIsTypeOf(ATL!MatchedRule) then if not self.inPattern.oclIsUndefined() then self.inPattern.elements->asSequence() else Sequence{} endif else Sequence{} endif, self.variables->asSequence(), if not self.outPattern.oclIsUndefined() then self.outPattern.elements->asSequence() else Sequence{} endif }->flatten(); -- This helper computes the Rule element in which the contextual PatterElement -- is declared. This is achieved by returning the Rule referred by the "rule" -- reference of the Pattern that conatins the contexual PatternElement. This -- last one is accessed through the "outPattern" reference if the contextual -- PatternElement is an OutPatternElement, throught the "inPattern" if it is -- an InPatternElement. -- CONTEXT: ATL!PatternElement -- RETURN: ATL!Rule helper context ATL!PatternElement def: "rule" : ATL!Rule = if self.oclIsKindOf(ATL!OutPatternElement) then self.outPattern."rule" else self.inPattern."rule" endif; -- This helper computes a sequence containing the VariableDeclarations that -- precede the contextual VariableDeclaration in its namespace. -- If the contextual VariableDeclaration is a PatternElement, the helper only -- returns this VD. -- Otherwise, it computes the container of the contextual VD. If the container -- is a LetExp, it returns a Sequence composed of the VD, and the results of -- the calls of the getUpD helper on the calculated container. -- If the container is an IteratorExp, the helper returns a Sequence composed -- of the VD and the results of the call of getUpD on the computed container. -- If the container is an IterateExp, the helper a Sequence containing the same -- elements that the one computed for an IteratorExp. -- Otherwise, the helper returns the only contextual VD as default value. -- CONTEXT: ATL!VariableDeclaration -- RETURN: Sequence(ATL!VariableDeclaration) helper context ATL!VariableDeclaration def: getDeclarations() : Sequence(ATL!VariableDeclaration) = if self.oclIsKindOf(ATL!PatternElement) then Sequence{self} else let container : OclAny = self.refImmediateComposite() in if container.oclIsTypeOf(ATL!LetExp) then Sequence{ self, container.getUpD() }->flatten() else if container.oclIsTypeOf(ATL!IteratorExp) then Sequence{ self, container.getUpD() }->flatten() else if container.oclIsTypeOf(ATL!IterateExp) then Sequence{ self, container.getUpD() }->flatten() else Sequence{ self }->flatten() endif endif endif endif; -- This helper computes a sequence containing the VariableDeclarations that are -- defined higher than the contextual OclExpression in its namespace tree. -- The helper first computes the container of the contextual OclExp. If this -- container is undefined, it retuns an empty sequence. -- Otherwise, if this container is not an OclExpression: -- * If the container is a RuleVariableDeclaration, the helper returns a -- sequence containing all the named elements of the rule that contains this -- InPattern. -- * If the container is a Binding, the helper returns a sequence containing -- all the named elements of the rule that contains this Binding. -- Otherwise, if the computed container is an OclExpression: -- * If the container is a LetExp, the helper returns a sequence composed of -- the LetExp variable and the result of its recursive call on the LetExp. -- * If the container is an IfExp, the helper returns a sequence composed of -- the result of its recursive call on the IfExp. -- * If the container is an IteratorExp, if the contextual OclExp is the -- source of the IteratorExp then the helper returns the result of its -- recursive call on the IteratorExp, else it returns this result with the -- "iterators" elements of the IteratorExp. -- * If the container is an IterateExp, the helper returns the same sequences -- that for an IteratorExp, with the additional "result" element in case the -- contextual OclExp is not the source of the IterateExp. -- Otherwise, the helper returns an empty sequence as default value. -- CONTEXT: ATL!OclExpression -- RETURN: Sequence(ATL!VariableDeclaration) helper context ATL!OclExpression def: getUpD() : Sequence(ATL!VariableDeclaration) = let container : ATL!Element = self.refImmediateComposite() in if container.oclIsUndefined() then Sequence{} else if not container.oclIsKindOf(ATL!OclExpression) then if container.oclIsTypeOf(ATL!RuleVariableDeclaration) then Sequence{ container."rule".namedElements }->flatten() else if container.oclIsTypeOf(ATL!Binding) then Sequence{ container.outPatternElement."rule".namedElements }->flatten() else Sequence{} endif endif else if container.oclIsTypeOf(ATL!LetExp) then Sequence{ container.variable, container.getUpD() }->flatten() else if container.oclIsTypeOf(ATL!IfExp) then Sequence{ container.getUpD() }->flatten() else if container.oclIsTypeOf(ATL!IteratorExp) then if container.source = self then Sequence{ container.getUpD() }->flatten() else Sequence{ container.iterators, container.getUpD() }->flatten() endif else if container.oclIsTypeOf(ATL!IterateExp) then if container.source = self then Sequence{ container.getUpD() }->flatten() else Sequence{ container.iterators, container.result, container.getUpD() }->flatten() endif else Sequence{} endif endif endif endif endif endif; -- This helper computes a sequence containing the VariableDeclarations that are -- defined lower than the contextual OclExpression in its namespace tree. -- If the contextual OclExpression is a LetExp, the helper returns a sequence -- composed of the LetExp variable and the result of its recursive call on the -- "in_" reference of the LetExp. -- Otherwise, if the contextual OclExpression is a IfExp, the helper returns a -- sequence composed of the results of its recursive calls on the "condition", -- "thenExpression" and "elseExpression" references of the IfExp. -- Otherwise, if the contextual OclExpression is an IteratorExp, the helper -- returns a sequence composed of the IteratorExp iterators along with the -- results of its recursive calls on the "source" and the "body" references -- of the IteratorExp. -- Otherwise, if the contextual OclExpression is an IterateExp, the helper -- returns the sequence returned for an IteratorExp with its additional result -- element. -- Otherwise, the helper returns an empty sequence as default value. -- CONTEXT: ATL!OclExpression -- RETURN: Sequence(ATL!VariableDeclaration) --helper context ATL!OclExpression -- def: getDownD() : Sequence(ATL!VariableDeclaration) = -- if self.oclIsTypeOf(ATL!LetExp) then -- Sequence{ -- self.variable, -- self.in_.getDownD() -- }->flatten() -- else if self.oclIsTypeOf(ATL!IfExp) then -- Sequence{ -- self.condition.getDownD(), -- self.thenExpression.getDownD(), -- self.elseExpression.getDownD() -- }->flatten() -- else if self.oclIsTypeOf(ATL!IteratorExp) then -- Sequence{ -- self.iterators, -- self.source.getDownD(), -- self.body.getDownD() -- }->flatten() -- else if self.oclIsTypeOf(ATL!IterateExp) then -- Sequence{ -- self.iterators, -- self.result, -- self.source.getDownD(), -- self.body.getDownD() -- }->flatten() -- else Sequence{} -- endif endif endif endif; -- This helper returns the root composite (container) of the contextual -- OclExpression. For this purpose, the helper first computes the immediate -- composite of the contextual OclExpression. -- If this container is undefined, the helper returns OclUndefined. -- Otherwise, if it is a kind of OclExpression, the helper returns the value -- provided by its recursive call on the computed container. -- Finally, if this container is not an OclExpression, the root composite has -- been reached (Binding/InPattern/Operation/Query/Attribute) and is returned. -- CONTEXT: ATL!OclExpression -- RETURN: ATL!Element helper context ATL!OclExpression def: getRootComposite() : OclAny = let container : OclAny = self.refImmediateComposite() in if container.oclIsUndefined() then OclUndefined else if container.oclIsKindOf(ATL!OclExpression) then container.getRootComposite() else container endif endif; ------------------------------------------------------------------------------- -- RULES ---------------------------------------------------------------------- ------------------------------------------------------------------------------- -- Rule 'FreeVariableIsSelfOrThisModule' -- This rule generates an 'error' Problem for each VariableDeclaration that has -- no composite, and whose name is different from both 'self' and 'thisModule'. -- The VariableExps that have not been previously declared in an ATL file are -- associated with a new VariableDeclaration without any composite in the -- correspoding ATL model. nodefault rule FreeVariableIsSelfOrThisModule { from s : ATL!VariableDeclaration ( s.refImmediateComposite().oclIsUndefined() and s.varName <> 'self' and s.varName <> 'thisModule' ) to t : Problem!Problem ( severity <- #error, location <- if s.variableExp->isEmpty() then s.location else s.variableExp->first().location endif, description <- 'variable \'' + s.varName + '\' undefined' ) } -- Rule 'ModelNameIsUnique' -- This rule generates an 'error' Problem when there exists models that have -- the same name that the checked model. nodefault rule ModelNameIsUnique { from s : ATL!OclModel ( thisModule.allModels->exists(e | e.name = s.name and e <> s) ) to t : Problem!Problem ( severity <- #error, location <- s.location, description <- 'model \'' + s.name + '\' already defined' ) } -- Rule 'RuleNameIsUnique' -- This rule generates an 'error' Problem when there exists rules that have -- the same name that the checked rule. nodefault rule RuleNameIsUnique { from s : ATL!Rule ( thisModule.allRules->exists(e | e.name = s.name and e <> s) ) to t : Problem!Problem ( severity <- #error, location <- s.location, description <- 'rule \'' + s.name + '\' already defined' ) } -- Rule 'HelperSignatureIsUnique' -- This rule generates an 'error' Problem when there exists helpers that have -- the same signature that the checked helper. -- Note that in current implementation, the helper signature corresponds to the -- name and the context of the helper. nodefault rule HelperSignatureIsUnique { from s : ATL!Helper ( thisModule.allHelpers ->exists(e | e <> s and -- name check s.definition.feature.name = e.definition.feature.name and -- context check ( if not s.definition.context_.oclIsUndefined() then if not e.definition.context_.oclIsUndefined() then if not s.definition.context_.context_.name.oclIsUndefined() then if not e.definition.context_.context_.name.oclIsUndefined() then s.definition.context_.context_.name = e.definition.context_.context_.name and if s.definition.feature.oclIsTypeOf(ATL!Operation) and e.definition.feature.oclIsTypeOf(ATL!Operation) then -- parameters check if not s.definition.feature.parameters.oclIsUndefined() then if not e.definition.feature.parameters.oclIsUndefined() then if s.definition.feature.parameters.size() = e.definition.feature.parameters.size() then s.definition.feature.parameters -> collect(param | param.type.oclType()) -> flatten() -> includesAll( e.definition.feature.parameters -> collect(param | param.type.oclType()) -> flatten() ) else false endif else false endif else false endif else false endif else false endif else e.definition.context_.context_.name.oclIsUndefined() endif else false endif else e.definition.context_.oclIsUndefined() endif ) ) ) to t : Problem!Problem ( severity <- #error, location <- s.location, description <- 'helper \'' + s.definition.feature.name + '\' already defined' ) } -- DISABLED ---- Rule 'BindingNameIsUniqueInPattern' ---- This rule generates an 'error' Problem when there exists, in a same pattern, ---- bindings that have the same name that the checked binding. --rule BindingNameIsUniqueInPattern { -- from -- s : ATL!Binding ( -- s.outPatternElement.bindings -- ->exists(e | e.propertyName = s.propertyName and e <> s) -- ) -- to -- t : Problem!Problem ( -- severity <- #error, -- location <- s.location, -- description <- -- 'binding \'' + s.propertyName + '\' already defined in pattern' -- ) --} -- Rule 'PatternNameIsUniqueInRule' -- This rule generates an 'error' Problem when there exists, in a same rule, -- some named elements (InPatternElement/OutPatternElement/ -- RuleVariableDeclaration) that have the same name that the checked pattern. nodefault rule PatternNameIsUniqueInRule { from s : ATL!PatternElement ( s."rule".namedElements ->exists(e | e.varName = s.varName and e <> s) ) to t : Problem!Problem ( severity <- #error, location <- s.location, description <- 'pattern or variable named \'' + s.varName + '\' already defined in rule' ) } -- Rule 'VariableNameIsUniqueInRule' -- This rule generates an 'error' Problem when there exists, in a same rule, -- some named elements (InPatternElement/OutPatternElement/ -- RuleVariableDeclaration) that have the same name that the checked rule -- variable declaration. nodefault rule VariableNameIsUniqueInRule { from s : ATL!RuleVariableDeclaration ( s."rule".namedElements ->exists(e | e.varName = s.varName and e <> s) ) to t : Problem!Problem ( severity <- #error, location <- s.location, description <- 'pattern or variable named \'' + s.varName + '\' already defined in rule' ) } -- Rule 'NoHelperWithCollectionAsContext' -- This rule generates an 'error' Problem for each Helper defined with a -- collection type as context. -- Note that this problem is due to the limitations of the current -- implementation nodefault rule NoHelperWithCollectionAsContext { from s : ATL!Helper ( if s.definition.context_.oclIsUndefined() then false else thisModule.collectionTypes ->exists(e | s.definition.context_.context_ = e) endif ) to t : Problem!Problem ( severity <- #error, location <- s.location, description <- 'helper \'' + s.definition.feature.name + '\': current implementation does not ' + 'support helpers with collection context' ) } -- Rule 'NoSelfOrThisModuleVariableDeclaration' -- This rule generates an 'error' Problem for each declaration of a variable -- named 'self' or 'thisModule' in the ATL program. -- Considered variable declarations must have a non-undefined immediate -- composite since the input ATL model may already include a 'self' and a -- 'thisModule' VD without any immediate composite that correspond to the -- global declarations of the 'self' and 'thisModule' variables. nodefault rule NoSelfOrThisModuleVariableDeclaration { from s : ATL!VariableDeclaration ( not s.refImmediateComposite().oclIsUndefined() and (s.varName = 'self' or s.varName = 'thisModule') ) to t : Problem!Problem ( severity <- #error, location <- s.location, description <- 'helper \'' + s.varName + '\' is not valid variable name' ) } -- Rule 'NoSelfVariableInRule' -- This rule generates an 'error' Problem for each 'self' variable expression -- that is contained by a rule element. nodefault rule NoSelfVariableInRule { from s : ATL!VariableExp ( if s.referredVariable.oclIsUndefined() then false else s.referredVariable.varName = 'self' and ( let rComp : OclAny = s.getRootComposite() in rComp.oclIsTypeOf(ATL!Binding) or rComp.oclIsTypeOf(ATL!InPattern) ) endif ) to t : Problem!Problem ( severity <- #error, location <- s.location, description <- 'rule \'' + s.referredVariable.varName + '\': use of the \'self\' variable prohibited in rules' ) } -- Rule 'NoResolveTempInSourcePattern' -- This rule generates an 'error' Problem for each call of the -- 'thisModule.resolveTemp()' operation within a source pattern of a rule. nodefault rule NoResolveTempInSourcePattern { from s : ATL!OperationCallExp ( s.operationName = 'resolveTemp' and ( if s.source.oclIsTypeOf(ATL!VariableExp) then if s.source.referredVariable.oclIsUndefined() then false else s.source.referredVariable.varName = 'thisModule' endif else false endif ) and s.getRootComposite().oclIsTypeOf(ATL!InPattern) ) to t : Problem!Problem ( severity <- #error, location <- s.location, description <- 'rule \'' + s.getRootComposite()."rule".name + '\': use of \'thisModule.resolveTemp()\' function ' + 'is prohibited in source patterns' ) } -- Rule 'NoResolveTempInModuleAttribute' -- This rule generates an 'error' Problem for each call of the -- 'thisModule.resolveTemp()' operation within a model attribute. nodefault rule NoResolveTempInModuleAttribute { from s : ATL!OperationCallExp ( s.operationName = 'resolveTemp' and ( if s.source.oclIsTypeOf(ATL!VariableExp) then if s.source.referredVariable.oclIsUndefined() then false else s.source.referredVariable.varName = 'thisModule' endif else false endif ) and s.getRootComposite().oclIsTypeOf(ATL!Attribute) ) to t : Problem!Problem ( severity <- #error, location <- s.location, description <- 'attribute \'' + s.getRootComposite().name + '\': use of \'thisModule.resolveTemp()\' function ' + 'is prohibited in attributes' ) } -- Rule 'ProhibitedMultiIteratorCollectionOperation' -- This rule generates an 'error' Problem for each IteratorExp of the -- singleIteratorExps set that is associated with several Iterators. nodefault rule ProhibitedMultiIteratorCollectionOperation { from s : ATL!IteratorExp ( thisModule.singleIteratorExps->exists(e | s.name = e) and s.iterators->size() > 1 ) to t : Problem!Problem ( severity <- #error, location <- s.location, description <- 'iterator \'' + s.name + '\' may have at most one iterator variable' ) } ---- Rule 'UnsupportedMultiIteratorCollectionOperation' ---- This rule generates an 'error' Problem for each IteratorExp of the ---- multiIteratorExps set that is associated with several Iterators. ---- Note that this problem is due to limitations of the current implementation. --nodefault rule UnsupportedMultiIteratorCollectionOperation { -- from -- s : ATL!IteratorExp ( -- thisModule.multiIteratorExps->exists(e | s.name = e) and -- s.iterators->size() > 1 -- ) -- to -- t : Problem!Problem ( -- severity <- #error, -- location <- s.location, -- description <- -- 'with current implementation, iterator \'' + s.name -- + '\' may have at most one iterator variable' -- ) --} -- Rule 'ParameterNameIsUniqueInOperation' -- This rule generates an 'error' Problem for each parameter for which there -- exists another parameter of the same name in the operation declaration. nodefault rule ParameterNameIsUniqueInOperation { from s : ATL!Parameter ( if not s.operation.oclIsUndefined() then (s.operation.parameters ->exists(e | s.varName = e.varName and s <> e)) and not( s.refImmediateComposite().oclIsUndefined() and s.varName <> 'self' and s.varName <> 'thisModule' ) else false endif ) to t : Problem!Problem ( severity <- #error, location <- s.location, description <- 'a parameter named \'' + s.varName + '\' is already declared in this operation' ) } -- Rule 'IteratorNameIsUniqueInLoop' -- This rule generates an 'error' Problem for each Iterator declaration for -- which there exists either another Iterator or a result variable declaration -- (for Iterate loop only) of the same name within the same loop definition. nodefault rule VariableNameIsUniqueInLoop { from s : ATL!Iterator ( if not s.loopExpr.oclIsUndefined() then s.loopExpr.iterators ->exists(e | s.varName = e.varName and s <> e) or if s.loopExpr.oclIsTypeOf(ATL!IterateExp) then s.loopExpr.result.varName = s.varName else false endif else false endif ) to t : Problem!Problem ( severity <- #error, location <- s.location, description <- 'a variable named \'' + s.varName + '\' is already declared in this loop' ) } -- Rule 'ResultNameIsUniqueInIterate' -- This rule generates an 'error' Problem for each 'result' variable -- declaration of an IterateExp for which there exists an Iterator variable of -- the same name in the Iterate loop definition. nodefault rule ResultNameIsUniqueInIterate { from s : ATL!VariableDeclaration ( if s.baseExp.oclIsUndefined() then false else s.baseExp.iterators ->exists(e | s.varName = e.varName and s <> e) endif ) to t : Problem!Problem ( severity <- #error, location <- s.location, description <- 'a variable named \'' + s.varName + '\' is already declared in this loop' ) } -- Rule 'VariableNameIsUniqueInContainer' -- This rule generates a 'warning' Problem for each declaration of a variable -- for which there exists another variable declaration of the same name in the -- same namespace (except multiple intances of an Iterator name in a same loop -- which handle 'error' Problems). nodefault rule VariableNameIsUniqueInContainer { from s : ATL!VariableDeclaration ( s.getDeclarations()->exists(e | s.varName = e.varName and s <> e) ) to t : Problem!Problem ( severity <- #warning, location <- s.location, description <- 'a variable named \'' + s.varName + '\' is already declared in this container' ) } nodefault rule NoSuperAttribute { from s : ATL!PropertyCallExp ( not s.oclIsKindOf(ATL!OperationCallExp) and s.source.oclIsKindOf(ATL!SuperExp) ) to t : Problem!Problem ( severity <- #error, location <- s.location, description <- 'Cannot access "super" attributes' ) } nodefault rule NoSuperOutsideHelper { from s : ATL!SuperExp ( not s.getRootComposite().oclIsKindOf(ATL!OclFeature) ) to t : Problem!Problem ( severity <- #error, location <- s.location, description <- 'Cannot access "super" outside operations or attributes' ) }