1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
//! Per-expression resolved symbol data, retained from body analysis.
//!
//! The static analyzer already resolves types for every expression during
//! analysis but historically discarded the intermediate state. This module
//! exposes that data so that downstream tools can build position indexes for
//! hover, go-to-definition, and completions.
use std::sync::Arc;
use mir_types::Type;
use php_ast::Span;
/// A single resolved symbol observed during body analysis.
#[derive(Debug, Clone)]
pub struct ResolvedSymbol {
/// Absolute path of the file this symbol was found in.
pub file: Arc<str>,
/// Byte-offset span covering only the identifier token (method name,
/// function name, variable sigil+name, etc.). Used for precise
/// go-to-definition and reference highlighting.
pub span: Span,
/// Byte-offset span of the full call expression, e.g. the entire
/// `$obj->method(args)` node. Set only for call-like symbols (method
/// calls, static calls, function calls). `symbol_at` uses this as a
/// fallback so that a cursor sitting in the whitespace between two
/// chained method calls still resolves to the innermost enclosing call.
pub expr_span: Option<Span>,
/// What kind of symbol this is.
pub kind: ReferenceKind,
/// The resolved type at this location.
pub resolved_type: Type,
}
impl ResolvedSymbol {
/// Return the reference-index lookup key, or `None` for kinds that are not
/// tracked there (e.g. variables). Delegates to [`ReferenceKind::to_name`].
pub fn codebase_key(&self) -> Option<String> {
self.kind.to_name().map(|name| name.codebase_key())
}
/// Convert to a typed [`crate::Name`] for use with
/// [`crate::AnalysisSession::definition_of`], [`crate::AnalysisSession::references_to`],
/// or [`crate::AnalysisSession::hover`]. Delegates to [`ReferenceKind::to_name`].
pub fn to_symbol(&self) -> Option<crate::Name> {
self.kind.to_name()
}
}
/// One declaration emitted by [`crate::AnalysisSession::document_symbols`].
/// Tool-agnostic shape for outline / breadcrumb features; consumers map this
/// onto whatever protocol-specific symbol type they need.
///
/// Forms a tree: classes/interfaces/traits/enums have `children` populated
/// with their methods, properties, and constants. Top-level functions and
/// constants have empty `children`.
#[derive(Debug, Clone)]
pub struct DocumentSymbol {
/// FQCN for classes/interfaces/traits/enums; FQN for functions /
/// constants; short name for members nested inside a class.
pub name: Arc<str>,
/// Coarse kind suitable for icon / severity selection in outlines.
pub kind: DeclarationKind,
/// Source location of the declaration (file + 1-based lines, 0-based
/// columns). `None` only for synthetic stub-only definitions that don't
/// have a recorded source span.
pub location: Option<mir_types::Location>,
/// For container symbols (Class, Interface, Trait, Enum), the nested
/// methods, properties, and constants declared on this type. Empty for
/// leaf kinds (Function, Constant, etc.).
pub children: Vec<DocumentSymbol>,
}
/// Coarse declaration kind used by [`DocumentSymbol`]. Maps onto outline-style
/// symbol kinds in any consumer protocol.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DeclarationKind {
Class,
Interface,
Trait,
Enum,
Function,
Method,
Property,
Constant,
EnumCase,
}
/// The kind of symbol reference that was resolved.
#[derive(Debug, Clone)]
pub enum ReferenceKind {
/// A variable reference (`$foo`).
Variable(Arc<str>),
/// An instance method call (`$obj->method()`).
MethodCall { class: Arc<str>, method: Arc<str> },
/// A static method call (`Foo::method()`).
StaticCall { class: Arc<str>, method: Arc<str> },
/// A property access (`$obj->prop`).
PropertyAccess { class: Arc<str>, property: Arc<str> },
/// A free function call (`foo()`).
FunctionCall(Arc<str>),
/// A class reference (`new Foo`, `instanceof Foo`, type hints).
ClassReference(Arc<str>),
/// A class constant access (`Config::VERSION`, `self::CONST`, `parent::CONST`).
ConstantAccess { class: Arc<str>, constant: Arc<str> },
/// A global constant reference (`FOO`, `\FOO`).
GlobalConstant(Arc<str>),
/// A symbol occurrence inside a `use` import statement (`use Foo\Bar;`,
/// `use function foo\bar;`, `use const FOO\BAR;`) — the import's own name
/// token, not a usage site. Kept distinct from the wrapped kind so a
/// consumer walking reference-index postings (e.g. a future index-based
/// rename) can tell an import line apart from a genuine usage: renaming
/// must update the import too, but a plain find-references / dead-code
/// "is this ever used" check must not count the bare import as one (there
/// is deliberately no `UnusedImport` check).
UseImport(Box<ReferenceKind>),
}
impl ReferenceKind {
/// Map to a typed [`crate::Name`], or `None` for kinds that don't correspond
/// to a codebase-level symbol (currently only `Variable`).
pub fn to_name(&self) -> Option<crate::Name> {
match self {
ReferenceKind::MethodCall { class, method }
| ReferenceKind::StaticCall { class, method } => {
Some(crate::Name::method(class.clone(), method.as_ref()))
}
ReferenceKind::PropertyAccess { class, property } => {
Some(crate::Name::property(class.clone(), property.clone()))
}
ReferenceKind::FunctionCall(fqn) => Some(crate::Name::function(fqn.clone())),
ReferenceKind::ClassReference(fqcn) => Some(crate::Name::class(fqcn.clone())),
ReferenceKind::ConstantAccess { class, constant } => {
Some(crate::Name::class_constant(class.clone(), constant.clone()))
}
ReferenceKind::GlobalConstant(fqn) => Some(crate::Name::global_constant(fqn.clone())),
ReferenceKind::Variable(_) => None,
ReferenceKind::UseImport(inner) => inner.to_name(),
}
}
}