Skip to content

Commit

Permalink
Add support for module level events
Browse files Browse the repository at this point in the history
  • Loading branch information
Maltby committed Feb 10, 2022
1 parent 9d413f6 commit ed504fa
Show file tree
Hide file tree
Showing 16 changed files with 527 additions and 61 deletions.
3 changes: 2 additions & 1 deletion crates/analyzer/src/db/queries/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,8 @@ pub fn contract_all_events(db: &dyn AnalyzerDb, contract: ContractId) -> Rc<[Eve
ast::ContractStmt::Function(_) => None,
ast::ContractStmt::Event(node) => Some(db.intern_event(Rc::new(items::Event {
ast: node.clone(),
contract,
module: contract.module(db),
contract: Some(contract),
}))),
})
.collect()
Expand Down
10 changes: 7 additions & 3 deletions crates/analyzer/src/db/queries/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use crate::context::{Analysis, AnalyzerContext, Constant};
use crate::db::AnalyzerDb;
use crate::errors::{self, ConstEvalError, TypeError};
use crate::namespace::items::{
Contract, ContractId, Function, Item, ModuleConstant, ModuleConstantId, ModuleId, ModuleSource,
Struct, StructId, TypeAlias, TypeDef,
Contract, ContractId, Event, Function, Item, ModuleConstant, ModuleConstantId, ModuleId,
ModuleSource, Struct, StructId, TypeAlias, TypeDef,
};
use crate::namespace::scopes::ItemScope;
use crate::namespace::types::{self, Type};
Expand Down Expand Up @@ -98,7 +98,11 @@ pub fn module_all_items(db: &dyn AnalyzerDb, module: ModuleId) -> Rc<[Item]> {
}
ast::ModuleStmt::Pragma(_) => None,
ast::ModuleStmt::Use(_) => None,
ast::ModuleStmt::Event(_) => todo!(),
ast::ModuleStmt::Event(node) => Some(Item::Event(db.intern_event(Rc::new(Event {
ast: node.clone(),
module,
contract: None,
})))),
ast::ModuleStmt::ParseError(_) => None,
})
.collect()
Expand Down
11 changes: 8 additions & 3 deletions crates/analyzer/src/namespace/items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1311,7 +1311,8 @@ impl StructFieldId {
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct Event {
pub ast: Node<ast::Event>,
pub contract: ContractId,
pub module: ModuleId,
pub contract: Option<ContractId>,
}

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone)]
Expand All @@ -1332,10 +1333,14 @@ impl EventId {
db.event_type(*self).value
}
pub fn module(&self, db: &dyn AnalyzerDb) -> ModuleId {
self.data(db).contract.module(db)
self.data(db).module
}
pub fn parent(&self, db: &dyn AnalyzerDb) -> Item {
Item::Type(TypeDef::Contract(self.data(db).contract))
if let Some(contract_id) = self.data(db).contract {
Item::Type(TypeDef::Contract(contract_id))
} else {
Item::Module(self.module(db))
}
}
pub fn sink_diagnostics(&self, db: &dyn AnalyzerDb, sink: &mut impl DiagnosticSink) {
sink.push_all(db.event_type(*self).diagnostics.iter());
Expand Down
19 changes: 8 additions & 11 deletions crates/analyzer/tests/analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ test_analysis! { create_contract, "features/create_contract.fe"}
test_analysis! { create_contract_from_init, "features/create_contract_from_init.fe"}
test_analysis! { empty, "features/empty.fe"}
test_analysis! { events, "features/events.fe"}
test_analysis! { module_level_events, "features/module_level_events.fe"}
test_analysis! { external_contract, "features/external_contract.fe"}
test_analysis! { for_loop_with_break, "features/for_loop_with_break.fe"}
test_analysis! { for_loop_with_continue, "features/for_loop_with_continue.fe"}
Expand Down Expand Up @@ -308,12 +309,11 @@ fn build_snapshot(db: &dyn AnalyzerDb, module: items::ModuleId) -> String {
)],
Item::Type(TypeDef::Struct(struct_)) => [
label_in_non_overlapping_groups(
&struct_
.fields(db)
.values()
.map(|field| (field.data(db).ast.span, field.typ(db).unwrap()))
.collect::<Vec<_>>(),

&struct_
.fields(db)
.values()
.map(|field| (field.data(db).ast.span, field.typ(db).unwrap()))
.collect::<Vec<_>>(),
),
struct_
.functions(db)
Expand Down Expand Up @@ -348,12 +348,9 @@ fn build_snapshot(db: &dyn AnalyzerDb, module: items::ModuleId) -> String {

Item::Function(id) => function_diagnostics(*id, db),
Item::Constant(id) => vec![build_display_diagnostic(id.span(db), &id.typ(db).unwrap())],


// Events can't be defined at the module level yet.
Item::Event(_)
Item::Event(id) => event_diagnostics(*id, db),
// Built-in stuff
| Item::Type(TypeDef::Primitive(_))
Item::Type(TypeDef::Primitive(_))
| Item::GenericType(_)
| Item::BuiltinFunction(_)
| Item::Intrinsic(_)
Expand Down
102 changes: 102 additions & 0 deletions crates/analyzer/tests/snapshots/analysis__module_level_events.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
---
source: crates/analyzer/tests/analysis.rs
expression: "build_snapshot(&db, module)"

---
note:
┌─ module_level_events.fe:2:5
2idx sender: address
^^^^^^^^^^^^^^^^^^^ address
3idx receiver: address
^^^^^^^^^^^^^^^^^^^^^ address
4value: u256
^^^^^^^^^^^ u256

note:
┌─ module_level_events.fe:6:5
6 │ ╭ fn transfer(to : address, value : u256):
7 │ │ emit Transfer(sender=msg.sender, receiver=to, value)
│ ╰────────────────────────────────────────────────────────────^ attributes hash: 5430479256040439660
= FunctionSignature {
self_decl: None,
params: [
FunctionParam {
name: "to",
typ: Ok(
Base(
Address,
),
),
},
FunctionParam {
name: "value",
typ: Ok(
Base(
Numeric(
U256,
),
),
),
},
],
return_type: Ok(
Base(
Unit,
),
),
}

note:
┌─ module_level_events.fe:7:30
7emit Transfer(sender=msg.sender, receiver=to, value)
^^^^^^^^^^ ^^ ^^^^^ u256: Value
│ │ │
│ │ address: Value
address: Value

note:
┌─ module_level_events.fe:7:9
7emit Transfer(sender=msg.sender, receiver=to, value)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attributes hash: 17986960071624595337
= Event {
name: "Transfer",
fields: [
EventField {
name: "sender",
typ: Ok(
Base(
Address,
),
),
is_indexed: true,
},
EventField {
name: "receiver",
typ: Ok(
Base(
Address,
),
),
is_indexed: true,
},
EventField {
name: "value",
typ: Ok(
Base(
Numeric(
U256,
),
),
),
is_indexed: false,
},
],
}


44 changes: 3 additions & 41 deletions crates/lowering/src/mappers/contracts.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use crate::context::ModuleContext;
use crate::mappers::{functions, types};
use crate::utils::ZeroSpanNode;
use fe_analyzer::namespace::items::{ContractFieldId, ContractId, EventId};
use crate::mappers::{events, functions, types};
use fe_analyzer::namespace::items::{ContractFieldId, ContractId};
use fe_parser::ast;
use fe_parser::node::Node;

Expand All @@ -17,7 +16,7 @@ pub fn contract_def(context: &mut ModuleContext, contract: ContractId) -> Node<a
let events = contract
.events(db)
.values()
.map(|event| ast::ContractStmt::Event(event_def(context, *event)))
.map(|event| ast::ContractStmt::Event(events::event_def(context, *event)))
.collect::<Vec<_>>();

let mut functions = contract
Expand Down Expand Up @@ -64,40 +63,3 @@ fn contract_field(context: &mut ModuleContext, field: ContractFieldId) -> Node<a
node.span,
)
}

