From 3ded54c1ae5cbfc4b57e1cbdf30f7ee8cfc3d7aa Mon Sep 17 00:00:00 2001 From: Sean Billig Date: Wed, 29 Sep 2021 20:13:41 -0700 Subject: [PATCH] wip --- crates/analyzer/src/builtins.rs | 58 +--- crates/analyzer/src/context.rs | 67 +++- crates/analyzer/src/db.rs | 21 +- crates/analyzer/src/db/queries/contracts.rs | 34 +- crates/analyzer/src/db/queries/functions.rs | 11 +- crates/analyzer/src/db/queries/module.rs | 141 ++++---- crates/analyzer/src/errors.rs | 44 ++- crates/analyzer/src/namespace/items.rs | 255 ++++++++++----- crates/analyzer/src/namespace/scopes.rs | 165 ++++++---- crates/analyzer/src/namespace/types.rs | 89 ++++- crates/analyzer/src/traversal/call_args.rs | 9 +- crates/analyzer/src/traversal/expressions.rs | 305 ++++++++++++------ crates/analyzer/src/traversal/functions.rs | 50 +-- crates/analyzer/src/traversal/types.rs | 257 +++++++++------ crates/analyzer/tests/analysis.rs | 6 +- crates/analyzer/tests/errors.rs | 2 + .../analysis__data_copying_stress.snap | 10 +- .../snapshots/analysis__erc20_token.snap | 30 +- .../tests/snapshots/analysis__keccak.snap | 24 +- .../analysis__return_u256_from_called_fn.snap | 10 +- ..._return_u256_from_called_fn_with_args.snap | 20 +- .../tests/snapshots/analysis__structs.snap | 8 +- .../snapshots/analysis__tuple_stress.snap | 10 +- .../tests/snapshots/analysis__uniswap.snap | 48 +-- .../snapshots/errors__abi_encode_u256.snap | 2 +- .../errors__array_constructor_call.snap | 4 +- .../errors__call_to_mut_fn_without_self.snap | 9 +- .../errors__duplicate_contract_in_module.snap | 20 +- .../errors__duplicate_struct_in_module.snap | 16 +- .../errors__duplicate_typedef_in_module.snap | 15 +- .../snapshots/errors__emit_type_name.snap | 12 + .../snapshots/errors__emit_variable.snap | 12 + ...ors__int_type_constructor_generic_arg.snap | 4 +- ...int_type_constructor_generic_arg_list.snap | 4 +- .../errors__int_type_generic_arg.snap | 4 +- .../errors__int_type_generic_arg_list.snap | 4 +- .../snapshots/errors__map_int_type_arg.snap | 2 +- .../snapshots/errors__map_int_type_args.snap | 2 +- .../snapshots/errors__map_map_key_type.snap | 2 +- .../errors__map_no_type_arg_list.snap | 6 +- .../snapshots/errors__map_no_type_args.snap | 4 +- .../snapshots/errors__map_one_type_arg.snap | 12 +- .../errors__map_three_type_args.snap | 12 +- .../tests/snapshots/errors__not_callable.snap | 26 +- .../errors__return_type_undefined.snap | 2 +- .../errors__shadow_builtin_function.snap | 8 +- .../errors__shadow_builtin_type.snap | 12 + ...__string_constructor_no_type_arg_list.snap | 6 +- ...rors__string_constructor_no_type_args.snap | 10 +- ...__string_constructor_non_int_type_arg.snap | 8 +- ..._string_constructor_two_int_type_args.snap | 10 +- ...ors__string_constructor_two_type_args.snap | 10 +- .../errors__string_no_type_arg_list.snap | 6 +- .../errors__string_no_type_args.snap | 10 +- .../errors__string_non_int_type_arg.snap | 8 +- .../errors__string_two_int_type_args.snap | 10 +- .../errors__string_two_type_args.snap | 10 +- .../errors__undefined_generic_type.snap | 2 +- .../snapshots/errors__undefined_type.snap | 2 +- .../errors__undefined_type_param.snap | 4 +- crates/lowering/src/mappers/module.rs | 49 +-- crates/parser/src/ast.rs | 8 + .../fixtures/compile_errors/not_callable.fe | 16 +- .../compile_errors/shadow_builtin_type.fe | 9 +- crates/yulgen/src/mappers/expressions.rs | 13 +- crates/yulgen/src/names/mod.rs | 16 +- 66 files changed, 1329 insertions(+), 746 deletions(-) create mode 100644 crates/analyzer/tests/snapshots/errors__emit_type_name.snap create mode 100644 crates/analyzer/tests/snapshots/errors__emit_variable.snap diff --git a/crates/analyzer/src/builtins.rs b/crates/analyzer/src/builtins.rs index b2a728ae5e..c170a2dde1 100644 --- a/crates/analyzer/src/builtins.rs +++ b/crates/analyzer/src/builtins.rs @@ -1,54 +1,4 @@ -use std::str::FromStr; -use strum::{AsRefStr, EnumString, IntoStaticStr}; - -#[derive(Debug, PartialEq, EnumString, AsRefStr)] -#[strum(serialize_all = "lowercase")] -pub enum ReservedName { - Type, - Object, - // Module, // someday: "std" - Function, -} - -/// Check if a name shadows a built-in type, object, or function. -/// Ideally, some of these things will move into a .fe standard library, -/// and we won't need some of this special name collision logic. -pub fn reserved_name(name: &str) -> Option { - if TypeName::from_str(name).is_ok() { - Some(ReservedName::Type) - } else if Object::from_str(name).is_ok() { - Some(ReservedName::Object) - } else if GlobalMethod::from_str(name).is_ok() { - Some(ReservedName::Function) - } else { - None - } -} - -#[derive(Debug, PartialEq, EnumString)] -#[strum(serialize_all = "lowercase")] -pub enum TypeName { - // Array, // when syntax is changed - #[strum(serialize = "Map")] - Map, - #[strum(serialize = "String")] - String_, - - Address, - Bool, - I8, - I16, - I32, - I64, - I128, - I256, - U8, - U16, - U32, - U64, - U128, - U256, -} +use strum::{AsRefStr, EnumIter, EnumString}; #[derive(Debug, PartialEq, EnumString)] #[strum(serialize_all = "snake_case")] @@ -58,20 +8,20 @@ pub enum ValueMethod { AbiEncode, } -#[derive(Clone, Copy, Debug, PartialEq, Eq, EnumString, IntoStaticStr, Hash)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, EnumString, AsRefStr, Hash, EnumIter)] #[strum(serialize_all = "snake_case")] pub enum GlobalMethod { Keccak256, } -#[derive(strum::ToString, Debug, PartialEq, EnumString)] +#[derive(Debug, PartialEq, EnumString, AsRefStr)] #[strum(serialize_all = "snake_case")] pub enum ContractTypeMethod { Create, Create2, } -#[derive(strum::ToString, Debug, PartialEq, EnumString)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, EnumString, EnumIter, AsRefStr)] #[strum(serialize_all = "lowercase")] pub enum Object { Block, diff --git a/crates/analyzer/src/context.rs b/crates/analyzer/src/context.rs index 8ccb8d6345..fdfa200aa5 100644 --- a/crates/analyzer/src/context.rs +++ b/crates/analyzer/src/context.rs @@ -1,7 +1,8 @@ use crate::builtins::GlobalMethod; use crate::errors::{self, CannotMove, TypeError}; -use crate::namespace::items::{DiagnosticSink, EventId}; +use crate::namespace::items::{DiagnosticSink, EventId, FunctionId, Item}; use crate::namespace::types::{FixedSize, Type}; +use crate::AnalyzerDb; use fe_common::diagnostics::Diagnostic; pub use fe_common::diagnostics::Label; use fe_common::Span; @@ -26,8 +27,9 @@ impl Analysis { } pub trait AnalyzerContext { - fn resolve_type(&self, name: &str) -> Option>; + fn resolve_name(&self, name: &str) -> Option; fn add_diagnostic(&mut self, diag: Diagnostic); + fn db(&self) -> &dyn AnalyzerDb; fn error(&mut self, message: &str, label_span: Span, label: &str) -> DiagnosticVoucher { self.register_diag(errors::error(message, label_span, label)) @@ -68,12 +70,62 @@ pub trait AnalyzerContext { )) } + fn name_conflict_error( + &mut self, + name_kind: &str, // Eg "function parameter" or "variable name" + name: &str, + original: &NamedThing, + original_span: Option, + duplicate_span: Span, + ) -> DiagnosticVoucher { + self.register_diag(errors::name_conflict_error( + name_kind, + name, + original, + original_span, + duplicate_span, + )) + } + fn register_diag(&mut self, diag: Diagnostic) -> DiagnosticVoucher { self.add_diagnostic(diag); DiagnosticVoucher(PhantomData::default()) } } +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum NamedThing { + Item(Item), + Variable { + name: String, + typ: Result, + span: Span, + }, +} + +impl NamedThing { + pub fn name_span(&self, db: &dyn AnalyzerDb) -> Option { + match self { + NamedThing::Item(item) => item.name_span(db), + NamedThing::Variable { span, .. } => Some(*span), + } + } + + pub fn is_builtin(&self) -> bool { + match self { + NamedThing::Item(item) => item.is_builtin(), + NamedThing::Variable { .. } => false, + } + } + + pub fn item_kind_display_name(&self) -> &str { + match self { + NamedThing::Item(item) => item.item_kind_display_name(), + NamedThing::Variable { .. } => "variable", + } + } +} + /// This should only be created by [`AnalyzerContext`]. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct DiagnosticVoucher(PhantomData<()>); @@ -83,8 +135,11 @@ pub struct TempContext { pub diagnostics: Vec, } impl AnalyzerContext for TempContext { - fn resolve_type(&self, _name: &str) -> Option> { - panic!("TempContext can't resolve types") + fn db(&self) -> &dyn AnalyzerDb { + panic!("TempContext has no analyzer db") + } + fn resolve_name(&self, _name: &str) -> Option { + panic!("TempContext can't resolve names") } fn add_diagnostic(&mut self, diag: Diagnostic) { self.diagnostics.push(diag) @@ -189,10 +244,10 @@ impl fmt::Display for ExpressionAttributes { /// The type of a function call. #[derive(Clone, Debug, PartialEq, Eq)] pub enum CallType { - BuiltinFunction { func: GlobalMethod }, + BuiltinFunction(GlobalMethod), TypeConstructor { typ: Type }, SelfAttribute { func_name: String, self_span: Span }, - Pure { func_name: String }, + PureFunction(FunctionId), ValueAttribute, TypeAttribute { typ: Type, func_name: String }, } diff --git a/crates/analyzer/src/db.rs b/crates/analyzer/src/db.rs index 71f049eb70..d73dead4a3 100644 --- a/crates/analyzer/src/db.rs +++ b/crates/analyzer/src/db.rs @@ -1,8 +1,8 @@ use crate::context::{Analysis, FunctionBody}; use crate::errors::TypeError; use crate::namespace::items::{ - self, ContractFieldId, ContractId, EventId, FunctionId, ModuleId, StructFieldId, StructId, - TypeAliasId, TypeDefId, + self, ContractFieldId, ContractId, EventId, FunctionId, Item, ModuleId, StructFieldId, + StructId, TypeAliasId, }; use crate::namespace::types; use indexmap::IndexMap; @@ -46,17 +46,12 @@ pub trait AnalyzerDb { fn intern_event(&self, data: Rc) -> EventId; // Module - #[salsa::invoke(queries::module::module_all_type_defs)] - fn module_all_type_defs(&self, module: ModuleId) -> Rc>; - #[salsa::invoke(queries::module::module_type_def_map)] - fn module_type_def_map(&self, module: ModuleId) -> Analysis>>; - #[salsa::invoke(queries::module::module_resolve_type)] - #[salsa::cycle(queries::module::module_resolve_type_cycle)] - fn module_resolve_type( - &self, - module: ModuleId, - name: String, - ) -> Option>; + #[salsa::invoke(queries::module::module_all_items)] + fn module_all_items(&self, module: ModuleId) -> Rc>; + #[salsa::invoke(queries::module::module_item_map)] + fn module_item_map(&self, module: ModuleId) -> Analysis>>; + #[salsa::invoke(queries::module::module_imported_item_map)] + fn module_imported_item_map(&self, module: ModuleId) -> Rc>; #[salsa::invoke(queries::module::module_contracts)] fn module_contracts(&self, module: ModuleId) -> Rc>; #[salsa::invoke(queries::module::module_structs)] diff --git a/crates/analyzer/src/db/queries/contracts.rs b/crates/analyzer/src/db/queries/contracts.rs index 55223232ca..fba8fc0b5c 100644 --- a/crates/analyzer/src/db/queries/contracts.rs +++ b/crates/analyzer/src/db/queries/contracts.rs @@ -1,8 +1,7 @@ -use crate::builtins; -use crate::context::AnalyzerContext; +use crate::context::{AnalyzerContext, NamedThing}; use crate::db::{Analysis, AnalyzerDb}; use crate::errors; -use crate::namespace::items::{self, ContractFieldId, ContractId, EventId, FunctionId}; +use crate::namespace::items::{self, ContractFieldId, ContractId, EventId, FunctionId, Item}; use crate::namespace::scopes::ItemScope; use crate::namespace::types; use crate::traversal::types::type_desc; @@ -13,6 +12,7 @@ use indexmap::map::{Entry, IndexMap}; use std::rc::Rc; pub fn contract_all_functions(db: &dyn AnalyzerDb, contract: ContractId) -> Rc> { + let module = contract.module(db); let body = &contract.data(db).ast.kind.body; Rc::new( body.iter() @@ -21,7 +21,8 @@ pub fn contract_all_functions(db: &dyn AnalyzerDb, contract: ContractId) -> Rc { Some(db.intern_function(Rc::new(items::Function { ast: node.clone(), - parent: contract, + contract, + module, }))) } }) @@ -42,14 +43,25 @@ pub fn contract_function_map( if def_name == "__init__" { continue; } - if let Some(reserved) = builtins::reserved_name(def_name) { - scope.error( - &format!( - "function name conflicts with built-in {}", - reserved.as_ref() - ), + + if let Some(event) = contract.event(db, def_name) { + scope.name_conflict_error( + "function", + def_name, + &NamedThing::Item(Item::Event(event)), + Some(event.name_span(db)), + def.kind.name.span, + ); + continue; + } + + if let Some(named_item) = scope.resolve_name(def_name) { + scope.name_conflict_error( + "function", + def_name, + &named_item, + named_item.name_span(db), def.kind.name.span, - &format!("`{}` is a built-in {}", def_name, reserved.as_ref()), ); continue; } diff --git a/crates/analyzer/src/db/queries/functions.rs b/crates/analyzer/src/db/queries/functions.rs index 9cc77ed1f7..9ee84e27fd 100644 --- a/crates/analyzer/src/db/queries/functions.rs +++ b/crates/analyzer/src/db/queries/functions.rs @@ -66,7 +66,16 @@ pub fn function_signature( ))), }); - if let Some(dup_idx) = names.get(&name.kind) { + if let Some(named_item) = scope.resolve_name(&name.kind) { + scope.name_conflict_error( + "function parameter", + &name.kind, + &named_item, + named_item.name_span(db), + name.span, + ); + None + } else if let Some(dup_idx) = names.get(&name.kind) { let dup_arg: &Node = &def.args[*dup_idx]; scope.duplicate_name_error( &format!("duplicate parameter names in function `{}`", def.name.kind), diff --git a/crates/analyzer/src/db/queries/module.rs b/crates/analyzer/src/db/queries/module.rs index 11ea20da9e..c81cc3ebd0 100644 --- a/crates/analyzer/src/db/queries/module.rs +++ b/crates/analyzer/src/db/queries/module.rs @@ -3,78 +3,117 @@ use crate::context::Analysis; use crate::db::AnalyzerDb; use crate::errors; use crate::namespace::items::{ - Contract, ContractId, ModuleId, Struct, StructId, TypeAlias, TypeDefId, + Contract, ContractId, Item, ModuleId, Struct, StructId, TypeAlias, TypeDef, }; use crate::namespace::types; use fe_common::diagnostics::Label; use fe_parser::ast; +use indexmap::indexmap; use indexmap::map::{Entry, IndexMap}; use std::rc::Rc; +use strum::IntoEnumIterator; -pub fn module_all_type_defs(db: &dyn AnalyzerDb, module: ModuleId) -> Rc> { +// Placeholder; someday std::prelude will be a proper module. +fn std_prelude_items() -> IndexMap { + let mut items = indexmap! { + "bool".to_string() => Item::Type(TypeDef::Primitive(types::Base::Bool)), + "address".to_string() => Item::Type(TypeDef::Primitive(types::Base::Address)), + }; + items.extend(types::Integer::iter().map(|typ| { + ( + typ.as_ref().to_string(), + Item::Type(TypeDef::Primitive(types::Base::Numeric(typ))), + ) + })); + items.extend( + types::GenericType::iter().map(|typ| (typ.name().to_string(), Item::GenericType(typ))), + ); + items.extend( + builtins::GlobalMethod::iter() + .map(|fun| (fun.as_ref().to_string(), Item::BuiltinFunction(fun))), + ); + items.extend(builtins::Object::iter().map(|obj| (obj.as_ref().to_string(), Item::Object(obj)))); + items +} + +// This is probably too simple for real module imports +pub fn module_imported_item_map(_: &dyn AnalyzerDb, _: ModuleId) -> Rc> { + Rc::new(std_prelude_items()) +} + +pub fn module_all_items(db: &dyn AnalyzerDb, module: ModuleId) -> Rc> { let ast::Module { body } = &module.data(db).ast; - let ids = body + + let items = body .iter() .filter_map(|stmt| match stmt { - ast::ModuleStmt::TypeAlias(node) => { - Some(TypeDefId::Alias(db.intern_type_alias(Rc::new(TypeAlias { + ast::ModuleStmt::TypeAlias(node) => Some(Item::Type(TypeDef::Alias( + db.intern_type_alias(Rc::new(TypeAlias { ast: node.clone(), module, - })))) - } - ast::ModuleStmt::Contract(node) => { - Some(TypeDefId::Contract(db.intern_contract(Rc::new(Contract { + })), + ))), + ast::ModuleStmt::Contract(node) => Some(Item::Type(TypeDef::Contract( + db.intern_contract(Rc::new(Contract { name: node.name().to_string(), ast: node.clone(), module, - })))) - } - ast::ModuleStmt::Struct(node) => { - Some(TypeDefId::Struct(db.intern_struct(Rc::new(Struct { + })), + ))), + ast::ModuleStmt::Struct(node) => Some(Item::Type(TypeDef::Struct(db.intern_struct( + Rc::new(Struct { ast: node.clone(), module, - })))) - } - _ => None, + }), + )))), + ast::ModuleStmt::Pragma(_) => None, + ast::ModuleStmt::Import(_) => todo!(), }) .collect(); - Rc::new(ids) + Rc::new(items) } -pub fn module_type_def_map( +pub fn module_item_map( db: &dyn AnalyzerDb, module: ModuleId, -) -> Analysis>> { +) -> Analysis>> { let mut diagnostics = vec![]; - let mut map = IndexMap::::new(); - for def in module.all_type_defs(db).iter() { - let def_name = def.name(db); - if let Some(reserved) = builtins::reserved_name(&def_name) { + let imports = db.module_imported_item_map(module); + let mut map = IndexMap::::new(); + + for item in module.all_items(db).iter() { + let item_name = item.name(db); + if let Some(builtin) = imports.get(&item_name) { + let builtin_kind = builtin.item_kind_display_name(); diagnostics.push(errors::error( - &format!("type name conflicts with built-in {}", reserved.as_ref()), - def.name_span(db), - &format!("`{}` is a built-in {}", def_name, reserved.as_ref()), + &format!("type name conflicts with built-in {}", builtin_kind), + item.name_span(db).expect("duplicate built-in names?"), + &format!("`{}` is a built-in {}", item_name, builtin_kind), )); continue; } - match map.entry(def.name(db)) { + match map.entry(item_name) { Entry::Occupied(entry) => { diagnostics.push(errors::fancy_error( "duplicate type name", vec![ Label::primary( - entry.get().span(db), + entry.get().name_span(db).unwrap(), format!("`{}` first defined here", entry.key()), ), - Label::secondary(def.span(db), format!("`{}` redefined here", entry.key())), + Label::secondary( + item.name_span(db) + .expect("built-in conflicts with user-defined name?"), + format!("`{}` redefined here", entry.key()), + ), ], vec![], )); } Entry::Vacant(entry) => { - entry.insert(*def); + entry.insert(*item); } } } @@ -84,36 +123,13 @@ pub fn module_type_def_map( } } -pub fn module_resolve_type( - db: &dyn AnalyzerDb, - module: ModuleId, - name: String, -) -> Option> { - Some(module.type_defs(db).get(&name)?.typ(db)) -} - -#[allow(clippy::ptr_arg)] -pub fn module_resolve_type_cycle( - _db: &dyn AnalyzerDb, - _cycle: &[String], - _module: &ModuleId, - _name: &String, -) -> Option> { - // The only possible type cycle currently is a recursive type alias, - // which is handled in queries/types.rs - // However, salsa will also call this function if there's such a cycle, - // so we can't panic here, and we can't return a TypeError because - // there's no way to emit a diagnostic! The only option is to return - // None, and handle type cycles in more specifc cycle handlers. - None -} - pub fn module_contracts(db: &dyn AnalyzerDb, module: ModuleId) -> Rc> { - let defs = module.all_type_defs(db); Rc::new( - defs.iter() - .filter_map(|def| match def { - TypeDefId::Contract(id) => Some(*id), + module + .all_items(db) + .iter() + .filter_map(|item| match item { + Item::Type(TypeDef::Contract(id)) => Some(*id), _ => None, }) .collect(), @@ -121,11 +137,12 @@ pub fn module_contracts(db: &dyn AnalyzerDb, module: ModuleId) -> Rc Rc> { - let defs = module.all_type_defs(db); Rc::new( - defs.iter() - .filter_map(|def| match def { - TypeDefId::Struct(id) => Some(*id), + module + .all_items(db) + .iter() + .filter_map(|item| match item { + Item::Type(TypeDef::Struct(id)) => Some(*id), _ => None, }) .collect(), diff --git a/crates/analyzer/src/errors.rs b/crates/analyzer/src/errors.rs index 5fc2c2f460..ee75b9b272 100644 --- a/crates/analyzer/src/errors.rs +++ b/crates/analyzer/src/errors.rs @@ -1,6 +1,6 @@ //! Semantic errors. -use crate::context::DiagnosticVoucher; +use crate::context::{DiagnosticVoucher, NamedThing}; use fe_common::diagnostics::{Diagnostic, Label, Severity}; use fe_common::Span; use std::fmt::Display; @@ -145,3 +145,45 @@ pub fn duplicate_name_error( vec![], ) } + +pub fn name_conflict_error( + name_kind: &str, // Eg "function parameter" or "variable name" + name: &str, + original: &NamedThing, + original_span: Option, + duplicate_span: Span, +) -> Diagnostic { + if let Some(original_span) = original_span { + fancy_error( + &format!( + "{} name `{}` conflicts with previously defined {}", + name_kind, + name, + original.item_kind_display_name() + ), + vec![ + Label::primary(original_span, format!("`{}` first defined here", name)), + Label::secondary(duplicate_span, format!("`{}` redefined here", name)), + ], + vec![], + ) + } else { + fancy_error( + &format!( + "{} name `{}` conflicts with built-in {}", + name_kind, + name, + original.item_kind_display_name() + ), + vec![Label::primary( + duplicate_span, + format!( + "`{}` is a built-in {}", + name, + original.item_kind_display_name() + ), + )], + vec![], + ) + } +} diff --git a/crates/analyzer/src/namespace/items.rs b/crates/analyzer/src/namespace/items.rs index e6c56a661f..6520a6b469 100644 --- a/crates/analyzer/src/namespace/items.rs +++ b/crates/analyzer/src/namespace/items.rs @@ -1,8 +1,8 @@ +use crate::builtins; use crate::context; use crate::errors::{self, TypeError}; use crate::impl_intern_key; -use crate::namespace::types; -use crate::namespace::types::SelfDecl; +use crate::namespace::types::{self, GenericType, SelfDecl}; use crate::traversal::pragma::check_pragma_version; use crate::AnalyzerDb; use fe_common::diagnostics::Diagnostic; @@ -11,19 +11,87 @@ use fe_parser::node::{Node, Span}; use indexmap::IndexMap; use std::rc::Rc; -pub trait DiagnosticSink { - fn push(&mut self, diag: &Diagnostic); - fn push_all<'a>(&mut self, iter: impl Iterator) { - iter.for_each(|diag| self.push(diag)) - } +/// A named item. This does not include things inside of +/// a function body. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum Item { + // Module // TODO: modules don't have names yet + // Constant // TODO: when `const` is implemented + Type(TypeDef), + // GenericType probably shouldn't be a separate category. + // Any of the items inside TypeDef (struct, alias, etc) + // could be optionally generic. + GenericType(GenericType), + // Events aren't normal types; they *could* be moved into + // TypeDef, but it would have consequences. + Event(EventId), + Function(FunctionId), + + // Needed until we can represent keccak256 as a FunctionId. + // We can't represent keccak256's arg type yet. + BuiltinFunction(builtins::GlobalMethod), + + // This should go away soon. The globals (block, msg, etc) will be replaced + // with a context struct that'll appear in the fn parameter list. + // `self` should just be removed from here and handled as a special parameter. + Object(builtins::Object), } -impl DiagnosticSink for Vec { - fn push(&mut self, diag: &Diagnostic) { - self.push(diag.clone()) +impl Item { + pub fn name(&self, db: &dyn AnalyzerDb) -> String { + match self { + Item::Type(id) => id.name(db), + Item::GenericType(id) => id.name().to_string(), + Item::Event(id) => id.name(db), + Item::Function(id) => id.name(db), + Item::BuiltinFunction(id) => id.as_ref().to_string(), + Item::Object(id) => id.as_ref().to_string(), + } } - fn push_all<'a>(&mut self, iter: impl Iterator) { - self.extend(iter.cloned()) + + pub fn name_span(&self, db: &dyn AnalyzerDb) -> Option { + match self { + Item::Type(id) => id.name_span(db), + Item::GenericType(_) => None, + Item::Event(id) => Some(id.name_span(db)), + Item::Function(id) => Some(id.name_span(db)), + Item::BuiltinFunction(_) => None, + Item::Object(_) => None, + } + } + + pub fn is_builtin(&self) -> bool { + match self { + Item::Type(TypeDef::Primitive(_)) => true, + Item::Type(_) => false, + Item::GenericType(_) => true, + Item::Event(_) => false, + Item::Function(_) => false, + Item::BuiltinFunction(_) => true, + Item::Object(_) => true, + } + } + + pub fn item_kind_display_name(&self) -> &'static str { + match self { + Item::Type(_) => "type", + Item::GenericType(_) => "type", + Item::Event(_) => "event", + Item::Function(_) => "function", + Item::BuiltinFunction(_) => "function", + Item::Object(_) => "object", + } + } + + pub fn sink_diagnostics(&self, db: &dyn AnalyzerDb, sink: &mut impl DiagnosticSink) { + match self { + Item::Type(id) => id.sink_diagnostics(db, sink), + Item::GenericType(_) => {} + Item::Event(id) => id.sink_diagnostics(db, sink), + Item::Function(id) => id.sink_diagnostics(db, sink), + Item::BuiltinFunction(_) => {} + Item::Object(_) => {} + } } } @@ -43,23 +111,25 @@ impl ModuleId { db.lookup_intern_module(*self) } - /// Maps defined type name to its [`TypeDefId`]. - /// Type defs include structs, type aliases, and contracts. - pub fn type_defs(&self, db: &dyn AnalyzerDb) -> Rc> { - db.module_type_def_map(*self).value + /// Returns a map of the named items in the module + pub fn items(&self, db: &dyn AnalyzerDb) -> Rc> { + db.module_item_map(*self).value } - /// Includes type defs with duplicate names - pub fn all_type_defs(&self, db: &dyn AnalyzerDb) -> Rc> { - db.module_all_type_defs(*self) + /// Includes duplicate names + pub fn all_items(&self, db: &dyn AnalyzerDb) -> Rc> { + db.module_all_items(*self) } - pub fn resolve_type( - &self, - db: &dyn AnalyzerDb, - name: &str, - ) -> Option> { - db.module_resolve_type(*self, name.into()) + pub fn imported_items(&self, db: &dyn AnalyzerDb) -> Rc> { + db.module_imported_item_map(*self) + } + + pub fn resolve_name(&self, db: &dyn AnalyzerDb, name: &str) -> Option { + self.items(db) + .get(name) + .copied() + .or_else(|| self.imported_items(db).get(name).copied()) } /// All contracts, including duplicates @@ -93,68 +163,65 @@ impl ModuleId { } } - // duplicate type name errors - sink.push_all(db.module_type_def_map(*self).diagnostics.iter()); + // duplicate item name errors + sink.push_all(db.module_item_map(*self).diagnostics.iter()); - // errors for each type def - self.all_type_defs(db) + // errors for each item + self.all_items(db) .iter() .for_each(|id| id.sink_diagnostics(db, sink)); } } #[derive(Debug, PartialEq, Eq, Copy, Clone)] -pub enum TypeDefId { +pub enum TypeDef { Alias(TypeAliasId), Struct(StructId), Contract(ContractId), + Primitive(types::Base), // Event(EventDefId), } -impl TypeDefId { +impl TypeDef { pub fn name(&self, db: &dyn AnalyzerDb) -> String { match self { - TypeDefId::Alias(id) => id.data(db).ast.name().to_string(), - TypeDefId::Struct(id) => id.data(db).ast.name().to_string(), - TypeDefId::Contract(id) => id.data(db).ast.name().to_string(), - } - } - - pub fn name_span(&self, db: &dyn AnalyzerDb) -> Span { - match self { - TypeDefId::Alias(id) => id.data(db).ast.kind.name.span, - TypeDefId::Struct(id) => id.data(db).ast.kind.name.span, - TypeDefId::Contract(id) => id.data(db).ast.kind.name.span, + TypeDef::Alias(id) => id.name(db), + TypeDef::Struct(id) => id.name(db), + TypeDef::Contract(id) => id.name(db), + TypeDef::Primitive(typ) => typ.to_string(), } } - pub fn span(&self, db: &dyn AnalyzerDb) -> Span { + pub fn name_span(&self, db: &dyn AnalyzerDb) -> Option { match self { - TypeDefId::Alias(id) => id.data(db).ast.span, - TypeDefId::Struct(id) => id.data(db).ast.span, - TypeDefId::Contract(id) => id.data(db).ast.span, + TypeDef::Alias(id) => Some(id.name_span(db)), + TypeDef::Struct(id) => Some(id.name_span(db)), + TypeDef::Contract(id) => Some(id.name_span(db)), + TypeDef::Primitive(_) => None, } } pub fn typ(&self, db: &dyn AnalyzerDb) -> Result { match self { - TypeDefId::Alias(id) => id.typ(db), - TypeDefId::Struct(id) => Ok(types::Type::Struct(types::Struct { + TypeDef::Alias(id) => id.typ(db), + TypeDef::Struct(id) => Ok(types::Type::Struct(types::Struct { id: *id, name: id.name(db), field_count: id.fields(db).len(), // for the EvmSized trait })), - TypeDefId::Contract(id) => Ok(types::Type::Contract(types::Contract { + TypeDef::Contract(id) => Ok(types::Type::Contract(types::Contract { id: *id, name: id.name(db), })), + TypeDef::Primitive(base) => Ok(types::Type::Base(*base)), } } pub fn sink_diagnostics(&self, db: &dyn AnalyzerDb, sink: &mut impl DiagnosticSink) { match self { - TypeDefId::Alias(id) => id.sink_diagnostics(db, sink), - TypeDefId::Struct(id) => id.sink_diagnostics(db, sink), - TypeDefId::Contract(id) => id.sink_diagnostics(db, sink), + TypeDef::Alias(id) => id.sink_diagnostics(db, sink), + TypeDef::Struct(id) => id.sink_diagnostics(db, sink), + TypeDef::Contract(id) => id.sink_diagnostics(db, sink), + TypeDef::Primitive(_) => {} } } } @@ -176,6 +243,12 @@ impl TypeAliasId { pub fn span(&self, db: &dyn AnalyzerDb) -> Span { self.data(db).ast.span } + pub fn name(&self, db: &dyn AnalyzerDb) -> String { + self.data(db).ast.name().to_string() + } + pub fn name_span(&self, db: &dyn AnalyzerDb) -> Span { + self.data(db).ast.kind.name.span + } pub fn typ(&self, db: &dyn AnalyzerDb) -> Result { db.type_alias_type(*self).value } @@ -198,12 +271,19 @@ pub struct Contract { pub struct ContractId(pub(crate) u32); impl_intern_key!(ContractId); impl ContractId { - pub fn name(&self, db: &dyn AnalyzerDb) -> String { - self.data(db).name.clone() - } pub fn data(&self, db: &dyn AnalyzerDb) -> Rc { db.lookup_intern_contract(*self) } + pub fn span(&self, db: &dyn AnalyzerDb) -> Span { + self.data(db).ast.span + } + pub fn name(&self, db: &dyn AnalyzerDb) -> String { + self.data(db).ast.name().to_string() + } + pub fn name_span(&self, db: &dyn AnalyzerDb) -> Span { + self.data(db).ast.kind.name.span + } + pub fn module(&self, db: &dyn AnalyzerDb) -> ModuleId { self.data(db).module } @@ -227,6 +307,12 @@ impl ContractId { Some((field.typ(db), index)) } + pub fn resolve_name(&self, db: &dyn AnalyzerDb, name: &str) -> Option { + self.pure_function(db, name) + .map(Item::Function) + .or_else(|| self.event(db, name).map(Item::Event)) + } + pub fn init_function(&self, db: &dyn AnalyzerDb) -> Option { db.contract_init_function(*self).value } @@ -340,45 +426,44 @@ impl ContractFieldId { #[derive(Debug, PartialEq, Eq, Hash, Clone)] pub struct Function { pub ast: Node, - pub parent: ContractId, + pub module: ModuleId, + pub contract: ContractId, } #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] pub struct FunctionId(pub(crate) u32); impl_intern_key!(FunctionId); impl FunctionId { + pub fn data(&self, db: &dyn AnalyzerDb) -> Rc { + db.lookup_intern_function(*self) + } + pub fn span(&self, db: &dyn AnalyzerDb) -> Span { + self.data(db).ast.span + } pub fn name(&self, db: &dyn AnalyzerDb) -> String { self.data(db).ast.name().to_string() } - + pub fn name_span(&self, db: &dyn AnalyzerDb) -> Span { + self.data(db).ast.kind.name.span + } pub fn is_public(&self, db: &dyn AnalyzerDb) -> bool { self.data(db).ast.kind.is_pub } - pub fn is_pure(&self, db: &dyn AnalyzerDb) -> bool { self.signature(db).self_decl == SelfDecl::None } - - pub fn data(&self, db: &dyn AnalyzerDb) -> Rc { - db.lookup_intern_function(*self) - } - - pub fn parent(&self, db: &dyn AnalyzerDb) -> ContractId { - self.data(db).parent + pub fn contract(&self, db: &dyn AnalyzerDb) -> ContractId { + self.data(db).contract } - pub fn module(&self, db: &dyn AnalyzerDb) -> ModuleId { - self.parent(db).module(db) + self.data(db).module } - pub fn signature(&self, db: &dyn AnalyzerDb) -> Rc { db.function_signature(*self).value } - pub fn body(&self, db: &dyn AnalyzerDb) -> Rc { db.function_body(*self).value } - pub fn sink_diagnostics(&self, db: &dyn AnalyzerDb, sink: &mut impl DiagnosticSink) { sink.push_all(db.function_signature(*self).diagnostics.iter()); sink.push_all(db.function_body(*self).diagnostics.iter()); @@ -398,9 +483,15 @@ impl StructId { pub fn data(&self, db: &dyn AnalyzerDb) -> Rc { db.lookup_intern_struct(*self) } + pub fn span(&self, db: &dyn AnalyzerDb) -> Span { + self.data(db).ast.span + } pub fn name(&self, db: &dyn AnalyzerDb) -> String { self.data(db).ast.name().to_string() } + pub fn name_span(&self, db: &dyn AnalyzerDb) -> Span { + self.data(db).ast.kind.name.span + } pub fn module(&self, db: &dyn AnalyzerDb) -> ModuleId { self.data(db).module } @@ -462,18 +553,38 @@ pub struct EventId(pub(crate) u32); impl_intern_key!(EventId); impl EventId { + pub fn name(&self, db: &dyn AnalyzerDb) -> String { + self.data(db).ast.name().to_string() + } + pub fn name_span(&self, db: &dyn AnalyzerDb) -> Span { + self.data(db).ast.kind.name.span + } pub fn data(&self, db: &dyn AnalyzerDb) -> Rc { db.lookup_intern_event(*self) } - pub fn typ(&self, db: &dyn AnalyzerDb) -> Rc { db.event_type(*self).value } pub fn module(&self, db: &dyn AnalyzerDb) -> ModuleId { self.data(db).contract.module(db) } - pub fn sink_diagnostics(&self, db: &dyn AnalyzerDb, sink: &mut impl DiagnosticSink) { sink.push_all(db.event_type(*self).diagnostics.iter()); } } + +pub trait DiagnosticSink { + fn push(&mut self, diag: &Diagnostic); + fn push_all<'a>(&mut self, iter: impl Iterator) { + iter.for_each(|diag| self.push(diag)) + } +} + +impl DiagnosticSink for Vec { + fn push(&mut self, diag: &Diagnostic) { + self.push(diag.clone()) + } + fn push_all<'a>(&mut self, iter: impl Iterator) { + self.extend(iter.cloned()) + } +} diff --git a/crates/analyzer/src/namespace/scopes.rs b/crates/analyzer/src/namespace/scopes.rs index ba202c564a..5724e3e1ec 100644 --- a/crates/analyzer/src/namespace/scopes.rs +++ b/crates/analyzer/src/namespace/scopes.rs @@ -1,9 +1,8 @@ #![allow(unstable_name_collisions)] // expect_none, which ain't gonna be stabilized -use crate::builtins; -use crate::context::{AnalyzerContext, CallType, ExpressionAttributes, FunctionBody}; +use crate::context::{AnalyzerContext, CallType, ExpressionAttributes, FunctionBody, NamedThing}; use crate::errors::{AlreadyDefined, TypeError}; -use crate::namespace::items::{EventId, FunctionId, ModuleId}; +use crate::namespace::items::{ContractId, EventId, FunctionId, Item, ModuleId}; use crate::namespace::types::{FixedSize, Type}; use crate::AnalyzerDb; use fe_common::diagnostics::Diagnostic; @@ -29,8 +28,13 @@ impl<'a> ItemScope<'a> { } impl<'a> AnalyzerContext for ItemScope<'a> { - fn resolve_type(&self, name: &str) -> Option> { - self.module.resolve_type(self.db, name) + fn db(&self) -> &dyn AnalyzerDb { + self.db + } + fn resolve_name(&self, name: &str) -> Option { + self.module + .resolve_name(self.db, name) + .map(NamedThing::Item) } fn add_diagnostic(&mut self, diag: Diagnostic) { self.diagnostics.push(diag) @@ -73,23 +77,19 @@ impl<'a> FunctionScope<'a> { } pub fn contract_field(&self, name: &str) -> Option<(Result, usize)> { - self.function.parent(self.db).field_type(self.db, name) + self.function.contract(self.db).field_type(self.db, name) } pub fn contract_function(&self, name: &str) -> Option { - self.function.parent(self.db).function(self.db, name) - } - - pub fn self_contract_function(&self, name: &str) -> Option { - self.function.parent(self.db).self_function(self.db, name) + self.function.contract(self.db).function(self.db, name) } pub fn pure_contract_function(&self, name: &str) -> Option { - self.function.parent(self.db).pure_function(self.db, name) + self.function.contract(self.db).pure_function(self.db, name) } - pub fn resolve_event(&self, name: &str) -> Option { - self.function.parent(self.db).event(self.db, name) + pub fn self_contract_function(&self, name: &str) -> Option { + self.function.contract(self.db).self_function(self.db, name) } pub fn function_return_type(&self) -> Result { @@ -167,12 +167,51 @@ impl<'a> FunctionScope<'a> { } impl<'a> AnalyzerContext for FunctionScope<'a> { + fn db(&self) -> &dyn AnalyzerDb { + self.db + } + fn add_diagnostic(&mut self, diag: Diagnostic) { self.diagnostics.borrow_mut().push(diag) } - fn resolve_type(&self, name: &str) -> Option> { - self.function.module(self.db).resolve_type(self.db, name) + fn resolve_name(&self, name: &str) -> Option { + // Getting param names and spans should be simpler + self.function + .signature(self.db) + .params + .iter() + .find_map(|param| { + (param.name == name).then(|| { + let span = self + .function + .data(self.db) + .ast + .kind + .args + .iter() + .find_map(|param| (param.name() == name).then(|| param.name_span())) + .expect("found param type but not span"); + + NamedThing::Variable { + name: name.to_string(), + typ: param.typ.clone(), + span, + } + }) + }) + .or_else(|| { + self.function + .contract(self.db) + .resolve_name(self.db, name) + .map(NamedThing::Item) + }) + .or_else(|| { + self.function + .module(self.db) + .resolve_name(self.db, name) + .map(NamedThing::Item) + }) } } @@ -191,8 +230,28 @@ pub enum BlockScopeType { } impl AnalyzerContext for BlockScope<'_, '_> { - fn resolve_type(&self, name: &str) -> Option> { - self.root.resolve_type(name) + fn db(&self) -> &dyn AnalyzerDb { + self.root.db + } + fn resolve_name(&self, name: &str) -> Option { + self.variable_defs + .get(name) + .map(|(typ, span)| NamedThing::Variable { + name: name.to_string(), + typ: Ok(typ.clone()), + span: *span, + }) + .or_else(|| { + if let Some(parent) = self.parent { + parent.resolve_name(name) + } else { + // If there's no parent block, check the contract and module + self.contract() + .pure_function(self.db(), name) + .map(|id| NamedThing::Item(Item::Function(id))) + .or_else(|| self.root.resolve_name(name)) + } + }) } fn add_diagnostic(&mut self, diag: Diagnostic) { self.root.diagnostics.borrow_mut().push(diag) @@ -218,29 +277,16 @@ impl<'a, 'b> BlockScope<'a, 'b> { } } - pub fn contract_name(&self) -> String { - self.root.function.parent(self.root.db).name(self.root.db) - } - - pub fn db(&self) -> &dyn AnalyzerDb { - self.root.db + pub fn contract(&self) -> ContractId { + self.root.function.contract(self.root.db) } - /// Lookup a definition in current or inherited block scope - pub fn var_type(&self, name: &str) -> Option> { - self.variable_defs - .get(name) - .map(|(typ, _span)| Ok(typ.clone())) - .or_else(|| self.parent?.var_type(name)) - .or_else(|| self.root.var_type(name)) + pub fn module(&self) -> ModuleId { + self.root.function.module(self.root.db) } - pub fn var_def_span(&self, name: &str) -> Option { - self.variable_defs - .get(name) - .map(|(_typ, span)| *span) - .or_else(|| self.parent?.var_def_span(name)) - .or_else(|| self.root.var_def_span(name)) + pub fn contract_name(&self) -> String { + self.root.function.contract(self.root.db).name(self.root.db) } /// Add a variable to the block scope. @@ -250,33 +296,36 @@ impl<'a, 'b> BlockScope<'a, 'b> { typ: FixedSize, span: Span, ) -> Result<(), AlreadyDefined> { - if let Some(reserved) = builtins::reserved_name(name) { - self.error( - &format!( - "variable name conflicts with built-in {}", - reserved.as_ref() - ), - span, - &format!("`{}` is a built-in {}", name, reserved.as_ref()), - ); - return Err(AlreadyDefined); - } - - // It's (currently) an error to shadow a variable in a nested scope - match self.var_def_span(name) { - Some(prev_span) => { + if let Some(named_item) = self.resolve_name(name) { + if named_item.is_builtin() { + self.error( + &format!( + "variable name conflicts with built-in {}", + named_item.item_kind_display_name(), + ), + span, + &format!( + "`{}` is a built-in {}", + name, + named_item.item_kind_display_name() + ), + ); + return Err(AlreadyDefined); + } else { + // It's (currently) an error to shadow a variable in a nested scope self.duplicate_name_error( &format!("duplicate definition of variable `{}`", name), name, - prev_span, + named_item + .name_span(self.db()) + .expect("missing name_span of non-builtin"), span, ); - Err(AlreadyDefined) - } - None => { - self.variable_defs.insert(name.to_string(), (typ, span)); - Ok(()) } + Err(AlreadyDefined) + } else { + self.variable_defs.insert(name.to_string(), (typ, span)); + Ok(()) } } diff --git a/crates/analyzer/src/namespace/types.rs b/crates/analyzer/src/namespace/types.rs index 5aeb02d266..98cb252baa 100644 --- a/crates/analyzer/src/namespace/types.rs +++ b/crates/analyzer/src/namespace/types.rs @@ -6,7 +6,7 @@ use num_traits::ToPrimitive; use std::convert::TryFrom; use std::fmt; use std::str::FromStr; -use strum::{EnumString, IntoStaticStr}; +use strum::{AsRefStr, EnumIter, EnumString}; use vec1::Vec1; pub fn u256_min() -> BigInt { @@ -52,7 +52,7 @@ pub enum FixedSize { Struct(Struct), } -#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum Base { Numeric(Integer), Bool, @@ -60,7 +60,7 @@ pub enum Base { Unit, } -#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash, IntoStaticStr, EnumString)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, AsRefStr, EnumString, EnumIter)] #[strum(serialize_all = "snake_case")] pub enum Integer { U256, @@ -79,7 +79,7 @@ pub enum Integer { pub const U256: Base = Base::Numeric(Integer::U256); -#[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Array { pub size: usize, pub inner: Base, @@ -133,6 +133,77 @@ pub struct FunctionParam { pub typ: Result, } +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, EnumString, AsRefStr, EnumIter)] +pub enum GenericType { + // Array, // TODO: when array syntax is changed + String, + Map, +} + +impl GenericType { + pub fn name(&self) -> &str { + self.as_ref() + } + pub fn params(&self) -> Vec { + match self { + GenericType::String => vec![GenericParam { + name: "max size".into(), + kind: GenericParamKind::Int, + }], + GenericType::Map => vec![ + GenericParam { + name: "key".into(), + kind: GenericParamKind::PrimitiveType, + }, + GenericParam { + name: "value".into(), + kind: GenericParamKind::AnyType, + }, + ], + } + } + + // see traversal::types::apply_generic_type_args for error checking + pub fn apply(&self, args: &[GenericArg]) -> Option { + match self { + GenericType::String => match args { + [GenericArg::Int(max_size)] => Some(Type::String(FeString { + max_size: *max_size, + })), + _ => None, + }, + GenericType::Map => match args { + [GenericArg::Type(key), GenericArg::Type(value)] => Some(Type::Map(Map { + key: key.as_primitive()?, + value: Box::new(value.clone()), + })), + _ => None, + }, + } + } +} + +pub struct GenericParam { + pub name: String, + pub kind: GenericParamKind, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum GenericParamKind { + Int, + + // Ideally these would be represented as trait constraints. + PrimitiveType, + // FixedSizeType, // not needed yet + AnyType, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum GenericArg { + Int(usize), + Type(Type), +} + impl Integer { /// Returns `true` if the integer is signed, otherwise `false` pub fn is_signed(&self) -> bool { @@ -268,6 +339,7 @@ pub trait TypeDowncast { fn as_string(&self) -> Option<&FeString>; fn as_map(&self) -> Option<&Map>; fn as_int(&self) -> Option; + fn as_primitive(&self) -> Option; } impl TypeDowncast for Type { @@ -301,6 +373,12 @@ impl TypeDowncast for Type { _ => None, } } + fn as_primitive(&self) -> Option { + match self { + Type::Base(base) => Some(*base), + _ => None, + } + } } impl TypeDowncast for Option<&Type> { @@ -319,6 +397,9 @@ impl TypeDowncast for Option<&Type> { fn as_int(&self) -> Option { self.and_then(|t| t.as_int()) } + fn as_primitive(&self) -> Option { + self.and_then(|t| t.as_primitive()) + } } impl From for Type { diff --git a/crates/analyzer/src/traversal/call_args.rs b/crates/analyzer/src/traversal/call_args.rs index cd87a66368..32d284d8df 100644 --- a/crates/analyzer/src/traversal/call_args.rs +++ b/crates/analyzer/src/traversal/call_args.rs @@ -57,7 +57,7 @@ pub fn validate_named_args( params: &[impl LabeledParameter], label_policy: LabelPolicy, ) -> Result<(), FatalError> { - validate_arg_count(scope, name, name_span, args, params.len()); + validate_arg_count(scope, name, name_span, args, params.len(), "argument"); validate_arg_labels(scope, args, params, label_policy); validate_arg_types(scope, name, args, params)?; Ok(()) @@ -69,6 +69,7 @@ pub fn validate_arg_count( name_span: Span, args: &Node>, param_count: usize, + argument_word: &str, ) -> Option { if args.kind.len() != param_count { let mut labels = vec![Label::primary( @@ -76,7 +77,7 @@ pub fn validate_arg_count( format!( "expects {} {}", param_count, - pluralize_conditionally("argument", param_count) + pluralize_conditionally(argument_word, param_count) ), )]; if args.kind.is_empty() { @@ -88,7 +89,7 @@ pub fn validate_arg_count( labels.last_mut().unwrap().message = format!( "supplied {} {}", args.kind.len(), - pluralize_conditionally("argument", args.kind.len()) + pluralize_conditionally(argument_word, args.kind.len()) ); } @@ -97,7 +98,7 @@ pub fn validate_arg_count( "`{}` expects {} {}, but {} {} provided", name, param_count, - pluralize_conditionally("argument", param_count), + pluralize_conditionally(argument_word, param_count), args.kind.len(), pluralize_conditionally(("was", "were"), args.kind.len()) ), diff --git a/crates/analyzer/src/traversal/expressions.rs b/crates/analyzer/src/traversal/expressions.rs index d7d54e5bea..f26b7094ba 100644 --- a/crates/analyzer/src/traversal/expressions.rs +++ b/crates/analyzer/src/traversal/expressions.rs @@ -2,15 +2,16 @@ use crate::builtins::{ BlockField, ChainField, ContractTypeMethod, GlobalMethod, MsgField, Object, SelfField, TxField, ValueMethod, }; -use crate::context::{AnalyzerContext, CallType, ExpressionAttributes, Location}; +use crate::context::{AnalyzerContext, CallType, ExpressionAttributes, Location, NamedThing}; use crate::errors::{FatalError, IndexingError, NotFixedSize}; +use crate::namespace::items::{FunctionId, Item}; use crate::namespace::scopes::BlockScope; use crate::namespace::types::{ Array, Base, Contract, FeString, Integer, SelfDecl, Struct, Tuple, Type, TypeDowncast, U256, }; use crate::operations; use crate::traversal::call_args::{validate_arg_count, validate_named_args, LabelPolicy}; -use crate::traversal::types::resolve_type_name; +use crate::traversal::types::apply_generic_type_args; use crate::traversal::utils::{add_bin_operations_errors, types_to_fixed_sizes}; use fe_common::diagnostics::Label; use fe_common::numeric; @@ -250,14 +251,44 @@ fn expr_name( fe::Expr::Name(name) => name, _ => unreachable!(), }; - match scope.var_type(name) { - Some(Ok(typ)) => { + + match scope.resolve_name(name) { + Some(NamedThing::Variable { typ, .. }) => { + let typ = typ?; let location = Location::assign_location(&typ); Ok(ExpressionAttributes::new(typ.into(), location)) } - Some(Err(err)) => Err(err.into()), + Some(item) => { + let item_kind = item.item_kind_display_name(); + let diag = if let Some(def_span) = item.name_span(scope.db()) { + scope.fancy_error( + &format!( + "`{}` is a {} name, and can't be used as an expression", + name, item_kind + ), + vec![ + Label::primary( + def_span, + &format!("`{}` is defined here as a {}", name, item_kind), + ), + Label::primary(exp.span, &format!("`{}` is used here as a value", name)), + ], + vec![], + ) + } else { + scope.error( + &format!( + "`{}` is a built-in {} name, and can't be used as an expression", + name, item_kind + ), + exp.span, + &format!("`{}` is used here as a value", name), + ) + }; + Err(FatalError::new(diag)) + } None => { - let voucher = scope.error( + let diag = scope.error( &format!("cannot find value `{}` in this scope", name), exp.span, "undefined", @@ -266,12 +297,10 @@ fn expr_name( Some(typ) => Ok(ExpressionAttributes::new( typ.clone(), Location::assign_location( - &typ.clone() - .try_into() - .map_err(|_| FatalError::new(voucher))?, + &typ.clone().try_into().map_err(|_| FatalError::new(diag))?, ), )), - None => Err(FatalError::new(voucher)), + None => Err(FatalError::new(diag)), } } } @@ -685,7 +714,7 @@ fn expr_call( } = &exp.kind { return match expr_call_type(scope, func, generic_args.as_ref())? { - CallType::BuiltinFunction { func: builtin } => { + CallType::BuiltinFunction(builtin) => { expr_call_builtin_function(scope, builtin, func.span, args) } CallType::TypeConstructor { typ } => { @@ -695,7 +724,7 @@ fn expr_call( func_name, self_span, } => expr_call_self_attribute(scope, &func_name, func.span, self_span, args), - CallType::Pure { func_name } => expr_call_pure(scope, &func_name, func.span, args), + CallType::PureFunction(func_id) => expr_call_pure(scope, func_id, args), CallType::ValueAttribute => expr_call_value_attribute(scope, func, args), CallType::TypeAttribute { typ, func_name } => { expr_call_type_attribute(scope, typ, &func_name, func.span, args) @@ -715,7 +744,7 @@ fn expr_call_builtin_function( let argument_attributes = expr_call_args(scope, args)?; match function { GlobalMethod::Keccak256 => { - validate_arg_count(scope, function.into(), name_span, args, 1); + validate_arg_count(scope, function.as_ref(), name_span, args, 1, "argument"); expect_no_label_on_first_arg(scope, args); if let Some(arg_typ) = argument_attributes.first().map(|attr| &attr.typ) { @@ -726,14 +755,17 @@ fn expr_call_builtin_function( .. }) ) { - let fn_name: &str = function.into(); scope.fancy_error( &format!( "`{}` can not be used as an argument to `{}`", - arg_typ, fn_name, + arg_typ, + function.as_ref(), ), vec![Label::primary(args.span, "wrong type")], - vec![format!("Note: `{}` expects a byte array argument", fn_name)], + vec![format!( + "Note: `{}` expects a byte array argument", + function.as_ref() + )], ); } }; @@ -809,7 +841,7 @@ fn expr_call_type_constructor( } // These all expect 1 arg, for now. - validate_arg_count(scope, &format!("{}", typ), name_span, args, 1); + validate_arg_count(scope, &format!("{}", typ), name_span, args, 1, "argument"); expect_no_label_on_first_arg(scope, args); match &typ { @@ -961,48 +993,29 @@ fn expr_call_self_attribute( fn expr_call_pure( scope: &mut BlockScope, - func_name: &str, - name_span: Span, + function: FunctionId, args: &Node>>, ) -> Result { - check_for_call_to_init_fn(scope, func_name, name_span)?; + assert!(function.is_pure(scope.db())); - if let Some(func) = scope.root.pure_contract_function(func_name) { - let sig = func.signature(scope.root.db); - validate_named_args( - scope, - func_name, - name_span, - args, - &sig.params, - LabelPolicy::AllowAnyUnlabeled, - )?; + let fn_name = function.name(scope.db()); + let name_span = function.name_span(scope.db()); + let sig = function.signature(scope.db()); + validate_named_args( + scope, + &fn_name, + name_span, + args, + &sig.params, + LabelPolicy::AllowAnyUnlabeled, + )?; - let return_type = sig.return_type.clone()?; - let return_location = Location::assign_location(&return_type); - Ok(ExpressionAttributes::new( - return_type.into(), - return_location, - )) - } else { - let voucher = if scope.root.self_contract_function(func_name).is_none() { - scope.fancy_error( - &format!("no function named `{}` exists in this contract", func_name), - vec![Label::primary(name_span, "undefined function")], - vec![], - ) - } else { - scope.fancy_error( - &format!("`{}` must be called using `self`", func_name), - vec![Label::primary(name_span, "function takes self")], - vec![format!( - "Suggestion: try `self.{}(...)` instead of `{}(...)`", - func_name, func_name - )], - ) - }; - Err(FatalError::new(voucher)) - } + let return_type = sig.return_type.clone()?; + let return_location = Location::assign_location(&return_type); + Ok(ExpressionAttributes::new( + return_type.into(), + return_location, + )) } fn expr_call_value_attribute( @@ -1037,8 +1050,8 @@ fn expr_call_value_attribute( ); } - // for now all of these function expect 0 arguments - validate_arg_count(scope, &attr.kind, attr.span, args, 0); + // for now all of these functions expect 0 arguments + validate_arg_count(scope, &attr.kind, attr.span, args, 0, "argument"); return match ValueMethod::from_str(&attr.kind) { Err(_) => { @@ -1097,10 +1110,10 @@ fn expr_call_value_attribute( scope.fancy_error( "`to_mem()` called on value in memory", vec![ - Label::primary(value.span, "this value is in storage"), + Label::primary(value.span, "this value is already in memory"), Label::secondary( attr.span, - "hint: to make a copy, use `.to_mem()` here", + "hint: to make a copy, use `.clone()` here", ), ], vec![], @@ -1151,7 +1164,7 @@ fn expr_call_value_attribute( _ => { scope.fancy_error( &format!( - "value of type {} does not support `abi_encode()`", + "value of type `{}` does not support `abi_encode()`", value_attributes.typ ), vec![Label::primary( @@ -1183,7 +1196,7 @@ fn expr_call_type_attribute( let arg_attributes = expr_call_args(scope, args)?; let contract_name = scope.contract_name(); - let report_circular_dependency = |scope: &mut BlockScope, method: String| { + let report_circular_dependency = |scope: &mut BlockScope, method: &str| { scope.fancy_error( &format!("`{contract}.{}(...)` called within `{contract}` creates an illegal circular dependency", method, contract=contract_name), vec![Label::primary(name_span, "Contract creation")], @@ -1194,10 +1207,10 @@ fn expr_call_type_attribute( match (typ.clone(), ContractTypeMethod::from_str(func_name)) { (Type::Contract(contract), Ok(ContractTypeMethod::Create2)) => { - validate_arg_count(scope, func_name, name_span, args, 2); + validate_arg_count(scope, func_name, name_span, args, 2, "argument"); if contract_name == contract.name { - report_circular_dependency(scope, ContractTypeMethod::Create2.to_string()); + report_circular_dependency(scope, ContractTypeMethod::Create2.as_ref()); } if !matches!( @@ -1216,10 +1229,10 @@ fn expr_call_type_attribute( )) } (Type::Contract(contract), Ok(ContractTypeMethod::Create)) => { - validate_arg_count(scope, func_name, name_span, args, 1); + validate_arg_count(scope, func_name, name_span, args, 1, "argument"); if contract_name == contract.name { - report_circular_dependency(scope, ContractTypeMethod::Create.to_string()); + report_circular_dependency(scope, ContractTypeMethod::Create.as_ref()); } if !matches!(&arg_attributes[0].typ, Type::Base(Base::Numeric(_))) { @@ -1301,7 +1314,10 @@ fn expr_call_type( ) -> Result { let call_type = match &func.kind { fe::Expr::Name(name) => expr_name_call_type(scope, name, func.span, generic_args), - fe::Expr::Attribute { .. } => expr_attribute_call_type(scope, func), + fe::Expr::Attribute { .. } => { + // TODO: err if there are generic args + expr_attribute_call_type(scope, func) + } _ => { let expression = expr(scope, func, None)?; Err(FatalError::new(scope.fancy_error( @@ -1325,21 +1341,106 @@ fn expr_name_call_type( name_span: Span, generic_args: Option<&Node>>, ) -> Result { - if let Some(typ) = resolve_type_name(scope, name, name_span, generic_args) { - Ok(CallType::TypeConstructor { typ: typ? }) - } else if let Ok(func) = GlobalMethod::from_str(name) { - if let Some(args) = generic_args { - scope.error( - &format!("`{}` function does not expect generic arguments", name), - args.span, - "unexpected generic argument list", - ); + check_for_call_to_init_fn(scope, name, name_span)?; + + let named_item = scope.resolve_name(name).ok_or_else(|| { + if let Some(function) = scope.root.contract_function(name) { + FatalError::new(scope.fancy_error( + &format!("`{}` must be called via `self`", name), + vec![ + Label::primary( + function.name_span(scope.db()), + &format!("`{}` is defined here as a function that takes `self`", name), + ), + Label::primary( + name_span, + format!("`{}` is called here as a standalone function", name), + ), + ], + vec![format!( + "Suggestion: use `self.{}(...)` instead of `{}(...)`", + name, name + )], + )) + } else { + FatalError::new(scope.error( + &format!("`{}` is not defined", name), + name_span, + &format!("`{}` has not been defined in this scope", name), + )) } - Ok(CallType::BuiltinFunction { func }) - } else { - Ok(CallType::Pure { - func_name: name.to_string(), - }) + })?; + + match named_item { + NamedThing::Variable { typ, span, .. } => Err(FatalError::new(scope.fancy_error( + &format!("`{}` is not callable", name), + vec![ + Label::secondary(span, format!("`{}` has type `{}`", name, typ?)), + Label::primary(name_span, format!("`{}` can't be used as a function", name)), + ], + vec![], + ))), + NamedThing::Item(Item::Object(_)) => Err(FatalError::new(scope.error( + &format!("`{}` is not callable", name), + name_span, + &format!( + "`{}` is a built-in object, and can't be used as a function", + name + ), + ))), + NamedThing::Item(Item::Event(_)) => Err(FatalError::new(scope.fancy_error( + &format!("`{}` is not callable", name), + vec![Label::primary( + name_span, + &format!( + "`{}` is an event, and can't be constructed in this context", + name + ), + )], + vec![format!("Hint: to emit an event, use `emit {}(..)`", name)], + ))), + NamedThing::Item(Item::BuiltinFunction(function)) => { + if let Some(args) = generic_args { + scope.error( + &format!("`{}` function does not expect generic arguments", name), + args.span, + "unexpected generic argument list", + ); + } + Ok(CallType::BuiltinFunction(function)) + } + + NamedThing::Item(Item::Function(function)) => { + if let Some(args) = generic_args { + scope.fancy_error( + &format!("`{}` function is not generic", name), + vec![Label::primary( + args.span, + "unexpected generic argument list", + )], + vec![], + ); + } + Ok(CallType::PureFunction(function)) + } + NamedThing::Item(Item::Type(id)) => { + if let Some(args) = generic_args { + scope.fancy_error( + &format!("`{}` type is not generic", name), + vec![Label::primary( + args.span, + "unexpected generic argument list", + )], + vec![], + ); + } + Ok(CallType::TypeConstructor { + typ: id.typ(scope.db())?, + }) + } + NamedThing::Item(Item::GenericType(generic)) => Ok(CallType::TypeConstructor { + typ: apply_generic_type_args(scope, generic, name_span, generic_args)?, + }), } } @@ -1349,30 +1450,32 @@ fn expr_attribute_call_type( ) -> Result { if let fe::Expr::Attribute { value, attr } = &exp.kind { if let fe::Expr::Name(name) = &value.kind { - match Object::from_str(name) { - Ok(Object::Block) | Ok(Object::Chain) | Ok(Object::Msg) | Ok(Object::Tx) => { - return Err(FatalError::new(scope.fancy_error( - &format!("no function `{}` exists on builtin `{}`", &attr.kind, &name), - vec![Label::primary(exp.span, "undefined function")], - vec![], - ))); - } - Ok(Object::Self_) => { - return Ok(CallType::SelfAttribute { + match scope.resolve_name(name) { + Some(NamedThing::Item(Item::Object(object))) => match object { + Object::Self_ => { + return Ok(CallType::SelfAttribute { + func_name: attr.kind.to_string(), + self_span: value.span, + }) + } + Object::Block | Object::Chain | Object::Msg | Object::Tx => { + return Err(FatalError::new(scope.fancy_error( + &format!("no function `{}` exists on builtin `{}`", &attr.kind, &name), + vec![Label::primary(exp.span, "undefined function")], + vec![], + ))); + } + }, + Some(NamedThing::Item(Item::Type(id))) => { + return Ok(CallType::TypeAttribute { + typ: id.typ(scope.db())?, func_name: attr.kind.to_string(), - self_span: value.span, }) } - Err(_) => {} - } - - if let Some(typ) = scope.resolve_type(name) { - return Ok(CallType::TypeAttribute { - typ: typ?, - func_name: attr.kind.to_string(), - }); - } - }; + // Everything else is handled in expr_call_value_attribute + _ => {} + }; + } return Ok(CallType::ValueAttribute); } diff --git a/crates/analyzer/src/traversal/functions.rs b/crates/analyzer/src/traversal/functions.rs index 8948543122..d270eacc34 100644 --- a/crates/analyzer/src/traversal/functions.rs +++ b/crates/analyzer/src/traversal/functions.rs @@ -1,5 +1,6 @@ -use crate::context::{AnalyzerContext, ExpressionAttributes, Location}; +use crate::context::{AnalyzerContext, ExpressionAttributes, Location, NamedThing}; use crate::errors::FatalError; +use crate::namespace::items::Item; use crate::namespace::scopes::{BlockScope, BlockScopeType}; use crate::namespace::types::{Base, FixedSize, Type}; use crate::traversal::call_args::LabelPolicy; @@ -131,26 +132,39 @@ fn while_loop(scope: &mut BlockScope, stmt: &Node) -> Result<(), F fn emit(scope: &mut BlockScope, stmt: &Node) -> Result<(), FatalError> { if let fe::FuncStmt::Emit { name, args } = &stmt.kind { - if let Some(event) = scope.root.resolve_event(&name.kind) { - scope.root.add_emit(stmt, event); - call_args::validate_named_args( - scope, - &name.kind, - name.span, - args, - &event.typ(scope.db()).fields, - LabelPolicy::AllowUnlabledIfNameEqual, - )?; - } else { - scope.error( - &format!("undefined event: `{}`", name.kind), - name.span, - "undefined event", - ); + match scope.resolve_name(&name.kind) { + None => { + scope.error( + &format!("undefined event: `{}`", name.kind), + name.span, + "undefined event", + ); + } + Some(NamedThing::Item(Item::Event(event))) => { + scope.root.add_emit(stmt, event); + call_args::validate_named_args( + scope, + &name.kind, + name.span, + args, + &event.typ(scope.db()).fields, + LabelPolicy::AllowUnlabledIfNameEqual, + )?; + } + Some(named_thing) => { + scope.error( + "`emit` expects an event", + name.span, + &format!( + "`{}` is a {} name; expected an event", + &name.kind, + named_thing.item_kind_display_name(), + ), + ); + } } return Ok(()); } - unreachable!() } diff --git a/crates/analyzer/src/traversal/types.rs b/crates/analyzer/src/traversal/types.rs index 2c67b569a1..967b5f63f7 100644 --- a/crates/analyzer/src/traversal/types.rs +++ b/crates/analyzer/src/traversal/types.rs @@ -1,131 +1,180 @@ -use crate::context::AnalyzerContext; +use crate::context::{AnalyzerContext, NamedThing}; use crate::errors::TypeError; -use crate::namespace::types::{Array, Base, FeString, FixedSize, Map, Tuple, Type}; +use crate::namespace::items::Item; +use crate::namespace::types::{ + Array, FixedSize, GenericArg, GenericParamKind, GenericType, Tuple, Type, +}; use crate::traversal::call_args::validate_arg_count; use fe_common::diagnostics::Label; -use fe_parser::ast as fe; +use fe_common::utils::humanize::pluralize_conditionally; +use fe_common::Spanned; +use fe_parser::ast; use fe_parser::node::{Node, Span}; use std::convert::TryFrom; -use std::str::FromStr; use vec1::Vec1; -pub fn resolve_type_name( +pub fn apply_generic_type_args( context: &mut dyn AnalyzerContext, - name: &str, + generic: GenericType, name_span: Span, - generic_args: Option<&Node>>, -) -> Option> { - match (name, generic_args) { - ("String", Some(args)) => match &args.kind[..] { - [fe::GenericArg::Int(len)] => Some(Ok(Type::String(FeString { max_size: len.kind }))), - _ => Some(Err(TypeError::new(context.fancy_error( - "invalid `String` type argument", - vec![Label::primary(args.span, "expected an integer")], - vec!["Example: String<100>".into()], - )))), - }, - ("String", None) => Some(Err(TypeError::new(context.fancy_error( - "`String` type requires a max size argument", - vec![Label::primary(name_span, "")], - vec!["Example: String<100>".into()], - )))), - ("Map", Some(args)) => { - let diag_voucher = validate_arg_count(context, name, name_span, args, 2); + args: Option<&Node>>, +) -> Result { + let params = generic.params(); - let key = match args.kind.get(0) { - Some(fe::GenericArg::TypeDesc(type_node)) => match type_desc(context, type_node) { - Ok(Type::Base(base)) => base, - Err(err) => return Some(Err(err)), - _ => { - return Some(Err(TypeError::new(context.error( - "`Map` key must be a primitive type", - type_node.span, - "this can't be used as a Map key", - )))); - } - }, - Some(fe::GenericArg::Int(node)) => { - return Some(Err(TypeError::new(context.error( - "`Map` key must be a type", - node.span, - "this should be a type name", - )))); - } - None => return Some(Err(TypeError::new(diag_voucher.unwrap()))), - }; - let value = match args.kind.get(1) { - Some(fe::GenericArg::TypeDesc(type_node)) => match type_desc(context, type_node) { - Ok(typ) => typ, - Err(err) => return Some(Err(err)), - }, - Some(fe::GenericArg::Int(node)) => { - return Some(Err(TypeError::new(context.error( - "`Map` value must be a type", - node.span, - "this should be a type name", - )))); - } - None => return Some(Err(TypeError::new(diag_voucher.unwrap()))), - }; - Some(Ok(Type::Map(Map { - key, - value: Box::new(value), - }))) - } - ("Map", None) => Some(Err(TypeError::new(context.fancy_error( - "`Map` type requires key and value type arguments", - vec![Label::primary(name_span, "")], - vec!["Example: Map".into()], - )))), - (_, _) => { - let typ = if let Ok(base_type) = Base::from_str(name) { - Type::Base(base_type) - } else { - match context.resolve_type(name) { - Some(Ok(typ)) => typ, - Some(Err(err)) => return Some(Err(err)), - None => return None, - } - }; + let args = args.ok_or_else(|| { + TypeError::new(context.fancy_error( + &format!( + "missing generic {} for type `{}`", + pluralize_conditionally("argument", params.len()), + generic.name() + ), + vec![Label::primary( + name_span, + &format!( + "expected {} generic {}", + params.len(), + pluralize_conditionally("argument", params.len()) + ), + )], + vec![friendly_generic_arg_example_string(generic)], + )) + })?; - if let Some(args) = generic_args { - // User-defined types can't be generic yet - context.fancy_error( - &format!("`{}` type is not generic", typ), - vec![Label::primary(args.span, "unexpected type argument list")], - vec![format!("Hint: use `{}`", typ)], - ); - } - Some(Ok(typ)) - } + if let Some(diag) = validate_arg_count( + context, + generic.name(), + name_span, + args, + params.len(), + "generic argument", + ) { + return Err(TypeError::new(diag)); } + + let concrete_args = params + .iter() + .zip(args.kind.iter()) + .map(|(param, arg)| match (param.kind, arg) { + (GenericParamKind::Int, ast::GenericArg::Int(int_node)) => { + Ok(GenericArg::Int(int_node.kind)) + } + (GenericParamKind::Int, ast::GenericArg::TypeDesc(_)) => { + Err(TypeError::new(context.fancy_error( + &format!("`{}` {} must be an integer", generic.name(), param.name), + vec![Label::primary(arg.span(), "expected an integer")], + vec![], + ))) + } + (GenericParamKind::PrimitiveType, ast::GenericArg::TypeDesc(type_node)) => { + match type_desc(context, type_node)? { + Type::Base(base) => Ok(GenericArg::Type(Type::Base(base))), + typ => Err(TypeError::new(context.error( + &format!( + "`{}` {} must be a primitive type", + generic.name(), + param.name + ), + type_node.span, + &format!("this has type `{}`; expected a primitive type", typ), + ))), + } + } + (GenericParamKind::AnyType, ast::GenericArg::TypeDesc(type_node)) => { + Ok(GenericArg::Type(type_desc(context, type_node)?)) + } + ( + GenericParamKind::PrimitiveType | GenericParamKind::AnyType, + ast::GenericArg::Int(_), + ) => Err(TypeError::new(context.fancy_error( + &format!("`{}` {} must be a type", generic.name(), param.name), + vec![Label::primary(arg.span(), "expected a type name")], + vec![], + ))), + }) + .collect::, _>>()?; + Ok(generic + .apply(&concrete_args) + .expect("failed to construct generic type after checking args")) } -fn resolve_type_name_or_err( +fn friendly_generic_arg_example_string(generic: GenericType) -> String { + let example_args = generic + .params() + .iter() + .map(|param| match param.kind { + GenericParamKind::Int => "32", + GenericParamKind::PrimitiveType => "u64", + GenericParamKind::AnyType => "String<32>", + }) + .collect::>(); + + format!("Example: `{}<{}>`", generic.name(), example_args.join(", ")) +} + +pub fn resolve_concrete_type( context: &mut dyn AnalyzerContext, name: &str, name_span: Span, - generic_args: Option<&Node>>, + generic_args: Option<&Node>>, ) -> Result { - if let Some(typ) = resolve_type_name(context, name, name_span, generic_args) { - typ - } else { - Err(TypeError::new(context.error( + let named_item = context.resolve_name(name).ok_or_else(|| { + TypeError::new(context.error( "undefined type", name_span, - "this type name has not been defined", - ))) + &format!("`{}` has not been defined", name), + )) + })?; + + match named_item { + NamedThing::Item(Item::Type(id)) => { + if let Some(args) = generic_args { + context.fancy_error( + &format!("`{}` type is not generic", name), + vec![Label::primary( + args.span, + "unexpected generic argument list", + )], + vec![], + ); + } + id.typ(context.db()) + } + NamedThing::Item(Item::GenericType(generic)) => { + apply_generic_type_args(context, generic, name_span, generic_args) + } + _ => Err(TypeError::new(context.fancy_error( + &format!("`{}` is not a type name", name), + if let Some(def_span) = named_item.name_span(context.db()) { + vec![ + Label::primary( + def_span, + format!( + "`{}` is defined here as a {}", + name, + named_item.item_kind_display_name() + ), + ), + Label::primary(name_span, format!("`{}` is used here as a type", name)), + ] + } else { + vec![Label::primary( + name_span, + format!("`{}` is a {}", name, named_item.item_kind_display_name()), + )] + }, + vec![], + ))), } } /// Maps a type description node to an enum type. pub fn type_desc( context: &mut dyn AnalyzerContext, - desc: &Node, + desc: &Node, ) -> Result { match &desc.kind { - fe::TypeDesc::Base { base } => resolve_type_name_or_err(context, base, desc.span, None), - fe::TypeDesc::Array { typ, dimension } => { + ast::TypeDesc::Base { base } => resolve_concrete_type(context, base, desc.span, None), + ast::TypeDesc::Array { typ, dimension } => { if let Type::Base(base) = type_desc(context, typ)? { Ok(Type::Array(Array { inner: base, @@ -139,10 +188,10 @@ pub fn type_desc( ))) } } - fe::TypeDesc::Generic { base, args } => { - resolve_type_name_or_err(context, &base.kind, base.span, Some(args)) + ast::TypeDesc::Generic { base, args } => { + resolve_concrete_type(context, &base.kind, base.span, Some(args)) } - fe::TypeDesc::Tuple { items } => { + ast::TypeDesc::Tuple { items } => { let types = items .iter() .map(|typ| match FixedSize::try_from(type_desc(context, typ)?) { @@ -158,6 +207,6 @@ pub fn type_desc( items: Vec1::try_from_vec(types).expect("tuple is empty"), })) } - fe::TypeDesc::Unit => Ok(Type::unit()), + ast::TypeDesc::Unit => Ok(Type::unit()), } } diff --git a/crates/analyzer/tests/analysis.rs b/crates/analyzer/tests/analysis.rs index 4c953f46cc..70e119693f 100644 --- a/crates/analyzer/tests/analysis.rs +++ b/crates/analyzer/tests/analysis.rs @@ -1,5 +1,5 @@ use fe_analyzer::context; -use fe_analyzer::namespace::items; +use fe_analyzer::namespace::items::{self, Item, TypeDef}; use fe_analyzer::namespace::types::{Event, FixedSize, FunctionSignature, Type}; use fe_analyzer::{AnalyzerDb, Db}; use fe_common::diagnostics::{diagnostics_string, print_diagnostics, Diagnostic, Label, Severity}; @@ -165,10 +165,10 @@ fn build_snapshot(path: &str, src: &str, module: items::ModuleId, db: &dyn Analy // contract and struct types aren't worth printing let type_aliases = module - .all_type_defs(db) + .all_items(db) .iter() .filter_map(|def| match def { - items::TypeDefId::Alias(alias) => { + Item::Type(TypeDef::Alias(alias)) => { Some((alias.data(db).ast.span, alias.typ(db).unwrap())) } _ => None, diff --git a/crates/analyzer/tests/errors.rs b/crates/analyzer/tests/errors.rs index 0dc691f108..e99cd72329 100644 --- a/crates/analyzer/tests/errors.rs +++ b/crates/analyzer/tests/errors.rs @@ -95,6 +95,8 @@ test_stmt! { clone_arg_count, "let x: u256[2] = [5, 6]\nlet y: u256[2] = x.clone test_stmt! { continue_without_loop, "continue" } test_stmt! { continue_without_loop_2, "if true:\n continue" } test_stmt! { emit_undefined_event, "emit MyEvent()" } +test_stmt! { emit_type_name, "emit u8()" } +test_stmt! { emit_variable, "let x: u8 = 10\nemit x()" } test_stmt! { int_type_generic_arg_list, "let x: u256<>" } test_stmt! { int_type_generic_arg, "let x: u256<10>" } test_stmt! { int_type_constructor_generic_arg_list, "u256<>(10)" } diff --git a/crates/analyzer/tests/snapshots/analysis__data_copying_stress.snap b/crates/analyzer/tests/snapshots/analysis__data_copying_stress.snap index 157905443d..2fdfa8053a 100644 --- a/crates/analyzer/tests/snapshots/analysis__data_copying_stress.snap +++ b/crates/analyzer/tests/snapshots/analysis__data_copying_stress.snap @@ -1128,11 +1128,13 @@ note: ┌─ stress/data_copying_stress.fe:69:9 │ 69 │ emit_my_event_internal( - │ ^^^^^^^^^^^^^^^^^^^^^^ attributes hash: 15175926995091823840 + │ ^^^^^^^^^^^^^^^^^^^^^^ attributes hash: 1592910002537916642 │ - = Pure { - func_name: "emit_my_event_internal", - } + = PureFunction( + FunctionId( + 8, + ), + ) note: ┌─ stress/data_copying_stress.fe:70:13 diff --git a/crates/analyzer/tests/snapshots/analysis__erc20_token.snap b/crates/analyzer/tests/snapshots/analysis__erc20_token.snap index c83180e075..82860d1163 100644 --- a/crates/analyzer/tests/snapshots/analysis__erc20_token.snap +++ b/crates/analyzer/tests/snapshots/analysis__erc20_token.snap @@ -2087,11 +2087,13 @@ note: ┌─ demos/erc20_token.fe:68:9 │ 68 │ _before_token_transfer(sender, recipient, value) - │ ^^^^^^^^^^^^^^^^^^^^^^ attributes hash: 283404834886046277 + │ ^^^^^^^^^^^^^^^^^^^^^^ attributes hash: 4503769092042421016 │ - = Pure { - func_name: "_before_token_transfer", - } + = PureFunction( + FunctionId( + 17, + ), + ) note: ┌─ demos/erc20_token.fe:74:27 @@ -2109,11 +2111,13 @@ note: ┌─ demos/erc20_token.fe:75:9 │ 75 │ _before_token_transfer(address(0), account, value) - │ ^^^^^^^^^^^^^^^^^^^^^^ attributes hash: 283404834886046277 + │ ^^^^^^^^^^^^^^^^^^^^^^ attributes hash: 4503769092042421016 │ - = Pure { - func_name: "_before_token_transfer", - } + = PureFunction( + FunctionId( + 17, + ), + ) note: ┌─ demos/erc20_token.fe:75:32 @@ -2155,11 +2159,13 @@ note: ┌─ demos/erc20_token.fe:82:9 │ 82 │ _before_token_transfer(account, address(0), value) - │ ^^^^^^^^^^^^^^^^^^^^^^ attributes hash: 283404834886046277 + │ ^^^^^^^^^^^^^^^^^^^^^^ attributes hash: 4503769092042421016 │ - = Pure { - func_name: "_before_token_transfer", - } + = PureFunction( + FunctionId( + 17, + ), + ) note: ┌─ demos/erc20_token.fe:82:41 diff --git a/crates/analyzer/tests/snapshots/analysis__keccak.snap b/crates/analyzer/tests/snapshots/analysis__keccak.snap index 23ec1df9ea..d32dad4dc6 100644 --- a/crates/analyzer/tests/snapshots/analysis__keccak.snap +++ b/crates/analyzer/tests/snapshots/analysis__keccak.snap @@ -142,30 +142,30 @@ note: ┌─ features/keccak.fe:4:16 │ 4 │ return keccak256(val) - │ ^^^^^^^^^ attributes hash: 11888120106960716956 + │ ^^^^^^^^^ attributes hash: 3985281278010092305 │ - = BuiltinFunction { - func: Keccak256, - } + = BuiltinFunction( + Keccak256, + ) note: ┌─ features/keccak.fe:7:16 │ 7 │ return keccak256(val) - │ ^^^^^^^^^ attributes hash: 11888120106960716956 + │ ^^^^^^^^^ attributes hash: 3985281278010092305 │ - = BuiltinFunction { - func: Keccak256, - } + = BuiltinFunction( + Keccak256, + ) note: ┌─ features/keccak.fe:10:16 │ 10 │ return keccak256(val) - │ ^^^^^^^^^ attributes hash: 11888120106960716956 + │ ^^^^^^^^^ attributes hash: 3985281278010092305 │ - = BuiltinFunction { - func: Keccak256, - } + = BuiltinFunction( + Keccak256, + ) diff --git a/crates/analyzer/tests/snapshots/analysis__return_u256_from_called_fn.snap b/crates/analyzer/tests/snapshots/analysis__return_u256_from_called_fn.snap index 9be3c0fa76..16cc5d215f 100644 --- a/crates/analyzer/tests/snapshots/analysis__return_u256_from_called_fn.snap +++ b/crates/analyzer/tests/snapshots/analysis__return_u256_from_called_fn.snap @@ -57,10 +57,12 @@ note: ┌─ features/return_u256_from_called_fn.fe:5:16 │ 5 │ return foo() - │ ^^^ attributes hash: 1664590513608378371 + │ ^^^ attributes hash: 5348760640954559882 │ - = Pure { - func_name: "foo", - } + = PureFunction( + FunctionId( + 1, + ), + ) diff --git a/crates/analyzer/tests/snapshots/analysis__return_u256_from_called_fn_with_args.snap b/crates/analyzer/tests/snapshots/analysis__return_u256_from_called_fn_with_args.snap index 63e9944754..df4e6d3720 100644 --- a/crates/analyzer/tests/snapshots/analysis__return_u256_from_called_fn_with_args.snap +++ b/crates/analyzer/tests/snapshots/analysis__return_u256_from_called_fn_with_args.snap @@ -266,20 +266,24 @@ note: ┌─ features/return_u256_from_called_fn_with_args.fe:11:16 │ 11 │ return foo(5, 2, cem(), 25 + 25, self.baz[0]) - │ ^^^ attributes hash: 1664590513608378371 + │ ^^^ attributes hash: 17659893670529691939 │ - = Pure { - func_name: "foo", - } + = PureFunction( + FunctionId( + 0, + ), + ) note: ┌─ features/return_u256_from_called_fn_with_args.fe:11:26 │ 11 │ return foo(5, 2, cem(), 25 + 25, self.baz[0]) - │ ^^^ attributes hash: 3933618496970144567 + │ ^^^ attributes hash: 5348760640954559882 │ - = Pure { - func_name: "cem", - } + = PureFunction( + FunctionId( + 1, + ), + ) diff --git a/crates/analyzer/tests/snapshots/analysis__structs.snap b/crates/analyzer/tests/snapshots/analysis__structs.snap index b14e923678..c0142a8e5a 100644 --- a/crates/analyzer/tests/snapshots/analysis__structs.snap +++ b/crates/analyzer/tests/snapshots/analysis__structs.snap @@ -1464,11 +1464,11 @@ note: ┌─ features/structs.fe:83:16 │ 83 │ return keccak256(house.abi_encode()) - │ ^^^^^^^^^ attributes hash: 11888120106960716956 + │ ^^^^^^^^^ attributes hash: 3985281278010092305 │ - = BuiltinFunction { - func: Keccak256, - } + = BuiltinFunction( + Keccak256, + ) note: ┌─ features/structs.fe:83:26 diff --git a/crates/analyzer/tests/snapshots/analysis__tuple_stress.snap b/crates/analyzer/tests/snapshots/analysis__tuple_stress.snap index 626efd0f15..2ea14c7d32 100644 --- a/crates/analyzer/tests/snapshots/analysis__tuple_stress.snap +++ b/crates/analyzer/tests/snapshots/analysis__tuple_stress.snap @@ -825,11 +825,13 @@ note: ┌─ stress/tuple_stress.fe:43:9 │ 43 │ emit_my_event(my_tuple) - │ ^^^^^^^^^^^^^ attributes hash: 4428583522641384060 + │ ^^^^^^^^^^^^^ attributes hash: 9946906573359017090 │ - = Pure { - func_name: "emit_my_event", - } + = PureFunction( + FunctionId( + 5, + ), + ) note: ┌─ stress/tuple_stress.fe:46:16 diff --git a/crates/analyzer/tests/snapshots/analysis__uniswap.snap b/crates/analyzer/tests/snapshots/analysis__uniswap.snap index 9712f51b93..0fc3df352f 100644 --- a/crates/analyzer/tests/snapshots/analysis__uniswap.snap +++ b/crates/analyzer/tests/snapshots/analysis__uniswap.snap @@ -5879,21 +5879,25 @@ note: ┌─ demos/uniswap.fe:144:36 │ 144 │ let root_k: u256 = sqrt(reserve0 * reserve1) - │ ^^^^ attributes hash: 11539501539171308117 + │ ^^^^ attributes hash: 4659268320881850436 │ - = Pure { - func_name: "sqrt", - } + = PureFunction( + FunctionId( + 23, + ), + ) note: ┌─ demos/uniswap.fe:145:41 │ 145 │ let root_k_last: u256 = sqrt(k_last) - │ ^^^^ attributes hash: 11539501539171308117 + │ ^^^^ attributes hash: 4659268320881850436 │ - = Pure { - func_name: "sqrt", - } + = PureFunction( + FunctionId( + 23, + ), + ) note: ┌─ demos/uniswap.fe:151:25 @@ -5977,11 +5981,13 @@ note: ┌─ demos/uniswap.fe:171:25 │ 171 │ liquidity = sqrt(amount0 * amount1) - MINIMUM_LIQUIDITY - │ ^^^^ attributes hash: 11539501539171308117 + │ ^^^^ attributes hash: 4659268320881850436 │ - = Pure { - func_name: "sqrt", - } + = PureFunction( + FunctionId( + 23, + ), + ) note: ┌─ demos/uniswap.fe:172:13 @@ -6013,11 +6019,13 @@ note: ┌─ demos/uniswap.fe:174:25 │ 174 │ liquidity = min((amount0 * total_supply) / reserve0, (amount1 * total_supply) / reserve1) - │ ^^^ attributes hash: 2385741873862807477 + │ ^^^ attributes hash: 1603704326590615486 │ - = Pure { - func_name: "min", - } + = PureFunction( + FunctionId( + 24, + ), + ) note: ┌─ demos/uniswap.fe:178:9 @@ -6433,11 +6441,11 @@ note: ┌─ demos/uniswap.fe:319:26 │ 319 │ let salt: u256 = keccak256((token0, token1).abi_encode()) - │ ^^^^^^^^^ attributes hash: 11888120106960716956 + │ ^^^^^^^^^ attributes hash: 3985281278010092305 │ - = BuiltinFunction { - func: Keccak256, - } + = BuiltinFunction( + Keccak256, + ) note: ┌─ demos/uniswap.fe:319:36 diff --git a/crates/analyzer/tests/snapshots/errors__abi_encode_u256.snap b/crates/analyzer/tests/snapshots/errors__abi_encode_u256.snap index 49f08e8e23..ee758a7ec4 100644 --- a/crates/analyzer/tests/snapshots/errors__abi_encode_u256.snap +++ b/crates/analyzer/tests/snapshots/errors__abi_encode_u256.snap @@ -3,7 +3,7 @@ source: crates/analyzer/tests/errors.rs expression: "error_string(&path, &src)" --- -error: value of type u256 does not support `abi_encode()` +error: value of type `u256` does not support `abi_encode()` ┌─ compile_errors/abi_encode_u256.fe:3:9 │ 3 │ 42.abi_encode() diff --git a/crates/analyzer/tests/snapshots/errors__array_constructor_call.snap b/crates/analyzer/tests/snapshots/errors__array_constructor_call.snap index 6e2b929d07..59cf026e28 100644 --- a/crates/analyzer/tests/snapshots/errors__array_constructor_call.snap +++ b/crates/analyzer/tests/snapshots/errors__array_constructor_call.snap @@ -3,10 +3,10 @@ source: crates/analyzer/tests/errors.rs expression: "error_string(\"[snippet]\", &src)" --- -error: cannot find value `u8` in this scope +error: `u8` is a built-in type name, and can't be used as an expression ┌─ [snippet]:3:3 │ 3 │ u8[3]([1, 2, 3]) - │ ^^ undefined + │ ^^ `u8` is used here as a value diff --git a/crates/analyzer/tests/snapshots/errors__call_to_mut_fn_without_self.snap b/crates/analyzer/tests/snapshots/errors__call_to_mut_fn_without_self.snap index cdee793959..8ab9edf62f 100644 --- a/crates/analyzer/tests/snapshots/errors__call_to_mut_fn_without_self.snap +++ b/crates/analyzer/tests/snapshots/errors__call_to_mut_fn_without_self.snap @@ -3,12 +3,15 @@ source: crates/analyzer/tests/errors.rs expression: "error_string(&path, &src)" --- -error: `baz` must be called using `self` +error: `baz` must be called via `self` ┌─ compile_errors/call_to_mut_fn_without_self.fe:3:9 │ 3 │ baz() - │ ^^^ function takes self + │ ^^^ `baz` is called here as a standalone function +4 │ +5 │ fn baz(self): + │ ^^^ `baz` is defined here as a function that takes `self` │ - = Suggestion: try `self.baz(...)` instead of `baz(...)` + = Suggestion: use `self.baz(...)` instead of `baz(...)` diff --git a/crates/analyzer/tests/snapshots/errors__duplicate_contract_in_module.snap b/crates/analyzer/tests/snapshots/errors__duplicate_contract_in_module.snap index 00d3c9652e..ec375d8b69 100644 --- a/crates/analyzer/tests/snapshots/errors__duplicate_contract_in_module.snap +++ b/crates/analyzer/tests/snapshots/errors__duplicate_contract_in_module.snap @@ -4,18 +4,12 @@ expression: "error_string(&path, &src)" --- error: duplicate type name - ┌─ compile_errors/duplicate_contract_in_module.fe:1:1 - │ -1 │ ╭ contract Foo: -2 │ │ -3 │ │ pub fn bar(): -4 │ │ pass - │ ╰──────────────^ `Foo` first defined here -5 │ -6 │ ╭ contract Foo: -7 │ │ -8 │ │ pub fn bar(): -9 │ │ pass - │ ╰────────────' `Foo` redefined here + ┌─ compile_errors/duplicate_contract_in_module.fe:1:10 + │ +1 │ contract Foo: + │ ^^^ `Foo` first defined here + · +6 │ contract Foo: + │ --- `Foo` redefined here diff --git a/crates/analyzer/tests/snapshots/errors__duplicate_struct_in_module.snap b/crates/analyzer/tests/snapshots/errors__duplicate_struct_in_module.snap index 38cf28ab45..2bf4765d23 100644 --- a/crates/analyzer/tests/snapshots/errors__duplicate_struct_in_module.snap +++ b/crates/analyzer/tests/snapshots/errors__duplicate_struct_in_module.snap @@ -4,14 +4,12 @@ expression: "error_string(&path, &src)" --- error: duplicate type name - ┌─ compile_errors/duplicate_struct_in_module.fe:1:1 - │ -1 │ ╭ struct MyStruct: -2 │ │ foo: u8 - │ ╰─────────────^ `MyStruct` first defined here -3 │ -4 │ ╭ struct MyStruct: -5 │ │ foo: u8 - │ ╰───────────' `MyStruct` redefined here + ┌─ compile_errors/duplicate_struct_in_module.fe:1:8 + │ +1 │ struct MyStruct: + │ ^^^^^^^^ `MyStruct` first defined here + · +4 │ struct MyStruct: + │ -------- `MyStruct` redefined here diff --git a/crates/analyzer/tests/snapshots/errors__duplicate_typedef_in_module.snap b/crates/analyzer/tests/snapshots/errors__duplicate_typedef_in_module.snap index 9ac715de82..fadc11bf37 100644 --- a/crates/analyzer/tests/snapshots/errors__duplicate_typedef_in_module.snap +++ b/crates/analyzer/tests/snapshots/errors__duplicate_typedef_in_module.snap @@ -4,12 +4,21 @@ expression: "error_string(&path, &src)" --- error: duplicate type name - ┌─ compile_errors/duplicate_typedef_in_module.fe:2:1 + ┌─ compile_errors/duplicate_typedef_in_module.fe:2:6 │ 2 │ type bar = u8 - │ ^^^^^^^^^^^^^ `bar` first defined here + │ ^^^ `bar` first defined here 3 │ 4 │ type bar = u8 - │ ------------- `bar` redefined here + │ --- `bar` redefined here + +error: function name `bar` conflicts with previously defined type + ┌─ compile_errors/duplicate_typedef_in_module.fe:2:6 + │ +2 │ type bar = u8 + │ ^^^ `bar` first defined here + · +8 │ pub fn bar(): + │ --- `bar` redefined here diff --git a/crates/analyzer/tests/snapshots/errors__emit_type_name.snap b/crates/analyzer/tests/snapshots/errors__emit_type_name.snap new file mode 100644 index 0000000000..ae7609391e --- /dev/null +++ b/crates/analyzer/tests/snapshots/errors__emit_type_name.snap @@ -0,0 +1,12 @@ +--- +source: crates/analyzer/tests/errors.rs +expression: "error_string(\"[snippet]\", &src)" + +--- +error: `emit` expects an event + ┌─ [snippet]:3:8 + │ +3 │ emit u8() + │ ^^ `u8` is a type name; expected an event + + diff --git a/crates/analyzer/tests/snapshots/errors__emit_variable.snap b/crates/analyzer/tests/snapshots/errors__emit_variable.snap new file mode 100644 index 0000000000..2e54f73173 --- /dev/null +++ b/crates/analyzer/tests/snapshots/errors__emit_variable.snap @@ -0,0 +1,12 @@ +--- +source: crates/analyzer/tests/errors.rs +expression: "error_string(\"[snippet]\", &src)" + +--- +error: `emit` expects an event + ┌─ [snippet]:4:8 + │ +4 │ emit x() + │ ^ `x` is a variable name; expected an event + + diff --git a/crates/analyzer/tests/snapshots/errors__int_type_constructor_generic_arg.snap b/crates/analyzer/tests/snapshots/errors__int_type_constructor_generic_arg.snap index 003ef4a269..f306f3ec61 100644 --- a/crates/analyzer/tests/snapshots/errors__int_type_constructor_generic_arg.snap +++ b/crates/analyzer/tests/snapshots/errors__int_type_constructor_generic_arg.snap @@ -7,8 +7,6 @@ error: `u256` type is not generic ┌─ [snippet]:3:7 │ 3 │ u256<1>(10) - │ ^^^ unexpected type argument list - │ - = Hint: use `u256` + │ ^^^ unexpected generic argument list diff --git a/crates/analyzer/tests/snapshots/errors__int_type_constructor_generic_arg_list.snap b/crates/analyzer/tests/snapshots/errors__int_type_constructor_generic_arg_list.snap index 2b6d0d76d6..f672c3b1b5 100644 --- a/crates/analyzer/tests/snapshots/errors__int_type_constructor_generic_arg_list.snap +++ b/crates/analyzer/tests/snapshots/errors__int_type_constructor_generic_arg_list.snap @@ -7,8 +7,6 @@ error: `u256` type is not generic ┌─ [snippet]:3:7 │ 3 │ u256<>(10) - │ ^^ unexpected type argument list - │ - = Hint: use `u256` + │ ^^ unexpected generic argument list diff --git a/crates/analyzer/tests/snapshots/errors__int_type_generic_arg.snap b/crates/analyzer/tests/snapshots/errors__int_type_generic_arg.snap index e0c3d18cb2..0dd7b4c6b2 100644 --- a/crates/analyzer/tests/snapshots/errors__int_type_generic_arg.snap +++ b/crates/analyzer/tests/snapshots/errors__int_type_generic_arg.snap @@ -7,8 +7,6 @@ error: `u256` type is not generic ┌─ [snippet]:3:14 │ 3 │ let x: u256<10> - │ ^^^^ unexpected type argument list - │ - = Hint: use `u256` + │ ^^^^ unexpected generic argument list diff --git a/crates/analyzer/tests/snapshots/errors__int_type_generic_arg_list.snap b/crates/analyzer/tests/snapshots/errors__int_type_generic_arg_list.snap index 060488b98c..7ca3281f75 100644 --- a/crates/analyzer/tests/snapshots/errors__int_type_generic_arg_list.snap +++ b/crates/analyzer/tests/snapshots/errors__int_type_generic_arg_list.snap @@ -7,8 +7,6 @@ error: `u256` type is not generic ┌─ [snippet]:3:14 │ 3 │ let x: u256<> - │ ^^ unexpected type argument list - │ - = Hint: use `u256` + │ ^^ unexpected generic argument list diff --git a/crates/analyzer/tests/snapshots/errors__map_int_type_arg.snap b/crates/analyzer/tests/snapshots/errors__map_int_type_arg.snap index 45e50b5765..631b9596ab 100644 --- a/crates/analyzer/tests/snapshots/errors__map_int_type_arg.snap +++ b/crates/analyzer/tests/snapshots/errors__map_int_type_arg.snap @@ -7,6 +7,6 @@ error: `Map` value must be a type ┌─ [snippet]:3:23 │ 3 │ let x: Map - │ ^^^ this should be a type name + │ ^^^ expected a type name diff --git a/crates/analyzer/tests/snapshots/errors__map_int_type_args.snap b/crates/analyzer/tests/snapshots/errors__map_int_type_args.snap index ff098c1575..544cb4f445 100644 --- a/crates/analyzer/tests/snapshots/errors__map_int_type_args.snap +++ b/crates/analyzer/tests/snapshots/errors__map_int_type_args.snap @@ -7,6 +7,6 @@ error: `Map` key must be a type ┌─ [snippet]:3:14 │ 3 │ let x: Map<10, 20> - │ ^^ this should be a type name + │ ^^ expected a type name diff --git a/crates/analyzer/tests/snapshots/errors__map_map_key_type.snap b/crates/analyzer/tests/snapshots/errors__map_map_key_type.snap index d533988b49..6a35981c9c 100644 --- a/crates/analyzer/tests/snapshots/errors__map_map_key_type.snap +++ b/crates/analyzer/tests/snapshots/errors__map_map_key_type.snap @@ -7,6 +7,6 @@ error: `Map` key must be a primitive type ┌─ [snippet]:3:14 │ 3 │ let x: Map, address> - │ ^^^^^^^^^^^ this can't be used as a Map key + │ ^^^^^^^^^^^ this has type `Map`; expected a primitive type diff --git a/crates/analyzer/tests/snapshots/errors__map_no_type_arg_list.snap b/crates/analyzer/tests/snapshots/errors__map_no_type_arg_list.snap index 42f1a55153..aa3cabc0c4 100644 --- a/crates/analyzer/tests/snapshots/errors__map_no_type_arg_list.snap +++ b/crates/analyzer/tests/snapshots/errors__map_no_type_arg_list.snap @@ -3,12 +3,12 @@ source: crates/analyzer/tests/errors.rs expression: "error_string(\"[snippet]\", &src)" --- -error: `Map` type requires key and value type arguments +error: missing generic arguments for type `Map` ┌─ [snippet]:3:10 │ 3 │ let x: Map - │ ^^^ + │ ^^^ expected 2 generic arguments │ - = Example: Map + = Example: `Map>` diff --git a/crates/analyzer/tests/snapshots/errors__map_no_type_args.snap b/crates/analyzer/tests/snapshots/errors__map_no_type_args.snap index ced399271e..955bbe35fc 100644 --- a/crates/analyzer/tests/snapshots/errors__map_no_type_args.snap +++ b/crates/analyzer/tests/snapshots/errors__map_no_type_args.snap @@ -3,12 +3,12 @@ source: crates/analyzer/tests/errors.rs expression: "error_string(\"[snippet]\", &src)" --- -error: `Map` expects 2 arguments, but 0 were provided +error: `Map` expects 2 generic arguments, but 0 were provided ┌─ [snippet]:3:10 │ 3 │ let x: Map<> │ ^^^-- supplied 0 arguments │ │ - │ expects 2 arguments + │ expects 2 generic arguments diff --git a/crates/analyzer/tests/snapshots/errors__map_one_type_arg.snap b/crates/analyzer/tests/snapshots/errors__map_one_type_arg.snap index 23ea5402cd..0fd915d2c5 100644 --- a/crates/analyzer/tests/snapshots/errors__map_one_type_arg.snap +++ b/crates/analyzer/tests/snapshots/errors__map_one_type_arg.snap @@ -3,18 +3,12 @@ source: crates/analyzer/tests/errors.rs expression: "error_string(\"[snippet]\", &src)" --- -error: `Map` expects 2 arguments, but 1 was provided +error: `Map` expects 2 generic arguments, but 1 was provided ┌─ [snippet]:3:10 │ 3 │ let x: Map - │ ^^^ - supplied 1 argument + │ ^^^ - supplied 1 generic argument │ │ - │ expects 2 arguments - -error: undefined type - ┌─ [snippet]:3:14 - │ -3 │ let x: Map - │ ^ this type name has not been defined + │ expects 2 generic arguments diff --git a/crates/analyzer/tests/snapshots/errors__map_three_type_args.snap b/crates/analyzer/tests/snapshots/errors__map_three_type_args.snap index 9124aec641..30879c6618 100644 --- a/crates/analyzer/tests/snapshots/errors__map_three_type_args.snap +++ b/crates/analyzer/tests/snapshots/errors__map_three_type_args.snap @@ -3,18 +3,12 @@ source: crates/analyzer/tests/errors.rs expression: "error_string(\"[snippet]\", &src)" --- -error: `Map` expects 2 arguments, but 3 were provided +error: `Map` expects 2 generic arguments, but 3 were provided ┌─ [snippet]:3:10 │ 3 │ let x: Map - │ ^^^ -- -- -- supplied 3 arguments + │ ^^^ -- -- -- supplied 3 generic arguments │ │ - │ expects 2 arguments - -error: invalid variable type - ┌─ [snippet]:3:10 - │ -3 │ let x: Map - │ ^^^^^^^^^^^^^^^ `Map` type can only be used as a contract field + │ expects 2 generic arguments diff --git a/crates/analyzer/tests/snapshots/errors__not_callable.snap b/crates/analyzer/tests/snapshots/errors__not_callable.snap index 47aaa69448..b307200473 100644 --- a/crates/analyzer/tests/snapshots/errors__not_callable.snap +++ b/crates/analyzer/tests/snapshots/errors__not_callable.snap @@ -4,9 +4,29 @@ expression: "error_string(&path, &src)" --- error: `u256` type is not callable - ┌─ compile_errors/not_callable.fe:4:9 + ┌─ compile_errors/not_callable.fe:7:5 │ -4 │ 5() - │ ^ this has type `u256` +7 │ 5() + │ ^ this has type `u256` + +error: `MyEvent` is not callable + ┌─ compile_errors/not_callable.fe:10:5 + │ +10 │ MyEvent(x=10) + │ ^^^^^^^ `MyEvent` is an event, and can't be constructed in this context + │ + = Hint: to emit an event, use `emit MyEvent(..)` + +error: `block` is not callable + ┌─ compile_errors/not_callable.fe:13:5 + │ +13 │ block() + │ ^^^^^ `block` is a built-in object, and can't be used as a function + +error: `self` is not callable + ┌─ compile_errors/not_callable.fe:16:5 + │ +16 │ self() + │ ^^^^ `self` is a built-in object, and can't be used as a function diff --git a/crates/analyzer/tests/snapshots/errors__return_type_undefined.snap b/crates/analyzer/tests/snapshots/errors__return_type_undefined.snap index b49eeef16f..24cb353807 100644 --- a/crates/analyzer/tests/snapshots/errors__return_type_undefined.snap +++ b/crates/analyzer/tests/snapshots/errors__return_type_undefined.snap @@ -7,6 +7,6 @@ error: undefined type ┌─ compile_errors/return_type_undefined.fe:2:17 │ 2 │ pub fn f() -> Foo: - │ ^^^ this type name has not been defined + │ ^^^ `Foo` has not been defined diff --git a/crates/analyzer/tests/snapshots/errors__shadow_builtin_function.snap b/crates/analyzer/tests/snapshots/errors__shadow_builtin_function.snap index 280b1cf515..90c6bb5298 100644 --- a/crates/analyzer/tests/snapshots/errors__shadow_builtin_function.snap +++ b/crates/analyzer/tests/snapshots/errors__shadow_builtin_function.snap @@ -3,25 +3,25 @@ source: crates/analyzer/tests/errors.rs expression: "error_string(&path, &src)" --- -error: function name conflicts with built-in function +error: function name `keccak256` conflicts with built-in function ┌─ compile_errors/shadow_builtin_function.fe:2:10 │ 2 │ pub fn keccak256(bytes: u8[4]) -> u8[4]: │ ^^^^^^^^^ `keccak256` is a built-in function -error: function name conflicts with built-in type +error: function name `u256` conflicts with built-in type ┌─ compile_errors/shadow_builtin_function.fe:5:10 │ 5 │ pub fn u256(x: u8) -> u256: │ ^^^^ `u256` is a built-in type -error: function name conflicts with built-in type +error: function name `bool` conflicts with built-in type ┌─ compile_errors/shadow_builtin_function.fe:8:10 │ 8 │ pub fn bool(x: u8) -> bool: │ ^^^^ `bool` is a built-in type -error: function name conflicts with built-in object +error: function name `self` conflicts with built-in object ┌─ compile_errors/shadow_builtin_function.fe:11:10 │ 11 │ pub fn self() -> bool: diff --git a/crates/analyzer/tests/snapshots/errors__shadow_builtin_type.snap b/crates/analyzer/tests/snapshots/errors__shadow_builtin_type.snap index 43f19c5f24..3dc96b3739 100644 --- a/crates/analyzer/tests/snapshots/errors__shadow_builtin_type.snap +++ b/crates/analyzer/tests/snapshots/errors__shadow_builtin_type.snap @@ -57,4 +57,16 @@ error: type name conflicts with built-in object 10 │ type self = u8 │ ^^^^ `self` is a built-in object +error: function parameter name `u8` conflicts with built-in type + ┌─ compile_errors/shadow_builtin_type.fe:13:8 + │ +13 │ fn f(u8: u256): + │ ^^ `u8` is a built-in type + +error: function parameter name `keccak256` conflicts with built-in function + ┌─ compile_errors/shadow_builtin_type.fe:16:8 + │ +16 │ fn g(keccak256: u8): + │ ^^^^^^^^^ `keccak256` is a built-in function + diff --git a/crates/analyzer/tests/snapshots/errors__string_constructor_no_type_arg_list.snap b/crates/analyzer/tests/snapshots/errors__string_constructor_no_type_arg_list.snap index db0301b58f..d742f08666 100644 --- a/crates/analyzer/tests/snapshots/errors__string_constructor_no_type_arg_list.snap +++ b/crates/analyzer/tests/snapshots/errors__string_constructor_no_type_arg_list.snap @@ -3,12 +3,12 @@ source: crates/analyzer/tests/errors.rs expression: "error_string(\"[snippet]\", &src)" --- -error: `String` type requires a max size argument +error: missing generic argument for type `String` ┌─ [snippet]:3:3 │ 3 │ String() - │ ^^^^^^ + │ ^^^^^^ expected 1 generic argument │ - = Example: String<100> + = Example: `String<32>` diff --git a/crates/analyzer/tests/snapshots/errors__string_constructor_no_type_args.snap b/crates/analyzer/tests/snapshots/errors__string_constructor_no_type_args.snap index 0d794bf0f4..fcbc44cf77 100644 --- a/crates/analyzer/tests/snapshots/errors__string_constructor_no_type_args.snap +++ b/crates/analyzer/tests/snapshots/errors__string_constructor_no_type_args.snap @@ -3,12 +3,12 @@ source: crates/analyzer/tests/errors.rs expression: "error_string(\"[snippet]\", &src)" --- -error: invalid `String` type argument - ┌─ [snippet]:3:9 +error: `String` expects 1 generic argument, but 0 were provided + ┌─ [snippet]:3:3 │ 3 │ String<>() - │ ^^ expected an integer - │ - = Example: String<100> + │ ^^^^^^-- supplied 0 arguments + │ │ + │ expects 1 generic argument diff --git a/crates/analyzer/tests/snapshots/errors__string_constructor_non_int_type_arg.snap b/crates/analyzer/tests/snapshots/errors__string_constructor_non_int_type_arg.snap index e55511083e..933e2332ab 100644 --- a/crates/analyzer/tests/snapshots/errors__string_constructor_non_int_type_arg.snap +++ b/crates/analyzer/tests/snapshots/errors__string_constructor_non_int_type_arg.snap @@ -3,12 +3,10 @@ source: crates/analyzer/tests/errors.rs expression: "error_string(\"[snippet]\", &src)" --- -error: invalid `String` type argument - ┌─ [snippet]:3:9 +error: `String` max size must be an integer + ┌─ [snippet]:3:10 │ 3 │ String() - │ ^^^^^ expected an integer - │ - = Example: String<100> + │ ^^^ expected an integer diff --git a/crates/analyzer/tests/snapshots/errors__string_constructor_two_int_type_args.snap b/crates/analyzer/tests/snapshots/errors__string_constructor_two_int_type_args.snap index 304bae10f4..6d5e294d23 100644 --- a/crates/analyzer/tests/snapshots/errors__string_constructor_two_int_type_args.snap +++ b/crates/analyzer/tests/snapshots/errors__string_constructor_two_int_type_args.snap @@ -3,12 +3,12 @@ source: crates/analyzer/tests/errors.rs expression: "error_string(\"[snippet]\", &src)" --- -error: invalid `String` type argument - ┌─ [snippet]:3:9 +error: `String` expects 1 generic argument, but 2 were provided + ┌─ [snippet]:3:3 │ 3 │ String<1, 2>() - │ ^^^^^^ expected an integer - │ - = Example: String<100> + │ ^^^^^^ - - supplied 2 generic arguments + │ │ + │ expects 1 generic argument diff --git a/crates/analyzer/tests/snapshots/errors__string_constructor_two_type_args.snap b/crates/analyzer/tests/snapshots/errors__string_constructor_two_type_args.snap index 668586e847..98196874b2 100644 --- a/crates/analyzer/tests/snapshots/errors__string_constructor_two_type_args.snap +++ b/crates/analyzer/tests/snapshots/errors__string_constructor_two_type_args.snap @@ -3,12 +3,12 @@ source: crates/analyzer/tests/errors.rs expression: "error_string(\"[snippet]\", &src)" --- -error: invalid `String` type argument - ┌─ [snippet]:3:9 +error: `String` expects 1 generic argument, but 2 were provided + ┌─ [snippet]:3:3 │ 3 │ String<1, u8>() - │ ^^^^^^^ expected an integer - │ - = Example: String<100> + │ ^^^^^^ - -- supplied 2 generic arguments + │ │ + │ expects 1 generic argument diff --git a/crates/analyzer/tests/snapshots/errors__string_no_type_arg_list.snap b/crates/analyzer/tests/snapshots/errors__string_no_type_arg_list.snap index 0ff98670ca..aa46881a69 100644 --- a/crates/analyzer/tests/snapshots/errors__string_no_type_arg_list.snap +++ b/crates/analyzer/tests/snapshots/errors__string_no_type_arg_list.snap @@ -3,12 +3,12 @@ source: crates/analyzer/tests/errors.rs expression: "error_string(\"[snippet]\", &src)" --- -error: `String` type requires a max size argument +error: missing generic argument for type `String` ┌─ [snippet]:3:10 │ 3 │ let x: String - │ ^^^^^^ + │ ^^^^^^ expected 1 generic argument │ - = Example: String<100> + = Example: `String<32>` diff --git a/crates/analyzer/tests/snapshots/errors__string_no_type_args.snap b/crates/analyzer/tests/snapshots/errors__string_no_type_args.snap index ebe5d9e19f..f4ceea688b 100644 --- a/crates/analyzer/tests/snapshots/errors__string_no_type_args.snap +++ b/crates/analyzer/tests/snapshots/errors__string_no_type_args.snap @@ -3,12 +3,12 @@ source: crates/analyzer/tests/errors.rs expression: "error_string(\"[snippet]\", &src)" --- -error: invalid `String` type argument - ┌─ [snippet]:3:16 +error: `String` expects 1 generic argument, but 0 were provided + ┌─ [snippet]:3:10 │ 3 │ let x: String<> - │ ^^ expected an integer - │ - = Example: String<100> + │ ^^^^^^-- supplied 0 arguments + │ │ + │ expects 1 generic argument diff --git a/crates/analyzer/tests/snapshots/errors__string_non_int_type_arg.snap b/crates/analyzer/tests/snapshots/errors__string_non_int_type_arg.snap index bfe9e54a43..02058f92a4 100644 --- a/crates/analyzer/tests/snapshots/errors__string_non_int_type_arg.snap +++ b/crates/analyzer/tests/snapshots/errors__string_non_int_type_arg.snap @@ -3,12 +3,10 @@ source: crates/analyzer/tests/errors.rs expression: "error_string(\"[snippet]\", &src)" --- -error: invalid `String` type argument - ┌─ [snippet]:3:16 +error: `String` max size must be an integer + ┌─ [snippet]:3:17 │ 3 │ let x: String - │ ^^^^ expected an integer - │ - = Example: String<100> + │ ^^ expected an integer diff --git a/crates/analyzer/tests/snapshots/errors__string_two_int_type_args.snap b/crates/analyzer/tests/snapshots/errors__string_two_int_type_args.snap index 31d4b056e7..33f19737f1 100644 --- a/crates/analyzer/tests/snapshots/errors__string_two_int_type_args.snap +++ b/crates/analyzer/tests/snapshots/errors__string_two_int_type_args.snap @@ -3,12 +3,12 @@ source: crates/analyzer/tests/errors.rs expression: "error_string(\"[snippet]\", &src)" --- -error: invalid `String` type argument - ┌─ [snippet]:3:16 +error: `String` expects 1 generic argument, but 2 were provided + ┌─ [snippet]:3:10 │ 3 │ let x: String<1, 2> - │ ^^^^^^ expected an integer - │ - = Example: String<100> + │ ^^^^^^ - - supplied 2 generic arguments + │ │ + │ expects 1 generic argument diff --git a/crates/analyzer/tests/snapshots/errors__string_two_type_args.snap b/crates/analyzer/tests/snapshots/errors__string_two_type_args.snap index 937e951585..eeccb0a97a 100644 --- a/crates/analyzer/tests/snapshots/errors__string_two_type_args.snap +++ b/crates/analyzer/tests/snapshots/errors__string_two_type_args.snap @@ -3,12 +3,12 @@ source: crates/analyzer/tests/errors.rs expression: "error_string(\"[snippet]\", &src)" --- -error: invalid `String` type argument - ┌─ [snippet]:3:16 +error: `String` expects 1 generic argument, but 2 were provided + ┌─ [snippet]:3:10 │ 3 │ let x: String<1, u8> - │ ^^^^^^^ expected an integer - │ - = Example: String<100> + │ ^^^^^^ - -- supplied 2 generic arguments + │ │ + │ expects 1 generic argument diff --git a/crates/analyzer/tests/snapshots/errors__undefined_generic_type.snap b/crates/analyzer/tests/snapshots/errors__undefined_generic_type.snap index c31e2fc0b8..4c03d0f540 100644 --- a/crates/analyzer/tests/snapshots/errors__undefined_generic_type.snap +++ b/crates/analyzer/tests/snapshots/errors__undefined_generic_type.snap @@ -7,6 +7,6 @@ error: undefined type ┌─ [snippet]:3:10 │ 3 │ let x: foobar = 10 - │ ^^^^^^ this type name has not been defined + │ ^^^^^^ `foobar` has not been defined diff --git a/crates/analyzer/tests/snapshots/errors__undefined_type.snap b/crates/analyzer/tests/snapshots/errors__undefined_type.snap index 1bcf57fdde..c4cde3502a 100644 --- a/crates/analyzer/tests/snapshots/errors__undefined_type.snap +++ b/crates/analyzer/tests/snapshots/errors__undefined_type.snap @@ -7,6 +7,6 @@ error: undefined type ┌─ [snippet]:3:10 │ 3 │ let x: foobar = 10 - │ ^^^^^^ this type name has not been defined + │ ^^^^^^ `foobar` has not been defined diff --git a/crates/analyzer/tests/snapshots/errors__undefined_type_param.snap b/crates/analyzer/tests/snapshots/errors__undefined_type_param.snap index 0742a6885f..21db7e4c5e 100644 --- a/crates/analyzer/tests/snapshots/errors__undefined_type_param.snap +++ b/crates/analyzer/tests/snapshots/errors__undefined_type_param.snap @@ -7,12 +7,12 @@ error: undefined type ┌─ compile_errors/undefined_type_param.fe:4:8 │ 4 │ x: MysteryType - │ ^^^^^^^^^^^ this type name has not been defined + │ ^^^^^^^^^^^ `MysteryType` has not been defined error: undefined type ┌─ compile_errors/undefined_type_param.fe:8:19 │ 8 │ pub fn a(val: DoesntExist): - │ ^^^^^^^^^^^ this type name has not been defined + │ ^^^^^^^^^^^ `DoesntExist` has not been defined diff --git a/crates/lowering/src/mappers/module.rs b/crates/lowering/src/mappers/module.rs index dd6be2e935..5410ebf055 100644 --- a/crates/lowering/src/mappers/module.rs +++ b/crates/lowering/src/mappers/module.rs @@ -2,7 +2,7 @@ use crate::context::ModuleContext; use crate::mappers::{contracts, types}; use crate::names; use crate::utils::ZeroSpanNode; -use fe_analyzer::namespace::items::{ModuleId, TypeDefId}; +use fe_analyzer::namespace::items::{Item, ModuleId, TypeDef}; use fe_analyzer::namespace::types::{Base, FixedSize, Tuple}; use fe_analyzer::AnalyzerDb; use fe_parser::ast; @@ -24,26 +24,33 @@ pub fn module(db: &dyn AnalyzerDb, module: ModuleId) -> ast::Module { }) .collect::>(); - lowered_body.extend(module.all_type_defs(db).iter().map(|def| match def { - TypeDefId::Alias(id) => { - let node = &id.data(db).ast; - let name = node.kind.name.clone(); - ast::ModuleStmt::TypeAlias(Node::new( - ast::TypeAlias { - name, - typ: types::type_desc( - &mut context, - node.kind.typ.clone(), - &id.typ(db).expect("type alias error"), - ), - }, - id.span(db), - )) - } - TypeDefId::Struct(id) => ast::ModuleStmt::Struct(id.data(db).ast.clone()), - TypeDefId::Contract(id) => { - ast::ModuleStmt::Contract(contracts::contract_def(&mut context, *id)) - } + lowered_body.extend(module.all_items(db).iter().map(|item| match item { + Item::Type(typedef) => match typedef { + TypeDef::Alias(id) => { + let node = &id.data(db).ast; + let name = node.kind.name.clone(); + ast::ModuleStmt::TypeAlias(Node::new( + ast::TypeAlias { + name, + typ: types::type_desc( + &mut context, + node.kind.typ.clone(), + &id.typ(db).expect("type alias error"), + ), + }, + id.span(db), + )) + } + TypeDef::Struct(id) => ast::ModuleStmt::Struct(id.data(db).ast.clone()), + TypeDef::Contract(id) => { + ast::ModuleStmt::Contract(contracts::contract_def(&mut context, *id)) + } + TypeDef::Primitive(_) => unreachable!(), + }, + Item::GenericType(_) => todo!("generic types can't be defined in fe yet"), + Item::Event(_) => todo!("events can't be defined at the module level yet"), + Item::Function(_) => todo!("functions can't be defined at the module level yet"), + Item::BuiltinFunction(_) | Item::Object(_) => unreachable!("special built-in stuff"), })); let struct_defs_from_tuples = context diff --git a/crates/parser/src/ast.rs b/crates/parser/src/ast.rs index a9670b3b30..a3d63853b9 100644 --- a/crates/parser/src/ast.rs +++ b/crates/parser/src/ast.rs @@ -74,6 +74,7 @@ pub struct Struct { #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)] pub enum TypeDesc { Unit, + // TODO: change `Base { base: String }` to `Name(String)` Base { base: String, }, @@ -347,6 +348,13 @@ impl Node { FunctionArg::Zelf => "self", } } + + pub fn name_span(&self) -> Span { + match &self.kind { + FunctionArg::Regular(arg) => arg.name.span, + FunctionArg::Zelf => self.span, + } + } } impl Node { diff --git a/crates/test-files/fixtures/compile_errors/not_callable.fe b/crates/test-files/fixtures/compile_errors/not_callable.fe index 5bb48df3ee..0cabaf516f 100644 --- a/crates/test-files/fixtures/compile_errors/not_callable.fe +++ b/crates/test-files/fixtures/compile_errors/not_callable.fe @@ -1,4 +1,16 @@ + contract Foo: + event MyEvent: + x: u8 + + fn call_int(): + 5() + + fn call_event(): + MyEvent(x=10) + + fn call_block(): + block() - pub fn bar(): - 5() \ No newline at end of file + fn call_self(self): + self() diff --git a/crates/test-files/fixtures/compile_errors/shadow_builtin_type.fe b/crates/test-files/fixtures/compile_errors/shadow_builtin_type.fe index f763179490..4bd8387a3e 100644 --- a/crates/test-files/fixtures/compile_errors/shadow_builtin_type.fe +++ b/crates/test-files/fixtures/compile_errors/shadow_builtin_type.fe @@ -7,4 +7,11 @@ type keccak256 = u8 type block = u8 type msg = u8 type chain = u8 -type self = u8 \ No newline at end of file +type self = u8 + +contract C: + fn f(u8: u256): + pass + + fn g(keccak256: u8): + pass diff --git a/crates/yulgen/src/mappers/expressions.rs b/crates/yulgen/src/mappers/expressions.rs index 0b3ca4d0a8..a0ac8be4d7 100644 --- a/crates/yulgen/src/mappers/expressions.rs +++ b/crates/yulgen/src/mappers/expressions.rs @@ -85,16 +85,14 @@ fn expr_call(context: &mut FnContext, exp: &Node) -> yul::Expression { .collect(); return match call_type { - CallType::BuiltinFunction { func } => match func { + CallType::BuiltinFunction(func) => match func { GlobalMethod::Keccak256 => { let first_arg = &args.kind.first().expect("Missing argument").kind.value; let attributes = context .expression_attributes(first_arg) .expect("missing attributes"); let size = FixedSize::try_from(attributes.typ.clone()).expect("Invalid type"); - let func_name: &str = func.into(); - - let func_name = identifier! { (func_name) }; + let func_name = identifier! { (func.as_ref()) }; let size = identifier_expression! { (size.size()) }; expression! { [func_name]([yul_args[0].to_owned()], [size]) } } @@ -103,10 +101,15 @@ fn expr_call(context: &mut FnContext, exp: &Node) -> yul::Expression { typ: Type::Struct(val), } => struct_operations::new(&val, yul_args), CallType::TypeConstructor { .. } => yul_args[0].to_owned(), - CallType::SelfAttribute { func_name, .. } | CallType::Pure { func_name } => { + CallType::PureFunction(func) => { + let func_name = names::func_name(&func.name(context.db)); + expression! { [func_name]([yul_args...]) } + } + CallType::SelfAttribute { func_name, .. } => { let func_name = names::func_name(&func_name); expression! { [func_name]([yul_args...]) } } + CallType::ValueAttribute => { if let fe::Expr::Attribute { value, attr } = &func.kind { let value_attributes = context diff --git a/crates/yulgen/src/names/mod.rs b/crates/yulgen/src/names/mod.rs index f29aa257d3..e273a033ac 100644 --- a/crates/yulgen/src/names/mod.rs +++ b/crates/yulgen/src/names/mod.rs @@ -7,14 +7,13 @@ pub mod abi; /// Generate a function name to perform checked addition pub fn checked_add(size: &Integer) -> yul::Identifier { - let size: &str = size.into(); - identifier! {(format!("checked_add_{}", size.to_lowercase()))} + identifier! {(format!("checked_add_{}", size.as_ref().to_lowercase()))} } /// Generate a function name to perform checked division pub fn checked_div(size: &Integer) -> yul::Identifier { let size: &str = if size.is_signed() { - size.into() + size.as_ref() } else { "unsigned" }; @@ -33,20 +32,18 @@ pub fn checked_mod(size: &Integer) -> yul::Identifier { /// Generate a function name to perform checked exponentiation pub fn checked_exp(size: &Integer) -> yul::Identifier { - let size: &str = size.into(); - identifier! {(format!("checked_exp_{}", size.to_lowercase()))} + identifier! {(format!("checked_exp_{}", size.as_ref().to_lowercase()))} } /// Generate a function name to perform checked multiplication pub fn checked_mul(size: &Integer) -> yul::Identifier { - let size: &str = size.into(); - identifier! {(format!("checked_mul_{}", size.to_lowercase()))} + identifier! {(format!("checked_mul_{}", size.as_ref().to_lowercase()))} } /// Generate a function name to perform checked subtraction pub fn checked_sub(size: &Integer) -> yul::Identifier { let size: &str = if size.is_signed() { - size.into() + size.as_ref() } else { "unsigned" }; @@ -55,8 +52,7 @@ pub fn checked_sub(size: &Integer) -> yul::Identifier { /// Generate a function name to adjust the size of the integer pub fn adjust_numeric_size(size: &Integer) -> yul::Identifier { - let size: &str = size.into(); - identifier! {(format!("adjust_numeric_{}", size.to_lowercase()))} + identifier! {(format!("adjust_numeric_{}", size.as_ref().to_lowercase()))} } /// Generate a safe function name for a user defined function