{"record":{"id":"fb87e8991952c633","repo":"apple/pkl","slug":"abstractmethodinnonabstracttype","errorCode":"abstractMethodInNonAbstractType","errorMessage":"abstractMethodInNonAbstractType","messagePattern":"abstractMethodInNonAbstractType","errorType":"error_code","errorClass":"VmException","httpStatus":null,"severity":"error","filePath":"pkl-core/src/main/java/org/pkl/core/ast/builder/AstBuilder.java","lineNumber":2002,"sourceCode":"      case EXTERNAL -> VmModifier.EXTERNAL;\n      case ABSTRACT -> VmModifier.ABSTRACT;\n      case OPEN -> VmModifier.OPEN;\n      case LOCAL -> VmModifier.LOCAL;\n      case HIDDEN -> VmModifier.HIDDEN;\n      case FIXED -> VmModifier.FIXED;\n      case CONST -> VmModifier.CONST;\n    };\n  }\n\n  private void checkAbstractMethodsAllowed(\n      int enclosingModifiers, List<ClassMethod> methods, String context) {\n    if (VmModifier.isAbstract(enclosingModifiers)) {\n      return;\n    }\n    for (var method : methods) {\n      for (var modifier : method.getModifiers()) {\n        if (modifier.getValue() == ModifierValue.ABSTRACT) {\n          throw exceptionBuilder()\n              .evalError(\"abstractMethodInNonAbstractType\", context)\n              .withSourceSection(createSourceSection(modifier.span()))\n              .build();\n        }\n      }\n    }\n  }\n\n  private UnresolvedPropertyNode[] doVisitClassProperties(\n      List<ClassProperty> propertyContexts, Set<String> propertyNames) {\n    var propertyNodes = new UnresolvedPropertyNode[propertyContexts.size()];\n\n    for (var i = 0; i < propertyNodes.length; i++) {\n      var propertyNode = visitClassProperty(propertyContexts.get(i));\n      checkDuplicateMember(propertyNode.getName(), propertyNode.getHeaderSection(), propertyNames);\n      propertyNodes[i] = propertyNode;\n    }\n","sourceCodeStart":1984,"sourceCodeEnd":2020,"githubUrl":"https://github.com/apple/pkl/blob/f3efcbfc9b60d30053b0536d664948d7aa1b8673/pkl-core/src/main/java/org/pkl/core/ast/builder/AstBuilder.java#L1984-L2020","documentation":"Abstract methods may only appear inside abstract classes/types. AstBuilder's check raises abstractMethodInNonAbstractType with the offending `abstract` modifier span when the enclosing type lacks the `abstract` modifier but any method in it declares `abstract`. This keeps the runtime from ever needing to dispatch to a body-less method on a concrete type.","triggerScenarios":"Declaring `abstract foo(): Unknown` inside a non-abstract `class Foo { ... }`; adding the abstract modifier to a method while forgetting to mark the enclosing class abstract.","commonSituations":"Refactoring a concrete class toward an interface-like shape; copying method signatures from an abstract base class into a concrete subclass.","solutions":["Mark the enclosing class `abstract class Foo { ... }`.","Remove the `abstract` modifier and provide a method body.","Move the abstract method to an abstract superclass."],"exampleFix":"// before\nclass Animal {\n  abstract speak(): String\n}\n\n// after\nabstract class Animal {\n  abstract speak(): String\n}","handlingStrategy":"validation","validationCode":"// Flag abstract methods inside non-abstract classes:\nif (!classDeclaresAbstract && methodBody.matches(\"\\\\s*abstract\\\\s+.*\")) {\n  report(file, lineNo, \"abstract method requires abstract enclosing class\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pair every `abstract` method with an `abstract class` declaration.","Only copy abstract signatures into abstract classes.","Prefer providing a default body in concrete classes instead of abstract methods."],"tags":["pkl","ast","abstract","class-definition"],"backgroundTag":"invalid-argument-value","analyzedSha":"f3efcbfc9b60d30053b0536d664948d7aa1b8673","analyzedAt":"2026-09-08T13:10:45.570Z","contentChangedAt":"2026-09-08T13:10:45.570Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}