Repository: nushell/nushell
File: crates/nu-parser/src/parser.rs (Rust)

Code before:

    if let Some(expr) = parse_range(working_set, span) {
        expr
    } else {
        working_set.parse_errors.truncate(starting_error_count);

        if matches!(shape, SyntaxShape::Signature) {
            parse_signature(working_set, span)
        } else {
            parse_full_cell_path(working_set, None, span)
        }
    }
}

pub fn parse_brace_expr(
    working_set: &mut StateWorkingSet,
    span: Span,
    shape: &SyntaxShape,
) -> Expression {
    // Try to detect what kind of value we're about to parse
    // FIXME: In the future, we should work over the token stream so we only have to do this once
    // before parsing begins

    // FIXME: we're still using the shape because we rely on it to know how to handle syntax where
    // the parse is ambiguous. We'll need to update the parts of the grammar where this is ambiguous
    // and then revisit the parsing.

    if span.end <= (span.start + 1) {
        working_set.error(ParseError::ExpectedWithStringMsg(
            format!("non-block value: {shape}"),
            span,
        ));
        return Expression::garbage(working_set, span);
    }

    let bytes = working_set.get_span_contents(Span::new(span.start + 1, span.end - 1));
    let (tokens, _) = lex(bytes, span.start + 1, &[b'\r', b'\n', b'\t'], &[b':'], true);

    let second_token = tokens
        .first()
        .map(|token| working_set.get_span_contents(token.span));

    let second_token_contents = tokens.first().map(|token| token.contents);

    let third_token = tokens
        .get(1)
        .map(|token| working_set.get_span_contents(token.span));

    if second_token.is_none() {
        // If we're empty, that means an empty record or closure
        if matches!(shape, SyntaxShape::Closure(_)) {

Review comment (suggestion):
Oh, sorry, it looks a little strange to me, why falling back to `parse_string` solve the issue?
I guess `^echo (id -u):(id -g)` will result in `(id -u):(id -g)`, because it's parsed as a string.

Code after:
) -> Expression {
    let starting_error_count = working_set.parse_errors.len();

    if let Some(expr) = parse_range(working_set, span) {
        return expr;
    }

    working_set.parse_errors.truncate(starting_error_count);

    if matches!(shape, SyntaxShape::Signature) {
        return parse_signature(working_set, span);
    }

    let fcp_expr = parse_full_cell_path(working_set, None, span);
    let fcp_error_count = working_set.parse_errors.len();
    if fcp_error_count > starting_error_count {
        let malformed_subexpr = working_set.parse_errors[starting_error_count..]
            .iter()
            .any(|e| match e {
                ParseError::Unclosed(right, _) if right == ")" => true,
                ParseError::Unbalanced(left, right, _) if left == "(" && right == ")" => true,
                _ => false,
            });
        if malformed_subexpr {
            working_set.parse_errors.truncate(starting_error_count);
            parse_string(working_set, span)
        } else {
            fcp_expr
        }
    } else {
        fcp_expr
    }
}

pub fn parse_brace_expr(
    working_set: &mut StateWorkingSet,
    span: Span,
    shape: &SyntaxShape,
) -> Expression {
    // Try to detect what kind of value we're about to parse
    // FIXME: In the future, we should work over the token stream so we only have to do this once
    // before parsing begins

    // FIXME: we're still using the shape because we rely on it to know how to handle syntax where
    // the parse is ambiguous. We'll need to update the parts of the grammar where this is ambiguous
    // and then revisit the parsing.

    if span.end <= (span.start + 1) {
        working_set.error(ParseError::ExpectedWithStringMsg(
            format!("non-block value: {shape}"),
            span,