diff --git a/monticore-grammar/build.gradle b/monticore-grammar/build.gradle index c4ac6d7dd7..cded2e5671 100644 --- a/monticore-grammar/build.gradle +++ b/monticore-grammar/build.gradle @@ -204,6 +204,7 @@ def grammarDependencies = ext { SIUnitLiterals = ["MCCommonLiterals", "SIUnits"] SIUnitTypes4Math = ["MCBasicTypes", "SIUnits"] SIUnitTypes4Computing = ["MCBasicTypes", "SIUnitTypes4Math"] + TypeParameters = ["BasicSymbols", "MCBasicTypes"] // three dependencies LambdaExpressions = ["BasicSymbols", "MCBasicTypes", "ExpressionsBasis"] @@ -372,6 +373,9 @@ checkArtifactsMCFullGenericTypes.dependsOn(checkArtifactsMCSimpleGenericTypes) checkArtifactsMCSimpleGenericTypes.dependsOn(checkArtifactsMCCollectionTypes) +checkArtifactsTypeParameters.dependsOn(checkArtifactsBasicSymbols) +checkArtifactsTypeParameters.dependsOn(checkArtifactsMCBasicTypes) + checkArtifactsCardinality.dependsOn(checkArtifactsMCBasics) checkArtifactsCardinality.dependsOn(checkArtifactsMCCommonLiterals) @@ -503,6 +507,9 @@ showArtifactsMCFullGenericTypes.dependsOn(showArtifactsMCSimpleGenericTypes) showArtifactsMCSimpleGenericTypes.dependsOn(showArtifactsMCCollectionTypes) +showArtifactsTypeParameters.dependsOn(showArtifactsBasicSymbols) +showArtifactsTypeParameters.dependsOn(showArtifactsMCBasicTypes) + showArtifactsCardinality.dependsOn(showArtifactsMCBasics) showArtifactsCardinality.dependsOn(showArtifactsMCCommonLiterals) diff --git a/monticore-grammar/src/main/grammars/de/monticore/Grammars.md b/monticore-grammar/src/main/grammars/de/monticore/Grammars.md index 18d51fd0ef..9a32c0e295 100644 --- a/monticore-grammar/src/main/grammars/de/monticore/Grammars.md +++ b/monticore-grammar/src/main/grammars/de/monticore/Grammars.md @@ -186,6 +186,18 @@ be issued (instead of runtime errors only). * `String` could be treated identical to `R".*"`, but it may be that the chosen typecheck wants to enforce explicit coercion. +### [TypeParameters.mc4](types/TypeParameters.mc4) (alpha) + +This grammar offers ways to define type parameters for, +e.g., classes or functions. +Modeling elements with type parameters represent +type-level functions that take type arguments; +For Example, the generic type `List` has one type parameter `T` +and therefore takes one type argument. +`List` or `List` cannot be used directly in a model, +instead, providing a type argument (e.g., `int`) +yields a type `List` that can be used. + ## Symbols: List of Grammars in package `de.monticore.symbols` These two grammars do not provide syntax themselves, but diff --git a/monticore-grammar/src/main/grammars/de/monticore/JavaLight.mc4 b/monticore-grammar/src/main/grammars/de/monticore/JavaLight.mc4 index 62143b8e80..faac0027a1 100644 --- a/monticore-grammar/src/main/grammars/de/monticore/JavaLight.mc4 +++ b/monticore-grammar/src/main/grammars/de/monticore/JavaLight.mc4 @@ -27,7 +27,8 @@ component grammar JavaLight extends AssignmentExpressions, JavaClassExpressions, MCCommonStatements, MCArrayStatements, - MCReturnStatements { + MCReturnStatements, + TypeParameters { interface ClassBodyDeclaration; @@ -45,12 +46,12 @@ symbolrule JavaMethod = isDefault: boolean; MethodDeclaration implements JavaMethod, ClassBodyDeclaration, InterfaceBodyDeclaration - = MCModifier* ("<"(MCTypeArgument||",")+">")? + = MCModifier* TypeParameters? MCReturnType Name FormalParameters (dim:"[" "]")* ("throws" Throws)? (MCJavaBlock | ";"); ConstructorDeclaration implements JavaMethod, ClassBodyDeclaration - = MCModifier* ("<"(MCTypeArgument||",")+">")? Name FormalParameters + = MCModifier* TypeParameters? Name FormalParameters ("throws" Throws)? MCJavaBlock; ConstDeclaration extends LocalVariableDeclarationStatement diff --git a/monticore-grammar/src/main/grammars/de/monticore/JavaLight.mlc b/monticore-grammar/src/main/grammars/de/monticore/JavaLight.mlc index abfe0684ba..f605e371e6 100644 --- a/monticore-grammar/src/main/grammars/de/monticore/JavaLight.mlc +++ b/monticore-grammar/src/main/grammars/de/monticore/JavaLight.mlc @@ -24,6 +24,7 @@ mlc JavaLight { mlc "de.monticore.statements.MCCommonStatements"; mlc "de.monticore.statements.MCArrayStatements"; mlc "de.monticore.statements.MCReturnStatements"; + mlc "de.monticore.types.TypeParameters"; } uses { diff --git a/monticore-grammar/src/main/grammars/de/monticore/types/TypeParameters.mc4 b/monticore-grammar/src/main/grammars/de/monticore/types/TypeParameters.mc4 new file mode 100644 index 0000000000..2ef5628a26 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/types/TypeParameters.mc4 @@ -0,0 +1,50 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types; + +/* Beta-version: This is intended to become a MontiCore stable grammar. */ + +import de.monticore.symbols.BasicSymbols; + +/** + * This grammar introduces type parameters such as T, U extends Set, + * and type parameter lists such as + * that can be used to declare generic model elements + * such as generic classes and functions. + * + * Like Java, each type parameter can have an upper bound + * consisting of an arbitrary number of types. +*/ + +component grammar TypeParameters + extends BasicSymbols, + MCBasicTypes { + + /** + * ASTTypeParameters are type parameter lists that are can be used to + * declare generic model elements. + * Each type parameter lists contains at least one type parameter. + * Example: + * + * > + */ + TypeParameters = + "<" (TypeParameter || ",")+ ">" + ; + + /** + * ASTTypeParameter represents one type parameter. + * It itself is represented in the symbol table as a TypeVarSymbol. + * Each type parameter has a name + * and can have an optional list of upper type bounds. + * Each bound list consists of an arbitrary positive number of types. + * Each of those types is a supertype of the corresponding type parameter. + * Example: + * T + * U extends T & Comparable + */ + TypeParameter implements TypeVar = + Name + ("extends" (MCType || "&")+)? + ; + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/types/TypeParameters.mlc b/monticore-grammar/src/main/grammars/de/monticore/types/TypeParameters.mlc new file mode 100644 index 0000000000..253407d0ee --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/types/TypeParameters.mlc @@ -0,0 +1,29 @@ +package de.monticore.types; + +mlc TypeParameters { + + //export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/types/TypeParameters.mc4"; + } + + //export handwritten code + export "$projectDir/src/main/java" { + include "de/monticore/types/typeparameters/**.java"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/types/typeparameters/**.java"; + } + + promote { + include "$projectDir/src/main/java/de/monticore/types3/*.java"; + } + + promote { + mlc "de.monticore.symbols.BasicSymbols"; + mlc "de.monticore.types.MCBasicTypes"; + } + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/types/Types.md b/monticore-grammar/src/main/grammars/de/monticore/types/Types.md index fec008c79a..79f369b951 100644 --- a/monticore-grammar/src/main/grammars/de/monticore/types/Types.md +++ b/monticore-grammar/src/main/grammars/de/monticore/types/Types.md @@ -17,6 +17,7 @@ the following language components: * [`MCArrayTypes`](MCArrayTypes.mc4) * [`MCFunctionTypes`](MCFunctionTypes.mc4) * [`MCStructuralTypes`](MCStructuralTypes.mc4) +* [`TypeParameters`](TypeParameters.mc4) * [`SIUnitTypes4Math`](../siunit/SIUnitTypes4Math.mc4) * [`SIUnitTypes4Computing`](../siunit/SIUnitTypes4Computing.mc4) * [`RegExType`](../regex/RegExType.mc4) @@ -65,6 +66,17 @@ arguments including wild card types. When using this language component, types such as `Person`, `Map, ? extends Person>`, and `a.b.C.E.F.H` are expressible. +## [`TypeParameters`](TypeParameters.mc4) + +This language component offers ways to define type parameter lists +for generic model elements, e.g., generic classes such as `Map`. +The resulting Symbols can than be used with, e.g., +[`MCSimpleGenericTypes`](MCSimpleGenericTypes.mc4), or +[`MCFullGenericTypes`](MCFullGenericTypes.mc4) +to define types by providing the corresponding type arguments. +Additionally, type parameters may optionally have upper bounds, +e.g., `T extends Person`, `U extends T & Comparable`. + ## [`MCArrayTypes`](MCArrayTypes.mc4) This language component allows to express array types like `Person[]` or diff --git a/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/IBasicSymbolsScope.java b/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/IBasicSymbolsScope.java index eb33d4730c..3c3285624a 100644 --- a/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/IBasicSymbolsScope.java +++ b/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/IBasicSymbolsScope.java @@ -15,7 +15,11 @@ public interface IBasicSymbolsScope extends IBasicSymbolsScopeTOP { * e.g. class C {} // T is bound within the class */ default boolean isTypeVariableBound(TypeVarSymbol typeVar) { - if (getLocalTypeVarSymbols().stream() + List localVars = resolveTypeVarLocallyMany( + false, typeVar.getName(), + AccessModifier.ALL_INCLUSION, (tv) -> true + ); + if (localVars.stream() .anyMatch(otherTypeVar -> otherTypeVar == typeVar)) { return true; } diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeVariable.java b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeVariable.java index d47b8f5aaa..4869940341 100644 --- a/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeVariable.java +++ b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeVariable.java @@ -320,14 +320,16 @@ public boolean deepEquals(SymTypeExpression sym) { return true; } SymTypeVariable symVar = (SymTypeVariable) sym; + if (!denotesSameVar(symVar)) { + return false; + } if (!getUpperBound().deepEquals(symVar.getUpperBound())) { return false; } else if (!getLowerBound().deepEquals(symVar.getLowerBound())) { return false; } - // cannot identify without a name at this point - return denotesSameVar(symVar); + return true; } @Override diff --git a/monticore-grammar/src/main/java/de/monticore/types/typeparameters/_symboltable/TypeParametersSTCompleteTypes.java b/monticore-grammar/src/main/java/de/monticore/types/typeparameters/_symboltable/TypeParametersSTCompleteTypes.java new file mode 100644 index 0000000000..458dcd5320 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/typeparameters/_symboltable/TypeParametersSTCompleteTypes.java @@ -0,0 +1,33 @@ +package de.monticore.types.typeparameters._symboltable; + +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.typeparameters._ast.ASTTypeParameter; +import de.monticore.types.typeparameters._visitor.TypeParametersVisitor2; +import de.monticore.types3.ITypeCalculator; + +import java.util.ArrayList; +import java.util.List; + +/** + * Sets the superTypes of the type parameter symbols. + */ +public class TypeParametersSTCompleteTypes implements TypeParametersVisitor2 { + + ITypeCalculator tc; + + public TypeParametersSTCompleteTypes(ITypeCalculator tc) { + this.tc = tc; + } + + @Override + public void visit(ASTTypeParameter node) { + List bounds = new ArrayList<>(); + for (ASTMCType astTypeBound : node.getMCTypeList()) { + bounds.add(tc.symTypeFromAST(astTypeBound)); + } + // error logged if obscure + node.getSymbol().setSuperTypesList(bounds); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/typeparameters/cocos/TypeParameterNoCyclicInheritance.java b/monticore-grammar/src/main/java/de/monticore/types/typeparameters/cocos/TypeParameterNoCyclicInheritance.java new file mode 100644 index 0000000000..72c3834c4d --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/typeparameters/cocos/TypeParameterNoCyclicInheritance.java @@ -0,0 +1,59 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.typeparameters.cocos; + +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeVariable; +import de.monticore.types.typeparameters._ast.ASTTypeParameter; +import de.monticore.types.typeparameters._cocos.TypeParametersASTTypeParameterCoCo; +import de.monticore.types3.SymTypeRelations; +import de.se_rwth.commons.logging.Log; + +import java.util.ArrayList; +import java.util.List; + +import static de.monticore.types.check.SymTypeExpressionFactory.createTypeVariable; + +/** + * Finds instances of circular inheritance, + * e.g., + */ +public class TypeParameterNoCyclicInheritance + implements TypeParametersASTTypeParameterCoCo { + + @Override + public void check(ASTTypeParameter node) { + SymTypeVariable thisVar = createTypeVariable(node.getSymbol()); + checkForCircularInheritance(List.of(thisVar), node); + } + + protected boolean checkForCircularInheritance( + List currentInheritanceList, + ASTTypeParameter node + ) { + List superTypes = SymTypeRelations.getNominalSuperTypes( + currentInheritanceList.get(currentInheritanceList.size() - 1) + ); + for (SymTypeExpression superType : superTypes) { + if (currentInheritanceList.stream().anyMatch(superType::deepEquals)) { + Log.error("0xFDC12 Checked supertypes of type variable \"" + + node.getName() + + "\" and found circular inheritance of type " + + superType.printFullName(), + node.get_SourcePositionStart(), + node.get_SourcePositionEnd() + ); + return false; + } + else { + List nextInheritanceList = new ArrayList<>(); + nextInheritanceList.addAll(currentInheritanceList); + nextInheritanceList.add(superType); + if (!checkForCircularInheritance(nextInheritanceList, node)) { + return false; + } + } + } + return true; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/typeparameters/cocos/TypeParametersHaveUniqueNames.java b/monticore-grammar/src/main/java/de/monticore/types/typeparameters/cocos/TypeParametersHaveUniqueNames.java new file mode 100644 index 0000000000..b48ce2376c --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/typeparameters/cocos/TypeParametersHaveUniqueNames.java @@ -0,0 +1,46 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.typeparameters.cocos; + +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbolTOP; +import de.monticore.types.typeparameters._ast.ASTTypeParameter; +import de.monticore.types.typeparameters._ast.ASTTypeParameters; +import de.monticore.types.typeparameters._cocos.TypeParametersASTTypeParametersCoCo; +import de.se_rwth.commons.logging.Log; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class TypeParametersHaveUniqueNames + implements TypeParametersASTTypeParametersCoCo { + + @Override + public void check(ASTTypeParameters node) { + List names = node.getTypeParameterList().stream() + .map(ASTTypeParameter::getSymbol) + .map(TypeSymbolTOP::getName) + .collect(Collectors.toList()); + Set duplicates = findDuplicates(names); + for (String dupName : duplicates) { + Log.error("0xFDC14 The same name \"" + dupName + + "\" has been used for multiple type parameters." + ); + } + } + + // Helper + + protected Set findDuplicates(List listContainingDuplicates) { + final Set setToReturn = new HashSet<>(); + final Set set1 = new HashSet<>(); + + for (String modifierName : listContainingDuplicates) { + if (!set1.add(modifierName)) { + setToReturn.add(modifierName); + } + } + return setToReturn; + } + +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/expressions/CombineExpressionsWithLiterals.mc4 b/monticore-grammar/src/test/grammars/de/monticore/expressions/CombineExpressionsWithLiterals.mc4 index b1ea472f3c..5bf1997da4 100644 --- a/monticore-grammar/src/test/grammars/de/monticore/expressions/CombineExpressionsWithLiterals.mc4 +++ b/monticore-grammar/src/test/grammars/de/monticore/expressions/CombineExpressionsWithLiterals.mc4 @@ -18,6 +18,7 @@ grammar CombineExpressionsWithLiterals extends de.monticore.types.MCArrayTypes, de.monticore.types.MCFunctionTypes, de.monticore.types.MCStructuralTypes, + de.monticore.types.TypeParameters, de.monticore.regex.RegExType, de.monticore.siunit.SIUnitTypes4Math, de.monticore.siunit.SIUnitTypes4Computing, diff --git a/monticore-grammar/src/test/grammars/de/monticore/types/TypeParametersTest.mc4 b/monticore-grammar/src/test/grammars/de/monticore/types/TypeParametersTest.mc4 new file mode 100644 index 0000000000..0464d5b492 --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/types/TypeParametersTest.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types; + +grammar TypeParametersTest extends + de.monticore.types.TypeParameters, + de.monticore.types.MCFullGenericTypes, + de.monticore.types.MCArrayTypes, + de.monticore.types.MCFunctionTypes, + de.monticore.types.MCStructuralTypes { +} \ No newline at end of file diff --git a/monticore-grammar/src/test/grammars/de/monticore/types/TypeParametersWithoutIntersectionTypesTest.mc4 b/monticore-grammar/src/test/grammars/de/monticore/types/TypeParametersWithoutIntersectionTypesTest.mc4 new file mode 100644 index 0000000000..b6ce614715 --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/types/TypeParametersWithoutIntersectionTypesTest.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types; + +grammar TypeParametersWithoutIntersectionTypesTest extends + de.monticore.types.TypeParameters, + de.monticore.types.MCFullGenericTypes, + de.monticore.types.MCArrayTypes, + de.monticore.types.MCFunctionTypes { +} \ No newline at end of file diff --git a/monticore-grammar/src/test/java/de/monticore/types/TypeParameterTest.java b/monticore-grammar/src/test/java/de/monticore/types/TypeParameterTest.java new file mode 100644 index 0000000000..73b599ffe0 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/TypeParameterTest.java @@ -0,0 +1,88 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types; + +import de.monticore.types.typeparameters._ast.ASTTypeParameter; +import de.monticore.types.typeparameters._ast.ASTTypeParameters; +import de.monticore.types.typeparameterstest.TypeParametersTestMill; +import de.monticore.types.typeparameterswithoutintersectiontypestest.TypeParametersWithoutIntersectionTypesTestMill; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class TypeParameterTest { + + @BeforeEach + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + } + + @Test + public void testTypeParameters() throws IOException { + String model = ""; + TypeParametersTestMill.reset(); + TypeParametersTestMill.init(); + Optional tpsOpt = + TypeParametersTestMill.parser().parse_StringTypeParameters(model); + assertTrue(tpsOpt.isPresent()); + ASTTypeParameters tps = tpsOpt.get(); + assertEquals(2, tps.sizeTypeParameters()); + ASTTypeParameter tp0 = tps.getTypeParameter(0); + assertEquals("T", tp0.getName()); + assertTrue(tp0.isEmptyMCTypes()); + ASTTypeParameter tp1 = tps.getTypeParameter(1); + assertEquals("U", tp1.getName()); + assertFalse(tp1.isEmptyMCTypes()); + assertEquals(1, tp1.sizeMCTypes()); + assertEquals("A.B", tp1.getMCType(0).printType()); + assertTrue(Log.getFindings().isEmpty()); + } + + // todo FDr write Trafo iff required -> discuss + @Test + @Disabled("needs trafo") + public void testTypeParametersMultipleBounds() throws IOException { + String model = ""; + TypeParametersTestMill.reset(); + TypeParametersTestMill.init(); + Optional tpsOpt = + TypeParametersTestMill.parser().parse_StringTypeParameters(model); + assertTrue(tpsOpt.isPresent()); + ASTTypeParameters tps = tpsOpt.get(); + assertEquals(1, tps.sizeTypeParameters()); + ASTTypeParameter tp0 = tps.getTypeParameter(0); + assertEquals("T", tp0.getName()); + assertEquals(2, tp0.sizeMCTypes()); + assertEquals("A", tp0.getMCType(0).printType()); + assertEquals("B", tp0.getMCType(1).printType()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testTypeParametersMultipleBoundsWithoutIntersectionTypes() throws IOException { + String model = ""; + TypeParametersWithoutIntersectionTypesTestMill.reset(); + TypeParametersWithoutIntersectionTypesTestMill.init(); + Optional tpsOpt = + TypeParametersWithoutIntersectionTypesTestMill.parser().parse_StringTypeParameters(model); + assertTrue(tpsOpt.isPresent()); + ASTTypeParameters tps = tpsOpt.get(); + assertEquals(1, tps.sizeTypeParameters()); + ASTTypeParameter tp0 = tps.getTypeParameter(0); + assertEquals("T", tp0.getName()); + assertEquals(2, tp0.sizeMCTypes()); + assertEquals("A", tp0.getMCType(0).printType()); + assertEquals("B", tp0.getMCType(1).printType()); + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/prettyprint/TypeParametersPrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/types/prettyprint/TypeParametersPrettyPrinterTest.java new file mode 100644 index 0000000000..d07d874a87 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/prettyprint/TypeParametersPrettyPrinterTest.java @@ -0,0 +1,87 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.prettyprint; + +import de.monticore.antlr4.MCConcreteParser; +import de.monticore.ast.ASTNode; +import de.monticore.types.typeparameterstest.TypeParametersTestMill; +import de.monticore.types.typeparameterstest._parser.TypeParametersTestParser; +import de.monticore.types3.AbstractTypeTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.io.IOException; +import java.util.Optional; +import java.util.function.Function; + +public class TypeParametersPrettyPrinterTest extends AbstractTypeTest { + + @BeforeEach + public void init() { + TypeParametersTestMill.reset(); + TypeParametersTestMill.init(); + } + + @ParameterizedTest + @ValueSource(strings = { + "", + " < T > ", + "", + ">", + ", N extends Node>", + "&B, V extends C&D>", + }) + public void testTypeParameters(String model) throws IOException { + TypeParametersTestParser parser = TypeParametersTestMill.parser(); + testPrettyPrinter( + model, parser, parser::parse_StringTypeParameters, + ast -> TypeParametersTestMill.prettyPrint(ast, true) + ); + } + + @ParameterizedTest + @ValueSource(strings = { + "T", + "LongTypeParameterName", + "T extends String", + "T extends Map>", + "T extends A & B", + "T extends A & B & C & D" + }) + public void testTypeParameter(String model) throws IOException { + TypeParametersTestParser parser = TypeParametersTestMill.parser(); + testPrettyPrinter( + model, parser, parser::parse_StringTypeParameter, + ast -> TypeParametersTestMill.prettyPrint(ast, true) + ); + } + + // this function could be used for all pretty printer tests if required, + // however, the parameters are not great. + protected void testPrettyPrinter( + String model, + MCConcreteParser parser, + ParseFunction parseFunc, + Function prettyPrintFunc + ) throws IOException { + Optional astOpt = parseFunc.apply(model); + assertNoFindings(); + Assertions.assertTrue(astOpt.isPresent()); + Assertions.assertFalse(parser.hasErrors()); + N ast = astOpt.get(); + String output = prettyPrintFunc.apply(ast); + assertNoFindings(); + astOpt = parseFunc.apply(output); + assertNoFindings(); + Assertions.assertFalse(parser.hasErrors()); + Assertions.assertTrue(astOpt.isPresent()); + Assertions.assertTrue(ast.deepEquals(astOpt.get())); + } + + @FunctionalInterface + protected interface ParseFunction { + Optional apply(String t) throws IOException; + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/typeparameters/cocos/TypeParametersHaveUniqueNamesTest.java b/monticore-grammar/src/test/java/de/monticore/types/typeparameters/cocos/TypeParametersHaveUniqueNamesTest.java new file mode 100644 index 0000000000..f3b2e77d7c --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/typeparameters/cocos/TypeParametersHaveUniqueNamesTest.java @@ -0,0 +1,73 @@ +package de.monticore.types.typeparameters.cocos; + +import de.monticore.types.typeparameters.TypeParametersMill; +import de.monticore.types.typeparameters._ast.ASTTypeParameters; +import de.monticore.types.typeparameterstest.TypeParametersTestMill; +import de.monticore.types.typeparameterstest._cocos.TypeParametersTestCoCoChecker; +import de.monticore.types.typeparameterstest._parser.TypeParametersTestParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.io.IOException; +import java.util.Optional; + +public class TypeParametersHaveUniqueNamesTest { + + TypeParametersTestCoCoChecker checker; + + @BeforeEach + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TypeParametersTestMill.reset(); + TypeParametersTestMill.init(); + checker = new TypeParametersTestCoCoChecker(); + checker.setTraverser(TypeParametersTestMill.traverser()); + checker.addCoCo(new TypeParametersHaveUniqueNames()); + } + + @ParameterizedTest + @ValueSource(strings = { + "", + "", + ", U extends T>" + }) + public void testValid(String model) throws IOException { + ASTTypeParameters params = parseAndCreateSymTab(model); + checker.checkAll(params); + Assertions.assertTrue(Log.getFindings().isEmpty()); + } + + @ParameterizedTest + @ValueSource(strings = { + "", + "", + ", U extends T, U extends NotT>" + }) + public void testInvalid(String model) throws IOException { + ASTTypeParameters params = parseAndCreateSymTab(model); + checker.checkAll(params); + Assertions.assertFalse(Log.getFindings().isEmpty()); + Assertions.assertEquals( + "0xFDC14", + Log.getFindings().get(0).getMsg().substring(0, 7) + ); + } + + protected ASTTypeParameters parseAndCreateSymTab(String model) + throws IOException { + TypeParametersTestParser parser = TypeParametersTestMill.parser(); + Optional astOpt = + parser.parse_StringTypeParameters(model); + Assertions.assertFalse(parser.hasErrors()); + Assertions.assertTrue(astOpt.isPresent()); + Assertions.assertTrue(Log.getFindings().isEmpty()); + TypeParametersMill.scopesGenitorDelegator().createFromAST(astOpt.get()); + return astOpt.get(); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/typeparameters/cocos/TypeParametersNoCyclicInheritanceTest.java b/monticore-grammar/src/test/java/de/monticore/types/typeparameters/cocos/TypeParametersNoCyclicInheritanceTest.java new file mode 100644 index 0000000000..4209473039 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/typeparameters/cocos/TypeParametersNoCyclicInheritanceTest.java @@ -0,0 +1,96 @@ +package de.monticore.types.typeparameters.cocos; + +import de.monticore.types.typeparameters.TypeParametersMill; +import de.monticore.types.typeparameters._ast.ASTTypeParameters; +import de.monticore.types.typeparameters._symboltable.ITypeParametersArtifactScope; +import de.monticore.types.typeparameters._symboltable.TypeParametersSTCompleteTypes; +import de.monticore.types.typeparameterstest.TypeParametersTestMill; +import de.monticore.types.typeparameterstest._cocos.TypeParametersTestCoCoChecker; +import de.monticore.types.typeparameterstest._parser.TypeParametersTestParser; +import de.monticore.types.typeparameterstest._visitor.TypeParametersTestTraverser; +import de.monticore.types3.ITypeCalculator; +import de.monticore.types3.Type4Ast; +import de.monticore.types3.TypeCalculator3; +import de.monticore.types3.generics.context.InferenceContext4Ast; +import de.monticore.types3.util.CombineExpressionsWithLiteralsTypeTraverserFactory; +import de.monticore.visitor.ITraverser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.io.IOException; +import java.util.Optional; + +public class TypeParametersNoCyclicInheritanceTest { + + TypeParametersTestCoCoChecker checker; + + @BeforeEach + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TypeParametersTestMill.reset(); + TypeParametersTestMill.init(); + checker = new TypeParametersTestCoCoChecker(); + checker.setTraverser(TypeParametersTestMill.traverser()); + checker.addCoCo(new TypeParameterNoCyclicInheritance()); + } + + @ParameterizedTest + @ValueSource(strings = { + "", + "" + }) + public void testValid(String model) throws IOException { + ASTTypeParameters params = parseAndCreateSymTab(model); + checker.checkAll(params); + Assertions.assertTrue(Log.getFindings().isEmpty()); + } + + @ParameterizedTest + @ValueSource(strings = { + "", + "", + }) + public void testInvalid(String model) throws IOException { + ASTTypeParameters params = parseAndCreateSymTab(model); + checker.checkAll(params); + Assertions.assertFalse(Log.getFindings().isEmpty()); + Assertions.assertEquals( + "0xFDC12", + Log.getFindings().get(0).getMsg().substring(0, 7) + ); + } + + protected ASTTypeParameters parseAndCreateSymTab(String model) + throws IOException { + TypeParametersTestParser parser = TypeParametersTestMill.parser(); + Optional astOpt = + parser.parse_StringTypeParameters(model); + Assertions.assertFalse(parser.hasErrors()); + Assertions.assertTrue(astOpt.isPresent()); + Assertions.assertTrue(Log.getFindings().isEmpty()); + ITypeParametersArtifactScope artifactScope = TypeParametersMill + .scopesGenitorDelegator().createFromAST(astOpt.get()); + artifactScope.setName("aName"); + TypeParametersTestTraverser stCompleter = + TypeParametersTestMill.traverser(); + ITypeCalculator tc = getTypeCalculator(); + stCompleter.add4TypeParameters(new TypeParametersSTCompleteTypes(tc)); + astOpt.get().accept(stCompleter); + return astOpt.get(); + } + + protected ITypeCalculator getTypeCalculator() { + Type4Ast type4Ast = new Type4Ast(); + InferenceContext4Ast infCtx4Ast = new InferenceContext4Ast(); + ITraverser typeTraverser = + new CombineExpressionsWithLiteralsTypeTraverserFactory() + .createTraverser(type4Ast, infCtx4Ast); + return new TypeCalculator3(typeTraverser, type4Ast, infCtx4Ast); + } + +}