ink_ir/ir/
idents_lint.rs

1// Copyright (C) Use Ink (UK) Ltd.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15/// Returns `Ok` if there are no occurrences of identifiers starting with `__ink_`.
16///
17/// # Errors
18///
19/// Returns a combined error for every instance of `__ink_` prefixed identifier found.
20pub fn ensure_no_ink_identifiers<T>(checked: &T) -> Result<(), syn::Error>
21where
22    T: VisitBy,
23{
24    let mut visitor = private::IdentVisitor::default();
25    checked.visit_by(&mut visitor);
26    visitor.into_result()
27}
28
29/// Makes sure to call the correct visitor function on the given visitor.
30pub trait VisitBy: private::Sealed {
31    fn visit_by(&self, visitor: &mut private::IdentVisitor);
32}
33
34mod private {
35    use super::VisitBy;
36    use proc_macro2::Ident;
37
38    /// Seals the implementation of `VisitBy`.
39    pub trait Sealed {}
40    impl Sealed for syn::ItemMod {}
41    impl Sealed for syn::ItemTrait {}
42    impl Sealed for syn::ItemFn {}
43
44    impl VisitBy for syn::ItemMod {
45        fn visit_by(&self, visitor: &mut IdentVisitor) {
46            syn::visit::visit_item_mod(visitor, self);
47        }
48    }
49
50    impl VisitBy for syn::ItemTrait {
51        fn visit_by(&self, visitor: &mut IdentVisitor) {
52            syn::visit::visit_item_trait(visitor, self);
53        }
54    }
55
56    impl VisitBy for syn::ItemFn {
57        fn visit_by(&self, visitor: &mut IdentVisitor) {
58            syn::visit::visit_item_fn(visitor, self);
59        }
60    }
61
62    /// Visitor to ensure that there are no identifiers starting with `__ink_` as prefix.
63    ///
64    /// # Errors
65    ///
66    /// If there are identifiers starting with `__ink_` as prefix in the input.
67    /// Will yield one combined error for all found encounters.
68    #[derive(Default)]
69    pub struct IdentVisitor {
70        errors: Vec<syn::Error>,
71    }
72
73    impl IdentVisitor {
74        /// Converts the visitor into the errors it found if any.
75        ///
76        /// Returns `Ok` if it found no errors during visitation.
77        pub fn into_result(self) -> Result<(), syn::Error> {
78            match self.errors.split_first() {
79                None => Ok(()),
80                Some((first, rest)) => {
81                    let mut combined = first.clone();
82                    for error in rest {
83                        combined.combine(error.clone());
84                    }
85                    Err(combined)
86                }
87            }
88        }
89    }
90
91    impl<'ast> syn::visit::Visit<'ast> for IdentVisitor {
92        fn visit_ident(&mut self, ident: &'ast Ident) {
93            if ident.to_string().starts_with("__ink_") {
94                self.errors.push(format_err!(
95                    ident,
96                    "encountered invalid identifier starting with __ink_",
97                ))
98            }
99        }
100    }
101}