9890: internal: refactor binary operator handling r=matklad a=matklad

bors r+
🤖

Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
bors[bot] 2021-08-14 15:11:17 +00:00 committed by GitHub
commit bd2219bd99
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 265 additions and 413 deletions

View File

@ -27,9 +27,8 @@
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
db::DefDatabase,
expr::{
dummy_expr_id, ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Label,
LabelId, Literal, LogicOp, MatchArm, MatchGuard, Ordering, Pat, PatId, RecordFieldPat,
RecordLitField, Statement,
dummy_expr_id, Array, BindingAnnotation, Expr, ExprId, Label, LabelId, Literal, MatchArm,
MatchGuard, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
},
intern::Interned,
item_scope::BuiltinShadowMode,
@ -509,7 +508,7 @@ fn maybe_collect_expr(&mut self, expr: ast::Expr) -> Option<ExprId> {
ast::Expr::BinExpr(e) => {
let lhs = self.collect_expr_opt(e.lhs());
let rhs = self.collect_expr_opt(e.rhs());
let op = e.op_kind().map(BinaryOp::from);
let op = e.op_kind();
self.alloc_expr(Expr::BinaryOp { lhs, rhs, op }, syntax_ptr)
}
ast::Expr::TupleExpr(e) => {
@ -954,50 +953,6 @@ fn check_cfg(&mut self, owner: &dyn ast::AttrsOwner) -> Option<()> {
}
}
impl From<ast::BinOp> for BinaryOp {
fn from(ast_op: ast::BinOp) -> Self {
match ast_op {
ast::BinOp::BooleanOr => BinaryOp::LogicOp(LogicOp::Or),
ast::BinOp::BooleanAnd => BinaryOp::LogicOp(LogicOp::And),
ast::BinOp::EqualityTest => BinaryOp::CmpOp(CmpOp::Eq { negated: false }),
ast::BinOp::NegatedEqualityTest => BinaryOp::CmpOp(CmpOp::Eq { negated: true }),
ast::BinOp::LesserEqualTest => {
BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Less, strict: false })
}
ast::BinOp::GreaterEqualTest => {
BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Greater, strict: false })
}
ast::BinOp::LesserTest => {
BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Less, strict: true })
}
ast::BinOp::GreaterTest => {
BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Greater, strict: true })
}
ast::BinOp::Addition => BinaryOp::ArithOp(ArithOp::Add),
ast::BinOp::Multiplication => BinaryOp::ArithOp(ArithOp::Mul),
ast::BinOp::Subtraction => BinaryOp::ArithOp(ArithOp::Sub),
ast::BinOp::Division => BinaryOp::ArithOp(ArithOp::Div),
ast::BinOp::Remainder => BinaryOp::ArithOp(ArithOp::Rem),
ast::BinOp::LeftShift => BinaryOp::ArithOp(ArithOp::Shl),
ast::BinOp::RightShift => BinaryOp::ArithOp(ArithOp::Shr),
ast::BinOp::BitwiseXor => BinaryOp::ArithOp(ArithOp::BitXor),
ast::BinOp::BitwiseOr => BinaryOp::ArithOp(ArithOp::BitOr),
ast::BinOp::BitwiseAnd => BinaryOp::ArithOp(ArithOp::BitAnd),
ast::BinOp::Assignment => BinaryOp::Assignment { op: None },
ast::BinOp::AddAssign => BinaryOp::Assignment { op: Some(ArithOp::Add) },
ast::BinOp::DivAssign => BinaryOp::Assignment { op: Some(ArithOp::Div) },
ast::BinOp::MulAssign => BinaryOp::Assignment { op: Some(ArithOp::Mul) },
ast::BinOp::RemAssign => BinaryOp::Assignment { op: Some(ArithOp::Rem) },
ast::BinOp::ShlAssign => BinaryOp::Assignment { op: Some(ArithOp::Shl) },
ast::BinOp::ShrAssign => BinaryOp::Assignment { op: Some(ArithOp::Shr) },
ast::BinOp::SubAssign => BinaryOp::Assignment { op: Some(ArithOp::Sub) },
ast::BinOp::BitOrAssign => BinaryOp::Assignment { op: Some(ArithOp::BitOr) },
ast::BinOp::BitAndAssign => BinaryOp::Assignment { op: Some(ArithOp::BitAnd) },
ast::BinOp::BitXorAssign => BinaryOp::Assignment { op: Some(ArithOp::BitXor) },
}
}
}
impl From<ast::LiteralKind> for Literal {
fn from(ast_lit_kind: ast::LiteralKind) -> Self {
match ast_lit_kind {

View File

@ -14,7 +14,6 @@
use hir_expand::name::Name;
use la_arena::{Idx, RawIdx};
use syntax::ast::RangeOp;
use crate::{
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
@ -24,6 +23,8 @@
BlockId,
};
pub use syntax::ast::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp};
pub type ExprId = Idx<Expr>;
pub(crate) fn dummy_expr_id() -> ExprId {
ExprId::from_raw(RawIdx::from(!0))
@ -179,47 +180,6 @@ pub enum Expr {
Literal(Literal),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum BinaryOp {
LogicOp(LogicOp),
ArithOp(ArithOp),
CmpOp(CmpOp),
Assignment { op: Option<ArithOp> },
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum LogicOp {
And,
Or,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum CmpOp {
Eq { negated: bool },
Ord { ordering: Ordering, strict: bool },
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Ordering {
Less,
Greater,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum ArithOp {
Add,
Mul,
Sub,
Div,
Rem,
Shl,
Shr,
BitXor,
BitOr,
BitAnd,
}
pub use syntax::ast::PrefixOp as UnaryOp;
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Array {
ElementList(Vec<ExprId>),

View File

@ -126,7 +126,7 @@ pub(super) fn element(
let ty = sema.type_of_expr(&expr)?.original;
if ty.is_raw_ptr() {
HlTag::Operator(HlOperator::Other) | HlMod::Unsafe
} else if let Some(ast::PrefixOp::Deref) = prefix_expr.op_kind() {
} else if let Some(ast::UnaryOp::Deref) = prefix_expr.op_kind() {
HlOperator::Other.into()
} else {
HlPunct::Other.into()

View File

@ -19,14 +19,20 @@
// ->
// ```
// fn main() {
// if !(x == 4 && !(y < 3.14)) {}
// if !(x == 4 && y >= 3.14) {}
// }
// ```
pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
let expr = ctx.find_node_at_offset::<ast::BinExpr>()?;
let op = expr.op_kind()?;
let op_range = expr.op_token()?.text_range();
let opposite_op = opposite_logic_op(op)?;
let opposite_op = match op {
ast::BinaryOp::LogicOp(ast::LogicOp::And) => "||",
ast::BinaryOp::LogicOp(ast::LogicOp::Or) => "&&",
_ => return None,
};
let cursor_in_range = op_range.contains_range(ctx.frange.range);
if !cursor_in_range {
return None;
@ -85,7 +91,7 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<(
.and_then(|paren_expr| paren_expr.syntax().parent())
.and_then(ast::PrefixExpr::cast)
.and_then(|prefix_expr| {
if prefix_expr.op_kind().unwrap() == ast::PrefixOp::Not {
if prefix_expr.op_kind().unwrap() == ast::UnaryOp::Not {
Some(prefix_expr)
} else {
None
@ -99,7 +105,7 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<(
if let Some(paren_expr) = paren_expr {
for term in terms {
let range = term.syntax().text_range();
let not_term = invert_boolean_expression(&ctx.sema, term);
let not_term = invert_boolean_expression(term);
edit.replace(range, not_term.syntax().text());
}
@ -114,21 +120,21 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<(
} else {
if let Some(lhs) = terms.pop_front() {
let lhs_range = lhs.syntax().text_range();
let not_lhs = invert_boolean_expression(&ctx.sema, lhs);
let not_lhs = invert_boolean_expression(lhs);
edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text()));
}
if let Some(rhs) = terms.pop_back() {
let rhs_range = rhs.syntax().text_range();
let not_rhs = invert_boolean_expression(&ctx.sema, rhs);
let not_rhs = invert_boolean_expression(rhs);
edit.replace(rhs_range, format!("{})", not_rhs.syntax().text()));
}
for term in terms {
let term_range = term.syntax().text_range();
let not_term = invert_boolean_expression(&ctx.sema, term);
let not_term = invert_boolean_expression(term);
edit.replace(term_range, not_term.syntax().text());
}
}
@ -136,15 +142,6 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<(
)
}
// Return the opposite text for a given logical operator, if it makes sense
fn opposite_logic_op(kind: ast::BinOp) -> Option<&'static str> {
match kind {
ast::BinOp::BooleanOr => Some("&&"),
ast::BinOp::BooleanAnd => Some("||"),
_ => None,
}
}
#[cfg(test)]
mod tests {
use crate::tests::{check_assist, check_assist_not_applicable};
@ -156,40 +153,12 @@ fn demorgan_handles_leq() {
check_assist(
apply_demorgan,
r#"
//- minicore: ord, derive
#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct S;
fn f() {
S < S &&$0 S <= S
}
"#,
r#"
#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct S;
fn f() {
!(S >= S || S > S)
}
"#,
);
check_assist(
apply_demorgan,
r#"
//- minicore: ord, derive
struct S;
fn f() {
S < S &&$0 S <= S
}
fn f() { S < S &&$0 S <= S }
"#,
r#"
struct S;
fn f() {
!(!(S < S) || !(S <= S))
}
fn f() { !(S >= S || S > S) }
"#,
);
}
@ -199,39 +168,12 @@ fn demorgan_handles_geq() {
check_assist(
apply_demorgan,
r#"
//- minicore: ord, derive
#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct S;
fn f() {
S > S &&$0 S >= S
}
"#,
r#"
#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct S;
fn f() {
!(S <= S || S < S)
}
"#,
);
check_assist(
apply_demorgan,
r#"
//- minicore: ord, derive
struct S;
fn f() {
S > S &&$0 S >= S
}
fn f() { S > S &&$0 S >= S }
"#,
r#"
struct S;
fn f() {
!(!(S > S) || !(S >= S))
}
fn f() { !(S <= S || S < S) }
"#,
);
}

View File

@ -97,7 +97,7 @@ pub(crate) fn convert_if_to_bool_then(acc: &mut Assists, ctx: &AssistContext) ->
e => e,
};
let cond = if invert_cond { invert_boolean_expression(&ctx.sema, cond) } else { cond };
let cond = if invert_cond { invert_boolean_expression(cond) } else { cond };
let arg_list = make::arg_list(Some(make::expr_closure(None, closure_body)));
let mcall = make::expr_method_call(cond, make::name_ref("then"), arg_list);
builder.replace(target, mcall.to_string());

View File

@ -115,7 +115,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
let new_expr = {
let then_branch =
make::block_expr(once(make::expr_stmt(early_expression).into()), None);
let cond = invert_boolean_expression(&ctx.sema, cond_expr);
let cond = invert_boolean_expression(cond_expr);
make::expr_if(make::condition(cond, None), then_branch, None)
.indent(if_indent_level)
};

View File

@ -892,7 +892,7 @@ fn expr_require_exclusive_access(ctx: &AssistContext, expr: &ast::Expr) -> Optio
let parent = expr.syntax().parent()?;
if let Some(bin_expr) = ast::BinExpr::cast(parent.clone()) {
if bin_expr.op_kind()?.is_assignment() {
if matches!(bin_expr.op_kind()?, ast::BinaryOp::Assignment { .. }) {
return Some(bin_expr.lhs()?.syntax() == expr.syntax());
}
return Some(false);

View File

@ -1,4 +1,4 @@
use syntax::ast::{AstNode, BinExpr, BinOp};
use syntax::ast::{self, AstNode, BinExpr};
use crate::{AssistContext, AssistId, AssistKind, Assists};
@ -56,14 +56,19 @@ enum FlipAction {
DontFlip,
}
impl From<BinOp> for FlipAction {
fn from(op_kind: BinOp) -> Self {
impl From<ast::BinaryOp> for FlipAction {
fn from(op_kind: ast::BinaryOp) -> Self {
match op_kind {
kind if kind.is_assignment() => FlipAction::DontFlip,
BinOp::GreaterTest => FlipAction::FlipAndReplaceOp("<"),
BinOp::GreaterEqualTest => FlipAction::FlipAndReplaceOp("<="),
BinOp::LesserTest => FlipAction::FlipAndReplaceOp(">"),
BinOp::LesserEqualTest => FlipAction::FlipAndReplaceOp(">="),
ast::BinaryOp::Assignment { .. } => FlipAction::DontFlip,
ast::BinaryOp::CmpOp(ast::CmpOp::Ord { ordering, strict }) => {
let rev_op = match (ordering, strict) {
(ast::Ordering::Less, true) => ">",
(ast::Ordering::Less, false) => ">=",
(ast::Ordering::Greater, true) => "<",
(ast::Ordering::Greater, false) => "<=",
};
FlipAction::FlipAndReplaceOp(rev_op)
}
_ => FlipAction::Flip,
}
}

View File

@ -47,7 +47,7 @@ pub(crate) fn invert_if(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
};
acc.add(AssistId("invert_if", AssistKind::RefactorRewrite), "Invert if", if_range, |edit| {
let flip_cond = invert_boolean_expression(&ctx.sema, cond.clone());
let flip_cond = invert_boolean_expression(cond.clone());
edit.replace_ast(cond, flip_cond);
let else_node = else_block.syntax();

View File

@ -39,7 +39,7 @@ pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext) -> Opti
let assign_expr = ctx.find_node_at_offset::<ast::BinExpr>()?;
let op_kind = assign_expr.op_kind()?;
if op_kind != ast::BinOp::Assignment {
if op_kind != (ast::BinaryOp::Assignment { op: None }) {
cov_mark::hit!(test_cant_pull_non_assignments);
return None;
}
@ -143,7 +143,7 @@ fn collect_block(&mut self, block: &ast::BlockExpr) -> Option<()> {
}
fn collect_expr(&mut self, expr: &ast::BinExpr) -> Option<()> {
if expr.op_kind()? == ast::BinOp::Assignment
if expr.op_kind()? == (ast::BinaryOp::Assignment { op: None })
&& is_equivalent(self.sema, &expr.lhs()?, &self.common_lhs)
{
self.assignments.push((expr.clone(), expr.rhs()?));
@ -173,8 +173,8 @@ fn is_equivalent(
}
}
(ast::Expr::PrefixExpr(prefix0), ast::Expr::PrefixExpr(prefix1))
if prefix0.op_kind() == Some(ast::PrefixOp::Deref)
&& prefix1.op_kind() == Some(ast::PrefixOp::Deref) =>
if prefix0.op_kind() == Some(ast::UnaryOp::Deref)
&& prefix1.op_kind() == Some(ast::UnaryOp::Deref) =>
{
cov_mark::hit!(test_pull_assignment_up_deref);
if let (Some(prefix0), Some(prefix1)) = (prefix0.expr(), prefix1.expr()) {

View File

@ -151,7 +151,7 @@ fn main() {
"#####,
r#####"
fn main() {
if !(x == 4 && !(y < 3.14)) {}
if !(x == 4 && y >= 3.14) {}
}
"#####,
)

View File

@ -5,12 +5,8 @@
use std::ops;
use hir::{Adt, HasSource, Semantics};
use ide_db::{
helpers::{FamousDefs, SnippetCap},
path_transform::PathTransform,
RootDatabase,
};
use hir::{Adt, HasSource};
use ide_db::{helpers::SnippetCap, path_transform::PathTransform, RootDatabase};
use itertools::Itertools;
use stdx::format_to;
use syntax::{
@ -207,30 +203,23 @@ pub(crate) fn vis_offset(node: &SyntaxNode) -> TextSize {
.unwrap_or_else(|| node.text_range().start())
}
pub(crate) fn invert_boolean_expression(
sema: &Semantics<RootDatabase>,
expr: ast::Expr,
) -> ast::Expr {
invert_special_case(sema, &expr).unwrap_or_else(|| make::expr_prefix(T![!], expr))
pub(crate) fn invert_boolean_expression(expr: ast::Expr) -> ast::Expr {
invert_special_case(&expr).unwrap_or_else(|| make::expr_prefix(T![!], expr))
}
fn invert_special_case(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<ast::Expr> {
fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
match expr {
ast::Expr::BinExpr(bin) => match bin.op_kind()? {
ast::BinOp::NegatedEqualityTest => bin.replace_op(T![==]).map(|it| it.into()),
ast::BinOp::EqualityTest => bin.replace_op(T![!=]).map(|it| it.into()),
// Swap `<` with `>=`, `<=` with `>`, ... if operands `impl Ord`
ast::BinOp::LesserTest if bin_impls_ord(sema, bin) => {
bin.replace_op(T![>=]).map(|it| it.into())
}
ast::BinOp::LesserEqualTest if bin_impls_ord(sema, bin) => {
bin.replace_op(T![>]).map(|it| it.into())
}
ast::BinOp::GreaterTest if bin_impls_ord(sema, bin) => {
bin.replace_op(T![<=]).map(|it| it.into())
}
ast::BinOp::GreaterEqualTest if bin_impls_ord(sema, bin) => {
bin.replace_op(T![<]).map(|it| it.into())
ast::BinaryOp::CmpOp(op) => {
let rev_op = match op {
ast::CmpOp::Eq { negated: false } => T![!=],
ast::CmpOp::Eq { negated: true } => T![==],
ast::CmpOp::Ord { ordering: ast::Ordering::Less, strict: true } => T![>=],
ast::CmpOp::Ord { ordering: ast::Ordering::Less, strict: false } => T![>],
ast::CmpOp::Ord { ordering: ast::Ordering::Greater, strict: true } => T![<=],
ast::CmpOp::Ord { ordering: ast::Ordering::Greater, strict: false } => T![<],
};
bin.replace_op(rev_op).map(ast::Expr::from)
}
// Parenthesize other expressions before prefixing `!`
_ => Some(make::expr_prefix(T![!], make::expr_paren(expr.clone()))),
@ -249,7 +238,7 @@ fn invert_special_case(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Opti
};
Some(make::expr_method_call(receiver, make::name_ref(method), arg_list))
}
ast::Expr::PrefixExpr(pe) if pe.op_kind()? == ast::PrefixOp::Not => {
ast::Expr::PrefixExpr(pe) if pe.op_kind()? == ast::UnaryOp::Not => {
if let ast::Expr::ParenExpr(parexpr) = pe.expr()? {
parexpr.expr()
} else {
@ -267,22 +256,6 @@ fn invert_special_case(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Opti
}
}
fn bin_impls_ord(sema: &Semantics<RootDatabase>, bin: &ast::BinExpr) -> bool {
match (
bin.lhs().and_then(|lhs| sema.type_of_expr(&lhs)).map(hir::TypeInfo::adjusted),
bin.rhs().and_then(|rhs| sema.type_of_expr(&rhs)).map(hir::TypeInfo::adjusted),
) {
(Some(lhs_ty), Some(rhs_ty)) if lhs_ty == rhs_ty => {
let krate = sema.scope(bin.syntax()).module().map(|it| it.krate());
let ord_trait = FamousDefs(sema, krate).core_cmp_Ord();
ord_trait.map_or(false, |ord_trait| {
lhs_ty.autoderef(sema.db).any(|ty| ty.impls_trait(sema.db, ord_trait, &[]))
})
}
_ => false,
}
}
pub(crate) fn next_prev() -> impl Iterator<Item = Direction> {
[Direction::Next, Direction::Prev].iter().copied()
}

View File

@ -1,7 +1,7 @@
//! This module contains functions to generate default trait impl function bodies where possible.
use syntax::{
ast::{self, edit::AstNodeEdit, make, AstNode, NameOwner},
ast::{self, edit::AstNodeEdit, make, AstNode, BinaryOp, CmpOp, LogicOp, NameOwner},
ted,
};
@ -325,7 +325,7 @@ fn gen_hash_call(target: ast::Expr) -> ast::Stmt {
fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
fn gen_eq_chain(expr: Option<ast::Expr>, cmp: ast::Expr) -> Option<ast::Expr> {
match expr {
Some(expr) => Some(make::expr_op(ast::BinOp::BooleanAnd, expr, cmp)),
Some(expr) => Some(make::expr_bin_op(expr, BinaryOp::LogicOp(LogicOp::And), cmp)),
None => Some(cmp),
}
}
@ -362,7 +362,8 @@ fn gen_tuple_field(field_name: &String) -> ast::Pat {
let lhs = make::expr_call(make_discriminant()?, make::arg_list(Some(lhs_name.clone())));
let rhs_name = make::expr_path(make::ext::ident_path("other"));
let rhs = make::expr_call(make_discriminant()?, make::arg_list(Some(rhs_name.clone())));
let eq_check = make::expr_op(ast::BinOp::EqualityTest, lhs, rhs);
let eq_check =
make::expr_bin_op(lhs, BinaryOp::CmpOp(CmpOp::Eq { negated: false }), rhs);
let mut case_count = 0;
let mut arms = vec![];
@ -386,7 +387,11 @@ fn gen_tuple_field(field_name: &String) -> ast::Pat {
let lhs = make::expr_path(make::ext::ident_path(l_name));
let rhs = make::expr_path(make::ext::ident_path(r_name));
let cmp = make::expr_op(ast::BinOp::EqualityTest, lhs, rhs);
let cmp = make::expr_bin_op(
lhs,
BinaryOp::CmpOp(CmpOp::Eq { negated: false }),
rhs,
);
expr = gen_eq_chain(expr, cmp);
}
@ -415,7 +420,11 @@ fn gen_tuple_field(field_name: &String) -> ast::Pat {
let lhs = make::expr_path(make::ext::ident_path(&l_name));
let rhs = make::expr_path(make::ext::ident_path(&r_name));
let cmp = make::expr_op(ast::BinOp::EqualityTest, lhs, rhs);
let cmp = make::expr_bin_op(
lhs,
BinaryOp::CmpOp(CmpOp::Eq { negated: false }),
rhs,
);
expr = gen_eq_chain(expr, cmp);
}
@ -455,7 +464,8 @@ fn gen_tuple_field(field_name: &String) -> ast::Pat {
let lhs = make::expr_field(lhs, &field.name()?.to_string());
let rhs = make::expr_path(make::ext::ident_path("other"));
let rhs = make::expr_field(rhs, &field.name()?.to_string());
let cmp = make::expr_op(ast::BinOp::EqualityTest, lhs, rhs);
let cmp =
make::expr_bin_op(lhs, BinaryOp::CmpOp(CmpOp::Eq { negated: false }), rhs);
expr = gen_eq_chain(expr, cmp);
}
make::block_expr(None, expr).indent(ast::edit::IndentLevel(1))
@ -469,7 +479,8 @@ fn gen_tuple_field(field_name: &String) -> ast::Pat {
let lhs = make::expr_field(lhs, &idx);
let rhs = make::expr_path(make::ext::ident_path("other"));
let rhs = make::expr_field(rhs, &idx);
let cmp = make::expr_op(ast::BinOp::EqualityTest, lhs, rhs);
let cmp =
make::expr_bin_op(lhs, BinaryOp::CmpOp(CmpOp::Eq { negated: false }), rhs);
expr = gen_eq_chain(expr, cmp);
}
make::block_expr(None, expr).indent(ast::edit::IndentLevel(1))

View File

@ -110,7 +110,7 @@ pub(crate) fn for_variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>)
}
ast::Expr::ParenExpr(inner) => next_expr = inner.expr(),
ast::Expr::TryExpr(inner) => next_expr = inner.expr(),
ast::Expr::PrefixExpr(prefix) if prefix.op_kind() == Some(ast::PrefixOp::Deref) => {
ast::Expr::PrefixExpr(prefix) if prefix.op_kind() == Some(ast::UnaryOp::Deref) => {
next_expr = prefix.expr()
}
_ => break,

View File

@ -655,7 +655,7 @@ fn reference_access(def: &Definition, name_ref: &ast::NameRef) -> Option<Referen
match_ast! {
match (node) {
ast::BinExpr(expr) => {
if expr.op_kind()?.is_assignment() {
if matches!(expr.op_kind()?, ast::BinaryOp::Assignment { .. }) {
// If the variable or field ends on the LHS's end then it's a Write (covers fields and locals).
// FIXME: This is not terribly accurate.
if let Some(lhs) = expr.lhs() {

View File

@ -5,6 +5,7 @@
mod token_ext;
mod node_ext;
mod expr_ext;
mod operators;
pub mod edit;
pub mod edit_in_place;
pub mod make;
@ -17,14 +18,21 @@
};
pub use self::{
expr_ext::{ArrayExprKind, BinOp, Effect, ElseBranch, LiteralKind, PrefixOp, RangeOp},
expr_ext::{ArrayExprKind, Effect, ElseBranch, LiteralKind},
generated::{nodes::*, tokens::*},
node_ext::{
AttrKind, AttrsOwnerNode, FieldKind, Macro, NameLike, NameOrNameRef, PathSegmentKind,
SelfParamKind, SlicePatComponents, StructKind, TypeBoundKind, VisibilityKind,
},
token_ext::*,
traits::*,
operators::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp},
token_ext::{
CommentKind, CommentPlacement, CommentShape, FormatSpecifier, HasFormatSpecifier, IsString,
QuoteOffsets, Radix,
},
traits::{
ArgListOwner, AttrsOwner, CommentIter, DocCommentsOwner, GenericParamsOwner, LoopBodyOwner,
ModuleItemOwner, NameOwner, TypeBoundsOwner, VisibilityOwner,
},
};
/// The main trait to go from untyped `SyntaxNode` to a typed ast. The

View File

@ -3,7 +3,11 @@
use rowan::WalkEvent;
use crate::{
ast::{self, support, AstChildren, AstNode},
ast::{
self,
operators::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp},
support, AstChildren, AstNode,
},
AstToken,
SyntaxKind::*,
SyntaxToken, T,
@ -193,24 +197,15 @@ pub fn blocks(&self) -> AstChildren<ast::BlockExpr> {
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum PrefixOp {
/// The `*` operator for dereferencing
Deref,
/// The `!` operator for logical inversion
Not,
/// The `-` operator for negation
Neg,
}
impl ast::PrefixExpr {
pub fn op_kind(&self) -> Option<PrefixOp> {
match self.op_token()?.kind() {
T![*] => Some(PrefixOp::Deref),
T![!] => Some(PrefixOp::Not),
T![-] => Some(PrefixOp::Neg),
_ => None,
}
pub fn op_kind(&self) -> Option<UnaryOp> {
let res = match self.op_token()?.kind() {
T![*] => UnaryOp::Deref,
T![!] => UnaryOp::Not,
T![-] => UnaryOp::Neg,
_ => return None,
};
Some(res)
}
pub fn op_token(&self) -> Option<SyntaxToken> {
@ -218,127 +213,51 @@ pub fn op_token(&self) -> Option<SyntaxToken> {
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum BinOp {
/// The `||` operator for boolean OR
BooleanOr,
/// The `&&` operator for boolean AND
BooleanAnd,
/// The `==` operator for equality testing
EqualityTest,
/// The `!=` operator for equality testing
NegatedEqualityTest,
/// The `<=` operator for lesser-equal testing
LesserEqualTest,
/// The `>=` operator for greater-equal testing
GreaterEqualTest,
/// The `<` operator for comparison
LesserTest,
/// The `>` operator for comparison
GreaterTest,
/// The `+` operator for addition
Addition,
/// The `*` operator for multiplication
Multiplication,
/// The `-` operator for subtraction
Subtraction,
/// The `/` operator for division
Division,
/// The `%` operator for remainder after division
Remainder,
/// The `<<` operator for left shift
LeftShift,
/// The `>>` operator for right shift
RightShift,
/// The `^` operator for bitwise XOR
BitwiseXor,
/// The `|` operator for bitwise OR
BitwiseOr,
/// The `&` operator for bitwise AND
BitwiseAnd,
/// The `=` operator for assignment
Assignment,
/// The `+=` operator for assignment after addition
AddAssign,
/// The `/=` operator for assignment after division
DivAssign,
/// The `*=` operator for assignment after multiplication
MulAssign,
/// The `%=` operator for assignment after remainders
RemAssign,
/// The `>>=` operator for assignment after shifting right
ShrAssign,
/// The `<<=` operator for assignment after shifting left
ShlAssign,
/// The `-=` operator for assignment after subtraction
SubAssign,
/// The `|=` operator for assignment after bitwise OR
BitOrAssign,
/// The `&=` operator for assignment after bitwise AND
BitAndAssign,
/// The `^=` operator for assignment after bitwise XOR
BitXorAssign,
}
impl BinOp {
pub fn is_assignment(self) -> bool {
matches!(
self,
BinOp::Assignment
| BinOp::AddAssign
| BinOp::DivAssign
| BinOp::MulAssign
| BinOp::RemAssign
| BinOp::ShrAssign
| BinOp::ShlAssign
| BinOp::SubAssign
| BinOp::BitOrAssign
| BinOp::BitAndAssign
| BinOp::BitXorAssign
)
}
}
impl ast::BinExpr {
pub fn op_details(&self) -> Option<(SyntaxToken, BinOp)> {
pub fn op_details(&self) -> Option<(SyntaxToken, BinaryOp)> {
self.syntax().children_with_tokens().filter_map(|it| it.into_token()).find_map(|c| {
#[rustfmt::skip]
let bin_op = match c.kind() {
T![||] => BinOp::BooleanOr,
T![&&] => BinOp::BooleanAnd,
T![==] => BinOp::EqualityTest,
T![!=] => BinOp::NegatedEqualityTest,
T![<=] => BinOp::LesserEqualTest,
T![>=] => BinOp::GreaterEqualTest,
T![<] => BinOp::LesserTest,
T![>] => BinOp::GreaterTest,
T![+] => BinOp::Addition,
T![*] => BinOp::Multiplication,
T![-] => BinOp::Subtraction,
T![/] => BinOp::Division,
T![%] => BinOp::Remainder,
T![<<] => BinOp::LeftShift,
T![>>] => BinOp::RightShift,
T![^] => BinOp::BitwiseXor,
T![|] => BinOp::BitwiseOr,
T![&] => BinOp::BitwiseAnd,
T![=] => BinOp::Assignment,
T![+=] => BinOp::AddAssign,
T![/=] => BinOp::DivAssign,
T![*=] => BinOp::MulAssign,
T![%=] => BinOp::RemAssign,
T![>>=] => BinOp::ShrAssign,
T![<<=] => BinOp::ShlAssign,
T![-=] => BinOp::SubAssign,
T![|=] => BinOp::BitOrAssign,
T![&=] => BinOp::BitAndAssign,
T![^=] => BinOp::BitXorAssign,
T![||] => BinaryOp::LogicOp(LogicOp::Or),
T![&&] => BinaryOp::LogicOp(LogicOp::And),
T![==] => BinaryOp::CmpOp(CmpOp::Eq { negated: false }),
T![!=] => BinaryOp::CmpOp(CmpOp::Eq { negated: true }),
T![<=] => BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Less, strict: false }),
T![>=] => BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Greater, strict: false }),
T![<] => BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Less, strict: true }),
T![>] => BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Greater, strict: true }),
T![+] => BinaryOp::ArithOp(ArithOp::Add),
T![*] => BinaryOp::ArithOp(ArithOp::Mul),
T![-] => BinaryOp::ArithOp(ArithOp::Sub),
T![/] => BinaryOp::ArithOp(ArithOp::Div),
T![%] => BinaryOp::ArithOp(ArithOp::Rem),
T![<<] => BinaryOp::ArithOp(ArithOp::Shl),
T![>>] => BinaryOp::ArithOp(ArithOp::Shr),
T![^] => BinaryOp::ArithOp(ArithOp::BitXor),
T![|] => BinaryOp::ArithOp(ArithOp::BitOr),
T![&] => BinaryOp::ArithOp(ArithOp::BitAnd),
T![=] => BinaryOp::Assignment { op: None },
T![+=] => BinaryOp::Assignment { op: Some(ArithOp::Add) },
T![*=] => BinaryOp::Assignment { op: Some(ArithOp::Mul) },
T![-=] => BinaryOp::Assignment { op: Some(ArithOp::Sub) },
T![/=] => BinaryOp::Assignment { op: Some(ArithOp::Div) },
T![%=] => BinaryOp::Assignment { op: Some(ArithOp::Rem) },
T![<<=] => BinaryOp::Assignment { op: Some(ArithOp::Shl) },
T![>>=] => BinaryOp::Assignment { op: Some(ArithOp::Shr) },
T![^=] => BinaryOp::Assignment { op: Some(ArithOp::BitXor) },
T![|=] => BinaryOp::Assignment { op: Some(ArithOp::BitOr) },
T![&=] => BinaryOp::Assignment { op: Some(ArithOp::BitAnd) },
_ => return None,
};
Some((c, bin_op))
})
}
pub fn op_kind(&self) -> Option<BinOp> {
pub fn op_kind(&self) -> Option<BinaryOp> {
self.op_details().map(|t| t.1)
}
@ -362,50 +281,6 @@ pub fn sub_exprs(&self) -> (Option<ast::Expr>, Option<ast::Expr>) {
}
}
impl std::fmt::Display for BinOp {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
BinOp::BooleanOr => write!(f, "||"),
BinOp::BooleanAnd => write!(f, "&&"),
BinOp::EqualityTest => write!(f, "=="),
BinOp::NegatedEqualityTest => write!(f, "!="),
BinOp::LesserEqualTest => write!(f, "<="),
BinOp::GreaterEqualTest => write!(f, ">="),
BinOp::LesserTest => write!(f, "<"),
BinOp::GreaterTest => write!(f, ">"),
BinOp::Addition => write!(f, "+"),
BinOp::Multiplication => write!(f, "*"),
BinOp::Subtraction => write!(f, "-"),
BinOp::Division => write!(f, "/"),
BinOp::Remainder => write!(f, "%"),
BinOp::LeftShift => write!(f, "<<"),
BinOp::RightShift => write!(f, ">>"),
BinOp::BitwiseXor => write!(f, "^"),
BinOp::BitwiseOr => write!(f, "|"),
BinOp::BitwiseAnd => write!(f, "&"),
BinOp::Assignment => write!(f, "="),
BinOp::AddAssign => write!(f, "+="),
BinOp::DivAssign => write!(f, "/="),
BinOp::MulAssign => write!(f, "*="),
BinOp::RemAssign => write!(f, "%="),
BinOp::ShrAssign => write!(f, ">>="),
BinOp::ShlAssign => write!(f, "<<="),
BinOp::SubAssign => write!(f, "-"),
BinOp::BitOrAssign => write!(f, "|="),
BinOp::BitAndAssign => write!(f, "&="),
BinOp::BitXorAssign => write!(f, "^="),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum RangeOp {
/// `..`
Exclusive,
/// `..=`
Inclusive,
}
impl ast::RangeExpr {
fn op_details(&self) -> Option<(usize, SyntaxToken, RangeOp)> {
self.syntax().children_with_tokens().enumerate().find_map(|(ix, child)| {

View File

@ -276,7 +276,8 @@ pub fn expr_path(path: ast::Path) -> ast::Expr {
pub fn expr_continue() -> ast::Expr {
expr_from_text("continue")
}
pub fn expr_op(op: ast::BinOp, lhs: ast::Expr, rhs: ast::Expr) -> ast::Expr {
// Consider `op: SyntaxKind` instead for nicer syntax at the call-site?
pub fn expr_bin_op(lhs: ast::Expr, op: ast::BinaryOp, rhs: ast::Expr) -> ast::Expr {
expr_from_text(&format!("{} {} {}", lhs, op, rhs))
}
pub fn expr_break(expr: Option<ast::Expr>) -> ast::Expr {

View File

@ -0,0 +1,122 @@
//! Defines a bunch of data-less enums for unary and binary operators.
//!
//! Types here don't know about AST, this allows re-using them for both AST and
//! HIR.
use std::fmt;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum RangeOp {
/// `..`
Exclusive,
/// `..=`
Inclusive,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum UnaryOp {
/// `*`
Deref,
/// `!`
Not,
/// `-`
Neg,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum BinaryOp {
LogicOp(LogicOp),
ArithOp(ArithOp),
CmpOp(CmpOp),
Assignment { op: Option<ArithOp> },
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum LogicOp {
And,
Or,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum CmpOp {
Eq { negated: bool },
Ord { ordering: Ordering, strict: bool },
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Ordering {
Less,
Greater,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum ArithOp {
Add,
Mul,
Sub,
Div,
Rem,
Shl,
Shr,
BitXor,
BitOr,
BitAnd,
}
impl fmt::Display for LogicOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let res = match self {
LogicOp::And => "&&",
LogicOp::Or => "||",
};
f.write_str(res)
}
}
impl fmt::Display for ArithOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let res = match self {
ArithOp::Add => "+",
ArithOp::Mul => "*",
ArithOp::Sub => "-",
ArithOp::Div => "/",
ArithOp::Rem => "%",
ArithOp::Shl => "<<",
ArithOp::Shr => ">>",
ArithOp::BitXor => "^",
ArithOp::BitOr => "|",
ArithOp::BitAnd => "&",
};
f.write_str(res)
}
}
impl fmt::Display for CmpOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let res = match self {
CmpOp::Eq { negated: false } => "==",
CmpOp::Eq { negated: true } => "!=",
CmpOp::Ord { ordering: Ordering::Less, strict: false } => "<=",
CmpOp::Ord { ordering: Ordering::Less, strict: true } => "<",
CmpOp::Ord { ordering: Ordering::Greater, strict: false } => ">=",
CmpOp::Ord { ordering: Ordering::Greater, strict: true } => ">",
};
f.write_str(res)
}
}
impl fmt::Display for BinaryOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
BinaryOp::LogicOp(op) => fmt::Display::fmt(op, f),
BinaryOp::ArithOp(op) => fmt::Display::fmt(op, f),
BinaryOp::CmpOp(op) => fmt::Display::fmt(op, f),
BinaryOp::Assignment { op } => {
f.write_str("=")?;
if let Some(op) = op {
fmt::Display::fmt(op, f)?;
}
Ok(())
}
}
}
}