diff --git a/pkl-core/src/main/java/org/pkl/core/ast/builder/AstBuilder.java b/pkl-core/src/main/java/org/pkl/core/ast/builder/AstBuilder.java index 91e815e73..f12dcb8e8 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/builder/AstBuilder.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/builder/AstBuilder.java @@ -1441,6 +1441,8 @@ public class AstBuilder extends AbstractAstBuilder { var scope = (ModuleScope) symbolTable.getCurrentScope(); scope.setModifiers(modifiers); + checkAbstractMembersAllowed(modifiers, mod.getProperties(), mod.getMethods()); + // visit imports first so that we already have the object member name available var imports = mod.getImports(); var importMembers = new ObjectMember[imports.size()]; @@ -1685,6 +1687,7 @@ public class AstBuilder extends AbstractAstBuilder { List properties = bodyNode != null ? bodyNode.getProperties() : List.of(); List methods = bodyNode != null ? bodyNode.getMethods() : List.of(); registerClassScopeNames(scope, properties, methods); + checkAbstractMembersAllowed(modifiers, properties, methods); var supertypeCtx = clazz.getSuperClass(); @@ -1775,6 +1778,30 @@ public class AstBuilder extends AbstractAstBuilder { }; } + private void checkAbstractMembersAllowed( + int enclosingModifiers, List properties, List methods) { + if (VmModifier.isAbstract(enclosingModifiers)) { + return; + } + for (var property : properties) { + checkMemberNotAbstract(property.getModifiers()); + } + for (var method : methods) { + checkMemberNotAbstract(method.getModifiers()); + } + } + + private void checkMemberNotAbstract(List modifiers) { + for (var modifier : modifiers) { + if (modifier.getValue() == ModifierValue.ABSTRACT) { + throw exceptionBuilder() + .evalError("abstractMemberInNonAbstractClass") + .withSourceSection(createSourceSection(modifier.span())) + .build(); + } + } + } + private UnresolvedPropertyNode[] doVisitClassProperties( List propertyContexts, Set propertyNames) { var propertyNodes = new UnresolvedPropertyNode[propertyContexts.size()]; diff --git a/pkl-core/src/main/resources/org/pkl/core/errorMessages.properties b/pkl-core/src/main/resources/org/pkl/core/errorMessages.properties index 0f378bb18..7ad9603aa 100644 --- a/pkl-core/src/main/resources/org/pkl/core/errorMessages.properties +++ b/pkl-core/src/main/resources/org/pkl/core/errorMessages.properties @@ -286,6 +286,11 @@ External members cannot have a body. abstractMemberCannotHaveBody=\ Abstract members cannot have a body. +abstractMemberInNonAbstractClass=\ +Cannot define an abstract member in a non-abstract class.\n\ +\n\ +A member can only be `abstract` if its enclosing class is also `abstract`. + methodNotDefined1=\ Method `{0}` is not defined for argument type `{1}`. diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/errors/abstractMemberInNonAbstractClass.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/abstractMemberInNonAbstractClass.pkl new file mode 100644 index 000000000..7417fbd26 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/abstractMemberInNonAbstractClass.pkl @@ -0,0 +1,5 @@ +class Foo { + abstract bar: Int +} + +res = new Foo { bar = 5 } diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/errors/abstractMemberInNonAbstractModule.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/abstractMemberInNonAbstractModule.pkl new file mode 100644 index 000000000..e9dff5a3d --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/abstractMemberInNonAbstractModule.pkl @@ -0,0 +1 @@ +abstract foo: Int diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/errors/abstractMethodInNonAbstractClass.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/abstractMethodInNonAbstractClass.pkl new file mode 100644 index 000000000..5f2a1213b --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/abstractMethodInNonAbstractClass.pkl @@ -0,0 +1,5 @@ +class Foo { + abstract function bar(): Int +} + +res = new Foo {} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/abstractMemberInNonAbstractClass.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/abstractMemberInNonAbstractClass.err new file mode 100644 index 000000000..836ca8f00 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/abstractMemberInNonAbstractClass.err @@ -0,0 +1,8 @@ +–– Pkl Error –– +Cannot define an abstract member in a non-abstract class. + +x | abstract bar: Int + ^^^^^^^^ +at abstractMemberInNonAbstractClass#Foo (file:///$snippetsDir/input/errors/abstractMemberInNonAbstractClass.pkl) + +A member can only be `abstract` if its enclosing class is also `abstract`. diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/abstractMemberInNonAbstractModule.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/abstractMemberInNonAbstractModule.err new file mode 100644 index 000000000..d31c181d0 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/abstractMemberInNonAbstractModule.err @@ -0,0 +1,8 @@ +–– Pkl Error –– +Cannot define an abstract member in a non-abstract class. + +x | abstract foo: Int + ^^^^^^^^ +at abstractMemberInNonAbstractModule (file:///$snippetsDir/input/errors/abstractMemberInNonAbstractModule.pkl) + +A member can only be `abstract` if its enclosing class is also `abstract`. diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/abstractMethodInNonAbstractClass.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/abstractMethodInNonAbstractClass.err new file mode 100644 index 000000000..a090b927a --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/abstractMethodInNonAbstractClass.err @@ -0,0 +1,8 @@ +–– Pkl Error –– +Cannot define an abstract member in a non-abstract class. + +x | abstract function bar(): Int + ^^^^^^^^ +at abstractMethodInNonAbstractClass#Foo (file:///$snippetsDir/input/errors/abstractMethodInNonAbstractClass.pkl) + +A member can only be `abstract` if its enclosing class is also `abstract`. diff --git a/pkl-doc/src/test/files/DocGeneratorTest/input/com.package1/moduleMethodModifiers.pkl b/pkl-doc/src/test/files/DocGeneratorTest/input/com.package1/moduleMethodModifiers.pkl index 6138dbb76..bd48b43c7 100644 --- a/pkl-doc/src/test/files/DocGeneratorTest/input/com.package1/moduleMethodModifiers.pkl +++ b/pkl-doc/src/test/files/DocGeneratorTest/input/com.package1/moduleMethodModifiers.pkl @@ -1,5 +1,5 @@ /// Module methods with different modifiers. -module com.package1.moduleMethodModifiers +abstract module com.package1.moduleMethodModifiers /// Method with `abstract` modifier. abstract function method1(arg: String): Boolean diff --git a/pkl-doc/src/test/files/DocGeneratorTest/output/run-1/com.package1/1.2.3/moduleMethodModifiers/index.html b/pkl-doc/src/test/files/DocGeneratorTest/output/run-1/com.package1/1.2.3/moduleMethodModifiers/index.html index 573620801..06c96ad88 100644 --- a/pkl-doc/src/test/files/DocGeneratorTest/output/run-1/com.package1/1.2.3/moduleMethodModifiers/index.html +++ b/pkl-doc/src/test/files/DocGeneratorTest/output/run-1/com.package1/1.2.3/moduleMethodModifiers/index.html @@ -32,7 +32,7 @@
-
module com.package1.moduleMethodModifiers
+
abstract module com.package1.moduleMethodModifiers

Module methods with different modifiers.

Module URI:
diff --git a/pkl-doc/src/test/files/DocGeneratorTest/output/run-2/com.package1/1.2.3/moduleMethodModifiers/index.html b/pkl-doc/src/test/files/DocGeneratorTest/output/run-2/com.package1/1.2.3/moduleMethodModifiers/index.html index 573620801..06c96ad88 100644 --- a/pkl-doc/src/test/files/DocGeneratorTest/output/run-2/com.package1/1.2.3/moduleMethodModifiers/index.html +++ b/pkl-doc/src/test/files/DocGeneratorTest/output/run-2/com.package1/1.2.3/moduleMethodModifiers/index.html @@ -32,7 +32,7 @@
-
module com.package1.moduleMethodModifiers
+
abstract module com.package1.moduleMethodModifiers

Module methods with different modifiers.

Module URI: