Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
sbillig committed Apr 6, 2022
1 parent 0feb862 commit a0d97b1
Show file tree
Hide file tree
Showing 63 changed files with 1,103 additions and 1,023 deletions.
6 changes: 0 additions & 6 deletions crates/analyzer/src/db/queries/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,6 @@ pub fn function_signature(
arg.span,
"instances of `Context` must be named `ctx`",
);
} else if !function.parent(db).is_contract() {
scope.error(
"`ctx` cannot be passed into pure functions",
arg.span,
"`ctx` can only be passed into contract functions",
);
} else if self_decl.is_some() && index != 1 {
scope.error(
"invalid parameter order",
Expand Down
25 changes: 0 additions & 25 deletions crates/analyzer/src/namespace/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -418,31 +418,6 @@ impl FunctionSignature {
pub fn expect_return_type(&self) -> FixedSize {
self.return_type.clone().expect("fn return type error")
}

/// 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<FixedSize> {
self.external_params()
.iter()
.map(|param| param.typ.clone().expect("fn param type error"))
.collect()
}
}

impl Type {
Expand Down
48 changes: 24 additions & 24 deletions crates/analyzer/src/traversal/assignments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,42 +18,42 @@ pub fn assign(scope: &mut BlockScope, stmt: &Node<fe::FuncStmt>) -> Result<(), F
_ => unreachable!(),
};

let target_attributes = expressions::expr(scope, target, None)?;
let rhs_attributes = expressions::expr(scope, rhs, Some(&target_attributes.typ))?;
let target_attr = expressions::expr(scope, target, None)?;
let rhs_attr = expressions::expr(scope, rhs, Some(&target_attr.typ))?;

check_assign_target(scope, target)?;

if target_attributes.typ != rhs_attributes.typ {
if target_attr.typ != rhs_attr.typ {
scope.fancy_error(
"mismatched types",
vec![
Label::primary(
target.span,
format!("this has type `{}`", target_attributes.typ),
),
Label::primary(target.span, format!("this has type `{}`", target_attr.typ)),
Label::secondary(
rhs.span,
format!("this value has incompatible type `{}`", rhs_attributes.typ),
format!("this value has incompatible type `{}`", rhs_attr.typ),
),
],
vec![],
);
}

if target_attributes.mutable && !rhs_attributes.mutable {
// XXX
// scope.fancy_error(
// "sneaky mutation", // XXX better error
// vec![
// Label::primary(target.span, "this is mutable"),
// Label::secondary(value.span, "this is immutable"),
// ],
// vec![],
// );
if target_attr.mutable
&& !rhs_attr.mutable
&& !matches!(target_attr.location, Location::Storage { .. })
&& !matches!(rhs_attr.location, Location::Storage { .. })
{
scope.fancy_error(
"sneaky mutation", // XXX better error
vec![
Label::primary(target.span, "this is mutable"),
Label::secondary(rhs.span, "this is immutable"),
],
vec![],
);
}

if matches!(
(target_attributes.location, rhs_attributes.final_location(),),
(target_attr.location, rhs_attr.final_location(),),
(Location::Memory, Location::Storage { .. })
) {
scope.fancy_error(
Expand Down Expand Up @@ -151,18 +151,18 @@ pub fn aug_assign(scope: &mut BlockScope, stmt: &Node<fe::FuncStmt>) -> Result<(
_ => unreachable!(),
};

let target_attributes = expressions::expr(scope, target, None)?;
let rhs_attributes = expressions::expr(scope, rhs, Some(&target_attributes.typ))?;
let target_attr = expressions::expr(scope, target, None)?;
let rhs_attr = expressions::expr(scope, rhs, Some(&target_attr.typ))?;
check_assign_target(scope, target)?;

if let Err(err) = operations::bin(&target_attributes.typ, &op.kind, &rhs_attributes.typ) {
if let Err(err) = operations::bin(&target_attr.typ, &op.kind, &rhs_attr.typ) {
add_bin_operations_errors(
scope,
&op.kind,
target.span,
&target_attributes.typ,
&target_attr.typ,
rhs.span,
&rhs_attributes.typ,
&rhs_attr.typ,
err,
);
}
Expand Down
125 changes: 75 additions & 50 deletions crates/analyzer/src/traversal/call_args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use smol_str::SmolStr;
pub trait LabeledParameter {
fn label(&self) -> Option<&str>;
fn typ(&self) -> Result<FixedSize, TypeError>;
fn mutable(&self) -> bool;
}

impl LabeledParameter for FunctionParam {
Expand All @@ -20,6 +21,9 @@ impl LabeledParameter for FunctionParam {
fn typ(&self) -> Result<FixedSize, TypeError> {
self.typ.clone()
}
fn mutable(&self) -> bool {
self.is_mut
}
}

impl LabeledParameter for EventField {
Expand All @@ -29,15 +33,22 @@ impl LabeledParameter for EventField {
fn typ(&self) -> Result<FixedSize, TypeError> {
self.typ.clone()
}
fn mutable(&self) -> bool {
self.name == "ctx" // hacktastic
}
}

impl LabeledParameter for (SmolStr, Result<FixedSize, TypeError>) {
// XXX wtf is this
impl LabeledParameter for (SmolStr, Result<FixedSize, TypeError>, bool) {
fn label(&self) -> Option<&str> {
Some(&self.0)
}
fn typ(&self) -> Result<FixedSize, TypeError> {
self.1.clone()
}
fn mutable(&self) -> bool {
self.2
}
}

pub fn validate_arg_count(
Expand Down Expand Up @@ -99,55 +110,6 @@ pub fn validate_named_args(
validate_arg_count(context, name, name_span, args, params.len(), "argument");

for (index, (param, arg)) in params.iter().zip(args.kind.iter()).enumerate() {
let expected_label = param.label();
let arg_val = &arg.kind.value;
match (expected_label, &arg.kind.label) {
(Some(expected_label), Some(actual_label)) => {
if expected_label != actual_label.kind {
let notes = if params
.iter()
.any(|param| param.label() == Some(actual_label.kind.as_str()))
{
vec!["Note: arguments must be provided in order.".into()]
} else {
vec![]
};
context.fancy_error(
"argument label mismatch",
vec![Label::primary(
actual_label.span,
format!("expected `{}`", expected_label),
)],
notes,
);
}
}
(Some(expected_label), None) => match &arg_val.kind {
fe::Expr::Name(var_name) if var_name == expected_label => {}
_ => {
context.fancy_error(
"missing argument label",
vec![Label::primary(
Span::new(arg_val.span.file_id, arg_val.span.start, arg_val.span.start),
format!("add `{}:` here", expected_label),
)],
vec![format!(
"Note: this label is optional if the argument is a variable named `{}`.",
expected_label
)],
);
}
},
(None, Some(actual_label)) => {
context.error(
"argument should not be labeled",
actual_label.span,
"remove this label",
);
}
(None, None) => {}
}

let param_type = param.typ()?;
let val_attrs =
assignable_expr(context, &arg.kind.value, Some(&param_type.clone().into()))?;
Expand All @@ -161,6 +123,69 @@ pub fn validate_named_args(
)
};
context.type_error(&msg, arg.kind.value.span, &param_type, &val_attrs.typ);
} else {
let arg_val = &arg.kind.value;

// We only emit label and mutability errors if the types match.
// If the types don't match, these errors can be confusing.
if param.mutable() && !val_attrs.mutable {
context.fancy_error(
"expected mut arg", // XXX better error
vec![
Label::primary(arg_val.span, "this is not mutable"),
// Label::secondary(param.span, "mutates arg"), XXX parameter span
],
vec![],
);
}

let expected_label = param.label();
match (expected_label, &arg.kind.label) {
(Some(expected_label), Some(actual_label)) => {
if expected_label != actual_label.kind {
let notes = if params
.iter()
.any(|param| param.label() == Some(actual_label.kind.as_str()))
{
vec!["Note: arguments must be provided in order.".into()]
} else {
vec![]
};
context.fancy_error(
"argument label mismatch",
vec![Label::primary(
actual_label.span,
format!("expected `{}`", expected_label),
)],
notes,
);
}
}
(Some(expected_label), None) => match &arg_val.kind {
fe::Expr::Name(var_name) if var_name == expected_label => {}
_ => {
context.fancy_error(
"missing argument label",
vec![Label::primary(
Span::new(arg_val.span.file_id, arg_val.span.start, arg_val.span.start),
format!("add `{}:` here", expected_label),
)],
vec![format!(
"Note: this label is optional if the argument is a variable named `{}`.",
expected_label
)],
);
}
},
(None, Some(actual_label)) => {
context.error(
"argument should not be labeled",
actual_label.span,
"remove this label",
);
}
(None, None) => {}
}
}
}
Ok(())
Expand Down
68 changes: 9 additions & 59 deletions crates/analyzer/src/traversal/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1182,15 +1182,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 @@ -1201,50 +1195,10 @@ fn expr_call_type_constructor(
ExpressionAttributes::new(typ.clone(), Location::Memory, true)
}
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, true)
Expand Down Expand Up @@ -1351,7 +1305,7 @@ fn expr_call_struct_constructor(
.id
.fields(db)
.iter()
.map(|(name, field)| (name.clone(), field.typ(db)))
.map(|(name, field)| (name.clone(), field.typ(db), false))
.collect::<Vec<_>>();
validate_named_args(context, name, name_span, args, &fields)?;

Expand Down Expand Up @@ -1472,11 +1426,7 @@ fn expr_call_method(

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

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

let calltype = match class {
Expand Down
Loading

0 comments on commit a0d97b1

Please sign in to comment.