1#![allow(unstable_name_collisions)] use crate::context::{
4 AnalyzerContext, CallType, Constant, ExpressionAttributes, FunctionBody, NamedThing,
5};
6use crate::errors::{AlreadyDefined, FatalError, IncompleteItem, TypeError};
7use crate::namespace::items::{FunctionId, ModuleId};
8use crate::namespace::items::{Item, TypeDef};
9use crate::namespace::types::{Type, TypeId};
10use crate::pattern_analysis::PatternMatrix;
11use crate::AnalyzerDb;
12use fe_common::diagnostics::Diagnostic;
13use fe_common::Span;
14use fe_parser::{ast, node::NodeId, Label};
15use fe_parser::{ast::Expr, node::Node};
16use indexmap::IndexMap;
17use std::cell::RefCell;
18use std::collections::BTreeMap;
19
20pub struct ItemScope<'a> {
21 db: &'a dyn AnalyzerDb,
22 module: ModuleId,
23 expressions: RefCell<IndexMap<NodeId, ExpressionAttributes>>,
24 pub diagnostics: RefCell<Vec<Diagnostic>>,
25}
26impl<'a> ItemScope<'a> {
27 pub fn new(db: &'a dyn AnalyzerDb, module: ModuleId) -> Self {
28 Self {
29 db,
30 module,
31 expressions: RefCell::new(IndexMap::default()),
32 diagnostics: RefCell::new(vec![]),
33 }
34 }
35}
36
37impl<'a> AnalyzerContext for ItemScope<'a> {
38 fn db(&self) -> &dyn AnalyzerDb {
39 self.db
40 }
41
42 fn add_expression(&self, node: &Node<ast::Expr>, attributes: ExpressionAttributes) {
43 self.expressions
44 .borrow_mut()
45 .insert(node.id, attributes)
46 .expect_none("expression attributes already exist");
47 }
48
49 fn update_expression(&self, node: &Node<ast::Expr>, f: &dyn Fn(&mut ExpressionAttributes)) {
50 f(self.expressions.borrow_mut().get_mut(&node.id).unwrap())
51 }
52
53 fn expr_typ(&self, expr: &Node<Expr>) -> Type {
54 self.expressions
55 .borrow()
56 .get(&expr.id)
57 .unwrap()
58 .typ
59 .typ(self.db())
60 }
61
62 fn add_constant(
63 &self,
64 _name: &Node<ast::SmolStr>,
65 _expr: &Node<ast::Expr>,
66 _value: crate::context::Constant,
67 ) {
68 }
71
72 fn constant_value_by_name(
73 &self,
74 name: &ast::SmolStr,
75 _span: Span,
76 ) -> Result<Option<Constant>, IncompleteItem> {
77 if let Some(constant) = self.module.resolve_constant(self.db, name)? {
78 Ok(constant.constant_value(self.db).ok())
81 } else {
82 Ok(None)
83 }
84 }
85
86 fn parent(&self) -> Item {
87 Item::Module(self.module)
88 }
89
90 fn module(&self) -> ModuleId {
91 self.module
92 }
93
94 fn parent_function(&self) -> FunctionId {
95 panic!("ItemContext has no parent function")
96 }
97
98 fn add_call(&self, _node: &Node<ast::Expr>, _call_type: CallType) {
99 unreachable!("Can't call function outside of function")
100 }
101 fn get_call(&self, _node: &Node<ast::Expr>) -> Option<CallType> {
102 unreachable!("Can't call function outside of function")
103 }
104
105 fn is_in_function(&self) -> bool {
106 false
107 }
108
109 fn inherits_type(&self, _typ: BlockScopeType) -> bool {
110 false
111 }
112
113 fn resolve_name(&self, name: &str, span: Span) -> Result<Option<NamedThing>, IncompleteItem> {
114 let resolved = self.module.resolve_name(self.db, name)?;
115
116 if let Some(named_thing) = &resolved {
117 check_visibility(self, named_thing, span);
118 }
119
120 Ok(resolved)
121 }
122
123 fn resolve_path(&self, path: &ast::Path, span: Span) -> Result<NamedThing, FatalError> {
124 let resolved = self.module.resolve_path_internal(self.db(), path);
125
126 let mut err = None;
127 for diagnostic in resolved.diagnostics.iter() {
128 err = Some(self.register_diag(diagnostic.clone()));
129 }
130
131 if let Some(err) = err {
132 return Err(FatalError::new(err));
133 }
134
135 if let Some(named_thing) = resolved.value {
136 check_visibility(self, &named_thing, span);
137 Ok(named_thing)
138 } else {
139 let err = self.error("unresolved path item", span, "not found");
140 Err(FatalError::new(err))
141 }
142 }
143
144 fn resolve_visible_path(&self, path: &ast::Path) -> Option<NamedThing> {
145 let resolved = self.module.resolve_path_internal(self.db(), path);
146
147 if resolved.diagnostics.len() > 0 {
148 return None;
149 }
150
151 let named_thing = resolved.value?;
152 if is_visible(self, &named_thing) {
153 Some(named_thing)
154 } else {
155 None
156 }
157 }
158
159 fn resolve_any_path(&self, path: &ast::Path) -> Option<NamedThing> {
160 let resolved = self.module.resolve_path_internal(self.db(), path);
161
162 if resolved.diagnostics.len() > 0 {
163 return None;
164 }
165
166 resolved.value
167 }
168
169 fn add_diagnostic(&self, diag: Diagnostic) {
170 self.diagnostics.borrow_mut().push(diag)
171 }
172
173 fn get_context_type(&self) -> Option<TypeId> {
175 if let Ok(Some(NamedThing::Item(Item::Type(TypeDef::Struct(id))))) =
176 self.resolve_name("Context", Span::dummy())
178 {
179 if id.module(self.db()).ingot(self.db()).name(self.db()) == "std" {
181 return Some(self.db().intern_type(Type::Struct(id)));
182 }
183 }
184
185 None
186 }
187}
188
189pub struct FunctionScope<'a> {
190 pub db: &'a dyn AnalyzerDb,
191 pub function: FunctionId,
192 pub body: RefCell<FunctionBody>,
193 pub diagnostics: RefCell<Vec<Diagnostic>>,
194}
195
196impl<'a> FunctionScope<'a> {
197 pub fn new(db: &'a dyn AnalyzerDb, function: FunctionId) -> Self {
198 Self {
199 db,
200 function,
201 body: RefCell::new(FunctionBody::default()),
202 diagnostics: RefCell::new(vec![]),
203 }
204 }
205
206 pub fn function_return_type(&self) -> Result<TypeId, TypeError> {
207 self.function.signature(self.db).return_type.clone()
208 }
209
210 pub fn map_variable_type<T>(&self, node: &Node<T>, typ: TypeId) {
211 self.add_node(node);
212 self.body
213 .borrow_mut()
214 .var_types
215 .insert(node.id, typ)
216 .expect_none("variable has already registered")
217 }
218
219 pub fn map_pattern_matrix(&self, node: &Node<ast::FuncStmt>, matrix: PatternMatrix) {
220 debug_assert!(matches!(node.kind, ast::FuncStmt::Match { .. }));
221 self.body
222 .borrow_mut()
223 .matches
224 .insert(node.id, matrix)
225 .expect_none("match statement attributes already exists")
226 }
227
228 fn add_node<T>(&self, node: &Node<T>) {
229 self.body.borrow_mut().spans.insert(node.id, node.span);
230 }
231}
232
233impl<'a> AnalyzerContext for FunctionScope<'a> {
234 fn db(&self) -> &dyn AnalyzerDb {
235 self.db
236 }
237
238 fn add_diagnostic(&self, diag: Diagnostic) {
239 self.diagnostics.borrow_mut().push(diag)
240 }
241
242 fn add_expression(&self, node: &Node<ast::Expr>, attributes: ExpressionAttributes) {
243 self.add_node(node);
244 self.body
245 .borrow_mut()
246 .expressions
247 .insert(node.id, attributes)
248 .expect_none("expression attributes already exist");
249 }
250
251 fn update_expression(&self, node: &Node<ast::Expr>, f: &dyn Fn(&mut ExpressionAttributes)) {
252 f(self
253 .body
254 .borrow_mut()
255 .expressions
256 .get_mut(&node.id)
257 .unwrap())
258 }
259
260 fn expr_typ(&self, expr: &Node<Expr>) -> Type {
261 self.body
262 .borrow()
263 .expressions
264 .get(&expr.id)
265 .unwrap()
266 .typ
267 .typ(self.db())
268 }
269
270 fn add_constant(&self, _name: &Node<ast::SmolStr>, expr: &Node<ast::Expr>, value: Constant) {
271 self.body
272 .borrow_mut()
273 .expressions
274 .get_mut(&expr.id)
275 .expect("expression attributes must exist before adding constant value")
276 .const_value = Some(value);
277 }
278
279 fn constant_value_by_name(
280 &self,
281 _name: &ast::SmolStr,
282 _span: Span,
283 ) -> Result<Option<Constant>, IncompleteItem> {
284 Ok(None)
285 }
286
287 fn parent(&self) -> Item {
288 self.function.parent(self.db())
289 }
290
291 fn module(&self) -> ModuleId {
292 self.function.module(self.db())
293 }
294
295 fn parent_function(&self) -> FunctionId {
296 self.function
297 }
298
299 fn add_call(&self, node: &Node<ast::Expr>, call_type: CallType) {
300 self.add_node(node);
302 self.body
303 .borrow_mut()
304 .calls
305 .insert(node.id, call_type)
306 .expect_none("call attributes already exist");
307 }
308 fn get_call(&self, node: &Node<ast::Expr>) -> Option<CallType> {
309 self.body.borrow().calls.get(&node.id).cloned()
310 }
311
312 fn is_in_function(&self) -> bool {
313 true
314 }
315
316 fn inherits_type(&self, _typ: BlockScopeType) -> bool {
317 false
318 }
319
320 fn resolve_name(&self, name: &str, span: Span) -> Result<Option<NamedThing>, IncompleteItem> {
321 let sig = self.function.signature(self.db);
322
323 if name == "self" {
324 return Ok(Some(NamedThing::SelfValue {
325 decl: sig.self_decl,
326 parent: self.function.sig(self.db).self_item(self.db),
327 span: self.function.self_span(self.db),
328 }));
329 }
330
331 let param = sig.params.iter().find_map(|param| {
333 (param.name == name).then(|| {
334 let span = self
335 .function
336 .data(self.db)
337 .ast
338 .kind
339 .sig
340 .kind
341 .args
342 .iter()
343 .find_map(|param| (param.name() == name).then(|| param.name_span()))
344 .expect("found param type but not span");
345
346 NamedThing::Variable {
347 name: name.into(),
348 typ: param.typ.clone(),
349 is_const: false,
350 span,
351 }
352 })
353 });
354
355 if let Some(param) = param {
356 Ok(Some(param))
357 } else {
358 let resolved =
359 if let Item::Type(TypeDef::Contract(contract)) = self.function.parent(self.db) {
360 contract.resolve_name(self.db, name)
361 } else {
362 self.function.module(self.db).resolve_name(self.db, name)
363 }?;
364
365 if let Some(named_thing) = &resolved {
366 check_visibility(self, named_thing, span);
367 }
368
369 Ok(resolved)
370 }
371 }
372
373 fn resolve_path(&self, path: &ast::Path, span: Span) -> Result<NamedThing, FatalError> {
374 let resolved = self
375 .function
376 .module(self.db())
377 .resolve_path_internal(self.db(), path);
378
379 let mut err = None;
380 for diagnostic in resolved.diagnostics.iter() {
381 err = Some(self.register_diag(diagnostic.clone()));
382 }
383
384 if let Some(err) = err {
385 return Err(FatalError::new(err));
386 }
387
388 if let Some(named_thing) = resolved.value {
389 check_visibility(self, &named_thing, span);
390 Ok(named_thing)
391 } else {
392 let err = self.error("unresolved path item", span, "not found");
393 Err(FatalError::new(err))
394 }
395 }
396
397 fn resolve_visible_path(&self, path: &ast::Path) -> Option<NamedThing> {
398 let resolved = self
399 .function
400 .module(self.db())
401 .resolve_path_internal(self.db(), path);
402
403 if resolved.diagnostics.len() > 0 {
404 return None;
405 }
406
407 let named_thing = resolved.value?;
408 if is_visible(self, &named_thing) {
409 Some(named_thing)
410 } else {
411 None
412 }
413 }
414
415 fn resolve_any_path(&self, path: &ast::Path) -> Option<NamedThing> {
416 let resolved = self
417 .function
418 .module(self.db())
419 .resolve_path_internal(self.db(), path);
420
421 if resolved.diagnostics.len() > 0 {
422 return None;
423 }
424
425 resolved.value
426 }
427
428 fn get_context_type(&self) -> Option<TypeId> {
429 if let Ok(Some(NamedThing::Item(Item::Type(TypeDef::Struct(id))))) =
430 self.resolve_name("Context", Span::dummy())
431 {
432 if id.module(self.db()).ingot(self.db()).name(self.db()) == "std" {
433 return Some(self.db().intern_type(Type::Struct(id)));
434 }
435 }
436
437 None
438 }
439}
440
441pub struct BlockScope<'a, 'b> {
442 pub root: &'a FunctionScope<'b>,
443 pub parent: Option<&'a BlockScope<'a, 'b>>,
444 pub variable_defs: BTreeMap<String, (TypeId, bool, Span)>,
446 pub constant_defs: RefCell<BTreeMap<String, Constant>>,
447 pub typ: BlockScopeType,
448}
449
450#[derive(Clone, Debug, PartialEq, Eq)]
451pub enum BlockScopeType {
452 Function,
453 IfElse,
454 Match,
455 MatchArm,
456 Loop,
457 Unsafe,
458}
459
460impl AnalyzerContext for BlockScope<'_, '_> {
461 fn db(&self) -> &dyn AnalyzerDb {
462 self.root.db
463 }
464
465 fn resolve_name(&self, name: &str, span: Span) -> Result<Option<NamedThing>, IncompleteItem> {
466 if let Some(var) =
467 self.variable_defs
468 .get(name)
469 .map(|(typ, is_const, span)| NamedThing::Variable {
470 name: name.into(),
471 typ: Ok(*typ),
472 is_const: *is_const,
473 span: *span,
474 })
475 {
476 Ok(Some(var))
477 } else if let Some(parent) = self.parent {
478 parent.resolve_name(name, span)
479 } else {
480 self.root.resolve_name(name, span)
481 }
482 }
483
484 fn add_expression(&self, node: &Node<ast::Expr>, attributes: ExpressionAttributes) {
485 self.root.add_expression(node, attributes)
486 }
487
488 fn update_expression(&self, node: &Node<ast::Expr>, f: &dyn Fn(&mut ExpressionAttributes)) {
489 self.root.update_expression(node, f)
490 }
491
492 fn expr_typ(&self, expr: &Node<Expr>) -> Type {
493 self.root.expr_typ(expr)
494 }
495
496 fn add_constant(&self, name: &Node<ast::SmolStr>, expr: &Node<ast::Expr>, value: Constant) {
497 self.constant_defs
498 .borrow_mut()
499 .insert(name.kind.clone().to_string(), value.clone())
500 .expect_none("expression attributes already exist");
501
502 self.root.add_constant(name, expr, value)
503 }
504
505 fn constant_value_by_name(
506 &self,
507 name: &ast::SmolStr,
508 span: Span,
509 ) -> Result<Option<Constant>, IncompleteItem> {
510 if let Some(constant) = self.constant_defs.borrow().get(name.as_str()) {
511 Ok(Some(constant.clone()))
512 } else if let Some(parent) = self.parent {
513 parent.constant_value_by_name(name, span)
514 } else {
515 match self.resolve_name(name, span)? {
516 Some(NamedThing::Item(Item::Constant(constant))) => {
517 Ok(constant.constant_value(self.db()).ok())
518 }
519 _ => Ok(None),
520 }
521 }
522 }
523
524 fn parent(&self) -> Item {
525 Item::Function(self.root.function)
526 }
527
528 fn module(&self) -> ModuleId {
529 self.root.module()
530 }
531
532 fn parent_function(&self) -> FunctionId {
533 self.root.function
534 }
535
536 fn add_call(&self, node: &Node<ast::Expr>, call_type: CallType) {
537 self.root.add_call(node, call_type)
538 }
539
540 fn get_call(&self, node: &Node<ast::Expr>) -> Option<CallType> {
541 self.root.get_call(node)
542 }
543
544 fn is_in_function(&self) -> bool {
545 true
546 }
547
548 fn inherits_type(&self, typ: BlockScopeType) -> bool {
549 self.typ == typ || self.parent.map_or(false, |scope| scope.inherits_type(typ))
550 }
551
552 fn resolve_path(&self, path: &ast::Path, span: Span) -> Result<NamedThing, FatalError> {
553 self.root.resolve_path(path, span)
554 }
555
556 fn resolve_visible_path(&self, path: &ast::Path) -> Option<NamedThing> {
557 self.root.resolve_visible_path(path)
558 }
559
560 fn resolve_any_path(&self, path: &ast::Path) -> Option<NamedThing> {
561 self.root.resolve_any_path(path)
562 }
563
564 fn add_diagnostic(&self, diag: Diagnostic) {
565 self.root.diagnostics.borrow_mut().push(diag)
566 }
567
568 fn get_context_type(&self) -> Option<TypeId> {
569 self.root.get_context_type()
570 }
571}
572
573impl<'a, 'b> BlockScope<'a, 'b> {
574 pub fn new(root: &'a FunctionScope<'b>, typ: BlockScopeType) -> Self {
575 BlockScope {
576 root,
577 parent: None,
578 variable_defs: BTreeMap::new(),
579 constant_defs: RefCell::new(BTreeMap::new()),
580 typ,
581 }
582 }
583
584 pub fn new_child(&'a self, typ: BlockScopeType) -> Self {
585 BlockScope {
586 root: self.root,
587 parent: Some(self),
588 variable_defs: BTreeMap::new(),
589 constant_defs: RefCell::new(BTreeMap::new()),
590 typ,
591 }
592 }
593
594 pub fn add_var(
596 &mut self,
597 name: &str,
598 typ: TypeId,
599 is_const: bool,
600 span: Span,
601 ) -> Result<(), AlreadyDefined> {
602 match self.resolve_name(name, span) {
603 Ok(Some(NamedThing::SelfValue { .. })) => {
604 let err = self.error(
605 "`self` can't be used as a variable name",
606 span,
607 "expected a name, found keyword `self`",
608 );
609 Err(AlreadyDefined::new(err))
610 }
611
612 Ok(Some(named_item)) => {
613 if named_item.is_builtin() {
614 let err = self.error(
615 &format!(
616 "variable name conflicts with built-in {}",
617 named_item.item_kind_display_name(),
618 ),
619 span,
620 &format!(
621 "`{}` is a built-in {}",
622 name,
623 named_item.item_kind_display_name()
624 ),
625 );
626 Err(AlreadyDefined::new(err))
627 } else {
628 let err = self.duplicate_name_error(
630 &format!("duplicate definition of variable `{name}`"),
631 name,
632 named_item
633 .name_span(self.db())
634 .expect("missing name_span of non-builtin"),
635 span,
636 );
637 Err(AlreadyDefined::new(err))
638 }
639 }
640 _ => {
641 self.variable_defs
642 .insert(name.to_string(), (typ, is_const, span));
643 Ok(())
644 }
645 }
646 }
647}
648
649trait OptionExt {
651 fn expect_none(self, msg: &str);
652}
653
654impl<T> OptionExt for Option<T> {
655 fn expect_none(self, msg: &str) {
656 if self.is_some() {
657 panic!("{}", msg)
658 }
659 }
660}
661
662pub fn check_visibility(context: &dyn AnalyzerContext, named_thing: &NamedThing, span: Span) {
665 if let NamedThing::Item(item) = named_thing {
666 let item_module = item
667 .module(context.db())
668 .unwrap_or_else(|| context.module());
669 if !item.is_public(context.db()) && item_module != context.module() {
670 let module_name = item_module.name(context.db());
671 let item_name = item.name(context.db());
672 let item_span = item.name_span(context.db()).unwrap_or(span);
673 let item_kind_name = item.item_kind_display_name();
674 context.fancy_error(
675 &format!("the {item_kind_name} `{item_name}` is private",),
676 vec![
677 Label::primary(span, format!("this {item_kind_name} is not `pub`")),
678 Label::secondary(item_span, format!("`{item_name}` is defined here")),
679 ],
680 vec![
681 format!("`{item_name}` can only be used within `{module_name}`"),
682 format!(
683 "Hint: use `pub` to make `{item_name}` visible from outside of `{module_name}`",
684 ),
685 ],
686 );
687 }
688 }
689}
690
691fn is_visible(context: &dyn AnalyzerContext, named_thing: &NamedThing) -> bool {
692 if let NamedThing::Item(item) = named_thing {
693 let item_module = item
694 .module(context.db())
695 .unwrap_or_else(|| context.module());
696 item.is_public(context.db()) || item_module == context.module()
697 } else {
698 true
699 }
700}