mir_analyzer/symbol.rs
1//! Per-expression resolved symbol data, retained from Pass 2.
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::Union;
11use php_ast::Span;
12
13/// A single resolved symbol observed during Pass 2.
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 in the source file.
19 pub span: Span,
20 /// What kind of symbol this is.
21 pub kind: SymbolKind,
22 /// The resolved type at this location.
23 pub resolved_type: Union,
24}
25
26impl ResolvedSymbol {
27 /// Return the key used in the salsa db's reference-location table for this
28 /// symbol, or `None` for kinds that are not tracked there (e.g. variables).
29 ///
30 /// Key format mirrors `MirDatabase::record_reference_location`:
31 /// - method / static call : `"ClassName::methodname"` (method lowercased)
32 /// - property access : `"ClassName::propName"`
33 /// - function call : fully-qualified function name
34 /// - class reference : fully-qualified class name
35 ///
36 /// Prefer [`Self::to_symbol`] for type-safe access.
37 pub fn codebase_key(&self) -> Option<String> {
38 match &self.kind {
39 SymbolKind::MethodCall { class, method } | SymbolKind::StaticCall { class, method } => {
40 Some(format!("{}::{}", class, method.to_lowercase()))
41 }
42 SymbolKind::PropertyAccess { class, property } => Some(format!("{class}::{property}")),
43 SymbolKind::FunctionCall(fqn) => Some(fqn.to_string()),
44 SymbolKind::ClassReference(fqcn) => Some(fqcn.to_string()),
45 SymbolKind::Variable(_) => None,
46 }
47 }
48
49 /// Convert this `ResolvedSymbol` to a typed [`crate::Symbol`] for use with
50 /// [`crate::AnalysisSession::definition_of`], [`crate::AnalysisSession::references_to`],
51 /// or [`crate::AnalysisSession::hover`].
52 ///
53 /// Returns `None` for kinds that don't map to a codebase-level symbol
54 /// (currently only `Variable` — local variables aren't tracked in the
55 /// codebase symbol table).
56 pub fn to_symbol(&self) -> Option<crate::Symbol> {
57 match &self.kind {
58 SymbolKind::MethodCall { class, method } | SymbolKind::StaticCall { class, method } => {
59 Some(crate::Symbol::method(class.clone(), method.as_ref()))
60 }
61 SymbolKind::PropertyAccess { class, property } => {
62 Some(crate::Symbol::property(class.clone(), property.clone()))
63 }
64 SymbolKind::FunctionCall(fqn) => Some(crate::Symbol::function(fqn.clone())),
65 SymbolKind::ClassReference(fqcn) => Some(crate::Symbol::class(fqcn.clone())),
66 SymbolKind::Variable(_) => None,
67 }
68 }
69}
70
71/// One declaration emitted by [`crate::AnalysisSession::document_symbols`].
72/// Tool-agnostic shape for outline / breadcrumb features; consumers map this
73/// onto whatever protocol-specific symbol type they need.
74///
75/// Forms a tree: classes/interfaces/traits/enums have `children` populated
76/// with their methods, properties, and constants. Top-level functions and
77/// constants have empty `children`.
78#[derive(Debug, Clone)]
79pub struct DocumentSymbol {
80 /// FQCN for classes/interfaces/traits/enums; FQN for functions /
81 /// constants; short name for members nested inside a class.
82 pub name: Arc<str>,
83 /// Coarse kind suitable for icon / severity selection in outlines.
84 pub kind: DocumentSymbolKind,
85 /// Source location of the declaration (file + 1-based lines, 0-based
86 /// columns). `None` only for synthetic stub-only definitions that don't
87 /// have a recorded source span.
88 pub location: Option<mir_codebase::storage::Location>,
89 /// For container symbols (Class, Interface, Trait, Enum), the nested
90 /// methods, properties, and constants declared on this type. Empty for
91 /// leaf kinds (Function, Constant, etc.).
92 pub children: Vec<DocumentSymbol>,
93}
94
95/// Coarse declaration kind used by [`DocumentSymbol`]. Maps onto outline-style
96/// symbol kinds in any consumer protocol.
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
98pub enum DocumentSymbolKind {
99 Class,
100 Interface,
101 Trait,
102 Enum,
103 Function,
104 Method,
105 Property,
106 Constant,
107 EnumCase,
108}
109
110/// The kind of symbol that was resolved.
111#[derive(Debug, Clone)]
112pub enum SymbolKind {
113 /// A variable reference (`$foo`).
114 Variable(Arc<str>),
115 /// An instance method call (`$obj->method()`).
116 MethodCall { class: Arc<str>, method: Arc<str> },
117 /// A static method call (`Foo::method()`).
118 StaticCall { class: Arc<str>, method: Arc<str> },
119 /// A property access (`$obj->prop`).
120 PropertyAccess { class: Arc<str>, property: Arc<str> },
121 /// A free function call (`foo()`).
122 FunctionCall(Arc<str>),
123 /// A class reference (`new Foo`, `instanceof Foo`, type hints).
124 ClassReference(Arc<str>),
125}