mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-21 07:41:58 +00:00
fix: Prevent wrong invocations of needs_parens_in
with non-ancestral "parent"s
This commit is contained in:
parent
c6ea7cbafa
commit
5b202cb663
4 changed files with 120 additions and 17 deletions
|
@ -1,5 +1,7 @@
|
|||
//! Precedence representation.
|
||||
|
||||
use stdx::always;
|
||||
|
||||
use crate::{
|
||||
ast::{self, BinaryOp, Expr, HasArgList, RangeItem},
|
||||
match_ast, AstNode, SyntaxNode,
|
||||
|
@ -140,6 +142,22 @@ pub fn precedence(expr: &ast::Expr) -> ExprPrecedence {
|
|||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -153,9 +171,19 @@ impl Expr {
|
|||
|
||||
/// 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 {
|
||||
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,
|
||||
|
@ -165,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(_));
|
||||
|
@ -199,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),
|
||||
};
|
||||
|
@ -413,13 +445,28 @@ impl Expr {
|
|||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue