Improved validation against imported ecore meta model (#1552)

closes #1551 

Signed-off-by: Sebastian Zarnekow <sebastian.zarnekow@gmail.com>
This commit is contained in:
Sebastian Zarnekow 2020-08-07 14:56:30 +02:00 committed by GitHub
parent 6d7097ef24
commit 108ea555d7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 1832 additions and 98 deletions

View file

@ -59,7 +59,7 @@ public class SerializationErrorTestLanguageSemanticSequencer extends AbstractDel
* Indent returns Indent
*
* Constraint:
* ((((req=TwoRequired opt=TwoOptions) | opt=TwoOptions)? indent+=Indent+) | indent+=Indent+)?
* ((req=TwoRequired? opt=TwoOptions indent+=Indent+) | (req=TwoRequired? indent+=Indent+) | indent+=Indent+)?
*/
protected void sequence_Indent(ISerializationContext context, Indent semanticObject) {
genericSequencer.createSequence(context, semanticObject);

View file

@ -2141,5 +2141,582 @@ class Xtext2EcoreTransformerTest extends AbstractXtextTests {
"Cannot create datatype BAR. If this is supposed to return EString, make sure you have imported 'http://www.eclipse.org/emf/2002/Ecore'", TestErrorAcceptor.ANY_EOBJECT)
getEPackageFromGrammar(grammar, 1)
}
@Test def void testTypeAfterAction_01() throws Exception {
var String grammar = '''
grammar test with org.eclipse.xtext.common.Terminals
import 'classpath:/org/eclipse/xtext/xtext/ecoreInference/multiinheritancetest.ecore'
SomeChild returns ParentA: ({ChildA} 'ChildA' | {ChildB} 'ChildB') aString=ID;
'''
val resource = getResourceFromString(grammar)
assertTrue(resource.errors.isEmpty)
}
@Test def void testTypeAfterAction_02() throws Exception {
var String grammar = '''
grammar test with org.eclipse.xtext.common.Terminals
import 'classpath:/org/eclipse/xtext/xtext/ecoreInference/multiinheritancetest.ecore'
SomeChild returns ParentA: ({ChildA} 'ChildA' | {ChildB} 'ChildB' | {ChildC} 'ChildC') aString=ID;
'''
errorAcceptorMock.acceptError(TransformationErrorCode.CannotCreateTypeInSealedMetamodel,
"Cannot find compatible feature aString in sealed EClass ChildC from imported package http://multiinheritancetest: The type 'ChildC' does not have a feature 'aString'.", TestErrorAcceptor.ANY_EOBJECT)
getEPackagesFromGrammar(grammar, 1)
}
@Test def void testTypeAfterAction_03() throws Exception {
var String grammar = '''
grammar test with org.eclipse.xtext.common.Terminals
import 'classpath:/org/eclipse/xtext/xtext/ecoreInference/multiinheritancetest.ecore'
SomeChild returns ParentA: ({ChildA} 'ChildA' | {ChildB} 'ChildB')? aString=ID;
'''
errorAcceptorMock.acceptError(TransformationErrorCode.CannotCreateTypeInSealedMetamodel,
"Cannot find compatible feature aString in sealed EClass ParentA from imported package http://multiinheritancetest: The type 'ParentA' does not have a feature 'aString'.", TestErrorAcceptor.ANY_EOBJECT)
getEPackagesFromGrammar(grammar, 1)
}
@Test def void testTypeAfterAction_04() throws Exception {
var String grammar = '''
grammar test with org.eclipse.xtext.common.Terminals
import 'classpath:/org/eclipse/xtext/xtext/ecoreInference/multiinheritancetest.ecore'
SomeChild returns ParentA:
{ChildA} 'ChildA' {ChildC.someContainment=current}
| {ChildB} 'ChildB' {ChildC.someContainment=current}
;
'''
val resource = getResourceFromString(grammar)
assertTrue(resource.errors.isEmpty)
}
@Test def void testTypeAfterAction_05() throws Exception {
var String grammar = '''
grammar test with org.eclipse.xtext.common.Terminals
import 'classpath:/org/eclipse/xtext/xtext/ecoreInference/multiinheritancetest.ecore'
SomeChild returns ParentA:
( {ChildA} 'ChildA' {ChildC.someContainment=current}
| {ChildB} 'ChildB' {ChildC.someContainment=current}
) {ChildC.someContainment=current}
;
'''
val resource = getResourceFromString(grammar)
assertTrue(resource.errors.isEmpty)
}
@Test def void testTypeAfterAction_06() throws Exception {
var String grammar = '''
grammar test with org.eclipse.xtext.common.Terminals
import 'classpath:/org/eclipse/xtext/xtext/ecoreInference/multiinheritancetest.ecore'
SomeChild returns ParentA:
( {ChildA} 'ChildA' | {ChildB} 'ChildB' ) {ChildC.someContainment=current}
;
'''
val resource = getResourceFromString(grammar)
assertTrue(resource.errors.isEmpty)
}
@Test def void testTypeAfterAction_07() throws Exception {
var String grammar = '''
grammar test with org.eclipse.xtext.common.Terminals
import 'classpath:/org/eclipse/xtext/xtext/ecoreInference/multiinheritancetest.ecore'
SomeChild returns ParentA:
{ChildA} 'ChildA' {ChildC.someContainment=current} 'ChildC' {ChildC.someContainment=current}
;
'''
val resource = getResourceFromString(grammar)
assertTrue(resource.errors.isEmpty)
}
@Test def void testTypeAfterAction_08() throws Exception {
var String grammar = '''
grammar test with org.eclipse.xtext.common.Terminals
import 'classpath:/org/eclipse/xtext/xtext/ecoreInference/multiinheritancetest.ecore'
SomeChild returns ParentA:
{ChildA} 'ChildA' ({ChildC.someContainment=current} 'ChildC')+ {ChildC.someContainment=current}
;
'''
val resource = getResourceFromString(grammar)
assertTrue(resource.errors.isEmpty)
}
@Test def void testTypeAfterAction_09() throws Exception {
var String grammar = '''
grammar test with org.eclipse.xtext.common.Terminals
import 'classpath:/org/eclipse/xtext/xtext/ecoreInference/multiinheritancetest.ecore'
SomeChild returns ParentA:
{ChildA} 'ChildA' ({ChildC.someContainment=current} 'ChildC')? {ChildC.someContainment=current}
;
'''
val resource = getResourceFromString(grammar)
assertTrue(resource.errors.isEmpty)
}
@Test def void testTypeAfterAction_10() throws Exception {
var String grammar = '''
grammar test with org.eclipse.xtext.common.Terminals
import 'classpath:/org/eclipse/xtext/xtext/ecoreInference/multiinheritancetest.ecore'
SomeChild returns ParentA:
{ChildA} 'ChildA' {ChildB.someContainment=current} 'ChildB' aString=ID
;
'''
val resource = getResourceFromString(grammar)
assertTrue(resource.errors.isEmpty)
}
@Test def void testTypeAfterAction_11() throws Exception {
var String grammar = '''
grammar test with org.eclipse.xtext.common.Terminals
import 'classpath:/org/eclipse/xtext/xtext/ecoreInference/multiinheritancetest.ecore'
SomeChild returns ParentA:
{ChildA} 'ChildA' ({ChildB.someContainment=current} 'ChildB')? aString=ID
;
'''
val resource = getResourceFromString(grammar)
assertTrue(resource.errors.isEmpty)
}
@Test def void testTypeAfterAction_12() throws Exception {
var String grammar = '''
grammar test with org.eclipse.xtext.common.Terminals
import 'classpath:/org/eclipse/xtext/xtext/ecoreInference/multiinheritancetest.ecore'
SomeChild returns ParentA:
( {ChildA} 'ChildA'
| {ChildB} 'ChildB'
| {ChildC} 'ChildC') parent=ChildA
;
ChildA: {ChildA} 'ChildA';
'''
val resource = getResourceFromString(grammar)
assertTrue(resource.errors.isEmpty)
}
@Test def void testTypeAfterAction_13() throws Exception {
var String grammar = '''
grammar test with org.eclipse.xtext.common.Terminals
import 'classpath:/org/eclipse/xtext/xtext/ecoreInference/multiinheritancetest.ecore'
SomeChild returns ParentA:
{ChildC} 'ChildC'
( {ChildA.someContainment=current} 'ChildA'
| {ChildB.someContainment=current} 'ChildB'
) parent=ChildA
;
ChildA: {ChildA} 'ChildA';
'''
val resource = getResourceFromString(grammar)
assertTrue(resource.errors.isEmpty)
}
@Test def void testTypeAfterAction_14() throws Exception {
var String grammar = '''
grammar test with org.eclipse.xtext.common.Terminals
import 'classpath:/org/eclipse/xtext/xtext/ecoreInference/multiinheritancetest.ecore'
SomeChild returns ParentA:
{ChildC} 'ChildC'
( {ChildA.someContainment=current} 'ChildA'
| {ChildB.someContainment=current} 'ChildB'
)* parent=ChildA
;
ChildA: {ChildA} 'ChildA';
'''
val resource = getResourceFromString(grammar)
assertTrue(resource.errors.isEmpty)
}
@Test def void testTypeAfterAction_15() throws Exception {
var String grammar = '''
grammar test with org.eclipse.xtext.common.Terminals
generate multiinheritancetest 'multiinheritancetest'
DeclParentA returns ParentA:
DeclChildA | DeclChildB | DeclChildC | {ParentA} attributeInParentA=ID;
DeclParentB returns ParentB:
DeclChildA | DeclChildB | DeclChildC | {ParentB} someContainment=DeclParentA;
DeclChildA returns ChildA:
aString=ID parent=DeclParentA;
DeclChildB returns ChildB:
aString=ID parent=DeclParentB;
DeclChildC returns ChildC:
parent=DeclChildA;
SomeChild returns ParentA: ({ChildA} 'ChildA' | {ChildB} 'ChildB') aString=ID;
'''
val resource = getResourceFromString(grammar)
assertTrue(resource.errors.isEmpty)
}
@Test def void testTypeAfterAction_16() throws Exception {
var String grammar = '''
grammar test with org.eclipse.xtext.common.Terminals
generate multiinheritancetest 'multiinheritancetest'
DeclParentA returns ParentA:
DeclChildA | DeclChildB | DeclChildC | {ParentA} attributeInParentA=ID;
DeclParentB returns ParentB:
DeclChildA | DeclChildB | DeclChildC | {ParentB} someContainment=DeclParentA;
DeclChildA returns ChildA:
aString=ID parent=DeclParentA;
DeclChildB returns ChildB:
aString=ID parent=DeclParentB;
DeclChildC returns ChildC:
parent=DeclChildA;
SomeChild returns ParentA: ({ChildA} 'ChildA' | {ChildB} 'ChildB' | {ChildC} 'ChildC') aString=ID;
'''
val resource = getResourceFromString(grammar)
assertTrue(resource.errors.isEmpty)
}
@Test def void testTypeAfterAction_17() throws Exception {
var String grammar = '''
grammar test with org.eclipse.xtext.common.Terminals
generate multiinheritancetest 'multiinheritancetest'
DeclParentA returns ParentA:
DeclChildA | DeclChildB | DeclChildC | {ParentA} attributeInParentA=ID;
DeclParentB returns ParentB:
DeclChildA | DeclChildB | DeclChildC | {ParentB} someContainment=DeclParentA;
DeclChildA returns ChildA:
aString=ID parent=DeclParentA;
DeclChildB returns ChildB:
aString=ID parent=DeclParentB;
DeclChildC returns ChildC:
parent=DeclChildA;
SomeChild returns ParentA: ({ChildA} 'ChildA' | {ChildB} 'ChildB')? aString=ID;
'''
val resource = getResourceFromString(grammar)
assertTrue(resource.errors.isEmpty)
}
@Test def void testTypeAfterAction_18() throws Exception {
var String grammar = '''
grammar test with org.eclipse.xtext.common.Terminals
generate multiinheritancetest 'multiinheritancetest'
DeclParentA returns ParentA:
DeclChildA | DeclChildB | DeclChildC | {ParentA} attributeInParentA=ID;
DeclParentB returns ParentB:
DeclChildA | DeclChildB | DeclChildC | {ParentB} someContainment=DeclParentA;
DeclChildA returns ChildA:
aString=ID parent=DeclParentA;
DeclChildB returns ChildB:
aString=ID parent=DeclParentB;
DeclChildC returns ChildC:
parent=DeclChildA;
SomeChild returns ParentA:
{ChildA} 'ChildA' {ChildC.someContainment=current}
| {ChildB} 'ChildB' {ChildC.someContainment=current}
;
'''
val resource = getResourceFromString(grammar)
assertTrue(resource.errors.isEmpty)
}
@Test def void testTypeAfterAction_19() throws Exception {
var String grammar = '''
grammar test with org.eclipse.xtext.common.Terminals
generate multiinheritancetest 'multiinheritancetest'
DeclParentA returns ParentA:
DeclChildA | DeclChildB | DeclChildC | {ParentA} attributeInParentA=ID;
DeclParentB returns ParentB:
DeclChildA | DeclChildB | DeclChildC | {ParentB} someContainment=DeclParentA;
DeclChildA returns ChildA:
aString=ID parent=DeclParentA;
DeclChildB returns ChildB:
aString=ID parent=DeclParentB;
DeclChildC returns ChildC:
parent=DeclChildA;
SomeChild returns ParentA:
( {ChildA} 'ChildA' {ChildC.someContainment=current}
| {ChildB} 'ChildB' {ChildC.someContainment=current}
) {ChildC.someContainment=current}
;
'''
val resource = getResourceFromString(grammar)
assertTrue(resource.errors.isEmpty)
}
@Test def void testTypeAfterAction_20() throws Exception {
var String grammar = '''
grammar test with org.eclipse.xtext.common.Terminals
generate multiinheritancetest 'multiinheritancetest'
DeclParentA returns ParentA:
DeclChildA | DeclChildB | DeclChildC | {ParentA} attributeInParentA=ID;
DeclParentB returns ParentB:
DeclChildA | DeclChildB | DeclChildC | {ParentB} someContainment=DeclParentA;
DeclChildA returns ChildA:
aString=ID parent=DeclParentA;
DeclChildB returns ChildB:
aString=ID parent=DeclParentB;
DeclChildC returns ChildC:
parent=DeclChildA;
SomeChild returns ParentA:
( {ChildA} 'ChildA' | {ChildB} 'ChildB' ) {ChildC.someContainment=current}
;
'''
val resource = getResourceFromString(grammar)
assertTrue(resource.errors.isEmpty)
}
@Test def void testTypeAfterAction_21() throws Exception {
var String grammar = '''
grammar test with org.eclipse.xtext.common.Terminals
generate multiinheritancetest 'multiinheritancetest'
DeclParentA returns ParentA:
DeclChildA | DeclChildB | DeclChildC | {ParentA} attributeInParentA=ID;
DeclParentB returns ParentB:
DeclChildA | DeclChildB | DeclChildC | {ParentB} someContainment=DeclParentA;
DeclChildA returns ChildA:
aString=ID parent=DeclParentA;
DeclChildB returns ChildB:
aString=ID parent=DeclParentB;
DeclChildC returns ChildC:
parent=DeclChildA;
SomeChild returns ParentA:
{ChildA} 'ChildA' {ChildC.someContainment=current} 'ChildC' {ChildC.someContainment=current}
;
'''
val resource = getResourceFromString(grammar)
assertTrue(resource.errors.isEmpty)
}
@Test def void testTypeAfterAction_22() throws Exception {
var String grammar = '''
grammar test with org.eclipse.xtext.common.Terminals
generate multiinheritancetest 'multiinheritancetest'
DeclParentA returns ParentA:
DeclChildA | DeclChildB | DeclChildC | {ParentA} attributeInParentA=ID;
DeclParentB returns ParentB:
DeclChildA | DeclChildB | DeclChildC | {ParentB} someContainment=DeclParentA;
DeclChildA returns ChildA:
aString=ID parent=DeclParentA;
DeclChildB returns ChildB:
aString=ID parent=DeclParentB;
DeclChildC returns ChildC:
parent=DeclChildA;
SomeChild returns ParentA:
{ChildA} 'ChildA' ({ChildC.someContainment=current} 'ChildC')+ {ChildC.someContainment=current}
;
'''
val resource = getResourceFromString(grammar)
assertTrue(resource.errors.isEmpty)
}
@Test def void testTypeAfterAction_23() throws Exception {
var String grammar = '''
grammar test with org.eclipse.xtext.common.Terminals
generate multiinheritancetest 'multiinheritancetest'
DeclParentA returns ParentA:
DeclChildA | DeclChildB | DeclChildC | {ParentA} attributeInParentA=ID;
DeclParentB returns ParentB:
DeclChildA | DeclChildB | DeclChildC | {ParentB} someContainment=DeclParentA;
DeclChildA returns ChildA:
aString=ID parent=DeclParentA;
DeclChildB returns ChildB:
aString=ID parent=DeclParentB;
DeclChildC returns ChildC:
parent=DeclChildA;
SomeChild returns ParentA:
{ChildA} 'ChildA' ({ChildC.someContainment=current} 'ChildC')? {ChildC.someContainment=current}
;
'''
val resource = getResourceFromString(grammar)
assertTrue(resource.errors.isEmpty)
}
@Test def void testTypeAfterAction_24() throws Exception {
var String grammar = '''
grammar test with org.eclipse.xtext.common.Terminals
generate multiinheritancetest 'multiinheritancetest'
DeclParentA returns ParentA:
DeclChildA | DeclChildB | DeclChildC | {ParentA} attributeInParentA=ID;
DeclParentB returns ParentB:
DeclChildA | DeclChildB | DeclChildC | {ParentB} someContainment=DeclParentA;
DeclChildA returns ChildA:
aString=ID parent=DeclParentA;
DeclChildB returns ChildB:
aString=ID parent=DeclParentB;
DeclChildC returns ChildC:
parent=DeclChildA;
SomeChild returns ParentA:
{ChildA} 'ChildA' {ChildB.someContainment=current} 'ChildB' aString=ID
;
'''
val resource = getResourceFromString(grammar)
assertTrue(resource.errors.isEmpty)
}
@Test def void testTypeAfterAction_25() throws Exception {
var String grammar = '''
grammar test with org.eclipse.xtext.common.Terminals
generate multiinheritancetest 'multiinheritancetest'
DeclParentA returns ParentA:
DeclChildA | DeclChildB | DeclChildC | {ParentA} attributeInParentA=ID;
DeclParentB returns ParentB:
DeclChildA | DeclChildB | DeclChildC | {ParentB} someContainment=DeclParentA;
DeclChildA returns ChildA:
aString=ID parent=DeclParentA;
DeclChildB returns ChildB:
aString=ID parent=DeclParentB;
DeclChildC returns ChildC:
parent=DeclChildA;
SomeChild returns ParentA:
{ChildA} 'ChildA' ({ChildB.someContainment=current} 'ChildB')? aString=ID
;
'''
val resource = getResourceFromString(grammar)
assertTrue(resource.errors.isEmpty)
}
@Test def void testTypeAfterAction_26() throws Exception {
var String grammar = '''
grammar test with org.eclipse.xtext.common.Terminals
generate multiinheritancetest 'multiinheritancetest'
DeclParentA returns ParentA:
DeclChildA | DeclChildB | DeclChildC | {ParentA} attributeInParentA=ID;
DeclParentB returns ParentB:
DeclChildA | DeclChildB | DeclChildC | {ParentB} someContainment=DeclParentA;
DeclChildA returns ChildA:
aString=ID parent=DeclParentA;
DeclChildB returns ChildB:
aString=ID parent=DeclParentB;
DeclChildC returns ChildC:
parent=DeclChildA;
SomeChild returns ParentA:
( {ChildA} 'ChildA'
| {ChildB} 'ChildB'
| {ChildC} 'ChildC') parent=ChildA
;
ChildA: {ChildA} 'ChildA';
'''
val resource = getResourceFromString(grammar)
assertTrue(resource.errors.isEmpty)
}
@Test def void testTypeAfterAction_27() throws Exception {
var String grammar = '''
grammar test with org.eclipse.xtext.common.Terminals
generate multiinheritancetest 'multiinheritancetest'
DeclParentA returns ParentA:
DeclChildA | DeclChildB | DeclChildC | {ParentA} attributeInParentA=ID;
DeclParentB returns ParentB:
DeclChildA | DeclChildB | DeclChildC | {ParentB} someContainment=DeclParentA;
DeclChildA returns ChildA:
aString=ID parent=DeclParentA;
DeclChildB returns ChildB:
aString=ID parent=DeclParentB;
DeclChildC returns ChildC:
parent=DeclChildA;
SomeChild returns ParentA:
{ChildC} 'ChildC'
( {ChildA.someContainment=current} 'ChildA'
| {ChildB.someContainment=current} 'ChildB'
) parent=ChildA
;
ChildA: {ChildA} 'ChildA';
'''
val resource = getResourceFromString(grammar)
assertTrue(resource.errors.isEmpty)
}
@Test def void testTypeAfterAction_28() throws Exception {
var String grammar = '''
grammar test with org.eclipse.xtext.common.Terminals
generate multiinheritancetest 'multiinheritancetest'
DeclParentA returns ParentA:
DeclChildA | DeclChildB | DeclChildC | {ParentA} attributeInParentA=ID;
DeclParentB returns ParentB:
DeclChildA | DeclChildB | DeclChildC | {ParentB} someContainment=DeclParentA;
DeclChildA returns ChildA:
aString=ID parent=DeclParentA;
DeclChildB returns ChildB:
aString=ID parent=DeclParentB;
DeclChildC returns ChildC:
parent=DeclChildA;
SomeChild returns ParentA:
{ChildC} 'ChildC'
( {ChildA.someContainment=current} 'ChildA'
| {ChildB.someContainment=current} 'ChildB'
)* parent=ChildA
;
ChildA: {ChildA} 'ChildA';
'''
val resource = getResourceFromString(grammar)
assertTrue(resource.errors.isEmpty)
}
@Test def void testAssignObjectAlternative_01() throws Exception {
var String grammar = '''
grammar test with org.eclipse.xtext.common.Terminals
import 'classpath:/org/eclipse/xtext/xtext/ecoreInference/multiinheritancetest.ecore'
ChildA_ returns ChildA:
someContainment=(ChildA|ChildB)
;
ChildA: {ChildA} 'ChildA';
ChildB: {ChildB} 'ChildB';
'''
val resource = getResourceFromString(grammar)
assertTrue(resource.errors.isEmpty)
}
@Test def void testAssignObjectAlternative_02() throws Exception {
var String grammar = '''
grammar test with org.eclipse.xtext.common.Terminals
generate multiinheritancetest 'multiinheritancetest'
DeclParentA returns ParentA:
DeclChildA | DeclChildB | DeclChildC | {ParentA} attributeInParentA=ID;
DeclParentB returns ParentB:
DeclChildA | DeclChildB | DeclChildC | {ParentB} someContainment=DeclParentA;
DeclChildA returns ChildA:
aString=ID parent=DeclParentA;
DeclChildB returns ChildB:
aString=ID parent=DeclParentB;
DeclChildC returns ChildC:
parent=DeclChildA;
ChildA:
someContainment=(DeclChildA|DeclChildB)
;
'''
val resource = getResourceFromString(grammar)
assertTrue(resource.errors.isEmpty)
}
}

View file

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="multiinheritancetest" nsURI="http://multiinheritancetest" nsPrefix="multiinheritancetest">
<eClassifiers xsi:type="ecore:EClass" name="ParentA">
<eStructuralFeatures xsi:type="ecore:EAttribute" name="attributeInParentA" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="ParentB">
<eStructuralFeatures xsi:type="ecore:EReference" name="someContainment" eType="#//ParentA"
containment="true"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="ChildA" eSuperTypes="#//ParentA #//ParentB">
<eStructuralFeatures xsi:type="ecore:EAttribute" name="aString" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
<eStructuralFeatures xsi:type="ecore:EReference" name="parent" eType="#//ParentA"
containment="true"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="ChildB" eSuperTypes="#//ParentA #//ParentB">
<eStructuralFeatures xsi:type="ecore:EAttribute" name="aString" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
<eStructuralFeatures xsi:type="ecore:EReference" name="parent" eType="#//ParentB"
containment="true"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="ChildC" eSuperTypes="#//ParentA #//ParentB">
<eStructuralFeatures xsi:type="ecore:EReference" name="parent" eType="#//ChildA"
containment="true"/>
</eClassifiers>
</ecore:EPackage>

View file

@ -9,7 +9,10 @@
*******************************************************************************/
package org.eclipse.xtext.xtext.ecoreInference;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.eclipse.emf.common.util.URI;
@ -33,6 +36,9 @@ import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.util.ReflectionUtil;
import org.eclipse.xtext.util.Strings;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
/**
* @author Jan Köhnlein - Initial contribution and API
* @author Heiko Behrens
@ -70,7 +76,7 @@ public abstract class EClassifierInfo {
public abstract boolean addSupertype(EClassifierInfo superTypeInfo);
public abstract boolean addFeature(String featureName, EClassifierInfo featureType, boolean isMultivalue,
public abstract boolean addFeature(String featureName, EClassifierInfoAccess featureTypeInfo, boolean isMultivalue,
boolean isContainment, AbstractElement parserElement) throws TransformationException;
public static class EClassInfo extends EClassifierInfo {
@ -156,10 +162,11 @@ public abstract class EClassifierInfo {
}
@Override
public boolean addFeature(String featureName, EClassifierInfo featureType, boolean isMultivalue,
public boolean addFeature(String featureName, EClassifierInfoAccess featureTypeInfo, boolean isMultivalue,
boolean isContainment, AbstractElement parserElement) throws TransformationException {
EClassifier featureClassifier = featureType.getEClassifier();
return addFeature(featureName, featureClassifier, isMultivalue, isContainment, parserElement);
ImmutableList<EClassifier> featureClassifiers = FluentIterable.from(featureTypeInfo.getCurrentTypes()).transform(EClassifierInfo::getEClassifier).toList();
EClassifier compatibleType = featureTypeInfo.getCurrentCompatibleType().getEClassifier();
return addFeature(featureName, featureClassifiers, compatibleType, isMultivalue, isContainment, parserElement);
}
public boolean addFeature(EStructuralFeature prototype) {
@ -169,7 +176,7 @@ public abstract class EClassifierInfo {
EReference reference = (EReference) prototype;
isContainment = reference.isContainment();
}
boolean result = addFeature(prototype.getName(), prototype.getEType(), prototype.isMany(), isContainment, null);
boolean result = addFeature(prototype.getName(), Arrays.asList(prototype.getEType()), prototype.getEType(), prototype.isMany(), isContainment, null);
if (result) {
EStructuralFeature newFeature = getEClass().getEStructuralFeature(prototype.getName());
SourceAdapter oldAdapter = SourceAdapter.find(prototype);
@ -279,7 +286,7 @@ public abstract class EClassifierInfo {
&& left.getEPackage().getNsURI().equals(right.getEPackage().getNsURI())) {
return true;
}
// Check all supertypes of the right class
// Check all super-types of the right class
for (EClass superClass : right.getEAllSuperTypes()) {
if (left.getClassifierID() == superClass.getClassifierID()
&& superClass.getEPackage() != null
@ -299,14 +306,26 @@ public abstract class EClassifierInfo {
return result;
}
public boolean isFeatureSemanticallyEqualTo(EStructuralFeature f1, EStructuralFeature f2) {
private boolean isFeatureSemanticallyEqualTo(EStructuralFeature f1, EStructuralFeature f2, Set<EClassifier> f2Types) {
if (f1 == f2) {
return true;
}
boolean result = isFeatureSemanticallyEqualApartFromType(f1, f2);
if (f1 instanceof EReference && f2 instanceof EReference) {
EClass f1Type = (EClass) f1.getEType();
EClass f2Type = (EClass) f2.getEType();
result &= f1Type.isSuperTypeOf(f2Type);
EClass f1Type = ((EReference)f1).getEReferenceType();
result &= ((EReference) f1).isContainment() == ((EReference) f2).isContainment();
result &= ((EReference) f1).isContainer() == ((EReference) f2).isContainer();
if (result) {
for(EClassifier f2Type: f2Types) {
if (f2Type instanceof EClass) {
if (!f1Type.isSuperTypeOf((EClass) f2Type)) {
return false;
}
} else {
return false;
}
}
}
} else {
result &= f1.getEType().equals(EcoreUtil2.getCompatibleType(f1.getEType(), f2.getEType()));
}
@ -327,32 +346,47 @@ public abstract class EClassifierInfo {
}
public FindResult containsSemanticallyEqualFeature(EStructuralFeature feature) {
return containsSemanticallyEqualFeature(getEClass().getEAllStructuralFeatures(), feature);
return containsSemanticallyEqualFeature(feature, Collections.singleton(feature.getEType()));
}
public FindResult containsSemanticallyEqualFeature(EStructuralFeature feature, Set<EClassifier> toBeAssignedTypes) {
return containsSemanticallyEqualFeature(getEClass().getEAllStructuralFeatures(), feature, toBeAssignedTypes);
}
public FindResult containsSemanticallyEqualFeature(Collection<EStructuralFeature> features,
EStructuralFeature feature) {
return containsSemanticallyEqualFeature(features, feature, Collections.singleton(feature.getEType()));
}
public FindResult containsSemanticallyEqualFeature(Collection<EStructuralFeature> features,
EStructuralFeature feature, Set<EClassifier> toBeAssignedTypes) {
EStructuralFeature potentiallyEqualFeature = findFeatureByName(features, feature.getName());
if (potentiallyEqualFeature == null)
return FindResult.FeatureDoesNotExist;
else if (isFeatureSemanticallyEqualTo(potentiallyEqualFeature, feature))
else if (isFeatureSemanticallyEqualTo(potentiallyEqualFeature, feature, toBeAssignedTypes))
return FindResult.FeatureExists;
else
return FindResult.DifferentFeatureWithSameNameExists;
}
private boolean addFeature(String featureName, EClassifier featureClassifier, boolean isMultivalue,
private boolean addFeature(
String featureName,
List<EClassifier> featureTypes,
EClassifier compatibleSuperType,
boolean isMultivalue,
boolean isContainment, AbstractElement grammarElement) throws TransformationException {
if (!isGenerated()) {
StringBuilder errorMessage = new StringBuilder();
if (!containsCompatibleFeature(featureName, isMultivalue, isContainment, featureClassifier, errorMessage)) {
throw new TransformationException(TransformationErrorCode.CannotCreateTypeInSealedMetamodel,
"Cannot find compatible feature "+featureName+" in sealed EClass "+getEClass().getName()+" from imported package "+getEClass().getEPackage().getNsURI()+": " + errorMessage.toString(),
grammarElement);
for(EClassifier featureType: featureTypes) {
if (!containsCompatibleFeature(featureName, isMultivalue, isContainment, featureType, errorMessage)) {
throw new TransformationException(TransformationErrorCode.CannotCreateTypeInSealedMetamodel,
"Cannot find compatible feature "+featureName+" in sealed EClass "+getEClass().getName()+" from imported package "+getEClass().getEPackage().getNsURI()+": " + errorMessage.toString(),
grammarElement);
}
}
return true;
}
EStructuralFeature newFeature = createFeatureWith(featureName, featureClassifier, isMultivalue,
EStructuralFeature newFeature = createFeatureWith(featureName, compatibleSuperType, isMultivalue,
isContainment);
FindResult containsSemanticallyEqualFeature = containsSemanticallyEqualFeature(newFeature);
@ -443,7 +477,7 @@ public abstract class EClassifierInfo {
}
@Override
public boolean addFeature(String featureName, EClassifierInfo featureType, boolean isMultivalue,
public boolean addFeature(String featureName, EClassifierInfoAccess featureType, boolean isMultivalue,
boolean isContainment, AbstractElement parserElement) throws TransformationException {
throw new UnsupportedOperationException("Cannot add feature " + featureName + " to simple datatype "
+ Strings.emptyIfNull(this.getEClassifier().getName()));

View file

@ -0,0 +1,39 @@
/*******************************************************************************
* Copyright (c) 2020 Sebastian Zarnekow and others.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.xtext.xtext.ecoreInference;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
/**
* Encapsulates the access to the type information during the static analysis of Xtext grammar rules.
*
* @since 2.23
*/
interface EClassifierInfoAccess {
/**
* The most specific single super type of all known potential types.
*/
EClassifierInfo getCurrentCompatibleType();
/**
* All types that have be used to instantiate the current object or all the types
* that are the outcome of a grammar alternative.
*/
default Collection<EClassifierInfo> getCurrentTypes() {
EClassifierInfo currentCompatibleType = getCurrentCompatibleType();
if (currentCompatibleType == null) {
return Collections.emptyList();
}
return Arrays.asList(currentCompatibleType);
}
}

