Skip to content

Commit 4349590

Browse files
committed
Unify variable/subject resolution across hover, type-definition,
references, and deprecated features
1 parent 49a86a0 commit 4349590

9 files changed

Lines changed: 579 additions & 2036 deletions

File tree

docs/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515

1616
### Changed
1717

18+
- **Unified type resolution.** Hover, go-to-type-definition, find-references, deprecated diagnostics, and replace-deprecated code actions now share a single variable/subject resolution pipeline. Fixes resolved in one feature automatically apply to all others.
1819
- **Diagnostic code identifiers.** Standardised all diagnostic codes to a consistent `snake_case` noun-phrase scheme: `undefined_variable``unknown_variable`, `type_error.argument``type_mismatch_argument`, `argument_count``argument_count_mismatch`, `deprecated``deprecated_usage`, `implementation_error``missing_implementation`. Users with editor filters or scripts that match on these codes will need to update them.
1920
- **LSP responsiveness.** Hover, go-to-definition, go-to-type-definition, signature help, code actions, document highlight, prepare-rename, and rename handlers now run on background threads instead of the async event loop. When one request is slow (e.g. resolving a deep trait hierarchy), other requests and cancellations are no longer blocked.
2021

src/code_actions/replace_deprecated.rs

Lines changed: 25 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,9 @@ use std::sync::Arc;
1717
use tower_lsp::lsp_types::*;
1818

1919
use crate::Backend;
20-
use crate::completion::resolver::Loaders;
2120
use crate::diagnostics::offset_range_to_lsp_range;
2221
use crate::symbol_map::SymbolKind;
23-
use crate::types::{ClassInfo, ResolvedType};
24-
use crate::util::resolve_to_fqn;
22+
use crate::types::ClassInfo;
2523
use crate::virtual_members::resolve_class_fully_cached;
2624

2725
/// File-level context needed for subject resolution.
@@ -498,82 +496,30 @@ fn resolve_subject_to_class(
498496
content: &str,
499497
backend: &Backend,
500498
) -> Option<ClassInfo> {
501-
let trimmed = subject_text.trim();
502-
503-
match trimmed {
504-
"self" | "static" | "$this" => {
505-
let cls = ctx
506-
.local_classes
507-
.iter()
508-
.find(|c| {
509-
!c.name.starts_with("__anonymous@")
510-
&& access_offset >= c.start_offset
511-
&& access_offset <= c.end_offset
512-
})
513-
.or_else(|| {
514-
ctx.local_classes
515-
.iter()
516-
.find(|c| !c.name.starts_with("__anonymous@"))
517-
})?;
518-
let fqn = if let Some(ns) = ctx.namespace {
519-
format!("{}\\{}", ns, cls.name)
520-
} else {
521-
cls.name.to_string()
522-
};
523-
backend
524-
.find_or_load_class(&fqn)
525-
.map(|arc| ClassInfo::clone(&arc))
526-
}
527-
"parent" => {
528-
let cls = ctx
529-
.local_classes
530-
.iter()
531-
.find(|c| !c.name.starts_with("__anonymous@") && c.parent_class.is_some())?;
532-
let parent = cls.parent_class.as_ref()?;
533-
let fqn = resolve_to_fqn(parent, ctx.use_map, ctx.namespace);
534-
backend
535-
.find_or_load_class(&fqn)
536-
.map(|arc| ClassInfo::clone(&arc))
537-
}
538-
_ if is_static && !trimmed.starts_with('$') => {
539-
let fqn = resolve_to_fqn(trimmed, ctx.use_map, ctx.namespace);
540-
backend
541-
.find_or_load_class(&fqn)
542-
.map(|arc| ClassInfo::clone(&arc))
543-
}
544-
_ if trimmed.starts_with('$') => {
545-
// Variable — use variable resolution.
546-
let enclosing_class = ctx
547-
.local_classes
548-
.iter()
549-
.find(|c| {
550-
!c.name.starts_with("__anonymous@")
551-
&& access_offset >= c.start_offset
552-
&& access_offset <= c.end_offset
553-
})
554-
.map(|c| ClassInfo::clone(c))
555-
.unwrap_or_default();
556-
557-
let function_loader = backend.function_loader_with(ctx.use_map, ctx.namespace);
558-
let class_loader =
559-
backend.class_loader_with(ctx.local_classes, ctx.use_map, ctx.namespace);
560-
561-
let results = ResolvedType::into_classes(
562-
crate::completion::variable::resolution::resolve_variable_types(
563-
trimmed,
564-
&enclosing_class,
565-
ctx.local_classes,
566-
content,
567-
access_offset,
568-
&class_loader,
569-
Loaders::with_function(Some(&function_loader)),
570-
),
571-
);
572-
573-
results.into_iter().next()
574-
}
575-
_ => None,
576-
}
499+
let class_loader = backend.class_loader_with(ctx.local_classes, ctx.use_map, ctx.namespace);
500+
let function_loader = backend.function_loader_with(ctx.use_map, ctx.namespace);
501+
let res_ctx = crate::subject_resolution::SubjectResolutionCtx {
502+
local_classes: ctx.local_classes,
503+
use_map: ctx.use_map,
504+
namespace: ctx.namespace,
505+
content,
506+
class_loader: &class_loader,
507+
function_loader: &function_loader,
508+
};
509+
510+
let php_type = crate::subject_resolution::resolve_subject_type(
511+
subject_text,
512+
is_static,
513+
access_offset,
514+
&res_ctx,
515+
)?;
516+
517+
// Load the first class from the resolved type.
518+
php_type
519+
.top_level_class_names()
520+
.into_iter()
521+
.find_map(|name| backend.find_or_load_class(&name))
522+
.map(|arc| ClassInfo::clone(&arc))
577523
}
578524

579525
#[cfg(test)]

0 commit comments

Comments
 (0)