diff --git a/crates/nu-command/src/core_commands/hide.rs b/crates/nu-command/src/core_commands/hide.rs index f8eecad99d..631f381e6c 100644 --- a/crates/nu-command/src/core_commands/hide.rs +++ b/crates/nu-command/src/core_commands/hide.rs @@ -17,11 +17,11 @@ impl Command for Hide { } fn usage(&self) -> &str { - "Hide definitions in the current scope" + "Hide symbols in the current scope" } fn extra_usage(&self) -> &str { - "If there is a definition and an environment variable with the same name in the current scope, first the definition will be hidden, then the environment variable." + "Symbols are hidden by priority: First aliases, then custom commands, then environment variables." } fn run( @@ -67,7 +67,7 @@ impl Command for Hide { overlay.env_var_with_head(name, &import_pattern.head.name) { output.push((name, id)); - } else if !overlay.has_decl(name) { + } else if !(overlay.has_alias(name) || overlay.has_decl(name)) { return Err(ShellError::EnvVarNotFoundAtRuntime( String::from_utf8_lossy(name).into(), *span, @@ -84,7 +84,7 @@ impl Command for Hide { overlay.env_var_with_head(name, &import_pattern.head.name) { output.push((name, id)); - } else if !overlay.has_decl(name) { + } else if !(overlay.has_alias(name) || overlay.has_decl(name)) { return Err(ShellError::EnvVarNotFoundAtRuntime( String::from_utf8_lossy(name).into(), *span, diff --git a/crates/nu-command/src/system/which_.rs b/crates/nu-command/src/system/which_.rs index 529a38f1d3..973d4a33c5 100644 --- a/crates/nu-command/src/system/which_.rs +++ b/crates/nu-command/src/system/which_.rs @@ -82,9 +82,9 @@ fn get_entries_in_aliases(engine_state: &EngineState, name: &str, span: Span) -> .into_iter() .map(|spans| { spans - .into_iter() + .iter() .map(|span| { - String::from_utf8_lossy(engine_state.get_span_contents(&span)).to_string() + String::from_utf8_lossy(engine_state.get_span_contents(span)).to_string() }) .join(" ") }) diff --git a/crates/nu-engine/src/eval.rs b/crates/nu-engine/src/eval.rs index 3b9461cc7d..535435dc8c 100644 --- a/crates/nu-engine/src/eval.rs +++ b/crates/nu-engine/src/eval.rs @@ -945,9 +945,10 @@ pub fn eval_variable( commands.push(Value::Record { cols, vals, span }) } - for alias in &frame.aliases { + for (alias_name, alias_id) in &frame.aliases { + let alias = engine_state.get_alias(*alias_id); let mut alias_text = String::new(); - for span in alias.1 { + for span in alias { let contents = engine_state.get_span_contents(span); if !alias_text.is_empty() { alias_text.push(' '); @@ -956,7 +957,7 @@ pub fn eval_variable( } aliases.push(( Value::String { - val: String::from_utf8_lossy(alias.0).to_string(), + val: String::from_utf8_lossy(alias_name).to_string(), span, }, Value::string(alias_text, span), diff --git a/crates/nu-parser/src/parse_keywords.rs b/crates/nu-parser/src/parse_keywords.rs index 474329caa3..69d2cefa96 100644 --- a/crates/nu-parser/src/parse_keywords.rs +++ b/crates/nu-parser/src/parse_keywords.rs @@ -1320,14 +1320,21 @@ pub fn parse_hide( if let Some(overlay_id) = working_set.find_overlay(&import_pattern.head.name) { (true, working_set.get_overlay(overlay_id).clone()) } else if import_pattern.members.is_empty() { - // The pattern head can be e.g. a function name, not just a module - if let Some(id) = working_set.find_decl(&import_pattern.head.name) { + // The pattern head can be: + if let Some(id) = working_set.find_alias(&import_pattern.head.name) { + // an alias, + let mut overlay = Overlay::new(); + overlay.add_alias(&import_pattern.head.name, id); + + (false, overlay) + } else if let Some(id) = working_set.find_decl(&import_pattern.head.name) { + // a custom command, let mut overlay = Overlay::new(); overlay.add_decl(&import_pattern.head.name, id); (false, overlay) } else { - // Or it could be an env var + // , or it could be an env var (handled by the engine) (false, Overlay::new()) } } else { @@ -1338,49 +1345,71 @@ pub fn parse_hide( }; // This kind of inverts the import pattern matching found in parse_use() - let decls_to_hide = if import_pattern.members.is_empty() { + let (aliases_to_hide, decls_to_hide) = if import_pattern.members.is_empty() { if is_module { - overlay.decls_with_head(&import_pattern.head.name) + ( + overlay.alias_names_with_head(&import_pattern.head.name), + overlay.decl_names_with_head(&import_pattern.head.name), + ) } else { - overlay.decls() + (overlay.alias_names(), overlay.decl_names()) } } else { match &import_pattern.members[0] { - ImportPatternMember::Glob { .. } => overlay.decls(), + ImportPatternMember::Glob { .. } => (overlay.alias_names(), overlay.decl_names()), ImportPatternMember::Name { name, span } => { - let mut output = vec![]; + let mut aliases = vec![]; + let mut decls = vec![]; - if let Some(item) = overlay.decl_with_head(name, &import_pattern.head.name) { - output.push(item); + if let Some(item) = + overlay.alias_name_with_head(name, &import_pattern.head.name) + { + aliases.push(item); + } else if let Some(item) = + overlay.decl_name_with_head(name, &import_pattern.head.name) + { + decls.push(item); } else if !overlay.has_env_var(name) { error = error.or(Some(ParseError::ExportNotFound(*span))); } - output + (aliases, decls) } ImportPatternMember::List { names } => { - let mut output = vec![]; + let mut aliases = vec![]; + let mut decls = vec![]; for (name, span) in names { - if let Some(item) = overlay.decl_with_head(name, &import_pattern.head.name) + if let Some(item) = + overlay.alias_name_with_head(name, &import_pattern.head.name) { - output.push(item); + aliases.push(item); + } else if let Some(item) = + overlay.decl_name_with_head(name, &import_pattern.head.name) + { + decls.push(item); } else if !overlay.has_env_var(name) { error = error.or(Some(ParseError::ExportNotFound(*span))); break; } } - output + (aliases, decls) } } }; + let import_pattern = { + let aliases: HashSet> = aliases_to_hide.iter().cloned().collect(); + let decls: HashSet> = decls_to_hide.iter().cloned().collect(); + + import_pattern.with_hidden(decls.union(&aliases).cloned().collect()) + }; + // TODO: `use spam; use spam foo; hide foo` will hide both `foo` and `spam foo` since // they point to the same DeclId. Do we want to keep it that way? working_set.hide_decls(&decls_to_hide); - let import_pattern = import_pattern - .with_hidden(decls_to_hide.iter().map(|(name, _)| name.clone()).collect()); + working_set.hide_aliases(&aliases_to_hide); // Create a new Use command call to pass the new import pattern let import_pattern_expr = Expression { diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index bd1187de95..e9be15149d 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -828,9 +828,11 @@ pub fn parse_call( if expand_aliases { // If the word is an alias, expand it and re-parse the expression - if let Some(expansion) = working_set.find_alias(&name) { + if let Some(alias_id) = working_set.find_alias(&name) { trace!("expanding alias"); + let expansion = working_set.get_alias(alias_id); + let orig_span = spans[pos]; let mut new_spans: Vec = vec![]; new_spans.extend(&spans[0..pos]); diff --git a/crates/nu-protocol/src/engine/engine_state.rs b/crates/nu-protocol/src/engine/engine_state.rs index fdc9ab8de4..da5a0948b3 100644 --- a/crates/nu-protocol/src/engine/engine_state.rs +++ b/crates/nu-protocol/src/engine/engine_state.rs @@ -1,7 +1,7 @@ use super::{Command, Stack}; use crate::{ - ast::Block, BlockId, DeclId, Example, Overlay, OverlayId, ShellError, Signature, Span, Type, - VarId, + ast::Block, AliasId, BlockId, DeclId, Example, Overlay, OverlayId, ShellError, Signature, Span, + Type, VarId, }; use core::panic; use std::{ @@ -20,12 +20,14 @@ use std::path::PathBuf; #[derive(Debug, Clone)] struct Visibility { decl_ids: HashMap, + alias_ids: HashMap, } impl Visibility { fn new() -> Self { Visibility { decl_ids: HashMap::new(), + alias_ids: HashMap::new(), } } @@ -33,27 +35,46 @@ impl Visibility { *self.decl_ids.get(decl_id).unwrap_or(&true) // by default it's visible } + fn is_alias_id_visible(&self, alias_id: &AliasId) -> bool { + *self.alias_ids.get(alias_id).unwrap_or(&true) // by default it's visible + } + fn hide_decl_id(&mut self, decl_id: &DeclId) { self.decl_ids.insert(*decl_id, false); } + fn hide_alias_id(&mut self, alias_id: &AliasId) { + self.alias_ids.insert(*alias_id, false); + } + fn use_decl_id(&mut self, decl_id: &DeclId) { self.decl_ids.insert(*decl_id, true); } + fn use_alias_id(&mut self, alias_id: &AliasId) { + self.alias_ids.insert(*alias_id, true); + } + fn merge_with(&mut self, other: Visibility) { // overwrite own values with the other self.decl_ids.extend(other.decl_ids); + self.alias_ids.extend(other.alias_ids); // self.env_var_ids.extend(other.env_var_ids); } fn append(&mut self, other: &Visibility) { - // take new values from other but keep own values + // take new values from the other but keep own values for (decl_id, visible) in other.decl_ids.iter() { if !self.decl_ids.contains_key(decl_id) { self.decl_ids.insert(*decl_id, *visible); } } + + for (alias_id, visible) in other.alias_ids.iter() { + if !self.alias_ids.contains_key(alias_id) { + self.alias_ids.insert(*alias_id, *visible); + } + } } } @@ -62,7 +83,7 @@ pub struct ScopeFrame { pub vars: HashMap, VarId>, predecls: HashMap, DeclId>, // temporary storage for predeclarations pub decls: HashMap, DeclId>, - pub aliases: HashMap, Vec>, + pub aliases: HashMap, AliasId>, pub env_vars: HashMap, BlockId>, pub overlays: HashMap, OverlayId>, visibility: Visibility, @@ -140,6 +161,7 @@ pub struct EngineState { file_contents: im::Vector<(Vec, usize, usize)>, vars: im::Vector, decls: im::Vector>, + aliases: im::Vector>, blocks: im::Vector, overlays: im::Vector, pub scope: im::Vector, @@ -169,6 +191,7 @@ impl EngineState { Type::Unknown ], decls: im::vector![], + aliases: im::vector![], blocks: im::vector![], overlays: im::vector![], scope: im::vector![ScopeFrame::new()], @@ -196,6 +219,7 @@ impl EngineState { self.files.extend(delta.files); self.file_contents.extend(delta.file_contents); self.decls.extend(delta.decls); + self.aliases.extend(delta.aliases); self.vars.extend(delta.vars); self.blocks.extend(delta.blocks); self.overlays.extend(delta.overlays); @@ -311,6 +335,10 @@ impl EngineState { self.decls.len() } + pub fn num_aliases(&self) -> usize { + self.aliases.len() + } + pub fn num_blocks(&self) -> usize { self.blocks.len() } @@ -344,12 +372,13 @@ impl EngineState { } } - pub fn find_aliases(&self, name: &str) -> Vec> { + pub fn find_aliases(&self, name: &str) -> Vec<&[Span]> { let mut output = vec![]; for frame in &self.scope { - if let Some(alias) = frame.aliases.get(name.as_bytes()) { - output.push(alias.clone()); + if let Some(alias_id) = frame.aliases.get(name.as_bytes()) { + let alias = self.get_alias(*alias_id); + output.push(alias.as_ref()); } } @@ -452,6 +481,13 @@ impl EngineState { .expect("internal error: missing declaration") } + pub fn get_alias(&self, alias_id: AliasId) -> &[Span] { + self.aliases + .get(alias_id) + .expect("internal error: missing alias") + .as_ref() + } + /// Get all IDs of all commands within scope, sorted by the commads' names pub fn get_decl_ids_sorted(&self, include_hidden: bool) -> impl Iterator { let mut decls_map = HashMap::new(); @@ -607,6 +643,7 @@ pub struct StateDelta { pub(crate) file_contents: Vec<(Vec, usize, usize)>, vars: Vec, // indexed by VarId decls: Vec>, // indexed by DeclId + aliases: Vec>, // indexed by AliasId pub blocks: Vec, // indexed by BlockId overlays: Vec, // indexed by OverlayId pub scope: Vec, @@ -627,6 +664,7 @@ impl StateDelta { file_contents: vec![], vars: vec![], decls: vec![], + aliases: vec![], blocks: vec![], overlays: vec![], scope: vec![ScopeFrame::new()], @@ -643,6 +681,10 @@ impl StateDelta { self.decls.len() } + pub fn num_aliases(&self) -> usize { + self.aliases.len() + } + pub fn num_blocks(&self) -> usize { self.blocks.len() } @@ -676,6 +718,10 @@ impl<'a> StateWorkingSet<'a> { self.delta.num_decls() + self.permanent_state.num_decls() } + pub fn num_aliases(&self) -> usize { + self.delta.num_aliases() + self.permanent_state.num_aliases() + } + pub fn num_blocks(&self) -> usize { self.delta.num_blocks() + self.permanent_state.num_blocks() } @@ -786,9 +832,49 @@ impl<'a> StateWorkingSet<'a> { None } - pub fn hide_decls(&mut self, decls: &[(Vec, DeclId)]) { + pub fn hide_alias(&mut self, name: &[u8]) -> Option { + let mut visibility: Visibility = Visibility::new(); + + // Since we can mutate scope frames in delta, remove the id directly + for scope in self.delta.scope.iter_mut().rev() { + visibility.append(&scope.visibility); + + if let Some(alias_id) = scope.aliases.remove(name) { + return Some(alias_id); + } + } + + // We cannot mutate the permanent state => store the information in the current scope frame + let last_scope_frame = self + .delta + .scope + .last_mut() + .expect("internal error: missing required scope frame"); + + for scope in self.permanent_state.scope.iter().rev() { + visibility.append(&scope.visibility); + + if let Some(alias_id) = scope.aliases.get(name) { + if visibility.is_alias_id_visible(alias_id) { + // Hide alias only if it's not already hidden + last_scope_frame.visibility.hide_alias_id(alias_id); + return Some(*alias_id); + } + } + } + + None + } + + pub fn hide_decls(&mut self, decls: &[Vec]) { for decl in decls.iter() { - self.hide_decl(&decl.0); // let's assume no errors + self.hide_decl(decl); // let's assume no errors + } + } + + pub fn hide_aliases(&mut self, aliases: &[Vec]) { + for alias in aliases.iter() { + self.hide_alias(alias); // let's assume no errors } } @@ -941,6 +1027,30 @@ impl<'a> StateWorkingSet<'a> { None } + pub fn find_alias(&self, name: &[u8]) -> Option { + let mut visibility: Visibility = Visibility::new(); + + for scope in self.delta.scope.iter().rev() { + visibility.append(&scope.visibility); + + if let Some(alias_id) = scope.aliases.get(name) { + return Some(*alias_id); + } + } + + for scope in self.permanent_state.scope.iter().rev() { + visibility.append(&scope.visibility); + + if let Some(alias_id) = scope.aliases.get(name) { + if visibility.is_alias_id_visible(alias_id) { + return Some(*alias_id); + } + } + } + + None + } + pub fn find_overlay(&self, name: &[u8]) -> Option { for scope in self.delta.scope.iter().rev() { if let Some(overlay_id) = scope.overlays.get(name) { @@ -1003,22 +1113,6 @@ impl<'a> StateWorkingSet<'a> { None } - pub fn find_alias(&self, name: &[u8]) -> Option<&[Span]> { - for scope in self.delta.scope.iter().rev() { - if let Some(spans) = scope.aliases.get(name) { - return Some(spans); - } - } - - for scope in self.permanent_state.scope.iter().rev() { - if let Some(spans) = scope.aliases.get(name) { - return Some(spans); - } - } - - None - } - pub fn add_variable(&mut self, mut name: Vec, ty: Type) -> VarId { let next_id = self.next_var_id(); @@ -1041,13 +1135,17 @@ impl<'a> StateWorkingSet<'a> { } pub fn add_alias(&mut self, name: Vec, replacement: Vec) { + self.delta.aliases.push(replacement); + let alias_id = self.num_aliases() - 1; + let last = self .delta .scope .last_mut() .expect("internal error: missing stack frame"); - last.aliases.insert(name, replacement); + last.aliases.insert(name, alias_id); + last.visibility.use_alias_id(&alias_id); } pub fn get_cwd(&self) -> String { @@ -1105,6 +1203,19 @@ impl<'a> StateWorkingSet<'a> { } } + pub fn get_alias(&self, alias_id: AliasId) -> &[Span] { + let num_permanent_aliases = self.permanent_state.num_aliases(); + if alias_id < num_permanent_aliases { + self.permanent_state.get_alias(alias_id) + } else { + self.delta + .aliases + .get(alias_id - num_permanent_aliases) + .expect("internal error: missing alias") + .as_ref() + } + } + pub fn find_commands_by_prefix(&self, name: &[u8]) -> Vec> { let mut output = vec![]; diff --git a/crates/nu-protocol/src/id.rs b/crates/nu-protocol/src/id.rs index c40f1633dc..a380bb8b67 100644 --- a/crates/nu-protocol/src/id.rs +++ b/crates/nu-protocol/src/id.rs @@ -1,4 +1,5 @@ pub type VarId = usize; pub type DeclId = usize; +pub type AliasId = usize; pub type BlockId = usize; pub type OverlayId = usize; diff --git a/crates/nu-protocol/src/overlay.rs b/crates/nu-protocol/src/overlay.rs index 81b7816113..dd0299d9c1 100644 --- a/crates/nu-protocol/src/overlay.rs +++ b/crates/nu-protocol/src/overlay.rs @@ -1,4 +1,4 @@ -use crate::{BlockId, DeclId, Span}; +use crate::{AliasId, BlockId, DeclId, Span}; use indexmap::IndexMap; @@ -9,6 +9,7 @@ use indexmap::IndexMap; #[derive(Debug, Clone)] pub struct Overlay { pub decls: IndexMap, DeclId>, + pub aliases: IndexMap, AliasId>, pub env_vars: IndexMap, BlockId>, pub span: Option, } @@ -17,6 +18,7 @@ impl Overlay { pub fn new() -> Self { Overlay { decls: IndexMap::new(), + aliases: IndexMap::new(), env_vars: IndexMap::new(), span: None, } @@ -25,6 +27,7 @@ impl Overlay { pub fn from_span(span: Span) -> Self { Overlay { decls: IndexMap::new(), + aliases: IndexMap::new(), env_vars: IndexMap::new(), span: Some(span), } @@ -34,6 +37,10 @@ impl Overlay { self.decls.insert(name.to_vec(), decl_id) } + pub fn add_alias(&mut self, name: &[u8], alias_id: AliasId) -> Option { + self.aliases.insert(name.to_vec(), alias_id) + } + pub fn add_env_var(&mut self, name: &[u8], block_id: BlockId) -> Option { self.env_vars.insert(name.to_vec(), block_id) } @@ -51,16 +58,35 @@ impl Overlay { self.decls.get(name).copied() } + pub fn get_alias_id(&self, name: &[u8]) -> Option { + self.aliases.get(name).copied() + } + pub fn has_decl(&self, name: &[u8]) -> bool { self.decls.contains_key(name) } - pub fn decl_with_head(&self, name: &[u8], head: &[u8]) -> Option<(Vec, DeclId)> { - if let Some(id) = self.get_decl_id(name) { + pub fn has_alias(&self, name: &[u8]) -> bool { + self.aliases.contains_key(name) + } + + pub fn decl_name_with_head(&self, name: &[u8], head: &[u8]) -> Option> { + if self.has_decl(name) { let mut new_name = head.to_vec(); new_name.push(b' '); new_name.extend(name); - Some((new_name, id)) + Some(new_name) + } else { + None + } + } + + pub fn alias_name_with_head(&self, name: &[u8], head: &[u8]) -> Option> { + if self.has_alias(name) { + let mut new_name = head.to_vec(); + new_name.push(b' '); + new_name.extend(name); + Some(new_name) } else { None } @@ -78,6 +104,30 @@ impl Overlay { .collect() } + pub fn decl_names_with_head(&self, head: &[u8]) -> Vec> { + self.decls + .keys() + .map(|name| { + let mut new_name = head.to_vec(); + new_name.push(b' '); + new_name.extend(name); + new_name + }) + .collect() + } + + pub fn alias_names_with_head(&self, head: &[u8]) -> Vec> { + self.aliases + .keys() + .map(|name| { + let mut new_name = head.to_vec(); + new_name.push(b' '); + new_name.extend(name); + new_name + }) + .collect() + } + pub fn decls(&self) -> Vec<(Vec, DeclId)> { self.decls .iter() @@ -85,6 +135,21 @@ impl Overlay { .collect() } + pub fn decl_names(&self) -> Vec> { + self.decls.keys().cloned().collect() + } + + pub fn alias_names(&self) -> Vec> { + self.aliases.keys().cloned().collect() + } + + pub fn aliases(&self) -> Vec<(Vec, AliasId)> { + self.aliases + .iter() + .map(|(name, id)| (name.clone(), *id)) + .collect() + } + pub fn get_env_var_id(&self, name: &[u8]) -> Option { self.env_vars.get(name).copied() } diff --git a/src/tests/test_hiding.rs b/src/tests/test_hiding.rs index 7929f2f04f..a876de63bf 100644 --- a/src/tests/test_hiding.rs +++ b/src/tests/test_hiding.rs @@ -9,6 +9,14 @@ fn hides_def() -> TestResult { ) } +#[test] +fn hides_alias() -> TestResult { + fail_test( + r#"alias foo = echo "foo"; hide foo; foo"#, + "", // we just care if it errors + ) +} + #[test] fn hides_env() -> TestResult { fail_test(r#"let-env foo = "foo"; hide foo; $env.foo"#, "did you mean") @@ -24,6 +32,14 @@ fn hides_def_then_redefines() -> TestResult { ) } +#[test] +fn hides_alias_then_redefines() -> TestResult { + run_test( + r#"alias foo = echo "foo"; hide foo; alias foo = echo "foo"; foo"#, + "foo", + ) +} + #[test] fn hides_env_then_redefines() -> TestResult { run_test( @@ -64,6 +80,38 @@ fn hides_def_in_scope_4() -> TestResult { ) } +#[test] +fn hides_alias_in_scope_1() -> TestResult { + fail_test( + r#"alias foo = echo "foo"; do { hide foo; foo }"#, + "", // we just care if it errors + ) +} + +#[test] +fn hides_alias_in_scope_2() -> TestResult { + run_test( + r#"alias foo = echo "foo"; do { alias foo = echo "bar"; hide foo; foo }"#, + "foo", + ) +} + +#[test] +fn hides_alias_in_scope_3() -> TestResult { + fail_test( + r#"alias foo = echo "foo"; do { hide foo; alias foo = echo "bar"; hide foo; foo }"#, + "", // we just care if it errors + ) +} + +#[test] +fn hides_alias_in_scope_4() -> TestResult { + fail_test( + r#"alias foo = echo "foo"; do { alias foo = echo "bar"; hide foo; hide foo; foo }"#, + "", // we just care if it errors + ) +} + #[test] fn hides_env_in_scope_1() -> TestResult { fail_test( @@ -104,6 +152,14 @@ fn hide_def_twice_not_allowed() -> TestResult { ) } +#[test] +fn hide_alias_twice_not_allowed() -> TestResult { + fail_test( + r#"alias foo = echo "foo"; hide foo; hide foo"#, + "did not find", + ) +} + #[test] fn hide_env_twice_not_allowed() -> TestResult { fail_test(r#"let-env foo = "foo"; hide foo; hide foo"#, "did not find") @@ -125,6 +181,38 @@ fn hides_def_runs_env_2() -> TestResult { ) } +#[test] +fn hides_alias_runs_def_1() -> TestResult { + run_test( + r#"def foo [] { "bar" }; alias foo = echo "foo"; hide foo; foo"#, + "bar", + ) +} + +#[test] +fn hides_alias_runs_def_2() -> TestResult { + run_test( + r#"alias foo = echo "foo"; def foo [] { "bar" }; hide foo; foo"#, + "bar", + ) +} + +#[test] +fn hides_alias_runs_env_1() -> TestResult { + run_test( + r#"let-env foo = "bar"; alias foo = echo "foo"; hide foo; $env.foo"#, + "bar", + ) +} + +#[test] +fn hides_alias_runs_env_2() -> TestResult { + run_test( + r#"alias foo = echo "foo"; let-env foo = "bar"; hide foo; $env.foo"#, + "bar", + ) +} + #[test] fn hides_def_and_env() -> TestResult { fail_test( @@ -133,6 +221,30 @@ fn hides_def_and_env() -> TestResult { ) } +#[test] +fn hides_alias_and_def() -> TestResult { + fail_test( + r#"alias foo = echo "foo"; def foo [] { "bar" }; hide foo; hide foo; foo"#, + "", // we just care if it errors + ) +} + +#[test] +fn hides_alias_def_and_env_1() -> TestResult { + fail_test( + r#"alias foo = echo "foo"; def foo [] { "foo" }; let-env foo = "bar"; hide foo; hide foo; hide foo; $env.foo"#, + "", // we just care if it errors + ) +} + +#[test] +fn hides_alias_def_and_env_2() -> TestResult { + fail_test( + r#"alias foo = echo "foo"; def foo [] { "foo" }; let-env foo = "bar"; hide foo; hide foo; hide foo; foo"#, + "", // we just care if it errors + ) +} + #[test] fn hides_def_import_1() -> TestResult { fail_test(