From cfac73f65143b94d9e3a6e9571465e5dc9793b3c Mon Sep 17 00:00:00 2001 From: WATANABE Yuki Date: Thu, 3 Nov 2022 00:48:36 +0900 Subject: [PATCH] Reject unset parameter with nounset option --- yash-semantics/src/expansion.rs | 6 ++++ yash-semantics/src/expansion/initial/param.rs | 36 ++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/yash-semantics/src/expansion.rs b/yash-semantics/src/expansion.rs index 8d05cdc2..11bc9863 100644 --- a/yash-semantics/src/expansion.rs +++ b/yash-semantics/src/expansion.rs @@ -110,6 +110,8 @@ pub enum ErrorCause { ArithError(ArithError), /// Assignment to a read-only variable. AssignReadOnly(ReadOnlyError), + /// Expansion of an unset parameter with the `nounset` option + UnsetParameter, /// Expansion of an empty value with an error switch EmptyExpansion(EmptyError), /// Assignment to a nonassignable parameter @@ -126,6 +128,7 @@ impl ErrorCause { CommandSubstError(_) => "error performing the command substitution", ArithError(_) => "error evaluating the arithmetic expansion", AssignReadOnly(_) => "cannot assign to read-only variable", + UnsetParameter => "unset parameter", EmptyExpansion(error) => error.message_or_default(), NonassignableParameter(_) => "cannot assign to parameter", } @@ -140,6 +143,7 @@ impl ErrorCause { CommandSubstError(e) => e.desc().into(), ArithError(e) => e.to_string().into(), AssignReadOnly(e) => e.to_string().into(), + UnsetParameter => "unset parameter disallowed by the nounset option".into(), EmptyExpansion(e) => e.state.description().into(), NonassignableParameter(e) => e.to_string().into(), } @@ -158,6 +162,7 @@ impl ErrorCause { &e.read_only_location, "the variable was made read-only here", )), + UnsetParameter => None, EmptyExpansion(_) => None, NonassignableParameter(_) => None, } @@ -171,6 +176,7 @@ impl std::fmt::Display for ErrorCause { CommandSubstError(errno) => write!(f, "error in command substitution: {errno}"), ArithError(error) => error.fmt(f), AssignReadOnly(error) => error.fmt(f), + UnsetParameter => "unset parameter".fmt(f), EmptyExpansion(error) => error.fmt(f), NonassignableParameter(error) => error.fmt(f), } diff --git a/yash-semantics/src/expansion/initial/param.rs b/yash-semantics/src/expansion/initial/param.rs index 71548cef..dbb0c770 100644 --- a/yash-semantics/src/expansion/initial/param.rs +++ b/yash-semantics/src/expansion/initial/param.rs @@ -19,8 +19,11 @@ use super::super::phrase::Phrase; use super::super::AttrChar; use super::super::Error; +use super::super::ErrorCause; use super::super::Origin; use super::Env; +use yash_env::option::Option::Unset; +use yash_env::option::State::Off; use yash_env::variable::Value; use yash_syntax::source::Location; use yash_syntax::syntax::Modifier; @@ -78,7 +81,14 @@ impl ParamRef<'_> { } } - // TODO Check for nounset error + // Check for nounset option error // + if value.is_none() && env.inner.options.get(Unset) == Off { + return Err(Error { + cause: ErrorCause::UnsetParameter, + location: self.location.clone(), + }); + } + // TODO Reject POSIXly unspecified combinations of name and modifier // Other modifiers // @@ -286,6 +296,30 @@ pub mod tests { assert_eq!(phrase, Phrase::one_empty_field()); } + #[test] + fn unset_option() { + let mut env = yash_env::Env::new_virtual(); + let mut env = Env::new(&mut env); + let param = param("foo"); + let param = ParamRef::from(¶m); + + let phrase = param.expand(&mut env).now_or_never().unwrap().unwrap(); + assert_eq!(phrase, Phrase::one_empty_field()); + } + + #[test] + fn nounset_option() { + let mut env = yash_env::Env::new_virtual(); + env.options.set(Unset, Off); + let mut env = Env::new(&mut env); + let param = param("foo"); + let param = ParamRef::from(¶m); + + let e = param.expand(&mut env).now_or_never().unwrap().unwrap_err(); + assert_eq!(e.cause, ErrorCause::UnsetParameter); + assert_eq!(e.location, Location::dummy("")); + } + #[test] fn expand_at_no_join_in_non_splitting_context() { let mut env = env_with_positional_params_and_ifs();