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
--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -32,6 +32,31 @@
 	out
 }
 
+fn format_signature(sig: &FunctionSignature) -> String {
+	let mut out = String::new();
+	out.push_str("\nFunction has the following signature: ");
+	out.push('(');
+	if sig.is_empty() {
+		out.push_str("/*no arguments*/");
+	} else {
+		for (i, (name, has_default)) in sig.iter().enumerate() {
+			if i != 0 {
+				out.push_str(", ");
+			}
+			if let Some(name) = name {
+				out.push_str(name);
+			} else {
+				out.push_str("<unnamed>");
+			}
+			if *has_default {
+				out.push_str(" = <default>");
+			}
+		}
+	}
+	out.push(')');
+	out
+}
+
 const fn format_empty_str(str: &str) -> &str {
 	if str.is_empty() {
 		"\"\" (empty string)"
@@ -40,6 +65,8 @@
 	}
 }
 
+type FunctionSignature = Vec<(Option<IStr>, bool)>;
+
 #[derive(Error, Debug, Clone, Trace)]
 pub enum Error {
 	#[error("intrinsic not found: {0}")]
@@ -84,10 +111,10 @@
 	UnknownFunctionParameter(String),
 	#[error("argument {0} is already bound")]
 	BindingParameterASecondTime(IStr),
-	#[error("too many args, function has {0}")]
-	TooManyArgsFunctionHas(usize),
-	#[error("function argument is not passed: {0}")]
-	FunctionParameterNotBoundInCall(IStr),
+	#[error("too many args, function has {0}{}", format_signature(.1))]
+	TooManyArgsFunctionHas(usize, FunctionSignature),
+	#[error("function argument is not passed: {0}{}", format_signature(.1))]
+	FunctionParameterNotBoundInCall(IStr, FunctionSignature),
 
 	#[error("external variable is not defined: {0}")]
 	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,