Mago 1.24.0
Mago 1.24.0 is mostly an analyzer-correctness release: recursive literal-scalar widening in by-reference out-types, wider mixed-operand detection on templated concatenations, and targeted fixes around isset / array_key_exists, list destructuring, iterable narrowing, and __call on non-final classes. It also adds a prefer-self-return-type linter rule with FQCN-to-use autofix, match-arm => alignment in the formatter, and a service memory fix that takes mago lint --fix --unsafe on Drupal core from ~11.6 GB RSS down to 748 MB.
New: mago-twig-syntax. A lossless Twig 3 lexer, AST, and parser published to crates.io. Not wired into the Mago binary yet: the plan is Twig support across the linter, formatter, and possibly the analyzer (#303), then Blade (#304). Twig is the testing ground for multi-language support; once that plumbing is confident, adding Blade should be much cheaper.
✨ Features
Linter
prefer-self-return-type: new rule suggestingselfinstead of the declaring class name in method return types. (#1442, 9081ce8)- FQCN autofix with
use-statement synthesis:no-fully-qualified-global-*fixers now insert a matchinguseat the top of the file. (#555, #1645, d16e16c) string-stylehandles single-quoted strings: fixer now converts'foo'to"foo"when the chosen style requires it. (#1674, b33a57b)
Formatter
- Align assignment-like match arms: column-align
=>across arms in match expressions, matching assignment alignment. (#1642, 34e0277)
Codex
- Detect
class_exists-guarded stubs as polyfills: user definitions are preferred over guarded fallbacks on merge. (0748989)
Prelude
IteratoronGenerator: stub now reflects thatGeneratorimplementsIterator. (e115682)ArrayAccessonDOMNodeList: stub reflects the interface exposed by the extension. (7efae17)
🐛 Bug Fixes
Analyzer
- Recursive literal-scalar widening in by-reference out-types: widens through arrays, iterables, generics, and conditionals. (#1686, 1575f1e)
- Baseline literal-scalar widening in by-reference out-types: initial fix for single-level literal tightening on
param-out. (#1678, 45926da) - Reject
mixedand union-shaped templates as concat operands:T as mixedand mixed-scalar unions now surfacemixed-operand. (a55b321) - Namespaced global-fallback calls pass function-casing check:
\Foo\bar()no longer misfires on global fallback. (#1680, f61f57d) - Reject incompatible parameter defaults; gate implicit-null on PHP < 9.0: catches value/default-type mismatches. (#1682, f04aefb)
- Narrow
isset/array_key_existsto false on provably missing keys: fixes inconsistent impossible-condition warnings. (#1679, 434f11e) - Skip
no-boolean-literal-comparisonon synthetic match-arm binaries: fixer no longer produces invalid code for match arms. (#1681, 67ea13e) - Only hard-error on missing
__callwhen the class-like is final: non-final classes keep PHPUnit-style magic method calls working. (#1676, 6953e8e) - Respect list siblings when reporting undefined array keys: fixes wrong
undefined-string-array-indexon list destructuring. (#1668, 2f248e8) - Don't corrupt locals when array-index narrowing drops every variant: fixes false
undefined-string-array-indexpositives. (#1669, 90f9c5d) - Preserve type parameters when narrowing iterable/array: generic arguments survive through iterable narrowing. (f815bcc)
Codex
- Disjoint literal class-string and string in identity check: fixes an incorrect subtype relation. (#1677, 0fed110)
- Hold alias-expansion guard across recursive calls: prevents stack overflow on deeply aliased type graphs. (58afef6)
- Prioritize user polyfills over builtins and external definitions: user-written
class_exists-guarded stubs win. (c3d7eb2) - Narrow
iterable<mixed, V>key toarray-keyagainst an array container: fixes over-wide key propagation. (ed712ba)
Syntax
- Reject
&reference prefix outside legal positions: catches previously-accepted syntax errors. (#1683, 53800a6)
Formatter
- Avoid inlining nested single-element array-like arguments: fixes regression where nested
[[$x]]got collapsed wrong. (#1672, #1673, de78a1f)
CLI
- Handle spaces in binary path: Composer wrapper and self-invocation no longer drop arguments on paths containing spaces. (#1471, 38baca6)
Service
- Reset arena between
apply_fixesbatches: bounds memory during--fix --unsafe; Drupal-core run peak goes from ~11.6 GB RSS to 748 MB. (#1670, 8b551dc)
🏗️ Internal
Twig
- Normalize path separators in fixture exclusion check: fixes a Windows CI failure in
mago-twig-syntaxtests. (ec890a3) - Exclude twig-syntax test fixtures from typos check: removes CI noise from vendored upstream Twig content. (a0eb189)
Syntax
- Remove
OptionfromLiteralString::kind: every literal string now carries a concrete kind. (#1675, 2fb1377)
Linter
- Drop
constant_fqn_keyin favor ofascii_lowercase_constant_name_atom: removes a dead helper. (067fa34) - Drop stagger logic from text-edit; fix edit-order invariants: simplifies the batch fixer. (363e231)
🙏 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:
- @UweOhse: #1668, #1669, #1678, #1679, #1681, #1682, #1683, #1686
- @BafS: #1680
- @theofidry: #1676
- @klausi: #1670
- @zip-fa: #1442
- @Brammm: #1645
- @norberttech: #555
Full Changelog: 1.23.1...1.24.0