sway_core/semantic_analysis/
module.rs

1use std::{
2    collections::{HashMap, HashSet},
3    fmt::Display,
4    fs,
5    sync::Arc,
6};
7
8use graph_cycles::Cycles;
9use indexmap::IndexMap;
10use itertools::Itertools;
11use sway_error::{
12    error::CompileError,
13    handler::{ErrorEmitted, Handler},
14    warning::{CompileWarning, Warning},
15};
16use sway_features::Feature;
17use sway_types::{BaseIdent, Named, SourceId, Span, Spanned};
18
19use crate::{
20    decl_engine::{DeclEngineGet, DeclId},
21    engine_threading::{DebugWithEngines, PartialEqWithEngines, PartialEqWithEnginesContext},
22    is_ty_module_cache_up_to_date,
23    language::{
24        parsed::*,
25        ty::{self, TyAstNodeContent, TyDecl, TyEnumDecl},
26        CallPath, ModName,
27    },
28    query_engine::{ModuleCacheKey, TypedModuleInfo},
29    semantic_analysis::*,
30    transform::AttributeKind,
31    BuildConfig, Engines, TypeInfo,
32};
33
34use super::{
35    declaration::auto_impl::{
36        abi_encoding::AbiEncodingAutoImplContext, debug::DebugAutoImplContext,
37        marker_traits::MarkerTraitsAutoImplContext,
38    },
39    symbol_collection_context::SymbolCollectionContext,
40};
41
42#[derive(Clone, Debug)]
43pub struct ModuleDepGraphEdge();
44
45impl Display for ModuleDepGraphEdge {
46    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47        write!(f, "")
48    }
49}
50
51pub type ModuleDepGraphNodeId = petgraph::graph::NodeIndex;
52
53#[derive(Clone, Debug)]
54pub enum ModuleDepGraphNode {
55    Module {},
56    Submodule { name: ModName },
57}
58
59impl DebugWithEngines for ModuleDepGraphNode {
60    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, _engines: &Engines) -> std::fmt::Result {
61        let text = match self {
62            ModuleDepGraphNode::Module { .. } => {
63                format!("{:?}", "Root module")
64            }
65            ModuleDepGraphNode::Submodule { name: mod_name } => {
66                format!("{:?}", mod_name.as_str())
67            }
68        };
69        f.write_str(&text)
70    }
71}
72
73// Represents an ordered graph between declaration id indexes.
74pub type ModuleDepNodeGraph = petgraph::graph::DiGraph<ModuleDepGraphNode, ModuleDepGraphEdge>;
75
76pub struct ModuleDepGraph {
77    dep_graph: ModuleDepNodeGraph,
78    root: ModuleDepGraphNodeId,
79    node_name_map: HashMap<String, ModuleDepGraphNodeId>,
80}
81
82impl ModuleDepGraph {
83    pub(crate) fn new() -> Self {
84        Self {
85            dep_graph: Default::default(),
86            root: Default::default(),
87            node_name_map: Default::default(),
88        }
89    }
90
91    pub fn add_node(&mut self, node: ModuleDepGraphNode) -> ModuleDepGraphNodeId {
92        let node_id = self.dep_graph.add_node(node.clone());
93        match node {
94            ModuleDepGraphNode::Module {} => {}
95            ModuleDepGraphNode::Submodule { name: mod_name } => {
96                self.node_name_map.insert(mod_name.to_string(), node_id);
97            }
98        };
99        node_id
100    }
101
102    pub fn add_root_node(&mut self) -> ModuleDepGraphNodeId {
103        self.root = self.add_node(super::module::ModuleDepGraphNode::Module {});
104        self.root
105    }
106
107    fn get_node_id_for_module(
108        &self,
109        mod_name: &sway_types::BaseIdent,
110    ) -> Option<ModuleDepGraphNodeId> {
111        self.node_name_map.get(&mod_name.to_string()).copied()
112    }
113
114    /// Prints out GraphViz DOT format for the dependency graph.
115    #[allow(dead_code)]
116    pub(crate) fn visualize(&self, engines: &Engines, print_graph: Option<String>) {
117        if let Some(graph_path) = print_graph {
118            use petgraph::dot::{Config, Dot};
119            let string_graph = self.dep_graph.filter_map(
120                |_idx, node| Some(format!("{:?}", engines.help_out(node))),
121                |_idx, edge| Some(format!("{}", edge)),
122            );
123
124            let output = format!(
125                "{:?}",
126                Dot::with_attr_getters(
127                    &string_graph,
128                    &[Config::NodeNoLabel, Config::EdgeNoLabel],
129                    &|_, er| format!("label = {:?}", er.weight()),
130                    &|_, nr| {
131                        let _node = &self.dep_graph[nr.0];
132                        let shape = "";
133                        let url = "".to_string();
134                        format!("{shape} label = {:?} {url}", nr.1)
135                    },
136                )
137            );
138
139            if graph_path.is_empty() {
140                tracing::info!("{output}");
141            } else {
142                let result = fs::write(graph_path.clone(), output);
143                if let Some(error) = result.err() {
144                    tracing::error!(
145                        "There was an issue while outputting module dep analysis graph to path {graph_path:?}\n{error}"
146                    );
147                }
148            }
149        }
150    }
151
152    /// Computes the ordered list by dependency, which will be used for evaluating the modules
153    /// in the correct order. We run a topological sort and cycle finding algorithm to check
154    /// for unsupported cyclic dependency cases.
155    pub(crate) fn compute_order(
156        &self,
157        handler: &Handler,
158    ) -> Result<ModuleEvaluationOrder, ErrorEmitted> {
159        // Check for dependency cycles in the graph by running the Johnson's algorithm.
160        let cycles = self.dep_graph.cycles();
161        if !cycles.is_empty() {
162            let mut modules = Vec::new();
163            for cycle in cycles.first().unwrap() {
164                let node = self.dep_graph.node_weight(*cycle).unwrap();
165                match node {
166                    ModuleDepGraphNode::Module {} => unreachable!(),
167                    ModuleDepGraphNode::Submodule { name } => modules.push(name.clone()),
168                };
169            }
170            return Err(handler.emit_err(CompileError::ModuleDepGraphCyclicReference { modules }));
171        }
172
173        // Do a topological sort to compute an ordered list of nodes.
174        let sorted = match petgraph::algo::toposort(&self.dep_graph, None) {
175            Ok(value) => value,
176            // If we were not able to toposort, this means there is likely a cycle in the module dependency graph,
177            // which we already handled above, so lets just return an empty evaluation order instead of panic'ing.
178            // module dependencies, which we have already reported.
179            Err(_) => return Err(handler.emit_err(CompileError::ModuleDepGraphEvaluationError {})),
180        };
181
182        let sorted = sorted
183            .into_iter()
184            .filter_map(|node_index| {
185                let node = self.dep_graph.node_weight(node_index);
186                match node {
187                    Some(node) => match node {
188                        ModuleDepGraphNode::Module {} => None, // root module
189                        ModuleDepGraphNode::Submodule { name: mod_name } => Some(mod_name.clone()),
190                    },
191                    None => None,
192                }
193            })
194            .rev()
195            .collect::<Vec<_>>();
196
197        Ok(sorted)
198    }
199}
200
201impl ty::TyModule {
202    /// Analyzes the given parsed module to produce a dependency graph.
203    pub fn build_dep_graph(
204        handler: &Handler,
205        parsed: &ParseModule,
206    ) -> Result<ModuleDepGraph, ErrorEmitted> {
207        let mut dep_graph = ModuleDepGraph::new();
208        dep_graph.add_root_node();
209
210        let ParseModule { submodules, .. } = parsed;
211
212        // Create graph nodes for each submodule.
213        submodules.iter().for_each(|(name, _submodule)| {
214            let sub_mod_node =
215                dep_graph.add_node(ModuleDepGraphNode::Submodule { name: name.clone() });
216            dep_graph
217                .dep_graph
218                .add_edge(dep_graph.root, sub_mod_node, ModuleDepGraphEdge {});
219        });
220
221        // Analyze submodules first in order of declaration.
222        submodules.iter().for_each(|(name, submodule)| {
223            let _ =
224                ty::TySubmodule::build_dep_graph(handler, &mut dep_graph, name.clone(), submodule);
225        });
226
227        Ok(dep_graph)
228    }
229
230    /// Collects the given parsed module to produce a module symbol map.
231    ///
232    /// Recursively collects submodules first.
233    pub fn collect(
234        handler: &Handler,
235        engines: &Engines,
236        ctx: &mut SymbolCollectionContext,
237        parsed: &ParseModule,
238    ) -> Result<(), ErrorEmitted> {
239        let ParseModule {
240            submodules,
241            tree,
242            module_eval_order,
243            attributes: _,
244            span: _,
245            hash: _,
246            ..
247        } = parsed;
248
249        // Analyze submodules first in order of evaluation previously computed by the dependency graph.
250        module_eval_order.iter().for_each(|eval_mod_name| {
251            let (name, submodule) = submodules
252                .iter()
253                .find(|(submod_name, _submodule)| eval_mod_name == submod_name)
254                .unwrap();
255            let _ = ty::TySubmodule::collect(handler, engines, ctx, name.clone(), submodule);
256        });
257
258        let _ = tree
259            .root_nodes
260            .iter()
261            .map(|node| ty::TyAstNode::collect(handler, engines, ctx, node))
262            .filter_map(|res| res.ok())
263            .collect::<Vec<_>>();
264
265        Ok(())
266    }
267
268    /// Retrieves a cached typed module if it's up to date.
269    ///
270    /// This function checks the cache for a typed module corresponding to the given source ID.
271    /// If found and up to date, it returns the cached module. Otherwise, it returns None.
272    fn get_cached_ty_module_if_up_to_date(
273        source_id: Option<&SourceId>,
274        engines: &Engines,
275        build_config: Option<&BuildConfig>,
276    ) -> Option<(Arc<ty::TyModule>, Arc<namespace::Module>)> {
277        let source_id = source_id?;
278
279        // Create a cache key and get the module cache
280        let path = engines.se().get_path(source_id);
281        let include_tests = build_config.is_some_and(|x| x.include_tests);
282        let key = ModuleCacheKey::new(path.clone().into(), include_tests);
283        let cache = engines.qe().module_cache.read();
284        cache.get(&key).and_then(|entry| {
285            entry.typed.as_ref().and_then(|typed| {
286                // Check if the cached module is up to date
287                let is_up_to_date = is_ty_module_cache_up_to_date(
288                    engines,
289                    &path.into(),
290                    include_tests,
291                    build_config,
292                );
293
294                // Return the cached module if it's up to date, otherwise None
295                if is_up_to_date {
296                    Some((typed.module.clone(), typed.namespace_module.clone()))
297                } else {
298                    None
299                }
300            })
301        })
302    }
303
304    /// Type-check the given parsed module to produce a typed module.
305    ///
306    /// Recursively type-checks submodules first.
307    pub fn type_check(
308        handler: &Handler,
309        mut ctx: TypeCheckContext,
310        engines: &Engines,
311        kind: TreeType,
312        parsed: &ParseModule,
313        build_config: Option<&BuildConfig>,
314    ) -> Result<Arc<Self>, ErrorEmitted> {
315        let ParseModule {
316            submodules,
317            tree,
318            attributes,
319            span,
320            module_eval_order,
321            ..
322        } = parsed;
323
324        // Try to get the cached root module if it's up to date
325        if let Some((ty_module, _namespace_module)) =
326            ty::TyModule::get_cached_ty_module_if_up_to_date(
327                parsed.span.source_id(),
328                engines,
329                build_config,
330            )
331        {
332            return Ok(ty_module);
333        }
334
335        // Type-check submodules first in order of evaluation previously computed by the dependency graph.
336        let submodules_res = module_eval_order
337            .iter()
338            .map(|eval_mod_name| {
339                let (name, submodule) = submodules
340                    .iter()
341                    .find(|(submod_name, _)| eval_mod_name == submod_name)
342                    .unwrap();
343
344                // Try to get the cached submodule
345                if let Some(cached_module) = ty::TyModule::get_cached_ty_module_if_up_to_date(
346                    submodule.module.span.source_id(),
347                    engines,
348                    build_config,
349                ) {
350                    // If cached, restore namespace module and return cached TySubmodule
351                    let (ty_module, namespace_module) = cached_module;
352                    ctx.namespace_mut()
353                        .current_module_mut()
354                        .import_cached_submodule(name, (*namespace_module).clone());
355
356                    let ty_submod = ty::TySubmodule {
357                        module: ty_module,
358                        mod_name_span: submodule.mod_name_span.clone(),
359                    };
360                    Ok::<(BaseIdent, ty::TySubmodule), ErrorEmitted>((name.clone(), ty_submod))
361                } else {
362                    // If not cached, type-check the submodule
363                    let type_checked_submodule = ty::TySubmodule::type_check(
364                        handler,
365                        ctx.by_ref(),
366                        engines,
367                        name.clone(),
368                        kind,
369                        submodule,
370                        build_config,
371                    )?;
372                    Ok((name.clone(), type_checked_submodule))
373                }
374            })
375            .collect::<Result<Vec<_>, _>>();
376
377        // TODO: Ordering should be solved across all modules prior to the beginning of type-check.
378        let ordered_nodes = node_dependencies::order_ast_nodes_by_dependency(
379            handler,
380            ctx.engines(),
381            tree.root_nodes.clone(),
382        )?;
383
384        let mut all_nodes = Self::type_check_nodes(handler, ctx.by_ref(), &ordered_nodes)?;
385        let submodules = submodules_res?;
386
387        let fallback_fn = collect_fallback_fn(&all_nodes, engines, handler)?;
388        match (&kind, &fallback_fn) {
389            (TreeType::Contract, _) | (_, None) => {}
390            (_, Some(fallback_fn)) => {
391                let fallback_fn = engines.de().get(fallback_fn);
392                return Err(handler.emit_err(CompileError::FallbackFnsAreContractOnly {
393                    span: fallback_fn.span.clone(),
394                }));
395            }
396        }
397
398        if ctx.experimental.new_encoding {
399            let main_decl = all_nodes.iter_mut().find_map(|x| match &mut x.content {
400                ty::TyAstNodeContent::Declaration(ty::TyDecl::FunctionDecl(decl)) => {
401                    let fn_decl = engines.de().get_function(&decl.decl_id);
402                    (fn_decl.name.as_str() == "main").then_some(fn_decl)
403                }
404                _ => None,
405            });
406
407            match (&kind, main_decl.is_some()) {
408                (TreeType::Predicate, true) => {
409                    let mut fn_generator = AbiEncodingAutoImplContext::new(&mut ctx);
410                    if let Ok(node) = fn_generator.generate_predicate_entry(
411                        engines,
412                        main_decl.as_ref().unwrap(),
413                        handler,
414                    ) {
415                        all_nodes.push(node)
416                    }
417                }
418                (TreeType::Script, true) => {
419                    let mut fn_generator = AbiEncodingAutoImplContext::new(&mut ctx);
420                    if let Ok(node) = fn_generator.generate_script_entry(
421                        engines,
422                        main_decl.as_ref().unwrap(),
423                        handler,
424                    ) {
425                        all_nodes.push(node)
426                    }
427                }
428                (TreeType::Contract, _) => {
429                    // collect all supertrait methods
430                    let contract_supertrait_fns = submodules
431                        .iter()
432                        .flat_map(|x| x.1.module.submodules_recursive())
433                        .flat_map(|x| x.1.module.contract_supertrait_fns(engines))
434                        .chain(
435                            all_nodes
436                                .iter()
437                                .flat_map(|x| x.contract_supertrait_fns(engines)),
438                        )
439                        .collect::<Vec<_>>();
440
441                    // collect all contract methods
442                    let mut contract_fns = submodules
443                        .iter()
444                        .flat_map(|x| x.1.module.submodules_recursive())
445                        .flat_map(|x| x.1.module.contract_fns(engines))
446                        .chain(all_nodes.iter().flat_map(|x| x.contract_fns(engines)))
447                        .collect::<Vec<_>>();
448
449                    // exclude all contract methods that are supertrait methods
450                    let partialeq_ctx = PartialEqWithEnginesContext::new(engines);
451                    contract_fns.retain(|method| {
452                        contract_supertrait_fns
453                            .iter()
454                            .all(|si| !PartialEqWithEngines::eq(method, si, &partialeq_ctx))
455                    });
456
457                    let mut fn_generator = AbiEncodingAutoImplContext::new(&mut ctx);
458                    if let Ok(node) = fn_generator.generate_contract_entry(
459                        engines,
460                        parsed.span.source_id(),
461                        &contract_fns,
462                        fallback_fn,
463                        handler,
464                    ) {
465                        all_nodes.push(node)
466                    }
467                }
468                _ => {}
469            }
470        }
471
472        #[allow(clippy::arc_with_non_send_sync)]
473        let ty_module = Arc::new(Self {
474            span: span.clone(),
475            submodules,
476            all_nodes,
477            attributes: attributes.clone(),
478        });
479
480        // Cache the ty module
481        if let Some(source_id) = span.source_id() {
482            let path = engines.se().get_path(source_id);
483            let version = build_config
484                .and_then(|config| config.lsp_mode.as_ref())
485                .and_then(|lsp| lsp.file_versions.get(&path).copied())
486                .flatten();
487
488            let include_tests = build_config.is_some_and(|x| x.include_tests);
489            let key = ModuleCacheKey::new(path.clone().into(), include_tests);
490            engines.qe().update_typed_module_cache_entry(
491                &key,
492                TypedModuleInfo {
493                    module: ty_module.clone(),
494                    namespace_module: Arc::new(ctx.namespace().current_module().clone()),
495                    version,
496                },
497            );
498        }
499
500        Ok(ty_module)
501    }
502
503    // Filter and gather impl items
504    fn get_all_impls(
505        ctx: TypeCheckContext<'_>,
506        nodes: &[AstNode],
507        predicate: fn(&ImplSelfOrTrait) -> bool,
508    ) -> HashMap<BaseIdent, HashSet<CallPath>> {
509        let engines = ctx.engines();
510        let mut impls = HashMap::<BaseIdent, HashSet<CallPath>>::new();
511
512        for node in nodes.iter() {
513            if let AstNodeContent::Declaration(Declaration::ImplSelfOrTrait(decl_id)) =
514                &node.content
515            {
516                let decl = &*engines.pe().get_impl_self_or_trait(decl_id);
517                let implementing_for = ctx.engines.te().get(decl.implementing_for.type_id());
518                let implementing_for = match &*implementing_for {
519                    TypeInfo::Struct(decl_id) => {
520                        Some(ctx.engines().de().get(decl_id).name().clone())
521                    }
522                    TypeInfo::Enum(decl) => Some(ctx.engines().de().get(decl).name().clone()),
523                    TypeInfo::Custom {
524                        qualified_call_path,
525                        ..
526                    } => Some(qualified_call_path.call_path.suffix.clone()),
527                    _ => None,
528                };
529
530                if let Some(implementing_for) = implementing_for {
531                    if predicate(decl) {
532                        impls
533                            .entry(implementing_for)
534                            .or_default()
535                            .insert(decl.trait_name.clone());
536                    }
537                }
538            }
539        }
540
541        impls
542    }
543
544    fn type_check_nodes(
545        handler: &Handler,
546        mut ctx: TypeCheckContext,
547        nodes: &[AstNode],
548    ) -> Result<Vec<ty::TyAstNode>, ErrorEmitted> {
549        let engines = ctx.engines();
550
551        // Check which structs and enums needs to have auto impl for `AbiEncode` and `AbiDecode`.
552        // We need to do this before type checking, because the impls must be right after
553        // the declarations.
554        let all_abi_encode_impls = Self::get_all_impls(ctx.by_ref(), nodes, |decl| {
555            decl.trait_name.suffix.as_str() == "AbiEncode"
556        });
557        let all_debug_impls = Self::get_all_impls(ctx.by_ref(), nodes, |decl| {
558            decl.trait_name.suffix.as_str() == "Debug"
559        });
560
561        let mut typed_nodes = vec![];
562        for node in nodes {
563            // Check if the encoding and debug traits are explicitly implemented.
564            let (auto_impl_encoding_traits, auto_impl_debug_traits) = match &node.content {
565                AstNodeContent::Declaration(Declaration::StructDeclaration(decl_id)) => {
566                    let decl = ctx.engines().pe().get_struct(decl_id);
567                    (
568                        !all_abi_encode_impls.contains_key(&decl.name),
569                        !all_debug_impls.contains_key(&decl.name),
570                    )
571                }
572                AstNodeContent::Declaration(Declaration::EnumDeclaration(decl_id)) => {
573                    let decl = ctx.engines().pe().get_enum(decl_id);
574                    (
575                        !all_abi_encode_impls.contains_key(&decl.name),
576                        !all_debug_impls.contains_key(&decl.name),
577                    )
578                }
579                _ => (false, false),
580            };
581
582            let Ok(node) = ty::TyAstNode::type_check(handler, ctx.by_ref(), node) else {
583                continue;
584            };
585
586            // Auto impl encoding traits only if they are not explicitly implemented.
587            let mut generated = vec![];
588            if ctx.experimental.new_encoding {
589                if let (true, mut ctx) = (
590                    auto_impl_encoding_traits,
591                    AbiEncodingAutoImplContext::new(&mut ctx),
592                ) {
593                    match &node.content {
594                        TyAstNodeContent::Declaration(decl @ TyDecl::StructDecl(_))
595                        | TyAstNodeContent::Declaration(decl @ TyDecl::EnumDecl(_)) => {
596                            let (abi_encode_impl, abi_decode_impl) =
597                                ctx.generate_abi_encode_and_decode_impls(engines, decl);
598                            generated.extend(abi_encode_impl);
599                            generated.extend(abi_decode_impl);
600                        }
601                        _ => {}
602                    }
603                };
604            }
605
606            // Auto impl debug traits only if they are not explicitly implemented
607            if auto_impl_debug_traits {
608                match &node.content {
609                    TyAstNodeContent::Declaration(decl @ TyDecl::StructDecl(_))
610                    | TyAstNodeContent::Declaration(decl @ TyDecl::EnumDecl(_)) => {
611                        let mut ctx = DebugAutoImplContext::new(&mut ctx);
612                        let a = ctx.generate_debug_impl(engines, decl);
613                        generated.extend(a);
614                    }
615                    _ => {}
616                }
617            }
618
619            // Always auto impl marker traits. If an explicit implementation exists, that will be
620            // reported as an error when type-checking trait impls.
621            if ctx.experimental.error_type {
622                let mut ctx = MarkerTraitsAutoImplContext::new(&mut ctx);
623                if let TyAstNodeContent::Declaration(TyDecl::EnumDecl(enum_decl)) = &node.content {
624                    let enum_decl = &*ctx.engines().de().get(&enum_decl.decl_id);
625
626                    let enum_marker_trait_impl =
627                        ctx.generate_enum_marker_trait_impl(engines, enum_decl);
628                    generated.extend(enum_marker_trait_impl);
629
630                    if check_is_valid_error_type_enum(handler, enum_decl).is_ok_and(|res| res) {
631                        let error_type_marker_trait_impl =
632                            ctx.generate_error_type_marker_trait_impl_for_enum(engines, enum_decl);
633                        generated.extend(error_type_marker_trait_impl);
634                    }
635                }
636            } else {
637                // Check for usages of `error_type` and `error` attributes without the feature enabled.
638                if let TyAstNodeContent::Declaration(TyDecl::EnumDecl(enum_decl)) = &node.content {
639                    let enum_decl = &*ctx.engines().de().get(&enum_decl.decl_id);
640                    for attr in enum_decl.attributes.of_kind(AttributeKind::ErrorType) {
641                        handler.emit_err(CompileError::FeatureIsDisabled {
642                            feature: Feature::ErrorType.name().to_string(),
643                            url: Feature::ErrorType.url().to_string(),
644                            span: attr.name.span(),
645                        });
646                    }
647
648                    for attr in enum_decl
649                        .variants
650                        .iter()
651                        .flat_map(|variant| variant.attributes.of_kind(AttributeKind::Error))
652                    {
653                        handler.emit_err(CompileError::FeatureIsDisabled {
654                            feature: Feature::ErrorType.name().to_string(),
655                            url: Feature::ErrorType.url().to_string(),
656                            span: attr.name.span(),
657                        });
658                    }
659                }
660            }
661
662            typed_nodes.push(node);
663            typed_nodes.extend(generated);
664        }
665
666        Ok(typed_nodes)
667    }
668}
669
670/// Performs all semantic checks for `error_type` and `error` attributes, and returns true if the
671/// `enum_decl` is a valid error type declaration.
672fn check_is_valid_error_type_enum(
673    handler: &Handler,
674    enum_decl: &TyEnumDecl,
675) -> Result<bool, ErrorEmitted> {
676    let has_error_type_attribute = enum_decl.attributes.has_error_type();
677
678    if has_error_type_attribute && enum_decl.variants.is_empty() {
679        handler.emit_warn(CompileWarning {
680            span: enum_decl.name().span(),
681            warning_content: Warning::ErrorTypeEmptyEnum {
682                enum_name: enum_decl.name().into(),
683            },
684        });
685    }
686
687    // We show warnings for error messages even if the error type enum
688    // is not well formed, e.g., if it doesn't have the `error_type` attribute.
689    let mut duplicated_error_messages = IndexMap::<&str, Vec<Span>>::new();
690    for (enum_variant_name, error_attr) in enum_decl.variants.iter().flat_map(|variant| {
691        variant
692            .attributes
693            .error()
694            .map(|error_attr| (&variant.name, error_attr))
695    }) {
696        error_attr.check_args_multiplicity(handler)?;
697        assert_eq!(
698            (1usize, 1usize),
699            (&error_attr.args_multiplicity()).into(),
700            "`#[error]` attribute must have argument multiplicity of exactly one"
701        );
702
703        let m_arg = &error_attr.args[0];
704        let error_msg = m_arg.get_string(handler, error_attr)?;
705
706        if error_msg.is_empty() {
707            handler.emit_warn(CompileWarning {
708                span: m_arg
709                    .value
710                    .as_ref()
711                    .expect("`m` argument has a valid empty string value")
712                    .span(),
713                warning_content: Warning::ErrorEmptyErrorMessage {
714                    enum_name: enum_decl.name().clone(),
715                    enum_variant_name: enum_variant_name.clone(),
716                },
717            });
718        } else {
719            // We ignore duplicated empty messages and for those show
720            // only the warning that the message is empty.
721            duplicated_error_messages
722                .entry(error_msg)
723                .or_default()
724                .push(
725                    m_arg
726                        .value
727                        .as_ref()
728                        .expect("`m` argument has a valid empty string value")
729                        .span(),
730                );
731        }
732    }
733
734    // Emit duplicated messages warnings, if we actually have duplicates.
735    for duplicated_error_messages in duplicated_error_messages
736        .into_values()
737        .filter(|spans| spans.len() > 1)
738    {
739        let (last_occurrence, previous_occurrences) = duplicated_error_messages
740            .split_last()
741            .expect("`duplicated_error_messages` has more then one element");
742        handler.emit_warn(CompileWarning {
743            span: last_occurrence.clone(),
744            warning_content: Warning::ErrorDuplicatedErrorMessage {
745                last_occurrence: last_occurrence.clone(),
746                previous_occurrences: previous_occurrences.into(),
747            },
748        });
749    }
750
751    handler.scope(|handler| {
752        if has_error_type_attribute {
753            let non_error_variants = enum_decl
754                .variants
755                .iter()
756                .filter(|variant| !variant.attributes.has_error())
757                .collect_vec();
758            if !non_error_variants.is_empty() {
759                handler.emit_err(CompileError::ErrorTypeEnumHasNonErrorVariants {
760                    enum_name: enum_decl.name().into(),
761                    non_error_variants: non_error_variants
762                        .iter()
763                        .map(|variant| (&variant.name).into())
764                        .collect(),
765                });
766            }
767        } else {
768            for variant in enum_decl
769                .variants
770                .iter()
771                .filter(|variant| variant.attributes.has_error())
772            {
773                handler.emit_err(CompileError::ErrorAttributeInNonErrorEnum {
774                    enum_name: enum_decl.name().into(),
775                    enum_variant_name: (&variant.name).into(),
776                });
777            }
778        }
779
780        Ok(())
781    })?;
782
783    Ok(has_error_type_attribute)
784}
785
786fn collect_fallback_fn(
787    all_nodes: &[ty::TyAstNode],
788    engines: &Engines,
789    handler: &Handler,
790) -> Result<Option<DeclId<ty::TyFunctionDecl>>, ErrorEmitted> {
791    let mut fallback_fns = all_nodes
792        .iter()
793        .filter_map(|x| match &x.content {
794            ty::TyAstNodeContent::Declaration(ty::TyDecl::FunctionDecl(decl)) => {
795                let d = engines.de().get(&decl.decl_id);
796                d.is_fallback().then_some(decl.decl_id)
797            }
798            _ => None,
799        })
800        .collect::<Vec<_>>();
801
802    let mut last_error = None;
803    for f in fallback_fns.iter().skip(1) {
804        let decl = engines.de().get(f);
805        last_error = Some(
806            handler.emit_err(CompileError::MultipleDefinitionsOfFallbackFunction {
807                name: decl.name.clone(),
808                span: decl.span.clone(),
809            }),
810        );
811    }
812
813    if let Some(last_error) = last_error {
814        return Err(last_error);
815    }
816
817    if let Some(fallback_fn) = fallback_fns.pop() {
818        let f = engines.de().get(&fallback_fn);
819        if !f.parameters.is_empty() {
820            Err(
821                handler.emit_err(CompileError::FallbackFnsCannotHaveParameters {
822                    span: f.span.clone(),
823                }),
824            )
825        } else {
826            Ok(Some(fallback_fn))
827        }
828    } else {
829        Ok(None)
830    }
831}
832
833impl ty::TySubmodule {
834    pub fn build_dep_graph(
835        _handler: &Handler,
836        module_dep_graph: &mut ModuleDepGraph,
837        mod_name: ModName,
838        submodule: &ParseSubmodule,
839    ) -> Result<(), ErrorEmitted> {
840        let ParseSubmodule { module, .. } = submodule;
841        let sub_mod_node = module_dep_graph.get_node_id_for_module(&mod_name).unwrap();
842        for node in module.tree.root_nodes.iter() {
843            match &node.content {
844                AstNodeContent::UseStatement(use_stmt) => {
845                    if let Some(use_mod_ident) = use_stmt.call_path.first() {
846                        if let Some(mod_name_node) =
847                            module_dep_graph.get_node_id_for_module(use_mod_ident)
848                        {
849                            // Prevent adding edge loops between the same node as that will throw off
850                            // the cyclic dependency analysis.
851                            if sub_mod_node != mod_name_node {
852                                module_dep_graph.dep_graph.add_edge(
853                                    sub_mod_node,
854                                    mod_name_node,
855                                    ModuleDepGraphEdge {},
856                                );
857                            }
858                        }
859                    }
860                }
861                AstNodeContent::Declaration(_) => {}
862                AstNodeContent::Expression(_) => {}
863                AstNodeContent::IncludeStatement(_) => {}
864                AstNodeContent::Error(_, _) => {}
865            }
866        }
867        Ok(())
868    }
869
870    pub fn collect(
871        handler: &Handler,
872        engines: &Engines,
873        parent_ctx: &mut SymbolCollectionContext,
874        mod_name: ModName,
875        submodule: &ParseSubmodule,
876    ) -> Result<(), ErrorEmitted> {
877        let ParseSubmodule {
878            module,
879            mod_name_span: _,
880            visibility,
881        } = submodule;
882        parent_ctx.enter_submodule(
883            handler,
884            engines,
885            mod_name,
886            *visibility,
887            module.span.clone(),
888            |submod_ctx| ty::TyModule::collect(handler, engines, submod_ctx, module),
889        )?
890    }
891
892    pub fn type_check(
893        handler: &Handler,
894        mut parent_ctx: TypeCheckContext,
895        engines: &Engines,
896        mod_name: ModName,
897        kind: TreeType,
898        submodule: &ParseSubmodule,
899        build_config: Option<&BuildConfig>,
900    ) -> Result<Self, ErrorEmitted> {
901        let ParseSubmodule {
902            module,
903            mod_name_span,
904            visibility,
905        } = submodule;
906        parent_ctx.enter_submodule(
907            handler,
908            mod_name,
909            *visibility,
910            module.span.clone(),
911            |submod_ctx| {
912                let module_res = ty::TyModule::type_check(
913                    handler,
914                    submod_ctx,
915                    engines,
916                    kind,
917                    module,
918                    build_config,
919                );
920                module_res.map(|module| ty::TySubmodule {
921                    module,
922                    mod_name_span: mod_name_span.clone(),
923                })
924            },
925        )?
926    }
927}