cairo_lang_semantic/items/
enm.rs

1use std::sync::Arc;
2
3use cairo_lang_defs::ids::{
4    EnumId, LanguageElementId, LookupItemId, ModuleItemId, VariantId, VariantLongId,
5};
6use cairo_lang_diagnostics::{Diagnostics, Maybe, ToMaybe};
7use cairo_lang_proc_macros::{DebugWithDb, SemanticObject};
8use cairo_lang_syntax::attribute::structured::{Attribute, AttributeListStructurize};
9use cairo_lang_syntax::node::{Terminal, TypedStablePtr, TypedSyntaxNode, ast};
10use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
11use cairo_lang_utils::{Intern, LookupIntern, Upcast};
12use smol_str::SmolStr;
13
14use super::attribute::SemanticQueryAttrs;
15use super::feature_kind::extract_item_feature_config;
16use super::generics::{GenericParamsData, semantic_generic_params};
17use crate::corelib::unit_ty;
18use crate::db::SemanticGroup;
19use crate::diagnostic::SemanticDiagnosticKind::*;
20use crate::diagnostic::{SemanticDiagnostics, SemanticDiagnosticsBuilder};
21use crate::expr::inference::InferenceId;
22use crate::expr::inference::canonic::ResultNoErrEx;
23use crate::resolve::{Resolver, ResolverData};
24use crate::substitution::{GenericSubstitution, SemanticRewriter};
25use crate::types::{add_type_based_diagnostics, resolve_type};
26use crate::{ConcreteEnumId, SemanticDiagnostic, semantic};
27
28#[cfg(test)]
29#[path = "enm_test.rs"]
30mod test;
31
32// Declaration
33#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb)]
34#[debug_db(dyn SemanticGroup + 'static)]
35pub struct EnumDeclarationData {
36    diagnostics: Diagnostics<SemanticDiagnostic>,
37    generic_params: Vec<semantic::GenericParam>,
38    attributes: Vec<Attribute>,
39    resolver_data: Arc<ResolverData>,
40}
41
42/// Query implementation of [crate::db::SemanticGroup::priv_enum_declaration_data].
43pub fn priv_enum_declaration_data(
44    db: &dyn SemanticGroup,
45    enum_id: EnumId,
46) -> Maybe<EnumDeclarationData> {
47    let mut diagnostics = SemanticDiagnostics::default();
48    // TODO(spapini): when code changes in a file, all the AST items change (as they contain a path
49    // to the green root that changes. Once ASTs are rooted on items, use a selector that picks only
50    // the item instead of all the module data.
51    let enum_ast = db.module_enum_by_id(enum_id)?.to_maybe()?;
52
53    // Generic params.
54    let generic_params_data = db.enum_generic_params_data(enum_id)?;
55    let inference_id =
56        InferenceId::LookupItemDeclaration(LookupItemId::ModuleItem(ModuleItemId::Enum(enum_id)));
57    let mut resolver = Resolver::with_data(
58        db,
59        (*generic_params_data.resolver_data).clone_with_inference_id(db, inference_id),
60    );
61    diagnostics.extend(generic_params_data.diagnostics);
62    let generic_params = generic_params_data.generic_params;
63    let attributes = enum_ast.attributes(db).structurize(db);
64
65    // Check fully resolved.
66    let inference = &mut resolver.inference();
67    inference.finalize(&mut diagnostics, enum_ast.stable_ptr(db).untyped());
68
69    let generic_params = inference.rewrite(generic_params).no_err();
70
71    let resolver_data = Arc::new(resolver.data);
72    Ok(EnumDeclarationData {
73        diagnostics: diagnostics.build(),
74        generic_params,
75        attributes,
76        resolver_data,
77    })
78}
79
80/// Query implementation of [crate::db::SemanticGroup::enum_declaration_diagnostics].
81pub fn enum_declaration_diagnostics(
82    db: &dyn SemanticGroup,
83    enum_id: EnumId,
84) -> Diagnostics<SemanticDiagnostic> {
85    db.priv_enum_declaration_data(enum_id).map(|data| data.diagnostics).unwrap_or_default()
86}
87
88/// Query implementation of [crate::db::SemanticGroup::enum_generic_params].
89pub fn enum_generic_params(
90    db: &dyn SemanticGroup,
91    enum_id: EnumId,
92) -> Maybe<Vec<semantic::GenericParam>> {
93    Ok(db.enum_generic_params_data(enum_id)?.generic_params)
94}
95
96/// Query implementation of [crate::db::SemanticGroup::enum_generic_params_data].
97pub fn enum_generic_params_data(
98    db: &dyn SemanticGroup,
99    enum_id: EnumId,
100) -> Maybe<GenericParamsData> {
101    let module_file_id = enum_id.module_file_id(db);
102    let mut diagnostics = SemanticDiagnostics::default();
103    let enum_ast = db.module_enum_by_id(enum_id)?.to_maybe()?;
104
105    // Generic params.
106    let inference_id =
107        InferenceId::LookupItemGenerics(LookupItemId::ModuleItem(ModuleItemId::Enum(enum_id)));
108    let mut resolver = Resolver::new(db, module_file_id, inference_id);
109    resolver.set_feature_config(&enum_id, &enum_ast, &mut diagnostics);
110    let generic_params = semantic_generic_params(
111        db,
112        &mut diagnostics,
113        &mut resolver,
114        module_file_id,
115        &enum_ast.generic_params(db),
116    );
117    let inference = &mut resolver.inference();
118    inference.finalize(&mut diagnostics, enum_ast.stable_ptr(db).untyped());
119
120    let generic_params = inference.rewrite(generic_params).no_err();
121    let resolver_data = Arc::new(resolver.data);
122    Ok(GenericParamsData { generic_params, diagnostics: diagnostics.build(), resolver_data })
123}
124
125/// Query implementation of [crate::db::SemanticGroup::enum_attributes].
126pub fn enum_attributes(db: &dyn SemanticGroup, enum_id: EnumId) -> Maybe<Vec<Attribute>> {
127    Ok(db.priv_enum_declaration_data(enum_id)?.attributes)
128}
129
130/// Query implementation of [crate::db::SemanticGroup::enum_declaration_resolver_data].
131pub fn enum_declaration_resolver_data(
132    db: &dyn SemanticGroup,
133    enum_id: EnumId,
134) -> Maybe<Arc<ResolverData>> {
135    Ok(db.priv_enum_declaration_data(enum_id)?.resolver_data)
136}
137
138// Definition
139#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb)]
140#[debug_db(dyn SemanticGroup + 'static)]
141pub struct EnumDefinitionData {
142    diagnostics: Diagnostics<SemanticDiagnostic>,
143    variants: OrderedHashMap<SmolStr, VariantId>,
144    variant_semantic: OrderedHashMap<VariantId, Variant>,
145    resolver_data: Arc<ResolverData>,
146}
147
148#[derive(Clone, Debug, Hash, PartialEq, Eq, DebugWithDb)]
149#[debug_db(dyn SemanticGroup + 'static)]
150pub struct Variant {
151    pub enum_id: EnumId,
152    pub id: VariantId,
153    pub ty: semantic::TypeId,
154    /// The index of the variant from within the variant list.
155    pub idx: usize,
156}
157
158#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, SemanticObject)]
159pub struct ConcreteVariant {
160    pub concrete_enum_id: ConcreteEnumId,
161    pub id: VariantId,
162    pub ty: semantic::TypeId,
163    /// The index of the variant from within the variant list.
164    #[dont_rewrite]
165    pub idx: usize,
166}
167
168/// Selector pattern of a match arm of a match on numeric values.
169/// Required for the dont_rewrite attribute to work.
170#[derive(Clone, Debug, Hash, PartialEq, Eq, DebugWithDb, SemanticObject)]
171#[debug_db(dyn SemanticGroup + 'static)]
172pub struct ValueSelectorArm {
173    #[dont_rewrite]
174    pub value: usize,
175}
176
177/// Selector pattern of a match arm.
178#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject)]
179pub enum MatchArmSelector {
180    VariantId(ConcreteVariant),
181    Value(ValueSelectorArm),
182}
183
184/// Query implementation of [crate::db::SemanticGroup::priv_enum_definition_data].
185pub fn priv_enum_definition_data(
186    db: &dyn SemanticGroup,
187    enum_id: EnumId,
188) -> Maybe<EnumDefinitionData> {
189    let module_file_id = enum_id.module_file_id(db);
190    let crate_id = module_file_id.0.owning_crate(db);
191    let mut diagnostics = SemanticDiagnostics::default();
192    // TODO(spapini): when code changes in a file, all the AST items change (as they contain a path
193    // to the green root that changes. Once ASTs are rooted on items, use a selector that picks only
194    // the item instead of all the module data.
195    let enum_ast = db.module_enum_by_id(enum_id)?.to_maybe()?;
196
197    // Generic params.
198    let generic_params_data = db.enum_generic_params_data(enum_id)?;
199    let inference_id =
200        InferenceId::LookupItemDefinition(LookupItemId::ModuleItem(ModuleItemId::Enum(enum_id)));
201    let mut resolver = Resolver::with_data(
202        db,
203        (*generic_params_data.resolver_data).clone_with_inference_id(db, inference_id),
204    );
205    diagnostics.extend(generic_params_data.diagnostics);
206
207    // Variants.
208    let mut variants = OrderedHashMap::default();
209    let mut variant_semantic = OrderedHashMap::default();
210    for (variant_idx, variant) in enum_ast.variants(db).elements(db).enumerate() {
211        let feature_restore = resolver
212            .data
213            .feature_config
214            .override_with(extract_item_feature_config(db, crate_id, &variant, &mut diagnostics));
215        let id = VariantLongId(module_file_id, variant.stable_ptr(db)).intern(db);
216        let ty = match variant.type_clause(db) {
217            ast::OptionTypeClause::Empty(_) => unit_ty(db),
218            ast::OptionTypeClause::TypeClause(type_clause) => {
219                resolve_type(db, &mut diagnostics, &mut resolver, &type_clause.ty(db))
220            }
221        };
222        let variant_name = variant.name(db).text(db);
223        if let Some(_other_variant) = variants.insert(variant_name.clone(), id) {
224            diagnostics
225                .report(variant.stable_ptr(db), EnumVariantRedefinition { enum_id, variant_name });
226        }
227        variant_semantic.insert(id, Variant { enum_id, id, ty, idx: variant_idx });
228        resolver.data.feature_config.restore(feature_restore);
229    }
230
231    // Check fully resolved.
232    let inference = &mut resolver.inference();
233    inference.finalize(&mut diagnostics, enum_ast.stable_ptr(db).untyped());
234
235    for (_, variant) in variant_semantic.iter_mut() {
236        variant.ty = inference.rewrite(variant.ty).no_err();
237    }
238
239    let resolver_data = Arc::new(resolver.data);
240    Ok(EnumDefinitionData {
241        diagnostics: diagnostics.build(),
242        variants,
243        variant_semantic,
244        resolver_data,
245    })
246}
247
248/// Query implementation of [crate::db::SemanticGroup::enum_definition_diagnostics].
249pub fn enum_definition_diagnostics(
250    db: &dyn SemanticGroup,
251    enum_id: EnumId,
252) -> Diagnostics<SemanticDiagnostic> {
253    let Ok(data) = db.priv_enum_definition_data(enum_id) else {
254        return Default::default();
255    };
256
257    let crate_id = data.resolver_data.module_file_id.0.owning_crate(db);
258
259    // If the enum is a phantom type, no need to check if its variants are fully valid types, as
260    // they won't be used.
261    if db
262        .declared_phantom_type_attributes(crate_id)
263        .iter()
264        .any(|attr| enum_id.has_attr(db, attr).unwrap_or_default())
265    {
266        return data.diagnostics;
267    }
268    let mut diagnostics = SemanticDiagnostics::from(data.diagnostics);
269    for (_, variant) in data.variant_semantic.iter() {
270        let stable_ptr = variant.id.stable_ptr(db);
271        add_type_based_diagnostics(db, &mut diagnostics, variant.ty, stable_ptr);
272        if variant.ty.is_phantom(db) {
273            diagnostics.report(stable_ptr, NonPhantomTypeContainingPhantomType);
274        }
275    }
276    diagnostics.build()
277}
278
279/// Query implementation of [crate::db::SemanticGroup::enum_definition_resolver_data].
280pub fn enum_definition_resolver_data(
281    db: &dyn SemanticGroup,
282    enum_id: EnumId,
283) -> Maybe<Arc<ResolverData>> {
284    Ok(db.priv_enum_definition_data(enum_id)?.resolver_data)
285}
286
287/// Query implementation of [crate::db::SemanticGroup::enum_variants].
288pub fn enum_variants(
289    db: &dyn SemanticGroup,
290    enum_id: EnumId,
291) -> Maybe<OrderedHashMap<SmolStr, VariantId>> {
292    Ok(db.priv_enum_definition_data(enum_id)?.variants)
293}
294
295/// Query implementation of [crate::db::SemanticGroup::variant_semantic].
296pub fn variant_semantic(
297    db: &dyn SemanticGroup,
298    enum_id: EnumId,
299    variant_id: VariantId,
300) -> Maybe<Variant> {
301    let data = db.priv_enum_definition_data(enum_id)?;
302    data.variant_semantic.get(&variant_id).cloned().to_maybe()
303}
304
305// TODO(spapini): Consider making these queries.
306pub trait SemanticEnumEx<'a>: Upcast<dyn SemanticGroup + 'a> {
307    /// Retrieves the [ConcreteVariant] for a [ConcreteEnumId] and a [Variant].
308    fn concrete_enum_variant(
309        &self,
310        concrete_enum_id: ConcreteEnumId,
311        variant: &Variant,
312    ) -> Maybe<ConcreteVariant> {
313        // TODO(spapini): Uphold the invariant that constructed ConcreteEnumId instances
314        //   always have the correct number of generic arguments.
315        let db = self.upcast();
316        let generic_params = db.enum_generic_params(concrete_enum_id.enum_id(db))?;
317        let generic_args = concrete_enum_id.lookup_intern(db).generic_args;
318        GenericSubstitution::new(&generic_params, &generic_args).substitute(
319            db,
320            ConcreteVariant { concrete_enum_id, id: variant.id, ty: variant.ty, idx: variant.idx },
321        )
322    }
323
324    /// Retrieves all the [ConcreteVariant]s for a [ConcreteEnumId].
325    fn concrete_enum_variants(
326        &self,
327        concrete_enum_id: ConcreteEnumId,
328    ) -> Maybe<Vec<ConcreteVariant>> {
329        // TODO(spapini): Uphold the invariant that constructed ConcreteEnumId instances
330        //   always have the correct number of generic arguments.
331        let db = self.upcast();
332        let enum_id = concrete_enum_id.enum_id(db);
333        db.enum_variants(enum_id)?
334            .values()
335            .map(|variant_id| {
336                db.concrete_enum_variant(
337                    concrete_enum_id,
338                    &db.variant_semantic(enum_id, *variant_id)?,
339                )
340            })
341            .collect()
342    }
343}
344
345impl<'a, T: Upcast<dyn SemanticGroup + 'a> + ?Sized> SemanticEnumEx<'a> for T {}