Skip to content

Commit fc5026d

Browse files
committed
Faster diagnostics on large projects
1 parent 273935a commit fc5026d

4 files changed

Lines changed: 31 additions & 13 deletions

File tree

docs/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4646
- **Lower memory use in the cross-file reference index.** The index backing Find References and the reference-count inlay hints now keeps only the distinct files and counts each symbol actually needs, instead of one entry per matching location plus never-read position data. On large projects this removes millions of short-lived allocations and shrinks the index to a fraction of its previous size, with no change to Find References or inlay hint results.
4747
- **Lower memory use for method lookups.** Each resolved class's method name index is now a sorted list searched with binary search instead of a hash map, using about a third of the memory for the same lookup speed. On large Laravel projects, where the resolved-class cache holds thousands of these indexes, this measurably shrinks total memory use.
4848
- **Lower memory use for member access spans.** The subject text recorded for every `->`/`::` access (e.g. `$this` in `$this->save()`) no longer allocates a string when it is a plain slice of the source, which covers the vast majority of accesses in typical PHP code. It now reuses the file's own bytes instead, with an allocation only for the rarer cases (chained calls, `new` expressions) where the recorded text differs from the source.
49+
- **Faster diagnostics on large projects.** Several diagnostic checks (by-reference parameter detection, the `Stringable`-to-`string` acceptance check, and `model-property<Model>` literal validation) now read a class's inheritance from the resolved-class cache instead of re-merging traits, parent classes, and generics from scratch on every call. On large Laravel projects this removed a measurable share of the diagnostic pass's CPU time, and as a side effect these checks now also see interface-declared members (e.g. a `__toString` declared only on an implemented interface).
4950

5051
### Removed
5152

src/class_lookup.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,11 @@ pub(crate) fn is_subtype_of_typed(
459459
if let Some(model_name) = g.args[0].base_name()
460460
&& let Some(cls) = class_loader(model_name)
461461
{
462-
let resolved = crate::inheritance::resolve_class_with_inheritance(&cls, class_loader);
462+
let resolved = crate::virtual_members::resolve_class_fully_maybe_cached(
463+
&cls,
464+
class_loader,
465+
crate::virtual_members::active_resolved_class_cache(),
466+
);
463467
return resolved.properties.iter().any(|p| &*p.name == prop_name)
464468
|| crate::virtual_members::laravel::where_property::collect_column_names(&cls)
465469
.iter()

src/diagnostics/type_errors/compatibility.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,11 @@ pub(crate) fn is_type_compatible(
289289
if param_type.is_string_type() && arg_type.is_object_like() {
290290
if let Some(class_name) = arg_type.base_name() {
291291
if let Some(cls) = class_loader(class_name) {
292-
let merged = crate::inheritance::resolve_class_with_inheritance(&cls, class_loader);
292+
let merged = crate::virtual_members::resolve_class_fully_maybe_cached(
293+
&cls,
294+
class_loader,
295+
crate::virtual_members::active_resolved_class_cache(),
296+
);
293297
let implements_stringable =
294298
crate::class_lookup::is_subtype_of(&cls, "Stringable", class_loader);
295299
let has_to_string = merged.get_method_ci("__toString").is_some();
@@ -399,8 +403,11 @@ pub(crate) fn is_type_compatible(
399403
if let Some(model_name) = args[0].base_name()
400404
&& let Some(cls) = class_loader(model_name)
401405
{
402-
let resolved =
403-
crate::inheritance::resolve_class_with_inheritance(&cls, class_loader);
406+
let resolved = crate::virtual_members::resolve_class_fully_maybe_cached(
407+
&cls,
408+
class_loader,
409+
crate::virtual_members::active_resolved_class_cache(),
410+
);
404411
let found = resolved.properties.iter().any(|p| &*p.name == prop_name)
405412
|| crate::virtual_members::laravel::where_property::collect_column_names(&cls)
406413
.iter()

src/diagnostics/undefined_variables/mod.rs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -167,9 +167,11 @@ impl Backend {
167167
let cls = self
168168
.find_or_load_class(&fqn)
169169
.or_else(|| self.find_or_load_class(class_name))?;
170-
let merged = crate::inheritance::resolve_class_with_inheritance(&cls, &|name| {
171-
self.find_or_load_class(name)
172-
});
170+
let merged = crate::virtual_members::resolve_class_fully_maybe_cached(
171+
&cls,
172+
&|name| self.find_or_load_class(name),
173+
Some(&self.resolved_class_cache),
174+
);
173175
let method = merged.get_method(method_name)?;
174176
let positions: Vec<usize> = method
175177
.parameters
@@ -185,9 +187,11 @@ impl Backend {
185187
let cls = self
186188
.find_or_load_class(&fqn)
187189
.or_else(|| self.find_or_load_class(class_name))?;
188-
let merged = crate::inheritance::resolve_class_with_inheritance(&cls, &|name| {
189-
self.find_or_load_class(name)
190-
});
190+
let merged = crate::virtual_members::resolve_class_fully_maybe_cached(
191+
&cls,
192+
&|name| self.find_or_load_class(name),
193+
Some(&self.resolved_class_cache),
194+
);
191195
let ctor = merged.get_method("__construct")?;
192196
let positions: Vec<usize> = ctor
193197
.parameters
@@ -203,9 +207,11 @@ impl Backend {
203207
let cls = self
204208
.find_or_load_class(&fqn)
205209
.or_else(|| self.find_or_load_class(class_name))?;
206-
let merged = crate::inheritance::resolve_class_with_inheritance(&cls, &|name| {
207-
self.find_or_load_class(name)
208-
});
210+
let merged = crate::virtual_members::resolve_class_fully_maybe_cached(
211+
&cls,
212+
&|name| self.find_or_load_class(name),
213+
Some(&self.resolved_class_cache),
214+
);
209215
let method = merged.get_method(method_name)?;
210216
let positions: Vec<usize> = method
211217
.parameters

0 commit comments

Comments
 (0)