forc_migrate/matching/
mod.rs

1#![allow(dead_code)]
2//! This module contains common API for matching elements
3//! within a lexed or a typed tree.
4//!
5//! A typical migration will search for certain elements in the
6//! lexed or typed tree and modify them within the lexed tree.
7//!
8//! In the long term we want to have advanced infrastructure for both
9//! matching and modifying parts of the trees, as discussed in
10//! https://github.com/FuelLabs/sway/issues/6836.
11//!
12//! Currently, we will start (very) small, by providing reusable
13//! module functions for matching parts of the trees.
14//!
15//! For concrete examples, see the match functions and trait impls
16//! implemented in the sub-modules.
17//!
18//! ## Design decisions
19//!
20//! The goal was pragmatic. To create a simple to develop and extend API that
21//! will offer easy discoverability of provided functions and methods, all in
22//! order to move cumbersome and error-prone matching code out of the migration
23//! logic.
24//!
25//! Note that, although similar to static analysis tools like, e.g. Rust's
26//! [Clippy](https://doc.rust-lang.org/clippy/), `forc migrate` is significantly
27//! different. Instead of providing hundreds of independent lints that
28//! automatically check for localized issues, migrations provide only a handful
29//! of migration steps, that are orchestrated within a single migration process,
30//! some of them possibly being interactive.
31//!
32//! Each migration step, in general, wants to take a look at a larger scope at a time,
33//! often a module. This makes a typical approach, of using fine-grain visitor functions
34//! less applicable. Also, the goal is to empower non-compiler developers to write
35//! migrations.
36//!
37//! All this led to the design in which a single migration step is in focus, and can:
38//! - search for elements of interest using the match functions,
39//! - build new and modify existing lexed elements using the [super::modifying],
40//!
41//! Migrations will use match functions to either search directly
42//! within a parent or recursively (deep) within a scope. Match functions can
43//! accept predicates to filter the searched elements. The predicates deliberately
44//! accept `&&TElement` or `&&mut TElement` so that can be easily passed to
45//! [Iterator::filter] function.
46//!
47//! For the cases when migrations do target individual expressions, and do not need
48//! to inspect a larger scope, the visitor pattern is still supported and available
49//! via the tree visitors that are defined in [super::visiting].
50//!
51//! ## Matching elements in trees
52//!
53//! Functions matching on lexed trees are coming in two variants, immutable and mutable.
54//! They differ in the mutability of their arguments and returned types, but
55//! otherwise implement the same matching logic.
56//!
57//! Matching can be done either directly within a parent, or recursively
58//! within a scope. E.g., we can match for `StorageField`s that are
59//! directly under the `storage` declaration, or for all `StorageField`s
60//! that are in the `storage` declaration, in any of the namespaces,
61//! recursively.
62//!
63//! Searching for elements "in-between", e.g., `StorageField`s in a particular
64//! sub-namespace, is currently not supported, and must be done manually
65//! within a migration.
66//!
67//! Matching is done on lexical or typed elements like, e.g., `StorageField`,
68//! or `TyStorageField`, without any more convenient abstraction provided for
69//! matching. This is also a simple beginning. A better matching framework
70//! would expose a stable higher level abstraction for matching and modifying.
71//!
72//! ## Locating equivalent elements across trees
73//!
74//! Often we will find an element in the lexed tree, e.g., a `StorageField` in
75//! order to change it, but will need additional information from its typed tree
76//! counterpart, `TyStorageField`, or vice versa. The [TyLocate] trait offers
77//! the [TyLocate::locate] method for finding a typed equivalent of a lexed
78//! element. The [LexedLocate] and [LexedLocateMut] do the opposite.
79//!
80//! Locating an equivalent will in most of the cases be implemented via equality
81//! of spans. Locating can also cause multiple traversals of the same part of
82//! a tree. For migrations, this will not cause a performance problem.
83
84mod lexed_tree;
85mod typed_tree;
86
87use sway_ast::attribute::{Annotated, Attribute, AttributeArg};
88use sway_ast::{AttributeDecl, ItemFn, PathType};
89pub(crate) use typed_tree::matchers as ty_match;
90pub(crate) use typed_tree::predicates::*;
91
92pub(crate) use lexed_tree::matchers as lexed_match;
93pub(crate) use lexed_tree::matchers_mut as lexed_match_mut;
94pub(crate) use lexed_tree::predicates::*;
95
96/// Matches for typed tree elements of type `T` located **directly** within
97/// the typed tree element `self`.
98///
99/// The matched elements must satisfy the `predicate`.
100pub(crate) trait TyElementsMatcher<T> {
101    fn match_elems<'a, P>(&'a self, predicate: P) -> impl Iterator<Item = &'a T>
102    where
103        P: Fn(&&'a T) -> bool + Clone + 'a,
104        T: 'a;
105}
106
107/// Matches for typed tree elements of type `T` located **recursively** within
108/// the typed tree element `self` or any of its children. The meaning of a
109/// "child" depends on the exact tree element `self`.
110///
111/// The matched elements must satisfy the `predicate`.
112pub(crate) trait TyElementsMatcherDeep<T> {
113    fn match_elems_deep<'a, F>(&'a self, predicate: F) -> Vec<&'a T>
114    where
115        F: Fn(&&'a T) -> bool + Clone + 'a,
116        T: 'a;
117}
118
119/// Within a typed tree element `self`, locates and returns the element of type `Ty`,
120/// that is the typed equivalent of the `lexed_element`.
121pub(crate) trait TyLocate<Lexed, Ty> {
122    fn locate(&self, lexed_element: &Lexed) -> Option<&Ty>;
123}
124
125/// Matches for lexed tree elements of type `T` located **directly** within
126/// the lexed tree element `self`.
127///
128/// The matched elements must satisfy the `predicate`.
129pub(crate) trait LexedElementsMatcherMut<T> {
130    fn match_elems_mut<'a, F>(&'a mut self, predicate: F) -> impl Iterator<Item = &'a mut T>
131    where
132        F: Fn(&&'a mut T) -> bool + Clone + 'a,
133        T: 'a;
134}
135
136pub(crate) trait LexedElementsMatcher<T> {
137    fn match_elems<'a, F>(&'a self, predicate: F) -> impl Iterator<Item = &'a T>
138    where
139        F: Fn(&&'a T) -> bool + Clone + 'a,
140        T: 'a;
141}
142
143/// Matches for lexed tree elements of type `T` located **recursively** within
144/// the lexed tree element `self` or any of its children. The meaning of a
145/// "child" depends on the exact tree element `self`.
146///
147/// The matched elements must satisfy the `predicate`.
148pub(crate) trait LexedElementsMatcherDeepMut<T> {
149    fn match_elems_deep_mut<'a, F>(&'a mut self, predicate: F) -> Vec<&'a mut T>
150    where
151        F: Fn(&&'a mut T) -> bool + Clone + 'a,
152        T: 'a;
153}
154
155pub(crate) trait LexedElementsMatcherDeep<T> {
156    fn match_elems_deep<'a, F>(&'a self, predicate: F) -> Vec<&'a T>
157    where
158        F: Fn(&&'a T) -> bool + Clone + 'a,
159        T: 'a;
160}
161
162/// Within a lexed tree element `self`, locates and returns the element of type `Lexed`,
163/// that is the lexed equivalent of the `ty_element`.
164pub(crate) trait LexedLocateMut<Ty, Lexed> {
165    fn locate_mut(&mut self, ty_element: &Ty) -> Option<&mut Lexed>;
166}
167
168/// Within a lexed tree element `self`, locates and returns the element of type `Lexed`,
169/// that is the lexed equivalent of the `ty_element`.
170pub(crate) trait LexedLocate<Ty, Lexed> {
171    fn locate(&self, ty_element: &Ty) -> Option<&Lexed>;
172}
173
174/// Within a lexed tree element `self`, locates and returns the element of type `Lexed`,
175/// that is the lexed equivalent of the `ty_element`, together with its annotations.
176pub(crate) trait LexedLocateAnnotatedMut<Ty, Lexed> {
177    fn locate_annotated_mut<'a>(
178        &'a mut self,
179        ty_element: &Ty,
180    ) -> Option<(&'a mut Vec<AttributeDecl>, &'a mut Lexed)>;
181}
182
183/// Within a lexed tree element `self`, locates and returns the element of type `Lexed`,
184/// that is the lexed equivalent of the `ty_element`, together with its annotations.
185pub(crate) trait LexedLocateAnnotated<Ty, Lexed> {
186    fn locate_annotated<'a>(
187        &'a self,
188        ty_element: &Ty,
189    ) -> Option<(&'a Vec<AttributeDecl>, &'a Lexed)>;
190}
191
192/// Within an annotated lexed tree element `self`, locates and returns the element of type `LexedAnnotated`,
193/// that is the annotated lexed equivalent of the `ty_element`.
194pub(crate) trait LexedLocateAsAnnotatedMut<Ty, LexedAnnotated> {
195    fn locate_as_annotated_mut(
196        &mut self,
197        ty_element: &Ty,
198    ) -> Option<&mut Annotated<LexedAnnotated>>;
199}
200
201/// Within an annotated lexed tree element `self`, locates and returns the element of type `LexedAnnotated`,
202/// that is the annotated lexed equivalent of the `ty_element`.
203pub(crate) trait LexedLocateAsAnnotated<Ty, LexedAnnotated> {
204    fn locate_as_annotated(&self, ty_element: &Ty) -> Option<&Annotated<LexedAnnotated>>;
205}
206
207impl<T, Ty, Lexed> LexedLocateMut<Ty, Lexed> for T
208where
209    T: LexedLocateAnnotatedMut<Ty, Lexed>,
210{
211    fn locate_mut(&mut self, ty_element: &Ty) -> Option<&mut Lexed> {
212        self.locate_annotated_mut(ty_element)
213            .map(|annotated| annotated.1)
214    }
215}
216
217impl<T, Ty, Lexed> LexedLocate<Ty, Lexed> for T
218where
219    T: LexedLocateAnnotated<Ty, Lexed>,
220{
221    fn locate(&self, ty_element: &Ty) -> Option<&Lexed> {
222        self.locate_annotated(ty_element)
223            .map(|annotated| annotated.1)
224    }
225}
226
227/// A predicate that returns true for any immutable input.
228pub(crate) fn any<T>(_t: &&T) -> bool {
229    true
230}
231
232/// A predicate that returns true for any mutable input.
233pub(crate) fn any_mut<T>(_t: &&mut T) -> bool {
234    true
235}
236
237/// Returns a predicate that evaluates to true if all the predicates passed
238/// as arguments evaluate to true.
239#[macro_export]
240macro_rules! all_of {
241    ($($i:expr),+) => {
242       $crate::matching::all_of([$($i, )*].as_slice())
243    };
244}
245
246/// Returns a predicate that evaluates to true if all the `predicates`
247/// evaluate to true.
248///
249/// Not intended to be used directly. Use [all_of!] macro instead.
250#[allow(dead_code)]
251pub(crate) fn all_of<T, P>(predicates: &[P]) -> impl Fn(&&T) -> bool + Clone + '_
252where
253    P: Fn(&&T) -> bool + Clone,
254{
255    move |t: &&T| {
256        let mut res = true;
257        for predicate in predicates {
258            res &= predicate(t);
259        }
260        res
261    }
262}
263
264/// Returns a predicate that evaluates to true if all the predicates passed
265/// as arguments evaluate to true.
266#[macro_export]
267macro_rules! all_of_mut {
268    ($($i:expr),+) => {
269       $crate::matching::all_of_mut([$($i, )*].as_slice())
270    };
271}
272
273/// Returns a predicate that evaluates to true if all the `predicates`
274/// evaluate to true.
275///
276/// Not intended to be used directly. Use [all_of_mut!] macro instead.
277#[allow(dead_code)]
278pub(crate) fn all_of_mut<T, P>(predicates: &[P]) -> impl Fn(&&mut T) -> bool + Clone + '_
279where
280    P: Fn(&&mut T) -> bool + Clone,
281{
282    move |t: &&mut T| {
283        let mut res = true;
284        for predicate in predicates {
285            res &= predicate(t);
286        }
287        res
288    }
289}
290
291/// Returns a predicate that evaluates to true if any of the predicates passed
292/// as arguments evaluate to true.
293#[macro_export]
294macro_rules! any_of {
295    ($($i:expr),+) => {
296       $crate::matching::any_of([$($i, )*].as_slice())
297    };
298}
299
300/// Returns a predicate that evaluates to true if any of the `predicates`
301/// evaluate to true.
302///
303/// Not intended to be used directly. Use [any_of!] macro instead.
304#[allow(dead_code)]
305pub(crate) fn any_of<T, P>(predicates: &[P]) -> impl Fn(&&T) -> bool + Clone + '_
306where
307    P: Fn(&&T) -> bool + Clone,
308{
309    move |t: &&T| {
310        let mut res = false;
311        for predicate in predicates {
312            res |= predicate(t);
313        }
314        res
315    }
316}
317
318/// Returns a predicate that evaluates to true if any of the predicates passed
319/// as arguments evaluate to true.
320#[macro_export]
321macro_rules! any_of_mut {
322    ($($i:expr),+) => {
323       $crate::matching::any_of_mut([$($i, )*].as_slice())
324    };
325}
326
327/// Returns a predicate that evaluates to true if any of the `predicates`
328/// evaluate to true.
329///
330/// Not intended to be used directly. Use [any_of_mut!] macro instead.
331#[allow(dead_code)]
332pub(crate) fn any_of_mut<T, P>(predicates: &[P]) -> impl Fn(&&mut T) -> bool + Clone + '_
333where
334    P: Fn(&&mut T) -> bool + Clone,
335{
336    move |t: &&mut T| {
337        let mut res = false;
338        for predicate in predicates {
339            res |= predicate(t);
340        }
341        res
342    }
343}
344
345/// Trait for inspecting if a tree element has the expected name.
346pub(crate) trait WithName {
347    /// Returns true if `Self` has the name `name`.
348    fn with_name<N: AsRef<str> + ?Sized>(&self, name: &N) -> bool;
349}
350
351/// Returns a predicate that evaluates to true if a [WithName]
352/// implementor has the name equal to `name`.
353pub(crate) fn with_name<T, N>(name: &N) -> impl Fn(&&T) -> bool + Clone + '_
354where
355    T: WithName,
356    N: AsRef<str> + ?Sized,
357{
358    move |t: &&T| t.with_name(name)
359}
360
361/// Returns a predicate that evaluates to true if a [WithName]
362/// implementor has the name equal to `name`.
363pub(crate) fn with_name_mut<T, N>(name: &N) -> impl Fn(&&mut T) -> bool + Clone + '_
364where
365    T: WithName,
366    N: AsRef<str> + ?Sized,
367{
368    move |t: &&mut T| t.with_name(name)
369}
370
371impl WithName for Attribute {
372    fn with_name<N: AsRef<str> + ?Sized>(&self, name: &N) -> bool {
373        self.name.as_str() == name.as_ref()
374    }
375}
376
377impl WithName for AttributeArg {
378    fn with_name<N: AsRef<str> + ?Sized>(&self, name: &N) -> bool {
379        self.name.as_str() == name.as_ref()
380    }
381}
382
383impl WithName for PathType {
384    fn with_name<N: AsRef<str> + ?Sized>(&self, name: &N) -> bool {
385        self.last_segment().name.as_str() == name.as_ref()
386    }
387}
388
389impl WithName for ItemFn {
390    fn with_name<N: AsRef<str> + ?Sized>(&self, name: &N) -> bool {
391        self.fn_signature.name.as_str() == name.as_ref()
392    }
393}