Mago 1.15.0
Mago 1.15.0 brings three new linter rules, configurable minimum-fail-level in TOML files, automatic watch mode restarts on config changes, PGO-optimized Linux x86 builds, and a wave of analyzer and codex bug fixes improving generics, intersection types, and type inference accuracy.
✨ Features
Analyzer
- PSL
Psl\Type\nullish()return type provider: Added type narrowing support forPsl\Type\nullish(), complementing existing PSL type providers (#1390, #1391) - PSL
Psl\Async\all()andPsl\Async\concurrently()return type providers: Both functions now preserve sealed array shapes, list structure, and non-empty status. For example,Psl\Async\all(['foo' => $awaitableString, 'bar' => $awaitableInt])correctly returnsarray{foo: string, bar: int}(#1423)
Linter
- New
no-alternative-syntaxrule: Detects alternative control structure syntax (if/endif,while/endwhile, etc.) and suggests using brace-based syntax instead (#1313) - New
no-short-bool-castrule: Flags!!$exprdouble-negation casts and suggests using(bool) $exprfor clarity (#1312) - New
prefer-pre-incrementrule: Suggests++$iover$i++and--$iover$i--when the return value is unused, as pre-increment avoids an unnecessary copy (#1311) - Added fixer for
no-alias-functionrule: Theno-alias-functionrule now supports automatic fixing, replacing aliased PHP functions with their canonical equivalents (#1297)
CLI
minimum-fail-levelconfiguration option: Theminimum-fail-levelsetting can now be configured inmago.tomlunder[analyzer],[linter], and[guard]sections, removing the need to pass--minimum-fail-levelon every invocation. The CLI flag still overrides the config value (#1343, #1384)- Auto-restart watch mode on config changes:
mago analyze --watchnow monitors the configuration file, baseline file,composer.json, andcomposer.lockfor changes and automatically restarts the analysis session when they are modified (#1402)
🐛 Bug Fixes
Analyzer
- Fixed false positive
ambiguous-object-method-accessaftermethod_existsnarrowing: Whenmethod_exists($this, 'foo')adds aHasMethodintersection type, calling other methods on$thisno longer falsely reports ambiguous access (#1413, #1426) - Fixed properties not resolved from object shapes in intersection types: Accessing properties on intersection types like
stdClass&object{tags: list<Tag>}now correctly resolves the property from the shaped object part (#1387, #1421) - Fixed false positive for
unimplemented-abstract-property-hookwith traits: Concrete properties from used traits now correctly satisfy interface abstract property hooks (#1415, #1420) - Fixed
$this/staticreturn type not enforced for non-final classes: Returningnew self()from a method declared as@return $thisor: staticnow correctly reports a type mismatch in non-final classes. Anonymous classes are treated as effectively final (#1410, #1411, #1418) - Fixed stale dynamic property cache across loops: Dynamic property types are no longer incorrectly cached between loop iterations (#1416, #1417)
- Fixed parent class template parameter resolution via
@extends: Template parameters inherited through@extendsannotations are now correctly resolved when accessing shaped array keys (#1412, #1414) - Fixed negative numeric string increment/decrement sign: The sign is now preserved when incrementing or decrementing negative numeric strings (#1404)
- Fixed integer literals exceeding
i64::MAXtreated as int instead of float: Very large integer literals are now correctly inferred asfloat(#1405)
Codex
- Fixed empty array literals incorrectly marked as
non_empty: Empty array[]used as a default parameter value is now correctly typed asnon_empty=false, fixing false positivedocblock-type-mismatcherrors when using generic classes with default empty arrays (#1422, #1425) - Fixed modulo type range inference: The modulo operator now correctly infers the result type range (#1403)
- Fixed shift amount bounds checking in constant inference: Shift operations with out-of-range amounts no longer panic (#1400)
- Fixed list identity comparison using wrong part: Fixed an incorrect comparison in list type identity checks (#1396)
Linter
- Fixed
variable-namerule for underscore-prefixed variables: Variables starting with_(e.g.,$_unused) are no longer flagged by thevariable-namenaming rule (#1395, #1398) - Fixed memory leak in linter rule registry: Replaced
Box::leakwith owned storage for rule descriptions (#1408)
Formatter
- Fixed parentheses added unnecessarily around mixed multiplicative operators: Parentheses are now only added when actually needed for operator precedence, not for same-precedence multiplicative operations (#1407)
- Fixed backslash handling in quote selection: The formatter now correctly handles backslashes when choosing between single and double quotes (#1401)
Type Syntax
- Fixed overflowing integers in shape keys: Shape keys with integer values exceeding
i64::MAXare now rejected instead of silently overflowing (#1406)
Syntax Core
- Fixed string escaping edge cases: Resolved edge cases in string escape sequence handling (#1394)
Atom
- Fixed uppercase range in SIMD case folding: Corrected the
[A-Z]range used in SIMD-accelerated case folding (#1393)
Database
- Fixed file read after deletion: The database watcher no longer attempts to read files that have been deleted (#1397)
📖 Documentation
- Fixed exit code and sort order documentation (#1409)
- Updated analyzer, linter, and guard configuration references with
minimum-fail-leveloption - Added watch mode documentation with automatic restart behavior
🏗️ Internal
- Enabled PGO (Profile-Guided Optimization) for Linux x86_64 GNU and musl builds (#1399)
- Added pointer-equality fast path in atomic type comparator (#1236)
- Re-generated linter documentation for new rules (#1424)
🙏 Thank You
Contributors
A huge thank you to everyone who contributed code to this release:
- @edsrzf — #1297
- @npo-mmenke — #1236
- @ostark — #1311, #1312, #1313
- @veewee — #1391
- @xHeaven — #1393, #1394, #1396, #1397, #1400, #1401, #1403, #1404, #1405, #1406, #1407, #1408, #1409, #1414, #1417
Issue Reporters
Thank you to everyone who reported issues that shaped this release:
- @bendavies — #1385
- @ddanielou — #1415
- @dotdash — #1410, #1411
- @karoun — #1343, #1395
- @mytskine — #1416
- @peterjaap — #1386
- @veewee — #1390
- @zip-fa — #1387, #1388, #1389, #1413, #1422
- @Zuruuh — #1392
Full Changelog: 1.14.1...1.15.0