cairo_lint/
corelib.rs

1use cairo_lang_defs::ids::EnumId;
2use cairo_lang_defs::{
3    db::DefsGroup,
4    ids::{
5        ExternFunctionId, FreeFunctionId, ImplDefId, ImplItemId, LookupItemId, ModuleId,
6        ModuleItemId, SubmoduleId, TopLevelLanguageElementId, TraitFunctionId, TraitItemId,
7    },
8};
9use cairo_lang_filesystem::ids::CrateId;
10use cairo_lang_semantic::items::imp::ImplSemantic;
11use cairo_lang_semantic::items::trt::TraitSemantic;
12use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
13use salsa::{Database, Update};
14
15pub const BOOL_PARTIAL_EQ_PATH: &str = "core::BoolPartialEq";
16pub const PANIC_PATH: &str = "core::panics::panic";
17pub const PANIC_WITH_BYTE_ARRAY_PATH: &str = "core::panics::panic_with_byte_array";
18pub const T_COPY_CLONE_PATH: &str = "core::clone::TCopyClone";
19pub const PARTIAL_ORD_LE_PATH: &str = "core::traits::PartialOrd::le";
20pub const PARTIAL_ORD_GE_PATH: &str = "core::traits::PartialOrd::ge";
21pub const ADD_TRAIT_FUNCTION_PATH: &str = "core::traits::Add::add";
22pub const SUB_TRAIT_FUNCTION_PATH: &str = "core::traits::Sub::sub";
23pub const INTEGER_MODULE_PATH: &str = "core::integer";
24pub const INTO_TRAIT_FUNCTION_PATH: &str = "core::traits::Into::into";
25pub const TRY_INTO_TRAIT_FUNCTION_PATH: &str = "core::traits::TryInto::try_into";
26pub const OPTION_TYPE_PATH: &str = "core::option::Option";
27
28static CORELIB_ITEM_PATHS: [&str; 12] = [
29    BOOL_PARTIAL_EQ_PATH,
30    PANIC_PATH,
31    PANIC_WITH_BYTE_ARRAY_PATH,
32    T_COPY_CLONE_PATH,
33    PARTIAL_ORD_LE_PATH,
34    PARTIAL_ORD_GE_PATH,
35    ADD_TRAIT_FUNCTION_PATH,
36    SUB_TRAIT_FUNCTION_PATH,
37    INTEGER_MODULE_PATH,
38    OPTION_TYPE_PATH,
39    INTO_TRAIT_FUNCTION_PATH,
40    TRY_INTO_TRAIT_FUNCTION_PATH,
41];
42
43#[derive(PartialEq, Eq, Hash, Debug, Clone, Update)]
44pub struct CorelibContext<'db> {
45    corelib_items: OrderedHashMap<String, Option<LookupItemId<'db>>>,
46}
47
48impl<'db> CorelibContext<'db> {
49    pub(crate) fn new(db: &'db dyn Database) -> Self {
50        let core_crate_id = CrateId::core(db);
51        let modules = db.crate_modules(core_crate_id);
52        Self {
53            corelib_items: CORELIB_ITEM_PATHS
54                .iter()
55                .map(|path| {
56                    for module in modules.iter() {
57                        let item_id = find_item_with_path(db, *module, path);
58                        if item_id.is_some() {
59                            return (path.to_string(), item_id);
60                        }
61                    }
62
63                    (path.to_string(), None)
64                })
65                .collect(),
66        }
67    }
68
69    // TODO (https://github.com/software-mansion/cairo-lint/issues/398): Write a macro for these getters to avoid boilerplate.
70    pub fn get_bool_partial_eq_impl_id(&self) -> ImplDefId<'db> {
71        let item = self
72            .corelib_items
73            .get(BOOL_PARTIAL_EQ_PATH)
74            .expect("Expected BoolPartialEq to be present in corelib items")
75            .expect("Expected BoolPartialEq to be defined in the corelib");
76        match item {
77            LookupItemId::ModuleItem(ModuleItemId::Impl(id)) => id,
78            _ => unreachable!("Expected BoolPartialEq to be an ImplDefId"),
79        }
80    }
81
82    pub fn get_panic_function_id(&self) -> ExternFunctionId<'db> {
83        let item = self
84            .corelib_items
85            .get(PANIC_PATH)
86            .expect("Expected panic to be present in corelib items")
87            .expect("Expected panic to be defined in the corelib");
88        match item {
89            LookupItemId::ModuleItem(ModuleItemId::ExternFunction(id)) => id,
90            _ => unreachable!("Expected panic to be a ExternFunction"),
91        }
92    }
93
94    pub fn get_panic_with_byte_array_function_id(&self) -> FreeFunctionId<'db> {
95        let item = self
96            .corelib_items
97            .get(PANIC_WITH_BYTE_ARRAY_PATH)
98            .expect("Expected panic_with_byte_array to be present in corelib items")
99            .expect("Expected panic_with_byte_array to be defined in the corelib");
100        match item {
101            LookupItemId::ModuleItem(ModuleItemId::FreeFunction(id)) => id,
102            _ => unreachable!("Expected panic_with_byte_array to be a FreeFunction"),
103        }
104    }
105
106    pub fn get_t_copy_clone_impl_id(&self) -> ImplDefId<'db> {
107        let item = self
108            .corelib_items
109            .get(T_COPY_CLONE_PATH)
110            .expect("Expected TCopyClone to be present in corelib items")
111            .expect("Expected TCopyClone to be defined in the corelib");
112        match item {
113            LookupItemId::ModuleItem(ModuleItemId::Impl(id)) => id,
114            _ => unreachable!("Expected TCopyClone to be an ImplDefId"),
115        }
116    }
117
118    pub fn get_partial_ord_le_trait_function_id(&self) -> TraitFunctionId<'db> {
119        let item = self
120            .corelib_items
121            .get(PARTIAL_ORD_LE_PATH)
122            .expect("Expected PartialOrd::le to be present in corelib items")
123            .expect("Expected PartialOrd::le to be defined in the corelib");
124        match item {
125            LookupItemId::TraitItem(TraitItemId::Function(id)) => id,
126            _ => unreachable!("Expected PartialOrd::le to be a TraitFunctionId"),
127        }
128    }
129
130    pub fn get_partial_ord_ge_trait_function_id(&self) -> TraitFunctionId<'db> {
131        let item = self
132            .corelib_items
133            .get(PARTIAL_ORD_GE_PATH)
134            .expect("Expected PartialOrd::ge to be present in corelib items")
135            .expect("Expected PartialOrd::ge to be defined in the corelib");
136        match item {
137            LookupItemId::TraitItem(TraitItemId::Function(id)) => id,
138            _ => unreachable!("Expected PartialOrd::ge to be a TraitFunctionId"),
139        }
140    }
141
142    pub fn get_add_trait_function_id(&self) -> TraitFunctionId<'db> {
143        let item = self
144            .corelib_items
145            .get(ADD_TRAIT_FUNCTION_PATH)
146            .expect("Expected Add::add to be present in corelib items")
147            .expect("Expected Add::add to be defined in the corelib");
148        match item {
149            LookupItemId::TraitItem(TraitItemId::Function(id)) => id,
150            _ => unreachable!("Expected Add::add to be a TraitFunctionId"),
151        }
152    }
153
154    pub fn get_sub_trait_function_id(&self) -> TraitFunctionId<'db> {
155        let item = self
156            .corelib_items
157            .get(SUB_TRAIT_FUNCTION_PATH)
158            .expect("Expected Sub::sub to be present in corelib items")
159            .expect("Expected Sub::sub to be defined in the corelib");
160        match item {
161            LookupItemId::TraitItem(TraitItemId::Function(id)) => id,
162            _ => unreachable!("Expected Sub::sub to be a TraitFunctionId"),
163        }
164    }
165
166    pub fn get_integer_module_id(&self) -> SubmoduleId<'db> {
167        let item = self
168            .corelib_items
169            .get(INTEGER_MODULE_PATH)
170            .expect("Expected integer module to be present in corelib items")
171            .expect("Expected integer module to be defined in the corelib");
172        match item {
173            LookupItemId::ModuleItem(ModuleItemId::Submodule(id)) => id,
174            _ => unreachable!("Expected integer module to be a Submodule"),
175        }
176    }
177
178    pub fn get_into_trait_function_id(&self) -> TraitFunctionId<'db> {
179        let item = self
180            .corelib_items
181            .get(INTO_TRAIT_FUNCTION_PATH)
182            .expect("Expected Into::into to be present in corelib items")
183            .expect("Expected Into::into to be defined in the corelib");
184        match item {
185            LookupItemId::TraitItem(TraitItemId::Function(id)) => id,
186            _ => unreachable!("Expected Into::into to be a TraitFunctionId"),
187        }
188    }
189
190    pub fn get_try_into_trait_function_id(&self) -> TraitFunctionId<'db> {
191        let item = self
192            .corelib_items
193            .get(TRY_INTO_TRAIT_FUNCTION_PATH)
194            .expect("Expected TryInto::try_into to be present in corelib items")
195            .expect("Expected TryInto::try_into to be defined in the corelib");
196        match item {
197            LookupItemId::TraitItem(TraitItemId::Function(id)) => id,
198            _ => unreachable!("Expected TryInto::try_into to be a TraitFunctionId"),
199        }
200    }
201    pub fn get_option_enum_id(&self) -> EnumId<'db> {
202        let item = self
203            .corelib_items
204            .get(OPTION_TYPE_PATH)
205            .expect("Expected Option to be present in corelib items")
206            .expect("Expected Option to be defined in the corelib");
207        match item {
208            LookupItemId::ModuleItem(ModuleItemId::Enum(id)) => id,
209            _ => unreachable!("Expected Option to be a EnumId"),
210        }
211    }
212}
213
214fn find_item_with_path<'db>(
215    db: &'db dyn Database,
216    module_id: ModuleId<'db>,
217    path: &str,
218) -> Option<LookupItemId<'db>> {
219    let items = module_id.module_data(db).ok()?.items(db);
220    for item in items.iter() {
221        if item.full_path(db) == path {
222            return Some(LookupItemId::ModuleItem(*item));
223        }
224        match item {
225            ModuleItemId::Submodule(submodule_id) => {
226                let submodule_item =
227                    find_item_with_path(db, ModuleId::Submodule(*submodule_id), path);
228                if submodule_item.is_some() {
229                    return submodule_item;
230                }
231            }
232            ModuleItemId::Impl(impl_id) => {
233                if let Ok(functions) = db.impl_functions(*impl_id) {
234                    for (_, impl_fn_id) in functions.iter() {
235                        if impl_fn_id.full_path(db) == path {
236                            return Some(LookupItemId::ImplItem(ImplItemId::Function(*impl_fn_id)));
237                        }
238                    }
239                }
240
241                if let Ok(types) = db.impl_types(*impl_id) {
242                    for (impl_type_id, _) in types.iter() {
243                        if impl_type_id.full_path(db) == path {
244                            return Some(LookupItemId::ImplItem(ImplItemId::Type(*impl_type_id)));
245                        }
246                    }
247                }
248
249                if let Ok(consts) = db.impl_constants(*impl_id) {
250                    for (impl_const_id, _) in consts.iter() {
251                        if impl_const_id.full_path(db) == path {
252                            return Some(LookupItemId::ImplItem(ImplItemId::Constant(
253                                *impl_const_id,
254                            )));
255                        }
256                    }
257                }
258            }
259            ModuleItemId::Trait(trait_id) => {
260                if let Ok(functions) = db.trait_functions(*trait_id) {
261                    for (_, trait_fn_id) in functions.iter() {
262                        if trait_fn_id.full_path(db) == path {
263                            return Some(LookupItemId::TraitItem(TraitItemId::Function(
264                                *trait_fn_id,
265                            )));
266                        }
267                    }
268                }
269
270                if let Ok(types) = db.trait_types(*trait_id) {
271                    for (_, trait_type_id) in types.iter() {
272                        if trait_type_id.full_path(db) == path {
273                            return Some(LookupItemId::TraitItem(TraitItemId::Type(
274                                *trait_type_id,
275                            )));
276                        }
277                    }
278                }
279
280                if let Ok(consts) = db.trait_constants(*trait_id) {
281                    for (_, trait_const_id) in consts.iter() {
282                        if trait_const_id.full_path(db) == path {
283                            return Some(LookupItemId::TraitItem(TraitItemId::Constant(
284                                *trait_const_id,
285                            )));
286                        }
287                    }
288                }
289            }
290            // The check for the item path happens before all the matches.
291            _ => (),
292        }
293    }
294    None
295}