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
This commit is contained in:
Wind 2025-05-24 03:30:21 +08:00 committed by GitHub
parent c10e483683
commit 60cb13c493
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 30 additions and 4 deletions

View File

@ -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::nu;
use nu_test_support::pipeline; use nu_test_support::pipeline;
use nu_test_support::playground::Playground; use nu_test_support::playground::Playground;
@ -334,3 +334,25 @@ fn source_respects_early_return() {
assert!(actual.err.is_empty()); 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());
})
}

View File

@ -1,7 +1,7 @@
use crate::{ use crate::{
exportable::Exportable, exportable::Exportable,
parse_block, 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}, type_check::{check_block_input_output, type_compatible},
}; };
use itertools::Itertools; 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 // This will load the defs from the file into the
// working set, if it was a successful parse. // working set, if it was a successful parse.
let block = parse( let mut block = parse(
working_set, working_set,
Some(&path.path().to_string_lossy()), Some(&path.path().to_string_lossy()),
&contents, &contents,
scoped, 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. // Remove the file from the stack of files being processed.
working_set.files.pop(); working_set.files.pop();

View File

@ -6496,7 +6496,7 @@ pub fn parse_block(
} }
/// Compile an IR block for the `Block`, adding a compile error on failure /// 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) { match nu_engine::compile(working_set, block) {
Ok(ir_block) => { Ok(ir_block) => {
block.ir_block = Some(ir_block); block.ir_block = Some(ir_block);