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}