forked from extern/nushell
Fix circular source causing Nushell to crash (#12262)
# Description EngineState now tracks the script currently running, instead of the parent directory of the script. This also provides an easy way to expose the current running script to the user (Issue #12195). Similarly, StateWorkingSet now tracks scripts instead of directories. `parsed_module_files` and `currently_parsed_pwd` are merged into one variable, `scripts`, which acts like a stack for tracking the current running script (which is on the top of the stack). Circular import check is added for `source` operations, in addition to module import. A simple testcase is added for circular source. <!-- if this PR closes one or more issues, you can automatically link the PR with them by using one of the [*linking keywords*](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword), e.g. - this PR should close #xxxx - fixes #xxxx you can also mention related issues, PRs or discussions! --> <!-- Thank you for improving Nushell. Please, check our [contributing guide](../CONTRIBUTING.md) and talk to the core team before making major changes. Description of your pull request goes here. **Provide examples and/or screenshots** if your changes affect the user experience. --> # User-Facing Changes <!-- List of all changes that impact the user experience here. This helps us keep track of breaking changes. --> It shouldn't have any user facing changes.
This commit is contained in:
@ -1871,32 +1871,16 @@ pub fn parse_module_block(
|
||||
(block, module, module_comments)
|
||||
}
|
||||
|
||||
/// Parse a module from a file.
|
||||
///
|
||||
/// The module name is inferred from the stem of the file, unless specified in `name_override`.
|
||||
fn parse_module_file(
|
||||
working_set: &mut StateWorkingSet,
|
||||
path: ParserPath,
|
||||
path_span: Span,
|
||||
name_override: Option<String>,
|
||||
) -> Option<ModuleId> {
|
||||
if let Some(i) = working_set
|
||||
.parsed_module_files
|
||||
.iter()
|
||||
.rposition(|p| p == path.path())
|
||||
{
|
||||
let mut files: Vec<String> = working_set
|
||||
.parsed_module_files
|
||||
.split_off(i)
|
||||
.iter()
|
||||
.map(|p| p.to_string_lossy().to_string())
|
||||
.collect();
|
||||
|
||||
files.push(path.path().to_string_lossy().to_string());
|
||||
|
||||
let msg = files.join("\nuses ");
|
||||
|
||||
working_set.error(ParseError::CyclicalModuleImport(msg, path_span));
|
||||
return None;
|
||||
}
|
||||
|
||||
// Infer the module name from the stem of the file, unless overridden.
|
||||
let module_name = if let Some(name) = name_override {
|
||||
name
|
||||
} else if let Some(stem) = path.file_stem() {
|
||||
@ -1909,6 +1893,7 @@ fn parse_module_file(
|
||||
return None;
|
||||
};
|
||||
|
||||
// Read the content of the module.
|
||||
let contents = if let Some(contents) = path.read(working_set) {
|
||||
contents
|
||||
} else {
|
||||
@ -1922,29 +1907,23 @@ fn parse_module_file(
|
||||
let file_id = working_set.add_file(path.path().to_string_lossy().to_string(), &contents);
|
||||
let new_span = working_set.get_span_for_file(file_id);
|
||||
|
||||
// Check if we've parsed the module before.
|
||||
if let Some(module_id) = working_set.find_module_by_span(new_span) {
|
||||
return Some(module_id);
|
||||
}
|
||||
|
||||
// Change the currently parsed directory
|
||||
let prev_currently_parsed_cwd = if let Some(parent) = path.parent() {
|
||||
working_set.currently_parsed_cwd.replace(parent.into())
|
||||
} else {
|
||||
working_set.currently_parsed_cwd.clone()
|
||||
};
|
||||
|
||||
// Add the file to the stack of parsed module files
|
||||
working_set.parsed_module_files.push(path.path_buf());
|
||||
// Add the file to the stack of files being processed.
|
||||
if let Err(e) = working_set.files.push(path.path_buf(), path_span) {
|
||||
working_set.error(e);
|
||||
return None;
|
||||
}
|
||||
|
||||
// Parse the module
|
||||
let (block, module, module_comments) =
|
||||
parse_module_block(working_set, new_span, module_name.as_bytes());
|
||||
|
||||
// Remove the file from the stack of parsed module files
|
||||
working_set.parsed_module_files.pop();
|
||||
|
||||
// Restore the currently parsed directory back
|
||||
working_set.currently_parsed_cwd = prev_currently_parsed_cwd;
|
||||
// Remove the file from the stack of files being processed.
|
||||
working_set.files.pop();
|
||||
|
||||
let _ = working_set.add_block(Arc::new(block));
|
||||
let module_id = working_set.add_module(&module_name, module, module_comments);
|
||||
@ -3425,12 +3404,11 @@ pub fn parse_source(working_set: &mut StateWorkingSet, lite_command: &LiteComman
|
||||
|
||||
if let Some(path) = find_in_dirs(&filename, working_set, &cwd, LIB_DIRS_VAR) {
|
||||
if let Some(contents) = path.read(working_set) {
|
||||
// Change currently parsed directory
|
||||
let prev_currently_parsed_cwd = if let Some(parent) = path.parent() {
|
||||
working_set.currently_parsed_cwd.replace(parent.into())
|
||||
} else {
|
||||
working_set.currently_parsed_cwd.clone()
|
||||
};
|
||||
// Add the file to the stack of files being processed.
|
||||
if let Err(e) = working_set.files.push(path.clone().path_buf(), spans[1]) {
|
||||
working_set.error(e);
|
||||
return garbage_pipeline(spans);
|
||||
}
|
||||
|
||||
// This will load the defs from the file into the
|
||||
// working set, if it was a successful parse.
|
||||
@ -3441,8 +3419,8 @@ pub fn parse_source(working_set: &mut StateWorkingSet, lite_command: &LiteComman
|
||||
scoped,
|
||||
);
|
||||
|
||||
// Restore the currently parsed directory back
|
||||
working_set.currently_parsed_cwd = prev_currently_parsed_cwd;
|
||||
// Remove the file from the stack of files being processed.
|
||||
working_set.files.pop();
|
||||
|
||||
// Save the block into the working set
|
||||
let block_id = working_set.add_block(block);
|
||||
@ -3831,11 +3809,10 @@ pub fn find_in_dirs(
|
||||
dirs_var_name: &str,
|
||||
) -> Option<ParserPath> {
|
||||
// Choose whether to use file-relative or PWD-relative path
|
||||
let actual_cwd = if let Some(currently_parsed_cwd) = &working_set.currently_parsed_cwd {
|
||||
currently_parsed_cwd.as_path()
|
||||
} else {
|
||||
Path::new(cwd)
|
||||
};
|
||||
let actual_cwd = working_set
|
||||
.files
|
||||
.current_working_directory()
|
||||
.unwrap_or(Path::new(cwd));
|
||||
|
||||
// Try if we have an existing virtual path
|
||||
if let Some(virtual_path) = working_set.find_virtual_path(filename) {
|
||||
@ -3895,11 +3872,10 @@ pub fn find_in_dirs(
|
||||
dirs_env: &str,
|
||||
) -> Option<PathBuf> {
|
||||
// Choose whether to use file-relative or PWD-relative path
|
||||
let actual_cwd = if let Some(currently_parsed_cwd) = &working_set.currently_parsed_cwd {
|
||||
currently_parsed_cwd.as_path()
|
||||
} else {
|
||||
Path::new(cwd)
|
||||
};
|
||||
let actual_cwd = working_set
|
||||
.files
|
||||
.current_working_directory()
|
||||
.unwrap_or(Path::new(cwd));
|
||||
|
||||
if let Ok(p) = canonicalize_with(filename, actual_cwd) {
|
||||
Some(p)
|
||||
|
Reference in New Issue
Block a user