vrl/compiler/expression/
function_call.rs

1use std::{fmt, sync::Arc};
2
3use super::Block;
4use crate::compiler::expression::function_call::Warning::AbortInfallible;
5use crate::compiler::state::{TypeInfo, TypeState};
6use crate::compiler::{
7    CompileConfig, Context, Expression, Function, Resolved, Span, TypeDef,
8    expression::{ExpressionError, FunctionArgument, levenstein},
9    function::{
10        ArgumentList, Closure, Example, FunctionCompileContext, Parameter,
11        closure::{self, VariableKind},
12    },
13    parser::{Ident, Node},
14    state::LocalEnv,
15    type_def::Details,
16    value::Kind,
17};
18use crate::diagnostic::{DiagnosticMessage, Label, Note, Severity, Urls};
19use crate::prelude::Note::SeeErrorDocs;
20
21pub(crate) struct Builder<'a> {
22    abort_on_error: bool,
23    arguments_with_unknown_type_validity: Vec<(Parameter, Node<FunctionArgument>)>,
24    call_span: Span,
25    ident_span: Span,
26    function_id: usize,
27    arguments: Arc<Vec<Node<FunctionArgument>>>,
28    closure: Option<(Vec<Ident>, closure::Input)>,
29    list: ArgumentList,
30    function: &'a dyn Function,
31}
32
33pub(crate) struct CallCompilationResult {
34    pub(crate) function_call: FunctionCall,
35    pub(crate) error: Option<FunctionCallError>,
36}
37
38impl<'a> Builder<'a> {
39    pub(crate) fn get_arg_list(&self) -> &ArgumentList {
40        &self.list
41    }
42
43    #[allow(clippy::too_many_lines)]
44    #[allow(clippy::too_many_arguments)]
45    pub(crate) fn new(
46        call_span: Span,
47        ident: Node<Ident>,
48        abort_on_error: bool,
49        arguments: Vec<Node<FunctionArgument>>,
50        funcs: &'a [Box<dyn Function>],
51        state_before_function_args: &TypeState,
52        state: &mut TypeState,
53        closure_variables: Option<Node<Vec<Node<Ident>>>>,
54    ) -> Result<Self, FunctionCallError> {
55        let (ident_span, ident) = ident.take();
56
57        // Check if function exists.
58        let Some((function_id, function)) = funcs
59            .iter()
60            .enumerate()
61            .find(|(_pos, f)| f.identifier() == ident.as_ref())
62        else {
63            let idents = funcs
64                .iter()
65                .map(|func| func.identifier())
66                .collect::<Vec<_>>();
67
68            return Err(FunctionCallError::Undefined {
69                ident_span,
70                ident: ident.clone(),
71                idents,
72            });
73        };
74
75        // Check function arity.
76        if arguments.len() > function.parameters().len() {
77            let arguments_span = {
78                let start = arguments.first().unwrap().span().start();
79                let end = arguments.last().unwrap().span().end();
80
81                Span::new(start, end)
82            };
83
84            return Err(FunctionCallError::WrongNumberOfArgs {
85                arguments_span,
86                max: function.parameters().len(),
87            });
88        }
89
90        // Keeps track of positional argument indices.
91        //
92        // Used to map a positional argument to its keyword. Keyword arguments
93        // can be used in any order, and don't count towards the index of
94        // positional arguments.
95        let mut index = 0;
96        let mut list = ArgumentList::default();
97
98        let mut arguments_with_unknown_type_validity = vec![];
99        for node in &arguments {
100            let (argument_span, argument) = node.clone().take();
101
102            let parameter = match argument.keyword() {
103                // positional argument
104                None => {
105                    index += 1;
106                    function.parameters().get(index - 1)
107                }
108
109                // keyword argument
110                Some(k) => function
111                    .parameters()
112                    .iter()
113                    .enumerate()
114                    .find(|(_, param)| param.keyword == k)
115                    .map(|(pos, param)| {
116                        if pos == index {
117                            index += 1;
118                        }
119
120                        param
121                    }),
122            }
123            .ok_or_else(|| FunctionCallError::UnknownKeyword {
124                keyword_span: argument.keyword_span().expect("exists"),
125                ident_span,
126                keywords: function.parameters().iter().map(|p| p.keyword).collect(),
127            })?;
128
129            // Check if the argument is of the expected type.
130            let argument_type_def = argument.expr().type_def(state_before_function_args);
131            let expr_kind = argument_type_def.kind();
132            let param_kind = parameter.kind();
133
134            if !param_kind.intersects(expr_kind) {
135                return Err(FunctionCallError::InvalidArgumentKind(
136                    InvalidArgumentErrorContext {
137                        function_ident: function.identifier(),
138                        abort_on_error,
139                        arguments_fmt: arguments
140                            .iter()
141                            .map(|arg| arg.inner().to_string())
142                            .collect::<Vec<_>>(),
143                        parameter: *parameter,
144                        got: expr_kind.clone(),
145                        argument,
146                        argument_span,
147                    },
148                ));
149            } else if param_kind.is_superset(expr_kind).is_err() {
150                arguments_with_unknown_type_validity.push((*parameter, node.clone()));
151            }
152
153            // Check if the argument is infallible.
154            if argument_type_def.is_fallible() {
155                return Err(FunctionCallError::FallibleArgument {
156                    expr_span: argument.span(),
157                });
158            }
159
160            list.insert(parameter.keyword, argument.into_inner());
161        }
162
163        // Check missing required arguments.
164        function
165            .parameters()
166            .iter()
167            .enumerate()
168            .filter(|(_, p)| p.required)
169            .filter(|(_, p)| !list.keywords().contains(&p.keyword))
170            .try_for_each(|(i, p)| -> Result<_, _> {
171                Err(FunctionCallError::MissingArgument {
172                    call_span,
173                    keyword: p.keyword,
174                    position: i,
175                })
176            })?;
177
178        // Check function closure validity.
179        let closure = Self::check_closure(
180            function.as_ref(),
181            closure_variables,
182            call_span,
183            &list,
184            state,
185            ident_span,
186        )?;
187
188        Ok(Self {
189            abort_on_error,
190            arguments_with_unknown_type_validity,
191            call_span,
192            ident_span,
193            function_id,
194            arguments: Arc::new(arguments),
195            closure,
196            list,
197            function: function.as_ref(),
198        })
199    }
200
201    #[allow(clippy::too_many_lines)]
202    fn check_closure(
203        function: &dyn Function,
204        closure_variables: Option<Node<Vec<Node<Ident>>>>,
205        call_span: Span,
206        list: &ArgumentList,
207        state: &mut TypeState,
208        ident_span: Span,
209    ) -> Result<Option<(Vec<Ident>, closure::Input)>, FunctionCallError> {
210        let closure = match (function.closure(), closure_variables) {
211            // Error if closure is provided for function that doesn't support
212            // any.
213            (None, Some(variables)) => {
214                let closure_span = variables.span();
215
216                return Err(FunctionCallError::UnexpectedClosure {
217                    call_span,
218                    closure_span,
219                });
220            }
221
222            // Error if closure is missing from function that expects one.
223            (Some(definition), None) => {
224                let example = definition.inputs.first().map(|input| input.example);
225
226                return Err(FunctionCallError::MissingClosure { call_span, example });
227            }
228
229            // Check for invalid closure signature.
230            (Some(definition), Some(variables)) => {
231                let mut matched = None;
232                let mut err_found_type_def = None;
233
234                for input in definition.inputs {
235                    // Check type definition for linked parameter.
236                    match list.arguments.get(input.parameter_keyword) {
237                        // No argument provided for the given parameter keyword.
238                        //
239                        // This means the closure can't act on the input
240                        // definition, so we continue on to the next. If no
241                        // input definitions are valid, the closure is invalid.
242                        None => (),
243
244                        // We've found the function argument over which the
245                        // closure is going to resolve. We need to ensure the
246                        // type of this argument is as expected by the closure.
247                        Some(expr) => {
248                            let type_def = expr.type_def(state);
249                            // The type definition of the value does not match
250                            // the expected closure type, continue to check if
251                            // the closure eventually accepts this definition.
252                            //
253                            // Keep track of the type information, so that we
254                            // can report these in a diagnostic error if no
255                            // other input definition matches.
256                            if input.kind.is_superset(type_def.kind()).is_err() {
257                                err_found_type_def = Some(type_def.kind().clone());
258                                continue;
259                            }
260
261                            matched = Some((input, expr));
262                            break;
263                        }
264                    }
265                }
266
267                // None of the inputs matched the value type, this is a user error.
268                match matched {
269                    None => {
270                        return Err(FunctionCallError::ClosureParameterTypeMismatch {
271                            call_span,
272                            found_kind: err_found_type_def.unwrap_or_else(Kind::any),
273                        });
274                    }
275
276                    Some((input, target)) => {
277                        // Now that we know we have a matching parameter argument with a valid type
278                        // definition, we can move on to checking/defining the closure arguments.
279                        //
280                        // In doing so we:
281                        //
282                        // - check the arity of the closure arguments
283                        // - set the expected type definition of each argument
284                        if input.variables.len() != variables.len() {
285                            let closure_arguments_span =
286                                variables.first().map_or(call_span, |node| {
287                                    (node.span().start(), variables.last().unwrap().span().end())
288                                        .into()
289                                });
290
291                            return Err(FunctionCallError::ClosureArityMismatch {
292                                ident_span,
293                                closure_arguments_span,
294                                expected: input.variables.len(),
295                                supplied: variables.len(),
296                            });
297                        }
298
299                        // Get the provided argument identifier in the same position as defined in the
300                        // input definition.
301                        //
302                        // That is, if the function closure definition expects:
303                        //
304                        //   [bytes, integer]
305                        //
306                        // Then, given for an actual implementation of:
307                        //
308                        //   foo() -> { |bar, baz| }
309                        //
310                        // We set "bar" (index 0) to return bytes, and "baz" (index 1) to return an
311                        // integer.
312                        for (index, input_var) in input.variables.clone().into_iter().enumerate() {
313                            let call_ident = &variables[index];
314                            let type_def = target.type_info(state).result;
315
316                            let (type_def, value) = match input_var.kind {
317                                // The variable kind is expected to be exactly
318                                // the kind provided by the closure definition.
319                                VariableKind::Exact(kind) => (kind.into(), None),
320
321                                // The variable kind is expected to be equal to
322                                // the ind of the target of the closure.
323                                VariableKind::Target => (
324                                    target.type_info(state).result,
325                                    target.resolve_constant(state),
326                                ),
327
328                                // The variable kind is expected to be equal to
329                                // the reduced kind of all values within the
330                                // target collection type.
331                                //
332                                // This assumes the target is a collection type,
333                                // or else it'll return "any".
334                                VariableKind::TargetInnerValue => {
335                                    let kind = if let Some(object) = type_def.as_object() {
336                                        object.reduced_kind()
337                                    } else if let Some(array) = type_def.as_array() {
338                                        array.reduced_kind()
339                                    } else {
340                                        Kind::any()
341                                    };
342
343                                    (kind.into(), None)
344                                }
345
346                                // The variable kind is expected to be equal to
347                                // the kind of all keys within the target
348                                // collection type.
349                                //
350                                // This means it's either a string for an
351                                // object, integer for an array, or
352                                // a combination of the two if the target isn't
353                                // known to be exactly one of the two.
354                                //
355                                // If the target can resolve to a non-collection
356                                // type, this again returns "any".
357                                VariableKind::TargetInnerKey => {
358                                    let mut kind = Kind::never();
359
360                                    if type_def.is_collection() {
361                                        if type_def.is_object() {
362                                            kind.add_bytes();
363                                        }
364                                        if type_def.is_array() {
365                                            kind.add_integer();
366                                        }
367                                    } else {
368                                        kind = Kind::any();
369                                    }
370
371                                    (kind.into(), None)
372                                }
373                            };
374
375                            let details = Details { type_def, value };
376
377                            state
378                                .local
379                                .insert_variable(call_ident.clone().into_inner(), details);
380                        }
381
382                        let variables = variables
383                            .into_inner()
384                            .into_iter()
385                            .map(Node::into_inner)
386                            .collect();
387
388                        Some((variables, input))
389                    }
390                }
391            }
392
393            _ => None,
394        };
395        Ok(closure)
396    }
397
398    pub(crate) fn compile(
399        mut self,
400        state_before_function_args: &TypeState,
401        state: &mut TypeState,
402        closure_block: Option<Node<(Block, TypeDef)>>,
403        local_snapshot: LocalEnv,
404        config: &mut CompileConfig,
405    ) -> Result<CallCompilationResult, FunctionCallError> {
406        let (closure, closure_fallible) =
407            self.compile_closure(closure_block, local_snapshot, state)?;
408
409        let call_span = self.call_span;
410        let ident_span = self.ident_span;
411
412        // We take the external context, and pass it to the function compile context, this allows
413        // functions mutable access to external state, but keeps the internal compiler state behind
414        // an immutable reference, to ensure compiler state correctness.
415        let temp_config = std::mem::take(config);
416
417        let mut compile_ctx = FunctionCompileContext::new(self.call_span, temp_config);
418
419        let expr = self
420            .function
421            .compile(
422                state_before_function_args,
423                &mut compile_ctx,
424                self.list.clone(),
425            )
426            .map_err(|error| FunctionCallError::Compilation { call_span, error })?;
427
428        // Re-insert the external context into the compiler state.
429        *config = compile_ctx.into_config();
430
431        // Asking for an infallible function to abort on error makes no sense.
432        // We consider this an error at compile-time, because it makes the
433        // resulting program incorrectly convey this function call might fail.
434        let mut warnings = Vec::new();
435        if self.abort_on_error
436            && self.arguments_with_unknown_type_validity.is_empty()
437            && !expr
438                .type_info(state_before_function_args)
439                .result
440                .is_fallible()
441        {
442            warnings.push(AbortInfallible {
443                ident_span,
444                abort_span: Span::new(ident_span.end(), ident_span.end() + 1),
445            });
446        }
447
448        // The function is expected to abort at boot-time if any error occurred,
449        // and one or more arguments are of an invalid type, so we'll return the
450        // appropriate error.
451        let mut invalid_argument_error = None;
452        if let Some((parameter, argument)) =
453            self.arguments_with_unknown_type_validity.first().cloned()
454            && !self.abort_on_error
455        {
456            invalid_argument_error = Some(FunctionCallError::InvalidArgumentKind(
457                InvalidArgumentErrorContext {
458                    function_ident: self.function.identifier(),
459                    abort_on_error: self.abort_on_error,
460                    arguments_fmt: self
461                        .arguments
462                        .iter()
463                        .map(|arg| arg.inner().to_string())
464                        .collect::<Vec<_>>(),
465                    parameter,
466                    got: argument
467                        .expr()
468                        .type_info(state_before_function_args)
469                        .result
470                        .into(),
471                    argument: argument.clone().into_inner(),
472                    argument_span: argument
473                        .keyword_span()
474                        .unwrap_or_else(|| argument.expr_span()),
475                },
476            ));
477        }
478
479        Ok(CallCompilationResult {
480            function_call: FunctionCall {
481                abort_on_error: self.abort_on_error,
482                expr,
483                arguments_with_unknown_type_validity: self.arguments_with_unknown_type_validity,
484                closure_fallible,
485                closure,
486                span: call_span,
487                ident: self.function.identifier(),
488                function_id: self.function_id,
489                arguments: self.arguments.clone(),
490                warnings,
491            },
492            error: invalid_argument_error,
493        })
494    }
495
496    fn compile_closure(
497        &mut self,
498        closure_block: Option<Node<(Block, TypeDef)>>,
499        mut locals: LocalEnv,
500        state: &mut TypeState,
501    ) -> Result<(Option<Closure>, bool), FunctionCallError> {
502        // Check if we have a closure we need to compile.
503        if let Some((variables, input)) = self.closure.clone() {
504            // TODO: This assumes the closure will run exactly once, which is incorrect.
505            // see: https://github.com/vectordotdev/vector/issues/13782
506
507            let block = closure_block.expect("closure must contain block");
508
509            // At this point, we've compiled the block, so we can remove the
510            // closure variables from the compiler's local environment.
511            for ident in &variables {
512                match locals.remove_variable(ident) {
513                    Some(details) => state.local.insert_variable(ident.clone(), details),
514                    None => {
515                        state.local.remove_variable(ident);
516                    }
517                }
518            }
519
520            let (block_span, (block, block_type_def)) = block.take();
521
522            let closure_fallible = block_type_def.is_fallible();
523
524            // Check the type definition of the resulting block.This needs to match
525            // whatever is configured by the closure input type.
526            let expected_kind = input.output.into_kind();
527            let found_kind = block_type_def
528                .kind()
529                .union(block_type_def.returns().clone());
530
531            if expected_kind.is_superset(&found_kind).is_err() {
532                return Err(FunctionCallError::ReturnTypeMismatch {
533                    block_span,
534                    found_kind,
535                    expected_kind,
536                });
537            }
538
539            let fnclosure = Closure::new(variables, block, block_type_def);
540            self.list.set_closure(fnclosure.clone());
541
542            // closure = Some(fnclosure);
543            Ok((Some(fnclosure), closure_fallible))
544        } else {
545            Ok((None, false))
546        }
547    }
548}
549
550#[derive(Clone)]
551pub struct FunctionCall {
552    abort_on_error: bool,
553    expr: Box<dyn Expression>,
554    arguments_with_unknown_type_validity: Vec<(Parameter, Node<FunctionArgument>)>,
555    closure_fallible: bool,
556    // will be used with: https://github.com/vectordotdev/vector/issues/13782
557    #[allow(dead_code)]
558    closure: Option<Closure>,
559
560    // used for enhancing runtime error messages (using abort-instruction).
561    //
562    // TODO: have span store line/col details to further improve this.
563    pub(crate) span: Span,
564
565    // used for equality check
566    pub(crate) ident: &'static str,
567
568    // May be used by the LLVM runtime. If not, it should be removed
569    #[allow(dead_code)]
570    function_id: usize,
571    arguments: Arc<Vec<Node<FunctionArgument>>>,
572
573    pub(crate) warnings: Vec<Warning>,
574}
575
576impl FunctionCall {
577    /// Takes the arguments passed and resolves them into the order they are defined
578    /// in the function
579    /// The error path in this function should never really be hit as the compiler should
580    /// catch these whilst creating the AST.
581    // May be used by the LLVM runtime. If not, it should be removed
582    #[allow(dead_code)]
583    fn resolve_arguments(
584        &self,
585        function: &dyn Function,
586    ) -> Result<Vec<(&'static str, Option<FunctionArgument>)>, String> {
587        let params = function.parameters().to_vec();
588        let mut result = params
589            .iter()
590            .map(|param| (param.keyword, None))
591            .collect::<Vec<_>>();
592
593        let mut unnamed = Vec::new();
594
595        // Position all the named parameters, keeping track of all the unnamed for later.
596        for param in self.arguments.iter() {
597            match param.keyword() {
598                None => unnamed.push(param.clone().take().1),
599                Some(keyword) => {
600                    match params.iter().position(|param| param.keyword == keyword) {
601                        None => {
602                            // The parameter was not found in the list.
603                            return Err(format!("parameter {keyword} not found."));
604                        }
605                        Some(pos) => {
606                            result[pos].1 = Some(param.clone().take().1);
607                        }
608                    }
609                }
610            }
611        }
612
613        // Position all the remaining unnamed parameters
614        let mut pos = 0;
615        for param in unnamed {
616            while result[pos].1.is_some() {
617                pos += 1;
618            }
619
620            if pos > result.len() {
621                return Err("Too many parameters".to_string());
622            }
623
624            result[pos].1 = Some(param);
625        }
626
627        Ok(result)
628    }
629
630    #[must_use]
631    pub fn arguments_fmt(&self) -> Vec<String> {
632        self.arguments
633            .iter()
634            .map(|arg| arg.inner().to_string())
635            .collect::<Vec<_>>()
636    }
637
638    #[must_use]
639    pub fn arguments_dbg(&self) -> Vec<String> {
640        self.arguments
641            .iter()
642            .map(|arg| format!("{:?}", arg.inner()))
643            .collect::<Vec<_>>()
644    }
645}
646
647impl Expression for FunctionCall {
648    fn resolve(&self, ctx: &mut Context) -> Resolved {
649        self.expr.resolve(ctx).map_err(|err| match err {
650            ExpressionError::Abort { .. }
651            | ExpressionError::Fallible { .. }
652            | ExpressionError::Missing { .. } => {
653                // propagate the error
654                err
655            }
656            ExpressionError::Return { span, .. } => ExpressionError::Error {
657                message: "return cannot be used inside closures".to_owned(),
658                labels: vec![Label::primary(
659                    "return cannot be used inside closures",
660                    span,
661                )],
662                notes: Vec::new(),
663            },
664            ExpressionError::Error {
665                message,
666                mut labels,
667                notes,
668            } => {
669                labels.push(Label::primary(message.clone(), self.span));
670
671                ExpressionError::Error {
672                    message: format!(
673                        r#"function call error for "{}" at ({}:{}): {}"#,
674                        self.ident,
675                        self.span.start(),
676                        self.span.end(),
677                        message
678                    ),
679                    labels,
680                    notes,
681                }
682            }
683        })
684    }
685
686    fn type_info(&self, state: &TypeState) -> TypeInfo {
687        let mut state = state.clone();
688
689        // TODO: functions with a closure do not correctly calculate type definitions
690        // see: https://github.com/vectordotdev/vector/issues/13782
691
692        // Evaluate arguments to correctly calculate any side-effects from them.
693        // This doesn't actually match current runtime behavior in some cases,
694        // but that will be changed.
695        // see: https://github.com/vectordotdev/vector/issues/13752
696        for arg_node in &*self.arguments {
697            let _result = arg_node.inner().expr().apply_type_info(&mut state);
698        }
699
700        let mut expr_result = self.expr.apply_type_info(&mut state);
701
702        // If one of the arguments only partially matches the function type
703        // definition, then we mark the entire function as fallible.
704        //
705        // This allows for progressive type-checking, by handling any potential
706        // type error the function throws, instead of having to enforce
707        // exact-type invariants for individual arguments.
708        //
709        // That is, this program triggers the `InvalidArgumentKind` error:
710        //
711        //     slice(10, 1)
712        //
713        // This is because `slice` expects either a string or an array, but it
714        // receives an integer. The concept of "progressive type checking" does
715        // not apply in this case, because this call can never succeed.
716        //
717        // However, given these example events:
718        //
719        //     { "foo": "bar" }
720        //     { "foo": 10.5 }
721        //
722        // If we were to run the same program, but against the `foo` field:
723        //
724        //     slice(.foo, 1)
725        //
726        // In this situation, progressive type checking _does_ make sense,
727        // because we can't know at compile-time what the eventual value of
728        // `.foo` will be. We mark `.foo` as "any", which includes the "array"
729        // and "string" types, so the program can now be made infallible by
730        // handling any potential type error the function returns:
731        //
732        //     slice(.foo, 1) ?? []
733        //
734        // Note that this rule doesn't just apply to "any" kind (in fact, "any"
735        // isn't a kind, it's simply a term meaning "all possible VRL values"),
736        // but it applies whenever there's an _intersection_ but not an exact
737        // _match_ between two types.
738        //
739        // Here's another example to demonstrate this:
740        //
741        //     { "foo": "foobar" }
742        //     { "foo": ["foo", "bar"] }
743        //     { "foo": 10.5 }
744        //
745        //     foo = slice(.foo, 1) ?? .foo
746        //     .foo = upcase(foo) ?? foo
747        //
748        // This would result in the following outcomes:
749        //
750        //     { "foo": "OOBAR" }
751        //     { "foo": ["bar", "baz"] }
752        //     { "foo": 10.5 }
753        //
754        // For the first event, both the `slice` and `upcase` functions succeed.
755        // For the second event, only the `slice` function succeeds.
756        // For the third event, both functions fail.
757        //
758
759        if !self.arguments_with_unknown_type_validity.is_empty() {
760            expr_result = expr_result.fallible();
761        }
762
763        // If the function has a closure attached, and that closure is fallible,
764        // then the function call itself becomes fallible.
765        //
766        // Given that `FunctionClosure` also implements `Expression`, and
767        // function implementations can access this closure, it is possible the
768        // function implementation already handles potential closure
769        // fallibility, but to be on the safe side, we ensure it is set properly
770        // here.
771        //
772        // Note that, since closures are tied to function calls, it is still
773        // possible to silence potential closure errors using the "abort on
774        // error" function-call feature (see below).
775        if self.closure_fallible {
776            expr_result = expr_result.fallible();
777        }
778
779        if self.abort_on_error {
780            expr_result = expr_result.infallible();
781        }
782
783        TypeInfo::new(state, expr_result)
784    }
785}
786
787impl fmt::Display for FunctionCall {
788    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
789        self.ident.fmt(f)?;
790        f.write_str("(")?;
791
792        let arguments = self.arguments_fmt();
793        let mut iter = arguments.iter().peekable();
794        while let Some(arg) = iter.next() {
795            f.write_str(arg)?;
796
797            if iter.peek().is_some() {
798                f.write_str(", ")?;
799            }
800        }
801
802        f.write_str(")")
803    }
804}
805
806impl fmt::Debug for FunctionCall {
807    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
808        f.write_str("FunctionCall(")?;
809        self.ident.fmt(f)?;
810
811        f.write_str("(")?;
812
813        let arguments = self.arguments_dbg();
814        let mut iter = arguments.iter().peekable();
815        while let Some(arg) = iter.next() {
816            f.write_str(arg)?;
817
818            if iter.peek().is_some() {
819                f.write_str(", ")?;
820            }
821        }
822
823        f.write_str("))")
824    }
825}
826
827impl PartialEq for FunctionCall {
828    fn eq(&self, other: &Self) -> bool {
829        self.ident == other.ident
830    }
831}
832
833// -----------------------------------------------------------------------------
834#[derive(Debug, Clone)]
835pub(crate) struct InvalidArgumentErrorContext {
836    pub(crate) function_ident: &'static str,
837    pub(crate) abort_on_error: bool,
838    pub(crate) arguments_fmt: Vec<String>,
839    pub(crate) parameter: Parameter,
840    pub(crate) got: Kind,
841    pub(crate) argument: FunctionArgument,
842    pub(crate) argument_span: Span,
843}
844
845#[derive(thiserror::Error, Debug, Clone)]
846pub(crate) enum Warning {
847    #[error("can't abort infallible function")]
848    AbortInfallible { ident_span: Span, abort_span: Span },
849}
850
851#[derive(thiserror::Error, Debug)]
852#[allow(clippy::large_enum_variant)]
853pub(crate) enum FunctionCallError {
854    #[error("call to undefined function")]
855    Undefined {
856        ident_span: Span,
857        ident: Ident,
858        idents: Vec<&'static str>,
859    },
860
861    #[error("wrong number of function arguments")]
862    WrongNumberOfArgs { arguments_span: Span, max: usize },
863
864    #[error("unknown function argument keyword")]
865    UnknownKeyword {
866        keyword_span: Span,
867        ident_span: Span,
868        keywords: Vec<&'static str>,
869    },
870
871    #[error("missing function argument")]
872    MissingArgument {
873        call_span: Span,
874        keyword: &'static str,
875        position: usize,
876    },
877
878    #[error("function compilation error: error[E{}] {}", error.code(), error)]
879    Compilation {
880        call_span: Span,
881        error: Box<dyn DiagnosticMessage>,
882    },
883
884    #[error("invalid argument type")]
885    InvalidArgumentKind(InvalidArgumentErrorContext),
886
887    #[error("fallible argument")]
888    FallibleArgument { expr_span: Span },
889
890    #[error("unexpected closure")]
891    UnexpectedClosure { call_span: Span, closure_span: Span },
892
893    #[error("missing closure")]
894    MissingClosure {
895        call_span: Span,
896        example: Option<Example>,
897    },
898
899    #[error("invalid closure arity")]
900    ClosureArityMismatch {
901        ident_span: Span,
902        closure_arguments_span: Span,
903        expected: usize,
904        supplied: usize,
905    },
906    #[error("type mismatch in closure parameter")]
907    ClosureParameterTypeMismatch { call_span: Span, found_kind: Kind },
908    #[error("type mismatch in closure return type")]
909    ReturnTypeMismatch {
910        block_span: Span,
911        found_kind: Kind,
912        expected_kind: Kind,
913    },
914}
915
916impl DiagnosticMessage for Warning {
917    fn code(&self) -> usize {
918        match self {
919            AbortInfallible { .. } => 620,
920        }
921    }
922
923    fn labels(&self) -> Vec<Label> {
924        match self {
925            AbortInfallible {
926                ident_span,
927                abort_span,
928            } => {
929                vec![
930                    Label::primary("this function can't fail", ident_span),
931                    Label::context("remove this abort (!) instruction", abort_span),
932                ]
933            }
934        }
935    }
936
937    fn notes(&self) -> Vec<Note> {
938        vec![SeeErrorDocs]
939    }
940
941    fn severity(&self) -> Severity {
942        Severity::Warning
943    }
944}
945
946impl DiagnosticMessage for FunctionCallError {
947    fn code(&self) -> usize {
948        use FunctionCallError::{
949            ClosureArityMismatch, ClosureParameterTypeMismatch, Compilation, FallibleArgument,
950            InvalidArgumentKind, MissingArgument, MissingClosure, ReturnTypeMismatch, Undefined,
951            UnexpectedClosure, UnknownKeyword, WrongNumberOfArgs,
952        };
953
954        match self {
955            Undefined { .. } => 105,
956            WrongNumberOfArgs { .. } => 106,
957            UnknownKeyword { .. } => 108,
958            Compilation { .. } => 610,
959            MissingArgument { .. } => 107,
960            InvalidArgumentKind { .. } => 110,
961            FallibleArgument { .. } => 630,
962            UnexpectedClosure { .. } => 109,
963            MissingClosure { .. } => 111,
964            ClosureArityMismatch { .. } => 120,
965            ClosureParameterTypeMismatch { .. } => 121,
966            ReturnTypeMismatch { .. } => 122,
967        }
968    }
969
970    #[allow(clippy::too_many_lines)]
971    fn labels(&self) -> Vec<Label> {
972        use FunctionCallError::{
973            ClosureArityMismatch, ClosureParameterTypeMismatch, Compilation, FallibleArgument,
974            InvalidArgumentKind, MissingArgument, MissingClosure, ReturnTypeMismatch, Undefined,
975            UnexpectedClosure, UnknownKeyword, WrongNumberOfArgs,
976        };
977
978        match self {
979            Undefined {
980                ident_span,
981                ident,
982                idents,
983            } => {
984                let mut vec = vec![Label::primary("undefined function", ident_span)];
985                let ident_chars = ident.as_ref().chars().collect::<Vec<_>>();
986
987                if let Some((idx, _)) = idents
988                    .iter()
989                    .map(|possible| {
990                        let possible_chars = possible.chars().collect::<Vec<_>>();
991                        levenstein::distance(&ident_chars, &possible_chars)
992                    })
993                    .enumerate()
994                    .min_by_key(|(_, score)| *score)
995                {
996                    {
997                        let guessed: &str = idents[idx];
998                        vec.push(Label::context(
999                            format!(r#"did you mean "{guessed}"?"#),
1000                            ident_span,
1001                        ));
1002                    }
1003                }
1004
1005                vec
1006            }
1007
1008            WrongNumberOfArgs {
1009                arguments_span,
1010                max,
1011            } => {
1012                let arg = if *max == 1 { "argument" } else { "arguments" };
1013
1014                vec![
1015                    Label::primary("too many function arguments", arguments_span),
1016                    Label::context(
1017                        format!("this function takes a maximum of {max} {arg}"),
1018                        arguments_span,
1019                    ),
1020                ]
1021            }
1022
1023            UnknownKeyword {
1024                keyword_span,
1025                ident_span,
1026                keywords,
1027            } => vec![
1028                Label::primary("unknown keyword", keyword_span),
1029                Label::context(
1030                    format!(
1031                        "this function accepts the following keywords: {}",
1032                        keywords
1033                            .iter()
1034                            .map(|k| format!(r#""{k}""#))
1035                            .collect::<Vec<_>>()
1036                            .join(", ")
1037                    ),
1038                    ident_span,
1039                ),
1040            ],
1041
1042            Compilation { call_span, error } => error
1043                .labels()
1044                .into_iter()
1045                .map(|mut label| {
1046                    label.span = *call_span;
1047                    label
1048                })
1049                .collect(),
1050
1051            MissingArgument {
1052                call_span,
1053                keyword,
1054                position,
1055            } => {
1056                vec![Label::primary(
1057                    format!(r#"required argument missing: "{keyword}" (position {position})"#),
1058                    call_span,
1059                )]
1060            }
1061
1062            InvalidArgumentKind(context) => {
1063                let keyword = context.parameter.keyword;
1064                let expected = context.parameter.kind();
1065                let expr_span = context.argument.span();
1066
1067                // TODO: extract this out into a helper
1068                let kind_str = |kind: &Kind| {
1069                    if kind.is_any() {
1070                        kind.to_string()
1071                    } else if kind.is_exact() {
1072                        format!("the exact type {kind}")
1073                    } else {
1074                        format!("one of {kind}")
1075                    }
1076                };
1077
1078                vec![
1079                    Label::primary(
1080                        format!("this expression resolves to {}", kind_str(&context.got)),
1081                        expr_span,
1082                    ),
1083                    Label::context(
1084                        format!(
1085                            r#"but the parameter "{}" expects {}"#,
1086                            keyword,
1087                            kind_str(&expected)
1088                        ),
1089                        context.argument_span,
1090                    ),
1091                ]
1092            }
1093
1094            FallibleArgument { expr_span } => vec![
1095                Label::primary("this expression can fail", expr_span),
1096                Label::context(
1097                    "handle the error before passing it in as an argument",
1098                    expr_span,
1099                ),
1100            ],
1101            UnexpectedClosure {
1102                call_span,
1103                closure_span,
1104            } => vec![
1105                Label::primary("unexpected closure", closure_span),
1106                Label::context("this function does not accept a closure", call_span),
1107            ],
1108            MissingClosure { call_span, .. } => {
1109                vec![Label::primary("this function expects a closure", call_span)]
1110            }
1111            ClosureArityMismatch {
1112                ident_span,
1113                closure_arguments_span,
1114                expected,
1115                supplied,
1116            } => vec![
1117                Label::primary(
1118                    format!("this function requires a closure with {expected} argument(s)"),
1119                    ident_span,
1120                ),
1121                Label::context(
1122                    format!("but {supplied} argument(s) are supplied"),
1123                    closure_arguments_span,
1124                ),
1125            ],
1126            ClosureParameterTypeMismatch {
1127                call_span,
1128                found_kind,
1129            } => vec![
1130                Label::primary(
1131                    "the closure tied to this function call expects a different input value",
1132                    call_span,
1133                ),
1134                Label::context(
1135                    format!(
1136                        "expression has an inferred type of {found_kind} where an array or object was expected"
1137                    ),
1138                    call_span,
1139                ),
1140            ],
1141            ReturnTypeMismatch {
1142                block_span,
1143                found_kind,
1144                expected_kind,
1145            } => vec![
1146                Label::primary("block returns invalid value type", block_span),
1147                Label::context(format!("expected: {expected_kind}"), block_span),
1148                Label::context(format!("received: {found_kind}"), block_span),
1149            ],
1150        }
1151    }
1152
1153    fn notes(&self) -> Vec<Note> {
1154        use FunctionCallError::{
1155            Compilation, FallibleArgument, InvalidArgumentKind, MissingClosure, WrongNumberOfArgs,
1156        };
1157
1158        match self {
1159            WrongNumberOfArgs { .. } => vec![Note::SeeDocs(
1160                "function arguments".to_owned(),
1161                Urls::expression_docs_url("#arguments"),
1162            )],
1163            FallibleArgument { .. } => vec![Note::SeeErrorDocs],
1164            InvalidArgumentKind(context) => {
1165                // TODO: move this into a generic helper function
1166                let kind = &context.parameter.kind();
1167                let argument = &context.argument;
1168                let guard = if kind.is_bytes() {
1169                    format!("string!({argument})")
1170                } else if kind.is_integer() {
1171                    format!("int!({argument})")
1172                } else if kind.is_float() {
1173                    format!("float!({argument})")
1174                } else if kind.is_boolean() {
1175                    format!("bool!({argument})")
1176                } else if kind.is_object() {
1177                    format!("object!({argument})")
1178                } else if kind.is_array() {
1179                    format!("array!({argument})")
1180                } else if kind.is_timestamp() {
1181                    format!("timestamp!({argument})")
1182                } else {
1183                    return vec![];
1184                };
1185
1186                let coerce = if kind.is_bytes() {
1187                    Some(format!(r#"to_string({argument}) ?? "default""#))
1188                } else if kind.is_integer() {
1189                    Some(format!("to_int({argument}) ?? 0"))
1190                } else if kind.is_float() {
1191                    Some(format!("to_float({argument}) ?? 0"))
1192                } else if kind.is_boolean() {
1193                    Some(format!("to_bool({argument}) ?? false"))
1194                } else if kind.is_timestamp() {
1195                    Some(format!("to_unix_timestamp({argument}) ?? now()"))
1196                } else {
1197                    None
1198                };
1199
1200                let args = {
1201                    let mut args = String::new();
1202                    let mut iter = context.arguments_fmt.iter().peekable();
1203                    while let Some(arg) = iter.next() {
1204                        args.push_str(arg);
1205                        if iter.peek().is_some() {
1206                            args.push_str(", ");
1207                        }
1208                    }
1209
1210                    args
1211                };
1212
1213                let abort = if context.abort_on_error { "!" } else { "" };
1214
1215                let mut notes = vec![];
1216
1217                let call = format!("{}{abort}({args})", context.function_ident);
1218
1219                notes.append(&mut Note::solution(
1220                    "ensuring an appropriate type at runtime",
1221                    vec![format!("{argument} = {guard}"), call.clone()],
1222                ));
1223
1224                if let Some(coerce) = coerce {
1225                    notes.append(&mut Note::solution(
1226                        "coercing to an appropriate type and specifying a default value as a fallback in case coercion fails",
1227                        vec![format!("{argument} = {coerce}"), call],
1228                    ));
1229                }
1230
1231                notes.push(Note::SeeErrorDocs);
1232
1233                notes
1234            }
1235
1236            Compilation { error, .. } => error.notes(),
1237
1238            MissingClosure { example, .. } if example.is_some() => {
1239                let code = example.unwrap().source.to_owned();
1240                vec![Note::Example(code)]
1241            }
1242
1243            _ => vec![],
1244        }
1245    }
1246}
1247
1248#[cfg(test)]
1249mod tests {
1250    use crate::compiler::{Category, FunctionExpression, value::kind};
1251
1252    use super::*;
1253
1254    #[derive(Clone, Debug)]
1255    struct Fn;
1256
1257    impl FunctionExpression for Fn {
1258        fn resolve(&self, _ctx: &mut Context) -> Resolved {
1259            todo!()
1260        }
1261
1262        fn type_def(&self, _state: &TypeState) -> TypeDef {
1263            TypeDef::null().infallible()
1264        }
1265    }
1266
1267    #[derive(Debug)]
1268    struct TestFn;
1269
1270    impl Function for TestFn {
1271        fn identifier(&self) -> &'static str {
1272            "test"
1273        }
1274
1275        fn usage(&self) -> &'static str {
1276            "Test function"
1277        }
1278
1279        fn category(&self) -> &'static str {
1280            Category::Debug.as_ref()
1281        }
1282
1283        fn return_kind(&self) -> u16 {
1284            kind::NULL
1285        }
1286
1287        fn examples(&self) -> &'static [crate::compiler::function::Example] {
1288            &[]
1289        }
1290
1291        fn parameters(&self) -> &'static [Parameter] {
1292            const PARAMETERS: &[Parameter] = &[
1293                Parameter::optional("one", kind::INTEGER, "one"),
1294                Parameter::optional("two", kind::INTEGER, "two"),
1295                Parameter::optional("three", kind::INTEGER, "three"),
1296            ];
1297
1298            PARAMETERS
1299        }
1300
1301        fn compile(
1302            &self,
1303            _state: &TypeState,
1304            _ctx: &mut FunctionCompileContext,
1305            _arguments: ArgumentList,
1306        ) -> crate::compiler::function::Compiled {
1307            Ok(Fn.as_expr())
1308        }
1309    }
1310
1311    fn create_node<T>(inner: T) -> Node<T> {
1312        Node::new(Span::new(0, 0), inner)
1313    }
1314
1315    fn create_argument(ident: Option<&str>, value: i64) -> FunctionArgument {
1316        use crate::compiler::expression::{Expr, Literal};
1317
1318        FunctionArgument::new(
1319            ident.map(|ident| create_node(Ident::new(ident))),
1320            create_node(Expr::Literal(Literal::Integer(value))),
1321        )
1322    }
1323
1324    fn create_function_call(arguments: Vec<Node<FunctionArgument>>) -> FunctionCall {
1325        let mut state = TypeState::default();
1326        let original_state = state.clone();
1327        let mut config = CompileConfig::default();
1328        Builder::new(
1329            Span::new(0, 0),
1330            Node::new(Span::new(0, 0), Ident::new("test")),
1331            false,
1332            arguments,
1333            &[Box::new(TestFn) as _],
1334            &original_state,
1335            &mut state,
1336            None,
1337        )
1338        .unwrap()
1339        .compile(
1340            &original_state,
1341            &mut state,
1342            None,
1343            LocalEnv::default(),
1344            &mut config,
1345        )
1346        .unwrap()
1347        .function_call
1348    }
1349
1350    #[test]
1351    fn resolve_arguments_simple() {
1352        let call = create_function_call(vec![
1353            create_node(create_argument(None, 1)),
1354            create_node(create_argument(None, 2)),
1355            create_node(create_argument(None, 3)),
1356        ]);
1357
1358        let params = call.resolve_arguments(&TestFn);
1359        let expected: Vec<(&'static str, Option<FunctionArgument>)> = vec![
1360            ("one", Some(create_argument(None, 1))),
1361            ("two", Some(create_argument(None, 2))),
1362            ("three", Some(create_argument(None, 3))),
1363        ];
1364
1365        assert_eq!(Ok(expected), params);
1366    }
1367
1368    #[test]
1369    fn resolve_arguments_named() {
1370        let call = create_function_call(vec![
1371            create_node(create_argument(Some("one"), 1)),
1372            create_node(create_argument(Some("two"), 2)),
1373            create_node(create_argument(Some("three"), 3)),
1374        ]);
1375
1376        let params = call.resolve_arguments(&TestFn);
1377        let expected: Vec<(&'static str, Option<FunctionArgument>)> = vec![
1378            ("one", Some(create_argument(Some("one"), 1))),
1379            ("two", Some(create_argument(Some("two"), 2))),
1380            ("three", Some(create_argument(Some("three"), 3))),
1381        ];
1382
1383        assert_eq!(Ok(expected), params);
1384    }
1385
1386    #[test]
1387    fn resolve_arguments_named_unordered() {
1388        let call = create_function_call(vec![
1389            create_node(create_argument(Some("three"), 3)),
1390            create_node(create_argument(Some("two"), 2)),
1391            create_node(create_argument(Some("one"), 1)),
1392        ]);
1393
1394        let params = call.resolve_arguments(&TestFn);
1395        let expected: Vec<(&'static str, Option<FunctionArgument>)> = vec![
1396            ("one", Some(create_argument(Some("one"), 1))),
1397            ("two", Some(create_argument(Some("two"), 2))),
1398            ("three", Some(create_argument(Some("three"), 3))),
1399        ];
1400
1401        assert_eq!(Ok(expected), params);
1402    }
1403
1404    #[test]
1405    fn resolve_arguments_unnamed_unordered_one() {
1406        let call = create_function_call(vec![
1407            create_node(create_argument(Some("three"), 3)),
1408            create_node(create_argument(None, 2)),
1409            create_node(create_argument(Some("one"), 1)),
1410        ]);
1411
1412        let params = call.resolve_arguments(&TestFn);
1413        let expected: Vec<(&'static str, Option<FunctionArgument>)> = vec![
1414            ("one", Some(create_argument(Some("one"), 1))),
1415            ("two", Some(create_argument(None, 2))),
1416            ("three", Some(create_argument(Some("three"), 3))),
1417        ];
1418
1419        assert_eq!(Ok(expected), params);
1420    }
1421
1422    #[test]
1423    fn resolve_arguments_unnamed_unordered_two() {
1424        let call = create_function_call(vec![
1425            create_node(create_argument(Some("three"), 3)),
1426            create_node(create_argument(None, 1)),
1427            create_node(create_argument(None, 2)),
1428        ]);
1429
1430        let params = call.resolve_arguments(&TestFn);
1431        let expected: Vec<(&'static str, Option<FunctionArgument>)> = vec![
1432            ("one", Some(create_argument(None, 1))),
1433            ("two", Some(create_argument(None, 2))),
1434            ("three", Some(create_argument(Some("three"), 3))),
1435        ];
1436
1437        assert_eq!(Ok(expected), params);
1438    }
1439}