sway_core/
lib.rs

1#![recursion_limit = "256"]
2
3#[macro_use]
4pub mod error;
5
6#[macro_use]
7pub mod engine_threading;
8
9pub mod abi_generation;
10pub mod asm_generation;
11mod asm_lang;
12mod build_config;
13pub mod compiler_generated;
14mod concurrent_slab;
15mod control_flow_analysis;
16mod debug_generation;
17pub mod decl_engine;
18pub mod ir_generation;
19pub mod language;
20pub mod marker_traits;
21mod metadata;
22pub mod query_engine;
23pub mod semantic_analysis;
24pub mod source_map;
25pub mod transform;
26pub mod type_system;
27
28use crate::ir_generation::check_function_purity;
29use crate::query_engine::ModuleCacheEntry;
30use crate::source_map::SourceMap;
31pub use asm_generation::from_ir::compile_ir_context_to_finalized_asm;
32use asm_generation::FinalizedAsm;
33pub use asm_generation::{CompiledBytecode, FinalizedEntry};
34pub use build_config::DbgGeneration;
35pub use build_config::{BuildConfig, BuildTarget, LspConfig, OptLevel, PrintAsm, PrintIr};
36use control_flow_analysis::ControlFlowGraph;
37pub use debug_generation::write_dwarf;
38use itertools::Itertools;
39use metadata::MetadataManager;
40use query_engine::{ModuleCacheKey, ModuleCommonInfo, ParsedModuleInfo, ProgramsCacheEntry};
41use semantic_analysis::program::TypeCheckFailed;
42use std::collections::hash_map::DefaultHasher;
43use std::collections::HashMap;
44use std::hash::{Hash, Hasher};
45use std::path::{Path, PathBuf};
46use std::sync::atomic::{AtomicBool, Ordering};
47use std::sync::Arc;
48use sway_ast::AttributeDecl;
49use sway_error::convert_parse_tree_error::ConvertParseTreeError;
50use sway_error::handler::{ErrorEmitted, Handler};
51use sway_error::warning::{CompileWarning, Warning};
52use sway_features::ExperimentalFeatures;
53use sway_ir::{
54    create_o1_pass_group, register_known_passes, Context, Kind, Module, PassGroup, PassManager,
55    PrintPassesOpts, ARG_DEMOTION_NAME, CONST_DEMOTION_NAME, DCE_NAME, FN_DEDUP_DEBUG_PROFILE_NAME,
56    FN_INLINE_NAME, GLOBALS_DCE_NAME, MEM2REG_NAME, MEMCPYOPT_NAME, MISC_DEMOTION_NAME,
57    RET_DEMOTION_NAME, SIMPLIFY_CFG_NAME, SROA_NAME,
58};
59use sway_types::span::Source;
60use sway_types::{SourceEngine, SourceLocation, Span};
61use sway_utils::{time_expr, PerformanceData, PerformanceMetric};
62use transform::{ArgsExpectValues, Attribute, AttributeKind, Attributes, ExpectedArgs};
63use types::{CollectTypesMetadata, CollectTypesMetadataContext, LogId, TypeMetadata};
64
65pub use semantic_analysis::namespace::{self, Namespace};
66pub mod types;
67
68use sway_error::error::CompileError;
69use sway_types::{ident::Ident, span, Spanned};
70pub use type_system::*;
71
72pub use language::Programs;
73use language::{lexed, parsed, ty, Visibility};
74use transform::to_parsed_lang::{self, convert_module_kind};
75
76pub mod fuel_prelude {
77    pub use fuel_vm::{self, fuel_asm, fuel_crypto, fuel_tx, fuel_types};
78}
79
80pub use engine_threading::Engines;
81
82/// Given an input `Arc<str>` and an optional [BuildConfig], parse the input into a [lexed::LexedProgram] and [parsed::ParseProgram].
83///
84/// # Example
85/// ```ignore
86/// # use sway_core::parse;
87/// # fn main() {
88///     let input = "script; fn main() -> bool { true }";
89///     let result = parse(input.into(), <_>::default(), None);
90/// # }
91/// ```
92///
93/// # Panics
94/// Panics if the parser panics.
95pub fn parse(
96    src: Source,
97    handler: &Handler,
98    engines: &Engines,
99    config: Option<&BuildConfig>,
100    experimental: ExperimentalFeatures,
101) -> Result<(lexed::LexedProgram, parsed::ParseProgram), ErrorEmitted> {
102    match config {
103        None => parse_in_memory(handler, engines, src, experimental, DbgGeneration::None),
104        // When a `BuildConfig` is given,
105        // the module source may declare `mod`s that must be parsed from other files.
106        Some(config) => parse_module_tree(
107            handler,
108            engines,
109            src,
110            config.canonical_root_module(),
111            None,
112            config.build_target,
113            config.dbg_generation,
114            config.include_tests,
115            experimental,
116            config.lsp_mode.as_ref(),
117        )
118        .map(
119            |ParsedModuleTree {
120                 tree_type: kind,
121                 lexed_module,
122                 parse_module,
123             }| {
124                let lexed = lexed::LexedProgram {
125                    kind,
126                    root: lexed_module,
127                };
128                let parsed = parsed::ParseProgram {
129                    kind,
130                    root: parse_module,
131                };
132                (lexed, parsed)
133            },
134        ),
135    }
136}
137
138/// Parses the tree kind in the input provided.
139///
140/// This will lex the entire input, but parses only the module kind.
141pub fn parse_tree_type(handler: &Handler, src: Source) -> Result<parsed::TreeType, ErrorEmitted> {
142    // Parsing only the module kind does not depend on any
143    // experimental feature. So, we can just pass the default
144    // experimental features here.
145    let experimental = ExperimentalFeatures::default();
146    sway_parse::parse_module_kind(handler, src, None, experimental)
147        .map(|kind| convert_module_kind(&kind))
148}
149
150/// Converts `attribute_decls` to [Attributes].
151///
152/// This function always returns [Attributes], even if the attributes are erroneous.
153/// Errors and warnings are returned via [Handler]. The callers should ignore eventual errors
154/// in attributes and proceed with the compilation. [Attributes] are tolerant to erroneous
155/// attributes and follows the last-wins principle, which allows annotated elements to
156/// proceed with compilation. After their successful compilation, callers need to inspect
157/// the [Handler] and still emit errors if there were any.
158pub(crate) fn attr_decls_to_attributes(
159    attribute_decls: &[AttributeDecl],
160    can_annotate: impl Fn(&Attribute) -> bool,
161    target_friendly_name: &'static str,
162) -> (Handler, Attributes) {
163    let handler = Handler::default();
164    // Check if attribute is an unsupported inner attribute (`#!`).
165    // Note that we are doing that before creating the flattened `attributes`,
166    // because we want the error to point at the `#!` token.
167    // Note also that we will still include those attributes into
168    // the `attributes`. There are cases, like e.g., LSP, where
169    // having complete list of attributes is needed.
170    // In the below analysis, though, we will be ignoring inner attributes,
171    // means not checking their content.
172    for attr_decl in attribute_decls
173        .iter()
174        .filter(|attr| !attr.is_doc_comment() && attr.is_inner())
175    {
176        handler.emit_err(CompileError::Unimplemented {
177            span: attr_decl.hash_kind.span(),
178            feature: "Using inner attributes (`#!`)".to_string(),
179            help: vec![],
180        });
181    }
182
183    let attributes = Attributes::new(attribute_decls);
184
185    // Check for unknown attributes.
186    for attribute in attributes.unknown().filter(|attr| attr.is_outer()) {
187        handler.emit_warn(CompileWarning {
188            span: attribute.name.span(),
189            warning_content: Warning::UnknownAttribute {
190                attribute: (&attribute.name).into(),
191                known_attributes: attributes.known_attribute_names(),
192            },
193        });
194    }
195
196    // Check for attributes annotating invalid targets.
197    for ((attribute_kind, _attribute_direction), mut attributes) in &attributes
198        .all()
199        .filter(|attr| attr.is_doc_comment() || attr.is_outer())
200        .chunk_by(|attr| (attr.kind, attr.direction))
201    {
202        // For doc comments, we want to show the error on a complete doc comment,
203        // and not on every documentation line.
204        if attribute_kind == AttributeKind::DocComment {
205            let first_doc_line = attributes
206                .next()
207                .expect("`chunk_by` guarantees existence of at least one element in the chunk");
208            if !can_annotate(first_doc_line) {
209                let last_doc_line = match attributes.last() {
210                    Some(last_attr) => last_attr,
211                    // There is only one doc line in the complete doc comment.
212                    None => first_doc_line,
213                };
214                handler.emit_err(
215                    ConvertParseTreeError::InvalidAttributeTarget {
216                        span: Span::join(
217                            first_doc_line.span.clone(),
218                            &last_doc_line.span.start_span(),
219                        ),
220                        attribute: first_doc_line.name.clone(),
221                        target_friendly_name,
222                        can_only_annotate_help: first_doc_line
223                            .can_only_annotate_help(target_friendly_name),
224                    }
225                    .into(),
226                );
227            }
228        } else {
229            // For other attributes, the error is shown for every individual attribute.
230            for attribute in attributes {
231                if !can_annotate(attribute) {
232                    handler.emit_err(
233                        ConvertParseTreeError::InvalidAttributeTarget {
234                            span: attribute.name.span(),
235                            attribute: attribute.name.clone(),
236                            target_friendly_name,
237                            can_only_annotate_help: attribute
238                                .can_only_annotate_help(target_friendly_name),
239                        }
240                        .into(),
241                    );
242                }
243            }
244        }
245    }
246
247    // In all the subsequent test we are checking only non-doc-comment attributes
248    // and only those that didn't produce invalid target or unsupported inner attributes errors.
249    let should_be_checked =
250        |attr: &&Attribute| !attr.is_doc_comment() && attr.is_outer() && can_annotate(attr);
251
252    // Check for attributes multiplicity.
253    for (_attribute_kind, attributes_of_kind) in
254        attributes.all_by_kind(|attr| should_be_checked(attr) && !attr.kind.allows_multiple())
255    {
256        if attributes_of_kind.len() > 1 {
257            let (last_attribute, previous_attributes) = attributes_of_kind
258                .split_last()
259                .expect("`attributes_of_kind` has more then one element");
260            handler.emit_err(
261                ConvertParseTreeError::InvalidAttributeMultiplicity {
262                    last_occurrence: (&last_attribute.name).into(),
263                    previous_occurrences: previous_attributes
264                        .iter()
265                        .map(|attr| (&attr.name).into())
266                        .collect(),
267                }
268                .into(),
269            );
270        }
271    }
272
273    // Check for arguments multiplicity.
274    // For attributes that can be applied only once but are applied several times
275    // we will still check arguments in every attribute occurrence.
276    for attribute in attributes.all().filter(should_be_checked) {
277        let _ = attribute.check_args_multiplicity(&handler);
278    }
279
280    // Check for expected arguments.
281    // For attributes that can be applied only once but are applied more times
282    // we will check arguments of every attribute occurrence.
283    // If an attribute does not expect any arguments, we will not check them,
284    // but emit only the above error about invalid number of arguments.
285    for attribute in attributes
286        .all()
287        .filter(|attr| should_be_checked(attr) && attr.can_have_arguments())
288    {
289        match attribute.expected_args() {
290            ExpectedArgs::None => unreachable!("`attribute` can have arguments"),
291            ExpectedArgs::Any => {}
292            ExpectedArgs::MustBeIn(expected_args) => {
293                for arg in attribute.args.iter() {
294                    if !expected_args.contains(&arg.name.as_str()) {
295                        handler.emit_err(
296                            ConvertParseTreeError::InvalidAttributeArg {
297                                attribute: attribute.name.clone(),
298                                arg: (&arg.name).into(),
299                                expected_args: expected_args.clone(),
300                            }
301                            .into(),
302                        );
303                    }
304                }
305            }
306            ExpectedArgs::ShouldBeIn(expected_args) => {
307                for arg in attribute.args.iter() {
308                    if !expected_args.contains(&arg.name.as_str()) {
309                        handler.emit_warn(CompileWarning {
310                            span: arg.name.span(),
311                            warning_content: Warning::UnknownAttributeArg {
312                                attribute: attribute.name.clone(),
313                                arg: (&arg.name).into(),
314                                expected_args: expected_args.clone(),
315                            },
316                        });
317                    }
318                }
319            }
320        }
321    }
322
323    // Check for expected argument values.
324    // We use here the same logic for what to check, as in the above check
325    // for expected arguments.
326    for attribute in attributes
327        .all()
328        .filter(|attr| should_be_checked(attr) && attr.can_have_arguments())
329    {
330        // In addition, if an argument **must** be in expected args but is not,
331        // we will not be checking it, but only emit the error above.
332        // But if it **should** be in expected args and is not,
333        // we still impose on it the expectation coming from its attribute.
334        fn check_value_expected(handler: &Handler, attribute: &Attribute, is_value_expected: bool) {
335            for arg in attribute.args.iter() {
336                if let ExpectedArgs::MustBeIn(expected_args) = attribute.expected_args() {
337                    if !expected_args.contains(&arg.name.as_str()) {
338                        continue;
339                    }
340                }
341
342                if (is_value_expected && arg.value.is_none())
343                    || (!is_value_expected && arg.value.is_some())
344                {
345                    handler.emit_err(
346                        ConvertParseTreeError::InvalidAttributeArgExpectsValue {
347                            attribute: attribute.name.clone(),
348                            arg: (&arg.name).into(),
349                            value_span: arg.value.as_ref().map(|literal| literal.span()),
350                        }
351                        .into(),
352                    );
353                }
354            }
355        }
356
357        match attribute.args_expect_values() {
358            ArgsExpectValues::Yes => check_value_expected(&handler, attribute, true),
359            ArgsExpectValues::No => check_value_expected(&handler, attribute, false),
360            ArgsExpectValues::Maybe => {}
361        }
362    }
363
364    (handler, attributes)
365}
366
367/// When no `BuildConfig` is given, we're assumed to be parsing in-memory with no submodules.
368fn parse_in_memory(
369    handler: &Handler,
370    engines: &Engines,
371    src: Source,
372    experimental: ExperimentalFeatures,
373    dbg_generation: DbgGeneration,
374) -> Result<(lexed::LexedProgram, parsed::ParseProgram), ErrorEmitted> {
375    let mut hasher = DefaultHasher::new();
376    src.text.hash(&mut hasher);
377    let hash = hasher.finish();
378    let module = sway_parse::parse_file(handler, src, None, experimental)?;
379
380    let (attributes_handler, attributes) = attr_decls_to_attributes(
381        &module.attributes,
382        |attr| attr.can_annotate_module_kind(),
383        module.value.kind.friendly_name(),
384    );
385    let attributes_error_emitted = handler.append(attributes_handler);
386
387    let (kind, tree) = to_parsed_lang::convert_parse_tree(
388        &mut to_parsed_lang::Context::new(BuildTarget::EVM, dbg_generation, experimental),
389        handler,
390        engines,
391        module.value.clone(),
392    )?;
393
394    match attributes_error_emitted {
395        Some(err) => Err(err),
396        None => {
397            let root = parsed::ParseModule {
398                span: span::Span::dummy(),
399                module_kind_span: module.value.kind.span(),
400                module_eval_order: vec![],
401                tree,
402                submodules: vec![],
403                attributes,
404                hash,
405            };
406            let lexed_program = lexed::LexedProgram::new(
407                kind,
408                lexed::LexedModule {
409                    tree: module,
410                    submodules: vec![],
411                },
412            );
413            Ok((lexed_program, parsed::ParseProgram { kind, root }))
414        }
415    }
416}
417
418pub struct Submodule {
419    name: Ident,
420    path: Arc<PathBuf>,
421    lexed: lexed::LexedSubmodule,
422    parsed: parsed::ParseSubmodule,
423}
424
425/// Contains the lexed and parsed submodules 'deps' of a module.
426pub type Submodules = Vec<Submodule>;
427
428/// Parse all dependencies `deps` as submodules.
429#[allow(clippy::too_many_arguments)]
430fn parse_submodules(
431    handler: &Handler,
432    engines: &Engines,
433    module_name: Option<&str>,
434    module: &sway_ast::Module,
435    module_dir: &Path,
436    build_target: BuildTarget,
437    dbg_generation: DbgGeneration,
438    include_tests: bool,
439    experimental: ExperimentalFeatures,
440    lsp_mode: Option<&LspConfig>,
441) -> Submodules {
442    // Assume the happy path, so there'll be as many submodules as dependencies, but no more.
443    let mut submods = Vec::with_capacity(module.submodules().count());
444    module.submodules().for_each(|submod| {
445        // Read the source code from the dependency.
446        // If we cannot, record as an error, but continue with other files.
447        let submod_path = Arc::new(module_path(module_dir, module_name, submod));
448        let submod_src: Source = match std::fs::read_to_string(&*submod_path) {
449            Ok(s) => s.as_str().into(),
450            Err(e) => {
451                handler.emit_err(CompileError::FileCouldNotBeRead {
452                    span: submod.name.span(),
453                    file_path: submod_path.to_string_lossy().to_string(),
454                    stringified_error: e.to_string(),
455                });
456                return;
457            }
458        };
459        if let Ok(ParsedModuleTree {
460            tree_type: kind,
461            lexed_module,
462            parse_module,
463        }) = parse_module_tree(
464            handler,
465            engines,
466            submod_src.clone(),
467            submod_path.clone(),
468            Some(submod.name.as_str()),
469            build_target,
470            dbg_generation,
471            include_tests,
472            experimental,
473            lsp_mode,
474        ) {
475            if !matches!(kind, parsed::TreeType::Library) {
476                let source_id = engines.se().get_source_id(submod_path.as_ref());
477                let span = span::Span::new(submod_src, 0, 0, Some(source_id)).unwrap();
478                handler.emit_err(CompileError::ImportMustBeLibrary { span });
479                return;
480            }
481
482            let parse_submodule = parsed::ParseSubmodule {
483                module: parse_module,
484                visibility: match submod.visibility {
485                    Some(..) => Visibility::Public,
486                    None => Visibility::Private,
487                },
488                mod_name_span: submod.name.span(),
489            };
490            let lexed_submodule = lexed::LexedSubmodule {
491                module: lexed_module,
492            };
493            let submodule = Submodule {
494                name: submod.name.clone(),
495                path: submod_path,
496                lexed: lexed_submodule,
497                parsed: parse_submodule,
498            };
499            submods.push(submodule);
500        }
501    });
502    submods
503}
504
505pub type SourceHash = u64;
506
507#[derive(Clone, Debug)]
508pub struct ParsedModuleTree {
509    pub tree_type: parsed::TreeType,
510    pub lexed_module: lexed::LexedModule,
511    pub parse_module: parsed::ParseModule,
512}
513
514/// Given the source of the module along with its path,
515/// parse this module including all of its submodules.
516#[allow(clippy::too_many_arguments)]
517fn parse_module_tree(
518    handler: &Handler,
519    engines: &Engines,
520    src: Source,
521    path: Arc<PathBuf>,
522    module_name: Option<&str>,
523    build_target: BuildTarget,
524    dbg_generation: DbgGeneration,
525    include_tests: bool,
526    experimental: ExperimentalFeatures,
527    lsp_mode: Option<&LspConfig>,
528) -> Result<ParsedModuleTree, ErrorEmitted> {
529    let query_engine = engines.qe();
530
531    // Parse this module first.
532    let module_dir = path.parent().expect("module file has no parent directory");
533    let source_id = engines.se().get_source_id(&path.clone());
534    let module = sway_parse::parse_file(handler, src.clone(), Some(source_id), experimental)?;
535
536    // Parse all submodules before converting to the `ParseTree`.
537    // This always recovers on parse errors for the file itself by skipping that file.
538    let submodules = parse_submodules(
539        handler,
540        engines,
541        module_name,
542        &module.value,
543        module_dir,
544        build_target,
545        dbg_generation,
546        include_tests,
547        experimental,
548        lsp_mode,
549    );
550
551    let (attributes_handler, attributes) = attr_decls_to_attributes(
552        &module.attributes,
553        |attr| attr.can_annotate_module_kind(),
554        module.value.kind.friendly_name(),
555    );
556    let attributes_error_emitted = handler.append(attributes_handler);
557
558    // Convert from the raw parsed module to the `ParseTree` ready for type-check.
559    let (kind, tree) = to_parsed_lang::convert_parse_tree(
560        &mut to_parsed_lang::Context::new(build_target, dbg_generation, experimental),
561        handler,
562        engines,
563        module.value.clone(),
564    )?;
565
566    if let Some(err) = attributes_error_emitted {
567        return Err(err);
568    }
569
570    let module_kind_span = module.value.kind.span();
571    let lexed_submodules = submodules
572        .iter()
573        .map(|s| (s.name.clone(), s.lexed.clone()))
574        .collect::<Vec<_>>();
575    let lexed = lexed::LexedModule {
576        tree: module,
577        submodules: lexed_submodules,
578    };
579
580    let mut hasher = DefaultHasher::new();
581    src.text.hash(&mut hasher);
582    let hash = hasher.finish();
583
584    let parsed_submodules = submodules
585        .iter()
586        .map(|s| (s.name.clone(), s.parsed.clone()))
587        .collect::<Vec<_>>();
588    let parsed = parsed::ParseModule {
589        span: span::Span::new(src, 0, 0, Some(source_id)).unwrap(),
590        module_kind_span,
591        module_eval_order: vec![],
592        tree,
593        submodules: parsed_submodules,
594        attributes,
595        hash,
596    };
597
598    // Let's prime the cache with the module dependency and hash data.
599    let modified_time = std::fs::metadata(path.as_path())
600        .ok()
601        .and_then(|m| m.modified().ok());
602    let dependencies = submodules.into_iter().map(|s| s.path).collect::<Vec<_>>();
603    let version = lsp_mode
604        .and_then(|lsp| lsp.file_versions.get(path.as_ref()).copied())
605        .unwrap_or(None);
606
607    let common_info = ModuleCommonInfo {
608        path: path.clone(),
609        include_tests,
610        dependencies,
611        hash,
612    };
613    let parsed_info = ParsedModuleInfo {
614        modified_time,
615        version,
616    };
617    let cache_entry = ModuleCacheEntry::new(common_info, parsed_info);
618    query_engine.update_or_insert_parsed_module_cache_entry(cache_entry);
619
620    Ok(ParsedModuleTree {
621        tree_type: kind,
622        lexed_module: lexed,
623        parse_module: parsed,
624    })
625}
626
627/// Checks if the typed module cache for a given path is up to date.
628///
629/// This function determines whether the cached typed representation of a module
630/// is still valid based on file versions and dependencies.
631///
632/// Note: This functionality is currently only supported when the compiler is
633/// initiated from the language server.
634pub(crate) fn is_ty_module_cache_up_to_date(
635    engines: &Engines,
636    path: &Arc<PathBuf>,
637    include_tests: bool,
638    build_config: Option<&BuildConfig>,
639) -> bool {
640    let cache = engines.qe().module_cache.read();
641    let key = ModuleCacheKey::new(path.clone(), include_tests);
642    cache.get(&key).is_some_and(|entry| {
643        entry.typed.as_ref().is_some_and(|typed| {
644            // Check if the cache is up to date based on file versions
645            let cache_up_to_date = build_config
646                .and_then(|x| x.lsp_mode.as_ref())
647                .and_then(|lsp| lsp.file_versions.get(path.as_ref()))
648                .is_none_or(|version| {
649                    version.is_none_or(|v| typed.version.is_some_and(|tv| v <= tv))
650                });
651
652            // If the cache is up to date, recursively check all dependencies
653            cache_up_to_date
654                && entry.common.dependencies.iter().all(|dep_path| {
655                    is_ty_module_cache_up_to_date(engines, dep_path, include_tests, build_config)
656                })
657        })
658    })
659}
660
661/// Checks if the parsed module cache for a given path is up to date.
662///
663/// This function determines whether the cached parsed representation of a module
664/// is still valid based on file versions, modification times, or content hashes.
665pub(crate) fn is_parse_module_cache_up_to_date(
666    engines: &Engines,
667    path: &Arc<PathBuf>,
668    include_tests: bool,
669    build_config: Option<&BuildConfig>,
670) -> bool {
671    let cache = engines.qe().module_cache.read();
672    let key = ModuleCacheKey::new(path.clone(), include_tests);
673    cache.get(&key).is_some_and(|entry| {
674        // Determine if the cached dependency information is still valid
675        let cache_up_to_date = build_config
676            .and_then(|x| x.lsp_mode.as_ref())
677            .and_then(|lsp| lsp.file_versions.get(path.as_ref()))
678            .map_or_else(
679                || {
680                    // If LSP mode is not active or file version is unavailable, fall back to filesystem checks.
681                    let modified_time = std::fs::metadata(path.as_path())
682                        .ok()
683                        .and_then(|m| m.modified().ok());
684                    // Check if modification time matches, or if not, compare file content hash
685                    entry.parsed.modified_time == modified_time || {
686                        let src = std::fs::read_to_string(path.as_path()).unwrap();
687                        let mut hasher = DefaultHasher::new();
688                        src.hash(&mut hasher);
689                        hasher.finish() == entry.common.hash
690                    }
691                },
692                |version| {
693                    // Determine if the parse cache is up-to-date in LSP mode:
694                    // - If there's no LSP file version (version is None), consider the cache up-to-date.
695                    // - If there is an LSP file version:
696                    //   - If there's no cached version (entry.parsed.version is None), the cache is outdated.
697                    //   - If there's a cached version, compare them: cache is up-to-date if the LSP file version
698                    //     is not greater than the cached version.
699                    version.is_none_or(|v| entry.parsed.version.is_some_and(|ev| v <= ev))
700                },
701            );
702
703        // Checks if the typed module cache for a given path is up to date// If the cache is up to date, recursively check all dependencies to make sure they have not been
704        // modified either.
705        cache_up_to_date
706            && entry.common.dependencies.iter().all(|dep_path| {
707                is_parse_module_cache_up_to_date(engines, dep_path, include_tests, build_config)
708            })
709    })
710}
711
712fn module_path(
713    parent_module_dir: &Path,
714    parent_module_name: Option<&str>,
715    submod: &sway_ast::Submodule,
716) -> PathBuf {
717    if let Some(parent_name) = parent_module_name {
718        parent_module_dir
719            .join(parent_name)
720            .join(submod.name.to_string())
721            .with_extension(sway_types::constants::DEFAULT_FILE_EXTENSION)
722    } else {
723        // top level module
724        parent_module_dir
725            .join(submod.name.to_string())
726            .with_extension(sway_types::constants::DEFAULT_FILE_EXTENSION)
727    }
728}
729
730pub fn build_module_dep_graph(
731    handler: &Handler,
732    parse_module: &mut parsed::ParseModule,
733) -> Result<(), ErrorEmitted> {
734    let module_dep_graph = ty::TyModule::build_dep_graph(handler, parse_module)?;
735    parse_module.module_eval_order = module_dep_graph.compute_order(handler)?;
736
737    for (_, submodule) in &mut parse_module.submodules {
738        build_module_dep_graph(handler, &mut submodule.module)?;
739    }
740    Ok(())
741}
742
743/// A possible occurrence of a `panic` expression that is located in code at [PanicOccurrence::loc].
744///
745/// Note that a single `panic` expression can have multiple [PanicOccurrence]s related to it.
746///
747/// For example:
748/// - `panic "Some message.";` will have just a single occurrence, with `msg` containing the message.
749/// - `panic some_value_of_a_concrete_type;` will have just a single occurrence, with `log_id` containing the [LogId] of the concrete type.
750/// - `panic some_value_of_a_generic_type;` will have multiple occurrences, one with `log_id` for every monomorphized type.
751///
752/// **Every [PanicOccurrence] has exactly one revert code assigned to it.**
753#[derive(Default, Debug, Clone, PartialEq, Eq, Hash)]
754pub struct PanicOccurrence {
755    pub loc: SourceLocation,
756    pub log_id: Option<LogId>,
757    pub msg: Option<String>,
758}
759
760/// [PanicOccurrence]s mapped to their corresponding revert codes.
761pub type PanicOccurrences = HashMap<PanicOccurrence, u64>;
762
763pub struct CompiledAsm {
764    pub finalized_asm: FinalizedAsm,
765    pub panic_occurrences: PanicOccurrences,
766}
767
768#[allow(clippy::too_many_arguments)]
769pub fn parsed_to_ast(
770    handler: &Handler,
771    engines: &Engines,
772    parse_program: &mut parsed::ParseProgram,
773    initial_namespace: namespace::Package,
774    build_config: Option<&BuildConfig>,
775    package_name: &str,
776    retrigger_compilation: Option<Arc<AtomicBool>>,
777    experimental: ExperimentalFeatures,
778) -> Result<ty::TyProgram, TypeCheckFailed> {
779    let lsp_config = build_config.map(|x| x.lsp_mode.clone()).unwrap_or_default();
780
781    // Build the dependency graph for the submodules.
782    build_module_dep_graph(handler, &mut parse_program.root).map_err(|error| TypeCheckFailed {
783        root_module: None,
784        namespace: initial_namespace.clone(),
785        error,
786    })?;
787
788    let collection_namespace = Namespace::new(handler, engines, initial_namespace.clone(), true)
789        .map_err(|error| TypeCheckFailed {
790            root_module: None,
791            namespace: initial_namespace.clone(),
792            error,
793        })?;
794    // Collect the program symbols.
795
796    let mut collection_ctx =
797        ty::TyProgram::collect(handler, engines, parse_program, collection_namespace).map_err(
798            |error| TypeCheckFailed {
799                root_module: None,
800                namespace: initial_namespace.clone(),
801                error,
802            },
803        )?;
804
805    let typecheck_namespace =
806        Namespace::new(handler, engines, initial_namespace, true).map_err(|error| {
807            TypeCheckFailed {
808                root_module: None,
809                namespace: collection_ctx.namespace().current_package_ref().clone(),
810                error,
811            }
812        })?;
813    // Type check the program.
814    let typed_program_opt = ty::TyProgram::type_check(
815        handler,
816        engines,
817        parse_program,
818        &mut collection_ctx,
819        typecheck_namespace,
820        package_name,
821        build_config,
822        experimental,
823    );
824
825    let mut typed_program = typed_program_opt?;
826
827    check_should_abort(handler, retrigger_compilation.clone()).map_err(|error| {
828        TypeCheckFailed {
829            root_module: Some(Arc::new(typed_program.root_module.clone())),
830            namespace: typed_program.namespace.current_package_ref().clone(),
831            error,
832        }
833    })?;
834    // Only clear the parsed AST nodes if we are running a regular compilation pipeline.
835    // LSP needs these to build its token map, and they are cleared by `clear_program` as
836    // part of the LSP garbage collection functionality instead.
837    if lsp_config.is_none() {
838        engines.pe().clear();
839    }
840
841    typed_program.check_deprecated(engines, handler);
842
843    match typed_program.check_recursive(engines, handler) {
844        Ok(()) => {}
845        Err(error) => {
846            handler.dedup();
847            return Err(TypeCheckFailed {
848                root_module: Some(Arc::new(typed_program.root_module.clone())),
849                namespace: typed_program.namespace.current_package().clone(),
850                error,
851            });
852        }
853    };
854
855    // Skip collecting metadata if we triggered an optimised build from LSP.
856    let types_metadata = if !lsp_config.as_ref().is_some_and(|lsp| lsp.optimized_build) {
857        // Collect information about the types used in this program
858        let types_metadata_result = typed_program.collect_types_metadata(
859            handler,
860            &mut CollectTypesMetadataContext::new(engines, experimental, package_name.to_string()),
861        );
862        let types_metadata = match types_metadata_result {
863            Ok(types_metadata) => types_metadata,
864            Err(error) => {
865                handler.dedup();
866                return Err(TypeCheckFailed {
867                    root_module: Some(Arc::new(typed_program.root_module.clone())),
868                    namespace: typed_program.namespace.current_package().clone(),
869                    error,
870                });
871            }
872        };
873
874        typed_program
875            .logged_types
876            .extend(types_metadata.iter().filter_map(|m| match m {
877                TypeMetadata::LoggedType(log_id, type_id) => Some((*log_id, *type_id)),
878                _ => None,
879            }));
880
881        typed_program
882            .messages_types
883            .extend(types_metadata.iter().filter_map(|m| match m {
884                TypeMetadata::MessageType(message_id, type_id) => Some((*message_id, *type_id)),
885                _ => None,
886            }));
887
888        let (print_graph, print_graph_url_format) = match build_config {
889            Some(cfg) => (
890                cfg.print_dca_graph.clone(),
891                cfg.print_dca_graph_url_format.clone(),
892            ),
893            None => (None, None),
894        };
895
896        check_should_abort(handler, retrigger_compilation.clone()).map_err(|error| {
897            TypeCheckFailed {
898                root_module: Some(Arc::new(typed_program.root_module.clone())),
899                namespace: typed_program.namespace.current_package_ref().clone(),
900                error,
901            }
902        })?;
903
904        // Perform control flow analysis and extend with any errors.
905        let _ = perform_control_flow_analysis(
906            handler,
907            engines,
908            &typed_program,
909            print_graph,
910            print_graph_url_format,
911        );
912
913        types_metadata
914    } else {
915        vec![]
916    };
917
918    // Evaluate const declarations, to allow storage slots initialization with consts.
919    let mut ctx = Context::new(engines.se(), experimental);
920    let module = Module::new(&mut ctx, Kind::Contract);
921    if let Err(errs) = ir_generation::compile::compile_constants_for_package(
922        engines,
923        &mut ctx,
924        module,
925        &typed_program.namespace,
926    ) {
927        errs.into_iter().for_each(|err| {
928            handler.emit_err(err.clone());
929        });
930    }
931
932    // CEI pattern analysis
933    let cei_analysis_warnings =
934        semantic_analysis::cei_pattern_analysis::analyze_program(engines, &typed_program);
935    for warn in cei_analysis_warnings {
936        handler.emit_warn(warn);
937    }
938
939    let mut md_mgr = MetadataManager::default();
940    // Check that all storage initializers can be evaluated at compile time.
941    typed_program
942        .get_typed_program_with_initialized_storage_slots(
943            handler,
944            engines,
945            &mut ctx,
946            &mut md_mgr,
947            module,
948        )
949        .map_err(|error: ErrorEmitted| {
950            handler.dedup();
951            TypeCheckFailed {
952                root_module: Some(Arc::new(typed_program.root_module.clone())),
953                namespace: typed_program.namespace.current_package_ref().clone(),
954                error,
955            }
956        })?;
957
958    // All unresolved types lead to compile errors.
959    for err in types_metadata.iter().filter_map(|m| match m {
960        TypeMetadata::UnresolvedType(name, call_site_span_opt) => {
961            Some(CompileError::UnableToInferGeneric {
962                ty: name.as_str().to_string(),
963                span: call_site_span_opt.clone().unwrap_or_else(|| name.span()),
964            })
965        }
966        _ => None,
967    }) {
968        handler.emit_err(err);
969    }
970
971    Ok(typed_program)
972}
973
974#[allow(clippy::too_many_arguments)]
975pub fn compile_to_ast(
976    handler: &Handler,
977    engines: &Engines,
978    src: Source,
979    initial_namespace: namespace::Package,
980    build_config: Option<&BuildConfig>,
981    package_name: &str,
982    retrigger_compilation: Option<Arc<AtomicBool>>,
983    experimental: ExperimentalFeatures,
984) -> Result<Programs, ErrorEmitted> {
985    check_should_abort(handler, retrigger_compilation.clone())?;
986
987    let query_engine = engines.qe();
988    let mut metrics = PerformanceData::default();
989    if let Some(config) = build_config {
990        let path = config.canonical_root_module();
991        let include_tests = config.include_tests;
992        // Check if we can re-use the data in the cache.
993        if is_parse_module_cache_up_to_date(engines, &path, include_tests, build_config) {
994            let mut entry = query_engine.get_programs_cache_entry(&path).unwrap();
995            entry.programs.metrics.reused_programs += 1;
996
997            let (warnings, errors) = entry.handler_data;
998            let new_handler = Handler::from_parts(warnings, errors);
999            handler.append(new_handler);
1000            return Ok(entry.programs);
1001        };
1002    }
1003
1004    // Parse the program to a concrete syntax tree (CST).
1005    let parse_program_opt = time_expr!(
1006        package_name,
1007        "parse the program to a concrete syntax tree (CST)",
1008        "parse_cst",
1009        parse(src, handler, engines, build_config, experimental),
1010        build_config,
1011        metrics
1012    );
1013
1014    check_should_abort(handler, retrigger_compilation.clone())?;
1015
1016    let (lexed_program, mut parsed_program) = match parse_program_opt {
1017        Ok(modules) => modules,
1018        Err(e) => {
1019            handler.dedup();
1020            return Err(e);
1021        }
1022    };
1023
1024    // If tests are not enabled, exclude them from `parsed_program`.
1025    if build_config.is_none_or(|config| !config.include_tests) {
1026        parsed_program.exclude_tests(engines);
1027    }
1028
1029    // Type check (+ other static analysis) the CST to a typed AST.
1030    let program = time_expr!(
1031        package_name,
1032        "parse the concrete syntax tree (CST) to a typed AST",
1033        "parse_ast",
1034        parsed_to_ast(
1035            handler,
1036            engines,
1037            &mut parsed_program,
1038            initial_namespace,
1039            build_config,
1040            package_name,
1041            retrigger_compilation.clone(),
1042            experimental
1043        ),
1044        build_config,
1045        metrics
1046    );
1047
1048    check_should_abort(handler, retrigger_compilation.clone())?;
1049
1050    handler.dedup();
1051
1052    let programs = Programs::new(
1053        Arc::new(lexed_program),
1054        Arc::new(parsed_program),
1055        program.map(Arc::new),
1056        metrics,
1057    );
1058
1059    if let Some(config) = build_config {
1060        let path = config.canonical_root_module();
1061        let cache_entry = ProgramsCacheEntry {
1062            path,
1063            programs: programs.clone(),
1064            handler_data: handler.clone().consume(),
1065        };
1066        query_engine.insert_programs_cache_entry(cache_entry);
1067    }
1068
1069    check_should_abort(handler, retrigger_compilation.clone())?;
1070
1071    Ok(programs)
1072}
1073
1074/// Given input Sway source code, try compiling to a `CompiledAsm`,
1075/// containing the asm in opcode form (not raw bytes/bytecode).
1076pub fn compile_to_asm(
1077    handler: &Handler,
1078    engines: &Engines,
1079    src: Source,
1080    initial_namespace: namespace::Package,
1081    build_config: &BuildConfig,
1082    package_name: &str,
1083    experimental: ExperimentalFeatures,
1084) -> Result<CompiledAsm, ErrorEmitted> {
1085    let ast_res = compile_to_ast(
1086        handler,
1087        engines,
1088        src,
1089        initial_namespace,
1090        Some(build_config),
1091        package_name,
1092        None,
1093        experimental,
1094    )?;
1095
1096    ast_to_asm(handler, engines, &ast_res, build_config, experimental)
1097}
1098
1099/// Given an AST compilation result, try compiling to a `CompiledAsm`,
1100/// containing the asm in opcode form (not raw bytes/bytecode).
1101pub fn ast_to_asm(
1102    handler: &Handler,
1103    engines: &Engines,
1104    programs: &Programs,
1105    build_config: &BuildConfig,
1106    experimental: ExperimentalFeatures,
1107) -> Result<CompiledAsm, ErrorEmitted> {
1108    let typed_program = match &programs.typed {
1109        Ok(typed_program) => typed_program,
1110        Err(err) => return Err(err.error),
1111    };
1112
1113    let mut panic_occurrences = PanicOccurrences::default();
1114
1115    let asm = match compile_ast_to_ir_to_asm(
1116        handler,
1117        engines,
1118        typed_program,
1119        &mut panic_occurrences,
1120        build_config,
1121        experimental,
1122    ) {
1123        Ok(res) => res,
1124        Err(err) => {
1125            handler.dedup();
1126            return Err(err);
1127        }
1128    };
1129
1130    Ok(CompiledAsm {
1131        finalized_asm: asm,
1132        panic_occurrences,
1133    })
1134}
1135
1136pub(crate) fn compile_ast_to_ir_to_asm(
1137    handler: &Handler,
1138    engines: &Engines,
1139    program: &ty::TyProgram,
1140    panic_occurrences: &mut PanicOccurrences,
1141    build_config: &BuildConfig,
1142    experimental: ExperimentalFeatures,
1143) -> Result<FinalizedAsm, ErrorEmitted> {
1144    // The IR pipeline relies on type information being fully resolved.
1145    // If type information is found to still be generic or unresolved inside of
1146    // IR, this is considered an internal compiler error. To resolve this situation,
1147    // we need to explicitly ensure all types are resolved before going into IR.
1148    //
1149    // We _could_ introduce a new type here that uses TypeInfo instead of TypeId and throw away
1150    // the engine, since we don't need inference for IR. That'd be a _lot_ of copy-pasted code,
1151    // though, so instead, we are just going to do a pass and throw any unresolved generics as
1152    // errors and then hold as a runtime invariant that none of the types will be unresolved in the
1153    // IR phase.
1154
1155    let mut ir = match ir_generation::compile_program(
1156        program,
1157        panic_occurrences,
1158        build_config.include_tests,
1159        engines,
1160        experimental,
1161    ) {
1162        Ok(ir) => ir,
1163        Err(errors) => {
1164            let mut last = None;
1165            for e in errors {
1166                last = Some(handler.emit_err(e));
1167            }
1168            return Err(last.unwrap());
1169        }
1170    };
1171
1172    // Find all the entry points for purity checking and DCE.
1173    let entry_point_functions: Vec<::sway_ir::Function> = ir
1174        .module_iter()
1175        .flat_map(|module| module.function_iter(&ir))
1176        .filter(|func| func.is_entry(&ir))
1177        .collect();
1178
1179    // Do a purity check on the _unoptimised_ IR.
1180    {
1181        let mut env = ir_generation::PurityEnv::default();
1182        let mut md_mgr = metadata::MetadataManager::default();
1183        for entry_point in &entry_point_functions {
1184            check_function_purity(handler, &mut env, &ir, &mut md_mgr, entry_point);
1185        }
1186    }
1187
1188    // Initialize the pass manager and register known passes.
1189    let mut pass_mgr = PassManager::default();
1190    register_known_passes(&mut pass_mgr);
1191    let mut pass_group = PassGroup::default();
1192
1193    match build_config.optimization_level {
1194        OptLevel::Opt1 => {
1195            pass_group.append_group(create_o1_pass_group());
1196        }
1197        OptLevel::Opt0 => {
1198            // We run a function deduplication pass that only removes duplicate
1199            // functions when everything, including the metadata are identical.
1200            pass_group.append_pass(FN_DEDUP_DEBUG_PROFILE_NAME);
1201
1202            // Inlining is necessary until #4899 is resolved.
1203            pass_group.append_pass(FN_INLINE_NAME);
1204
1205            // Do DCE so other optimizations run faster.
1206            pass_group.append_pass(GLOBALS_DCE_NAME);
1207            pass_group.append_pass(DCE_NAME);
1208        }
1209    }
1210
1211    // Target specific transforms should be moved into something more configured.
1212    if build_config.build_target == BuildTarget::Fuel {
1213        // FuelVM target specific transforms.
1214        //
1215        // Demote large by-value constants, arguments and return values to by-reference values
1216        // using temporaries.
1217        pass_group.append_pass(CONST_DEMOTION_NAME);
1218        pass_group.append_pass(ARG_DEMOTION_NAME);
1219        pass_group.append_pass(RET_DEMOTION_NAME);
1220        pass_group.append_pass(MISC_DEMOTION_NAME);
1221
1222        // Convert loads and stores to mem_copies where possible.
1223        pass_group.append_pass(MEMCPYOPT_NAME);
1224
1225        // Run a DCE and simplify-cfg to clean up any obsolete instructions.
1226        pass_group.append_pass(DCE_NAME);
1227        pass_group.append_pass(SIMPLIFY_CFG_NAME);
1228
1229        match build_config.optimization_level {
1230            OptLevel::Opt1 => {
1231                pass_group.append_pass(SROA_NAME);
1232                pass_group.append_pass(MEM2REG_NAME);
1233                pass_group.append_pass(DCE_NAME);
1234            }
1235            OptLevel::Opt0 => {}
1236        }
1237    }
1238
1239    // Run the passes.
1240    let print_passes_opts: PrintPassesOpts = (&build_config.print_ir).into();
1241    let res =
1242        if let Err(ir_error) = pass_mgr.run_with_print(&mut ir, &pass_group, &print_passes_opts) {
1243            Err(handler.emit_err(CompileError::InternalOwned(
1244                ir_error.to_string(),
1245                span::Span::dummy(),
1246            )))
1247        } else {
1248            Ok(())
1249        };
1250    res?;
1251
1252    compile_ir_context_to_finalized_asm(handler, &ir, Some(build_config))
1253}
1254
1255/// Given input Sway source code, compile to [CompiledBytecode], containing the asm in bytecode form.
1256#[allow(clippy::too_many_arguments)]
1257pub fn compile_to_bytecode(
1258    handler: &Handler,
1259    engines: &Engines,
1260    src: Source,
1261    initial_namespace: namespace::Package,
1262    build_config: &BuildConfig,
1263    source_map: &mut SourceMap,
1264    package_name: &str,
1265    experimental: ExperimentalFeatures,
1266) -> Result<CompiledBytecode, ErrorEmitted> {
1267    let mut asm_res = compile_to_asm(
1268        handler,
1269        engines,
1270        src,
1271        initial_namespace,
1272        build_config,
1273        package_name,
1274        experimental,
1275    )?;
1276    asm_to_bytecode(
1277        handler,
1278        &mut asm_res,
1279        source_map,
1280        engines.se(),
1281        build_config,
1282    )
1283}
1284
1285/// Size of the prelude's CONFIGURABLES_OFFSET section, in bytes.
1286pub const PRELUDE_CONFIGURABLES_SIZE_IN_BYTES: usize = 8;
1287/// Offset (in bytes) of the CONFIGURABLES_OFFSET section in the prelude.
1288pub const PRELUDE_CONFIGURABLES_OFFSET_IN_BYTES: usize = 16;
1289/// Total size of the prelude in bytes. Instructions start right after.
1290pub const PRELUDE_SIZE_IN_BYTES: usize = 32;
1291
1292/// Given bytecode, overwrite the existing offset to configurables offset in the prelude with the given one.
1293pub fn set_bytecode_configurables_offset(
1294    compiled_bytecode: &mut CompiledBytecode,
1295    md: &[u8; PRELUDE_CONFIGURABLES_SIZE_IN_BYTES],
1296) {
1297    assert!(
1298        compiled_bytecode.bytecode.len()
1299            >= PRELUDE_CONFIGURABLES_OFFSET_IN_BYTES + PRELUDE_CONFIGURABLES_SIZE_IN_BYTES
1300    );
1301    let code = &mut compiled_bytecode.bytecode;
1302    for (index, byte) in md.iter().enumerate() {
1303        code[index + PRELUDE_CONFIGURABLES_OFFSET_IN_BYTES] = *byte;
1304    }
1305}
1306
1307/// Given the assembly (opcodes), compile to [CompiledBytecode], containing the asm in bytecode form.
1308pub fn asm_to_bytecode(
1309    handler: &Handler,
1310    asm: &mut CompiledAsm,
1311    source_map: &mut SourceMap,
1312    source_engine: &SourceEngine,
1313    build_config: &BuildConfig,
1314) -> Result<CompiledBytecode, ErrorEmitted> {
1315    let compiled_bytecode =
1316        asm.finalized_asm
1317            .to_bytecode_mut(handler, source_map, source_engine, build_config)?;
1318    Ok(compiled_bytecode)
1319}
1320
1321/// Given a [ty::TyProgram], which is type-checked Sway source, construct a graph to analyze
1322/// control flow and determine if it is valid.
1323fn perform_control_flow_analysis(
1324    handler: &Handler,
1325    engines: &Engines,
1326    program: &ty::TyProgram,
1327    print_graph: Option<String>,
1328    print_graph_url_format: Option<String>,
1329) -> Result<(), ErrorEmitted> {
1330    let dca_res = dead_code_analysis(handler, engines, program);
1331    let rpa_errors = return_path_analysis(engines, program);
1332    let rpa_res = handler.scope(|handler| {
1333        for err in rpa_errors {
1334            handler.emit_err(err);
1335        }
1336        Ok(())
1337    });
1338
1339    if let Ok(graph) = dca_res.clone() {
1340        graph.visualize(engines, print_graph, print_graph_url_format);
1341    }
1342    dca_res?;
1343    rpa_res
1344}
1345
1346/// Constructs a dead code graph from all modules within the graph and then attempts to find dead
1347/// code.
1348///
1349/// Returns the graph that was used for analysis.
1350fn dead_code_analysis<'a>(
1351    handler: &Handler,
1352    engines: &'a Engines,
1353    program: &ty::TyProgram,
1354) -> Result<ControlFlowGraph<'a>, ErrorEmitted> {
1355    let decl_engine = engines.de();
1356    let mut dead_code_graph = ControlFlowGraph::new(engines);
1357    let tree_type = program.kind.tree_type();
1358    module_dead_code_analysis(
1359        handler,
1360        engines,
1361        &program.root_module,
1362        &tree_type,
1363        &mut dead_code_graph,
1364    )?;
1365    let warnings = dead_code_graph.find_dead_code(decl_engine);
1366    for warn in warnings {
1367        handler.emit_warn(warn);
1368    }
1369    Ok(dead_code_graph)
1370}
1371
1372/// Recursively collect modules into the given `ControlFlowGraph` ready for dead code analysis.
1373fn module_dead_code_analysis<'eng: 'cfg, 'cfg>(
1374    handler: &Handler,
1375    engines: &'eng Engines,
1376    module: &ty::TyModule,
1377    tree_type: &parsed::TreeType,
1378    graph: &mut ControlFlowGraph<'cfg>,
1379) -> Result<(), ErrorEmitted> {
1380    module
1381        .submodules
1382        .iter()
1383        .try_fold((), |(), (_, submodule)| {
1384            let tree_type = parsed::TreeType::Library;
1385            module_dead_code_analysis(handler, engines, &submodule.module, &tree_type, graph)
1386        })?;
1387    let res = {
1388        ControlFlowGraph::append_module_to_dead_code_graph(
1389            engines,
1390            &module.all_nodes,
1391            tree_type,
1392            graph,
1393        )
1394        .map_err(|err| handler.emit_err(err))
1395    };
1396    graph.connect_pending_entry_edges();
1397    res
1398}
1399
1400fn return_path_analysis(engines: &Engines, program: &ty::TyProgram) -> Vec<CompileError> {
1401    let mut errors = vec![];
1402    module_return_path_analysis(engines, &program.root_module, &mut errors);
1403    errors
1404}
1405
1406fn module_return_path_analysis(
1407    engines: &Engines,
1408    module: &ty::TyModule,
1409    errors: &mut Vec<CompileError>,
1410) {
1411    for (_, submodule) in &module.submodules {
1412        module_return_path_analysis(engines, &submodule.module, errors);
1413    }
1414    let graph = ControlFlowGraph::construct_return_path_graph(engines, &module.all_nodes);
1415    match graph {
1416        Ok(graph) => errors.extend(graph.analyze_return_paths(engines)),
1417        Err(mut error) => errors.append(&mut error),
1418    }
1419}
1420
1421/// Check if the retrigger compilation flag has been set to true in the language server.
1422/// If it has, there is a new compilation request, so we should abort the current compilation.
1423fn check_should_abort(
1424    handler: &Handler,
1425    retrigger_compilation: Option<Arc<AtomicBool>>,
1426) -> Result<(), ErrorEmitted> {
1427    if let Some(ref retrigger_compilation) = retrigger_compilation {
1428        if retrigger_compilation.load(Ordering::SeqCst) {
1429            return Err(handler.cancel());
1430        }
1431    }
1432    Ok(())
1433}
1434
1435#[test]
1436fn test_basic_prog() {
1437    let handler = Handler::default();
1438    let engines = Engines::default();
1439    let prog = parse(
1440        r#"
1441        contract;
1442
1443    enum yo
1444    <T>
1445    where
1446    T: IsAThing
1447    {
1448        x: u32,
1449        y: MyStruct<u32>
1450    }
1451
1452    enum  MyOtherSumType
1453    {
1454        x: u32,
1455        y: MyStruct<u32>
1456    }
1457        struct MyStruct<T> {
1458            field_name: u64,
1459            other_field: T,
1460        }
1461
1462
1463    fn generic_function
1464    <T>
1465    (arg1: u64,
1466    arg2: T)
1467    ->
1468    T
1469    where T: Display,
1470          T: Debug {
1471          let x: MyStruct =
1472          MyStruct
1473          {
1474              field_name:
1475              5
1476          };
1477          return
1478          match
1479            arg1
1480          {
1481               1
1482               => true,
1483               _ => { return false; },
1484          };
1485    }
1486
1487    struct MyStruct {
1488        test: string,
1489    }
1490
1491
1492
1493    use stdlib::println;
1494
1495    trait MyTrait {
1496        // interface points
1497        fn myfunc(x: int) -> unit;
1498        } {
1499        // methods
1500        fn calls_interface_fn(x: int) -> unit {
1501            // declare a byte
1502            let x = 0b10101111;
1503            let mut y = 0b11111111;
1504            self.interface_fn(x);
1505        }
1506    }
1507
1508    pub fn prints_number_five() -> u8 {
1509        let x: u8 = 5;
1510        println(x);
1511         x.to_string();
1512         let some_list = [
1513         5,
1514         10 + 3 / 2,
1515         func_app(my_args, (so_many_args))];
1516        return 5;
1517    }
1518    "#
1519        .into(),
1520        &handler,
1521        &engines,
1522        None,
1523        ExperimentalFeatures::default(),
1524    );
1525    prog.unwrap();
1526}
1527#[test]
1528fn test_parenthesized() {
1529    let handler = Handler::default();
1530    let engines = Engines::default();
1531    let prog = parse(
1532        r#"
1533        contract;
1534        pub fn some_abi_func() -> unit {
1535            let x = (5 + 6 / (1 + (2 / 1) + 4));
1536            return;
1537        }
1538    "#
1539        .into(),
1540        &handler,
1541        &engines,
1542        None,
1543        ExperimentalFeatures::default(),
1544    );
1545    prog.unwrap();
1546}
1547
1548#[test]
1549fn test_unary_ordering() {
1550    use crate::language::{self, parsed};
1551    let handler = Handler::default();
1552    let engines = Engines::default();
1553    let prog = parse(
1554        r#"
1555    script;
1556    fn main() -> bool {
1557        let a = true;
1558        let b = true;
1559        !a && b;
1560    }"#
1561        .into(),
1562        &handler,
1563        &engines,
1564        None,
1565        ExperimentalFeatures::default(),
1566    );
1567    let (.., prog) = prog.unwrap();
1568    // this should parse as `(!a) && b`, not `!(a && b)`. So, the top level
1569    // expression should be `&&`
1570    if let parsed::AstNode {
1571        content:
1572            parsed::AstNodeContent::Declaration(parsed::Declaration::FunctionDeclaration(decl_id)),
1573        ..
1574    } = &prog.root.tree.root_nodes[0]
1575    {
1576        let fn_decl = engines.pe().get_function(decl_id);
1577        if let parsed::AstNode {
1578            content:
1579                parsed::AstNodeContent::Expression(parsed::Expression {
1580                    kind:
1581                        parsed::ExpressionKind::LazyOperator(parsed::LazyOperatorExpression {
1582                            op, ..
1583                        }),
1584                    ..
1585                }),
1586            ..
1587        } = &fn_decl.body.contents[2]
1588        {
1589            assert_eq!(op, &language::LazyOp::And)
1590        } else {
1591            panic!("Was not lazy operator.")
1592        }
1593    } else {
1594        panic!("Was not ast node")
1595    };
1596}
1597
1598#[test]
1599fn test_parser_recovery() {
1600    let handler = Handler::default();
1601    let engines = Engines::default();
1602    let prog = parse(
1603        r#"
1604    script;
1605    fn main() -> bool {
1606        let
1607        let a = true;
1608        true
1609    }"#
1610        .into(),
1611        &handler,
1612        &engines,
1613        None,
1614        ExperimentalFeatures::default(),
1615    );
1616    let (_, _) = prog.unwrap();
1617    assert!(handler.has_errors());
1618    dbg!(handler);
1619}