Skip to content

Releases

v1.0.24

Choose a tag to compare

@shanjunmei shanjunmei released this 14 Sep 08:40

[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, and ForceAlias keeps the import name in sync with what the inlined closures actually reference. This resolves the undefined: time / "time" imported as gotime failure.
  • No more gogotime from 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 — range Key/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, generating undefined: 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.Context variables are still rejected with a clear message.

📦 Examples

  • New example/closure_scope_locals/ (every in-closure variable form) and example/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; seen dedup keyed by types.Object; error reporting keeps the first capture error.

v1.0.23

Choose a tag to compare

@shanjunmei shanjunmei released this 04 Sep 02:15

[v1.0.23] - 2026-09-04

🐛 Bug Fixes

  • Identity closures no longer misread as type conversions — closures like T(p), &p, *p are 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

Choose a tag to compare

@shanjunmei shanjunmei released this 27 Aug 09:32

[v1.0.22] - 2026-08-27

🔧 Changed

  • Minimum Go version lowered to 1.22 — go.mod now requires Go 1.22.0 (down from 1.25.0) and golang.org/x/tools is 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.Load failures and digen init now print actionable 💡 Fix: guidance; digen check exits non-zero when any package is invalid.

♻️ Internals

  • Cache keys switched from content hashes to file size + mtime; cmd/digenv1 marked // Deprecated:; digen explain error strings normalized (clears staticcheck ST1005).

v1.0.21

Choose a tag to compare

@shanjunmei shanjunmei released this 25 Aug 04:11

[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 — buildIIFECall now parses the closure signature instead of string-cutting ClosureDef.
  • Generator error-handling chain fixed — formatParams / writeMainFunc now propagate errors correctly instead of swallowing them.
  • New closure-capture examples example/closure_capture_exported and example/closure_capture_timehour, plus regression tests.
  • Docs site left-nav (TOC) follows language switches in real time — applyLang now 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 -cache docs — 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 -cache is actually slower for the common "edit then digen ./..." flow and only pays off for large DI graphs or repeated read-only subcommands.

♻️ Internals

  • Closure-extraction code restructured — internal/extractor/closure.go split into closure.go / closure_analysis.go / closure_emission.go; generation semantics unchanged.

v1.0.20

Choose a tag to compare

@shanjunmei shanjunmei released this 21 Aug 04:36

[v1.0.20] - 2026-08-21

🔧 Changed

  • Identity-closure collapse is now always applied, independent of -inline — closures such as func(p *Impl) Iface { return p }, func(p *T) T { return *p }, func(p T) *T { return &p }, direct type conversion func(p T) U { return U(p) }, and type assertion func(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 -inline switch as IIFE inlining; now it is unconditional.
  • -inline now controls IIFE inlining only (default false). It no longer affects identity closures. If you relied on -inline=false to 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

Choose a tag to compare

@shanjunmei shanjunmei released this 18 Aug 02:48

[v1.0.19] - 2026-08-18

digen CLI subcommands + generation-time contract pre-check

✨ New Features

  • Five new digen subcommands (reuse the generation pipeline, accept all existing flags):
    • digen init — scaffold a di.go with the dig.Build entry point
    • digen check — validate DI contracts without writing any file (lightweight static check)
    • digen graph — print the provider dependency graph as Mermaid
    • digen explain <type> — explain how a type / provider is resolved
    • digen completion <shell> — emit bash / zsh / fish completion scripts
    • digen -h and the completion scripts now list every flag and subcommand.

🔧 Improved Diagnostics

  • Generation-time contract pre-check: ensures di.go holds wiring only. If any wiring references a package-level symbol defined inside a //go:build digen file, 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.Supply value 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 digen file and referenced by dig.Provide / Invoke / Supply or 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 digen tag, or reference it from an imported package.
  • Run digen check for 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

Choose a tag to compare

@shanjunmei shanjunmei released this 15 Aug 14:23

✨ Generation-time hardening & diagnostics

  • Providers may no longer declare a context.Context parameter
    Added checkProviderContextParams: if a provider (constructor registered via Provide, a closure, or dig.Module) declares a context.Context parameter, digen now rejects it at generation time with a file:line:col location and a 💡 Fix:, aborting without writing a broken file.
    Reason: providers are resolved eagerly inside InitApp, before the runtime context.Context exists, so the parameter would be undefined in the generated code; context injection is only valid inside dig.Invoke (which runs in the inner func(ctx)). Added regression example example/gen_failures/provider_ctx/.

  • //go:build digen is now validated at generation time
    Added checkBuildSourceConstraint: if a source file containing a dig.Build call lacks the //go:build digen constraint, digen errors at the BuildFinalNodes stage with a 💡 Fix:, aborting generation (no file written). Reason: digen hard-codes //go:build !digen on the generated file, so without the matching tag on the source, a normal go build compiles both files and fails with InitApp redeclared. This promotes the long-standing "iron rule" from a doc convention into an invariant actually enforced by the generator.

  • Post-generation go/types safety net
    internal/generator/generator.go adds typeCheckGenerated, run after format.Source and before os.WriteFile. Because the user's source is already type-checked during loading (packages.Load with NeedTypes and -tags=digen, while dig_gen.go is 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.Supply of a module parameter no longer emits an undefined cross-package reference
    Fixed a generator bug where dig.Supply(x) whose value x is a function parameter or local variable of an inlined dig.Module was package-qualified (e.g. user.cfg), triggering undefined: <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 like db.Index and role.Config("production"). The fix also prevents the inlined supply from pulling an unused import for the source package. Added regression example example/supply_param/ (+ example/supply_param_helper/), auto-discovered by example/successtest.

  • AST-based type-name rewriting replaces regex rewriting
    Retired the fragile replaceTypeNames regex 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 precise go/ast + astutil.Apply rewrite: first collect a Pos -> "alias.Name" rewrite plan keyed by token.Pos (explicitly skipping SelectorExpr.Sel), then apply it on a clone, touching only matched identifiers — never string literals, comments, or same-named locals. The replacePkgPathWithAlias path→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.Node is now formalized as a serializable, schema-versioned stable IR: internal/model gains CachedExtraction (Nodes + ImportAliasMap/PkgAliasMap/PkgNameMap + SchemaVersion) and Node / Arg gain JSON tags; UnmarshalJSON errors on a SchemaVer mismatch instead of silently misreading. A new internal/ir package handles disk read/write (JSON by default, atomic write via temp file + rename), enabled by the cmd/digen -cache (default off) / -cachedir flags. 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")
    The checkFunctionVisibilityInClosure added in v1.0.17 walks every bare-identifier call (fn(args) / T(x)) in a closure body before generation and runs checkGenerationVisibility on the call's function identifier. But checkGenerationVisibility also 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 case user.Module(cfg), where the closure lives in user but the target is cmd/app), curPkg != mainPkgPath and 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 exactly var "f" is private.
    Fix (internal/extractor/visibility.go, commit 81e2a78): the *types.Var branch of checkGenerationVisibility now returns early with if !isPackageLevelVar(o) { return nil }, validating only genuine package-level variables; the *types.TypeName case is guarded by the same isPackageLevelVar reasoning. 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 beyond private_visibility / closure_private_fn.

📦 Examples and Documentation Updates

  • Automated regression tests for gen_failures/
    Added example/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 — so provider_ctx and other failure fixtures are caught by CI. Also added the //go:build digen tag (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 digen requirement is now actually enforced by digen at generation time.

v1.0.17

Choose a tag to compare

@shanjunmei shanjunmei released this 13 Aug 09:11

🐛 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 illegal pkg.buildAuditAuthorizer cross-package reference. Because digen does 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 uses checkGenerationVisibility to reject unexported cross-package symbols (same-package, exported, and builtin symbols are allowed), producing a clear private error and no broken file
    • Extended checkGenerationVisibility's switch with a *types.TypeName case to also cover unexported cross-package types in type conversions T(x)
    • Added regression example example/gen_failures/closure_private_fn/
  • Fix parameter shadowing in example/db/db.go RedisClient.Ping
    Ping(index RedisDbIndex) used the package-level variable Index instead of the index parameter in its fmt.Printf, making the parameter effectively unused. Now correctly references the index parameter.

  • Fix duplicate-binding error message for default Supply
    When two default (unnamed) dig.Supply calls provide the same type, the error message incorrectly showed with name "" instead of (default). Root cause: in the unnamed case keyNamed == keyDefault, so the named-key check matched first. Refactored into if instanceName != "" { ... } else { ... } branches so the default-instance error correctly shows (default).

♻️ Refactor and Optimisation

  • Generator config and sample adjustment
    Regenerated all example dig_gen.go files with the unified dv variable prefix, inline inlining enabled by default, and debug logging removed from generated code.
  • unused_provider example reworked
    example/gen_failures/unused_provider now registers its provider via the dig.Provide + dig.Supply pattern.

📦 Examples and Documentation Updates

  • New success examples
    example/app_runtime_err (runtime error-propagation paths: panic for provider errors, propagation for invoke errors) and example/app_xpkg_generic (cross-package generic cache.Cache[*common.Config] where both the generic type and the type argument come from different packages).
  • New failure examples
    Under example/gen_failures/ added ambiguous, 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
    Added docs/ static site (project intro, core features, quick start, API overview, comparison matrix, examples, version timeline) plus a .nojekyll file.
  • 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 under prompts/.

🔧 Chores

  • Bumped version references in README / CHANGELOG / system prompts to v1.0.17.

v1.0.16

Choose a tag to compare

@shanjunmei shanjunmei released this 07 Aug 07:11

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 default drop (discard) to error (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 variable Db to db to avoid naming conflicts with the imported db package 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 generated dig_gen.go files, making troubleshooting easier.


📦 Examples and Documentation Updates

  • Update Redis DB Index configuration example
    In the example module, added a new RedisDbIndex type and a default variable, updated the Ping method to receive and log the db index, and injected the db index via DI.

  • Update all generated dig_gen.go files
    Synchronised the regenerated dig_gen.go files across all example modules.


🔧 Chores

  • Updated dependency versions and change logs
  • Removed the debug-aliases flag 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.16

v1.0.15 - 2026-08-03

Choose a tag to compare

@shanjunmei shanjunmei released this 03 Aug 08:54

Fixed

  • Type package collection logic (core robustness fix):
    • collectUsedPkgsFromType: *types.Named branch incorrectly walked TypeParams() (declaration-level type params like T any, which have no real package path); now walks TypeArgs() (instantiation type args like *common.Config) while retaining TypeParams() 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 by collectTypeNameAndUsedPkgs AST traversal
    • Added *types.Signature branch: function/method signature types (e.g. a provider returning func(*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-path func(*github.com/.../common.Config) error instead of an alias, breaking compilation. Now walks Recv/Params/Results/TypeParams constraints, relying on seen for deduplication
    • addPkgToUsed changed from "typePkg single-value top-level package" to directly reuse collectUsedPkgsFromType full tree walk; no longer misses cross-package references inside Map key/value, slice elements, or generic type arguments
    • allTypes loop in generateClosureDef changed similarly: closure params / free vars / return types no longer rely on typePkg for only the top-level package; instead all cross-package references are tree-collected and EnsureAlias-ed individually

Removed

  • Standalone -debug-aliases diagnostic flag: Alias mapping diagnostics are now unified under the global -debug log (prints [alias] prefixed alias info automatically when -debug is on); no separate flag is provided anymore
  • findExcludedPackagesInClosure helper function: The BFS transitive import closure naturally excludes "other main packages" and "other library packages containing dig.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.Module per function restriction: findSingleModuleCall → findAllModuleCalls; helper functions may now contain multiple dig.Module calls whose args are merged; Module calls inside control flow (if/switch/for/select) remain unsupported
  • Removed gen_failures/multi_module and gen_failures/multi_module_call regression 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/setup cleanup: Removed the redundant digen build tag from full.go and deleted stub.go (a placeholder only used in non-digen builds)