Skip to content

Commit e683151

Browse files
committed
Address false-positive argument count errors on overloaded built-in
functions
1 parent c4cff65 commit e683151

4 files changed

Lines changed: 448 additions & 30 deletions

File tree

docs/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1616
- **Inherited methods missing through deep stub chains.** Methods like `getCode()` and `getMessage()` are now found on classes that inherit through multi-level chains where intermediate classes live in stubs (e.g. `QueryException``PDOException``RuntimeException``Exception`). Previously the inheritance chain broke when a stub file contained multiple namespace blocks, causing parent class names to be resolved against the wrong namespace.
1717
- **False-positive diagnostics for same-named variables in different methods.** When two methods in the same class both used a variable like `$order`, the diagnostic cache resolved it once and reused that type for every other method in the class. The second method saw the wrong type and flagged valid member accesses as unknown. Both the per-file subject cache and the cross-collector resolution cache are now scoped to the enclosing function/method/closure body, so each method resolves variables independently.
1818
- **False-positive diagnostics for `$this` inside traits.** Accessing host-class members via `$this->`, `self::`, `static::`, or `parent::` inside a trait method no longer produces "not found" warnings, including chain expressions like `static::where(...)->update(...)` and accesses inside closures or arrow functions nested within trait methods. Traits are incomplete by nature and expect the host class to provide these members.
19+
- **False-positive argument count errors on overloaded built-in functions.** Functions like `array_keys`, `mt_rand`, and `rand` accept multiple valid argument counts that phpstorm-stubs cannot express with a single declaration. An overload map derived from PHPStan's `functionMap.php` now provides the correct minimum argument count for 233 affected built-in functions.
1920
- **Type narrowing inside `return` statements.** `instanceof` checks in `&&` chains and ternary conditions now narrow the variable type when the expression is the operand of a `return` statement. Previously, `return $e instanceof QueryException && $e->errorInfo;` would flag `errorInfo` as unknown because narrowing only applied inside standalone expression statements and `if` conditions.
2021
- **CLI analyze performance (pathological files).** Single-file analysis of Eloquent-heavy services dropped from ~6 min to ~63 s (5.8× faster). The shared package (~2 500 files) dropped from 14 m 28 s to 1 m 25 s (10× faster, now ~30 % faster than PHPStan on the same machine). Key changes:
2122
- *Negative class cache.* `find_or_load_class` now caches "not found" results so repeated references to unknown types skip the four-phase lookup (fqn_index → classmap → PSR-4 → stubs). Invalidated when new classes are discovered.

docs/todo.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ unlikely to move the needle for most users.
102102
| --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------- | ----------- |
103103
| | **[Bugs](todo/bugs.md)** | | |
104104
| B4 | [Variable reassignment loses type when parameter name is reused](todo/bugs.md#b4-variable-reassignment-loses-type-when-parameter-name-is-reused) | Medium | Medium |
105-
| B7 | [Overloaded built-in function signatures in stubs](todo/bugs.md#b7-overloaded-built-in-function-signatures-in-stubs) | Low | Low |
105+
| B8 | [Stub parser does not handle `#[PhpStormStubsElementAvailable]` attributes](todo/bugs.md#b8-stub-parser-does-not-handle-phpstormstubselementavailable-attributes) | Low | Low |
106106
| | **[Completion](todo/completion.md)** | | |
107107
| C1 | Array functions needing new code paths | Medium | High |
108108
| C10 | [Lazy documentation via `completionItem/resolve`](todo/completion.md#c10-lazy-documentation-via-completionitemresolve) | Medium | Medium |

docs/todo/bugs.md

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -38,37 +38,37 @@ within the same scope at the cursor offset.
3838

3939
---
4040

41-
#### B7. Overloaded built-in function signatures not representable in stubs
41+
#### B8. Stub parser does not handle `#[PhpStormStubsElementAvailable]` attributes
4242

4343
| | |
4444
|---|---|
4545
| **Impact** | Low |
4646
| **Effort** | Low |
4747

48-
Some PHP built-in functions have genuinely overloaded signatures where
49-
the valid argument counts depend on which "form" is being called. The
50-
phpstorm-stubs format cannot express this: it declares a single
51-
signature, so one form's required parameters become false requirements
52-
for the other form.
53-
54-
Around 415 cases where parameters were simply missing their default
55-
values have been fixed upstream in phpstorm-stubs. The remaining cases
56-
are true overloads that the stub format cannot represent:
57-
58-
- `array_keys(array $array): array` vs
59-
`array_keys(array $array, mixed $filter_value, bool $strict = false): array`
60-
- `mt_rand(): int` vs `mt_rand(int $min, int $max): int`
61-
62-
PHPStan solves this with a separate function signature map
63-
(`functionMap.php`) that overrides stub signatures with corrected
64-
metadata including multiple accepted argument count ranges. PHPantom
65-
needs a similar mechanism.
66-
67-
**Observed:** 10 diagnostics in `shared` (8 `array_keys`, 2 `mt_rand`).
68-
69-
**Fix:** Maintain a small overload map (similar to PHPStan's
70-
`functionMap.php`) that declares alternative minimum argument counts
71-
for functions with true overloads. The argument count checker consults
72-
this map before flagging. The map only needs entries for functions
73-
where the stub's single signature cannot represent the valid call
74-
forms.
48+
The regex-based stub parser in `classmap_scanner.rs` does not strip
49+
`#[PhpStormStubsElementAvailable]` attributes from function signatures.
50+
When a stub uses this attribute to declare a parameter that only exists
51+
in certain PHP versions, the parser counts it as a separate required
52+
parameter alongside the variadic replacement, inflating the required
53+
argument count.
54+
55+
For example, `array_push` is declared as:
56+
57+
```
58+
function array_push(
59+
array &$array,
60+
#[PhpStormStubsElementAvailable(from: '5.3', to: '7.2')] $values,
61+
mixed ...$values
62+
): int {}
63+
```
64+
65+
The parser sees three parameters (`$array`, `$values`,
66+
`...$values`) and counts two as required, when the correct required
67+
count is one (`$array` only, since `...$values` is variadic).
68+
69+
This affects roughly 230 stub functions. The argument count checker
70+
currently works around the issue with an overload map derived from
71+
PHPStan's `functionMap.php` (see `overload_min_args()` in
72+
`argument_count.rs`). The proper fix is to make the stub parser
73+
ignore parameters annotated with `#[PhpStormStubsElementAvailable]`
74+
when a variadic parameter of the same name follows.

0 commit comments

Comments
 (0)