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