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