Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/syntax/src/ast/prec.rs')
-rw-r--r--crates/syntax/src/ast/prec.rs112
1 files changed, 92 insertions, 20 deletions
diff --git a/crates/syntax/src/ast/prec.rs b/crates/syntax/src/ast/prec.rs
index 5d33f132ac..4f0e2cad17 100644
--- a/crates/syntax/src/ast/prec.rs
+++ b/crates/syntax/src/ast/prec.rs
@@ -1,5 +1,7 @@
//! Precedence representation.
+use stdx::always;
+
use crate::{
ast::{self, BinaryOp, Expr, HasArgList, RangeItem},
match_ast, AstNode, SyntaxNode,
@@ -7,7 +9,7 @@ use crate::{
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub enum ExprPrecedence {
- // return, break, yield, closures
+ // return val, break val, yield val, closures
Jump,
// = += -= *= /= %= &= |= ^= <<= >>=
Assign,
@@ -35,10 +37,27 @@ pub enum ExprPrecedence {
Cast,
// unary - * ! & &mut
Prefix,
- // paths, loops, function calls, array indexing, field expressions, method calls
+ // function calls, array indexing, field expressions, method calls
+ Postfix,
+ // paths, loops,
Unambiguous,
}
+impl ExprPrecedence {
+ pub fn needs_parentheses_in(self, other: ExprPrecedence) -> bool {
+ match other {
+ ExprPrecedence::Unambiguous => false,
+ // postfix ops have higher precedence than any other operator, so we need to wrap
+ // any inner expression that is below
+ ExprPrecedence::Postfix => self < ExprPrecedence::Postfix,
+ // We need to wrap all binary like things, thats everything below prefix except for
+ // jumps (as those are prefix operations as well)
+ ExprPrecedence::Prefix => ExprPrecedence::Jump < self && self < ExprPrecedence::Prefix,
+ parent => self <= parent,
+ }
+ }
+}
+
#[derive(PartialEq, Debug)]
pub enum Fixity {
/// The operator is left-associative
@@ -56,11 +75,18 @@ pub fn precedence(expr: &ast::Expr) -> ExprPrecedence {
Some(_) => ExprPrecedence::Unambiguous,
},
+ Expr::BreakExpr(e) if e.expr().is_some() => ExprPrecedence::Jump,
+ Expr::BecomeExpr(e) if e.expr().is_some() => ExprPrecedence::Jump,
+ Expr::ReturnExpr(e) if e.expr().is_some() => ExprPrecedence::Jump,
+ Expr::YeetExpr(e) if e.expr().is_some() => ExprPrecedence::Jump,
+ Expr::YieldExpr(e) if e.expr().is_some() => ExprPrecedence::Jump,
+
Expr::BreakExpr(_)
- | Expr::ContinueExpr(_)
+ | Expr::BecomeExpr(_)
| Expr::ReturnExpr(_)
| Expr::YeetExpr(_)
- | Expr::YieldExpr(_) => ExprPrecedence::Jump,
+ | Expr::YieldExpr(_)
+ | Expr::ContinueExpr(_) => ExprPrecedence::Unambiguous,
Expr::RangeExpr(..) => ExprPrecedence::Range,
@@ -89,33 +115,49 @@ pub fn precedence(expr: &ast::Expr) -> ExprPrecedence {
Expr::LetExpr(_) | Expr::PrefixExpr(_) | Expr::RefExpr(_) => ExprPrecedence::Prefix,
+ Expr::AwaitExpr(_)
+ | Expr::CallExpr(_)
+ | Expr::FieldExpr(_)
+ | Expr::IndexExpr(_)
+ | Expr::MethodCallExpr(_)
+ | Expr::TryExpr(_) => ExprPrecedence::Postfix,
+
Expr::ArrayExpr(_)
| Expr::AsmExpr(_)
- | Expr::AwaitExpr(_)
- | Expr::BecomeExpr(_)
| Expr::BlockExpr(_)
- | Expr::CallExpr(_)
- | Expr::FieldExpr(_)
| Expr::ForExpr(_)
| Expr::FormatArgsExpr(_)
| Expr::IfExpr(_)
- | Expr::IndexExpr(_)
| Expr::Literal(_)
| Expr::LoopExpr(_)
| Expr::MacroExpr(_)
| Expr::MatchExpr(_)
- | Expr::MethodCallExpr(_)
| Expr::OffsetOfExpr(_)
| Expr::ParenExpr(_)
| Expr::PathExpr(_)
| Expr::RecordExpr(_)
- | Expr::TryExpr(_)
| Expr::TupleExpr(_)
| Expr::UnderscoreExpr(_)
| Expr::WhileExpr(_) => ExprPrecedence::Unambiguous,
}
}
+fn check_ancestry(ancestor: &SyntaxNode, descendent: &SyntaxNode) -> bool {
+ let bail = || always!(false, "{} is not an ancestor of {}", ancestor, descendent);
+
+ if !ancestor.text_range().contains_range(descendent.text_range()) {
+ return bail();
+ }
+
+ for anc in descendent.ancestors() {
+ if anc == *ancestor {
+ return true;
+ }
+ }
+
+ bail()
+}
+
impl Expr {
pub fn precedence(&self) -> ExprPrecedence {
precedence(self)
@@ -128,10 +170,20 @@ impl Expr {
// - https://github.com/rust-lang/rust/blob/b6852428a8ea9728369b64b9964cad8e258403d3/compiler/rustc_ast/src/util/parser.rs#L296
/// Returns `true` if `self` would need to be wrapped in parentheses given that its parent is `parent`.
- pub fn needs_parens_in(&self, parent: SyntaxNode) -> bool {
+ pub fn needs_parens_in(&self, parent: &SyntaxNode) -> bool {
+ self.needs_parens_in_place_of(parent, self.syntax())
+ }
+
+ /// Returns `true` if `self` would need to be wrapped in parentheses if it replaces `place_of`
+ /// given that `place_of`'s parent is `parent`.
+ pub fn needs_parens_in_place_of(&self, parent: &SyntaxNode, place_of: &SyntaxNode) -> bool {
+ if !check_ancestry(parent, place_of) {
+ return false;
+ }
+
match_ast! {
match parent {
- ast::Expr(e) => self.needs_parens_in_expr(&e),
+ ast::Expr(e) => self.needs_parens_in_expr(&e, place_of),
ast::Stmt(e) => self.needs_parens_in_stmt(Some(&e)),
ast::StmtList(_) => self.needs_parens_in_stmt(None),
ast::ArgList(_) => false,
@@ -141,7 +193,7 @@ impl Expr {
}
}
- fn needs_parens_in_expr(&self, parent: &Expr) -> bool {
+ fn needs_parens_in_expr(&self, parent: &Expr, place_of: &SyntaxNode) -> bool {
// Parentheses are necessary when calling a function-like pointer that is a member of a struct or union
// (e.g. `(a.f)()`).
let is_parent_call_expr = matches!(parent, ast::Expr::CallExpr(_));
@@ -175,13 +227,17 @@ impl Expr {
if self.is_paren_like()
|| parent.is_paren_like()
- || self.is_prefix() && (parent.is_prefix() || !self.is_ordered_before(parent))
- || self.is_postfix() && (parent.is_postfix() || self.is_ordered_before(parent))
+ || self.is_prefix()
+ && (parent.is_prefix()
+ || !self.is_ordered_before_parent_in_place_of(parent, place_of))
+ || self.is_postfix()
+ && (parent.is_postfix()
+ || self.is_ordered_before_parent_in_place_of(parent, place_of))
{
return false;
}
- let (left, right, inv) = match self.is_ordered_before(parent) {
+ let (left, right, inv) = match self.is_ordered_before_parent_in_place_of(parent, place_of) {
true => (self, parent, false),
false => (parent, self, true),
};
@@ -384,17 +440,33 @@ impl Expr {
BreakExpr(e) => e.expr().is_none(),
ContinueExpr(_) => true,
YieldExpr(e) => e.expr().is_none(),
+ BecomeExpr(e) => e.expr().is_none(),
_ => false,
}
}
- fn is_ordered_before(&self, other: &Expr) -> bool {
+ fn is_ordered_before_parent_in_place_of(&self, parent: &Expr, place_of: &SyntaxNode) -> bool {
+ use rowan::TextSize;
use Expr::*;
- return order(self) < order(other);
+ let self_range = self.syntax().text_range();
+ let place_of_range = place_of.text_range();
+
+ let self_order_adjusted = order(self) - self_range.start() + place_of_range.start();
+
+ let parent_order = order(parent);
+ let parent_order_adjusted = if parent_order <= place_of_range.start() {
+ parent_order
+ } else if parent_order >= place_of_range.end() {
+ parent_order - place_of_range.len() + self_range.len()
+ } else {
+ return false;
+ };
+
+ return self_order_adjusted < parent_order_adjusted;
/// Returns text range that can be used to compare two expression for order (which goes first).
- fn order(this: &Expr) -> rowan::TextSize {
+ fn order(this: &Expr) -> TextSize {
// For non-paren-like operators: get the operator itself
let token = match this {
RangeExpr(e) => e.op_token(),