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