diff --git a/docs/design/expressions/README.md b/docs/design/expressions/README.md
index a6dccf90ea25..aa411906c558 100644
--- a/docs/design/expressions/README.md
+++ b/docs/design/expressions/README.md
@@ -85,6 +85,9 @@ graph BT
complement["^x"]
click complement "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/bitwise.md"
+ incDec["++x;
--x;"]
+ click incDec "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/assignment.md"
+
unary((" "))
as["x as T"]
@@ -136,7 +139,12 @@ graph BT
if>"if x then y else z"]
click if "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/if.md"
- expressionEnd["x;"]
+ insideParens["(...)"]
+
+ assignment["x = y;
x $= y;"]
+ click assignment "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/assignment.md"
+
+ expressionStatement["x;"]
top --> parens & braces & unqualifiedName
@@ -146,8 +154,7 @@ graph BT
memberAccess --> top
pointer --> memberAccess
- negation --> pointer
- complement --> pointer
+ negation & complement & incDec --> pointer
unary --> negation & complement
%% Use a longer arrow here to put `not` next to `and` and `or`.
not -------> memberAccess
@@ -157,7 +164,8 @@ graph BT
logicalOperand --> comparison & not
and & or --> logicalOperand
logicalExpression --> and & or
- if & expressionEnd --> logicalExpression
+ if & expressionStatement --> logicalExpression
+ insideParens & assignment --> if
```
The diagram's attributes are:
diff --git a/toolchain/parser/precedence.cpp b/toolchain/parser/precedence.cpp
index 96a312d99f67..d1432274ea04 100644
--- a/toolchain/parser/precedence.cpp
+++ b/toolchain/parser/precedence.cpp
@@ -17,6 +17,7 @@ enum PrecedenceLevel : int8_t {
// Terms.
TermPrefix,
// Numeric.
+ IncrementDecrement,
NumericPrefix,
Modulo,
Multiplicative,
@@ -53,7 +54,8 @@ struct OperatorPriorityTable {
// Start with a list of ,
// relationships.
MarkHigherThan({Highest}, {TermPrefix, LogicalPrefix});
- MarkHigherThan({TermPrefix}, {NumericPrefix, BitwisePrefix});
+ MarkHigherThan({TermPrefix},
+ {NumericPrefix, BitwisePrefix, IncrementDecrement});
MarkHigherThan({NumericPrefix, BitwisePrefix},
{As, Multiplicative, Modulo, BitwiseAnd, BitwiseOr,
BitwiseXor, BitShift});
@@ -64,7 +66,7 @@ struct OperatorPriorityTable {
MarkHigherThan({Relational, LogicalPrefix}, {LogicalAnd, LogicalOr});
MarkHigherThan({LogicalAnd, LogicalOr}, {If});
MarkHigherThan({If}, {Assignment});
- MarkHigherThan({Assignment}, {Lowest});
+ MarkHigherThan({Assignment, IncrementDecrement}, {Lowest});
// Types are mostly a separate precedence graph.
MarkHigherThan({Highest}, {TypePrefix});
@@ -208,7 +210,7 @@ auto PrecedenceGroup::ForLeading(TokenKind kind)
case TokenKind::MinusMinus:
case TokenKind::PlusPlus:
- return PrecedenceGroup(Assignment);
+ return PrecedenceGroup(IncrementDecrement);
case TokenKind::Caret:
return PrecedenceGroup(BitwisePrefix);
diff --git a/toolchain/parser/testdata/operators/fail_precedence_assign.carbon b/toolchain/parser/testdata/operators/fail_precedence_assign.carbon
index 253dfe243cf0..68f75145fcad 100644
--- a/toolchain/parser/testdata/operators/fail_precedence_assign.carbon
+++ b/toolchain/parser/testdata/operators/fail_precedence_assign.carbon
@@ -23,6 +23,10 @@ fn F() {
// CHECK:STDERR: a + ++a;
// CHECK:STDERR: ^
a + ++a;
+ // CHECK:STDERR: fail_precedence_assign.carbon:[[@LINE+3]]:5: Parentheses are required around this unary `if` operator.
+ // CHECK:STDERR: ++if c then a else b;
+ // CHECK:STDERR: ^
+ ++if c then a else b;
}
// CHECK:STDOUT: [
@@ -56,6 +60,14 @@ fn F() {
// CHECK:STDOUT: {kind: 'PrefixOperator', text: '++', subtree_size: 2},
// CHECK:STDOUT: {kind: 'InfixOperator', text: '+', subtree_size: 4},
// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 5},
-// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 31},
+// CHECK:STDOUT: {kind: 'NameExpression', text: 'c'},
+// CHECK:STDOUT: {kind: 'IfExpressionIf', text: 'if', subtree_size: 2},
+// CHECK:STDOUT: {kind: 'NameExpression', text: 'a'},
+// CHECK:STDOUT: {kind: 'IfExpressionThen', text: 'then', subtree_size: 2},
+// CHECK:STDOUT: {kind: 'NameExpression', text: 'b'},
+// CHECK:STDOUT: {kind: 'IfExpressionElse', text: 'else', subtree_size: 6},
+// CHECK:STDOUT: {kind: 'PrefixOperator', text: '++', subtree_size: 7},
+// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 8},
+// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 39},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]
diff --git a/toolchain/parser/testdata/operators/precedence_assign.carbon b/toolchain/parser/testdata/operators/precedence_assign.carbon
index 151724b993e2..9b539d8642b4 100644
--- a/toolchain/parser/testdata/operators/precedence_assign.carbon
+++ b/toolchain/parser/testdata/operators/precedence_assign.carbon
@@ -9,9 +9,8 @@ fn F(c: bool) {
var b: i32;
var p: i32*;
*p = if c then 1 else 2;
- // These are valid to _parse_ even though rejected semantically.
+ // This is valid to _parse_ even though rejected semantically.
(if c then a else b) += if c then 1 else 2;
- ++if c then a else b;
}
// CHECK:STDOUT: [
@@ -65,14 +64,6 @@ fn F(c: bool) {
// CHECK:STDOUT: {kind: 'IfExpressionElse', text: 'else', subtree_size: 6},
// CHECK:STDOUT: {kind: 'InfixOperator', text: '+=', subtree_size: 15},
// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 16},
-// CHECK:STDOUT: {kind: 'NameExpression', text: 'c'},
-// CHECK:STDOUT: {kind: 'IfExpressionIf', text: 'if', subtree_size: 2},
-// CHECK:STDOUT: {kind: 'NameExpression', text: 'a'},
-// CHECK:STDOUT: {kind: 'IfExpressionThen', text: 'then', subtree_size: 2},
-// CHECK:STDOUT: {kind: 'NameExpression', text: 'b'},
-// CHECK:STDOUT: {kind: 'IfExpressionElse', text: 'else', subtree_size: 6},
-// CHECK:STDOUT: {kind: 'PrefixOperator', text: '++', subtree_size: 7},
-// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 8},
-// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 59},
+// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 51},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]