From 60cb13c493221cf3beac28da20b18a82fbadf873 Mon Sep 17 00:00:00 2001 From: Wind Date: Sat, 24 May 2025 03:30:21 +0800 Subject: [PATCH] `source`: make sure the block is compiled when parsing (#15798) # Description Fixes: https://github.com/nushell/nushell/issues/15749 When sourcing a file, the ir block might be empty because it has been used before, this pr is going to make sure that the ir block is compiled. # User-Facing Changes ``` touch aaa.nu use aaa.nu source aaa.nu ``` Will no longer raise an error. # Tests + Formatting Added 1 test # After Submitting NaN --- .../nu-command/tests/commands/source_env.rs | 24 ++++++++++++++++++- crates/nu-parser/src/parse_keywords.rs | 8 +++++-- crates/nu-parser/src/parser.rs | 2 +- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/crates/nu-command/tests/commands/source_env.rs b/crates/nu-command/tests/commands/source_env.rs index a293c6ed92..7a40c80cb8 100644 --- a/crates/nu-command/tests/commands/source_env.rs +++ b/crates/nu-command/tests/commands/source_env.rs @@ -1,4 +1,4 @@ -use nu_test_support::fs::Stub::{FileWithContent, FileWithContentToBeTrimmed}; +use nu_test_support::fs::Stub::{EmptyFile, FileWithContent, FileWithContentToBeTrimmed}; use nu_test_support::nu; use nu_test_support::pipeline; use nu_test_support::playground::Playground; @@ -334,3 +334,25 @@ fn source_respects_early_return() { assert!(actual.err.is_empty()); } + +#[test] +fn source_after_use_should_not_error() { + Playground::setup("source_after_use", |dirs, sandbox| { + sandbox.with_files(&[EmptyFile("spam.nu")]); + + let inp = &[r#"use spam.nu"#, r#"source spam.nu"#]; + let actual = nu!(cwd: dirs.test(), &inp.join("; ")); + assert!(actual.err.is_empty()); + }) +} + +#[test] +fn use_after_source_should_not_error() { + Playground::setup("use_after_source", |dirs, sandbox| { + sandbox.with_files(&[EmptyFile("spam.nu")]); + + let inp = &[r#"source spam.nu"#, r#"use spam.nu"#]; + let actual = nu!(cwd: dirs.test(), &inp.join("; ")); + assert!(actual.err.is_empty()); + }) +} diff --git a/crates/nu-parser/src/parse_keywords.rs b/crates/nu-parser/src/parse_keywords.rs index 42cf330b6b..7f8416cbd3 100644 --- a/crates/nu-parser/src/parse_keywords.rs +++ b/crates/nu-parser/src/parse_keywords.rs @@ -1,7 +1,7 @@ use crate::{ exportable::Exportable, parse_block, - parser::{parse_attribute, parse_redirection, redirecting_builtin_error}, + parser::{compile_block, parse_attribute, parse_redirection, redirecting_builtin_error}, type_check::{check_block_input_output, type_compatible}, }; use itertools::Itertools; @@ -3805,12 +3805,16 @@ pub fn parse_source(working_set: &mut StateWorkingSet, lite_command: &LiteComman // This will load the defs from the file into the // working set, if it was a successful parse. - let block = parse( + let mut block = parse( working_set, Some(&path.path().to_string_lossy()), &contents, scoped, ); + if block.ir_block.is_none() { + let block_mut = Arc::make_mut(&mut block); + compile_block(working_set, block_mut); + } // Remove the file from the stack of files being processed. working_set.files.pop(); diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index 49fcdfb445..ef72e8a104 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -6496,7 +6496,7 @@ pub fn parse_block( } /// Compile an IR block for the `Block`, adding a compile error on failure -fn compile_block(working_set: &mut StateWorkingSet<'_>, block: &mut Block) { +pub fn compile_block(working_set: &mut StateWorkingSet<'_>, block: &mut Block) { match nu_engine::compile(working_set, block) { Ok(ir_block) => { block.ir_block = Some(ir_block);