Repository navigation
Releases
v1.0.24
[v1.0.24] - 2026-09-14
🐛 Bug Fixes
- Generated code type-checks again when a dependency re-aliases a stdlib package — a dependency's
import gotime "time"no longer leaks into your generated file's imports, andForceAliaskeeps the import name in sync with what the inlined closures actually reference. This resolves theundefined: time/"time" imported as gotimefailure. - No more
gogotimefrom alias self-substitution — package-alias rewriting is now boundary-aware and idempotent; aliases that contain the package path (e.g.gotime⊃time) are not re-substituted, and multi-byte UTF-8 identifiers/string literals are handled correctly. - Closures can use in-scope variables without false errors —
rangeKey/Value, nested closure parameters, type-switch guards, if-init vars, and named results are no longer wrongly reported as "capturing a local variable". A subtle false negative (name-shadowed outer variable slipping through, generatingundefined: x) is also fixed. - Package-level variables referenced in wiring keep their real value — a main-package package-level variable is now referenced directly instead of being silently promoted to a DI-injected parameter, so the generated code no longer accidentally pulls the value from the container. Package-level
context.Contextvariables are still rejected with a clear message.
📦 Examples
- New
example/closure_scope_locals/(every in-closure variable form) andexample/gen_failures/closure_shadow_outer/(name-shadowing must error), with regression tests.
♻️ Internals
- Capture detection switched from a name set to an object-position interval;
seendedup keyed bytypes.Object; error reporting keeps the first capture error.
v1.0.23
[v1.0.23] - 2026-09-04
🐛 Bug Fixes
- Identity closures no longer misread as type conversions — closures like
T(p),&p,*pare now always recognized as identity conversions and collapsed correctly (e.g. interface/pointer wrapping).
♻️ Internals
- Closure-analysis code simplified and hardened with defensive checks; no runtime behavior change.
v1.0.22
[v1.0.22] - 2026-08-27
🔧 Changed
- Minimum Go version lowered to 1.22 —
go.modnow requires Go 1.22.0 (down from 1.25.0) andgolang.org/x/toolsis pinned to v0.30.0. digen now builds and runs on a much wider range of Go toolchains with no loss of capability.
🐛 Bug Fixes
- String-constant double-escaping fixed — closures capturing a string constant are no longer double-quoted; the generated literal is now correct. Added regression example
example/closure_capture_string_const. - Unused-provider detection fixed — unused providers that return an error are now handled correctly.
⚡ Improved
- Batch generation is much faster —
digen ./...now uses a batch type-checking pass. - Resilient generation — the pipeline is split into extract / validate / write; a failing package is skipped at write time instead of aborting the whole run.
- Deterministic digit aliases — alias generation no longer depends on parsing order.
- Better diagnostics —
packages.Loadfailures anddigen initnow print actionable💡 Fix:guidance;digen checkexits non-zero when any package is invalid.
♻️ Internals
- Cache keys switched from content hashes to file size + mtime;
cmd/digenv1marked// Deprecated:;digen explainerror strings normalized (clears staticcheck ST1005).
v1.0.21
[v1.0.21] - 2026-08-25
🐛 Bug Fixes
- Closures can now reference cross-package exported variables/constants (e.g.
pkg.ExportedVar,pkg.ExportedConst,time.Hour) without a false "capturing a local variable" error — the extractor whitelists cross-package exported symbols and applies the correct import-alias replacement. - More robust IIFE inlining —
buildIIFECallnow parses the closure signature instead of string-cuttingClosureDef. - Generator error-handling chain fixed —
formatParams/writeMainFuncnow propagate errors correctly instead of swallowing them. - New closure-capture examples
example/closure_capture_exportedandexample/closure_capture_timehour, plus regression tests. - Docs site left-nav (TOC) follows language switches in real time —
applyLangnow re-derives each TOC link label per the active language (in place, no node rebuild) and keeps the search placeholder in sync; fixes the in-page language switch leaving the left TOC stuck.
📝 Documentation
- Corrected the
-cachedocs — they previously claimed-cache"skips extraction / type-checking"; type-checking (packages.Load) was never cached and still runs every time. Docs now say "skips extraction only (type-checking still runs every time)", noting that-cacheis actually slower for the common "edit thendigen ./..." flow and only pays off for large DI graphs or repeated read-only subcommands.
♻️ Internals
- Closure-extraction code restructured —
internal/extractor/closure.gosplit intoclosure.go/closure_analysis.go/closure_emission.go; generation semantics unchanged.
v1.0.20
[v1.0.20] - 2026-08-21
🔧 Changed
- Identity-closure collapse is now always applied, independent of
-inline— closures such asfunc(p *Impl) Iface { return p },func(p *T) T { return *p },func(p T) *T { return &p }, direct type conversionfunc(p T) U { return U(p) }, and type assertionfunc(p any) T { return p.(T) }all collapse to a single inline expression (T(p)/*p/&p/p.(T)). Previously this was coupled to the same-inlineswitch as IIFE inlining; now it is unconditional. -inlinenow controls IIFE inlining only (defaultfalse). It no longer affects identity closures. If you relied on-inline=falseto disable identity-closure collapsing, that no longer works — identity collapse is always on (it is a literal-equivalent, zero-semantic-change transform).
v1.0.19
[v1.0.19] - 2026-08-18
digenCLI subcommands + generation-time contract pre-check
✨ New Features
- Five new
digensubcommands (reuse the generation pipeline, accept all existing flags):digen init— scaffold adi.gowith thedig.Buildentry pointdigen check— validate DI contracts without writing any file (lightweight static check)digen graph— print the provider dependency graph as Mermaiddigen explain <type>— explain how a type / provider is resolveddigen completion <shell>— emit bash / zsh / fish completion scriptsdigen -hand the completion scripts now list every flag and subcommand.
🔧 Improved Diagnostics
- Generation-time contract pre-check: ensures
di.goholds wiring only. If any wiring references a package-level symbol defined inside a//go:build digenfile, generation aborts (no file written) with a clear error and a💡 Fix:that names the file and tells you to move the definition to a non-digen file or an imported package. Covers constructor signatures,dig.Supplyvalue types, closure signatures & free-variable types, merged parameter types, recursing through pointers / slices / maps / channels and generic type arguments. - Post-generation safety net upgraded: now classifies contract violations vs internal generator bugs. Contract violations get the same
💡 Fix:as the pre-check; genuine bugs emit a pre-filled issue link. Also fixes a previously misleading error message.
⬆️ Upgrade Notes
- This is a largely additive, non-breaking release. The only behavior change: the generation-time contract pre-check now catches a class of wiring that was already invalid before any file is written — a package-level symbol (type / func / var / const) defined inside a
//go:build digenfile and referenced bydig.Provide/Invoke/Supplyor a closure body. Previously this only surfaced during the post-generation type check; now generation aborts immediately with a💡 Fix:. - If you do reference a symbol defined in a digen file from your wiring, follow the
💡 Fix:to move the definition to a file without the//go:build digentag, or reference it from an imported package. - Run
digen checkfor a pure static contract check before triggering a full generation.
🛠 Internals & Quality (for contributors)
- Build-constraint checks consolidated into a single source of truth,
internal/buildconstraint(FileHasDigenConstraint/RequiresDigen), removing duplicated logic. - New golden-file byte-diff regression test (
example/golden/golden_test.go) covering 11 complex examples, catching silent "compiles but semantically drifted" changes.
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.
v1.0.17
🐛 Bug Fixes
-
Fix bare function/type calls inside closures not intercepting unexported cross-package symbols
When the extractor lifts a closure from its original package into the generation target package, it prefixes bare identifier calls with the package qualifier. If the original package contains an unexported function (e.g.buildAuditAuthorizer), lifting produces an illegalpkg.buildAuditAuthorizercross-package reference. Becausedigendoes not compile the generated output, it silently exited 0 and emitted uncompilable code.Fix:
- Added
checkFunctionVisibilityInClosure: before generation, walks all bare identifier calls (fn(args)/T(x)) in the closure body and usescheckGenerationVisibilityto reject unexported cross-package symbols (same-package, exported, and builtin symbols are allowed), producing a clearprivateerror and no broken file - Extended
checkGenerationVisibility's switch with a*types.TypeNamecase to also cover unexported cross-package types in type conversionsT(x) - Added regression example
example/gen_failures/closure_private_fn/
- Added
-
Fix parameter shadowing in
example/db/db.goRedisClient.Ping
Ping(index RedisDbIndex)used the package-level variableIndexinstead of theindexparameter in itsfmt.Printf, making the parameter effectively unused. Now correctly references theindexparameter. -
Fix duplicate-binding error message for default Supply
When two default (unnamed)dig.Supplycalls provide the same type, the error message incorrectly showedwith name ""instead of(default). Root cause: in the unnamed casekeyNamed == keyDefault, so the named-key check matched first. Refactored intoif instanceName != "" { ... } else { ... }branches so the default-instance error correctly shows(default).
♻️ Refactor and Optimisation
- Generator config and sample adjustment
Regenerated all exampledig_gen.gofiles with the unifieddvvariable prefix, inline inlining enabled by default, and debug logging removed from generated code. - unused_provider example reworked
example/gen_failures/unused_providernow registers its provider via thedig.Provide+dig.Supplypattern.
📦 Examples and Documentation Updates
- New success examples
example/app_runtime_err(runtime error-propagation paths: panic for provider errors, propagation for invoke errors) andexample/app_xpkg_generic(cross-package genericcache.Cache[*common.Config]where both the generic type and the type argument come from different packages). - New failure examples
Underexample/gen_failures/addedambiguous,duplicate_named,duplicate_supply,private_visibility,unused_provider, covering named-instance ambiguity, duplicate named binding, duplicate default Supply, cross-package unexported visibility, and unused provider error paths. - GitHub Pages site
Addeddocs/static site (project intro, core features, quick start, API overview, comparison matrix, examples, version timeline) plus a.nojekyllfile. - GitHub Pages bilingual toggle
Added 中文/EN language switching (localStorage persistence, synced title/meta, code blocks stay language-neutral). - System prompts & industrial coding-skill docs updated
Trimmed and synced the files underprompts/.
🔧 Chores
- Bumped version references in README / CHANGELOG / system prompts to v1.0.17.
v1.0.16
v1.0.16 Release Notes
Release Date: 2026-08-07
Statistics: 9 commits · 21 files changed · +941/-441 lines
Compare: v1.0.15...v1.0.16
✨ New Features
-
ShadowGuard variable name shadowing protection
Added a shadow protection mechanism that automatically detects and prevents variable name shadowing during code generation, improving the robustness of generated code. -
Default behavior for unused parameters changed
Changed the handling of unused parameters from the defaultdrop(discard) toerror(report an error), helping developers catch potential parameter omissions earlier.
🐛 Bug Fixes
-
Fix missing quotes for generic type parameters in logs
Improved the generator's logging of Identity Target Type by properly wrapping type parameters with quotes, making log output more consistent and readable. -
Fix identifier handling for cross‑package bare function calls
Fixed an issue where the extractor incorrectly recognised identifiers when handling cross‑package bare function calls. -
Fix free variable shadowing of package aliases
Resolved a problem where free variable parameter names shadowed package aliases in non‑closure items. Adjusted the extraction logic to register package aliases earlier, ensuring that ShadowGuard correctly handles parameter lists and free variable mappings. -
Fix shadowing caused by case‑sensitive variable names
Renamed the package‑level variableDbtodbto avoid naming conflicts with the importeddbpackage alias, and updated all code references accordingly.
♻️ Refactor and Optimisation
-
Refactor ShadowGuard and free variable handling
Refactored the shadowing logic for free variables and improved the ShadowGuard mechanism, enhancing the overall robustness of the code generator. -
Enhanced metadata in generated code
Added debug logs and clearer metadata information to the generateddig_gen.gofiles, making troubleshooting easier.
📦 Examples and Documentation Updates
-
Update Redis DB Index configuration example
In theexamplemodule, added a newRedisDbIndextype and a default variable, updated thePingmethod to receive and log the db index, and injected the db index via DI. -
Update all generated dig_gen.go files
Synchronised the regenerateddig_gen.gofiles across all example modules.
🔧 Chores
- Updated dependency versions and change logs
- Removed the
debug-aliasesflag and lifted the "one function per module" restriction - Improved the third‑party library comparison matrix and cleaned up example code
⬆️ Upgrade Guide
go get github.com/shanjunmei/dig@v1.0.16v1.0.15 - 2026-08-03
Fixed
- Type package collection logic (core robustness fix):
collectUsedPkgsFromType:*types.Namedbranch incorrectly walkedTypeParams()(declaration-level type params likeT any, which have no real package path); now walksTypeArgs()(instantiation type args like*common.Config) while retainingTypeParams()walk for cross-package references inside constraints- Does NOT call
walk(t.Underlying())on*types.Named: self-referential types (e.g.type Node struct{ Next *Node }) cause infinite recursion / stack overflow; cross-package references in struct fields / method signatures are covered bycollectTypeNameAndUsedPkgsAST traversal - Added
*types.Signaturebranch: function/method signature types (e.g. a provider returningfunc(*common.Config) error, or an invoke parameter being a function type) were previously not recursed into, so cross-package references inside signature params/results were not collected; generated code emitted full-pathfunc(*github.com/.../common.Config) errorinstead of an alias, breaking compilation. Now walksRecv/Params/Results/TypeParamsconstraints, relying onseenfor deduplication addPkgToUsedchanged from "typePkgsingle-value top-level package" to directly reusecollectUsedPkgsFromTypefull tree walk; no longer misses cross-package references inside Map key/value, slice elements, or generic type argumentsallTypesloop ingenerateClosureDefchanged similarly: closure params / free vars / return types no longer rely ontypePkgfor only the top-level package; instead all cross-package references are tree-collected andEnsureAlias-ed individually
Removed
- Standalone
-debug-aliasesdiagnostic flag: Alias mapping diagnostics are now unified under the global-debuglog (prints[alias]prefixed alias info automatically when-debugis on); no separate flag is provided anymore findExcludedPackagesInClosurehelper function: The BFS transitive import closure naturally excludes "other main packages" and "other library packages containingdig.Build" (Go forbids importing main packages, and library packages are never imported twice); the extra exclusion logic was dead code, removed without affecting generated output- Single
dig.Moduleper function restriction:findSingleModuleCall→findAllModuleCalls; helper functions may now contain multipledig.Modulecalls whose args are merged; Module calls inside control flow (if/switch/for/select) remain unsupported - Removed
gen_failures/multi_moduleandgen_failures/multi_module_callregression examples (no longer error cases)
Changed
- Third-party library comparison matrix expanded: README and system prompts now include a complete dig / Wire / Fx comparison across architecture, API design, error handling, runtime/operations, and project status dimensions, plus per-tool trade-off analysis
example/setupcleanup: Removed the redundantdigenbuild tag fromfull.goand deletedstub.go(a placeholder only used in non-digen builds)