git.delta.rocks / jrsonnet / refs/commits / 50ca1d2d134a

difftreelog

feat(evaluator) function signature help

Yaroslav Bolyukin2022-07-23parent: #907d6da.patch.diff
in: master
When calling functions with wrong arguments, evaluator will now suggest
correct function signature

2 files changed

modifiedcrates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth
32 out32 out
33}33}
34
35fn format_signature(sig: &FunctionSignature) -> String {
36 let mut out = String::new();
37 out.push_str("\nFunction has the following signature: ");
38 out.push('(');
39 if sig.is_empty() {
40 out.push_str("/*no arguments*/");
41 } else {
42 for (i, (name, has_default)) in sig.iter().enumerate() {
43 if i != 0 {
44 out.push_str(", ");
45 }
46 if let Some(name) = name {
47 out.push_str(name);
48 } else {
49 out.push_str("<unnamed>");
50 }
51 if *has_default {
52 out.push_str(" = <default>");
53 }
54 }
55 }
56 out.push(')');
57 out
58}
3459
35const fn format_empty_str(str: &str) -> &str {60const fn format_empty_str(str: &str) -> &str {
36 if str.is_empty() {61 if str.is_empty() {
40 }65 }
41}66}
67
68type FunctionSignature = Vec<(Option<IStr>, bool)>;
4269
43#[derive(Error, Debug, Clone, Trace)]70#[derive(Error, Debug, Clone, Trace)]
44pub enum Error {71pub enum Error {
84 UnknownFunctionParameter(String),111 UnknownFunctionParameter(String),
85 #[error("argument {0} is already bound")]112 #[error("argument {0} is already bound")]
86 BindingParameterASecondTime(IStr),113 BindingParameterASecondTime(IStr),
87 #[error("too many args, function has {0}")]114 #[error("too many args, function has {0}{}", format_signature(.1))]
88 TooManyArgsFunctionHas(usize),115 TooManyArgsFunctionHas(usize, FunctionSignature),
89 #[error("function argument is not passed: {0}")]116 #[error("function argument is not passed: {0}{}", format_signature(.1))]
90 FunctionParameterNotBoundInCall(IStr),117 FunctionParameterNotBoundInCall(IStr, FunctionSignature),
91118
92 #[error("external variable is not defined: {0}")]119 #[error("external variable is not defined: {0}")]
93 UndefinedExternalVariable(IStr),120 UndefinedExternalVariable(IStr),
modifiedcrates/jrsonnet-evaluator/src/function/parse.rsdiffbeforeafterboth
49 let mut passed_args = GcHashMap::with_capacity(params.len());49 let mut passed_args = GcHashMap::with_capacity(params.len());
50 if args.unnamed_len() > params.len() {50 if args.unnamed_len() > params.len() {
51 throw!(TooManyArgsFunctionHas(params.len()))51 throw!(TooManyArgsFunctionHas(
52 params.len(),
53 params.iter().map(|p| (p.0.name(), p.1.is_some())).collect()
54 ))
52 }55 }
5356
126 .0129 .0
127 .clone()130 .clone()
128 .name()131 .name()
129 .unwrap_or_else(|| "<destruct>".into())132 .unwrap_or_else(|| "<destruct>".into()),
133 params.iter().map(|p| (p.0.name(), p.1.is_some())).collect()
130 ));134 ));
131 }135 }
132 }136 }
160 let mut passed_args = GcHashMap::with_capacity(params.len());164 let mut passed_args = GcHashMap::with_capacity(params.len());
161 if args.unnamed_len() > params.len() {165 if args.unnamed_len() > params.len() {
162 throw!(TooManyArgsFunctionHas(params.len()))166 throw!(TooManyArgsFunctionHas(
167 params.len(),
168 params
169 .iter()
170 .map(|p| (Some(p.name.as_ref().into()), p.has_default))
171 .collect()
172 ))
163 }173 }
164174
203 });213 });
204 if !found {214 if !found {
205 throw!(FunctionParameterNotBoundInCall(param.name.clone().into()));215 throw!(FunctionParameterNotBoundInCall(
216 param.name.clone().into(),
217 params
218 .iter()
219 .map(|p| (Some(p.name.as_ref().into()), p.has_default))
220 .collect()
221 ));
206 }222 }
207 }223 }
215/// and with unbound values causing error to be returned231/// and with unbound values causing error to be returned
216pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Result<Context> {232pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Result<Context> {
217 #[derive(Trace)]233 #[derive(Trace)]
218 struct DependsOnUnbound(IStr);234 struct DependsOnUnbound(IStr, ParamsDesc);
219 impl ThunkValue for DependsOnUnbound {235 impl ThunkValue for DependsOnUnbound {
220 type Output = Val;236 type Output = Val;
221 fn get(self: Box<Self>, _: State) -> Result<Val> {237 fn get(self: Box<Self>, _: State) -> Result<Val> {
222 Err(FunctionParameterNotBoundInCall(self.0.clone()).into())238 Err(FunctionParameterNotBoundInCall(
239 self.0.clone(),
240 self.1.iter().map(|p| (p.0.name(), p.1.is_some())).collect(),
241 )
242 .into())
223 }243 }
243 destruct(263 destruct(
244 &param.0,264 &param.0,
245 Thunk::new(tb!(DependsOnUnbound(265 Thunk::new(tb!(DependsOnUnbound(
246 param.0.name().unwrap_or_else(|| "<destruct>".into())266 param.0.name().unwrap_or_else(|| "<destruct>".into()),
267 params.clone()
247 ))),268 ))),
248 fctx.clone(),269 fctx.clone(),
249 &mut bindings,270 &mut bindings,