mirror of
https://github.com/nushell/nushell.git
synced 2025-08-22 07:40:28 +02:00
fix(parser): env shorthand false positive (#16337)
More restrictions on env shorthand parsing to avoid false positives. Fixes #16332
This commit is contained in:
@@ -116,6 +116,22 @@ pub fn is_math_expression_like(working_set: &mut StateWorkingSet, span: Span) ->
|
||||
is_range
|
||||
}
|
||||
|
||||
fn is_env_variable_name(bytes: &[u8]) -> bool {
|
||||
if bytes.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let first = bytes[0];
|
||||
if !first.is_ascii_alphabetic() && first != b'_' {
|
||||
return false;
|
||||
}
|
||||
|
||||
bytes
|
||||
.iter()
|
||||
.skip(1)
|
||||
.all(|&b| b.is_ascii_alphanumeric() || b == b'_')
|
||||
}
|
||||
|
||||
fn is_identifier(bytes: &[u8]) -> bool {
|
||||
bytes.iter().all(|x| is_identifier_byte(*x))
|
||||
}
|
||||
@@ -5892,49 +5908,38 @@ pub fn parse_expression(working_set: &mut StateWorkingSet, spans: &[Span]) -> Ex
|
||||
// Check if there is any environment shorthand
|
||||
let name = working_set.get_span_contents(spans[pos]);
|
||||
|
||||
let split = name.splitn(2, |x| *x == b'=');
|
||||
let split: Vec<_> = split.collect();
|
||||
if !name.starts_with(b"^")
|
||||
&& split.len() == 2
|
||||
&& !split[0].is_empty()
|
||||
&& !split[0].ends_with(b"..")
|
||||
// was range op ..=
|
||||
{
|
||||
let point = split[0].len() + 1;
|
||||
let split: Vec<_> = name.splitn(2, |x| *x == b'=').collect();
|
||||
if split.len() != 2 || !is_env_variable_name(split[0]) {
|
||||
break;
|
||||
}
|
||||
|
||||
let starting_error_count = working_set.parse_errors.len();
|
||||
let point = split[0].len() + 1;
|
||||
let starting_error_count = working_set.parse_errors.len();
|
||||
|
||||
let lhs_span = Span::new(spans[pos].start, spans[pos].start + point - 1);
|
||||
if !is_identifier(working_set.get_span_contents(lhs_span)) {
|
||||
break;
|
||||
}
|
||||
|
||||
let lhs = parse_string_strict(working_set, lhs_span);
|
||||
let rhs = if spans[pos].start + point < spans[pos].end {
|
||||
let rhs_span = Span::new(spans[pos].start + point, spans[pos].end);
|
||||
|
||||
if working_set.get_span_contents(rhs_span).starts_with(b"$") {
|
||||
parse_dollar_expr(working_set, rhs_span)
|
||||
} else {
|
||||
parse_string_strict(working_set, rhs_span)
|
||||
}
|
||||
let rhs = if spans[pos].start + point < spans[pos].end {
|
||||
let rhs_span = Span::new(spans[pos].start + point, spans[pos].end);
|
||||
if split[1].starts_with(b"$") {
|
||||
parse_dollar_expr(working_set, rhs_span)
|
||||
} else {
|
||||
Expression::new(
|
||||
working_set,
|
||||
Expr::String(String::new()),
|
||||
Span::unknown(),
|
||||
Type::Nothing,
|
||||
)
|
||||
};
|
||||
|
||||
if starting_error_count == working_set.parse_errors.len() {
|
||||
shorthand.push((lhs, rhs));
|
||||
pos += 1;
|
||||
} else {
|
||||
working_set.parse_errors.truncate(starting_error_count);
|
||||
break;
|
||||
parse_string_strict(working_set, rhs_span)
|
||||
}
|
||||
} else {
|
||||
Expression::new(
|
||||
working_set,
|
||||
Expr::String(String::new()),
|
||||
Span::unknown(),
|
||||
Type::Nothing,
|
||||
)
|
||||
};
|
||||
|
||||
let lhs_span = Span::new(spans[pos].start, spans[pos].start + point - 1);
|
||||
let lhs = parse_string_strict(working_set, lhs_span);
|
||||
|
||||
if starting_error_count == working_set.parse_errors.len() {
|
||||
shorthand.push((lhs, rhs));
|
||||
pos += 1;
|
||||
} else {
|
||||
working_set.parse_errors.truncate(starting_error_count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@@ -1036,3 +1036,9 @@ fn external_argument_with_subexpressions() -> TestResult {
|
||||
run_test(r#"^echo ")('foo')(""#, ")('foo')(")?;
|
||||
fail_test(r#"^echo foo( 'bar'"#, "Unexpected end of code")
|
||||
}
|
||||
|
||||
// https://github.com/nushell/nushell/issues/16332
|
||||
#[test]
|
||||
fn quote_escape_but_not_env_shorthand() -> TestResult {
|
||||
run_test(r#""\"=foo""#, "\"=foo")
|
||||
}
|
||||
|
Reference in New Issue
Block a user