Skip to content

Commit

Permalink
ctx rule change (contract type cast, external fn calls) (#703)
Browse files Browse the repository at this point in the history
- not required for contract type cast, eg `Foo(address(0))`
- is required when calling external contract fn that takes ctx
  • Loading branch information
sbillig authored Jun 1, 2022
1 parent 7f12b32 commit cacaefd
Show file tree
Hide file tree
Showing 34 changed files with 309 additions and 443 deletions.
27 changes: 0 additions & 27 deletions crates/analyzer/src/namespace/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,33 +379,6 @@ pub struct EventField {
pub is_indexed: bool,
}

impl FunctionSignature {
/// Parameters without `ctx`, if it is a contract function that declares it.
///
/// This is used when calling a contract method externally.
pub fn external_params(&self) -> &[FunctionParam] {
if self.ctx_decl.is_some() {
&self.params[1..]
} else {
&self.params
}
}

/// Parameter types without `ctx`, if it is a contract function that
/// declares it.
///
/// This is used when calling a contract method externally.
///
/// # Panics
/// Panics if any param type is an `Err`
pub fn external_param_types(&self) -> Vec<Type> {
self.external_params()
.iter()
.map(|param| param.typ.clone().expect("fn param type error"))
.collect()
}
}

impl Type {
pub fn name(&self) -> SmolStr {
match self {
Expand Down
1 change: 1 addition & 0 deletions crates/analyzer/src/traversal/call_args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ pub fn validate_named_args(
params: &[impl LabeledParameter],
) -> Result<(), FatalError> {
validate_arg_count(context, name, name_span, args, params.len(), "argument");
// TODO: if the first arg is missing, every other arg will get a label and type error

for (index, (param, arg)) in params.iter().zip(args.kind.iter()).enumerate() {
let expected_label = param.label();
Expand Down
68 changes: 8 additions & 60 deletions crates/analyzer/src/traversal/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1147,15 +1147,9 @@ fn expr_call_type_constructor(
_ => {}
}

if matches!(typ, Type::Contract(_)) {
validate_arg_count(context, &format!("{}", typ), name_span, args, 2, "argument");
expect_no_label_on_arg(context, args, 0);
expect_no_label_on_arg(context, args, 1);
} else {
// These all expect 1 arg, for now.
validate_arg_count(context, &format!("{}", typ), name_span, args, 1, "argument");
expect_no_label_on_arg(context, args, 0);
}
// These all expect 1 arg, for now.
validate_arg_count(context, &format!("{}", typ), name_span, args, 1, "argument");
expect_no_label_on_arg(context, args, 0);

let expr_attrs = match &typ {
Type::String(string_type) => {
Expand All @@ -1166,50 +1160,10 @@ fn expr_call_type_constructor(
ExpressionAttributes::new(typ.clone(), Location::Memory)
}
Type::Contract(_) => {
if let Some(first_arg) = &args.kind.get(0) {
let first_arg_attr = assignable_expr(context, &first_arg.kind.value, None)?;
if let Some(context_type) = context.get_context_type() {
if first_arg_attr.typ != Type::Struct(context_type.clone()) {
context.type_error(
"type mismatch",
first_arg.span,
&context_type,
&first_arg_attr.typ,
);
}
} else {
context.fancy_error(
"`Context` is not defined",
vec![
Label::primary(
args.span,
"`ctx` must be defined and passed into the contract constructor",
),
Label::secondary(
context.parent_function().name_span(context.db()),
"Note: declare `ctx` in this function signature",
),
Label::secondary(
context.parent_function().name_span(context.db()),
"Example: `pub fn foo(ctx: Context, ...)`",
),
],
vec![
"Note: import context with `use std::context::Context`".into(),
"Example: MyContract(ctx, contract_address)".into(),
],
);
}
}
if let Some(second_arg) = &args.kind.get(1) {
let second_arg_attr = assignable_expr(context, &second_arg.kind.value, None)?;
if second_arg_attr.typ != Type::Base(Base::Address) {
context.type_error(
"type mismatch",
second_arg.span,
&Base::Address,
&second_arg_attr.typ,
);
if let Some(arg) = &args.kind.get(0) {
let arg_attr = assignable_expr(context, &arg.kind.value, None)?;
if arg_attr.typ != Type::Base(Base::Address) {
context.type_error("type mismatch", arg.span, &Base::Address, &arg_attr.typ);
}
}
ExpressionAttributes::new(typ.clone(), Location::Value)
Expand Down Expand Up @@ -1397,13 +1351,7 @@ fn expr_call_method(
}

let sig = method.signature(context.db());

let params = if is_self {
&sig.params
} else {
sig.external_params()
};
validate_named_args(context, &field.kind, field.span, args, params)?;
validate_named_args(context, &field.kind, field.span, args, &sig.params)?;

let calltype = match class {
Class::Contract(contract) => {
Expand Down
3 changes: 1 addition & 2 deletions crates/analyzer/tests/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ test_stmt! { break_without_loop, "break" }
test_stmt! { break_without_loop_2, "if true { break }" }
test_stmt! { call_undefined_function_on_contract, "self.doesnt_exist()" }
test_stmt! { call_address_with_wrong_type, "address(true)" }
test_stmt! { call_address_with_label, "address(val: 0)" }
test_stmt! { call_keccak_without_parameter, "keccak256()" }
test_stmt! { call_keccak_with_wrong_type, "keccak256(true)" }
test_stmt! { call_keccak_with_2_args, "keccak256(1, 2)" }
Expand Down Expand Up @@ -323,12 +324,10 @@ test_file! { ctx_init }
test_file! { ctx_pure }
test_file! { ctx_undeclared }
test_file! { ctx_missing_internal_call }
test_file! { ctx_passed_external_call }
test_file! { ctx_missing_create }
test_file! { ctx_missing_load }
test_file! { ctx_missing_event }
test_file! { ctx_builtins_param_incorrect_type }
test_file! { ctx_undefined_contract_init }
test_file! { ctx_undefined_create }
test_file! { ctx_undefined_create2 }
test_file! { ctx_undefined_event }
65 changes: 24 additions & 41 deletions crates/analyzer/tests/snapshots/analysis__external_contract.snap
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,8 @@ note:
┌─ external_contract.fe:24:5
24 │ ╭ pub fn call_emit_event(ctx: Context, foo_address: address, my_num: u256, my_addrs: Array<address, 5>, my_string: String<11>) {
25 │ │ let foo: Foo = Foo(ctx, foo_address)
26 │ │ foo.emit_event(my_num, my_addrs, my_string)
25 │ │ let foo: Foo = Foo(foo_address)
26 │ │ foo.emit_event(ctx, my_num, my_addrs, my_string)
27 │ │ }
│ ╰─────^ attributes hash: 11744736773867210426
Expand Down Expand Up @@ -324,62 +324,47 @@ note:
note:
┌─ external_contract.fe:25:13
25let foo: Foo = Foo(ctx, foo_address)
25let foo: Foo = Foo(foo_address)
^^^ Foo

note:
┌─ external_contract.fe:25:28
25let foo: Foo = Foo(ctx, foo_address)
^^^ ^^^^^^^^^^^ address: Value
│ │
Context: Memory
25let foo: Foo = Foo(foo_address)
^^^^^^^^^^^ address: Value

note:
┌─ external_contract.fe:25:24
25let foo: Foo = Foo(ctx, foo_address)
^^^^^^^^^^^^^^^^^^^^^ Foo: Value
26foo.emit_event(my_num, my_addrs, my_string)
^^^ ^^^^^^ ^^^^^^^^ ^^^^^^^^^ String<11>: Memory
│ │ │ │
│ │ │ Array<address, 5>: Memory
│ │ u256: Value
25let foo: Foo = Foo(foo_address)
^^^^^^^^^^^^^^^^ Foo: Value
26foo.emit_event(ctx, my_num, my_addrs, my_string)
^^^ ^^^ ^^^^^^ ^^^^^^^^ ^^^^^^^^^ String<11>: Memory
│ │ │ │ │
│ │ │ │ Array<address, 5>: Memory
│ │ │ u256: Value
│ │ Context: Memory
Foo: Value

note:
┌─ external_contract.fe:26:9
26foo.emit_event(my_num, my_addrs, my_string)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (): Value
26foo.emit_event(ctx, my_num, my_addrs, my_string)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (): Value

note:
┌─ external_contract.fe:29:5
29 │ ╭ pub fn call_build_array(ctx: Context, foo_address: address, a: u256, b: u256) -> Array<u256, 3> {
30 │ │ let foo: Foo = Foo(ctx, foo_address)
29 │ ╭ pub fn call_build_array(foo_address: address, a: u256, b: u256) -> Array<u256, 3> {
30 │ │ let foo: Foo = Foo(foo_address)
31 │ │ return foo.build_array(a, b)
32 │ │ }
│ ╰─────^ attributes hash: 14801245347360386258
│ ╰─────^ attributes hash: 4627171240003453976
= FunctionSignature {
self_decl: None,
ctx_decl: Some(
Mutable,
),
ctx_decl: None,
params: [
FunctionParam {
label: None,
name: "ctx",
typ: Ok(
Struct(
Struct {
name: "Context",
field_count: 0,
},
),
),
},
FunctionParam {
label: None,
name: "foo_address",
Expand Down Expand Up @@ -427,22 +412,20 @@ note:
note:
┌─ external_contract.fe:30:13
30let foo: Foo = Foo(ctx, foo_address)
30let foo: Foo = Foo(foo_address)
^^^ Foo

note:
┌─ external_contract.fe:30:28
30let foo: Foo = Foo(ctx, foo_address)
^^^ ^^^^^^^^^^^ address: Value
│ │
Context: Memory
30let foo: Foo = Foo(foo_address)
^^^^^^^^^^^ address: Value

note:
┌─ external_contract.fe:30:24
30let foo: Foo = Foo(ctx, foo_address)
^^^^^^^^^^^^^^^^^^^^^ Foo: Value
30let foo: Foo = Foo(foo_address)
^^^^^^^^^^^^^^^^ Foo: Value
31return foo.build_array(a, b)
^^^ ^ ^ u256: Value
│ │ │
Expand Down
35 changes: 10 additions & 25 deletions crates/analyzer/tests/snapshots/analysis__two_contracts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -159,31 +159,17 @@ note:
note:
┌─ two_contracts.fe:23:5
23 │ ╭ pub fn set_foo_addr(self, ctx: Context, _ addr: address) {
24 │ │ self.other = Foo(ctx, addr)
23 │ ╭ pub fn set_foo_addr(self, _ addr: address) {
24 │ │ self.other = Foo(addr)
25 │ │ }
│ ╰─────^ attributes hash: 736575608159807992
│ ╰─────^ attributes hash: 3263176293298741376
= FunctionSignature {
self_decl: Some(
Mutable,
),
ctx_decl: Some(
Mutable,
),
ctx_decl: None,
params: [
FunctionParam {
label: None,
name: "ctx",
typ: Ok(
Struct(
Struct {
name: "Context",
field_count: 0,
},
),
),
},
FunctionParam {
label: Some(
"_",
Expand All @@ -206,23 +192,22 @@ note:
note:
┌─ two_contracts.fe:24:9
24self.other = Foo(ctx, addr)
24self.other = Foo(addr)
^^^^ Bar: Value

note:
┌─ two_contracts.fe:24:9
24self.other = Foo(ctx, addr)
^^^^^^^^^^ ^^^ ^^^^ address: Value
│ │ │
│ │ Context: Memory
24self.other = Foo(addr)
^^^^^^^^^^ ^^^^ address: Value
│ │
Foo: Storage { nonce: Some(0) }

note:
┌─ two_contracts.fe:24:22
24self.other = Foo(ctx, addr)
^^^^^^^^^^^^^^ Foo: Value
24self.other = Foo(addr)
^^^^^^^^^ Foo: Value

note:
┌─ two_contracts.fe:27:5
Expand Down
Loading

0 comments on commit cacaefd

Please sign in to comment.