Releases: carthage-software/mago
Mago 1.5.0
Mago 1.5.0
This release brings PSR-11 container support, custom help text for disallowed functions, and several important bug fixes for type narrowing and linter false positives.
✨ Features
Analyzer
-
PSR-11 container support: New
psr-containerplugin that infers types fromclass-stringarguments when callingContainerInterface::get(). This eliminatesmixed-method-accessandmixed-argumenterrors when using PSR-11 service containers (#1015) by @Noojuno[analyzer] plugins = ["psr-container"]
Linter
-
Custom help text for disallowed functions: The
disallowed-functionsrule now supports custom help messages per function or extension. Entries can be simple strings or objects withnameand optionalhelpfields (#1024)[linter.rules] disallowed-functions = { functions = [ "eval", { name = "error_log", help = "Use MyLogger instead." }, ], extensions = [ "curl", { name = "ffi", help = "FFI is disabled for security reasons." }, ], }
🐛 Bug Fixes
Analyzer
-
Nullsafe operator type narrowing: Fixed incorrect type narrowing in the false branch of if statements using nullsafe operators (
?->). Previously, the variable was incorrectly narrowed tonullin the else branch. Nowif ($user?->isAuthorized())correctly preserves the original type in the else branch, matching the semantics of$user !== null && $user->isAuthorized()(#1025) -
Redundant instanceof detection: Fixed false positive for undefined variables when a variable is assigned in exhaustive if/elseif branches over a union type. The analyzer now correctly detects redundant
instanceofchecks and tracks variable assignments across all branches (#1026)
Linter
- False positive in
prefer-first-class-callable: Skip suggesting first-class callable syntax for runtime-dependent call targets where conversion would change evaluation semantics. This includes method chains (adminUrlGenerator()->generateUrl()), nullsafe calls ($obj?->method()), and dynamic method names ($obj->$method()) (#1027, #1020) by @kzmshx
🏗️ Internal
- Bump
bytesfrom 1.11.0 to 1.11.1 (#1023) by @dependabot - Documentation updates and optimizations
🙏 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 were fixed in this release:
Full Changelog: 1.4.1...1.5.0
Mago 1.4.1
Mago 1.4.1
Patch release with two bug fixes.
🐛 Bug Fixes
Syntax
- Void cast parsing: Fix parsing of
(void)cast expressions (#1021)
Analyzer
JSON_THROW_ON_ERRORdetection: DetectJSON_THROW_ON_ERRORflag when combined with other flags using bitwise OR (e.g.,JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT) (#1022, #886) by @kzmshx
🙏 Thank You
Full Changelog: 1.4.0...1.4.1
Mago 1.4.0
This release brings significant improvements including method call assertions, a new parser configuration section, fault-tolerant parsing, new linter fixers, and numerous bug fixes addressing false positives in the analyzer. A massive thank you to everyone who contributed!
✨ Features
Analyzer
- Method call assertions: Support for
@assert-if-trueand@assert-if-falsedocblock assertions on method calls, enabling type narrowing based on method return values (#763) - Unreachable else detection: Detect unreachable else clauses when all enum cases are already handled in if-elseif chains (#356)
Linter
- Fixer for
no-redundant-string-concat: Automatically fix redundant string concatenations (#1018) by @dotdash - Fixer for
no-trailing-space: Automatically remove trailing whitespace (#1017) by @dotdash - Fixer for
braced-string-interpolation: Automatically add braces around interpolated variables (#1013) by @dotdash - Configurable early-continue threshold: New
max-statements-before-early-continueoption to control when theearly-continuerule recommends refactoring (#979, #975) by @dotdash
Parser
- New
[parser]configuration section: Configure parser-level settings including the ability to disable short opening tag recognition viaenable-short-tags = false(#841) - Fault-tolerant parsing: Major parser rewrite introducing fault-tolerant parsing, a foundational step towards LSP support. The parser now recovers from syntax errors and continues analysis, providing better diagnostics for files with minor issues (#988) by @azjezz
CLI
FORCE_COLORenvironment variable: Force colored output even when piping to files or other commands, taking precedence overNO_COLOR(#1012)
⚡ Performance
- Parser rewrite: Complete rewrite of the parser resulting in ~3x faster parsing and ~2x faster type parsing (#988) by @azjezz
- MiMalloc allocator: Use MiMalloc on GNU targets for improved memory allocation performance (#999) by @dotdash
- Atom optimizations: Improve performance of
ascii_lowercase_atomfor common cases
🐛 Bug Fixes
Analyzer
- False positive with references in closures: Skip undefined variable errors for reference captures in closure use clauses (#1003, #982) by @kzmshx
- False positive with intersection types: Resolve properties on intersection types from instanceof checks (#996)
- False positive in intersection type ordering: Flatten nested intersection types during scanning to resolve method lookup (#1002, #1004)
- False positive with asymmetric visibility: Allow inheritance of properties with
private(set)in child classes (#985) - False positive with nullsafe chains: Clear nullsafe_null flag when null is removed during type narrowing (#998)
- False positive with redundant comparisons in loops: Suppress redundant comparison warnings inside loops where values change (#1006)
- False positive with dynamic array access: Mark list access with non-literal index as possibly undefined (#1005)
- False positive with empty() on optional keys: Narrow possibly-undefined array keys after
!empty()check (#973) - False positive with array building in loops: Convert never-typed array elements to empty arrays inside loops (#976, #968)
$_SERVERarray shape typo: Fixarvctypo toargcin$_SERVERarray shape (#990, #972) by @kzmshx
Formatter
- Comments not moving with sorted methods: When
sort-class-methodsis enabled, comments now move with their associated methods instead of stacking on the first method (#994) - Preset override bug: User-specified values that match framework defaults are no longer incorrectly overwritten by preset values (#1010)
- Pint preset accuracy: Set
space-around-assignment-in-declaretofalsein Pint preset to match actual Pint behavior (#1011, #974) by @kzmshx
Linter
- False positive with aliased imports: Handle aliases correctly in same-namespace import redundancy check (#989, #983) by @kzmshx
- False positive with reference captures: Skip
inline-variable-returnwhen closure has reference capture (#997, #981) by @kzmshx
Docblock
- UTF-8 boundary panic: Fix panic on multi-byte UTF-8 whitespace in docblock parsing (#971, #967) by @kzmshx
Prelude (Type Stubs)
- Array pointer functions: Improve signatures of
end,prev,next,resetwith@param-outannotations to preserve array type after by-reference calls (#980, #984) array_unshiftsignature: Preserve list type in@param-outannotation (#970)
Codex
- Constant references in defaults: Resolve constant references in function default parameter values, fixing issues with functions like
pathinfo()where the second argument is optional (#969) - Internal interfaces: Hide internal interfaces from public API (#1000)
🏗️ Internal
- Update
.gitattributesto exclude more files from export (#1008) by @shyim - Add
conststo typos dictionary
🙏 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 were fixed in this release:
- @bendavies — #356, #981, #982, #983
- @Bleksak — #984, #1000, #1002, #1004, #1006
- @bronek89 — #998, #1005
- @ddanielou — #969, #972
- @Dima-369 — #968, #973, #977
- @dotdash — #975
- @dynasource — #980
- @innocenzi — #841
- @klunejko — #994
- @KorvinSzanto — #985, #996
- @kzmshx — #1010
- @Nadyita — #970
- @norberttech — #763
- @scop — #1012
- @valpuia — #974
Full Changelog: 1.3.0...1.4.0
Mago 1.3.0
Mago 1.3.0
This release comes with significant performance improvements, bug fixes addressing false positives, and new formatter options. A massive thank you to everyone who reported issues and contributed!
✨ Features
Formatter
always-next-linebrace style: Newalways-next-linevalue for brace style options (method-brace-style,function-brace-style,closure-brace-style,classlike-brace-style,control-brace-style) that always places the opening brace on the next line, even for multiline signatures (#897)- Drupal preset: Add Drupal coding standards preset (#905) by @klausi
⚡ Performance
This release includes substantial performance optimizations:
- Reduced lock contention: Optimize empty atom handling to reduce lock contention (#956) by @dotdash
- Faster lexer/parser: Reduce allocations in lexer and parser for faster parsing
- Optimized clause lookups: Use
AtomSetinstead ofVecfor clause key lookups - Template type improvements: Remove template_types clone and use
HashMapfor bounds - Algebra optimizations: Use index-based tracking to avoid expensive clause cloning
- Faster context cloning: Use
AtomMapandControlActionSetbitfield for faster block context cloning - Type combination thresholds: Add literal type combination thresholds to prevent O(n²) complexity (#939, #940)
- HashMap for constants: Use
HashMapinstead ofIndexMapfor constants and enum_cases where ordering is not required - Deterministic method resolution: Use
IndexMapforoverridden_method_idsto ensure deterministic ordering (#907)
🐛 Bug Fixes
Analyzer
- Static return type resolution: Resolve
staticreturn types correctly in method chains across inheritance (#880, #949, #964) - Docblock inheritance: Prevent incorrect docblock inheritance when child class narrows return type (#960, #962)
- Unused method false positives: Skip trait-overriding methods in unused method check (#945)
- Unused property false positives: Skip non-private overridden properties in unused property check (#941, #943)
- Type narrowing for mixed arrays: Preserve type narrowing for array elements when base type is
mixed(#946, #947) - Nested isset narrowing: Handle nested array access in
isset()type narrowing (#900) - defined()/function_exists() assertions: Propagate assertions through
&&operator (#912) - Anonymous class constructors: Validate anonymous class constructor arguments (#958)
- Callable parameter inference: Make callable parameter type inference order-independent
- Never to string cast: Allow casting
neverto string - Combiner thresholds: Use user-configured combiner thresholds instead of defaults
Formatter
- Pint preset accuracy: Update Pint preset to match actual Pint formatting (#920)
CLI
- NO_COLOR support: Respect
NO_COLORenvironment variable and--colors neverflag globally across all output (#922) - Baseline fix filtering: Filter out baseline issues before applying fixes (#910)
Docblock
- By-reference parameters: Allow by-reference parameter syntax (
&$param) in@paramtags (#955)
Prelude (Type Stubs)
- sprintf family: Allow
nullandboolvalues insprintf,printf,sscanf, andfscanffunctions (#953) - $_FILES superglobal: Mark
$_FILESsuperglobal as potentially empty (#954)
🏗️ Internal
- Migrate template types from
VectoIndexMapwithGenericTemplatestruct for cleaner code - Replace block context boolean flags with
u32bitflags - Skip path separator normalization on non-Windows platforms
- Drop support for powerpc* and s390x tier 2 targets
- Update toolchain and dependencies
🙏 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 were fixed in this release:
- @bendavies — #955, #958, #960, #962
- @Bleksak — #880, #957
- @ddanielou — #953, #954
- @demonege — #910
- @Dima-369 — #912, #939, #940
- @dotdash — #946, #947
- @dragosprotung — #941, #945
- @dynasource — #943
- @kzmshx — #967
- @llaville — #922
- @MartkCz — #897
- @mytskine — #949
- @Nadyita — #964
- @Noojuno — #907
- @oleg-st — #900
- @poisa — #920
Full Changelog: 1.2.2...1.3.0
Mago 1.2.2
Patch release to fix binary builds broken by upstream dependency.
🐛 Bug Fixes
- Fix PGO build command syntax: Update cargo pgo optimize command to match new CLI syntax required by cargo-pgo v0.2.10 (Kobzol/cargo-pgo#80)
📝 Notes
The 1.2.1 release binaries failed to build due to a breaking change in cargo-pgo v0.2.10 (released January 24, 2026) which changed how arguments are passed to the optimize command. This release contains no code changes-only a CI workflow fix.
Full Changelog: 1.2.1...1.2.2
Mago 1.2.1
🙈 Oops
Turns out releasing at 4 AM after a break isn't the best idea. Sorry about that!
🐛 Bug Fix
- Static method closure types: Fixed incorrect return type inference for first-class callables on inherited static methods (e.g.,
Y::create(...)now correctly returnsYinstead of the parent classX)
Full Changelog: 1.2.0...1.2.1
Mago 1.2.0
First of all, I want to apologize for the delayed release — I've been on a break the past couple of weeks. Thank you all for your patience! 🙏
This release brings significant improvements to the analyzer, including unused code detection, better type inference, and numerous bug fixes addressing false positives reported by the community.
✨ Features
Analyzer
- Unused code detection: Mago now detects unused private methods and properties (#867, #929)
- Write-only property detection: Detect private properties that are written to but never read
- Undefined type reference errors: Report errors when referencing classes, interfaces, enums, or type aliases that don't exist (#891)
- Symbol namespace tracking: Keep track of symbol namespaces for improved import validation
Linter
no-issetrule: New rule to prevent usage of theissetconstruct (#924) by @djschilling- Redundant import detection: Detect redundant same-namespace and root-namespace imports in
no-redundant-userule (#921) - Auto-fixers for
lowercase-type-hintandlowercase-keyword: New fixers for these linter rules (#911) by @dotdash
Configuration
- Distribution config files: Add support for loading
mago.dist.tomlandmago.dist.jsonfiles (#903) by @Bleksak
Formatter
- Inline abstract property hooks: New
inline-abstract-property-hookssetting for PER-CS 4.10 compliant property hook formatting (#919)
🐛 Bug Fixes
Analyzer
- Nullsafe operator chain fix: Track nullsafe-origin null to prevent false positives in chained access (#931)
- Partial application symbol tracking: Track symbol references from closure creation (
$foo->bar(...)) and partial function application ($foo->bar(?, ?)) to prevent false positive unused method errors - Namespace imports: Allow imports of symbol namespaces (#933)
- Magic method
parent::calls: Treatparent::calls as instance calls for magic methods (#916) #[Override]for@method: Don't suggest#[Override]attribute for@methodpseudo-methods (#914)- Null coalesce with
neverRHS: Suppress false positive for possibly-undefined array index in null coalesce withneverRHS (#923) - String concatenation memory usage: Add threshold to stop exponential memory usage with complex string concatenations (#908, #909) by @Noojuno
- Inherited docblock narrowing: Narrow inherited docblock return types based on child's native type
Prelude (Type Stubs)
DatePeriodtype parameters: SpecifyIteratorAggregatetype parameters forDatePeriod(#932)strposoffset type:strpossupports negative offsets, replaceint<0, max>withint(#890) by @Bleksak
Formatter
- Property assignment alignment: Align property assignments with variable assignments (#926)
Linter
- CLI flags in
no-insecure-comparison: Ignore CLI flags like--passwordin the rule (#917)
CLI
NO_COLORenvironment variable: RespectNO_COLORenv variable (#922)
Documentation
🏗️ Internal
- Update Nix flake to Rust 1.91.1 (#927) by @Zuruuh
- Simplify
FormatterPresetconfig deserialization (#879) by @magic-akari
🙏 Thank You
Contributors
A huge thank you to everyone who contributed to this release:
Issue Reporters
Thank you to everyone who reported issues that were fixed in this release:
- @bendavies — #891
- @BenMorel — #919
- @Bleksak — #933, #932, #930, #929
- @Dima-369 — #932
- @djschilling — #836
- @dotdash — #931, #923
- @guvra — #867
- @lemmon — #921, #917
- @llaville — #922
- @Noojuno — #909
- @oleg-st — #889
- @ulrichsg — #916, #914
- @Valentin-Gaudin — #926
🚀 What's Next
More improvements and features coming soon! Stay tuned.
Full Changelog: 1.1.0...1.2.0
Mago 1.1.0
Mago 1.1.0
This release includes a significant number of new features, bug fixes, and improvements across the formatter, analyzer, linter, and codex.
Note: This release includes a breaking change in the formatter's default behavior for heredoc indentation.
Breaking Changes
Formatter
- Heredoc content is now indented by default. To restore the previous behavior, set
indent-heredoctofalsein your configuration.
Features
Formatter
- Added formatter presets support for quick configuration (
psr-12,per-cs,drupal, etc.) (#839) - Added
following-clause-on-newlineoption to placeelse,catch, andfinallyon a new line (#860) - Added
uppercase-literal-keywordoption for uppercaseTRUE,FALSE,NULLliterals (Drupal style) (#857) - Added
empty-line-before-class-like-closesetting for empty line before closing brace (#855) - Added
newline-after-class-like-opening-bracesetting (#853)
Analyzer
- Added support for
@mixindocblock annotations - Added docblock type mismatch detection for function parameters and return types
Linter
- Added Pest-specific lint rules for better Pest PHP testing framework support
- Added auto-fix for the
explicit-nullable-paramlint (#847)
Guard
- Added mode selection and ability to skip unconfigured guards
Bug Fixes
Formatter
- Fixed kebab-case alias support for settings values
- Fixed indentation in arrays when expression spans multiple lines (#864)
Analyzer
- Fixed method signature validation to compare against direct interface instead of grandparent interface (#871)
- Fixed type assertion propagation through null coalesce expressions (
$x['foo'] ?? null) - Fixed
invalid-extendreporting when a readonly class extends a non-readonly class (#873) - Fixed
staticand$thistype handling and expansion in template contexts - Fixed recursive template constraint inference
- Fixed
array_filtertype narrowing to support first-class callables and string literals - Fixed readonly property reinitialization in
clonefor PHP 8.3+ - Fixed incorrect type narrowing propagation in OR conditions
Codex
- Fixed type alias resolution in member reference expansion (e.g.,
self::TypeAliasin@extends) - Fixed parameter type inheritance when method has partial docblock
- Fixed asymmetric visibility handling for hooked properties
- Fixed native return type metadata population
Orchestrator
- Fixed analysis progress bar to use 4-byte emoji for better terminal compatibility (#866)
Internal
- Improved analyzer onboarding experience in CLI
- Added Pest and Tempest to the
initflow - Added support for
non-empty-mixedtype syntax - Added filename fallback for Tempest view detection in linter
New Contributors
- @Michael4d45 made their first contribution in #839
- @klausi made their first contribution in #853
Full Changelog: 1.0.3...1.1.0
Mago 1.0.3
This release includes several bug fixes across the formatter, linter, analyzer, and prelude, along with new features.
As always, we'd love to hear from you! Please keep filing bug reports and feature requests - your feedback is what drives Mago forward.
Our team will be taking a well-deserved break over the holidays. Merry Christmas to those who celebrate, and happy holidays to everyone! We'll be back in full swing in the new year, but rest assured we'll still be keeping an eye out for any critical issues.
Bug Fixes
Formatter
- Fix idempotency issue with match expressions containing trailing comments on conditions
- Fix nullable union type formatting (e.g.,
null|arrayno longer causes formatting oscillation) - Fix call arguments breaking when containing wide binary expressions
- Avoid early return inside
wrap!macro to prevent node stack corruption
Linter
- Avoid overlapping edits between
prefer-arrow-functionandprefer-static-closurerules - Fix
prefer-anonymous-migrationrule to correctly flag named migration classes - Strip leading backslash from use statement FQNs in
no-redundant-userule
Analyzer
- Handle multiple docblocks before statements correctly (#818)
- Detect
Closure::bindscope changes for protected member access analysis
Text Edit
- Detect overlapping edits when insert and replace operations share the same start offset
Prelude
- Mark
getimagesizefromstringsecond parameter as optional
Features
Linter
- Add new
no-assign-in-argumentrule to detect assignments in call arguments (#821)
Analyzer
- Detect
Closure::bindscope changes, allowing accurate analysis of protected/private member access within rebound closures
Internal
- Refactor formatter to use
if letto fixunnecessary_unwrapwarning (#781)
New Contributors
- @chrisopperwall-qz made their first contribution in #821
Full Changelog: 1.0.2...1.0.3
Mago 1.0.2
Mago 1.0.2
Bug Fixes
Formatter
- Fixed idempotent formatting issues with comments inside binary operations (#796)
- Fixed idempotent formatting for comments inside nested parenthesized binary expressions (#812)
- Fixed idempotent formatting for trailing comments on if/elseif blocks (#813)
- Changed
inline-empty-classlike-bracesdefault totrueper PER-CS specification - Relaxed binary inlining rules for improved formatting
Analyzer
- Fixed dependent template constraint resolution in
@extendsvalidation - Fixed docblock array shapes support for closure parameters
array_mapnow correctly returnsnon-empty-arraywhen given anon-empty-array(#815)
Docblock
- Fixed panic on multi-byte UTF-8 whitespace in docblock tags
Prelude
- Corrected
current()return type
Features
- Docblock variables are now always populated (#802)
Configuration Changes
- PSR naming rules are now disabled by default (#805)
Documentation
- Fixed documentation:
null-pipeis not a valid value fornull-type-hintsetting (#814)
Release Assets
- Added
source-code.tar.gzandsource-code.ziparchives containing full source code - Removed Debian packages from release assets
New Contributors
- @tuqqu made their first contribution in #805
- @MidnightDesign made their first contribution in #814
Full Changelog: 1.0.1...1.0.2