Skip to content

feat(feature): add Validator plugin extension and character-level pre-transform phase#62

Open
bbodi wants to merge 1 commit into
MadAppGang:mainfrom
bbodi:feat/feature-validator-and-char-level-phase
Open

feat(feature): add Validator plugin extension and character-level pre-transform phase#62
bbodi wants to merge 1 commit into
MadAppGang:mainfrom
bbodi:feat/feature-validator-and-char-level-phase

Conversation

@bbodi

@bbodi bbodi commented May 18, 2026

Copy link
Copy Markdown

Summary

Adds the plumbing that lets out-of-tree feature plugins (e.g. dingo-mut) hook into the transpilation pipeline without the core having to know about them by name. No built-in plugin participates yet — this PR is pure infrastructure.

1. New Validator plugin extension (pkg/feature/plugin.go)

  • Optional interface: any Plugin can additionally implement Validator to run a post-typecheck pass against the Go AST and go/types.Info.
  • New ValidateContext carries FileSet, GoFile, TypeInfo, the shared cross-phase Registry, and the original Dingo filename.
  • New ValidationError (Plugin, Pos, Line, Column, Message) with an Error() method so diagnostics flow through the usual error machinery.

2. Engine.Validate + Engine.IsEnabled (pkg/feature/engine.go)

  • Engine.Validate(fset, file, info, filename) walks every enabled plugin in the same priority order as Transform (character-level first, then token-level), collects each Validator's []ValidationError, and back-fills Plugin from p.Name() when the plugin left it blank.
  • Engine.IsEnabled(name) exposes the existing internal predicate so pipeline callers can gate optional phases on a specific plugin's config without scanning EnabledPluginNames.

3. Pipeline hook (pkg/transpiler/pure_pipeline.go)

  • Step 0.5 — run Engine.TransformCharacterLevel over the raw source before any built-in transformation. The pipeline does not know which plugins participate; built-ins and externals look identical from here.
  • Step 3.55 — after parsing + typechecking, run Engine.Validate. On the first error the pipeline aborts with <plugin> check error at <file>:<line>:<col>: <msg>, falling back to the literal label validation when the plugin did not name itself.
  • The Engine and the feature.EnabledFeatures map are constructed once near the top of the pipeline so both phases share them.

Why this is split out

The mutability work (separate plugin, separate repo) needs a stable surface to plug into. Landing the plumbing on its own keeps that PR's diff focused on the actual mutability semantics rather than the engine wiring.

Test plan

  • go build ./...
  • go test ./pkg/feature/... ./pkg/transpiler/...
  • CLAUDE.md forbidden-pattern check on touched files (no new bytes.Index / strings.Index / regex / byte-offset scans introduced)

🤖 Generated with Claude Code

…acter-level pre-transform phase

Introduces two pieces of plumbing in the feature engine that are needed for
out-of-tree plugins (e.g. dingo-mut) to hook into the transpilation pipeline
without the core having to know about them by name:

1. Validator plugin extension (pkg/feature/plugin.go)
   - New optional interface `Validator` that any Plugin can implement to run
     a post-typecheck pass against the Go AST + go/types Info.
   - New `ValidateContext` carrying FileSet, GoFile, TypeInfo, the shared
     cross-phase Registry, and the original Dingo filename.
   - New `ValidationError` struct (Plugin, Pos, Line, Column, Message) with
     an `Error()` method so callers can surface diagnostics through the
     usual error machinery.

2. Engine.Validate + Engine.IsEnabled (pkg/feature/engine.go)
   - `Engine.Validate(fset, file, info, filename)` walks every enabled
     plugin in the same priority order Transform uses (character-level
     first, then token-level), collects the slice of `ValidationError`s
     each Validator returns, and back-fills the plugin name when the
     plugin left it blank.
   - `Engine.IsEnabled(name)` exposes the existing internal predicate so
     pipeline callers can gate optional phases on a particular plugin's
     config without scanning EnabledPluginNames.

3. Pipeline hook (pkg/transpiler/pure_pipeline.go)
   - Step 0.5: run `Engine.TransformCharacterLevel` over the raw source
     before any built-in transformation. The pipeline does not know which
     plugins participate; built-ins and externals look identical.
   - Step 3.55: after parsing + typechecking, run `Engine.Validate`. If
     any validator reports an error, the pipeline aborts with a
     `<plugin> check error at <file>:<line>:<col>: <msg>` diagnostic,
     using a generic "validation" label when the plugin did not name
     itself.
   - The Engine and the feature.EnabledFeatures map are now built once
     near the top of the pipeline so both phases share them.

No built-in plugin currently implements `Validator`; this change is the
pure plumbing addition that unblocks the out-of-tree mutability plugin.
bbodi added a commit to bbodi/dingo that referenced this pull request May 18, 2026
…g#62 into fork main

Combines all four currently open pull requests against MadAppGang/dingo into
the local fork's main so downstream work (e.g. the nodes.go cascade fix) can
build on top of all of them without one-by-one branch juggling. The
upstream PRs remain unaffected because their base branch is MadAppGang/dingo:main,
not bbodi/dingo:main.

Included:
  MadAppGang#59 fix(ast/transform): recognise method receiver param lists for type annotations
  MadAppGang#60 test(tokenizer): cover ENUM and GUARD keywords at both surfaces
  MadAppGang#61 fix(ast/enum_parser): allow arbitrary nesting of '*' and '[]' in variant field types
  MadAppGang#62 feat(feature): add Validator plugin extension and character-level pre-transform phase
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant