Auto merge of #14952 - lowr:fix/assignments-are-right-associative, r=HKalbasi
fix: assignment operators are right associative Fixes #14944 Assignment operators, be they simple or complex, are right associative in Rust ([reference]). We need to consider that fact when computing [binding power][bp] of infix operators. The changes in `0072_destructuring_assignment.{rs,rast}` are unexpected, but I'm pretty sure it's a typo and fixed the `.rs` file accordingly. [reference]: https://doc.rust-lang.org/reference/expressions.html#expression-precedence [bp]: https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html
This commit is contained in:
commit
dd0c29c934
@ -4,8 +4,8 @@
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub(crate) use self::atom::{block_expr, match_arm_list};
|
pub(crate) use atom::{block_expr, match_arm_list};
|
||||||
pub(super) use self::atom::{literal, LITERAL_FIRST};
|
pub(super) use atom::{literal, LITERAL_FIRST};
|
||||||
|
|
||||||
#[derive(PartialEq, Eq)]
|
#[derive(PartialEq, Eq)]
|
||||||
pub(super) enum Semicolon {
|
pub(super) enum Semicolon {
|
||||||
@ -188,47 +188,56 @@ struct Restrictions {
|
|||||||
prefer_stmt: bool,
|
prefer_stmt: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum Associativity {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
||||||
/// Binding powers of operators for a Pratt parser.
|
/// Binding powers of operators for a Pratt parser.
|
||||||
///
|
///
|
||||||
/// See <https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html>
|
/// See <https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html>
|
||||||
|
///
|
||||||
|
/// Note that Rust doesn't define associativity for some infix operators (e.g. `==` and `..`) and
|
||||||
|
/// requires parentheses to disambiguate. We just treat them as left associative.
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
fn current_op(p: &Parser<'_>) -> (u8, SyntaxKind) {
|
fn current_op(p: &Parser<'_>) -> (u8, SyntaxKind, Associativity) {
|
||||||
const NOT_AN_OP: (u8, SyntaxKind) = (0, T![@]);
|
use Associativity::*;
|
||||||
|
const NOT_AN_OP: (u8, SyntaxKind, Associativity) = (0, T![@], Left);
|
||||||
match p.current() {
|
match p.current() {
|
||||||
T![|] if p.at(T![||]) => (3, T![||]),
|
T![|] if p.at(T![||]) => (3, T![||], Left),
|
||||||
T![|] if p.at(T![|=]) => (1, T![|=]),
|
T![|] if p.at(T![|=]) => (1, T![|=], Right),
|
||||||
T![|] => (6, T![|]),
|
T![|] => (6, T![|], Left),
|
||||||
T![>] if p.at(T![>>=]) => (1, T![>>=]),
|
T![>] if p.at(T![>>=]) => (1, T![>>=], Right),
|
||||||
T![>] if p.at(T![>>]) => (9, T![>>]),
|
T![>] if p.at(T![>>]) => (9, T![>>], Left),
|
||||||
T![>] if p.at(T![>=]) => (5, T![>=]),
|
T![>] if p.at(T![>=]) => (5, T![>=], Left),
|
||||||
T![>] => (5, T![>]),
|
T![>] => (5, T![>], Left),
|
||||||
T![=] if p.at(T![=>]) => NOT_AN_OP,
|
T![=] if p.at(T![=>]) => NOT_AN_OP,
|
||||||
T![=] if p.at(T![==]) => (5, T![==]),
|
T![=] if p.at(T![==]) => (5, T![==], Left),
|
||||||
T![=] => (1, T![=]),
|
T![=] => (1, T![=], Right),
|
||||||
T![<] if p.at(T![<=]) => (5, T![<=]),
|
T![<] if p.at(T![<=]) => (5, T![<=], Left),
|
||||||
T![<] if p.at(T![<<=]) => (1, T![<<=]),
|
T![<] if p.at(T![<<=]) => (1, T![<<=], Right),
|
||||||
T![<] if p.at(T![<<]) => (9, T![<<]),
|
T![<] if p.at(T![<<]) => (9, T![<<], Left),
|
||||||
T![<] => (5, T![<]),
|
T![<] => (5, T![<], Left),
|
||||||
T![+] if p.at(T![+=]) => (1, T![+=]),
|
T![+] if p.at(T![+=]) => (1, T![+=], Right),
|
||||||
T![+] => (10, T![+]),
|
T![+] => (10, T![+], Left),
|
||||||
T![^] if p.at(T![^=]) => (1, T![^=]),
|
T![^] if p.at(T![^=]) => (1, T![^=], Right),
|
||||||
T![^] => (7, T![^]),
|
T![^] => (7, T![^], Left),
|
||||||
T![%] if p.at(T![%=]) => (1, T![%=]),
|
T![%] if p.at(T![%=]) => (1, T![%=], Right),
|
||||||
T![%] => (11, T![%]),
|
T![%] => (11, T![%], Left),
|
||||||
T![&] if p.at(T![&=]) => (1, T![&=]),
|
T![&] if p.at(T![&=]) => (1, T![&=], Right),
|
||||||
// If you update this, remember to update `expr_let()` too.
|
// If you update this, remember to update `expr_let()` too.
|
||||||
T![&] if p.at(T![&&]) => (4, T![&&]),
|
T![&] if p.at(T![&&]) => (4, T![&&], Left),
|
||||||
T![&] => (8, T![&]),
|
T![&] => (8, T![&], Left),
|
||||||
T![/] if p.at(T![/=]) => (1, T![/=]),
|
T![/] if p.at(T![/=]) => (1, T![/=], Right),
|
||||||
T![/] => (11, T![/]),
|
T![/] => (11, T![/], Left),
|
||||||
T![*] if p.at(T![*=]) => (1, T![*=]),
|
T![*] if p.at(T![*=]) => (1, T![*=], Right),
|
||||||
T![*] => (11, T![*]),
|
T![*] => (11, T![*], Left),
|
||||||
T![.] if p.at(T![..=]) => (2, T![..=]),
|
T![.] if p.at(T![..=]) => (2, T![..=], Left),
|
||||||
T![.] if p.at(T![..]) => (2, T![..]),
|
T![.] if p.at(T![..]) => (2, T![..], Left),
|
||||||
T![!] if p.at(T![!=]) => (5, T![!=]),
|
T![!] if p.at(T![!=]) => (5, T![!=], Left),
|
||||||
T![-] if p.at(T![-=]) => (1, T![-=]),
|
T![-] if p.at(T![-=]) => (1, T![-=], Right),
|
||||||
T![-] => (10, T![-]),
|
T![-] => (10, T![-], Left),
|
||||||
T![as] => (12, T![as]),
|
T![as] => (12, T![as], Left),
|
||||||
|
|
||||||
_ => NOT_AN_OP
|
_ => NOT_AN_OP
|
||||||
}
|
}
|
||||||
@ -273,7 +282,7 @@ fn expr_bp(
|
|||||||
|
|
||||||
loop {
|
loop {
|
||||||
let is_range = p.at(T![..]) || p.at(T![..=]);
|
let is_range = p.at(T![..]) || p.at(T![..=]);
|
||||||
let (op_bp, op) = current_op(p);
|
let (op_bp, op, associativity) = current_op(p);
|
||||||
if op_bp < bp {
|
if op_bp < bp {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -306,7 +315,11 @@ fn expr_bp(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
expr_bp(p, None, Restrictions { prefer_stmt: false, ..r }, op_bp + 1);
|
let op_bp = match associativity {
|
||||||
|
Associativity::Left => op_bp + 1,
|
||||||
|
Associativity::Right => op_bp,
|
||||||
|
};
|
||||||
|
expr_bp(p, None, Restrictions { prefer_stmt: false, ..r }, op_bp);
|
||||||
lhs = m.complete(p, if is_range { RANGE_EXPR } else { BIN_EXPR });
|
lhs = m.complete(p, if is_range { RANGE_EXPR } else { BIN_EXPR });
|
||||||
}
|
}
|
||||||
Some((lhs, BlockLike::NotBlock))
|
Some((lhs, BlockLike::NotBlock))
|
||||||
|
@ -183,4 +183,273 @@ SOURCE_FILE
|
|||||||
COMMENT "//---&*1 - --2 * 9;"
|
COMMENT "//---&*1 - --2 * 9;"
|
||||||
WHITESPACE "\n"
|
WHITESPACE "\n"
|
||||||
R_CURLY "}"
|
R_CURLY "}"
|
||||||
|
WHITESPACE "\n\n"
|
||||||
|
FN
|
||||||
|
FN_KW "fn"
|
||||||
|
WHITESPACE " "
|
||||||
|
NAME
|
||||||
|
IDENT "right_associative"
|
||||||
|
PARAM_LIST
|
||||||
|
L_PAREN "("
|
||||||
|
R_PAREN ")"
|
||||||
|
WHITESPACE " "
|
||||||
|
BLOCK_EXPR
|
||||||
|
STMT_LIST
|
||||||
|
L_CURLY "{"
|
||||||
|
WHITESPACE "\n "
|
||||||
|
EXPR_STMT
|
||||||
|
BIN_EXPR
|
||||||
|
PATH_EXPR
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "a"
|
||||||
|
WHITESPACE " "
|
||||||
|
EQ "="
|
||||||
|
WHITESPACE " "
|
||||||
|
BIN_EXPR
|
||||||
|
PATH_EXPR
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "b"
|
||||||
|
WHITESPACE " "
|
||||||
|
EQ "="
|
||||||
|
WHITESPACE " "
|
||||||
|
PATH_EXPR
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "c"
|
||||||
|
SEMICOLON ";"
|
||||||
|
WHITESPACE "\n "
|
||||||
|
EXPR_STMT
|
||||||
|
BIN_EXPR
|
||||||
|
PATH_EXPR
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "a"
|
||||||
|
WHITESPACE " "
|
||||||
|
EQ "="
|
||||||
|
WHITESPACE " "
|
||||||
|
BIN_EXPR
|
||||||
|
PATH_EXPR
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "b"
|
||||||
|
WHITESPACE " "
|
||||||
|
PLUSEQ "+="
|
||||||
|
WHITESPACE " "
|
||||||
|
BIN_EXPR
|
||||||
|
PATH_EXPR
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "c"
|
||||||
|
WHITESPACE " "
|
||||||
|
MINUSEQ "-="
|
||||||
|
WHITESPACE " "
|
||||||
|
PATH_EXPR
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "d"
|
||||||
|
SEMICOLON ";"
|
||||||
|
WHITESPACE "\n "
|
||||||
|
EXPR_STMT
|
||||||
|
BIN_EXPR
|
||||||
|
PATH_EXPR
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "a"
|
||||||
|
WHITESPACE " "
|
||||||
|
EQ "="
|
||||||
|
WHITESPACE " "
|
||||||
|
BIN_EXPR
|
||||||
|
PATH_EXPR
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "b"
|
||||||
|
WHITESPACE " "
|
||||||
|
STAREQ "*="
|
||||||
|
WHITESPACE " "
|
||||||
|
BIN_EXPR
|
||||||
|
PATH_EXPR
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "c"
|
||||||
|
WHITESPACE " "
|
||||||
|
SLASHEQ "/="
|
||||||
|
WHITESPACE " "
|
||||||
|
BIN_EXPR
|
||||||
|
PATH_EXPR
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "d"
|
||||||
|
WHITESPACE " "
|
||||||
|
PERCENTEQ "%="
|
||||||
|
WHITESPACE " "
|
||||||
|
PATH_EXPR
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "e"
|
||||||
|
SEMICOLON ";"
|
||||||
|
WHITESPACE "\n "
|
||||||
|
EXPR_STMT
|
||||||
|
BIN_EXPR
|
||||||
|
PATH_EXPR
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "a"
|
||||||
|
WHITESPACE " "
|
||||||
|
EQ "="
|
||||||
|
WHITESPACE " "
|
||||||
|
BIN_EXPR
|
||||||
|
PATH_EXPR
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "b"
|
||||||
|
WHITESPACE " "
|
||||||
|
AMPEQ "&="
|
||||||
|
WHITESPACE " "
|
||||||
|
BIN_EXPR
|
||||||
|
PATH_EXPR
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "c"
|
||||||
|
WHITESPACE " "
|
||||||
|
PIPEEQ "|="
|
||||||
|
WHITESPACE " "
|
||||||
|
BIN_EXPR
|
||||||
|
PATH_EXPR
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "d"
|
||||||
|
WHITESPACE " "
|
||||||
|
CARETEQ "^="
|
||||||
|
WHITESPACE " "
|
||||||
|
PATH_EXPR
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "e"
|
||||||
|
SEMICOLON ";"
|
||||||
|
WHITESPACE "\n "
|
||||||
|
EXPR_STMT
|
||||||
|
BIN_EXPR
|
||||||
|
PATH_EXPR
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "a"
|
||||||
|
WHITESPACE " "
|
||||||
|
EQ "="
|
||||||
|
WHITESPACE " "
|
||||||
|
BIN_EXPR
|
||||||
|
PATH_EXPR
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "b"
|
||||||
|
WHITESPACE " "
|
||||||
|
SHLEQ "<<="
|
||||||
|
WHITESPACE " "
|
||||||
|
BIN_EXPR
|
||||||
|
PATH_EXPR
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "c"
|
||||||
|
WHITESPACE " "
|
||||||
|
SHREQ ">>="
|
||||||
|
WHITESPACE " "
|
||||||
|
PATH_EXPR
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "d"
|
||||||
|
SEMICOLON ";"
|
||||||
|
WHITESPACE "\n"
|
||||||
|
R_CURLY "}"
|
||||||
|
WHITESPACE "\n\n"
|
||||||
|
FN
|
||||||
|
FN_KW "fn"
|
||||||
|
WHITESPACE " "
|
||||||
|
NAME
|
||||||
|
IDENT "mixed_associativity"
|
||||||
|
PARAM_LIST
|
||||||
|
L_PAREN "("
|
||||||
|
R_PAREN ")"
|
||||||
|
WHITESPACE " "
|
||||||
|
BLOCK_EXPR
|
||||||
|
STMT_LIST
|
||||||
|
L_CURLY "{"
|
||||||
|
WHITESPACE "\n "
|
||||||
|
COMMENT "// (a + b) = (c += ((d * e) = f))"
|
||||||
|
WHITESPACE "\n "
|
||||||
|
EXPR_STMT
|
||||||
|
BIN_EXPR
|
||||||
|
BIN_EXPR
|
||||||
|
PATH_EXPR
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "a"
|
||||||
|
WHITESPACE " "
|
||||||
|
PLUS "+"
|
||||||
|
WHITESPACE " "
|
||||||
|
PATH_EXPR
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "b"
|
||||||
|
WHITESPACE " "
|
||||||
|
EQ "="
|
||||||
|
WHITESPACE " "
|
||||||
|
BIN_EXPR
|
||||||
|
PATH_EXPR
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "c"
|
||||||
|
WHITESPACE " "
|
||||||
|
PLUSEQ "+="
|
||||||
|
WHITESPACE " "
|
||||||
|
BIN_EXPR
|
||||||
|
BIN_EXPR
|
||||||
|
PATH_EXPR
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "d"
|
||||||
|
WHITESPACE " "
|
||||||
|
STAR "*"
|
||||||
|
WHITESPACE " "
|
||||||
|
PATH_EXPR
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "e"
|
||||||
|
WHITESPACE " "
|
||||||
|
EQ "="
|
||||||
|
WHITESPACE " "
|
||||||
|
PATH_EXPR
|
||||||
|
PATH
|
||||||
|
PATH_SEGMENT
|
||||||
|
NAME_REF
|
||||||
|
IDENT "f"
|
||||||
|
SEMICOLON ";"
|
||||||
|
WHITESPACE "\n"
|
||||||
|
R_CURLY "}"
|
||||||
WHITESPACE "\n"
|
WHITESPACE "\n"
|
||||||
|
@ -12,3 +12,16 @@ fn binding_power() {
|
|||||||
//1 = 2 .. 3;
|
//1 = 2 .. 3;
|
||||||
//---&*1 - --2 * 9;
|
//---&*1 - --2 * 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn right_associative() {
|
||||||
|
a = b = c;
|
||||||
|
a = b += c -= d;
|
||||||
|
a = b *= c /= d %= e;
|
||||||
|
a = b &= c |= d ^= e;
|
||||||
|
a = b <<= c >>= d;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mixed_associativity() {
|
||||||
|
// (a + b) = (c += ((d * e) = f))
|
||||||
|
a + b = c += d * e = f;
|
||||||
|
}
|
||||||
|
@ -168,42 +168,46 @@ SOURCE_FILE
|
|||||||
WHITESPACE "\n "
|
WHITESPACE "\n "
|
||||||
EXPR_STMT
|
EXPR_STMT
|
||||||
BIN_EXPR
|
BIN_EXPR
|
||||||
BIN_EXPR
|
CALL_EXPR
|
||||||
CALL_EXPR
|
PATH_EXPR
|
||||||
PATH_EXPR
|
PATH
|
||||||
PATH
|
PATH_SEGMENT
|
||||||
PATH_SEGMENT
|
NAME_REF
|
||||||
NAME_REF
|
IDENT "Some"
|
||||||
IDENT "Some"
|
ARG_LIST
|
||||||
ARG_LIST
|
L_PAREN "("
|
||||||
L_PAREN "("
|
RANGE_EXPR
|
||||||
RANGE_EXPR
|
DOT2 ".."
|
||||||
DOT2 ".."
|
R_PAREN ")"
|
||||||
R_PAREN ")"
|
WHITESPACE " "
|
||||||
WHITESPACE " "
|
EQ "="
|
||||||
EQ "="
|
WHITESPACE " "
|
||||||
WHITESPACE " "
|
CALL_EXPR
|
||||||
METHOD_CALL_EXPR
|
PATH_EXPR
|
||||||
CALL_EXPR
|
PATH
|
||||||
PATH_EXPR
|
PATH_SEGMENT
|
||||||
PATH
|
NAME_REF
|
||||||
PATH_SEGMENT
|
IDENT "Some"
|
||||||
NAME_REF
|
ARG_LIST
|
||||||
IDENT "Some"
|
L_PAREN "("
|
||||||
ARG_LIST
|
LITERAL
|
||||||
L_PAREN "("
|
INT_NUMBER "0"
|
||||||
LITERAL
|
R_PAREN ")"
|
||||||
INT_NUMBER "0"
|
SEMICOLON ";"
|
||||||
R_PAREN ")"
|
WHITESPACE "\n "
|
||||||
DOT "."
|
EXPR_STMT
|
||||||
WHITESPACE "\n "
|
BIN_EXPR
|
||||||
NAME_REF
|
CALL_EXPR
|
||||||
IDENT "Ok"
|
PATH_EXPR
|
||||||
ARG_LIST
|
PATH
|
||||||
L_PAREN "("
|
PATH_SEGMENT
|
||||||
UNDERSCORE_EXPR
|
NAME_REF
|
||||||
UNDERSCORE "_"
|
IDENT "Ok"
|
||||||
R_PAREN ")"
|
ARG_LIST
|
||||||
|
L_PAREN "("
|
||||||
|
UNDERSCORE_EXPR
|
||||||
|
UNDERSCORE "_"
|
||||||
|
R_PAREN ")"
|
||||||
WHITESPACE " "
|
WHITESPACE " "
|
||||||
EQ "="
|
EQ "="
|
||||||
WHITESPACE " "
|
WHITESPACE " "
|
||||||
|
@ -4,7 +4,7 @@ fn foo() {
|
|||||||
(_) = ..;
|
(_) = ..;
|
||||||
struct S { a: i32 }
|
struct S { a: i32 }
|
||||||
S { .. } = S { ..S::default() };
|
S { .. } = S { ..S::default() };
|
||||||
Some(..) = Some(0).
|
Some(..) = Some(0);
|
||||||
Ok(_) = 0;
|
Ok(_) = 0;
|
||||||
let (a, b);
|
let (a, b);
|
||||||
[a, .., b] = [1, .., 2];
|
[a, .., b] = [1, .., 2];
|
||||||
|
Loading…
Reference in New Issue
Block a user