semantic/parser/
parser_core.rs1use std::sync::{Arc, OnceLock};
5
6use objects::object::{ContentHash, OccurrenceRole, ScopeEntry, ScopeKind};
7use tree_sitter::{Node, Tree as TSTree};
8
9use super::{
10 parser_language::Language,
11 parser_pool::parse_fresh,
12 parser_types::{CallSite, FunctionDef, Import},
13 syntax_index::{FunctionRef, ImportRef, SyntaxIndex},
14};
15
16#[derive(Debug)]
18pub struct ParsedFile {
19 pub language: Language,
20 pub source: Arc<str>,
21 content_hash: ContentHash,
22 tree: TSTree,
23 index: OnceLock<SyntaxIndex>,
24}
25
26impl ParsedFile {
27 pub fn parse(source: impl AsRef<str>, language: Language) -> Option<Self> {
29 let source = Arc::<str>::from(source.as_ref());
30 let content_hash = ContentHash::compute(source.as_bytes());
31 Self::parse_with_hash(source, language, content_hash)
32 }
33
34 pub fn parse_owned(source: String, language: Language) -> Option<Self> {
36 let content_hash = ContentHash::compute(source.as_bytes());
37 Self::parse_with_hash(Arc::<str>::from(source), language, content_hash)
38 }
39
40 pub(crate) fn parse_with_hash(
41 source: Arc<str>,
42 language: Language,
43 content_hash: ContentHash,
44 ) -> Option<Self> {
45 let tree = parse_fresh(source.as_bytes(), language)?;
46 if tree.root_node().has_error() {
47 return None;
48 }
49
50 Some(Self {
51 language,
52 source,
53 content_hash,
54 tree,
55 index: OnceLock::new(),
56 })
57 }
58
59 pub fn content_hash(&self) -> ContentHash {
61 self.content_hash
62 }
63
64 pub fn source(&self) -> &str {
66 &self.source
67 }
68
69 pub fn root_node(&self) -> Node<'_> {
71 self.tree.root_node()
72 }
73
74 pub fn syntax_index(&self) -> &SyntaxIndex {
76 self.index.get_or_init(|| {
77 SyntaxIndex::build(self.language, self.source.as_ref(), self.root_node())
78 })
79 }
80
81 pub fn functions(&self) -> impl Iterator<Item = FunctionRef<'_>> + '_ {
83 self.syntax_index().functions(self.source.as_ref())
84 }
85
86 pub fn imports(&self) -> impl Iterator<Item = ImportRef<'_>> + '_ {
88 self.syntax_index().imports(self.source.as_ref())
89 }
90
91 pub fn extract_functions(&self) -> Vec<FunctionDef> {
93 self.functions().map(FunctionRef::to_owned).collect()
94 }
95
96 pub fn extract_calls(&self) -> Vec<CallSite> {
99 self.syntax_index()
100 .occurrences()
101 .iter()
102 .filter(|occurrence| occurrence.role == OccurrenceRole::Call)
103 .map(|occurrence| CallSite {
104 name: occurrence.name.clone(),
105 qualifier: occurrence.qualifier.clone(),
106 })
107 .collect()
108 }
109
110 pub fn extract_own_calls(&self) -> Vec<CallSite> {
113 let index = self.syntax_index();
114 let scopes = index.semantic_scopes();
115 let Some(owner) = outermost_function_scope(scopes) else {
116 return Vec::new();
117 };
118 index
119 .occurrences()
120 .iter()
121 .filter(|occurrence| {
122 occurrence.role == OccurrenceRole::Call
123 && nearest_function_scope(scopes, occurrence.scope) == Some(owner)
124 })
125 .map(|occurrence| CallSite {
126 name: occurrence.name.clone(),
127 qualifier: occurrence.qualifier.clone(),
128 })
129 .collect()
130 }
131
132 pub fn extract_imports(&self) -> Vec<Import> {
134 self.imports().map(ImportRef::to_owned).collect()
135 }
136
137 pub fn is_function_kind(kind: &str, language: Language) -> bool {
139 super::syntax_index::is_function_kind(kind, language)
140 }
141}
142
143fn outermost_function_scope(scopes: &[ScopeEntry]) -> Option<u32> {
144 scopes.iter().find_map(|scope| {
145 if scope.kind != ScopeKind::Function {
146 return None;
147 }
148 let mut parent = scope.parent;
149 while let Some(id) = parent {
150 let entry = scopes.get(id as usize)?;
151 if entry.kind == ScopeKind::Function {
152 return None;
153 }
154 parent = entry.parent;
155 }
156 Some(scope.local_id)
157 })
158}
159
160fn nearest_function_scope(scopes: &[ScopeEntry], scope: u32) -> Option<u32> {
161 let mut current = Some(scope);
162 while let Some(id) = current {
163 let entry = scopes.get(id as usize)?;
164 if entry.kind == ScopeKind::Function {
165 return Some(id);
166 }
167 current = entry.parent;
168 }
169 None
170}