nickel_lang_core/term/pattern/
bindings.rs

1//! Pattern analysis.
2
3use crate::{
4    identifier::LocIdent,
5    term::{pattern::*, record::Field},
6};
7
8pub trait Bindings {
9    /// Returns a list of all variables bound by this pattern, together with the path to the field
10    /// they match and the associated extra annotations.
11    ///
12    /// # Example
13    ///
14    /// For a pattern `{a = x @ {foo, bar | Number = z, ..rest}, d = e}`, the result of this function
15    /// contains:
16    ///
17    /// - `(["a"], "x", empty field)` for the `x` variable
18    /// - `(["a"], "rest", empty field)` for the `rest` variable
19    /// - `(["a", "foo"], "foo", empty field)` for the `foo` variable
20    /// - `(["a", "bar"], "z", field with Number contract)` for the `z` variable
21    /// - `(["d"], "e", empty field)` for the `e` variable
22    fn bindings(&self) -> Vec<(Vec<LocIdent>, LocIdent, Field)>;
23}
24
25trait InjectBindings {
26    /// Same as [Bindings::bindings], but work relative to a current path inside a pattern and
27    /// injects the bindings into a working vector instead of returning the result. This method is
28    /// mostly used internally and is the one performing the actual work.
29    ///
30    /// Other modules of the LSP should use [Bindings::bindings] directly.
31    ///
32    /// # Parameters
33    ///
34    /// - `bindings`: the vector to inject the bindings into.
35    /// - `path`: the field path to the sub-pattern being analysed.
36    /// - `parent_extra`: the extra annotations associated with a potential parent field pattern.
37    ///   For example, when injecting the bindings of `{foo ? 5 = x @ y @ z}`, all the introduced
38    ///   variables should refer to default annotation of `foo`. This annotation is thus passed
39    ///   along when calling to the sub-patterns' [Self::inject_bindings].
40    fn inject_bindings(
41        &self,
42        bindings: &mut Vec<(Vec<LocIdent>, LocIdent, Field)>,
43        path: Vec<LocIdent>,
44        parent_extra: Option<&Field>,
45    );
46}
47
48impl Bindings for Pattern {
49    fn bindings(&self) -> Vec<(Vec<LocIdent>, LocIdent, Field)> {
50        let mut bindings = Vec::new();
51        self.inject_bindings(&mut bindings, Vec::new(), None);
52        bindings
53    }
54}
55
56impl InjectBindings for Pattern {
57    fn inject_bindings(
58        &self,
59        bindings: &mut Vec<(Vec<LocIdent>, LocIdent, Field)>,
60        path: Vec<LocIdent>,
61        parent_deco: Option<&Field>,
62    ) {
63        if let Some(alias) = self.alias {
64            bindings.push((
65                path.clone(),
66                alias,
67                parent_deco.cloned().unwrap_or_default(),
68            ));
69        }
70
71        self.data.inject_bindings(bindings, path, parent_deco);
72    }
73}
74
75impl InjectBindings for PatternData {
76    fn inject_bindings(
77        &self,
78        bindings: &mut Vec<(Vec<LocIdent>, LocIdent, Field)>,
79        path: Vec<LocIdent>,
80        parent_deco: Option<&Field>,
81    ) {
82        match self {
83            PatternData::Any(id) => {
84                bindings.push((path, *id, parent_deco.cloned().unwrap_or_default()))
85            }
86            PatternData::Record(record_pat) => {
87                record_pat.inject_bindings(bindings, path, parent_deco)
88            }
89            PatternData::Array(array_pat) => array_pat.inject_bindings(bindings, path, parent_deco),
90            PatternData::Enum(evariant_pat) => {
91                evariant_pat.inject_bindings(bindings, path, parent_deco)
92            }
93            PatternData::Or(or_pat) => or_pat.inject_bindings(bindings, path, parent_deco),
94            // Wildcard and constant patterns don't bind any variable
95            PatternData::Wildcard | PatternData::Constant(_) => (),
96        }
97    }
98}
99
100impl InjectBindings for RecordPattern {
101    fn inject_bindings(
102        &self,
103        bindings: &mut Vec<(Vec<LocIdent>, LocIdent, Field)>,
104        path: Vec<LocIdent>,
105        parent_extra: Option<&Field>,
106    ) {
107        for field_pat in self.patterns.iter() {
108            // Field patterns have their own annotation, so there's no need to propagate
109            // `parent_extra` any further
110            field_pat.inject_bindings(bindings, path.clone(), None);
111        }
112
113        if let TailPattern::Capture(rest) = self.tail {
114            // If a contract is attached to the whole record pattern in the enclosing pattern, the
115            // rest doesn't exactly match this contract: there are some fields missing. Still, it
116            // sounds more useful to keep the whole metadata - including documentation - for
117            // autocompletion and the like, even if it's an over-approximation.
118            bindings.push((path, rest, parent_extra.cloned().unwrap_or_default()));
119        }
120    }
121}
122
123impl InjectBindings for ArrayPattern {
124    fn inject_bindings(
125        &self,
126        bindings: &mut Vec<(Vec<LocIdent>, LocIdent, Field)>,
127        path: Vec<LocIdent>,
128        _parent_extra: Option<&Field>,
129    ) {
130        for subpat in self.patterns.iter() {
131            // Array elements shouldn't inherit the annotation from their parent (for once, they
132            // are of a different type), so we reset `parent_extra` to `None`.
133            subpat.inject_bindings(bindings, path.clone(), None);
134        }
135
136        if let TailPattern::Capture(rest) = self.tail {
137            bindings.push((path, rest, Default::default()));
138        }
139    }
140}
141
142impl InjectBindings for FieldPattern {
143    fn inject_bindings(
144        &self,
145        bindings: &mut Vec<(Vec<LocIdent>, LocIdent, Field)>,
146        mut path: Vec<LocIdent>,
147        _parent_extra: Option<&Field>,
148    ) {
149        path.push(self.matched_id);
150        self.pattern
151            .inject_bindings(bindings, path, Some(&Field::from(self.annotation.clone())));
152    }
153}
154
155impl InjectBindings for EnumPattern {
156    fn inject_bindings(
157        &self,
158        bindings: &mut Vec<(Vec<LocIdent>, LocIdent, Field)>,
159        path: Vec<LocIdent>,
160        _parent_extra: Option<&Field>,
161    ) {
162        //TODO: I'm not sure we should just transparently forward to the variant's argument. Maybe
163        //we need a more complex notion of path here, that knows when we enter an enum variant?
164        if let Some(ref arg_pat) = self.pattern {
165            arg_pat.inject_bindings(bindings, path, None);
166        }
167    }
168}
169
170impl InjectBindings for OrPattern {
171    fn inject_bindings(
172        &self,
173        bindings: &mut Vec<(Vec<LocIdent>, LocIdent, Field)>,
174        path: Vec<LocIdent>,
175        parent_extra: Option<&Field>,
176    ) {
177        for subpat in self.patterns.iter() {
178            subpat.inject_bindings(bindings, path.clone(), parent_extra);
179        }
180    }
181}