-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto merge of #13733 - WaffleLapkin:remove_parens, r=Veykril
feat: Add "Remove redundant parentheses" assist ![Peek 2022-12-08 22-22](https://user-images.githubusercontent.com/38225716/206542898-d6c97468-d615-4c5b-8650-f89b9c0321a0.gif) Can be quite handy when refactoring :)
- Loading branch information
Showing
5 changed files
with
226 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
use syntax::{ast, AstNode}; | ||
|
||
use crate::{AssistContext, AssistId, AssistKind, Assists}; | ||
|
||
// Assist: remove_parentheses | ||
// | ||
// Removes redundant parentheses. | ||
// | ||
// ``` | ||
// fn main() { | ||
// _ = $0(2) + 2; | ||
// } | ||
// ``` | ||
// -> | ||
// ``` | ||
// fn main() { | ||
// _ = 2 + 2; | ||
// } | ||
// ``` | ||
pub(crate) fn remove_parentheses(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { | ||
let parens = ctx.find_node_at_offset::<ast::ParenExpr>()?; | ||
|
||
let cursor_in_range = | ||
parens.l_paren_token()?.text_range().contains_range(ctx.selection_trimmed()) | ||
|| parens.r_paren_token()?.text_range().contains_range(ctx.selection_trimmed()); | ||
if !cursor_in_range { | ||
return None; | ||
} | ||
|
||
let expr = parens.expr()?; | ||
|
||
let parent = ast::Expr::cast(parens.syntax().parent()?); | ||
let is_ok_to_remove = expr.precedence() >= parent.as_ref().and_then(ast::Expr::precedence); | ||
if !is_ok_to_remove { | ||
return None; | ||
} | ||
|
||
let target = parens.syntax().text_range(); | ||
acc.add( | ||
AssistId("remove_parentheses", AssistKind::Refactor), | ||
"Remove redundant parentheses", | ||
target, | ||
|builder| builder.replace_ast(parens.into(), expr), | ||
) | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use crate::tests::{check_assist, check_assist_not_applicable}; | ||
|
||
use super::*; | ||
|
||
#[test] | ||
fn remove_parens_simple() { | ||
check_assist(remove_parentheses, r#"fn f() { $0(2) + 2; }"#, r#"fn f() { 2 + 2; }"#); | ||
check_assist(remove_parentheses, r#"fn f() { ($02) + 2; }"#, r#"fn f() { 2 + 2; }"#); | ||
check_assist(remove_parentheses, r#"fn f() { (2)$0 + 2; }"#, r#"fn f() { 2 + 2; }"#); | ||
check_assist(remove_parentheses, r#"fn f() { (2$0) + 2; }"#, r#"fn f() { 2 + 2; }"#); | ||
} | ||
|
||
#[test] | ||
fn remove_parens_precedence() { | ||
check_assist( | ||
remove_parentheses, | ||
r#"fn f() { $0(2 * 3) + 1; }"#, | ||
r#"fn f() { 2 * 3 + 1; }"#, | ||
); | ||
check_assist(remove_parentheses, r#"fn f() { ( $0(2) ); }"#, r#"fn f() { ( 2 ); }"#); | ||
check_assist(remove_parentheses, r#"fn f() { $0(2?)?; }"#, r#"fn f() { 2??; }"#); | ||
check_assist(remove_parentheses, r#"fn f() { f(($02 + 2)); }"#, r#"fn f() { f(2 + 2); }"#); | ||
check_assist( | ||
remove_parentheses, | ||
r#"fn f() { (1<2)&&$0(3>4); }"#, | ||
r#"fn f() { (1<2)&&3>4; }"#, | ||
); | ||
} | ||
|
||
#[test] | ||
fn remove_parens_doesnt_apply_precedence() { | ||
check_assist_not_applicable(remove_parentheses, r#"fn f() { $0(2 + 2) * 8; }"#); | ||
check_assist_not_applicable(remove_parentheses, r#"fn f() { $0(2 + 2).f(); }"#); | ||
check_assist_not_applicable(remove_parentheses, r#"fn f() { $0(2 + 2).await; }"#); | ||
check_assist_not_applicable(remove_parentheses, r#"fn f() { $0!(2..2); }"#); | ||
} | ||
|
||
#[test] | ||
fn remove_parens_doesnt_apply_with_cursor_not_on_paren() { | ||
check_assist_not_applicable(remove_parentheses, r#"fn f() { (2 +$0 2) }"#); | ||
check_assist_not_applicable(remove_parentheses, r#"fn f() {$0 (2 + 2) }"#); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
//! Precedence representation. | ||
use crate::ast::{self, BinExpr, Expr}; | ||
|
||
/// Precedence of an expression. | ||
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] | ||
pub enum ExprPrecedence { | ||
// N.B.: Order is important | ||
Closure, | ||
Jump, | ||
Range, | ||
Bin(BinOpPresedence), | ||
Prefix, | ||
Postfix, | ||
Paren, | ||
} | ||
|
||
/// Precedence of a binary operator. | ||
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] | ||
pub enum BinOpPresedence { | ||
// N.B.: Order is important | ||
/// `=`, `+=`, `-=`, `*=`, `/=`, `%=`, `|=`, `&=` | ||
Assign, | ||
/// `||` | ||
LOr, | ||
/// `&&` | ||
LAnd, | ||
/// `<`, `<=`, `>`, `>=`, `==` and `!=` | ||
Cmp, | ||
/// `|` | ||
BitOr, | ||
/// `^` | ||
BitXor, | ||
/// `&` | ||
BitAnd, | ||
/// `<<` and `>>` | ||
Shift, | ||
/// `+` and `-` | ||
Add, | ||
/// `*`, `/` and `%` | ||
Mul, | ||
/// `as` | ||
As, | ||
} | ||
|
||
impl Expr { | ||
/// Returns precedence of this expression. | ||
/// Usefull to preserve semantics in assists. | ||
/// | ||
/// Returns `None` if this is a [`BinExpr`] and its [`op_kind`] returns `None`. | ||
/// | ||
/// [`op_kind`]: BinExpr::op_kind | ||
/// [`BinExpr`]: Expr::BinExpr | ||
pub fn precedence(&self) -> Option<ExprPrecedence> { | ||
// Copied from <https://github.com/rust-lang/rust/blob/b6852428a8ea9728369b64b9964cad8e258403d3/compiler/rustc_ast/src/util/parser.rs#L296> | ||
use Expr::*; | ||
|
||
let prec = match self { | ||
ClosureExpr(_) => ExprPrecedence::Closure, | ||
|
||
ContinueExpr(_) | ReturnExpr(_) | YieldExpr(_) | BreakExpr(_) => ExprPrecedence::Jump, | ||
|
||
RangeExpr(_) => ExprPrecedence::Range, | ||
|
||
BinExpr(bin_expr) => return bin_expr.precedence().map(ExprPrecedence::Bin), | ||
CastExpr(_) => ExprPrecedence::Bin(BinOpPresedence::As), | ||
|
||
BoxExpr(_) | RefExpr(_) | LetExpr(_) | PrefixExpr(_) => ExprPrecedence::Prefix, | ||
|
||
AwaitExpr(_) | CallExpr(_) | MethodCallExpr(_) | FieldExpr(_) | IndexExpr(_) | ||
| TryExpr(_) | MacroExpr(_) => ExprPrecedence::Postfix, | ||
|
||
ArrayExpr(_) | TupleExpr(_) | Literal(_) | PathExpr(_) | ParenExpr(_) | IfExpr(_) | ||
| WhileExpr(_) | ForExpr(_) | LoopExpr(_) | MatchExpr(_) | BlockExpr(_) | ||
| RecordExpr(_) | UnderscoreExpr(_) => ExprPrecedence::Paren, | ||
}; | ||
|
||
Some(prec) | ||
} | ||
} | ||
|
||
impl BinExpr { | ||
/// Returns precedence of this binary expression. | ||
/// Usefull to preserve semantics in assists. | ||
/// | ||
/// Returns `None` if [`op_kind`] returns `None`. | ||
/// | ||
/// [`op_kind`]: BinExpr::op_kind | ||
pub fn precedence(&self) -> Option<BinOpPresedence> { | ||
use ast::{ArithOp::*, BinaryOp::*, LogicOp::*}; | ||
|
||
let prec = match self.op_kind()? { | ||
LogicOp(op) => match op { | ||
And => BinOpPresedence::LAnd, | ||
Or => BinOpPresedence::LOr, | ||
}, | ||
ArithOp(op) => match op { | ||
Add => BinOpPresedence::Add, | ||
Mul => BinOpPresedence::Mul, | ||
Sub => BinOpPresedence::Add, | ||
Div => BinOpPresedence::Mul, | ||
Rem => BinOpPresedence::Mul, | ||
Shl => BinOpPresedence::Shift, | ||
Shr => BinOpPresedence::Shift, | ||
BitXor => BinOpPresedence::BitXor, | ||
BitOr => BinOpPresedence::BitOr, | ||
BitAnd => BinOpPresedence::BitAnd, | ||
}, | ||
CmpOp(_) => BinOpPresedence::Cmp, | ||
Assignment { .. } => BinOpPresedence::Assign, | ||
}; | ||
|
||
Some(prec) | ||
} | ||
} |