fn event_def(context: &mut ModuleContext, event: EventId) -> Node<ast::Event> {
let ast_fields = &event.data(context.db).ast.kind.fields;
let fields = event
.typ(context.db)
.fields
.iter()
.zip(ast_fields.iter())
.map(|(field, node)| {
ast::EventField {
is_idx: field.is_indexed,
name: field.name.clone().into_node(),
typ: types::type_desc(
context,
node.kind.typ.clone(),
&field
.typ
.as_ref()
.expect("event field type error")
.clone()
.into(),
),
}
.into_node()
})
.collect();

let node = &event.data(context.db).ast;
Node::new(
ast::Event {
name: node.kind.name.clone(),
fields,
pub_qual: None,
},
node.span,
)
}
43 changes: 43 additions & 0 deletions crates/lowering/src/mappers/events.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use crate::context::ModuleContext;
use crate::mappers::types;
use crate::utils::ZeroSpanNode;
use fe_analyzer::namespace::items::EventId;
use fe_parser::ast;
use fe_parser::node::Node;

pub fn event_def(context: &mut ModuleContext, event: EventId) -> Node<ast::Event> {
let ast_fields = &event.data(context.db).ast.kind.fields;
let fields = event
.typ(context.db)
.fields
.iter()
.zip(ast_fields.iter())
.map(|(field, node)| {
ast::EventField {
is_idx: field.is_indexed,
name: field.name.clone().into_node(),
typ: types::type_desc(
context,
node.kind.typ.clone(),
&field
.typ
.as_ref()
.expect("event field type error")
.clone()
.into(),
),
}
.into_node()
})
.collect();

let node = &event.data(context.db).ast;
Node::new(
ast::Event {
name: node.kind.name.clone(),
fields,
pub_qual: None,
},
node.span,
)
}
1 change: 1 addition & 0 deletions crates/lowering/src/mappers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mod contracts;
mod events;
mod expressions;
mod functions;
pub mod module;
Expand Down
4 changes: 2 additions & 2 deletions crates/lowering/src/mappers/module.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::context::ModuleContext;
use crate::mappers::{contracts, functions, structs, types};
use crate::mappers::{contracts, events, functions, structs, types};
use crate::names;
use crate::utils::ZeroSpanNode;
use fe_analyzer::namespace::items::{Item, ModuleId, TypeDef};
Expand Down Expand Up @@ -57,7 +57,7 @@ pub fn module(db: &dyn AnalyzerDb, module: ModuleId) -> ast::Module {
))),

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::Event(id) => Some(ast::ModuleStmt::Event(events::event_def(&mut context, *id))),
Item::BuiltinFunction(_) | Item::Intrinsic(_) | Item::Object(_) => {
unreachable!("special built-in stuff")
}
Expand Down
1 change: 1 addition & 0 deletions crates/lowering/tests/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,6 @@ test_file! { module_fn, "lowering/module_fn.fe" }
test_file! { struct_fn, "lowering/struct_fn.fe" }
test_file! { ternary, "lowering/ternary.fe" }
test_file! { and_or, "lowering/and_or.fe" }
test_file! { module_level_events, "lowering/module_level_events.fe" }
// TODO: the analyzer rejects lowered nested tuples.
// test_file!(array_tuple, "lowering/array_tuple.fe");
14 changes: 14 additions & 0 deletions crates/lowering/tests/snapshots/lowering__module_level_events.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
source: crates/lowering/tests/lowering.rs
expression: lowered_code

---
event Transfer:
idx sender: address
idx receiver: address
value: u256

contract Foo:
fn transfer(to: address, value: u256) -> ():
emit Transfer(sender=msg.sender, receiver=to, value)
return ()
10 changes: 10 additions & 0 deletions crates/parser/tests/cases/parse_ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,3 +235,13 @@ contract GuestBook:
pub fn get_msg(self, addr: address) -> BookMsg:
return self.guest_book[addr]
"# }

test_parse! { module_level_events, try_parse_module, r#"
event Transfer:
idx sender: address
idx receiver: address
value: u256
contract Foo:
fn transfer(to : address, value : u256):
emit Transfer(sender=msg.sender, receiver=to, value)
"# }
Loading

0 comments on commit ed504fa

Please sign in to comment.