github carthage-software/mago 1.15.0
Mago 1.15.0

latest releases: 1.15.2, 1.15.1
17 hours ago

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 for Psl\Type\nullish(), complementing existing PSL type providers (#1390, #1391)
  • PSL Psl\Async\all() and Psl\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 returns array{foo: string, bar: int} (#1423)

Linter

  • New no-alternative-syntax rule: Detects alternative control structure syntax (if/endif, while/endwhile, etc.) and suggests using brace-based syntax instead (#1313)
  • New no-short-bool-cast rule: Flags !!$expr double-negation casts and suggests using (bool) $expr for clarity (#1312)
  • New prefer-pre-increment rule: Suggests ++$i over $i++ and --$i over $i-- when the return value is unused, as pre-increment avoids an unnecessary copy (#1311)
  • Added fixer for no-alias-function rule: The no-alias-function rule now supports automatic fixing, replacing aliased PHP functions with their canonical equivalents (#1297)

CLI

  • minimum-fail-level configuration option: The minimum-fail-level setting can now be configured in mago.toml under [analyzer], [linter], and [guard] sections, removing the need to pass --minimum-fail-level on every invocation. The CLI flag still overrides the config value (#1343, #1384)
  • Auto-restart watch mode on config changes: mago analyze --watch now monitors the configuration file, baseline file, composer.json, and composer.lock for changes and automatically restarts the analysis session when they are modified (#1402)

🐛 Bug Fixes

Analyzer

  • Fixed false positive ambiguous-object-method-access after method_exists narrowing: When method_exists($this, 'foo') adds a HasMethod intersection type, calling other methods on $this no 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-hook with traits: Concrete properties from used traits now correctly satisfy interface abstract property hooks (#1415, #1420)
  • Fixed $this/static return type not enforced for non-final classes: Returning new self() from a method declared as @return $this or : static now 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 @extends annotations 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::MAX treated as int instead of float: Very large integer literals are now correctly inferred as float (#1405)

Codex

  • Fixed empty array literals incorrectly marked as non_empty: Empty array [] used as a default parameter value is now correctly typed as non_empty=false, fixing false positive docblock-type-mismatch errors 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-name rule for underscore-prefixed variables: Variables starting with _ (e.g., $_unused) are no longer flagged by the variable-name naming rule (#1395, #1398)
  • Fixed memory leak in linter rule registry: Replaced Box::leak with 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::MAX are 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-level option
  • 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:

Issue Reporters

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


Full Changelog: 1.14.1...1.15.0

Don't miss a new mago release

NewReleases is sending notifications on new releases.