Repository: typst/typst
File: crates/typst/src/eval/call.rs (Rust)

Code before:
        Value::Symbol(_) | Value::Func(_) | Value::Type(_) | Value::Module(_)
    ) {
        Ok((target.field(&field).at(field.span())?, args))
    } else {
        let mut error = error!(
            field.span(),
            "type {} has no method `{}`",
            target.ty(),
            field.as_str(),
        );

        match target {
            Value::Dict(ref dict) if matches!(dict.get(&field), Ok(Value::Func(_))) => {
                error.hint(eco_format!(
                    "to call the function stored in the dictionary, \
                        surround the field access with parentheses, e.g. `(dict.{})(..)`",
                    field.as_str(),
                ));
            }
            _ if target.field(&field).is_ok() => {
                error.hint(eco_format!(
                    "did you mean to access the field `{}`?",
                    field.as_str()
                ));
            }
            _ => (),
        }

        bail!(error)
    }
}

/// Check if the expression is in a math context.
fn in_math(expr: ast::Expr) -> bool {
    match expr {
        ast::Expr::MathIdent(_) => true,
        ast::Expr::FieldAccess(access) => in_math(access.target()),
        _ => false,
    }
}

/// For non-functions in math, we wrap the arguments in parentheses.
fn wrap_args_in_math(
    callee: Value,
    callee_span: Span,
    mut args: Args,
    trailing_comma: bool,
) -> SourceResult<Value> {
    let mut body = Content::empty();
    for (i, arg) in args.all::<Content>()?.into_iter().enumerate() {
        if i > 0 {

Review comment (suggestion):
very nitpicky nitpick but i think this is more usual

```suggestion
            _ => {}
```

Code after:
            }
            target => (target.clone(), args),
        }
    } else {
        let target = target_expr.eval(vm)?;
        let args = args.eval(vm)?.spanned(span);
        (target, args)
    };

    if let Value::Plugin(plugin) = &target {
        // Call plugins by converting args to bytes.
        let bytes = args.all::<Bytes>()?;
        args.finish()?;
        let value = plugin.call(&field, bytes).at(span)?.into_value();
        Ok(FieldCall::Resolved(value))
    } else if let Some(callee) = target.ty().scope().get(&field) {
        args.insert(0, target_expr.span(), target);
        Ok(FieldCall::Normal(callee.clone(), args))
    } else if matches!(
        target,
        Value::Symbol(_) | Value::Func(_) | Value::Type(_) | Value::Module(_)
    ) {
        // Certain value types may have their own ways to access method fields.
        // e.g. `$arrow.r(v)$`, `table.cell[..]`
        let value = target.field(&field).at(field.span())?;
        Ok(FieldCall::Normal(value, args))
    } else {
        // Otherwise we cannot call this field.
        bail!(missing_field_call_error(target, field))
    }
}

/// Produce an error when we cannot call the field.
fn missing_field_call_error(target: Value, field: Ident) -> SourceDiagnostic {
    let mut error =
        error!(field.span(), "type {} has no method `{}`", target.ty(), field.as_str());

    match target {
        Value::Dict(ref dict) if matches!(dict.get(&field), Ok(Value::Func(_))) => {
            error.hint(eco_format!(
                "to call the function stored in the dictionary, surround \
                the field access with parentheses, e.g. `(dict.{})(..)`",
                field.as_str(),
            ));
        }
        _ if target.field(&field).is_ok() => {
            error.hint(eco_format!(
                "did you mean to access the field `{}`?",
                field.as_str(),
            ));
        }