github carthage-software/mago 1.26.0
Mago 1.26.0

6 hours ago

Mago 1.26.0

A feature release with two big analyzer additions: closures, arrow functions, plain functions, and methods now auto-infer if-true / if-false assertions from their bodies, so array_filter($xs, fn ($x) => $x instanceof Foo) narrows the result type without any docblock, and two new opt-in settings (strict-array-index-existence, allow-array-truthy-operand) tighten array-ops semantics. The linter gains prefer-array-spread and a deny mode for yoda-conditions. The formatter picks up inline-single-breaking-value-argument (used in the Tempest preset). On the CLI side, mago init now writes a $schema URL pinned to the running version into the generated config, and the published JSON schema is now versioned. The rest is a large batch of analyzer false-positive fixes and a --retain-code-respecting --fix dispatcher.

✨ Features

Analyzer

  • Auto-inferred assertions: closures, arrow fns, functions, methods infer if_true / if_false from their body. (#1794, 14269d6)
  • strict-array-index-existence: opt-in; tightens array indexing; deprecates allow-possibly-undefined-array-keys. (#1766, 0e563c9)
  • allow-array-truthy-operand: opt-in setting that suppresses invalid-operand when arrays are used in logical ops. (#1765, 07f6a24)

Linter

  • prefer-array-spread rule: detects array_merge(...) calls that can be rewritten as spread. (#1767, bc8d67a)
  • yoda-conditions deny mode: rule gains a deny mode that flags constants on the right of comparisons. (#1785, a4e47e5)

Formatter

  • inline-single-breaking-value-argument: keeps a lone breaking value argument inline; off by default, on in Tempest. (#1148, 8964575)

CLI

  • $schema in mago.toml: configs accept a $schema URL; mago init writes one pinned to the installed version. (#1771, e71e78b)
  • Versioned config schema: schema is now published per release at https://mago.carthage.software/<version>/schema.json. (f1e705d)

🐛 Bug Fixes

Analyzer

  • array_filter narrowing via callback: result type is now narrowed using the callback's inferred assertions. (#1794, 14269d6)
  • list-destructure-string-key false positive: suppressed when known items already cover the destructure positions. (#1796, b3b11c5)
  • Interface property reads possibly undefined: silences false isset / ?? redundancy on interface fields. (#1786, 9e873b0)
  • void materialization in unions: combining void with another type now produces T|null, not T. (#1787, 47fb42a)
  • redundant-type-comparison after impure @assert: suppressed for impure or value-returning assert calls. (#1781, #831, 53dad31)
  • ::class on undefined classes: now reports non-existent-class-like instead of silently typing as class-string. (#1768, 052e0cd)
  • unset($var) no longer fires possibly-undefined-variable: unset is a write target, not a read. (#1764, 3f0e7f8)
  • Unbounded loop boundary: for ($i = 0; ; $i++) no longer trips the boundary check. (#1762, c64199b)
  • Template parameter through new $class-string<T>(): preserves T instead of widening to the constraint. (#1760, 64f3df8)
  • Loose-equal falsy values on != <falsy>: drops false-positive narrowings that ignored PHP's loose comparison rules. (#1757, 6b45a66)
  • Assigned vars marked possibly-in-scope: removes spurious undefined-variable after conditional assignment. (#1759, 659a8d3)

Codex

  • Discriminated tagged-array unions through the combiner: preserves discriminator keys when unioning shapes. (#1775, 426f536)
  • Closure named-object cast: now routes through a closure-flagged signature instead of __invoke. (#1770, 068a7eb)

Linter

  • no-redundant-yield-from trailing-comma fix: auto-fix no longer leaves a stray , after the inlined element. (#1797, 4b49d01)
  • prefer-static-closure and parent::: closures whose body uses parent:: are no longer suggested as static. (#1783, cf2e944)
  • yoda-conditions and class constants: treats class constants as constant-like for the position check. (#1784, f945b75)

Formatter

  • Hardline after declaration-attached docblocks: forces a hardline after multi-line docblocks attached to declarations. (#1774, 2249d6a)

CLI

  • --retain-code honored before --fix: filter applied before fix dispatch, so fixes run only for retained codes. (#1782, 35f1a37)

Database

  • column_number at line start: fixes wrong column offsets in gitlab / github / checkstyle / sarif reports. (#1790, 856ceca)

Type-syntax / Docblock

  • By-reference parameters in callable type strings: Closure(int &$x): void parses correctly in docblocks. (#1772, be2d08f)

Prelude

  • exif_read_data signature: corrects parameter count and types. (#1758, c3fdb0f)

📖 Documentation

  • README links: fixes broken /guide/installation and /guide/getting-started links to point at /latest/en/.... (#1788, 3f29da7)
  • Rule examples stacked vertically: bad/good examples now render on top of each other. (#1773, 1892eff)
  • French translations polish: fixes calques, translation errors, untranslated code comments. (105a518)
  • Chinese translations polish: idiomatic phrasing and formal register pass across zh/**. (edd0fc3)
  • OG / Twitter previews: padded banner.png for cleaner social cards. (ddbb0f7)
  • Versioned dropdown: docs site renders the version dropdown from /versions.json at runtime. (66f406c)
  • Legacy bookmarks redirect: requests to old paths redirect to /latest. (4d1f370)

🏗️ Internal

Performance

  • Iterator-based argument / parameter accessors: replaces Vec-returning helpers; one-pass superglobal narrowing. (f43a5d1)

Refactor

  • MetadataFlags per file type: extracts the per-file-type metadata flag construction into a single helper. (#1769, 1fdb15f)

CI / Tooling

  • Stricter workspace clippy lints: tightens the lint baseline across crates. (#1761, 5de4859)
  • Run clippy on all targets: tests and benches included. (#1776, 82cd8ae)
  • Pinned Rust toolchains: CI now pins exact toolchain versions. (#1777, 72da50b)
  • Signed API commits in nightly workflow: nightly bot commits via the signed-API path. (#1789, e801af1)

🙏 Thank You

Contributors

A huge thank you to everyone who contributed code to this release:

Issue Reporters

Thank you to everyone who reported issues that shaped this release:

Full Changelog: 1.25.2...1.26.0

Don't miss a new mago release

NewReleases is sending notifications on new releases.