nifty_check/checker/
args.rs1use nifty_syntax::Span;
4use nifty_syntax::ast::{Arg, Expr};
5
6use super::Checker;
7use crate::elements::Param;
8
9pub(super) struct Args<'a> {
10 callee: String,
11 declared: Option<&'static [Param]>,
14 positional: Vec<&'a Expr>,
15 positional_taken: usize,
16 named: Vec<(&'a str, &'a Expr, Span, bool)>,
17}
18
19impl<'a> Args<'a> {
20 pub(super) fn new(checker: &mut Checker, callee: String, args: &'a [Arg]) -> Self {
22 let mut positional = Vec::new();
23 let mut named: Vec<(&str, &Expr, Span, bool)> = Vec::new();
24 for arg in args {
25 match &arg.label {
26 None => positional.push(&arg.value),
27 Some(label) => {
28 if named.iter().any(|(name, ..)| *name == label.name) {
29 checker.error(format!("`{}` is given twice", label.name), label.span);
30 } else {
31 named.push((&label.name, &arg.value, label.span, false));
32 }
33 }
34 }
35 }
36 Self {
37 callee,
38 declared: None,
39 positional,
40 positional_taken: 0,
41 named,
42 }
43 }
44
45 pub(super) fn built_in(
47 checker: &mut Checker,
48 name: &str,
49 params: &'static [Param],
50 args: &'a [Arg],
51 ) -> Self {
52 Self {
53 declared: Some(params),
54 ..Self::new(checker, format!("`{name}`"), args)
55 }
56 }
57
58 pub(super) fn positional(&mut self) -> Option<&'a Expr> {
60 let arg = self.positional.get(self.positional_taken).copied();
61 if arg.is_some() {
62 self.positional_taken += 1;
63 }
64 arg
65 }
66
67 pub(super) fn named(&mut self, name: &str) -> Option<&'a Expr> {
68 debug_assert!(
69 self.declared
70 .is_none_or(|params| params.iter().any(|param| param.name == name)),
71 "{} reads `{name}`, which its table entry doesn't declare",
72 self.callee
73 );
74 let entry = self.named.iter_mut().find(|(label, ..)| *label == name)?;
75 entry.3 = true;
76 Some(entry.1)
77 }
78
79 pub(super) fn param(&mut self, name: &str) -> Option<&'a Expr> {
81 self.positional().or_else(|| self.named(name))
82 }
83
84 pub(super) fn finish(self, checker: &mut Checker) {
86 for extra in &self.positional[self.positional_taken..] {
87 checker.error(
88 format!("{} doesn't take this many arguments", self.callee),
89 extra.span,
90 );
91 }
92 for (name, _, span, used) in &self.named {
93 if !used {
94 checker.error(
95 format!("{} has no argument named `{name}`", self.callee),
96 *span,
97 );
98 }
99 }
100 }
101}