mirror of
https://github.com/nushell/nushell.git
synced 2024-11-22 00:13:21 +01:00
make command signature parsing more strict (#14309)
# User-Facing Changes The parser now errors on more invalid command signatures: ```nushell # expected parameter or flag def foo [ bar: int: ] {} # expected type def foo [ bar: = ] {} def foo [ bar: ] {} # expected default value def foo [ bar = ] {} ```
This commit is contained in:
parent
e5cec8f4eb
commit
6e1118681d
@ -2,6 +2,13 @@ use nu_test_support::nu;
|
|||||||
use nu_test_support::playground::Playground;
|
use nu_test_support::playground::Playground;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn def_with_trailing_comma() {
|
||||||
|
let actual = nu!("def test-command [ foo: int, ] { $foo }; test-command 1");
|
||||||
|
|
||||||
|
assert!(actual.out == "1");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn def_with_comment() {
|
fn def_with_comment() {
|
||||||
Playground::setup("def_with_comment", |dirs, _| {
|
Playground::setup("def_with_comment", |dirs, _| {
|
||||||
@ -72,6 +79,13 @@ fn def_errors_with_comma_before_equals() {
|
|||||||
assert!(actual.err.contains("expected parameter"));
|
assert!(actual.err.contains("expected parameter"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn def_errors_with_colon_before_equals() {
|
||||||
|
let actual = nu!("def test-command [ foo: = 1 ] {}");
|
||||||
|
|
||||||
|
assert!(actual.err.contains("expected type"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn def_errors_with_comma_before_colon() {
|
fn def_errors_with_comma_before_colon() {
|
||||||
let actual = nu!("def test-command [ foo, : int ] {}");
|
let actual = nu!("def test-command [ foo, : int ] {}");
|
||||||
@ -85,7 +99,6 @@ fn def_errors_with_multiple_colons() {
|
|||||||
assert!(actual.err.contains("expected type"));
|
assert!(actual.err.contains("expected type"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[ignore = "This error condition is not implemented yet"]
|
|
||||||
#[test]
|
#[test]
|
||||||
fn def_errors_with_multiple_types() {
|
fn def_errors_with_multiple_types() {
|
||||||
let actual = nu!("def test-command [ foo:int:string ] {}");
|
let actual = nu!("def test-command [ foo:int:string ] {}");
|
||||||
@ -93,6 +106,20 @@ fn def_errors_with_multiple_types() {
|
|||||||
assert!(actual.err.contains("expected parameter"));
|
assert!(actual.err.contains("expected parameter"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn def_errors_with_trailing_colon() {
|
||||||
|
let actual = nu!("def test-command [ foo: int: ] {}");
|
||||||
|
|
||||||
|
assert!(actual.err.contains("expected parameter"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn def_errors_with_trailing_default_value() {
|
||||||
|
let actual = nu!("def test-command [ foo: int = ] {}");
|
||||||
|
|
||||||
|
assert!(actual.err.contains("expected default value"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn def_errors_with_multiple_commas() {
|
fn def_errors_with_multiple_commas() {
|
||||||
let actual = nu!("def test-command [ foo,,bar ] {}");
|
let actual = nu!("def test-command [ foo,,bar ] {}");
|
||||||
|
@ -3392,6 +3392,7 @@ pub fn parse_signature_helper(working_set: &mut StateWorkingSet, span: Span) ->
|
|||||||
Arg,
|
Arg,
|
||||||
AfterCommaArg,
|
AfterCommaArg,
|
||||||
Type,
|
Type,
|
||||||
|
AfterType,
|
||||||
DefaultValue,
|
DefaultValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3425,7 +3426,9 @@ pub fn parse_signature_helper(working_set: &mut StateWorkingSet, span: Span) ->
|
|||||||
let mut args: Vec<Arg> = vec![];
|
let mut args: Vec<Arg> = vec![];
|
||||||
let mut parse_mode = ParseMode::Arg;
|
let mut parse_mode = ParseMode::Arg;
|
||||||
|
|
||||||
for token in &output {
|
for (index, token) in output.iter().enumerate() {
|
||||||
|
let last_token = index == output.len() - 1;
|
||||||
|
|
||||||
match token {
|
match token {
|
||||||
Token {
|
Token {
|
||||||
contents: crate::TokenContents::Item | crate::TokenContents::AssignmentOperator,
|
contents: crate::TokenContents::Item | crate::TokenContents::AssignmentOperator,
|
||||||
@ -3437,10 +3440,12 @@ pub fn parse_signature_helper(working_set: &mut StateWorkingSet, span: Span) ->
|
|||||||
// The : symbol separates types
|
// The : symbol separates types
|
||||||
if contents == b":" {
|
if contents == b":" {
|
||||||
match parse_mode {
|
match parse_mode {
|
||||||
|
ParseMode::Arg if last_token => working_set
|
||||||
|
.error(ParseError::Expected("type", Span::new(span.end, span.end))),
|
||||||
ParseMode::Arg => {
|
ParseMode::Arg => {
|
||||||
parse_mode = ParseMode::Type;
|
parse_mode = ParseMode::Type;
|
||||||
}
|
}
|
||||||
ParseMode::AfterCommaArg => {
|
ParseMode::AfterCommaArg | ParseMode::AfterType => {
|
||||||
working_set.error(ParseError::Expected("parameter or flag", span));
|
working_set.error(ParseError::Expected("parameter or flag", span));
|
||||||
}
|
}
|
||||||
ParseMode::Type | ParseMode::DefaultValue => {
|
ParseMode::Type | ParseMode::DefaultValue => {
|
||||||
@ -3452,9 +3457,15 @@ pub fn parse_signature_helper(working_set: &mut StateWorkingSet, span: Span) ->
|
|||||||
// The = symbol separates a variable from its default value
|
// The = symbol separates a variable from its default value
|
||||||
else if contents == b"=" {
|
else if contents == b"=" {
|
||||||
match parse_mode {
|
match parse_mode {
|
||||||
ParseMode::Type | ParseMode::Arg => {
|
ParseMode::Arg | ParseMode::AfterType if last_token => working_set.error(
|
||||||
|
ParseError::Expected("default value", Span::new(span.end, span.end)),
|
||||||
|
),
|
||||||
|
ParseMode::Arg | ParseMode::AfterType => {
|
||||||
parse_mode = ParseMode::DefaultValue;
|
parse_mode = ParseMode::DefaultValue;
|
||||||
}
|
}
|
||||||
|
ParseMode::Type => {
|
||||||
|
working_set.error(ParseError::Expected("type", span));
|
||||||
|
}
|
||||||
ParseMode::AfterCommaArg => {
|
ParseMode::AfterCommaArg => {
|
||||||
working_set.error(ParseError::Expected("parameter or flag", span));
|
working_set.error(ParseError::Expected("parameter or flag", span));
|
||||||
}
|
}
|
||||||
@ -3467,7 +3478,9 @@ pub fn parse_signature_helper(working_set: &mut StateWorkingSet, span: Span) ->
|
|||||||
// The , symbol separates params only
|
// The , symbol separates params only
|
||||||
else if contents == b"," {
|
else if contents == b"," {
|
||||||
match parse_mode {
|
match parse_mode {
|
||||||
ParseMode::Arg => parse_mode = ParseMode::AfterCommaArg,
|
ParseMode::Arg | ParseMode::AfterType => {
|
||||||
|
parse_mode = ParseMode::AfterCommaArg
|
||||||
|
}
|
||||||
ParseMode::AfterCommaArg => {
|
ParseMode::AfterCommaArg => {
|
||||||
working_set.error(ParseError::Expected("parameter or flag", span));
|
working_set.error(ParseError::Expected("parameter or flag", span));
|
||||||
}
|
}
|
||||||
@ -3480,7 +3493,7 @@ pub fn parse_signature_helper(working_set: &mut StateWorkingSet, span: Span) ->
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match parse_mode {
|
match parse_mode {
|
||||||
ParseMode::Arg | ParseMode::AfterCommaArg => {
|
ParseMode::Arg | ParseMode::AfterCommaArg | ParseMode::AfterType => {
|
||||||
// Long flag with optional short form following with no whitespace, e.g. --output, --age(-a)
|
// Long flag with optional short form following with no whitespace, e.g. --output, --age(-a)
|
||||||
if contents.starts_with(b"--") && contents.len() > 2 {
|
if contents.starts_with(b"--") && contents.len() > 2 {
|
||||||
// Split the long flag from the short flag with the ( character as delimiter.
|
// Split the long flag from the short flag with the ( character as delimiter.
|
||||||
@ -3790,7 +3803,7 @@ pub fn parse_signature_helper(working_set: &mut StateWorkingSet, span: Span) ->
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parse_mode = ParseMode::Arg;
|
parse_mode = ParseMode::AfterType;
|
||||||
}
|
}
|
||||||
ParseMode::DefaultValue => {
|
ParseMode::DefaultValue => {
|
||||||
if let Some(last) = args.last_mut() {
|
if let Some(last) = args.last_mut() {
|
||||||
|
Loading…
Reference in New Issue
Block a user