Mago is a toolchain for PHP that aims to provide a set of tools to help developers write better code.
This report summarizes scanner evidence for carthage-software/mago, a Rust project with 3,541 GitHub stars. SynthScan examined 706,854 lines of code across 6042 source files, recording 1214 pattern matches distributed across 7 syntactic categories. The legacy adjusted pattern signal is 1.8 (Low pattern signal); it is not an authorship estimate.
Use the findings to ask whether maintainers can explain intent, verify dependencies, simplify unnecessary scaffolding, and focus review effort. Risk Payload v1 adds ownership, supply-chain, over-engineering, and review-effort vectors when available. These are prompts for human verification, not probabilities, security findings, or measured maintenance costs.
Use each vector to guide manual verification of the underlying findings.
Payload schema: 1.0.0 · Engine: 2.0.0 · Scoring: heuristic-density-v1 · Scan date: 2026-10-31.
Can maintainers explain intent, assumptions, and trade-offs? 1203 finding(s).
Are imports declared and package/API names verified? 11 finding(s).
Are wrappers, guards, and repeated structures necessary? 460 finding(s).
Which changes need focused human review? 1214 finding(s).
This chart tracks the legacy adjusted pattern score across scan runs. Changes can reflect source changes, scan coverage, or detector/scoring versions; they do not measure AI adoption or prove that a team refactored code. Compare runs only when their scoring versions are compatible. The dashed line tracks the raw pattern-hit count.
Groups findings by configured severity weight. Severity is not a confidence estimate, security rating, or proof that a package is missing. Verify imports against the project's manifests and inspect each finding in its local design context.
This horizontal bar chart shows where configured pattern scores accumulate across directories. Treat the bars as locations to inspect, not as evidence of authorship or code quality. Use the file-level findings to decide whether a directory needs a focused ownership, dependency, complexity, or review check.
The scanner identified 1214 distinct pattern matches across 7 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a detector match to inspect in context. Expand any category row to inspect the individual file paths, line numbers, code snippets, and the lexical context (CODE, COMMENT, or STRING) in which each match was detected.
Reading the findings table: The Severity column indicates the configured finding weight (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — legacy comment-context matches receive a ×1.5 score weight. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier under the legacy scoring model.
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | crates/database/src/error.rs | 21 | /// An attempt was made to commit or consume a `ChangeLog` while other | COMMENT |
| LOW | crates/database/src/matcher.rs | 1 | //! Path exclusion matching with mixed glob + prefix semantics. | COMMENT |
| LOW | crates/database/src/matcher.rs | 121 | COMMENT | |
| LOW | crates/database/src/lib.rs | 1 | #![allow(clippy::exhaustive_enums)] | COMMENT |
| LOW | crates/database/src/lib.rs | 21 | //! - **Change Tracking**: Record and batch apply file modifications via [`ChangeLog`] | COMMENT |
| LOW | crates/database/src/lib.rs | 41 | //! | COMMENT |
| LOW | crates/database/src/lib.rs | 121 | #[derive(Debug, Clone, Copy)] | COMMENT |
| LOW | crates/database/src/lib.rs | 341 | /// # Arguments | COMMENT |
| LOW | crates/database/src/lib.rs | 421 | files: Vec::new(), | COMMENT |
| LOW | crates/database/src/lib.rs | 461 | .filter_map(|(index, file)| file.path.as_ref().map(|path| (path.clone(), index))) | COMMENT |
| LOW | crates/database/src/lib.rs | 481 | fn get(&self, id: &FileId) -> Result<Arc<File>, DatabaseError>; | COMMENT |
| LOW | crates/database/src/change.rs | 61 | /// Records a request to update an existing file's content by its `FileId`. | COMMENT |
| LOW | crates/database/src/change.rs | 101 | } | COMMENT |
| LOW | crates/database/src/watcher.rs | 241 | self.patch_base_paths.clear(); | COMMENT |
| LOW | crates/database/src/watcher.rs | 361 | if changed_files.is_empty() { None } else { Some(changed_files) } | COMMENT |
| LOW | crates/database/src/loader.rs | 101 | pub fn add_memory_source(&mut self, name: &'static str, contents: &'static str, file_type: FileType) { | COMMENT |
| LOW | crates/database/src/exclusion.rs | 1 | //! File and directory exclusion patterns for database loading. | COMMENT |
| LOW | crates/database/src/exclusion.rs | 21 | COMMENT | |
| LOW | crates/database/src/exclusion.rs | 41 | /// | COMMENT |
| LOW | crates/database/src/exclusion.rs | 61 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] | COMMENT |
| LOW | crates/database/src/utils.rs | 41 | #[inline] | COMMENT |
| LOW | crates/database/src/file.rs | 21 | /// Distinguishes between the origins of source files. | COMMENT |
| LOW | crates/database/src/file.rs | 41 | /// PHPDoc flags for vendored or built-in code. | COMMENT |
| LOW | crates/database/src/file.rs | 101 | let id = FileId::new(&name); | COMMENT |
| LOW | crates/database/src/file.rs | 121 | /// | COMMENT |
| LOW | crates/database/src/file.rs | 141 | /// # Parameters | COMMENT |
| LOW | crates/database/src/file.rs | 161 | /// | COMMENT |
| LOW | crates/database/src/file.rs | 181 | match self.lines.get(line as usize + 1) { | COMMENT |
| LOW | crates/reporting/src/reporter.rs | 41 | /// | COMMENT |
| LOW | crates/reporting/src/reporter.rs | 101 | pub fn new(database: ReadDatabase, config: ReporterConfig) -> Self { | COMMENT |
| LOW | crates/reporting/src/reporter.rs | 201 | /// # Errors | COMMENT |
| LOW | crates/reporting/src/error.rs | 1 | //! Error types for the reporting crate. | COMMENT |
| LOW | crates/reporting/src/color.rs | 1 | /// Choice for colorizing output. | COMMENT |
| LOW | crates/reporting/src/lib.rs | 1 | #![allow(clippy::pub_use, clippy::exhaustive_enums)] | COMMENT |
| LOW | crates/reporting/src/lib.rs | 61 | /// `{ pattern = "Symfony", code = "mixed-assignment" }`. | COMMENT |
| LOW | crates/reporting/src/lib.rs | 161 | } | COMMENT |
| LOW | crates/reporting/src/lib.rs | 381 | pub fn len(&self) -> usize { | COMMENT |
| LOW | crates/reporting/src/lib.rs | 401 | /// let annotation = Annotation::new(AnnotationKind::Primary, span); | COMMENT |
| LOW | crates/reporting/src/lib.rs | 421 | /// let span = Span::new(file, start, end); | COMMENT |
| LOW | crates/reporting/src/lib.rs | 441 | /// let end = Position::new(5); | COMMENT |
| LOW | crates/reporting/src/lib.rs | 481 | /// Downgrades the level to the next lower severity. | COMMENT |
| LOW | crates/reporting/src/lib.rs | 541 | /// | COMMENT |
| LOW | crates/reporting/src/lib.rs | 561 | /// ``` | COMMENT |
| LOW | crates/reporting/src/lib.rs | 581 | /// # Examples | COMMENT |
| LOW | crates/reporting/src/lib.rs | 601 | #[must_use] | COMMENT |
| LOW | crates/reporting/src/lib.rs | 681 | /// # Examples | COMMENT |
| LOW | crates/reporting/src/baseline.rs | 1 | //! Baseline functionality for filtering known issues. | COMMENT |
| LOW | crates/reporting/src/baseline.rs | 41 | } | COMMENT |
| LOW | crates/reporting/src/baseline.rs | 81 | /// File paths are stored in a normalized format (using forward slashes) | COMMENT |
| LOW | crates/reporting/src/output.rs | 1 | use std::io; | COMMENT |
| LOW | crates/reporting/src/formatter/mod.rs | 41 | /// Example: `"phpstorm://open?file=%file%&line=%line%"` | COMMENT |
| LOW | crates/reporting/src/formatter/mod.rs | 81 | /// GitHub Actions format. | COMMENT |
| LOW | crates/reporting/src/formatter/utils.rs | 201 | /// Returns a borrowing iterator over the issues that pass the formatter's | COMMENT |
| LOW | crates/twig-syntax/tests/fixture_runner.rs | 1 | #![allow(clippy::unwrap_used, clippy::expect_used, clippy::else_if_without_else)] | COMMENT |
| LOW | crates/twig-syntax/src/macros.rs | 1 | //! `T![...]` - ergonomic matching against [`TwigTokenKind`](crate::token::TwigTokenKind). | COMMENT |
| LOW | crates/twig-syntax/src/settings.rs | 1 | /// Settings for the Twig lexer. | COMMENT |
| LOW | crates/twig-syntax/src/cst/mod.rs | 1 | //! CST types for Twig templates. | COMMENT |
| LOW | crates/twig-syntax/src/cst/trivia.rs | 1 | use strum::Display; | COMMENT |
| LOW | crates/twig-syntax/src/cst/statement/mod.rs | 61 | /// level: raw template text, `{{ expr }}` prints, `{% verbatim %}` blocks, | COMMENT |
| LOW | crates/twig-syntax/src/lexer/mod.rs | 1 | //! Stateful lexer for Twig templates. | COMMENT |
| 668 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | …es/linter/src/rule/safety/no_service_state_mutation.rs | 832 | public function getAndIncrement(): int | CODE |
| LOW | …es/linter/src/rule/safety/no_service_state_mutation.rs | 854 | public function getOrSet(int $default): int | CODE |
| LOW | …ency_arrow_fn_returns_keyed_array_with_call/before.php | 5 | protected function getOriginalWithoutRewindingModel($key = null, $default = null) | CODE |
| LOW | …tency_arrow_fn_returns_keyed_array_with_call/after.php | 5 | protected function getOriginalWithoutRewindingModel($key = null, $default = null) | CODE |
| LOW | …ses/idempotent_chain_with_complex_arguments/before.php | 43 | public function testChainWithExpandedArray() | CODE |
| LOW | …ases/idempotent_chain_with_complex_arguments/after.php | 42 | public function testChainWithExpandedArray() | CODE |
| LOW | …mpotency_corpus_apiplatform_schema_property/before.php | 557 | private function addNullabilityToTypeDefinition(array $jsonSchema, LegacyType $type): array | CODE |
| LOW | …empotency_corpus_apiplatform_schema_property/after.php | 643 | private function addNullabilityToTypeDefinition(array $jsonSchema, LegacyType $type): array | CODE |
| LOW | …ates/formatter/tests/cases/issue_428_part_3/before.php | 6 | private function initializeResponseFactory(): ResponseFactoryInterface | CODE |
| LOW | crates/formatter/tests/cases/issue_428_part_3/after.php | 5 | private function initializeResponseFactory(): ResponseFactoryInterface | CODE |
| LOW | crates/formatter/tests/cases/match_breaking/before.php | 5 | private function getStubFileFromConfigType(ConfigType $configType): StubFile | CODE |
| LOW | crates/formatter/tests/cases/match_breaking/after.php | 5 | private function getStubFileFromConfigType(ConfigType $configType): StubFile | CODE |
| LOW | …ests/cases/idempotency_corpus_carbon_period/before.php | 2508 | protected function incrementCurrentDateUntilValid(): void | CODE |
| LOW | …tests/cases/idempotency_corpus_carbon_period/after.php | 2526 | protected function incrementCurrentDateUntilValid(): void | CODE |
| LOW | crates/formatter/tests/cases/issue_1806/before.php | 18 | public function veryLongFunctionSignature(int $param1, int $param2, int $param3, int $param4): void | CODE |
| LOW | crates/formatter/tests/cases/issue_1806/after.php | 19 | public function veryLongFunctionSignature( | CODE |
| LOW | crates/formatter/tests/cases/psl_regressions/before.php | 27 | public function testTypedThrowsWhenUnableToCoerce(): void | CODE |
| LOW | crates/formatter/tests/cases/psl_regressions/after.php | 30 | public function testTypedThrowsWhenUnableToCoerce(): void | CODE |
| LOW⚡ | crates/formatter/tests/cases/break_fn_args/before.php | 36 | function superLoooooooooooooooooooooooooooooooooooooooooooooooooooooongFuuuuuuuuuuuuuuuctioooooooooooooonNameeeeeeeeeeee | CODE |
| LOW⚡ | crates/formatter/tests/cases/break_fn_args/before.php | 44 | function superLoooooooooooooooooooooooooooooooooooooooooooooooooooooongFuuuuuuuuuuuuuuuctioooooooooooooonNameeeeeeeeeeee | CODE |
| LOW⚡ | crates/formatter/tests/cases/break_fn_args/before.php | 48 | function superLoooooooooooooooooooooooooooooooooooooooooooooooooooooongFuuuuuuuuuuuuuuuctioooooooooooooonNameeeeeeeeeeee | CODE |
| LOW⚡ | crates/formatter/tests/cases/break_fn_args/before.php | 53 | function superLoooooooooooooooooooooooooooooooooooooooooooooooooooooongFuuuuuuuuuuuuuuuctioooooooooooooonNameeeeeeeeeeee | CODE |
| LOW⚡ | crates/formatter/tests/cases/break_fn_args/before.php | 67 | function superLoooooooooooooooooooooooooooooooooooooooooooooooooooooongFuuuuuuuuuuuuuuuctioooooooooooooonNameeeeeeee | CODE |
| LOW⚡ | crates/formatter/tests/cases/break_fn_args/before.php | 76 | function superLoooooooooooooooooooooooooooooooooooooooooooooooooooooongFuuuuuuuuuuuuuuuctioooooooooooooonNameeeeeeee | CODE |
| LOW⚡ | crates/formatter/tests/cases/break_fn_args/before.php | 83 | function superLoooooooooooooooooooooooooooooooooooooooooooooooooooooongFuuuuuuuuuuuuuuuctioooooooooooooonNameeeeeeee | CODE |
| LOW⚡ | crates/formatter/tests/cases/break_fn_args/before.php | 90 | function superLoooooooooooooooooooooooooooooooooooooooooooooooooooooongFuuuuuuuuuuuuuuuctioooooooooooooonNameeeeeeee | CODE |
| LOW⚡ | crates/formatter/tests/cases/break_fn_args/after.php | 42 | function superLoooooooooooooooooooooooooooooooooooooooooooooooooooooongFuuuuuuuuuuuuuuuctioooooooooooooonNameeeeeeeeeeee | CODE |
| LOW⚡ | crates/formatter/tests/cases/break_fn_args/after.php | 50 | function superLoooooooooooooooooooooooooooooooooooooooooooooooooooooongFuuuuuuuuuuuuuuuctioooooooooooooonNameeeeeeeeeeee | CODE |
| LOW⚡ | crates/formatter/tests/cases/break_fn_args/after.php | 56 | function superLoooooooooooooooooooooooooooooooooooooooooooooooooooooongFuuuuuuuuuuuuuuuctioooooooooooooonNameeeeeeeeeeee | CODE |
| LOW⚡ | crates/formatter/tests/cases/break_fn_args/after.php | 62 | function superLoooooooooooooooooooooooooooooooooooooooooooooooooooooongFuuuuuuuuuuuuuuuctioooooooooooooonNameeeeeeeeeeee | CODE |
| LOW⚡ | crates/formatter/tests/cases/break_fn_args/after.php | 74 | public function superLoooooooooooooooooooooooooooooooooooooooooooooooooooooongFuuuuuuuuuuuuuuuctioooooooooooooonName | CODE |
| LOW⚡ | crates/formatter/tests/cases/break_fn_args/after.php | 82 | public function superLoooooooooooooooooooooooooooooooooooooooooooooooooooooongFuuuuuuuuuuuuuuuctioooooooooooooonName | CODE |
| LOW⚡ | crates/formatter/tests/cases/break_fn_args/after.php | 88 | public function superLoooooooooooooooooooooooooooooooooooooooooooooooooooooongFuuuuuuuuuuuuuuuctioooooooooooooonName | CODE |
| LOW⚡ | crates/formatter/tests/cases/break_fn_args/after.php | 94 | public function superLoooooooooooooooooooooooooooooooooooooooooooooooooooooongFuuuuuuuuuuuuuuuctioooooooooooooonName | CODE |
| LOW | crates/formatter/tests/cases/issue_1153/before.php | 8 | private function createSingleCursorPagination( // @phpstan-ignore-line | CODE |
| LOW | crates/formatter/tests/cases/issue_1153/after.php | 10 | private function createSingleCursorPagination( // @phpstan-ignore-line | CODE |
| LOW | …idempotency_corpus_apiplatform_type_factory/before.php | 185 | private function addNullabilityToTypeDefinition(array $jsonSchema, LegacyType|NativeType $type): array | CODE |
| LOW | …/idempotency_corpus_apiplatform_type_factory/after.php | 188 | private function addNullabilityToTypeDefinition(array $jsonSchema, LegacyType|NativeType $type): array | CODE |
| LOW⚡ | crates/prelude/assets/extensions/zip.php | 327 | public function setExternalAttributesName(string $name, int $opsys, int $attr, int $flags = null): bool {} | CODE |
| LOW⚡ | crates/prelude/assets/extensions/zip.php | 329 | public function getExternalAttributesName(string $name, int &$opsys, int &$attr, int $flags = null): bool {} | CODE |
| LOW⚡ | crates/prelude/assets/extensions/zip.php | 331 | public function setExternalAttributesIndex(int $index, int $opsys, int $attr, int $flags = null): bool {} | CODE |
| LOW⚡ | crates/prelude/assets/extensions/zip.php | 333 | public function getExternalAttributesIndex(int $index, int &$opsys, int &$attr, int $flags = null): bool {} | CODE |
| LOW⚡ | crates/prelude/assets/extensions/zip.php | 335 | public static function isEncryptionMethodSupported(int $method, bool $enc = true): bool {} | CODE |
| LOW⚡ | crates/prelude/assets/extensions/zip.php | 337 | public static function isCompressionMethodSupported(int $method, bool $enc = true): bool {} | CODE |
| LOW | crates/prelude/assets/extensions/aerospike.php | 1003 | public static function predicateGeoContainsGeoJSONPoint($bin, $point) {} | CODE |
| LOW | crates/prelude/assets/extensions/aerospike.php | 1014 | public static function predicateGeoContainsPoint($bin, $long, $lat) {} | CODE |
| LOW | crates/prelude/assets/extensions/aerospike.php | 1024 | public static function predicateGeoWithinGeoJSONRegion($bin, $region) {} | CODE |
| LOW | crates/prelude/assets/extensions/rdkafka.php | 744 | public function oauthbearerSetTokenFailure(string $error): void {} | CODE |
| LOW | crates/prelude/assets/extensions/rdkafka.php | 821 | public function setOauthbearerTokenRefreshCb(callable $callback): void {} | CODE |
| LOW | crates/prelude/assets/extensions/rdkafka.php | 1066 | public function oauthbearerSetTokenFailure(string $error): void {} | CODE |
| LOW | crates/prelude/assets/extensions/oauth.php | 291 | public function callTimestampNonceHandler() {} | CODE |
| LOW | crates/prelude/assets/extensions/reflection.php | 638 | public function setRawValueWithoutLazyInitialization(object $object, mixed $value): void {} | CODE |
| LOW | crates/prelude/assets/extensions/reflection.php | 820 | public function getDefaultValueConstantName(): ?string {} | CODE |
| LOW | crates/prelude/assets/extensions/reflection.php | 947 | public function getNumberOfRequiredParameters(): int {} | CODE |
| LOW | crates/prelude/assets/extensions/reflection.php | 1439 | public function newInstanceWithoutConstructor(): object {} | CODE |
| LOW | crates/prelude/assets/extensions/reflection.php | 1567 | public function isUninitializedLazyObject(object $object): bool {} | CODE |
| LOW | crates/prelude/assets/extensions/reflection.php | 1573 | public function markLazyObjectAsInitialized(object $object): object {} | CODE |
| LOW | crates/prelude/assets/extensions/intl.php | 870 | public static function getKeywordValuesForLocale( | CODE |
| LOW | crates/prelude/assets/extensions/intl.php | 898 | public function getMinimalDaysInFirstWeek(): int|false {} | CODE |
| LOW | crates/prelude/assets/extensions/intl.php | 912 | public function getRepeatedWallTimeOption(): int {} | CODE |
| 400 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | crates/fingerprint/src/argument.rs | 144 | "placeholder".hash(hasher); | CODE |
| LOW | …ter/src/rule/correctness/no_missing_format_argument.rs | 149 | if parsed.placeholder_count == 1 { "placeholder" } else { "placeholders" }, | CODE |
| LOW | …/formatter/tests/cases/symfony_form_builder/before.php | 53 | 'placeholder' => 'No document', | CODE |
| LOW | …s/formatter/tests/cases/symfony_form_builder/after.php | 59 | 'placeholder' => 'No document', | CODE |
| LOW | crates/formatter/tests/cases/issue_1149/before.php | 6 | 'placeholder', | CODE |
| LOW | crates/formatter/tests/cases/issue_1149/after.php | 5 | 'placeholder', | CODE |
| LOW | crates/analyzer/tests/cases/mutually_exclusive_null.php | 64 | $corp = createPersonEntity(companyName: 'Acme Inc'); | CODE |
| LOW | …s/analyzer/tests/cases/complex_template_constraint.php | 62 | 'name' => 'John Doe', | CODE |
| LOW | crates/analyzer/src/utils/php_emulation.rs | 279 | assert_eq!(str_increment("user@example.com"), None); | CODE |
| LOW | crates/analyzer/src/expression/instantiation.rs | 701 | $collection = new Collection(['name' => 'John Doe']); | CODE |
| LOW | crates/analyzer/src/expression/assignment/mod.rs | 1490 | 'name' => 'John Doe', | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | crates/analyzer/src/invocation/post_process.rs | 1484 | // Step 1: Ensure we have both the name and offset for the parameter. | COMMENT |
| LOW | crates/analyzer/src/invocation/post_process.rs | 1504 | // Step 2: Resolve the argument with the correct precedence. | COMMENT |
| LOW | …/src/reconciler/simple_negated_assertion_reconciler.rs | 1635 | // Step 4: Convert remaining combinations to TList objects | COMMENT |
| LOW⚡ | src/commands/init.rs | 621 | // Step 1: Plugin detection/selection | COMMENT |
| LOW⚡ | src/commands/init.rs | 624 | // Step 2: Strictness preset selection | COMMENT |
| LOW | src/commands/init.rs | 645 | // Step 3: Optional customization | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | crates/analyzer/src/invocation/post_process.rs | 1484 | // Step 1: Ensure we have both the name and offset for the parameter. | COMMENT |
| LOW | crates/analyzer/src/invocation/post_process.rs | 1504 | // Step 2: Resolve the argument with the correct precedence. | COMMENT |
| LOW | …/src/reconciler/simple_negated_assertion_reconciler.rs | 1635 | // Step 4: Convert remaining combinations to TList objects | COMMENT |
| LOW⚡ | src/commands/init.rs | 621 | // Step 1: Plugin detection/selection | COMMENT |
| LOW⚡ | src/commands/init.rs | 624 | // Step 2: Strictness preset selection | COMMENT |
| LOW | src/commands/init.rs | 645 | // Step 3: Optional customization | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | crates/codex/src/ttype/atomic/reference/mod.rs | 80 | /// Kept original name `type_params` as requested for fields. | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | crates/analyzer/src/context/mod.rs | 151 | /// accounting for PHP's nuanced resolution rules: | COMMENT |
| MEDIUM | crates/analyzer/src/expression/call/function_call.rs | 603 | main_list_can_be_empty([]); // Definitely empty | CODE |