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
--- a/crates/jrsonnet-evaluator/src/function/parse.rs
+++ b/crates/jrsonnet-evaluator/src/function/parse.rs
@@ -48,7 +48,10 @@
 ) -> Result<Context> {
 	let mut passed_args = GcHashMap::with_capacity(params.len());
 	if args.unnamed_len() > params.len() {
-		throw!(TooManyArgsFunctionHas(params.len()))
+		throw!(TooManyArgsFunctionHas(
+			params.len(),
+			params.iter().map(|p| (p.0.name(), p.1.is_some())).collect()
+		))
 	}
 
 	let mut filled_named = 0;
@@ -126,7 +129,8 @@
 							.0
 							.clone()
 							.name()
-							.unwrap_or_else(|| "<destruct>".into())
+							.unwrap_or_else(|| "<destruct>".into()),
+						params.iter().map(|p| (p.0.name(), p.1.is_some())).collect()
 					));
 				}
 			}
@@ -159,7 +163,13 @@
 ) -> Result<GcHashMap<BuiltinParamName, Thunk<Val>>> {
 	let mut passed_args = GcHashMap::with_capacity(params.len());
 	if args.unnamed_len() > params.len() {
-		throw!(TooManyArgsFunctionHas(params.len()))
+		throw!(TooManyArgsFunctionHas(
+			params.len(),
+			params
+				.iter()
+				.map(|p| (Some(p.name.as_ref().into()), p.has_default))
+				.collect()
+		))
 	}
 
 	let mut filled_args = 0;
@@ -202,7 +212,13 @@
 					}
 				});
 				if !found {
-					throw!(FunctionParameterNotBoundInCall(param.name.clone().into()));
+					throw!(FunctionParameterNotBoundInCall(
+						param.name.clone().into(),
+						params
+							.iter()
+							.map(|p| (Some(p.name.as_ref().into()), p.has_default))
+							.collect()
+					));
 				}
 			}
 			unreachable!();
@@ -215,11 +231,15 @@
 /// and with unbound values causing error to be returned
 pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Result<Context> {
 	#[derive(Trace)]
-	struct DependsOnUnbound(IStr);
+	struct DependsOnUnbound(IStr, ParamsDesc);
 	impl ThunkValue for DependsOnUnbound {
 		type Output = Val;
 		fn get(self: Box<Self>, _: State) -> Result<Val> {
-			Err(FunctionParameterNotBoundInCall(self.0.clone()).into())
+			Err(FunctionParameterNotBoundInCall(
+				self.0.clone(),
+				self.1.iter().map(|p| (p.0.name(), p.1.is_some())).collect(),
+			)
+			.into())
 		}
 	}
 
@@ -243,7 +263,8 @@
 			destruct(
 				&param.0,
 				Thunk::new(tb!(DependsOnUnbound(
-					param.0.name().unwrap_or_else(|| "<destruct>".into())
+					param.0.name().unwrap_or_else(|| "<destruct>".into()),
+					params.clone()
 				))),
 				fctx.clone(),
 				&mut bindings,