1use std::collections::HashMap;
6
7use nifty_syntax::Span;
8use nifty_syntax::ast::{
9 Arg, Block, ElseBranch, Expr, ExprKind, File, Ident, IfStmt, Item, Iterable, StmtKind,
10 StringPart, TypeExpr, TypeExprKind, TypedName,
11};
12
13use super::{Index, Occurrence, Role, Symbol, SymbolId, SymbolKind, Unresolved, Visibility};
14
15pub(super) fn build(files: &[&File]) -> Index {
16 let mut builder = Builder {
17 symbols: Vec::new(),
18 occurrences: Vec::new(),
19 scopes: vec![HashMap::new()],
20 blocks: Vec::new(),
21 };
22 let items = || files.iter().flat_map(|file| &file.items);
23 let declared: Vec<Declared> = items().map(|item| builder.declare_item(item)).collect();
24 for (item, declared) in items().zip(declared) {
25 builder.resolve_item(item, &declared);
26 }
27 builder
28 .occurrences
29 .sort_by_key(|occurrence| occurrence.span);
30 Index {
31 symbols: builder.symbols,
32 occurrences: builder.occurrences,
33 }
34}
35
36#[derive(Default)]
38struct Declared {
39 params: Vec<SymbolId>,
40}
41
42#[derive(Clone, Copy)]
44enum NameUse {
45 Value,
46 Callee,
47}
48
49struct Builder<'ast> {
50 symbols: Vec<Symbol>,
51 occurrences: Vec<Occurrence>,
52 scopes: Vec<HashMap<&'ast str, SymbolId>>,
54 blocks: Vec<Span>,
56}
57
58impl<'ast> Builder<'ast> {
59 fn declare_item(&mut self, item: &'ast Item) -> Declared {
60 match item {
61 Item::State(decl) => {
62 self.declare_global(&decl.name, SymbolKind::State);
63 Declared::default()
64 }
65 Item::Struct(decl) => {
66 let id = self.declare_global(&decl.name, SymbolKind::Struct);
67 for field in &decl.fields {
68 self.declare(
69 &field.name,
70 SymbolKind::Field,
71 Some(id),
72 Visibility::Everywhere,
73 );
74 }
75 Declared::default()
76 }
77 Item::Fn(decl) => {
78 self.declare_callable(&decl.name, SymbolKind::Function, &decl.params, &decl.body)
79 }
80 Item::View(decl) => {
81 self.declare_callable(&decl.name, SymbolKind::View, &decl.params, &decl.body)
82 }
83 Item::Named(named) => {
84 self.declare_global(&named.name, SymbolKind::Window);
85 Declared::default()
86 }
87 Item::Stmt(_) => Declared::default(),
88 }
89 }
90
91 fn declare_callable(
92 &mut self,
93 name: &'ast Ident,
94 kind: SymbolKind,
95 params: &[TypedName],
96 body: &Block,
97 ) -> Declared {
98 let id = self.declare_global(name, kind);
99 let visibility = Visibility::Within(body.span);
100 let params = params
101 .iter()
102 .map(|param| self.declare(¶m.name, SymbolKind::Parameter, Some(id), visibility))
103 .collect();
104 Declared { params }
105 }
106
107 fn resolve_item(&mut self, item: &'ast Item, declared: &Declared) {
108 match item {
109 Item::State(decl) => {
110 self.resolve_type_annotation(decl.ty.as_ref());
111 self.resolve_expr(&decl.value);
112 }
113 Item::Struct(decl) => {
114 for field in &decl.fields {
115 self.resolve_type(&field.ty);
116 }
117 }
118 Item::Fn(decl) => {
119 self.resolve_type_annotation(decl.return_type.as_ref());
120 self.resolve_callable_body(&decl.params, &declared.params, &decl.body);
121 }
122 Item::View(decl) => {
123 self.resolve_callable_body(&decl.params, &declared.params, &decl.body);
124 }
125 Item::Named(named) => self.resolve_stmt(&named.call.kind),
126 Item::Stmt(stmt) => self.resolve_stmt(&stmt.kind),
127 }
128 }
129
130 fn resolve_callable_body(
131 &mut self,
132 params: &'ast [TypedName],
133 ids: &[SymbolId],
134 body: &'ast Block,
135 ) {
136 self.scopes.push(HashMap::new());
137 for (param, &id) in params.iter().zip(ids) {
138 self.resolve_type(¶m.ty);
139 self.bind(¶m.name.name, id);
140 }
141 self.resolve_block(body);
142 self.scopes.pop();
143 }
144
145 fn resolve_block(&mut self, block: &'ast Block) {
146 self.scopes.push(HashMap::new());
147 self.blocks.push(block.span);
148 for stmt in &block.stmts {
149 self.resolve_stmt(&stmt.kind);
150 }
151 self.blocks.pop();
152 self.scopes.pop();
153 }
154
155 fn resolve_stmt(&mut self, stmt: &'ast StmtKind) {
156 match stmt {
157 StmtKind::Let { name, ty, value } => {
158 self.resolve_type_annotation(ty.as_ref());
159 self.resolve_expr(value);
160 let visibility = Visibility::After {
161 file: value.span.file,
162 start: value.span.end,
163 end: self.blocks.last().map(|block| block.end),
164 };
165 let id = self.declare(name, SymbolKind::Local, None, visibility);
166 self.bind(&name.name, id);
167 }
168 StmtKind::Assign { target, value, .. } => {
169 self.resolve_expr(target);
170 self.resolve_expr(value);
171 }
172 StmtKind::If(if_stmt) => self.resolve_if(if_stmt),
173 StmtKind::For {
174 index,
175 item,
176 iterable,
177 body,
178 } => {
179 match iterable {
180 Iterable::List(list) => self.resolve_expr(list),
181 Iterable::Range { start, end } => {
182 self.resolve_expr(start);
183 self.resolve_expr(end);
184 }
185 }
186 self.scopes.push(HashMap::new());
187 let visibility = Visibility::Within(body.span);
188 for name in index.iter().chain([item]) {
189 let id = self.declare(name, SymbolKind::Local, None, visibility);
190 self.bind(&name.name, id);
191 }
192 self.resolve_block(body);
193 self.scopes.pop();
194 }
195 StmtKind::Return(value) => {
196 if let Some(value) = value {
197 self.resolve_expr(value);
198 }
199 }
200 StmtKind::Expr(expr) => self.resolve_expr(expr),
201 }
202 }
203
204 fn resolve_if(&mut self, if_stmt: &'ast IfStmt) {
205 self.resolve_expr(&if_stmt.condition);
206 self.resolve_block(&if_stmt.then_block);
207 match &if_stmt.else_branch {
208 Some(ElseBranch::If(nested)) => self.resolve_if(nested),
209 Some(ElseBranch::Block(block)) => self.resolve_block(block),
210 None => {}
211 }
212 }
213
214 fn resolve_expr(&mut self, expr: &'ast Expr) {
215 match &expr.kind {
216 ExprKind::Name(name) => {
217 self.resolve_name(name, expr.span, NameUse::Value);
218 }
219 ExprKind::ImplicitMember(member) => {
220 self.unresolved(member.span, Unresolved::ImplicitMember);
221 }
222 ExprKind::String(parts) => {
223 for part in parts {
224 if let StringPart::Expr(expr) = part {
225 self.resolve_expr(expr);
226 }
227 }
228 }
229 ExprKind::List(items) => {
230 for item in items {
231 self.resolve_expr(item);
232 }
233 }
234 ExprKind::Unary { operand, .. } => self.resolve_expr(operand),
235 ExprKind::Binary { lhs, rhs, .. } => {
236 self.resolve_expr(lhs);
237 self.resolve_expr(rhs);
238 }
239 ExprKind::Call {
240 callee,
241 args,
242 trailing_block,
243 } => {
244 let callee = self.resolve_callee(callee);
245 self.resolve_args(callee, args);
246 if let Some(block) = trailing_block {
247 self.resolve_block(block);
248 }
249 }
250 ExprKind::Field { base, name } => {
251 self.resolve_expr(base);
252 self.unresolved(name.span, Unresolved::Field);
253 }
254 ExprKind::Index { base, index } => {
255 self.resolve_expr(base);
256 self.resolve_expr(index);
257 }
258 ExprKind::Int(_) | ExprKind::Float(_) | ExprKind::Bool(_) | ExprKind::Error => {}
259 }
260 }
261
262 fn resolve_callee(&mut self, callee: &'ast Expr) -> Option<SymbolId> {
264 match &callee.kind {
265 ExprKind::Name(name) => self.resolve_name(name, callee.span, NameUse::Callee),
266 ExprKind::Field { base, name } => {
267 self.resolve_expr(base);
268 self.unresolved(name.span, Unresolved::Method);
269 None
270 }
271 _ => {
272 self.resolve_expr(callee);
273 None
274 }
275 }
276 }
277
278 fn resolve_args(&mut self, callee: Option<SymbolId>, args: &'ast [Arg]) {
281 for arg in args {
282 let member = callee
283 .zip(arg.label.as_ref())
284 .and_then(|(callee, label)| Some((self.member(callee, &label.name)?, label.span)));
285 if let Some((member, span)) = member {
286 self.occur(span, Role::Reference(member));
287 }
288 self.resolve_expr(&arg.value);
289 }
290 }
291
292 fn resolve_name(&mut self, name: &str, span: Span, name_use: NameUse) -> Option<SymbolId> {
293 let found = self.lookup(name);
294 match found {
295 Some(id) => self.occur(span, Role::Reference(id)),
296 None => self.unresolved(
297 span,
298 match name_use {
299 NameUse::Value => Unresolved::Variable,
300 NameUse::Callee => Unresolved::Function,
301 },
302 ),
303 }
304 found
305 }
306
307 fn resolve_type_annotation(&mut self, ty: Option<&TypeExpr>) {
308 if let Some(ty) = ty {
309 self.resolve_type(ty);
310 }
311 }
312
313 fn resolve_type(&mut self, ty: &TypeExpr) {
314 match &ty.kind {
315 TypeExprKind::Named(name) => {
316 let declared_struct = self.scopes[0]
317 .get(name.as_str())
318 .copied()
319 .filter(|&id| self.symbols[id.0].kind == SymbolKind::Struct);
320 match declared_struct {
321 Some(id) => self.occur(ty.span, Role::Reference(id)),
322 None => self.unresolved(ty.span, Unresolved::Type),
323 }
324 }
325 TypeExprKind::List(element) => self.resolve_type(element),
326 }
327 }
328
329 fn lookup(&self, name: &str) -> Option<SymbolId> {
330 self.scopes
331 .iter()
332 .rev()
333 .find_map(|scope| scope.get(name).copied())
334 }
335
336 fn member(&self, parent: SymbolId, name: &str) -> Option<SymbolId> {
337 self.symbols
338 .iter()
339 .position(|symbol| symbol.parent == Some(parent) && symbol.name == name)
340 .map(SymbolId)
341 }
342
343 fn declare_global(&mut self, name: &'ast Ident, kind: SymbolKind) -> SymbolId {
345 let id = self.declare(name, kind, None, Visibility::Everywhere);
346 self.scopes[0].entry(&name.name).or_insert(id);
347 id
348 }
349
350 fn declare(
351 &mut self,
352 name: &Ident,
353 kind: SymbolKind,
354 parent: Option<SymbolId>,
355 visibility: Visibility,
356 ) -> SymbolId {
357 let id = SymbolId(self.symbols.len());
358 self.symbols.push(Symbol {
359 name: name.name.clone(),
360 kind,
361 parent,
362 visibility,
363 });
364 self.occur(name.span, Role::Declaration(id));
365 id
366 }
367
368 fn bind(&mut self, name: &'ast str, id: SymbolId) {
369 if let Some(scope) = self.scopes.last_mut() {
370 scope.insert(name, id);
371 }
372 }
373
374 fn unresolved(&mut self, span: Span, kind: Unresolved) {
375 self.occur(span, Role::Unresolved(kind));
376 }
377
378 fn occur(&mut self, span: Span, role: Role) {
379 self.occurrences.push(Occurrence { span, role });
380 }
381}