diff --git a/Changelog.md b/Changelog.md index 16f2900b2..e901a0513 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,25 @@ # Changelog +## Unreleased + +### Candid + +* Breaking changes: + + `pp_args` and `pp_init_args` noew require a `&[ArgType]` parameter. The `pp_rets` function has been added, with the signature of the old `pp_args`. + +### candid_parser + +* Breaking changes: + + The `args` field in both `FuncType` and `IDLInitArgs` now have type `Vec`. + +* Non-breaking changes: + + Supports parsing the arguments' names for `func` and `service` (init args). + +### candid_derive + +* Keeps argument names for Rust functions. + ## 2025-05-15 ### Candid 0.10.14 diff --git a/rust/bench/bench.rs b/rust/bench/bench.rs index b92c9d310..5f6e0272d 100644 --- a/rust/bench/bench.rs +++ b/rust/bench/bench.rs @@ -215,11 +215,16 @@ fn nns() -> BenchResult { let (env, serv) = nns_did.load().unwrap(); let args = candid_parser::parse_idl_args(motion_proposal).unwrap(); let serv = serv.unwrap(); - let arg_tys = &env.get_method(&serv, "manage_neuron").unwrap().args; + let method = &env.get_method(&serv, "manage_neuron").unwrap(); + let arg_tys = method + .args + .iter() + .map(|arg| arg.typ.clone()) + .collect::>(); drop(_p); let bytes = { let _p = bench_scope("1. Encoding"); - args.to_bytes_with_types(&env, arg_tys).unwrap() + args.to_bytes_with_types(&env, &arg_tys).unwrap() }; { let _p = bench_scope("2. Decoding"); diff --git a/rust/candid/src/binary_parser.rs b/rust/candid/src/binary_parser.rs index d0c1ea0ea..ca8cd1636 100644 --- a/rust/candid/src/binary_parser.rs +++ b/rust/candid/src/binary_parser.rs @@ -1,4 +1,4 @@ -use crate::types::internal::{Field, Function, Label, Type, TypeInner}; +use crate::types::internal::{ArgType, Field, Function, Label, Type, TypeInner}; use crate::types::{FuncMode, TypeEnv}; use anyhow::{anyhow, Context, Result}; use binread::io::{Read, Seek}; @@ -201,7 +201,10 @@ impl ConsType { let mut args = Vec::new(); let mut rets = Vec::new(); for arg in &f.args { - args.push(arg.to_type(len)?); + args.push(ArgType { + name: None, + typ: arg.to_type(len)?, + }); } for ret in &f.rets { rets.push(ret.to_type(len)?); diff --git a/rust/candid/src/pretty/candid.rs b/rust/candid/src/pretty/candid.rs index 5f67607de..0f3b80ed2 100644 --- a/rust/candid/src/pretty/candid.rs +++ b/rust/candid/src/pretty/candid.rs @@ -1,5 +1,7 @@ use crate::pretty::utils::*; -use crate::types::{Field, FuncMode, Function, Label, SharedLabel, Type, TypeEnv, TypeInner}; +use crate::types::{ + ArgType, Field, FuncMode, Function, Label, SharedLabel, Type, TypeEnv, TypeInner, +}; use pretty::RcDoc; static KEYWORDS: [&str; 30] = [ @@ -149,7 +151,7 @@ fn pp_fields(fs: &[Field], is_variant: bool) -> RcDoc { pub fn pp_function(func: &Function) -> RcDoc { let args = pp_args(&func.args); - let rets = pp_args(&func.rets); + let rets = pp_rets(&func.rets); let modes = pp_modes(&func.modes); args.append(" ->") .append(RcDoc::space()) @@ -157,10 +159,23 @@ pub fn pp_function(func: &Function) -> RcDoc { .nest(INDENT_SPACE) } -pub fn pp_args(args: &[Type]) -> RcDoc { - let doc = concat(args.iter().map(pp_ty), ","); +pub fn pp_args(args: &[ArgType]) -> RcDoc { + let args = args.iter().map(|arg| { + if let Some(name) = &arg.name { + pp_text(name).append(kwd(" :")).append(pp_ty(&arg.typ)) + } else { + pp_ty(&arg.typ) + } + }); + let doc = concat(args, ","); + enclose("(", doc, ")") +} + +pub fn pp_rets(rets: &[Type]) -> RcDoc { + let doc = concat(rets.iter().map(pp_ty), ","); enclose("(", doc, ")") } + pub fn pp_mode(mode: &FuncMode) -> RcDoc { match mode { FuncMode::Oneway => RcDoc::text("oneway"), @@ -205,7 +220,7 @@ fn pp_actor(ty: &Type) -> RcDoc { } } -pub fn pp_init_args<'a>(env: &'a TypeEnv, args: &'a [Type]) -> RcDoc<'a> { +pub fn pp_init_args<'a>(env: &'a TypeEnv, args: &'a [ArgType]) -> RcDoc<'a> { pp_defs(env).append(pp_args(args)) } pub fn compile(env: &TypeEnv, actor: &Option) -> String { diff --git a/rust/candid/src/ser.rs b/rust/candid/src/ser.rs index c35d1baac..53fee18ef 100644 --- a/rust/candid/src/ser.rs +++ b/rust/candid/src/ser.rs @@ -335,13 +335,16 @@ impl TypeSerialize { } } TypeInner::Func(ref func) => { - for ty in func.args.iter().chain(func.rets.iter()) { + for ty in &func.args { + self.build_type(&ty.typ)?; + } + for ty in &func.rets { self.build_type(ty)?; } sleb128_encode(&mut buf, Opcode::Func as i64)?; leb128_encode(&mut buf, func.args.len() as u64)?; for ty in &func.args { - self.encode(&mut buf, ty)?; + self.encode(&mut buf, &ty.typ)?; } leb128_encode(&mut buf, func.rets.len() as u64)?; for ty in &func.rets { diff --git a/rust/candid/src/types/internal.rs b/rust/candid/src/types/internal.rs index ed9003c40..4a70cdba0 100644 --- a/rust/candid/src/types/internal.rs +++ b/rust/candid/src/types/internal.rs @@ -148,7 +148,14 @@ impl TypeContainer { } TypeInner::Func(func) => TypeInner::Func(Function { modes: func.modes.clone(), - args: func.args.iter().map(|arg| self.go(arg)).collect(), + args: func + .args + .iter() + .map(|arg| ArgType { + name: arg.name.clone(), + typ: self.go(&arg.typ), + }) + .collect(), rets: func.rets.iter().map(|arg| self.go(arg)).collect(), }), TypeInner::Service(serv) => TypeInner::Service( @@ -156,9 +163,16 @@ impl TypeContainer { .map(|(id, t)| (id.clone(), self.go(t))) .collect(), ), - TypeInner::Class(inits, ref ty) => { - TypeInner::Class(inits.iter().map(|t| self.go(t)).collect(), self.go(ty)) - } + TypeInner::Class(inits, ref ty) => TypeInner::Class( + inits + .iter() + .map(|t| ArgType { + name: t.name.clone(), + typ: self.go(&t.typ), + }) + .collect(), + self.go(ty), + ), t => t.clone(), } .into() @@ -196,7 +210,7 @@ pub enum TypeInner { Variant(Vec), Func(Function), Service(Vec<(String, Type)>), - Class(Vec, Type), + Class(Vec, Type), Principal, Future, } @@ -278,7 +292,14 @@ impl Type { let func = func.clone(); Func(Function { modes: func.modes, - args: func.args.into_iter().map(|t| t.subst(tau)).collect(), + args: func + .args + .into_iter() + .map(|t| ArgType { + name: t.name, + typ: t.typ.subst(tau), + }) + .collect(), rets: func.rets.into_iter().map(|t| t.subst(tau)).collect(), }) } @@ -287,7 +308,15 @@ impl Type { .map(|(meth, ty)| (meth.clone(), ty.subst(tau))) .collect(), ), - Class(args, ty) => Class(args.iter().map(|t| t.subst(tau)).collect(), ty.subst(tau)), + Class(args, ty) => Class( + args.iter() + .map(|t| ArgType { + name: t.name.clone(), + typ: t.typ.subst(tau), + }) + .collect(), + ty.subst(tau), + ), _ => return self.clone(), } .into() @@ -353,7 +382,12 @@ pub fn text_size(t: &Type, limit: i32) -> Result { let mut cnt = mode + 6; let mut limit = limit - cnt; for t in &func.args { - cnt += text_size(t, limit)?; + if let Some(name) = t.name.as_ref() { + let id_size = name.len() as i32; + cnt += id_size + text_size(&t.typ, limit - id_size - 3)? + 3; + } else { + cnt += text_size(&t.typ, limit)?; + } limit -= cnt; } for t in &func.rets { @@ -510,9 +544,16 @@ pub enum FuncMode { #[derive(Debug, PartialEq, Hash, Eq, Clone, PartialOrd, Ord)] pub struct Function { pub modes: Vec, - pub args: Vec, + pub args: Vec, pub rets: Vec, } + +#[derive(Debug, PartialEq, Hash, Eq, Clone, PartialOrd, Ord)] +pub struct ArgType { + pub name: Option, + pub typ: Type, +} + #[cfg(feature = "printer")] impl fmt::Display for Function { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -539,16 +580,16 @@ impl Function { /// `func!((u8, &str) -> (Nat) query)` expands to `Type(Rc::new(TypeInner::Func(...)))` macro_rules! func { ( ( $($arg:ty),* $(,)? ) -> ( $($ret:ty),* $(,)? ) ) => { - Into::<$crate::types::Type>::into($crate::types::TypeInner::Func($crate::types::Function { args: vec![$(<$arg>::ty()),*], rets: vec![$(<$ret>::ty()),*], modes: vec![] })) + Into::<$crate::types::Type>::into($crate::types::TypeInner::Func($crate::types::Function { args: vec![$(<$arg>::ty()),*].into_iter().map(|arg| $crate::types::ArgType { name: None, typ: arg }).collect(), rets: vec![$(<$ret>::ty()),*], modes: vec![] })) }; ( ( $($arg:ty),* $(,)? ) -> ( $($ret:ty),* $(,)? ) query ) => { - Into::<$crate::types::Type>::into($crate::types::TypeInner::Func($crate::types::Function { args: vec![$(<$arg>::ty()),*], rets: vec![$(<$ret>::ty()),*], modes: vec![$crate::types::FuncMode::Query] })) + Into::<$crate::types::Type>::into($crate::types::TypeInner::Func($crate::types::Function { args: vec![$(<$arg>::ty()),*].into_iter().map(|arg| $crate::types::ArgType { name: None, typ: arg }).collect(), rets: vec![$(<$ret>::ty()),*], modes: vec![$crate::types::FuncMode::Query] })) }; ( ( $($arg:ty),* $(,)? ) -> ( $($ret:ty),* $(,)? ) composite_query ) => { - Into::<$crate::types::Type>::into($crate::types::TypeInner::Func($crate::types::Function { args: vec![$(<$arg>::ty()),*], rets: vec![$(<$ret>::ty()),*], modes: vec![$crate::types::FuncMode::CompositeQuery] })) + Into::<$crate::types::Type>::into($crate::types::TypeInner::Func($crate::types::Function { args: vec![$(<$arg>::ty()),*].into_iter().map(|arg| $crate::types::ArgType { name: None, typ: arg }).collect(), rets: vec![$(<$ret>::ty()),*], modes: vec![$crate::types::FuncMode::CompositeQuery] })) }; ( ( $($arg:ty),* $(,)? ) -> ( $($ret:ty),* $(,)? ) oneway ) => { - Into::<$crate::types::Type>::into($crate::types::TypeInner::Func($crate::types::Function { args: vec![$(<$arg>::ty()),*], rets: vec![$(<$ret>::ty()),*], modes: vec![$crate::types::FuncMode::Oneway] })) + Into::<$crate::types::Type>::into($crate::types::TypeInner::Func($crate::types::Function { args: vec![$(<$arg>::ty()),*].into_iter().map(|arg| $crate::types::ArgType { name: None, typ: arg }).collect(), rets: vec![$(<$ret>::ty()),*], modes: vec![$crate::types::FuncMode::Oneway] })) }; } #[macro_export] diff --git a/rust/candid/src/types/mod.rs b/rust/candid/src/types/mod.rs index 49af606db..72b385c90 100644 --- a/rust/candid/src/types/mod.rs +++ b/rust/candid/src/types/mod.rs @@ -15,7 +15,7 @@ pub mod type_env; pub mod value; pub use self::internal::{ - get_type, Field, FuncMode, Function, Label, SharedLabel, Type, TypeId, TypeInner, + get_type, ArgType, Field, FuncMode, Function, Label, SharedLabel, Type, TypeId, TypeInner, }; pub use type_env::TypeEnv; diff --git a/rust/candid/src/types/subtype.rs b/rust/candid/src/types/subtype.rs index 0d3fb2455..1e002317d 100644 --- a/rust/candid/src/types/subtype.rs +++ b/rust/candid/src/types/subtype.rs @@ -129,8 +129,18 @@ fn subtype_( if f1.modes != f2.modes { return Err(Error::msg("Function mode mismatch")); } - let args1 = to_tuple(&f1.args); - let args2 = to_tuple(&f2.args); + let f1_args = f1 + .args + .iter() + .map(|arg| arg.typ.clone()) + .collect::>(); + let f2_args = f2 + .args + .iter() + .map(|arg| arg.typ.clone()) + .collect::>(); + let args1 = to_tuple(&f1_args); + let args2 = to_tuple(&f2_args); let rets1 = to_tuple(&f1.rets); let rets2 = to_tuple(&f2.rets); subtype_(report, gamma, env, &args2, &args1) @@ -212,8 +222,18 @@ pub fn equal(gamma: &mut Gamma, env: &TypeEnv, t1: &Type, t2: &Type) -> Result<( if f1.modes != f2.modes { return Err(Error::msg("Function mode mismatch")); } - let args1 = to_tuple(&f1.args); - let args2 = to_tuple(&f2.args); + let f1_args = f1 + .args + .iter() + .map(|arg| arg.typ.clone()) + .collect::>(); + let f2_args = f2 + .args + .iter() + .map(|arg| arg.typ.clone()) + .collect::>(); + let args1 = to_tuple(&f1_args); + let args2 = to_tuple(&f2_args); let rets1 = to_tuple(&f1.rets); let rets2 = to_tuple(&f2.rets); equal(gamma, env, &args1, &args2).context("Mismatch in function input type")?; @@ -221,8 +241,16 @@ pub fn equal(gamma: &mut Gamma, env: &TypeEnv, t1: &Type, t2: &Type) -> Result<( Ok(()) } (Class(init1, ty1), Class(init2, ty2)) => { - let init_1 = to_tuple(init1); - let init_2 = to_tuple(init2); + let init1_typ = init1 + .iter() + .map(|arg| arg.typ.clone()) + .collect::>(); + let init2_typ = init2 + .iter() + .map(|arg| arg.typ.clone()) + .collect::>(); + let init_1 = to_tuple(&init1_typ); + let init_2 = to_tuple(&init2_typ); equal(gamma, env, &init_1, &init_2).context(format!( "Mismatch in init args: {} and {}", pp_args(init1), @@ -277,7 +305,7 @@ fn to_tuple(args: &[Type]) -> Type { .into() } #[cfg(not(feature = "printer"))] -fn pp_args(args: &[crate::types::Type]) -> String { +fn pp_args(args: &[crate::types::ArgType]) -> String { use std::fmt::Write; let mut s = String::new(); write!(&mut s, "(").unwrap(); @@ -288,7 +316,7 @@ fn pp_args(args: &[crate::types::Type]) -> String { s } #[cfg(feature = "printer")] -fn pp_args(args: &[crate::types::Type]) -> String { +fn pp_args(args: &[crate::types::ArgType]) -> String { use crate::pretty::candid::pp_args; pp_args(args).pretty(80).to_string() } diff --git a/rust/candid/tests/types.rs b/rust/candid/tests/types.rs index 95b06ce52..9c8fcb965 100644 --- a/rust/candid/tests/types.rs +++ b/rust/candid/tests/types.rs @@ -186,8 +186,8 @@ fn test_func() { pub struct Wrap(List); #[derive(CandidType)] pub struct NamedStruct { - a: u16, - b: i32, + pub a: u16, + pub b: i32, } #[derive(CandidType)] pub enum A { @@ -220,6 +220,21 @@ fn test_func() { unreachable!() } + #[candid_method] + fn id_tuple_destructure((a, b): (u8, u8)) -> (u8, u8) { + (a, b) + } + + #[candid_method] + fn id_struct_destructure(internal::NamedStruct { a, b }: internal::NamedStruct) -> (u16, i32) { + (a, b) + } + + #[candid_method] + fn id_unused_arg(_a: u8) -> Result, candid::Empty> { + unreachable!() + } + #[candid_method(init)] fn init(_: List) {} @@ -244,13 +259,14 @@ type Wrap = record { head : int8; tail : opt Box }; service : (List_2) -> { id_struct : (record { List }) -> (Result) query; id_struct_composite : (record { List }) -> (Result) composite_query; + id_struct_destructure : (NamedStruct) -> (nat16, int32); + id_tuple_destructure : (record { nat8; nat8 }) -> (nat8, nat8); + id_unused_arg : (nat8) -> (Result); id_variant : (vec A) -> (Result_1); "oneway" : (text) -> () oneway; - "🐂" : (text, int32) -> (text, int32) query; + "🐂" : (a : text, b : int32) -> (text, int32) query; }"#; assert_eq!(expected, __export_service()); - //println!("{}", __export_service()); - //assert!(false); } #[test] @@ -270,8 +286,27 @@ fn test_counter() { fn read(&self) -> usize { self.counter } + #[candid_method] + fn set(&mut self, value: usize) { + self.counter = value; + } + } + candid::export_service!(); + let expected = r#"service : { + inc : () -> (); + read : () -> (nat64) query; + set : (value : nat64) -> (); +}"#; + assert_eq!(expected, __export_service()); +} + +#[test] +fn test_init_named_args() { + #[candid_method(init)] + fn init(a: u8) { + let _ = a; } candid::export_service!(); - let expected = "service : { inc : () -> (); read : () -> (nat64) query }"; + let expected = r#"service : (a : nat8) -> {}"#; assert_eq!(expected, __export_service()); } diff --git a/rust/candid_derive/src/func.rs b/rust/candid_derive/src/func.rs index 94ba4da77..7c886563e 100644 --- a/rust/candid_derive/src/func.rs +++ b/rust/candid_derive/src/func.rs @@ -6,9 +6,14 @@ use std::collections::BTreeMap; use std::sync::Mutex; use syn::{Error, ItemFn, Meta, Result, ReturnType, Signature, Type}; +type RawArgs = Vec<(Option, String)>; +type RawRets = Vec; +type ParsedArgs = Vec<(Option, Type)>; +type ParsedRets = Vec; + struct Method { - args: Vec, - rets: Vec, + args: RawArgs, + rets: RawRets, modes: String, } @@ -19,7 +24,7 @@ struct Method { lazy_static! { static ref METHODS: Mutex>> = Mutex::new(Some(BTreeMap::default())); - static ref INIT: Mutex>>> = Mutex::new(Some(Option::default())); + static ref INIT: Mutex>> = Mutex::new(Some(Option::default())); } pub(crate) fn candid_method(attrs: Vec, fun: ItemFn) -> Result { @@ -35,11 +40,11 @@ pub(crate) fn candid_method(attrs: Vec, fun: ItemFn) -> Result = args + let args: RawArgs = args .iter() - .map(|t| format!("{}", t.to_token_stream())) + .map(|(name, t)| (name.clone(), format!("{}", t.to_token_stream()))) .collect(); - let rets: Vec = rets + let rets: RawRets = rets .iter() .map(|t| format!("{}", t.to_token_stream())) .collect(); @@ -79,7 +84,7 @@ pub(crate) fn export_service(path: Option) -> TokenStream { .map(|t| generate_arg(quote! { init_args }, t)) .collect::>(); quote! { - let mut init_args = Vec::new(); + let mut init_args: Vec = Vec::new(); #(#args)* } }); @@ -95,7 +100,7 @@ pub(crate) fn export_service(path: Option) -> TokenStream { .collect::>(); let rets = rets .iter() - .map(|t| generate_arg(quote! { rets }, t)) + .map(|t| generate_ret(quote! { rets }, t)) .collect::>(); let modes = match modes.as_ref() { "query" => quote! { vec![#candid::types::FuncMode::Query] }, @@ -106,9 +111,9 @@ pub(crate) fn export_service(path: Option) -> TokenStream { }; quote! { { - let mut args = Vec::new(); + let mut args: Vec = Vec::new(); #(#args)* - let mut rets = Vec::new(); + let mut rets: Vec = Vec::new(); #(#rets)* let func = Function { args, rets, modes: #modes }; service.push((#name.to_string(), TypeInner::Func(func).into())); @@ -116,7 +121,7 @@ pub(crate) fn export_service(path: Option) -> TokenStream { } }); let service = quote! { - use #candid::types::{CandidType, Function, Type, TypeInner}; + use #candid::types::{CandidType, Function, Type, ArgType, TypeInner}; let mut service = Vec::<(String, Type)>::new(); let mut env = #candid::types::internal::TypeContainer::new(); #(#gen_tys)* @@ -147,14 +152,25 @@ pub(crate) fn export_service(path: Option) -> TokenStream { } } -fn generate_arg(name: TokenStream, ty: &str) -> TokenStream { +fn generate_arg(name: TokenStream, (arg_name, ty): &(Option, String)) -> TokenStream { + let arg_name = arg_name + .as_ref() + .map(|n| quote! { Some(#n.to_string()) }) + .unwrap_or(quote! { None }); + let ty = syn::parse_str::(ty.as_str()).unwrap(); + quote! { + #name.push(ArgType { name: #arg_name, typ: env.add::<#ty>() }); + } +} + +fn generate_ret(name: TokenStream, ty: &str) -> TokenStream { let ty = syn::parse_str::(ty).unwrap(); quote! { #name.push(env.add::<#ty>()); } } -fn get_args(sig: &Signature) -> Result<(Vec, Vec)> { +fn get_args(sig: &Signature) -> Result<(ParsedArgs, ParsedRets)> { let mut args = Vec::new(); for arg in &sig.inputs { match arg { @@ -163,7 +179,20 @@ fn get_args(sig: &Signature) -> Result<(Vec, Vec)> { return Err(Error::new_spanned(arg, "only works for borrowed self")); } } - syn::FnArg::Typed(syn::PatType { ty, .. }) => args.push(ty.as_ref().clone()), + syn::FnArg::Typed(syn::PatType { ty, pat, .. }) => { + if let syn::Pat::Ident(syn::PatIdent { ident, .. }) = pat.as_ref() { + let arg_name = ident.to_string(); + if arg_name.starts_with("_") { + // If the argument name starts with _, it usually means it's not used. + // We don't need to include it in the IDL. + args.push((None, ty.as_ref().clone())); + } else { + args.push((Some(arg_name), ty.as_ref().clone())); + } + } else { + args.push((None, ty.as_ref().clone())); + } + } } } let rets = match &sig.output { diff --git a/rust/candid_parser/src/bindings/analysis.rs b/rust/candid_parser/src/bindings/analysis.rs index edd316bc7..bf80a0524 100644 --- a/rust/candid_parser/src/bindings/analysis.rs +++ b/rust/candid_parser/src/bindings/analysis.rs @@ -56,7 +56,8 @@ pub fn chase_type<'a>( } } Func(f) => { - for ty in f.args.iter().chain(f.rets.iter()) { + let args = f.args.iter().map(|arg| &arg.typ); + for ty in args.clone().chain(f.rets.iter()) { chase_type(seen, res, env, ty)?; } } @@ -67,7 +68,7 @@ pub fn chase_type<'a>( } Class(args, t) => { for arg in args.iter() { - chase_type(seen, res, env, arg)?; + chase_type(seen, res, env, &arg.typ)?; } chase_type(seen, res, env, t)?; } @@ -94,7 +95,7 @@ pub fn chase_def_use<'a>( if let TypeInner::Class(args, _) = actor.as_ref() { for (i, arg) in args.iter().enumerate() { let mut used = Vec::new(); - chase_type(&mut BTreeSet::new(), &mut used, env, arg)?; + chase_type(&mut BTreeSet::new(), &mut used, env, &arg.typ)?; for var in used { res.entry(var.to_string()) .or_insert_with(Vec::new) @@ -106,7 +107,7 @@ pub fn chase_def_use<'a>( let func = env.as_func(ty)?; for (i, arg) in func.args.iter().enumerate() { let mut used = Vec::new(); - chase_type(&mut BTreeSet::new(), &mut used, env, arg)?; + chase_type(&mut BTreeSet::new(), &mut used, env, &arg.typ)?; for var in used { res.entry(var.to_string()) .or_insert_with(Vec::new) @@ -159,7 +160,8 @@ pub fn infer_rec<'a>(_env: &'a TypeEnv, def_list: &'a [&'a str]) -> Result { - for ty in f.args.iter().chain(f.rets.iter()) { + let args = f.args.iter().map(|arg| &arg.typ); + for ty in args.clone().chain(f.rets.iter()) { go(seen, res, _env, ty)?; } } @@ -170,7 +172,7 @@ pub fn infer_rec<'a>(_env: &'a TypeEnv, def_list: &'a [&'a str]) -> Result { for arg in args.iter() { - go(seen, res, _env, arg)?; + go(seen, res, _env, &arg.typ)?; } go(seen, res, _env, t)?; } diff --git a/rust/candid_parser/src/bindings/javascript.rs b/rust/candid_parser/src/bindings/javascript.rs index 02476638d..c0db60de6 100644 --- a/rust/candid_parser/src/bindings/javascript.rs +++ b/rust/candid_parser/src/bindings/javascript.rs @@ -1,7 +1,7 @@ use super::analysis::{chase_actor, chase_types, infer_rec}; use candid::pretty::candid::pp_mode; use candid::pretty::utils::*; -use candid::types::{Field, Function, Label, SharedLabel, Type, TypeEnv, TypeInner}; +use candid::types::{ArgType, Field, Function, Label, SharedLabel, Type, TypeEnv, TypeInner}; use pretty::RcDoc; use std::collections::BTreeSet; @@ -159,14 +159,19 @@ fn pp_fields(fs: &[Field]) -> RcDoc { fn pp_function(func: &Function) -> RcDoc { let args = pp_args(&func.args); - let rets = pp_args(&func.rets); + let rets = pp_rets(&func.rets); let modes = pp_modes(&func.modes); let items = [args, rets, modes]; let doc = concat(items.iter().cloned(), ","); enclose("(", doc, ")").nest(INDENT_SPACE) } -fn pp_args(args: &[Type]) -> RcDoc { +fn pp_args(args: &[ArgType]) -> RcDoc { + let doc = concat(args.iter().map(|arg| pp_ty(&arg.typ)), ","); + enclose("[", doc, "]") +} + +fn pp_rets(args: &[Type]) -> RcDoc { let doc = concat(args.iter().map(pp_ty), ","); enclose("[", doc, "]") } @@ -243,11 +248,12 @@ pub fn compile(env: &TypeEnv, actor: &Option) -> String { let def_list = chase_actor(env, actor).unwrap(); let recs = infer_rec(env, &def_list).unwrap(); let defs = pp_defs(env, &def_list, &recs); - let init = if let TypeInner::Class(ref args, _) = actor.as_ref() { - args.as_slice() + let types = if let TypeInner::Class(ref args, _) = actor.as_ref() { + args.iter().map(|arg| arg.typ.clone()).collect::>() } else { - &[][..] + Vec::new() }; + let init = types.as_slice(); let actor = kwd("return").append(pp_actor(actor, &recs)).append(";"); let body = defs.append(actor); let doc = str("export const idlFactory = ({ IDL }) => ") @@ -256,7 +262,7 @@ pub fn compile(env: &TypeEnv, actor: &Option) -> String { let init_defs = chase_types(env, init).unwrap(); let init_recs = infer_rec(env, &init_defs).unwrap(); let init_defs_doc = pp_defs(env, &init_defs, &init_recs); - let init_doc = kwd("return").append(pp_args(init)).append(";"); + let init_doc = kwd("return").append(pp_rets(init)).append(";"); let init_doc = init_defs_doc.append(init_doc); let init_doc = str("export const init = ({ IDL }) => ").append(enclose_space("{", init_doc, "};")); @@ -374,7 +380,7 @@ pub mod test { } fn pp_encode<'a>(args: &'a candid::IDLArgs, tys: &'a [candid::types::Type]) -> RcDoc<'a> { let vals = value::pp_args(args); - let tys = super::pp_args(tys); + let tys = super::pp_rets(tys); let items = [tys, vals]; let params = concat(items.iter().cloned(), ","); str("IDL.encode").append(enclose("(", params, ")")) @@ -382,7 +388,7 @@ pub mod test { fn pp_decode<'a>(bytes: &'a [u8], tys: &'a [candid::types::Type]) -> RcDoc<'a> { let hex = pp_hex(bytes); - let tys = super::pp_args(tys); + let tys = super::pp_rets(tys); let items = [tys, hex]; let params = concat(items.iter().cloned(), ","); str("IDL.decode").append(enclose("(", params, ")")) @@ -417,7 +423,7 @@ import { Principal } from './principal'; use HostAssert::*; let test_func = match cmd { Encode(args, tys, _, _) | NotEncode(args, tys) => { - let items = [super::pp_args(tys), pp_encode(args, tys)]; + let items = [super::pp_rets(tys), pp_encode(args, tys)]; let params = concat(items.iter().cloned(), ","); str("IDL.decode").append(enclose("(", params, ")")) } diff --git a/rust/candid_parser/src/bindings/motoko.rs b/rust/candid_parser/src/bindings/motoko.rs index d0fcdbd49..f7ceb949a 100644 --- a/rust/candid_parser/src/bindings/motoko.rs +++ b/rust/candid_parser/src/bindings/motoko.rs @@ -3,7 +3,7 @@ use candid::pretty::candid::is_valid_as_id; use candid::pretty::utils::*; -use candid::types::FuncMode; +use candid::types::{ArgType, FuncMode}; use candid::types::{Field, Function, Label, SharedLabel, Type, TypeEnv, TypeInner}; use pretty::RcDoc; @@ -172,7 +172,7 @@ fn pp_variant(field: &Field) -> RcDoc { fn pp_function(func: &Function) -> RcDoc { let args = pp_args(&func.args); - let rets = pp_args(&func.rets); + let rets = pp_rets(&func.rets); match func.modes.as_slice() { [FuncMode::Oneway] => kwd("shared").append(args).append(" -> ").append("()"), [FuncMode::Query] => kwd("shared query") @@ -194,7 +194,35 @@ fn pp_function(func: &Function) -> RcDoc { } .nest(INDENT_SPACE) } -fn pp_args(args: &[Type]) -> RcDoc { +fn pp_args(args: &[ArgType]) -> RcDoc { + match args { + [ty] => { + let typ = if is_tuple(&ty.typ) { + enclose("(", pp_ty(&ty.typ), ")") + } else { + pp_ty(&ty.typ) + }; + if let Some(name) = &ty.name { + enclose("(", escape(name, false).append(" : ").append(typ), ")") + } else { + typ + } + } + _ => { + let args = args.iter().map(|arg| { + if let Some(name) = &arg.name { + escape(name, false).append(" : ").append(pp_ty(&arg.typ)) + } else { + pp_ty(&arg.typ) + } + }); + let doc = concat(args, ","); + enclose("(", doc, ")") + } + } +} + +fn pp_rets(args: &[Type]) -> RcDoc { match args { [ty] => { if is_tuple(ty) { diff --git a/rust/candid_parser/src/bindings/rust.rs b/rust/candid_parser/src/bindings/rust.rs index c2f58fbbd..4fa80e7e1 100644 --- a/rust/candid_parser/src/bindings/rust.rs +++ b/rust/candid_parser/src/bindings/rust.rs @@ -3,8 +3,8 @@ use crate::{ configs::{ConfigState, ConfigTree, Configs, Context, StateElem}, Deserialize, }; -use candid::pretty::utils::*; use candid::types::{Field, Function, Label, SharedLabel, Type, TypeEnv, TypeInner}; +use candid::{pretty::utils::*, types::ArgType}; use convert_case::{Case, Casing}; use pretty::RcDoc; use serde::Serialize; @@ -168,9 +168,15 @@ impl<'a> State<'a> { .map(|(k, v)| (k.clone(), v.clone())) .collect(), ); - let src = candid::pretty::candid::pp_init_args(&env, &[src.clone()]) - .pretty(80) - .to_string(); + let src = candid::pretty::candid::pp_init_args( + &env, + &[ArgType { + name: None, + typ: src.clone(), + }], + ) + .pretty(80) + .to_string(); let match_path = self.state.config_source.get("use_type").unwrap().join("."); let test_name = use_type.replace(|c: char| !c.is_ascii_alphanumeric(), "_"); let body = format!( @@ -487,7 +493,22 @@ fn test_{test_name}() {{ } lines(res.into_iter()) } - fn pp_args<'b>(&mut self, args: &'b [Type], prefix: &'b str) -> RcDoc<'b> { + fn pp_args<'b>(&mut self, args: &'b [ArgType], prefix: &'b str) -> RcDoc<'b> { + let doc: Vec<_> = args + .iter() + .enumerate() + .map(|(i, t)| { + let lab = t.name.clone().unwrap_or_else(|| format!("{prefix}{i}")); + let old = self.state.push_state(&StateElem::Label(&lab)); + let res = self.pp_ty(&t.typ, true); + self.state.pop_state(old, StateElem::Label(&lab)); + res + }) + .collect(); + let doc = concat(doc.into_iter(), ","); + enclose("(", doc, ")") + } + fn pp_rets<'b>(&mut self, args: &'b [Type], prefix: &'b str) -> RcDoc<'b> { let doc: Vec<_> = args .iter() .enumerate() @@ -506,7 +527,7 @@ fn test_{test_name}() {{ let lab = StateElem::TypeStr("func"); let old = self.state.push_state(&lab); let args = self.pp_args(&f.args, "arg"); - let rets = self.pp_args(&f.rets, "ret"); + let rets = self.pp_rets(&f.rets, "ret"); let modes = candid::pretty::candid::pp_modes(&f.modes); let res = args .append(" ->") @@ -552,8 +573,8 @@ fn test_{test_name}() {{ .args .iter() .enumerate() - .map(|(i, ty)| { - let lab = format!("arg{i}"); + .map(|(i, arg)| { + let lab = arg.name.clone().unwrap_or_else(|| format!("arg{i}")); let old = self.state.push_state(&StateElem::Label(&lab)); let name = self .state @@ -562,7 +583,7 @@ fn test_{test_name}() {{ .clone() .unwrap_or_else(|| lab.clone()); self.state.update_stats("name"); - let res = self.pp_ty(ty, true); + let res = self.pp_ty(&arg.typ, true); self.state.pop_state(old, StateElem::Label(&lab)); (name, res) }) @@ -609,8 +630,8 @@ fn test_{test_name}() {{ let args: Vec<_> = args .iter() .enumerate() - .map(|(i, ty)| { - let lab = format!("arg{i}"); + .map(|(i, arg)| { + let lab = arg.name.clone().unwrap_or_else(|| format!("arg{i}")); let old = self.state.push_state(&StateElem::Label(&lab)); let name = self .state @@ -619,7 +640,7 @@ fn test_{test_name}() {{ .clone() .unwrap_or_else(|| lab.clone()); self.state.update_stats("name"); - let res = self.pp_ty(ty, true); + let res = self.pp_ty(&arg.typ, true); self.state.pop_state(old, StateElem::Label(&lab)); (name, res.pretty(LINE_WIDTH).to_string()) }) @@ -896,8 +917,8 @@ impl NominalState<'_> { .args .into_iter() .enumerate() - .map(|(i, ty)| { - let lab = format!("arg{i}"); + .map(|(i, arg)| { + let lab = arg.name.clone().unwrap_or_else(|| format!("arg{i}")); let old = self.state.push_state(&StateElem::Label(&lab)); let idx = if i == 0 { "".to_string() @@ -905,10 +926,13 @@ impl NominalState<'_> { i.to_string() }; path.push(TypePath::Func(format!("arg{idx}"))); - let ty = self.nominalize(env, path, &ty); + let ty = self.nominalize(env, path, &arg.typ); path.pop(); self.state.pop_state(old, StateElem::Label(&lab)); - ty + ArgType { + name: arg.name.clone(), + typ: ty, + } }) .collect(), rets: func @@ -982,14 +1006,17 @@ impl NominalState<'_> { }, TypeInner::Class(args, ty) => TypeInner::Class( args.iter() - .map(|ty| { + .map(|arg| { let elem = StateElem::Label("init"); let old = self.state.push_state(&elem); path.push(TypePath::Init); - let ty = self.nominalize(env, path, ty); + let ty = self.nominalize(env, path, &arg.typ); path.pop(); self.state.pop_state(old, elem); - ty + ArgType { + name: arg.name.clone(), + typ: ty, + } }) .collect(), self.nominalize(env, path, ty), diff --git a/rust/candid_parser/src/bindings/typescript.rs b/rust/candid_parser/src/bindings/typescript.rs index 1f4f3bfce..5a4460bf2 100644 --- a/rust/candid_parser/src/bindings/typescript.rs +++ b/rust/candid_parser/src/bindings/typescript.rs @@ -109,7 +109,7 @@ fn pp_field<'a>(env: &'a TypeEnv, field: &'a Field, is_ref: bool) -> RcDoc<'a> { } fn pp_function<'a>(env: &'a TypeEnv, func: &'a Function) -> RcDoc<'a> { - let args = func.args.iter().map(|ty| pp_ty(env, ty, true)); + let args = func.args.iter().map(|arg| pp_ty(env, &arg.typ, true)); let args = enclose("[", concat(args, ","), "]"); let rets = match func.rets.len() { 0 => str("undefined"), diff --git a/rust/candid_parser/src/grammar.lalrpop b/rust/candid_parser/src/grammar.lalrpop index 5d00fe827..3a3f3bb05 100644 --- a/rust/candid_parser/src/grammar.lalrpop +++ b/rust/candid_parser/src/grammar.lalrpop @@ -1,6 +1,6 @@ -use super::types::{IDLType, PrimType, TypeField, FuncType, Binding, Dec, IDLProg, IDLTypes, IDLInitArgs}; +use super::types::{IDLType, PrimType, TypeField, FuncType, Binding, Dec, IDLProg, IDLTypes, IDLInitArgs, IDLArgType}; use super::test::{Assert, Input, Test}; -use super::token::{Token, error2, LexicalError, Span}; +use super::token::{Token, error, error2, LexicalError, Span}; use candid::{Principal, types::Label}; use candid::types::value::{IDLField, IDLValue, IDLArgs, VariantValue}; use candid::types::{TypeEnv, FuncMode}; @@ -217,16 +217,24 @@ VariantFieldTyp: TypeField = { FieldId =>? Ok(TypeField { label: Label::Id(<>), typ: IDLType::PrimT(PrimType::Null) }), } -TupTyp: Vec = "(" > ")" => <>; +ArgTupTyp: Vec = "(" > ")" =>? { + let args = <>; + let mut named_args: Vec = args.iter().filter_map(|a| a.name.clone()).collect(); + named_args.sort(); + check_unique(named_args.iter()).map_err(|e| error(e))?; + Ok(args) +}; + +TupTyp: Vec = "(" > ")" => <>; FuncTyp: FuncType = { - "->" => + "->" => FuncType { modes, args, rets }, } -ArgTyp: IDLType = { - Typ => <>, - Name ":" => <>, +ArgTyp: IDLArgType = { + => IDLArgType::new(t), + ":" => IDLArgType::new_with_name(t, n), } FuncMode: FuncMode = { @@ -263,7 +271,7 @@ Actor: IDLType = { MainActor: IDLType = { "service" "id"? ":" ";"? => <>, - "service" "id"? ":" "->" ";"? => IDLType::ClassT(args, Box::new(t)), + "service" "id"? ":" "->" ";"? => IDLType::ClassT(args, Box::new(t)), } pub IDLProg: IDLProg = { @@ -271,7 +279,7 @@ pub IDLProg: IDLProg = { } pub IDLInitArgs: IDLInitArgs = { - > => IDLInitArgs { decs, args } + > => IDLInitArgs { decs, args } } // Test file diff --git a/rust/candid_parser/src/lib.rs b/rust/candid_parser/src/lib.rs index 9853c3d02..ca850bcdf 100644 --- a/rust/candid_parser/src/lib.rs +++ b/rust/candid_parser/src/lib.rs @@ -69,7 +69,7 @@ //! //! let method = env.get_method(&actor, "g").unwrap(); //! assert_eq!(method.is_query(), true); -//! assert_eq!(method.args, vec![TypeInner::Var("List".to_string()).into()]); +//! assert_eq!(method.args.iter().map(|arg| arg.typ.clone()).collect::>(), vec![TypeInner::Var("List".to_string()).into()]); //! # Ok(()) //! # } //! ``` @@ -102,7 +102,7 @@ //! let method = env.get_method(&actor, "f").unwrap(); //! let args = parse_idl_args("(42, 42, 42, 42)")?; //! // Serialize arguments with candid types -//! let encoded = args.to_bytes_with_types(&env, &method.args)?; +//! let encoded = args.to_bytes_with_types(&env, &method.args.iter().map(|arg| arg.typ.clone()).collect::>())?; //! let decoded = IDLArgs::from_bytes(&encoded)?; //! assert_eq!(decoded.args, //! vec![IDLValue::Nat8(42), diff --git a/rust/candid_parser/src/types.rs b/rust/candid_parser/src/types.rs index feb470e24..91ed6efdb 100644 --- a/rust/candid_parser/src/types.rs +++ b/rust/candid_parser/src/types.rs @@ -11,7 +11,7 @@ pub enum IDLType { RecordT(Vec), VariantT(Vec), ServT(Vec), - ClassT(Vec, Box), + ClassT(Vec, Box), PrincipalT, } @@ -63,10 +63,34 @@ pub enum PrimType { #[derive(Debug, Clone)] pub struct FuncType { pub modes: Vec, - pub args: Vec, + pub args: Vec, pub rets: Vec, } +#[derive(Debug, Clone)] +pub struct IDLArgType { + pub typ: IDLType, + pub name: Option, +} + +impl IDLArgType { + pub fn new(typ: IDLType) -> Self { + Self { typ, name: None } + } + + /// Create a new IDLArgType with a name. + /// If the name is an `u32` number, we set it to None + /// as we don't want to use it as a arg name. + pub fn new_with_name(typ: IDLType, name: String) -> Self { + let name = if name.parse::().is_ok() { + None + } else { + Some(name) + }; + Self { typ, name } + } +} + #[derive(Debug, Clone)] pub struct TypeField { pub label: Label, @@ -95,7 +119,7 @@ pub struct IDLProg { #[derive(Debug)] pub struct IDLInitArgs { pub decs: Vec, - pub args: Vec, + pub args: Vec, } impl std::str::FromStr for IDLProg { diff --git a/rust/candid_parser/src/typing.rs b/rust/candid_parser/src/typing.rs index 807f3690b..3cc20367f 100644 --- a/rust/candid_parser/src/typing.rs +++ b/rust/candid_parser/src/typing.rs @@ -1,6 +1,6 @@ use super::types::*; use crate::{pretty_parse, Error, Result}; -use candid::types::{Field, Function, Type, TypeEnv, TypeInner}; +use candid::types::{ArgType, Field, Function, Type, TypeEnv, TypeInner}; use candid::utils::check_unique; use std::collections::{BTreeMap, BTreeSet}; use std::path::{Path, PathBuf}; @@ -68,8 +68,8 @@ pub fn check_type(env: &Env, t: &IDLType) -> Result { IDLType::PrincipalT => Ok(TypeInner::Principal.into()), IDLType::FuncT(func) => { let mut t1 = Vec::new(); - for t in func.args.iter() { - t1.push(check_type(env, t)?); + for arg in func.args.iter() { + t1.push(check_arg(env, arg)?); } let mut t2 = Vec::new(); for t in func.rets.iter() { @@ -99,6 +99,13 @@ pub fn check_type(env: &Env, t: &IDLType) -> Result { } } +fn check_arg(env: &Env, arg: &IDLArgType) -> Result { + Ok(ArgType { + name: arg.name.clone(), + typ: check_type(env, &arg.typ)?, + }) +} + fn check_fields(env: &Env, fs: &[TypeField]) -> Result> { // field label duplication is checked in the parser let mut res = Vec::new(); @@ -188,7 +195,7 @@ fn check_actor(env: &Env, actor: &Option) -> Result> { Some(IDLType::ClassT(ts, t)) => { let mut args = Vec::new(); for arg in ts.iter() { - args.push(check_type(env, arg)?); + args.push(check_arg(env, arg)?); } let serv = check_type(env, t)?; env.te.as_service(&serv)?; @@ -257,13 +264,13 @@ pub fn check_init_args( te: &mut TypeEnv, main_env: &TypeEnv, prog: &IDLInitArgs, -) -> Result> { +) -> Result> { let mut env = Env { te, pre: false }; check_decs(&mut env, &prog.decs)?; env.te.merge(main_env)?; let mut args = Vec::new(); for arg in prog.args.iter() { - args.push(check_type(&env, arg)?); + args.push(check_arg(&env, arg)?); } Ok(args) } diff --git a/rust/candid_parser/src/utils.rs b/rust/candid_parser/src/utils.rs index 6e24b4320..1552d67e2 100644 --- a/rust/candid_parser/src/utils.rs +++ b/rust/candid_parser/src/utils.rs @@ -54,7 +54,10 @@ pub fn instantiate_candid(candid: CandidSource) -> Result<(Vec, (TypeEnv, let serv = serv.ok_or_else(|| Error::msg("the Candid interface has no main service type"))?; let serv = env.trace_type(&serv)?; Ok(match serv.as_ref() { - TypeInner::Class(args, ty) => (args.clone(), (env, ty.clone())), + TypeInner::Class(args, ty) => ( + args.iter().map(|arg| arg.typ.clone()).collect::>(), + (env, ty.clone()), + ), TypeInner::Service(_) => (vec![], (env, serv)), _ => unreachable!(), }) @@ -110,6 +113,6 @@ pub fn check_rust_type(candid_args: &str) -> Result<()> { let ty = rust_env.add::(); let ty = env.merge_type(rust_env.env, ty); let mut gamma = std::collections::HashSet::new(); - equal(&mut gamma, &env, &args[0], &ty)?; + equal(&mut gamma, &env, &args[0].typ, &ty)?; Ok(()) } diff --git a/rust/candid_parser/tests/assets/class.did b/rust/candid_parser/tests/assets/class.did index 662461b1c..fb1fdfb7f 100644 --- a/rust/candid_parser/tests/assets/class.did +++ b/rust/candid_parser/tests/assets/class.did @@ -1,6 +1,6 @@ type Profile = record { age: nat8; name: text }; type List = opt record { int; List }; -service : (int, List, Profile) -> { +service : (int, l : List, Profile) -> { get : () -> (List); set : (List) -> (List); } diff --git a/rust/candid_parser/tests/assets/collision_arguments.did b/rust/candid_parser/tests/assets/collision_arguments.did new file mode 100644 index 000000000..c7f3117e0 --- /dev/null +++ b/rust/candid_parser/tests/assets/collision_arguments.did @@ -0,0 +1,4 @@ +type f = func (a : nat, b : bool, a : text) -> (); +service : { + f : f; +}; diff --git a/rust/candid_parser/tests/assets/collision_arguments2.did b/rust/candid_parser/tests/assets/collision_arguments2.did new file mode 100644 index 000000000..0a4773fe6 --- /dev/null +++ b/rust/candid_parser/tests/assets/collision_arguments2.did @@ -0,0 +1,3 @@ +service : (b : nat, bool, b : text) -> { + f : (text, text) -> (); +}; diff --git a/rust/candid_parser/tests/assets/ok/class.did b/rust/candid_parser/tests/assets/ok/class.did index a47497564..4850a508e 100644 --- a/rust/candid_parser/tests/assets/ok/class.did +++ b/rust/candid_parser/tests/assets/ok/class.did @@ -1,3 +1,6 @@ type List = opt record { int; List }; type Profile = record { age : nat8; name : text }; -service : (int, List, Profile) -> { get : () -> (List); set : (List) -> (List) } +service : (int, l : List, Profile) -> { + get : () -> (List); + set : (List) -> (List); +} diff --git a/rust/candid_parser/tests/assets/ok/class.mo b/rust/candid_parser/tests/assets/ok/class.mo index ad5666f45..41ab07b63 100644 --- a/rust/candid_parser/tests/assets/ok/class.mo +++ b/rust/candid_parser/tests/assets/ok/class.mo @@ -4,7 +4,7 @@ module { public type List = ?(Int, List); public type Profile = { age : Nat8; name : Text }; - public type Self = (Int, List, Profile) -> async actor { + public type Self = (Int, l : List, Profile) -> async actor { get : shared () -> async List; set : shared List -> async List; } diff --git a/rust/candid_parser/tests/assets/ok/class.rs b/rust/candid_parser/tests/assets/ok/class.rs index ecb1b1904..be0429b4a 100644 --- a/rust/candid_parser/tests/assets/ok/class.rs +++ b/rust/candid_parser/tests/assets/ok/class.rs @@ -9,7 +9,7 @@ pub struct List(pub Option<(candid::Int,Box,)>); pub struct Profile { pub age: u8, pub name: String } #[ic_cdk::init] -fn init(arg0: candid::Int, arg1: List, arg2: Profile) { +fn init(arg0: candid::Int, l: List, arg2: Profile) { unimplemented!() } #[ic_cdk::update] diff --git a/rust/candid_parser/tests/assets/ok/collision_arguments.fail b/rust/candid_parser/tests/assets/ok/collision_arguments.fail new file mode 100644 index 000000000..9aa36e9cc --- /dev/null +++ b/rust/candid_parser/tests/assets/ok/collision_arguments.fail @@ -0,0 +1 @@ +Candid parser error: label 'a' hash collision with 'a' diff --git a/rust/candid_parser/tests/assets/ok/collision_arguments2.fail b/rust/candid_parser/tests/assets/ok/collision_arguments2.fail new file mode 100644 index 000000000..0c2043a40 --- /dev/null +++ b/rust/candid_parser/tests/assets/ok/collision_arguments2.fail @@ -0,0 +1 @@ +Candid parser error: label 'b' hash collision with 'b' diff --git a/rust/candid_parser/tests/assets/ok/example.did b/rust/candid_parser/tests/assets/ok/example.did index 2ec745f59..6bd434f09 100644 --- a/rust/candid_parser/tests/assets/ok/example.did +++ b/rust/candid_parser/tests/assets/ok/example.did @@ -4,7 +4,7 @@ type List = opt record { head : int; tail : List }; type a = variant { a; b : b }; type b = record { int; nat }; type broker = service { - find : (text) -> (service { current : () -> (nat32); up : () -> () }); + find : (name : text) -> (service { current : () -> (nat32); up : () -> () }); }; type f = func (List, func (int32) -> (int64)) -> (opt List, res); type list = opt node; @@ -26,7 +26,7 @@ type node = record { head : nat; tail : list }; type res = variant { Ok : record { int; nat }; Err : record { error : text } }; type s = service { f : t; g : (list) -> (B, tree, stream) }; type stream = opt record { head : nat; next : func () -> (stream) query }; -type t = func (s) -> (); +type t = func (server : s) -> (); type tree = variant { branch : record { val : int; left : tree; right : tree }; leaf : int; @@ -34,7 +34,7 @@ type tree = variant { service : { bbbbb : (b) -> (); f : t; - f1 : (list, blob, opt bool) -> () oneway; + f1 : (list, test : blob, opt bool) -> () oneway; g : (list) -> (B, tree, stream); g1 : (my_type, List, opt List, nested) -> (int, broker, nested_res) query; h : (vec opt text, variant { A : nat; B : opt text }, opt List) -> ( diff --git a/rust/candid_parser/tests/assets/ok/example.mo b/rust/candid_parser/tests/assets/ok/example.mo index 4847215a5..76bb1f058 100644 --- a/rust/candid_parser/tests/assets/ok/example.mo +++ b/rust/candid_parser/tests/assets/ok/example.mo @@ -8,7 +8,7 @@ module { public type a = { #a; #b : b }; public type b = (Int, Nat); public type broker = actor { - find : shared Text -> async actor { + find : shared (name : Text) -> async actor { current : shared () -> async Nat32; up : shared () -> async (); }; @@ -36,7 +36,7 @@ module { public type res = { #Ok : (Int, Nat); #Err : { error : Text } }; public type s = actor { f : t; g : shared list -> async (B, tree, stream) }; public type stream = ?{ head : Nat; next : shared query () -> async stream }; - public type t = shared s -> async (); + public type t = shared (server : s) -> async (); public type tree = { #branch : { val : Int; left : tree; right : tree }; #leaf : Int; @@ -44,7 +44,7 @@ module { public type Self = actor { bbbbb : shared b -> async (); f : t; - f1 : shared (list, Blob, ?Bool) -> (); + f1 : shared (list, test : Blob, ?Bool) -> (); g : shared list -> async (B, tree, stream); g1 : shared query (my_type, List, ?List, nested) -> async ( Int, diff --git a/rust/candid_parser/tests/assets/ok/example.rs b/rust/candid_parser/tests/assets/ok/example.rs index 03559464c..c4a9d4002 100644 --- a/rust/candid_parser/tests/assets/ok/example.rs +++ b/rust/candid_parser/tests/assets/ok/example.rs @@ -107,11 +107,11 @@ impl Service { pub async fn bbbbb(&self, arg0: &B) -> Result<()> { ic_cdk::call(self.0, "bbbbb", (arg0,)).await } - pub async fn f(&self, arg0: &S) -> Result<()> { - ic_cdk::call(self.0, "f", (arg0,)).await + pub async fn f(&self, server: &S) -> Result<()> { + ic_cdk::call(self.0, "f", (server,)).await } - pub async fn f_1(&self, arg0: &List, arg1: &serde_bytes::ByteBuf, arg2: &Option) -> Result<()> { - ic_cdk::call(self.0, "f1", (arg0,arg1,arg2,)).await + pub async fn f_1(&self, arg0: &List, test: &serde_bytes::ByteBuf, arg2: &Option) -> Result<()> { + ic_cdk::call(self.0, "f1", (arg0,test,arg2,)).await } pub async fn g(&self, arg0: &List) -> Result<(B,Tree,Stream,)> { ic_cdk::call(self.0, "g", (arg0,)).await diff --git a/rust/candid_parser/tests/assets/ok/fieldnat.did b/rust/candid_parser/tests/assets/ok/fieldnat.did index cc0d724b3..921423bb4 100644 --- a/rust/candid_parser/tests/assets/ok/fieldnat.did +++ b/rust/candid_parser/tests/assets/ok/fieldnat.did @@ -1,7 +1,7 @@ type non_tuple = record { 1 : text; 2 : text }; type tuple = record { text; text }; service : { - bab : (int, nat) -> (); + bab : (two : int, nat) -> (); bar : (record { "2" : int }) -> (variant { e20; e30 }); bas : (record { int; int }) -> (record { text; nat }); baz : (record { 2 : int; "2" : nat }) -> (record {}); diff --git a/rust/candid_parser/tests/assets/ok/fieldnat.mo b/rust/candid_parser/tests/assets/ok/fieldnat.mo index 64f771834..f38c0359f 100644 --- a/rust/candid_parser/tests/assets/ok/fieldnat.mo +++ b/rust/candid_parser/tests/assets/ok/fieldnat.mo @@ -5,7 +5,7 @@ module { public type non_tuple = { _1_ : Text; _2_ : Text }; public type tuple = (Text, Text); public type Self = actor { - bab : shared (Int, Nat) -> async (); + bab : shared (two : Int, Nat) -> async (); bar : shared { _50_ : Int } -> async { #e20; #e30 }; bas : shared ((Int, Int)) -> async ((Text, Nat)); baz : shared { _2_ : Int; _50_ : Nat } -> async {}; diff --git a/rust/candid_parser/tests/assets/ok/fieldnat.rs b/rust/candid_parser/tests/assets/ok/fieldnat.rs index 93720cb94..3d335e76b 100644 --- a/rust/candid_parser/tests/assets/ok/fieldnat.rs +++ b/rust/candid_parser/tests/assets/ok/fieldnat.rs @@ -29,8 +29,8 @@ pub struct FooRet { pub _2_: candid::Int, pub _2: candid::Int } pub struct Service(pub Principal); impl Service { - pub async fn bab(&self, arg0: &candid::Int, arg1: &candid::Nat) -> Result<()> { - ic_cdk::call(self.0, "bab", (arg0,arg1,)).await + pub async fn bab(&self, two: &candid::Int, arg1: &candid::Nat) -> Result<()> { + ic_cdk::call(self.0, "bab", (two,arg1,)).await } pub async fn bar(&self, arg0: &BarArg) -> Result<(BarRet,)> { ic_cdk::call(self.0, "bar", (arg0,)).await diff --git a/rust/candid_parser/tests/assets/ok/keyword.did b/rust/candid_parser/tests/assets/ok/keyword.did index 591168733..43965aefa 100644 --- a/rust/candid_parser/tests/assets/ok/keyword.did +++ b/rust/candid_parser/tests/assets/ok/keyword.did @@ -7,7 +7,7 @@ type node = record { head : nat; tail : list }; type o = opt o; type return = service { f : t; g : (list) -> (if, stream) }; type stream = opt record { head : nat; next : func () -> (stream) query }; -type t = func (return) -> (); +type t = func (server : return) -> (); service : { Oneway : () -> () oneway; f_ : (o) -> (o); diff --git a/rust/candid_parser/tests/assets/ok/keyword.mo b/rust/candid_parser/tests/assets/ok/keyword.mo index 2802968e2..408ff6f82 100644 --- a/rust/candid_parser/tests/assets/ok/keyword.mo +++ b/rust/candid_parser/tests/assets/ok/keyword.mo @@ -11,7 +11,7 @@ module { public type o = ?o; public type return_ = actor { f : t; g : shared list -> async (if_, stream) }; public type stream = ?{ head : Nat; next : shared query () -> async stream }; - public type t = shared return_ -> async (); + public type t = shared (server : return_) -> async (); public type Self = actor { Oneway : shared () -> (); f__ : shared o -> async o; diff --git a/rust/candid_parser/tests/assets/ok/keyword.rs b/rust/candid_parser/tests/assets/ok/keyword.rs index 20df13b49..e0b8ddb7b 100644 --- a/rust/candid_parser/tests/assets/ok/keyword.rs +++ b/rust/candid_parser/tests/assets/ok/keyword.rs @@ -66,8 +66,8 @@ impl Service { pub async fn r#return(&self, arg0: &O) -> Result<(O,)> { ic_cdk::call(self.0, "return", (arg0,)).await } - pub async fn service(&self, arg0: &Return) -> Result<()> { - ic_cdk::call(self.0, "service", (arg0,)).await + pub async fn service(&self, server: &Return) -> Result<()> { + ic_cdk::call(self.0, "service", (server,)).await } pub async fn tuple(&self, arg0: &(candid::Int,serde_bytes::ByteBuf,String,)) -> Result<((candid::Int,u8,),)> { ic_cdk::call(self.0, "tuple", (arg0,)).await diff --git a/rust/candid_parser/tests/assets/ok/recursion.did b/rust/candid_parser/tests/assets/ok/recursion.did index 3f8214477..bc4278a62 100644 --- a/rust/candid_parser/tests/assets/ok/recursion.did +++ b/rust/candid_parser/tests/assets/ok/recursion.did @@ -4,7 +4,7 @@ type list = opt node; type node = record { head : nat; tail : list }; type s = service { f : t; g : (list) -> (B, tree, stream) }; type stream = opt record { head : nat; next : func () -> (stream) query }; -type t = func (s) -> (); +type t = func (server : s) -> (); type tree = variant { branch : record { val : int; left : tree; right : tree }; leaf : int; diff --git a/rust/candid_parser/tests/assets/ok/recursion.mo b/rust/candid_parser/tests/assets/ok/recursion.mo index fd8b2b9c1..6cc51fe02 100644 --- a/rust/candid_parser/tests/assets/ok/recursion.mo +++ b/rust/candid_parser/tests/assets/ok/recursion.mo @@ -8,7 +8,7 @@ module { public type node = { head : Nat; tail : list }; public type s = actor { f : t; g : shared list -> async (B, tree, stream) }; public type stream = ?{ head : Nat; next : shared query () -> async stream }; - public type t = shared s -> async (); + public type t = shared (server : s) -> async (); public type tree = { #branch : { val : Int; left : tree; right : tree }; #leaf : Int; diff --git a/rust/candid_parser/tests/assets/ok/recursion.rs b/rust/candid_parser/tests/assets/ok/recursion.rs index 236f22543..3eb52d95f 100644 --- a/rust/candid_parser/tests/assets/ok/recursion.rs +++ b/rust/candid_parser/tests/assets/ok/recursion.rs @@ -31,8 +31,8 @@ candid::define_service!(pub S : { pub struct Service(pub Principal); impl Service { - pub async fn f(&self, arg0: &S) -> Result<()> { - ic_cdk::call(self.0, "f", (arg0,)).await + pub async fn f(&self, server: &S) -> Result<()> { + ic_cdk::call(self.0, "f", (server,)).await } pub async fn g(&self, arg0: &List) -> Result<(B,Tree,Stream,)> { ic_cdk::call(self.0, "g", (arg0,)).await diff --git a/rust/candid_parser/tests/value.rs b/rust/candid_parser/tests/value.rs index 56ec90e3c..e23a579a4 100644 --- a/rust/candid_parser/tests/value.rs +++ b/rust/candid_parser/tests/value.rs @@ -37,7 +37,16 @@ service : { let method = env.get_method(&actor, "f").unwrap(); { let args = parse_idl_args("(42,42,42,42)").unwrap(); - let encoded = args.to_bytes_with_types(&env, &method.args).unwrap(); + let encoded = args + .to_bytes_with_types( + &env, + &method + .args + .iter() + .map(|arg| arg.typ.clone()) + .collect::>(), + ) + .unwrap(); let decoded = IDLArgs::from_bytes(&encoded).unwrap(); assert_eq!( decoded.args, diff --git a/tools/didc/src/main.rs b/tools/didc/src/main.rs index 68861b2a0..26e0836ab 100644 --- a/tools/didc/src/main.rs +++ b/tools/didc/src/main.rs @@ -146,10 +146,9 @@ impl TypeAnnotation { .ok_or_else(|| Error::msg("Cannot use --method with a non-service did file"))?; let func = env.get_method(&actor, meth)?; let types = match mode { - Mode::Encode => &func.args, - Mode::Decode => &func.rets, - } - .clone(); + Mode::Encode => func.args.iter().map(|arg| arg.typ.clone()).collect(), + Mode::Decode => func.rets.clone(), + }; Ok((env, types)) } _ => unreachable!(),