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::*;
|
||||
|
||||
pub(crate) use self::atom::{block_expr, match_arm_list};
|
||||
pub(super) use self::atom::{literal, LITERAL_FIRST};
|
||||
pub(crate) use atom::{block_expr, match_arm_list};
|
||||
pub(super) use atom::{literal, LITERAL_FIRST};
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub(super) enum Semicolon {
|
||||
@ -188,47 +188,56 @@ struct Restrictions {
|
||||
prefer_stmt: bool,
|
||||
}
|
||||
|
||||
enum Associativity {
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
/// Binding powers of operators for a Pratt parser.
|
||||
///
|
||||
/// 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]
|
||||
fn current_op(p: &Parser<'_>) -> (u8, SyntaxKind) {
|
||||
const NOT_AN_OP: (u8, SyntaxKind) = (0, T![@]);
|
||||
fn current_op(p: &Parser<'_>) -> (u8, SyntaxKind, Associativity) {
|
||||
use Associativity::*;
|
||||
const NOT_AN_OP: (u8, SyntaxKind, Associativity) = (0, T![@], Left);
|
||||
match p.current() {
|
||||
T![|] if p.at(T![||]) => (3, T![||]),
|
||||
T![|] if p.at(T![|=]) => (1, T![|=]),
|
||||
T![|] => (6, T![|]),
|
||||
T![>] if p.at(T![>>=]) => (1, T![>>=]),
|
||||
T![>] if p.at(T![>>]) => (9, T![>>]),
|
||||
T![>] if p.at(T![>=]) => (5, T![>=]),
|
||||
T![>] => (5, T![>]),
|
||||
T![|] if p.at(T![||]) => (3, T![||], Left),
|
||||
T![|] if p.at(T![|=]) => (1, T![|=], Right),
|
||||
T![|] => (6, T![|], Left),
|
||||
T![>] if p.at(T![>>=]) => (1, T![>>=], Right),
|
||||
T![>] if p.at(T![>>]) => (9, T![>>], Left),
|
||||
T![>] if p.at(T![>=]) => (5, T![>=], Left),
|
||||
T![>] => (5, T![>], Left),
|
||||
T![=] if p.at(T![=>]) => NOT_AN_OP,
|
||||
T![=] if p.at(T![==]) => (5, T![==]),
|
||||
T![=] => (1, T![=]),
|
||||
T![<] if p.at(T![<=]) => (5, T![<=]),
|
||||
T![<] if p.at(T![<<=]) => (1, T![<<=]),
|
||||
T![<] if p.at(T![<<]) => (9, T![<<]),
|
||||
T![<] => (5, T![<]),
|
||||
T![+] if p.at(T![+=]) => (1, T![+=]),
|
||||
T![+] => (10, T![+]),
|
||||
T![^] if p.at(T![^=]) => (1, T![^=]),
|
||||
T![^] => (7, T![^]),
|
||||
T![%] if p.at(T![%=]) => (1, T![%=]),
|
||||
T![%] => (11, T![%]),
|
||||
T![&] if p.at(T![&=]) => (1, T![&=]),
|
||||
T![=] if p.at(T![==]) => (5, T![==], Left),
|
||||
T![=] => (1, T![=], Right),
|
||||
T![<] if p.at(T![<=]) => (5, T![<=], Left),
|
||||
T![<] if p.at(T![<<=]) => (1, T![<<=], Right),
|
||||
T![<] if p.at(T![<<]) => (9, T![<<], Left),
|
||||
T![<] => (5, T![<], Left),
|
||||
T![+] if p.at(T![+=]) => (1, T![+=], Right),
|
||||
T![+] => (10, T![+], Left),
|
||||
T![^] if p.at(T![^=]) => (1, T![^=], Right),
|
||||
T![^] => (7, T![^], Left),
|
||||
T![%] if p.at(T![%=]) => (1, T![%=], Right),
|
||||
T![%] => (11, T![%], Left),
|
||||
T![&] if p.at(T![&=]) => (1, T![&=], Right),
|
||||
// If you update this, remember to update `expr_let()` too.
|
||||
T![&] if p.at(T![&&]) => (4, T![&&]),
|
||||
T![&] => (8, T![&]),
|
||||
T![/] if p.at(T![/=]) => (1, T![/=]),
|
||||
T![/] => (11, T![/]),
|
||||
T![*] if p.at(T![*=]) => (1, T![*=]),
|
||||
T![*] => (11, T![*]),
|
||||
T![.] if p.at(T![..=]) => (2, T![..=]),
|
||||
T![.] if p.at(T![..]) => (2, T![..]),
|
||||
T![!] if p.at(T![!=]) => (5, T![!=]),
|
||||
T![-] if p.at(T![-=]) => (1, T![-=]),
|
||||
T![-] => (10, T![-]),
|
||||
T![as] => (12, T![as]),
|
||||
T![&] if p.at(T![&&]) => (4, T![&&], Left),
|
||||
T![&] => (8, T![&], Left),
|
||||
T![/] if p.at(T![/=]) => (1, T![/=], Right),
|
||||
T![/] => (11, T![/], Left),
|
||||
T![*] if p.at(T![*=]) => (1, T![*=], Right),
|
||||
T![*] => (11, T![*], Left),
|
||||
T![.] if p.at(T![..=]) => (2, T![..=], Left),
|
||||
T![.] if p.at(T![..]) => (2, T![..], Left),
|
||||
T![!] if p.at(T![!=]) => (5, T![!=], Left),
|
||||
T![-] if p.at(T![-=]) => (1, T![-=], Right),
|
||||
T![-] => (10, T![-], Left),
|
||||
T![as] => (12, T![as], Left),
|
||||
|
||||
_ => NOT_AN_OP
|
||||
}
|
||||
@ -273,7 +282,7 @@ fn expr_bp(
|
||||
|
||||
loop {
|
||||
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 {
|
||||
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 });
|
||||
}
|
||||
Some((lhs, BlockLike::NotBlock))
|
||||
|
@ -183,4 +183,273 @@ SOURCE_FILE
|
||||
COMMENT "//---&*1 - --2 * 9;"
|
||||
WHITESPACE "\n"
|
||||
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"
|
||||
|
@ -12,3 +12,16 @@ fn binding_power() {
|
||||
//1 = 2 .. 3;
|
||||
//---&*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 "
|
||||
EXPR_STMT
|
||||
BIN_EXPR
|
||||
BIN_EXPR
|
||||
CALL_EXPR
|
||||
PATH_EXPR
|
||||
PATH
|
||||
PATH_SEGMENT
|
||||
NAME_REF
|
||||
IDENT "Some"
|
||||
ARG_LIST
|
||||
L_PAREN "("
|
||||
RANGE_EXPR
|
||||
DOT2 ".."
|
||||
R_PAREN ")"
|
||||
WHITESPACE " "
|
||||
EQ "="
|
||||
WHITESPACE " "
|
||||
METHOD_CALL_EXPR
|
||||
CALL_EXPR
|
||||
PATH_EXPR
|
||||
PATH
|
||||
PATH_SEGMENT
|
||||
NAME_REF
|
||||
IDENT "Some"
|
||||
ARG_LIST
|
||||
L_PAREN "("
|
||||
LITERAL
|
||||
INT_NUMBER "0"
|
||||
R_PAREN ")"
|
||||
DOT "."
|
||||
WHITESPACE "\n "
|
||||
NAME_REF
|
||||
IDENT "Ok"
|
||||
ARG_LIST
|
||||
L_PAREN "("
|
||||
UNDERSCORE_EXPR
|
||||
UNDERSCORE "_"
|
||||
R_PAREN ")"
|
||||
CALL_EXPR
|
||||
PATH_EXPR
|
||||
PATH
|
||||
PATH_SEGMENT
|
||||
NAME_REF
|
||||
IDENT "Some"
|
||||
ARG_LIST
|
||||
L_PAREN "("
|
||||
RANGE_EXPR
|
||||
DOT2 ".."
|
||||
R_PAREN ")"
|
||||
WHITESPACE " "
|
||||
EQ "="
|
||||
WHITESPACE " "
|
||||
CALL_EXPR
|
||||
PATH_EXPR
|
||||
PATH
|
||||
PATH_SEGMENT
|
||||
NAME_REF
|
||||
IDENT "Some"
|
||||
ARG_LIST
|
||||
L_PAREN "("
|
||||
LITERAL
|
||||
INT_NUMBER "0"
|
||||
R_PAREN ")"
|
||||
SEMICOLON ";"
|
||||
WHITESPACE "\n "
|
||||
EXPR_STMT
|
||||
BIN_EXPR
|
||||
CALL_EXPR
|
||||
PATH_EXPR
|
||||
PATH
|
||||
PATH_SEGMENT
|
||||
NAME_REF
|
||||
IDENT "Ok"
|
||||
ARG_LIST
|
||||
L_PAREN "("
|
||||
UNDERSCORE_EXPR
|
||||
UNDERSCORE "_"
|
||||
R_PAREN ")"
|
||||
WHITESPACE " "
|
||||
EQ "="
|
||||
WHITESPACE " "
|
||||
|
@ -4,7 +4,7 @@ fn foo() {
|
||||
(_) = ..;
|
||||
struct S { a: i32 }
|
||||
S { .. } = S { ..S::default() };
|
||||
Some(..) = Some(0).
|
||||
Some(..) = Some(0);
|
||||
Ok(_) = 0;
|
||||
let (a, b);
|
||||
[a, .., b] = [1, .., 2];
|
||||
|
Loading…
Reference in New Issue
Block a user