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