Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support events outside of contracts #654

Merged
merged 1 commit into from
Feb 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
2 │ idx sender: address
│ ^^^^^^^^^^^^^^^^^^^ address
3 │ idx receiver: address
│ ^^^^^^^^^^^^^^^^^^^^^ address
4 │ value: 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
7 │ emit Transfer(sender=msg.sender, receiver=to, value)
│ ^^^^^^^^^^ ^^ ^^^^^ u256: Value
│ │ │
│ │ address: Value
│ address: Value

note:
┌─ module_level_events.fe:7:9
7 │ emit 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