1use crate::{
2 display::Displayable,
3 errors::FatalError,
4 namespace::items::{EnumVariantId, TypeDef},
5 pattern_analysis::PatternMatrix,
6};
7
8use crate::namespace::items::{
9 ContractId, DiagnosticSink, FunctionId, FunctionSigId, Item, TraitId,
10};
11use crate::namespace::types::{Generic, SelfDecl, Type, TypeId};
12use crate::AnalyzerDb;
13use crate::{
14 builtins::{ContractTypeMethod, GlobalFunction, Intrinsic, ValueMethod},
15 namespace::scopes::BlockScopeType,
16};
17use crate::{
18 errors::{self, IncompleteItem, TypeError},
19 namespace::items::ModuleId,
20};
21use fe_common::diagnostics::Diagnostic;
22pub use fe_common::diagnostics::Label;
23use fe_common::Span;
24use fe_parser::ast;
25use fe_parser::node::{Node, NodeId};
26
27use indexmap::IndexMap;
28use num_bigint::BigInt;
29use smol_str::SmolStr;
30use std::fmt::{self, Debug};
31use std::hash::Hash;
32use std::marker::PhantomData;
33use std::rc::Rc;
34use std::{cell::RefCell, collections::HashMap};
35
36#[derive(Debug, PartialEq, Eq, Hash, Clone)]
37pub struct Analysis<T> {
38 pub value: T,
39 pub diagnostics: Rc<[Diagnostic]>,
40}
41impl<T> Analysis<T> {
42 pub fn new(value: T, diagnostics: Rc<[Diagnostic]>) -> Self {
43 Self { value, diagnostics }
44 }
45 pub fn sink_diagnostics(&self, sink: &mut impl DiagnosticSink) {
46 self.diagnostics.iter().for_each(|diag| sink.push(diag))
47 }
48 pub fn has_diag(&self) -> bool {
49 !self.diagnostics.is_empty()
50 }
51}
52
53pub trait AnalyzerContext {
54 fn resolve_name(&self, name: &str, span: Span) -> Result<Option<NamedThing>, IncompleteItem>;
55 fn resolve_path(&self, path: &ast::Path, span: Span) -> Result<NamedThing, FatalError>;
57 fn resolve_visible_path(&self, path: &ast::Path) -> Option<NamedThing>;
59 fn resolve_any_path(&self, path: &ast::Path) -> Option<NamedThing>;
61
62 fn add_diagnostic(&self, diag: Diagnostic);
63 fn db(&self) -> &dyn AnalyzerDb;
64
65 fn error(&self, message: &str, label_span: Span, label: &str) -> DiagnosticVoucher {
66 self.register_diag(errors::error(message, label_span, label))
67 }
68
69 fn add_expression(&self, node: &Node<ast::Expr>, attributes: ExpressionAttributes);
75
76 fn update_expression(&self, node: &Node<ast::Expr>, f: &dyn Fn(&mut ExpressionAttributes));
82
83 fn expr_typ(&self, expr: &Node<ast::Expr>) -> Type;
89
90 fn add_constant(&self, name: &Node<ast::SmolStr>, expr: &Node<ast::Expr>, value: Constant);
92
93 fn constant_value_by_name(
95 &self,
96 name: &ast::SmolStr,
97 span: Span,
98 ) -> Result<Option<Constant>, IncompleteItem>;
99
100 fn parent(&self) -> Item;
115
116 fn module(&self) -> ModuleId;
118
119 fn parent_function(&self) -> FunctionId;
126
127 fn root_item(&self) -> Item {
142 let mut item = self.parent();
143 while let Item::Function(func_id) = item {
144 item = func_id.parent(self.db());
145 }
146 item
147 }
148
149 fn add_call(&self, node: &Node<ast::Expr>, call_type: CallType);
154 fn get_call(&self, node: &Node<ast::Expr>) -> Option<CallType>;
155
156 fn is_in_function(&self) -> bool;
158
159 fn inherits_type(&self, typ: BlockScopeType) -> bool;
161
162 fn get_context_type(&self) -> Option<TypeId>;
164
165 fn type_error(
166 &self,
167 message: &str,
168 span: Span,
169 expected: TypeId,
170 actual: TypeId,
171 ) -> DiagnosticVoucher {
172 self.register_diag(errors::type_error(
173 message,
174 span,
175 expected.display(self.db()),
176 actual.display(self.db()),
177 ))
178 }
179
180 fn not_yet_implemented(&self, feature: &str, span: Span) -> DiagnosticVoucher {
181 self.register_diag(errors::not_yet_implemented(feature, span))
182 }
183
184 fn fancy_error(
185 &self,
186 message: &str,
187 labels: Vec<Label>,
188 notes: Vec<String>,
189 ) -> DiagnosticVoucher {
190 self.register_diag(errors::fancy_error(message, labels, notes))
191 }
192
193 fn duplicate_name_error(
194 &self,
195 message: &str,
196 name: &str,
197 original: Span,
198 duplicate: Span,
199 ) -> DiagnosticVoucher {
200 self.register_diag(errors::duplicate_name_error(
201 message, name, original, duplicate,
202 ))
203 }
204
205 fn name_conflict_error(
206 &self,
207 name_kind: &str, name: &str,
209 original: &NamedThing,
210 original_span: Option<Span>,
211 duplicate_span: Span,
212 ) -> DiagnosticVoucher {
213 self.register_diag(errors::name_conflict_error(
214 name_kind,
215 name,
216 original,
217 original_span,
218 duplicate_span,
219 ))
220 }
221
222 fn register_diag(&self, diag: Diagnostic) -> DiagnosticVoucher {
223 self.add_diagnostic(diag);
224 DiagnosticVoucher(PhantomData)
225 }
226}
227
228#[derive(Clone, Debug, PartialEq, Eq)]
229pub enum NamedThing {
230 Item(Item),
231 EnumVariant(EnumVariantId),
232 SelfValue {
233 decl: Option<SelfDecl>,
235
236 parent: Option<Item>,
239 span: Option<Span>,
240 },
241 Variable {
243 name: SmolStr,
244 typ: Result<TypeId, TypeError>,
245 is_const: bool,
246 span: Span,
247 },
248}
249
250impl NamedThing {
251 pub fn name(&self, db: &dyn AnalyzerDb) -> SmolStr {
252 match self {
253 NamedThing::Item(item) => item.name(db),
254 NamedThing::EnumVariant(variant) => variant.name(db),
255 NamedThing::SelfValue { .. } => "self".into(),
256 NamedThing::Variable { name, .. } => name.clone(),
257 }
258 }
259
260 pub fn name_span(&self, db: &dyn AnalyzerDb) -> Option<Span> {
261 match self {
262 NamedThing::Item(item) => item.name_span(db),
263 NamedThing::EnumVariant(variant) => Some(variant.span(db)),
264 NamedThing::SelfValue { span, .. } => *span,
265 NamedThing::Variable { span, .. } => Some(*span),
266 }
267 }
268
269 pub fn is_builtin(&self) -> bool {
270 match self {
271 NamedThing::Item(item) => item.is_builtin(),
272 NamedThing::EnumVariant(_)
273 | NamedThing::Variable { .. }
274 | NamedThing::SelfValue { .. } => false,
275 }
276 }
277
278 pub fn item_kind_display_name(&self) -> &str {
279 match self {
280 NamedThing::Item(item) => item.item_kind_display_name(),
281 NamedThing::EnumVariant(_) => "enum variant",
282 NamedThing::Variable { .. } => "variable",
283 NamedThing::SelfValue { .. } => "value",
284 }
285 }
286
287 pub fn resolve_path_segment(
288 &self,
289 db: &dyn AnalyzerDb,
290 segment: &SmolStr,
291 ) -> Option<NamedThing> {
292 if let Self::Item(Item::Type(TypeDef::Enum(enum_))) = self {
293 if let Some(variant) = enum_.variant(db, segment) {
294 return Some(NamedThing::EnumVariant(variant));
295 }
296 }
297
298 match self {
299 Self::Item(item) => item
300 .items(db)
301 .get(segment)
302 .map(|resolved| NamedThing::Item(*resolved)),
303
304 _ => None,
305 }
306 }
307}
308
309#[derive(Debug, Clone, PartialEq, Eq, Hash)]
311pub struct DiagnosticVoucher(PhantomData<()>);
312
313impl DiagnosticVoucher {
314 pub fn assume_the_parser_handled_it() -> Self {
315 Self(PhantomData)
316 }
317}
318
319#[derive(Default)]
320pub struct TempContext {
321 pub diagnostics: RefCell<Vec<Diagnostic>>,
322}
323impl AnalyzerContext for TempContext {
324 fn db(&self) -> &dyn AnalyzerDb {
325 panic!("TempContext has no analyzer db")
326 }
327
328 fn resolve_name(&self, _name: &str, _span: Span) -> Result<Option<NamedThing>, IncompleteItem> {
329 panic!("TempContext can't resolve names")
330 }
331
332 fn resolve_path(&self, _path: &ast::Path, _span: Span) -> Result<NamedThing, FatalError> {
333 panic!("TempContext can't resolve paths")
334 }
335
336 fn resolve_visible_path(&self, _path: &ast::Path) -> Option<NamedThing> {
337 panic!("TempContext can't resolve paths")
338 }
339
340 fn resolve_any_path(&self, _path: &ast::Path) -> Option<NamedThing> {
341 panic!("TempContext can't resolve paths")
342 }
343
344 fn add_expression(&self, _node: &Node<ast::Expr>, _attributes: ExpressionAttributes) {
345 panic!("TempContext can't store expression")
346 }
347
348 fn update_expression(&self, _node: &Node<ast::Expr>, _f: &dyn Fn(&mut ExpressionAttributes)) {
349 panic!("TempContext can't update expression");
350 }
351
352 fn expr_typ(&self, _expr: &Node<ast::Expr>) -> Type {
353 panic!("TempContext can't return expression type")
354 }
355
356 fn add_constant(&self, _name: &Node<ast::SmolStr>, _expr: &Node<ast::Expr>, _value: Constant) {
357 panic!("TempContext can't store constant")
358 }
359
360 fn constant_value_by_name(
361 &self,
362 _name: &ast::SmolStr,
363 _span: Span,
364 ) -> Result<Option<Constant>, IncompleteItem> {
365 Ok(None)
366 }
367
368 fn parent(&self) -> Item {
369 panic!("TempContext has no root item")
370 }
371
372 fn module(&self) -> ModuleId {
373 panic!("TempContext has no module")
374 }
375
376 fn parent_function(&self) -> FunctionId {
377 panic!("TempContext has no parent function")
378 }
379
380 fn add_call(&self, _node: &Node<ast::Expr>, _call_type: CallType) {
381 panic!("TempContext can't add call");
382 }
383
384 fn get_call(&self, _node: &Node<ast::Expr>) -> Option<CallType> {
385 panic!("TempContext can't have calls");
386 }
387
388 fn is_in_function(&self) -> bool {
389 false
390 }
391
392 fn inherits_type(&self, _typ: BlockScopeType) -> bool {
393 false
394 }
395
396 fn add_diagnostic(&self, diag: Diagnostic) {
397 self.diagnostics.borrow_mut().push(diag)
398 }
399
400 fn get_context_type(&self) -> Option<TypeId> {
401 panic!("TempContext can't resolve Context")
402 }
403}
404
405#[derive(Default, Clone, Debug, PartialEq, Eq)]
406pub struct FunctionBody {
407 pub expressions: IndexMap<NodeId, ExpressionAttributes>,
408 pub matches: IndexMap<NodeId, PatternMatrix>,
410 pub var_types: IndexMap<NodeId, TypeId>,
412 pub calls: IndexMap<NodeId, CallType>,
413 pub spans: HashMap<NodeId, Span>,
414}
415
416#[derive(Clone, Debug, PartialEq, Eq)]
418pub struct ExpressionAttributes {
419 pub typ: TypeId,
420 pub const_value: Option<Constant>,
422 pub type_adjustments: Vec<Adjustment>,
423}
424impl ExpressionAttributes {
425 pub fn original_type(&self) -> TypeId {
426 self.typ
427 }
428 pub fn adjusted_type(&self) -> TypeId {
429 if let Some(adj) = self.type_adjustments.last() {
430 adj.into
431 } else {
432 self.typ
433 }
434 }
435}
436
437#[derive(Copy, Clone, Debug, PartialEq, Eq)]
438pub struct Adjustment {
439 pub into: TypeId,
440 pub kind: AdjustmentKind,
441}
442
443#[derive(Copy, Clone, Debug, PartialEq, Eq)]
444pub enum AdjustmentKind {
445 Copy,
446 Load,
448 IntSizeIncrease,
449 StringSizeIncrease,
450}
451
452impl ExpressionAttributes {
453 pub fn new(typ: TypeId) -> Self {
454 Self {
455 typ,
456 const_value: None,
457 type_adjustments: vec![],
458 }
459 }
460}
461
462impl crate::display::DisplayWithDb for ExpressionAttributes {
463 fn format(&self, db: &dyn AnalyzerDb, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
464 let ExpressionAttributes {
465 typ,
466 const_value,
467 type_adjustments,
468 } = self;
469 write!(f, "{}", typ.display(db))?;
470 if let Some(val) = &const_value {
471 write!(f, " = {val:?}")?;
472 }
473 for adj in type_adjustments {
474 write!(f, " -{:?}-> {}", adj.kind, adj.into.display(db))?;
475 }
476 Ok(())
477 }
478}
479
480#[derive(Clone, Debug, PartialEq, Eq)]
482pub enum CallType {
483 BuiltinFunction(GlobalFunction),
484 Intrinsic(Intrinsic),
485 BuiltinValueMethod {
486 method: ValueMethod,
487 typ: TypeId,
488 },
489
490 BuiltinAssociatedFunction {
492 contract: ContractId,
493 function: ContractTypeMethod,
494 },
495
496 AssociatedFunction {
498 typ: TypeId,
499 function: FunctionId,
500 },
501 ValueMethod {
503 typ: TypeId,
504 method: FunctionId,
505 },
506 TraitValueMethod {
511 trait_id: TraitId,
512 method: FunctionSigId,
513 generic_type: Generic,
516 },
517 External {
518 contract: ContractId,
519 function: FunctionId,
520 },
521 Pure(FunctionId),
522 TypeConstructor(TypeId),
523 EnumConstructor(EnumVariantId),
524}
525
526impl CallType {
527 pub fn function(&self) -> Option<FunctionId> {
528 use CallType::*;
529 match self {
530 BuiltinFunction(_)
531 | BuiltinValueMethod { .. }
532 | TypeConstructor(_)
533 | EnumConstructor(_)
534 | Intrinsic(_)
535 | TraitValueMethod { .. }
536 | BuiltinAssociatedFunction { .. } => None,
537 AssociatedFunction { function: id, .. }
538 | ValueMethod { method: id, .. }
539 | External { function: id, .. }
540 | Pure(id) => Some(*id),
541 }
542 }
543
544 pub fn function_name(&self, db: &dyn AnalyzerDb) -> SmolStr {
545 match self {
546 CallType::BuiltinFunction(f) => f.as_ref().into(),
547 CallType::Intrinsic(f) => f.as_ref().into(),
548 CallType::BuiltinValueMethod { method, .. } => method.as_ref().into(),
549 CallType::BuiltinAssociatedFunction { function, .. } => function.as_ref().into(),
550 CallType::AssociatedFunction { function: id, .. }
551 | CallType::ValueMethod { method: id, .. }
552 | CallType::External { function: id, .. }
553 | CallType::Pure(id) => id.name(db),
554 CallType::TraitValueMethod { method: id, .. } => id.name(db),
555 CallType::TypeConstructor(typ) => typ.display(db).to_string().into(),
556 CallType::EnumConstructor(variant) => {
557 let enum_name = variant.parent(db).name(db);
558 let variant_name = variant.name(db);
559 format!("{enum_name}::{variant_name}").into()
560 }
561 }
562 }
563
564 pub fn is_unsafe(&self, db: &dyn AnalyzerDb) -> bool {
565 if let CallType::Intrinsic(_) = self {
566 true
567 } else if let CallType::TypeConstructor(type_id) = self {
568 if let Type::Struct(struct_) = type_id.typ(db) {
572 struct_.name(db) == "Context" && struct_.module(db).ingot(db).name(db) == "std"
573 } else {
574 false
575 }
576 } else {
577 self.function().map(|id| id.is_unsafe(db)).unwrap_or(false)
578 }
579 }
580}
581
582impl fmt::Display for CallType {
583 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
584 write!(f, "{self:?}")
585 }
586}
587
588#[derive(Debug, Clone, PartialEq, Eq)]
590pub enum Constant {
591 Int(BigInt),
592 Address(BigInt),
593 Bool(bool),
594 Str(SmolStr),
595}