github carthage-software/mago 1.23.0
Mago 1.23.0

latest release: 1.23.1
11 hours ago

Mago 1.23.0

Mago 1.23.0 adds three new CLI surface-area features and fixes two rough edges. The analyzer no longer reports spurious template-constraint violations when a constraint uses wildcard class-constant references like self::*_REFERENCE. A new --substitute ORIG=TEMP flag across mago analyze, mago lint, and mago guard lets mutation-testing frameworks swap a host file with a mutated copy for a single invocation without touching anything on disk. mago init now offers a "minimal diff" opt-in that flips every preserve-* formatter toggle on for users who want to keep their existing line breaks intact. The Composer wrapper forwards GITHUB_TOKEN / GH_TOKEN when downloading the pre-built binary, unblocking CI runners and shared networks that hit anonymous rate limits. And the braced-string-interpolation autofix no longer bails on adjacent interpolations like "$comma$y".

✨ Features

CLI

  • --substitute ORIG=TEMP for file-content substitution: New shared flag on mago analyze, mago lint, and mago guard that replaces one host file in the project with another file for a single invocation, without modifying anything on disk. Both paths must be absolute, the flag can be repeated, and under the hood TEMP is appended to the host paths while ORIG is added to the excludes for that run, so the rest of the project is still scanned and cross-file inference continues to see the replacement. Primarily designed for mutation-testing frameworks (such as Infection) that generate a mutated copy of a source file and want the analyzer or linter to evaluate it against the rest of the project without writing the mutation to its original location. Conflicts with --stdin-input, --staged, and --watch where they apply. (infection/infection#3046)

Init

  • Minimal-diff formatter opt-in: mago init now asks "Opt into the smallest possible diff when formatting?" at the end of the Formatter Configuration step. Answering yes writes every preserve-breaking-* option (member-access chains, argument lists, parameter lists, attribute lists, conditional expressions, condition expressions, array-likes, and preserve-redundant-logical-binary-expression-parentheses) as true into the generated mago.toml, so existing line breaks are kept intact. Defaults to no; let Mago decide unless you have a strong preference. (#825)

Composer wrapper

  • Forward GITHUB_TOKEN / GH_TOKEN when downloading the binary: The Composer package is a thin wrapper that downloads the matching pre-built binary from the GitHub release on first invocation. On shared CI runners and networks that share an egress IP with many consumers, GitHub's anonymous rate limit can reject that download. The wrapper now mirrors what mago self-update already does: it reads GITHUB_TOKEN then GH_TOKEN from the environment and sends the first non-empty value as Authorization: Bearer <token> on the download request. The curl path uses CURLOPT_HTTPHEADER (and relies on curl stripping the Authorization header on the cross-origin redirect to objects.githubusercontent.com); the file_get_contents fallback sets the header through a stream_context_create HTTP context. Both paths now also send a User-Agent: mago-composer/<version> header. No token is required when rate limits aren't an issue; the wrapper still works anonymously by default. (#1665)

🐛 Bug Fixes

Analyzer

  • Expand template constraints before inferring bounds: Template constraints containing class-constant references (most commonly self::*_REFERENCE patterns used to restrict an integer template to a set of *_REFERENCE class constants) were compared against inferred bounds in their raw, unexpanded form. Because int(0) is not contained by a TReference::Member::EndsWith("_REFERENCE") node, the containment check failed and produced a spurious "template constraint violation" with unknown-ref(…::*_REFERENCE) as the expected type, which also leaked through the deferred-violations path. Constraints are now expanded via expand_union (with the correct self_class derived from the template's defining entity) at every containment site in infer_templates_from_input_and_container_types: the object type-parameter path, the bare generic parameter path, and the template_result.template_types iteration. (#902, #858)

Linter

  • braced-string-interpolation autofix merges adjacent }{ edits: For a composite string like "$comma$y", the autofix previously emitted a closing } at the end offset of $comma and an opening { at the start offset of $y. Those offsets are identical when two interpolated expressions sit flush against each other, so the fixer's overlap detector treated the two inserts as conflicting edits and skipped the fix entirely with a Overlapping edit for … warning. The fix now detects adjacency in the collected expression list and emits a single }{ insert at the shared boundary. Same shape applied to $o[a]$o[b] and similar back-to-back array-access interpolations. (#1667)

🙏 Thank You

Issue Reporters

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

Full Changelog: 1.22.0...1.23.0

Don't miss a new mago release

NewReleases is sending notifications on new releases.