Mago 1.21.1
Mago 1.21.1 is a bug-fix release that cleans up five regressions introduced by the features shipped in 1.21.0. The string-style fixer no longer emits invalid PHP for chains that aren't rooted in a variable, the prefer-explode-over-preg-split fixer emits clean double-quoted escapes for separators containing control characters instead of splatting raw newlines or tabs into the source, the analyzer now expands generics like value-of<Enum> before comparing class-constant values and property defaults, the type parser accepts new as a class-constant name after :: despite the new new<T> type construct, and OSC 8 editor-URL hyperlinks on Windows drop the \\?\ verbatim prefix so terminal-to-editor links work again.
🐛 Bug Fixes
Linter
string-stylefixer only interpolates chains rooted in a variable: Fixed the concat-to-interpolation auto-fix for thestring-stylerule to correctly reject chains that aren't rooted in a$variable. Previously,"Hello " . SomeEnum::World->valuewas rewritten to"Hello {SomeEnum::World->value}"which is invalid PHP, since interpolation braces only accept chains rooted in a variable. The check now recurses through property, null-safe property, array, method, and null-safe method accesses, returningtrueonly when the chain's root is a variable (#1658)prefer-explode-over-preg-splitfixer emits proper escapes for control characters: When the extracted separator contains control bytes (\n,\t,\r,\x01, etc.), the fixer now emits a double-quoted PHP string with escape sequences instead of dropping raw control bytes into a single-quoted string. Sopreg_split("/\n\n/", $s)now becomesexplode("\n\n", $s)instead ofexplode('<LF><LF>', $s)which split the call onto multiple lines. Non-ASCII bytes still use single quotes since their UTF-8 encoding round-trips verbatim (#1655)
Analyzer
- Expand declared type before comparing constant values and property defaults: The
invalid-constant-valueandinvalid-property-default-valuechecks introduced in 1.21.0 did not expand derived types likevalue-of<Enum>before comparing against the initializer's type, producing false positives on patterns such as/** @var array<value-of<Color>, int> */ const WEIGHTS = [Color::Red->value => 1, ...]. The declared type is now expanded (self/static, generics, templates, conditionals, class constants) before the subtype check runs. The parameter-default path was already correctly expanding (#1657)
Type Syntax
- Allow
newas a member-name identifier after::: Thenew<T>type construct introduced in 1.21.0 madenewa reserved keyword in the type grammar, which in turn broke class-constant and enum-case references likeAction::NEWorFoo::new. The parser now treatsnewas an identifier in every post-::context (plain, qualified, and fully-qualified class references; plus thearray{Foo::NEW: int}shape-key pattern) when it isn't followed by<. Top-levelnew<T>still parses as theNewTypeconstruct unchanged (#1654)
Reporting
- Strip Win32 verbatim prefix from OSC 8 editor-URL hyperlinks: On Windows, file paths surfaced to the terminal through OSC 8 editor hyperlinks (
--editor-url) carried the\\?\verbatim prefix thatstd::fs::canonicalizeadds, which editors andfile://handlers don't accept. The prefix is now stripped before the path is templated into the URL —\\?\C:\…becomesC:\…, and\\?\UNC\server\share\…becomes\\server\share\…. Applies to both the rich and emacs formatters (#1659)
🙏 Thank You
Issue Reporters
Thank you to everyone who reported issues that shaped this release:
- @dragosprotung — #1654
- @UweOhse — #1655
- @ddanielou — #1657
- @nikophil — #1658
- @oruborus — #1659
Full Changelog: 1.21.0...1.21.1