View file

@ -34,7 +34,6 @@ import org.eclipse.xtext.xtext.ecoreInference.EClassifierInfo.EClassInfo;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
/**
* A possible extension would be to normalize the type hierarchy and remove
@ -165,22 +164,10 @@ public class EClassifierInfos {
while (i.hasNext())
result = getCompatibleType(result, i.next());
return result;
}
public EClassifier getCompatibleTypeNameOf(Collection<EClassifier> classifiers, boolean useParent) {
final Collection<EClassifierInfo> types = Sets.newLinkedHashSet();
for (EClassifier classifier : classifiers) {
final EClassifierInfo info = getInfoOrNull(classifier);
if (info == null)
return null;
types.add(info);
if (result == null) {
return getInfoOrNull(GrammarUtil.findEObject(grammar));
}
final EClassifierInfo compatibleType = getCompatibleTypeOf(types);
if (compatibleType != null)
return compatibleType.getEClassifier();
return GrammarUtil.findEObject(grammar);
return result;
}
public List<EClassInfo> getAllEClassInfos() {

View file

@ -9,6 +9,7 @@
*******************************************************************************/
package org.eclipse.xtext.xtext.ecoreInference;
import java.util.Collections;
import java.util.Set;
import org.eclipse.emf.ecore.EClassifier;
@ -32,7 +33,7 @@ import com.google.common.collect.Sets;
/**
* @author Sebastian Zarnekow - Initial contribution and API
*/
final class ElementTypeCalculator extends XtextSwitch<EClassifier> implements Function<AbstractElement, EClassifier>{
final class ElementTypeCalculator extends XtextSwitch<Set<EClassifier>> implements Function<AbstractElement, Set<EClassifier>>{
private final EClassifierInfos classifierInfos;
@ -41,16 +42,16 @@ final class ElementTypeCalculator extends XtextSwitch<EClassifier> implements Fu
}
@Override
public EClassifier caseKeyword(Keyword object) {
public Set<EClassifier> caseKeyword(Keyword object) {
EDataType eString = GrammarUtil.findEString(GrammarUtil.getGrammar(object));
if (eString != null)
return eString;
return Collections.singleton(eString);
// nowhere imported - use the instance from the registry
return EcorePackage.Literals.ESTRING;
return Collections.singleton(EcorePackage.Literals.ESTRING);
}
@Override
public EClassifier caseTypeRef(TypeRef object) {
public Set<EClassifier> caseTypeRef(TypeRef object) {
if (object.getClassifier() == null) {
if (object.getMetamodel() == null || object.getMetamodel().getEPackage() == null)
return null;
@ -61,18 +62,18 @@ final class ElementTypeCalculator extends XtextSwitch<EClassifier> implements Fu
if (info != null)
object.setClassifier(info.getEClassifier());
}
return object.getClassifier();
return Collections.singleton(object.getClassifier());
}
@Override
public EClassifier caseAbstractRule(AbstractRule object) {
public Set<EClassifier> caseAbstractRule(AbstractRule object) {
if (object.getType() != null)
return doSwitch(object.getType());
return null;
}
@Override
public EClassifier caseCompoundElement(CompoundElement object) {
public Set<EClassifier> caseCompoundElement(CompoundElement object) {
// since we do not allow unassigned rule calls and
// actions, it is safe to use the same logic for
// unordered groups as for groups
@ -82,31 +83,27 @@ final class ElementTypeCalculator extends XtextSwitch<EClassifier> implements Fu
}
@Override
public EClassifier caseRuleCall(RuleCall ruleCall) {
public Set<EClassifier> caseRuleCall(RuleCall ruleCall) {
if (ruleCall.getRule() != null)
return doSwitch(ruleCall.getRule());
return null;
}
@Override
public EClassifier caseAlternatives(Alternatives object) {
final Set<EClassifier> types = Sets.newLinkedHashSet();
public Set<EClassifier> caseAlternatives(Alternatives object) {
Set<EClassifier> types = Sets.newLinkedHashSet();
for (AbstractElement group : object.getElements())
types.add(doSwitch(group));
try {
return classifierInfos.getCompatibleTypeNameOf(types, true);
} catch (IllegalArgumentException e) {
return null;
}
types.addAll(doSwitch(group));
return types;
}
@Override
public EClassifier caseCrossReference(CrossReference object) {
public Set<EClassifier> caseCrossReference(CrossReference object) {
return doSwitch(object.getType());
}
@Override
public EClassifier apply(AbstractElement param) {
public Set<EClassifier> apply(AbstractElement param) {
return doSwitch(param);
}
}

View file

@ -9,8 +9,10 @@
*******************************************************************************/
package org.eclipse.xtext.xtext.ecoreInference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EDataType;
@ -32,14 +34,16 @@ import com.google.common.collect.Sets;
* <a href="https://www.eclipse.org/Xtext/documentation/301_grammarlanguage.html#metamodel-inference">documentation</a>
* for details.
*
*
*
* @author Heiko Behrens - Initial contribution and API
* @author Sebastian Zarnekow
*/
public class Xtext2EcoreInterpretationContext {
public class Xtext2EcoreInterpretationContext implements EClassifierInfoAccess {
private final EClassifierInfos eClassifierInfos;
private final Function<AbstractElement, EClassifier> classifierCalculator;
private final Function<AbstractElement, Set<EClassifier>> classifierCalculator;
private final Collection<EClassifierInfo> currentTypes = Sets.newLinkedHashSet();
@ -76,12 +80,13 @@ public class Xtext2EcoreInterpretationContext {
final String featureName = assignment.getFeature();
boolean isMultivalue = GrammarUtil.isMultipleAssignment(assignment);
boolean isContainment = true;
EClassifierInfo featureTypeInfo;
EClassifierInfoAccess featureTypeInfo;
if (GrammarUtil.isBooleanAssignment(assignment)) {
checkNoFragmentRuleCall(assignment.getTerminal());
EDataType eBoolean = GrammarUtil.findEBoolean(GrammarUtil.getGrammar(assignment));
featureTypeInfo = getEClassifierInfoOrThrowException(eBoolean, assignment);
EClassifierInfo classifierInfo = getEClassifierInfoOrThrowException(eBoolean, assignment);
featureTypeInfo = ()->classifierInfo;
isMultivalue = false;
}
else {
@ -90,13 +95,30 @@ public class Xtext2EcoreInterpretationContext {
throw new TransformationException(TransformationErrorCode.NoSuchTypeAvailable, "Cannot derive type from incomplete assignment.", assignment);
}
checkNoFragmentRuleCall(terminal);
EClassifier type = getTerminalType(terminal);
List<EClassifierInfo> classifierInfos = new ArrayList<>();
for(EClassifier type: getTerminalTypes(terminal)) {
classifierInfos.add(getEClassifierInfoOrThrowException(type, assignment));
}
isContainment = isContainmentAssignment(assignment);
featureTypeInfo = getEClassifierInfoOrThrowException(type, assignment);
featureTypeInfo = new EClassifierInfoAccess() {
@Override
public Collection<EClassifierInfo> getCurrentTypes() {
return classifierInfos;
}
@Override
public EClassifierInfo getCurrentCompatibleType() {
try {
return eClassifierInfos.getCompatibleTypeOf(classifierInfos);
} catch(IllegalArgumentException e) {
return null;
}
}
};
}
addFeature(featureName, featureTypeInfo, isMultivalue, isContainment, assignment);
}
private void checkNoFragmentRuleCall(AbstractElement terminal) throws TransformationException {
if (GrammarUtil.isEObjectFragmentRuleCall(terminal)) {
throw new TransformationException(TransformationErrorCode.InvalidFragmentCall, "Cannot call a fragment from an assignment", terminal);
@ -133,14 +155,14 @@ public class Xtext2EcoreInterpretationContext {
}.doSwitch(assignment.getTerminal());
}
public void addFeature(String featureName, EClassifierInfo featureTypeInfo, boolean isMultivalue,
public void addFeature(String featureName, EClassifierInfoAccess featureTypeInfo, boolean isMultivalue,
boolean isContainment, AbstractElement parserElement) throws TransformationException {
for (EClassifierInfo type : currentTypes)
type.addFeature(featureName, featureTypeInfo, isMultivalue, isContainment, parserElement);
}
private EClassifier getTerminalType(AbstractElement terminal) throws TransformationException {
final EClassifier result = classifierCalculator.apply(terminal);
private Set<EClassifier> getTerminalTypes(AbstractElement terminal) throws TransformationException {
Set<EClassifier> result = classifierCalculator.apply(terminal);
if (result == null) {
final ICompositeNode node = NodeModelUtils.getNode(terminal);
if (node != null) {
@ -190,8 +212,16 @@ public class Xtext2EcoreInterpretationContext {
return result;
}
public Collection<EClassifierInfo> getCurrentTypes() {
return currentTypes;
}
public EClassifierInfo getCurrentCompatibleType() {
return eClassifierInfos.getCompatibleTypeOf(currentTypes);
try {
return eClassifierInfos.getCompatibleTypeOf(currentTypes);
} catch(IllegalArgumentException e) {
return null;
}
}
public Xtext2EcoreInterpretationContext spawnContextWithReferencedType(EClassifierInfo referencedType, EObject parserElement) {

View file

@ -64,6 +64,7 @@ import org.eclipse.xtext.xtext.GrammarResource;
import com.google.common.base.Function;
import com.google.common.base.Predicates;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
@ -117,27 +118,25 @@ public class Xtext2EcoreTransformer {
}
public List<EPackage> getGeneratedPackages() {
final List<EPackage> result = new ArrayList<EPackage>();
final ResourceSet resourceSet = grammar.eResource().getResourceSet();
ResourceSet resourceSet = grammar.eResource().getResourceSet();
if (resourceSet == null)
throw new NullPointerException("resourceSet may not be null");
Iterables.addAll(result, Iterables.filter(Iterables.transform(
Iterables.filter(grammar.getMetamodelDeclarations(), GeneratedMetamodel.class),
new Function<AbstractMetamodelDeclaration, EPackage>() {
@Override
public EPackage apply(AbstractMetamodelDeclaration param) {
EPackage pack = (EPackage) param.eGet(XtextPackage.Literals.ABSTRACT_METAMODEL_DECLARATION__EPACKAGE, false);
if (pack != null && !pack.eIsProxy()) {
return pack;
}
return null;
}
}), Predicates.notNull()));
return getPackagesSortedByName(result);
return FluentIterable.from(grammar.getMetamodelDeclarations())
.filter(GeneratedMetamodel.class)
.transform(mm->{
EPackage pack = (EPackage) mm.eGet(XtextPackage.Literals.ABSTRACT_METAMODEL_DECLARATION__EPACKAGE, false);
if (pack != null && !pack.eIsProxy()) {
return pack;
}
return null;
})
.filter(Predicates.notNull())
.toSortedList(Comparator.comparing(EPackage::getName));
}
/*
* pre-conditions - ensure non-duplicate aliases - ensure all aliases have matching metamodel declarations
* preconditions - ensure non-duplicate aliases - ensure all aliases have matching metamodel declarations
*/
public void transform() {
eClassifierInfos = new EClassifierInfos(grammar);
@ -187,17 +186,6 @@ public class Xtext2EcoreTransformer {
}
}
private static List<EPackage> getPackagesSortedByName(Collection<EPackage> packages) {
final ArrayList<EPackage> result = new ArrayList<EPackage>(packages);
Collections.sort(result, new Comparator<EPackage>() {
@Override
public int compare(EPackage o1, EPackage o2) {
return o1.getName().compareTo(o2.getName());
}
});
return result;
}
private boolean deriveTypes() {
return deriveTypesImpl() && checkDatatypeRules() && deriveTypeHierarchy();
}
@ -496,11 +484,9 @@ public class Xtext2EcoreTransformer {
try {
TypeRef actionTypeRef = object.getType();
EClassifierInfo actionType = findOrCreateEClassifierInfo(actionTypeRef, null, true);
EClassifierInfo currentCompatibleType = context.getCurrentCompatibleType();
Xtext2EcoreInterpretationContext ctx = context.spawnContextWithReferencedType(actionType, object);
if (object.getFeature() != null) {
ctx.addFeature(object.getFeature(), currentCompatibleType,
GrammarUtil.isMultipleAssignment(object), true, object);
ctx.addFeature(object.getFeature(), context, GrammarUtil.isMultipleAssignment(object), true, object);
}
return ctx;
}
@ -982,7 +968,7 @@ public class Xtext2EcoreTransformer {
"Cannot find EPackage for type '" + typeRef.getClassifier().getName() + "'", typeRef);
EClassifierInfo info = eClassifierInfos.getInfo(typeRef);
if (info == null) {
// we assumend EString for terminal rules and datatype rules, so
// we assumed EString for terminal rules and data-type rules, so
// we have to do a look up in super grammar
EDataType dataType = GrammarUtil.findEString(GrammarUtil.getGrammar(typeRef));
if (dataType != null && typeRef.getClassifier() == dataType) {
@ -1039,7 +1025,7 @@ public class Xtext2EcoreTransformer {
EClassifierInfo result;
if (classifier instanceof EClass)
result = EClassifierInfo.createEClassInfo((EClass) classifier, true, getGeneratedEPackageURIs(), GrammarUtil.getGrammar(typeRef));
else // datatype or enum
else // data-type or enum
result = EClassifierInfo.createEDataTypeInfo((EDataType) classifier, true);
if (!eClassifierInfos.addInfo(typeRef, result))