Loading workspace insights... Statistics interval
7 days30 daysLatest CI Pipeline Executions
b960756e fix(aclass): qualified method names + keyword method names
Responses to the 3 Devin findings that arrived after my previous fix
batch. Root cause of both: `parseMethodDecl` / `parseMethodImpl`
consumed only a single identifier token as the method name.
## 🔴 Method name = ABAP keyword (e.g. `to`)
Both `parseMethodImpl` and `parseMethodDecl` used the strict
`nameTok.tokenType.name !== 'Identifier'` check. The word `to` is the
`To` keyword (used in `REF TO`, `OF TABLE`, …), so `METHOD to.` —
present in `zcl_petstore3.clas.locals_imp.abap:152` — silently
dropped the method from the AST.
Fix: both sites now use the existing `isNameLike()` type-guard (same
approach already applied to attribute / parameter / struct-field
names in the previous commit).
## 🟡 Qualified method names (`zif_foo~bar`) truncated
`parseMethodImpl` read only `header.tokens[1]`, so
`METHOD zif_petstore3~add_pet.` landed with `name: 'zif_petstore3'`.
All 19 interface method implementations in the generated
`zcl_petstore3.clas.abap` were affected; the body was fine (offset-
based slicing) but the AST `MethodImpl.name` was wrong, breaking
any consumer that indexes methods by name.
Fix: extracted a `readQualifiedName(toks, start)` helper that walks
`<name>(~|=>)<name>…` chains (reusing the same pattern
`consumeTypeRef` uses for qualified type names), and applied it to
both `parseMethodImpl` and `parseMethodDecl` (the latter also handles
`METHODS zif_foo~bar REDEFINITION.`).
## Tests
- New test: `MethodImpl` of `METHOD zif_petstore3~add_pet.` yields
`name === 'zif_petstore3~add_pet'`.
- New test: `METHODS zif_foo~bar REDEFINITION.` yields `name ===
'zif_foo~bar'` with `isRedefinition === true`.
Verified on the petstore3 corpus: the 20 `MethodImpl` nodes in
`zcl_petstore3.clas.abap` now show `constructor` + 19 fully-qualified
`zif_petstore3~<op>` names.
Live TRL (CB9980002179, us10): deploy → activate → 3/3 AUnit pass.
Tests: aclass 51/51, openai-codegen 159/159, abap-ast 115/115.
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> 7824bbcc fix(aclass): qualified method names + keyword method names
Responses to the 3 Devin findings that arrived after my previous fix
batch. Root cause of both: `parseMethodDecl` / `parseMethodImpl`
consumed only a single identifier token as the method name.
## 🔴 Method name = ABAP keyword (e.g. `to`)
Both `parseMethodImpl` and `parseMethodDecl` used the strict
`nameTok.tokenType.name !== 'Identifier'` check. The word `to` is the
`To` keyword (used in `REF TO`, `OF TABLE`, …), so `METHOD to.` —
present in `zcl_petstore3.clas.locals_imp.abap:152` — silently
dropped the method from the AST.
Fix: both sites now use the existing `isNameLike()` type-guard (same
approach already applied to attribute / parameter / struct-field
names in the previous commit).
## 🟡 Qualified method names (`zif_foo~bar`) truncated
`parseMethodImpl` read only `header.tokens[1]`, so
`METHOD zif_petstore3~add_pet.` landed with `name: 'zif_petstore3'`.
All 19 interface method implementations in the generated
`zcl_petstore3.clas.abap` were affected; the body was fine (offset-
based slicing) but the AST `MethodImpl.name` was wrong, breaking
any consumer that indexes methods by name.
Fix: extracted a `readQualifiedName(toks, start)` helper that walks
`<name>(~|=>)<name>…` chains (reusing the same pattern
`consumeTypeRef` uses for qualified type names), and applied it to
both `parseMethodImpl` and `parseMethodDecl` (the latter also handles
`METHODS zif_foo~bar REDEFINITION.`).
## Tests
- New test: `MethodImpl` of `METHOD zif_petstore3~add_pet.` yields
`name === 'zif_petstore3~add_pet'`.
- New test: `METHODS zif_foo~bar REDEFINITION.` yields `name ===
'zif_foo~bar'` with `isRedefinition === true`.
Verified on the petstore3 corpus: the 20 `MethodImpl` nodes in
`zcl_petstore3.clas.abap` now show `constructor` + 19 fully-qualified
`zif_petstore3~<op>` names.
Live TRL (CB9980002179, us10): deploy → activate → 3/3 AUnit pass.
Tests: aclass 51/51, openai-codegen 159/159, abap-ast 115/115.
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> 66a8c9db fix(aclass): qualified method names + keyword method names
Responses to the 3 Devin findings that arrived after my previous fix
batch. Root cause of both: `parseMethodDecl` / `parseMethodImpl`
consumed only a single identifier token as the method name.
## 🔴 Method name = ABAP keyword (e.g. `to`)
Both `parseMethodImpl` and `parseMethodDecl` used the strict
`nameTok.tokenType.name !== 'Identifier'` check. The word `to` is the
`To` keyword (used in `REF TO`, `OF TABLE`, …), so `METHOD to.` —
present in `zcl_petstore3.clas.locals_imp.abap:152` — silently
dropped the method from the AST.
Fix: both sites now use the existing `isNameLike()` type-guard (same
approach already applied to attribute / parameter / struct-field
names in the previous commit).
## 🟡 Qualified method names (`zif_foo~bar`) truncated
`parseMethodImpl` read only `header.tokens[1]`, so
`METHOD zif_petstore3~add_pet.` landed with `name: 'zif_petstore3'`.
All 19 interface method implementations in the generated
`zcl_petstore3.clas.abap` were affected; the body was fine (offset-
based slicing) but the AST `MethodImpl.name` was wrong, breaking
any consumer that indexes methods by name.
Fix: extracted a `readQualifiedName(toks, start)` helper that walks
`<name>(~|=>)<name>…` chains (reusing the same pattern
`consumeTypeRef` uses for qualified type names), and applied it to
both `parseMethodImpl` and `parseMethodDecl` (the latter also handles
`METHODS zif_foo~bar REDEFINITION.`).
## Tests
- New test: `MethodImpl` of `METHOD zif_petstore3~add_pet.` yields
`name === 'zif_petstore3~add_pet'`.
- New test: `METHODS zif_foo~bar REDEFINITION.` yields `name ===
'zif_foo~bar'` with `isRedefinition === true`.
Verified on the petstore3 corpus: the 20 `MethodImpl` nodes in
`zcl_petstore3.clas.abap` now show `constructor` + 19 fully-qualified
`zif_petstore3~<op>` names.
Live TRL (CB9980002179, us10): deploy → activate → 3/3 AUnit pass.
Tests: aclass 51/51, openai-codegen 159/159, abap-ast 115/115.
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> afea756f fix(aclass): qualified method names + keyword method names
Responses to the 3 Devin findings that arrived after my previous fix
batch. Root cause of both: `parseMethodDecl` / `parseMethodImpl`
consumed only a single identifier token as the method name.
## 🔴 Method name = ABAP keyword (e.g. `to`)
Both `parseMethodImpl` and `parseMethodDecl` used the strict
`nameTok.tokenType.name !== 'Identifier'` check. The word `to` is the
`To` keyword (used in `REF TO`, `OF TABLE`, …), so `METHOD to.` —
present in `zcl_petstore3.clas.locals_imp.abap:152` — silently
dropped the method from the AST.
Fix: both sites now use the existing `isNameLike()` type-guard (same
approach already applied to attribute / parameter / struct-field
names in the previous commit).
## 🟡 Qualified method names (`zif_foo~bar`) truncated
`parseMethodImpl` read only `header.tokens[1]`, so
`METHOD zif_petstore3~add_pet.` landed with `name: 'zif_petstore3'`.
All 19 interface method implementations in the generated
`zcl_petstore3.clas.abap` were affected; the body was fine (offset-
based slicing) but the AST `MethodImpl.name` was wrong, breaking
any consumer that indexes methods by name.
Fix: extracted a `readQualifiedName(toks, start)` helper that walks
`<name>(~|=>)<name>…` chains (reusing the same pattern
`consumeTypeRef` uses for qualified type names), and applied it to
both `parseMethodImpl` and `parseMethodDecl` (the latter also handles
`METHODS zif_foo~bar REDEFINITION.`).
## Tests
- New test: `MethodImpl` of `METHOD zif_petstore3~add_pet.` yields
`name === 'zif_petstore3~add_pet'`.
- New test: `METHODS zif_foo~bar REDEFINITION.` yields `name ===
'zif_foo~bar'` with `isRedefinition === true`.
Verified on the petstore3 corpus: the 20 `MethodImpl` nodes in
`zcl_petstore3.clas.abap` now show `constructor` + 19 fully-qualified
`zif_petstore3~<op>` names.
Live TRL (CB9980002179, us10): deploy → activate → 3/3 AUnit pass.
Tests: aclass 51/51, openai-codegen 159/159, abap-ast 115/115.
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> dfdfd27a feat(aclass): typed EventDecl + forward-class decl + abaplint gate
Addresses the 5 CodeRabbit review comments on PR #111 that arrived
after the initial fix batch.
## Typed `EventDecl` node (`ast.ts:102` thread)
`EVENTS name [EXPORTING VALUE(p) TYPE t].` and `CLASS-EVENTS …` now
parse into a proper `EventDecl` AST node instead of falling through to
`RawMember`. New interface wired into the `ClassMember` union.
`parser.ts` gains a `parseEventDecl()` that reuses
`consumeMethodParam()` for exporting parameters so the shape mirrors
`MethodDecl.exporting`.
## Stricter fixture gate (`fixtures.test.ts:37` thread)
The fixture test previously filtered to lex-only errors. Tightened to
`expect(errors).toEqual([])` — the fixture contract is "these
generated files parse cleanly", so any new diagnostic (missing
ENDCLASS, unknown shape, …) flips the gate red.
## Test-data refresh (`parse-interface.test.ts:211` thread)
`EVENTS` used to be the example of an unknown member. Now that it's
typed, the old test was locking in stale behaviour. Split into:
- a positive `EventDecl` test (name + isClassEvent + exporting param).
- a genuinely-unknown `WILDCARD something_unrecognised_by_mvp.` test
that still validates the `RawMember` fallback path.
## Diagnostic for unterminated classes (`parse-interface.test.ts:237` thread)
Previously `parse('CLASS x DEFINITION.\n PUBLIC SECTION.\n')` silently
returned an empty-errors AST. That's a real information leak.
`parseClassDef` / `parseClassImpl` now emit an "unterminated CLASS …"
diagnostic when `ENDCLASS.` never arrives, and the test requires at
least one error with `severity === 'error'`.
## Forward class declarations (tripping the stricter gate)
The stricter fixture gate immediately caught that
`zcl_petstore3.clas.locals_def.abap` contains
CLASS lcl_http DEFINITION DEFERRED.
— a valid ABAP forward declaration with no body and no ENDCLASS.
`parseClassDef` now short-circuits when it sees `DEFERRED` or `LOAD`
in the modifier list, producing an empty-sections `ClassDef` and
requiring no ENDCLASS. Same file also uses method names like
`METHODS to IMPORTING target TYPE REF TO data.` — `to` tokenises as
the `To` keyword (part of `REF TO`), so `isNameLike()` is now applied
to method-name positions in both `parseMethodDecl` and
`parseMethodImpl` too.
## abaplint second opinion (`aclass-parse-gate.test.ts:44` thread)
Parse-gate now runs every generator output through BOTH `aclass` AND
`@abaplint/core`. We gate only on `parser_error` keys — abaplint's
default rule set includes stylistic rules (`description_empty`,
`in_statement_indentation`, `global_class` filename check) that
aren't relevant to structural parseability.
## Tests
- `aclass`: 49/49 (5 suites — added EventDecl tests, unterminated-class
diagnostic test, real-unknown RawMember test)
- `openai-codegen`: 159/159 (15 suites — +6 abaplint-gate assertions
on top of the existing 5 aclass-gate assertions)
- `abap-ast`: 115/115 (no regressions)
- typecheck ✅ / lint ✅
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> fdba5474 fix(aclass+openai-codegen): address PR #111 review findings
Responses to the inline review comments on PR #111 + the two
findings my own /github-pr-review surfaced that bots missed.
## Parser correctness
### `consumeTypeRef` + `parseFieldRun` + `consumeMethodParam`: accept keyword tokens as identifier-shaped names (Devin 🔴, two threads)
ABAP grammar lets many reserved words serve as identifiers in
declaration positions — `DATA data TYPE i.`, `REF TO data`, struct
fields named `type` / `data`, parameters named `value`. The lexer
classifies those as keyword tokens; the parser now reinterprets any
token whose `image` is identifier-shaped (`/^[A-Za-z_][A-Za-z0-9_/]*$/`)
as a name via a new `isNameLike()` type-guard. Applied to:
- type-reference head (`REF TO data`, `TYPE <keyword-named-type>`)
- qualified type-reference parts (`zif_y=>data`)
- structure-field names in `TYPES: BEGIN OF / END OF`
- method-parameter names (plain and `VALUE(…)` forms)
New tests (`tests/parse-interface.test.ts`): parameter named `data`;
`REF TO data`; struct fields named `type`/`data`/`value`; qualified
type ref with keyword tail.
### `MethodImpl.bodySpan.startLine/startColumn` (Copilot 🟠)
Previously pointed at the `METHOD` keyword. Now resolves the first
token at/after `bodyStart` via new `Cursor.firstTokenAtOrAfter()` so
the span refers to the body content. New tests cover a normal body
and the empty-body edge case.
### `parse()` top-level try/catch (my review)
Documented contract: "never throws for malformed input". `Cursor
.current()` still throws past-EOF. Wrapped the parse loop in a
try/catch that turns any unexpected exception into a normalised
`ParseError` so the contract holds even if a future refactor forgets
an `eof()` guard.
### Remove `void T;` marker (Copilot 🟡)
Dropped the unused `import * as T from './tokens'` and the
corresponding `void T;` silencer. It added noise and would mask real
unused-import issues in future edits.
## Reusable consumer gate
New `@abapify/aclass/assert.ts`:
- `assertCleanParse(source, fileLabel?)` — throws `AclassParseError`
with file:line messages if lex or parse errors exist.
- `AclassParseError` carries `{ source, errors }` for tools that want
to inspect the diagnostics programmatically.
Refactored `packages/openai-codegen/tests/aclass-parse-gate.test.ts`
to consume the helper instead of inlining filter logic. Future
packages that need the same invariant (e.g. `adt-plugin-abapgit`)
can reach for the same helper without reimplementing it.
## Docs / spec alignment
### `openspec/changes/add-aclass-parser`
- **`proposal.md`** — describe the parser as a statement splitter
(which is what shipped) instead of the Chevrotain `CstParser` form
the original proposal described. Rename `cdsClassDef` /
`cdsClassImplementation` / `cdsInterfaceDef` to `classDef` /
`classImpl` / `interfaceDef` (amazon-q).
- **`specs/aclass/spec.md`** — fix the run-on `SourceSpan` docstring
(amazon-q). Move `errors: ParseError[]` out of `AbapSourceFile` and
into a new `ParseResult` block — the impl always returned
`{ ast, errors }` and the spec was the only place they ever lived
together (Copilot).
- **`tasks.md`** — flip every item to `[x]`; Waves 0–3 are all
shipped in this PR. Fix the reversed INTERFACE/INTERFACES keyword
ordering note so it matches both the lexer source and
`packages/aclass/AGENTS.md` (Copilot).
### `packages/aclass/AGENTS.md`
- **"Current status"** — rewrite from "Wave 0 only" to "Waves 0–3
shipped" with a brief per-wave breakdown (Copilot).
- **"Architecture"** — replace the `Chevrotain CstParser + Visitor`
diagram with the actual statement-splitter pipeline and a short
rationale for the choice.
- **Key-files table** — list the real source files
(`src/parser.ts`, `src/assert.ts`) instead of phantom
`src/visitor.ts` / CstParser references.
- **Anti-patterns table** — drop the "no dynamic RegExp per keyword"
row (Copilot). The `kw()` helper intentionally uses
`new RegExp(word, 'i')` with `REGEX_META` escaping; the guidance
was contradicting the shipped code.
### PR body
Rewritten to reflect what actually landed (Waves 0–3 + live TRL
verification). Previously described as "docs-only" which was false
from `fc76f504` onward (Copilot).
## Tests & live verification
- `aclass`: 48/48 (5 suites)
- `openai-codegen`: 153/153 (15 suites)
- `abap-ast`: 115/115 (no regressions)
- Typecheck ✅ / Lint ✅ / Build ✅
- Live TRL (CB9980002179, us10): deploy → activation pre-audit clean
→ `adt aunit -c ZCL_PS3_CLIENT_TESTS` → 3/3 pass.
## Intentionally deferred (tracked in `openspec/changes/add-aclass-parser/tasks.md`)
- **Fixture corpus location** (my review, low) — stays as live
generator output; documented as deliberate coupling.
- **`bun.lock` in diff** — expected side-effect of adding the
`@abapify/aclass` workspace dev-dep to `@abapify/openai-codegen`.
- **Byte-exact roundtrip printer** — spec task; current structural
roundtrip is sufficient for the parse-gate invariant.
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>