Repository navigation
v1.0.18
✨ Generation-time hardening & diagnostics
-
Providers may no longer declare a
context.Contextparameter
AddedcheckProviderContextParams: if a provider (constructor registered viaProvide, a closure, ordig.Module) declares acontext.Contextparameter, digen now rejects it at generation time with afile:line:collocation and a💡 Fix:, aborting without writing a broken file.
Reason: providers are resolved eagerly insideInitApp, before the runtimecontext.Contextexists, so the parameter would be undefined in the generated code; context injection is only valid insidedig.Invoke(which runs in the innerfunc(ctx)). Added regression exampleexample/gen_failures/provider_ctx/. -
//go:build digenis now validated at generation time
AddedcheckBuildSourceConstraint: if a source file containing adig.Buildcall lacks the//go:build digenconstraint, digen errors at theBuildFinalNodesstage with a💡 Fix:, aborting generation (no file written). Reason: digen hard-codes//go:build !digenon the generated file, so without the matching tag on the source, a normalgo buildcompiles both files and fails withInitApp redeclared. This promotes the long-standing "iron rule" from a doc convention into an invariant actually enforced by the generator. -
Post-generation
go/typessafety net
internal/generator/generator.goaddstypeCheckGenerated, run afterformat.Sourceand beforeos.WriteFile. Because the user's source is already type-checked during loading (packages.LoadwithNeedTypesand-tags=digen, whiledig_gen.gois excluded via//go:build !digen), any type error in the generated file is unconditionally an internal generator bug. On trigger: (1) no broken file is written; (2) the error is explicitly labeled an internal generator bug with the generated-file location; (3) a one-click, pre-filled GitHub issue link (https://github.com/shanjunmei/dig/issues/new?title=...&body=...) plus a copy-paste template is printed to drive the report upstream. If the type-check infrastructure itself fails, it best-effort falls back to writing the file normally. The net only backstops unknown extraction/generation defects — it never replaces semantic rules and never masquerades as a user error. -
dig.Supplyof a module parameter no longer emits an undefined cross-package reference
Fixed a generator bug wheredig.Supply(x)whose valuexis a function parameter or local variable of an inlineddig.Modulewas package-qualified (e.g.user.cfg), triggeringundefined: <pkg>.<param>in the post-generation safety net. Such free variables are captured by the target function's own scope and are now referenced verbatim; only package-level symbols (var/func/const/type) are still qualified, preserving existing behaviour for cases likedb.Indexandrole.Config("production"). The fix also prevents the inlined supply from pulling an unused import for the source package. Added regression exampleexample/supply_param/(+example/supply_param_helper/), auto-discovered byexample/successtest. -
AST-based type-name rewriting replaces regex rewriting
Retired the fragilereplaceTypeNamesregex string rewriter (the word-boundary regex would wrongly rewrite same-named tokens inside string literals / comments, and bare-name matching could clobber same-named locals). It is replaced by a precisego/ast+astutil.Applyrewrite: first collect aPos -> "alias.Name"rewrite plan keyed bytoken.Pos(explicitly skippingSelectorExpr.Sel), then apply it on a clone, touching only matched identifiers — never string literals, comments, or same-named locals. ThereplacePkgPathWithAliaspath→alias rewrite for type strings is kept for backward compatibility. Generated output is behaviourally identical. -
Stable serializable IR and optional disk cache
The extractor→generator intermediate representation[]model.Nodeis now formalized as a serializable, schema-versioned stable IR:internal/modelgainsCachedExtraction(Nodes + ImportAliasMap/PkgAliasMap/PkgNameMap +SchemaVersion) andNode/Arggain JSON tags;UnmarshalJSONerrors on aSchemaVermismatch instead of silently misreading. A newinternal/irpackage handles disk read/write (JSON by default, atomic write via temp file + rename), enabled by thecmd/digen-cache(default off) /-cachedirflags. When enabled, unchanged packages skip the expensive extraction / type-check step and reuse the cached IR; the cache key covers config knobs,runtime.Version(), and (recursively) the source hashes of the package and its transitive dependencies, so a dependency API change auto-invalidates the cache with no manual cleanup. Any cache-path failure gracefully falls back to re-extraction, and the cache is off by default so generation semantics are unaffected.
🐛 Bug Fixes (regressions introduced in v1.0.17)
- Closure parameters / local variables falsely reported as
private("var X is private")
ThecheckFunctionVisibilityInClosureadded in v1.0.17 walks every bare-identifier call (fn(args)/T(x)) in a closure body before generation and runscheckGenerationVisibilityon the call's function identifier. ButcheckGenerationVisibilityalso treats closure parameters (and local function / type variables) — which are*types.Var/*types.TypeName— as "package-level unexported symbols". When the closure is defined in a package different from the generation target (the classic cross-package module-inlining caseuser.Module(cfg), where the closure lives inuserbut the target iscmd/app),curPkg != mainPkgPathand the name is unexported, so it is misclassified as "not visible across packages" and rejected — meaning a perfectly valid closure-parameter call fails to generate.dig.Invoke(func(f func() Config){ _ = f() })triggers exactlyvar "f" is private.
Fix (internal/extractor/visibility.go, commit81e2a78): the*types.Varbranch ofcheckGenerationVisibilitynow returns early withif !isPackageLevelVar(o) { return nil }, validating only genuine package-level variables; the*types.TypeNamecase is guarded by the sameisPackageLevelVarreasoning. Because the closure body is inlined into the target package, parameters and locals never constitute a cross-package reference and are always allowed. This also removes the false positives for legitimate cases beyondprivate_visibility/closure_private_fn.
📦 Examples and Documentation Updates
- Automated regression tests for
gen_failures/
Addedexample/gen_failures/gentest/gen_failures_test.go, which walks each subdirectory, builds digen into a temp path, and asserts a non-zero exit plus a matching expected-error substring — soprovider_ctxand other failure fixtures are caught by CI. Also added the//go:build digentag (tag-only, no logic change) to four previously untagged fixtures (ambiguous/cycle/missing_provider/named_mismatch). - Fixed an inaccurate "digen static check" claim in
prompts/system_prompt_dig.md/_en.md— the source-file//go:build digenrequirement is now actually enforced by digen at generation time.