diff --git a/crates/analyzer/src/namespace/items.rs b/crates/analyzer/src/namespace/items.rs index 2877303966..1368445536 100644 --- a/crates/analyzer/src/namespace/items.rs +++ b/crates/analyzer/src/namespace/items.rs @@ -814,26 +814,17 @@ pub enum TypeDef { Contract(ContractId), Primitive(types::Base), } + impl TypeDef { pub fn items(&self, db: &dyn AnalyzerDb) -> Rc> { match self { TypeDef::Struct(val) => { - Rc::new( - val.functions(db) - .iter() - .filter_map(|(name, field)| { - if field.takes_self(db) { - // In the future we probably want to resolve instance methods as well. But this would require - // the caller to pass an instance as the first argument e.g. `Rectangle::can_hold(self_instance, other)`. - // This isn't yet supported so for now path access to functions is limited to static functions only. - None - } else { - Some((name.to_owned(), Item::Function(*field))) - } - }) - .collect(), - ) + // In the future we probably want to resolve instance methods as well. But this would require + // the caller to pass an instance as the first argument e.g. `Rectangle::can_hold(self_instance, other)`. + // This isn't yet supported so for now path access to functions is limited to static functions only. + val.pure_functions_as_items(db) } + TypeDef::Contract(val) => val.pure_functions_as_items(db), _ => todo!("cannot access items in types yet"), } } @@ -1323,6 +1314,37 @@ impl FunctionId { } } +trait FunctionsAsItems { + fn functions(&self, db: &dyn AnalyzerDb) -> Rc>; + + fn pure_functions_as_items(&self, db: &dyn AnalyzerDb) -> Rc> { + Rc::new( + self.functions(db) + .iter() + .filter_map(|(name, field)| { + if field.takes_self(db) { + None + } else { + Some((name.to_owned(), Item::Function(*field))) + } + }) + .collect(), + ) + } +} + +impl FunctionsAsItems for StructId { + fn functions(&self, db: &dyn AnalyzerDb) -> Rc> { + self.functions(db) + } +} + +impl FunctionsAsItems for ContractId { + fn functions(&self, db: &dyn AnalyzerDb) -> Rc> { + self.functions(db) + } +} + #[derive(Debug, PartialEq, Eq, Hash, Clone)] pub struct Struct { pub ast: Node, diff --git a/crates/analyzer/src/traversal/expressions.rs b/crates/analyzer/src/traversal/expressions.rs index 520e14e4d8..c9ca77a81b 100644 --- a/crates/analyzer/src/traversal/expressions.rs +++ b/crates/analyzer/src/traversal/expressions.rs @@ -1459,7 +1459,8 @@ fn expr_call_method( // and the global objects are replaced by `Context`, we can remove this. // All other `NamedThing`s will be handled correctly by `expr()`. if let fe::Expr::Name(name) = &target.kind { - if let Ok(Some(NamedThing::Item(Item::Type(def)))) = context.resolve_name(name, target.span) { + if let Ok(Some(NamedThing::Item(Item::Type(def)))) = context.resolve_name(name, target.span) + { let typ = def.typ(context.db())?; return expr_call_type_attribute(context, typ, target.span, field, generic_args, args); } diff --git a/crates/test-files/fixtures/features/contract_pure_fns.fe b/crates/test-files/fixtures/features/contract_pure_fns.fe new file mode 100644 index 0000000000..0d0f46f1e3 --- /dev/null +++ b/crates/test-files/fixtures/features/contract_pure_fns.fe @@ -0,0 +1,19 @@ +contract Example { + + pub fn run_test(self) { + assert self.bar(42, 26) == 68 + assert self.bar2(42, 26) == 68 + } + + pub fn bar(self, _ x: u256, _ y: u256) -> u256 { + return pure_bar(x, y) + } + + pub fn bar2(self, _ x: u256, _ y: u256) -> u256 { + return Example::pure_bar(x, y) + } + + fn pure_bar(x: u256, y: u256) -> u256 { + return x + y + } +} diff --git a/crates/tests/src/features.rs b/crates/tests/src/features.rs index 3b85f067d7..92f46eee35 100644 --- a/crates/tests/src/features.rs +++ b/crates/tests/src/features.rs @@ -2070,7 +2070,8 @@ fn ctx_init_in_call() { fixture_file, case::simple_traits("simple_traits.fe"), case::generic_functions("generic_functions.fe"), - case::generic_functions_primitves("generic_functions_primitves.fe") + case::generic_functions_primitves("generic_functions_primitves.fe"), + case::contract_pure_fns("contract_pure_fns.fe") )] fn execution_tests(fixture_file: &str) { with_executor(&|mut executor| { diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_4_contract_pure_fns.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_4_contract_pure_fns.snap new file mode 100644 index 0000000000..09b509cccb --- /dev/null +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_4_contract_pure_fns.snap @@ -0,0 +1,7 @@ +--- +source: crates/tests/src/features.rs +expression: "format!(\"{}\", harness.gas_reporter)" + +--- +run_test([]) used 35 gas + diff --git a/newsfragments/767.feature.md b/newsfragments/767.feature.md new file mode 100644 index 0000000000..3f17922430 --- /dev/null +++ b/newsfragments/767.feature.md @@ -0,0 +1 @@ +Allow contract associated functions to be called via `ContractName::function_name()` syntax.