mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-28 10:39:45 +00:00
Merge pull request #19171 from ShoyuVanilla/migrate-de-morgan-assist
internal: Migrate `apply_demorgan` to `SyntaxEditor`
This commit is contained in:
commit
f6edb7178e
10 changed files with 351 additions and 75 deletions
|
|
@ -76,6 +76,8 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
|
||||||
|
|
||||||
let cfg = ctx.config.import_path_config();
|
let cfg = ctx.config.import_path_config();
|
||||||
|
|
||||||
|
let make = SyntaxFactory::new();
|
||||||
|
|
||||||
let module = ctx.sema.scope(expr.syntax())?.module();
|
let module = ctx.sema.scope(expr.syntax())?.module();
|
||||||
let (mut missing_pats, is_non_exhaustive, has_hidden_variants): (
|
let (mut missing_pats, is_non_exhaustive, has_hidden_variants): (
|
||||||
Peekable<Box<dyn Iterator<Item = (ast::Pat, bool)>>>,
|
Peekable<Box<dyn Iterator<Item = (ast::Pat, bool)>>>,
|
||||||
|
|
@ -93,7 +95,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|variant| {
|
.filter_map(|variant| {
|
||||||
Some((
|
Some((
|
||||||
build_pat(ctx, module, variant, cfg)?,
|
build_pat(ctx, &make, module, variant, cfg)?,
|
||||||
variant.should_be_hidden(ctx.db(), module.krate()),
|
variant.should_be_hidden(ctx.db(), module.krate()),
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
|
|
@ -144,10 +146,11 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
|
||||||
let is_hidden = variants
|
let is_hidden = variants
|
||||||
.iter()
|
.iter()
|
||||||
.any(|variant| variant.should_be_hidden(ctx.db(), module.krate()));
|
.any(|variant| variant.should_be_hidden(ctx.db(), module.krate()));
|
||||||
let patterns =
|
let patterns = variants
|
||||||
variants.into_iter().filter_map(|variant| build_pat(ctx, module, variant, cfg));
|
.into_iter()
|
||||||
|
.filter_map(|variant| build_pat(ctx, &make, module, variant, cfg));
|
||||||
|
|
||||||
(ast::Pat::from(make::tuple_pat(patterns)), is_hidden)
|
(ast::Pat::from(make.tuple_pat(patterns)), is_hidden)
|
||||||
})
|
})
|
||||||
.filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat));
|
.filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat));
|
||||||
(
|
(
|
||||||
|
|
@ -176,9 +179,11 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
|
||||||
let is_hidden = variants
|
let is_hidden = variants
|
||||||
.iter()
|
.iter()
|
||||||
.any(|variant| variant.should_be_hidden(ctx.db(), module.krate()));
|
.any(|variant| variant.should_be_hidden(ctx.db(), module.krate()));
|
||||||
let patterns =
|
let patterns = variants
|
||||||
variants.into_iter().filter_map(|variant| build_pat(ctx, module, variant, cfg));
|
.into_iter()
|
||||||
(ast::Pat::from(make::slice_pat(patterns)), is_hidden)
|
.filter_map(|variant| build_pat(ctx, &make, module, variant, cfg));
|
||||||
|
|
||||||
|
(ast::Pat::from(make.slice_pat(patterns)), is_hidden)
|
||||||
})
|
})
|
||||||
.filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat));
|
.filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat));
|
||||||
(
|
(
|
||||||
|
|
@ -203,8 +208,6 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
|
||||||
"Fill match arms",
|
"Fill match arms",
|
||||||
ctx.sema.original_range(match_expr.syntax()).range,
|
ctx.sema.original_range(match_expr.syntax()).range,
|
||||||
|builder| {
|
|builder| {
|
||||||
let make = SyntaxFactory::new();
|
|
||||||
|
|
||||||
// having any hidden variants means that we need a catch-all arm
|
// having any hidden variants means that we need a catch-all arm
|
||||||
needs_catch_all_arm |= has_hidden_variants;
|
needs_catch_all_arm |= has_hidden_variants;
|
||||||
|
|
||||||
|
|
@ -243,7 +246,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
|
||||||
|
|
||||||
if needs_catch_all_arm && !has_catch_all_arm {
|
if needs_catch_all_arm && !has_catch_all_arm {
|
||||||
cov_mark::hit!(added_wildcard_pattern);
|
cov_mark::hit!(added_wildcard_pattern);
|
||||||
let arm = make.match_arm(make::wildcard_pat().into(), None, make::ext::expr_todo());
|
let arm = make.match_arm(make.wildcard_pat().into(), None, make::ext::expr_todo());
|
||||||
arms.push(arm);
|
arms.push(arm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -290,7 +293,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
editor.add_mappings(make.finish_with_mappings());
|
editor.add_mappings(make.take());
|
||||||
builder.add_file_edits(ctx.file_id(), editor);
|
builder.add_file_edits(ctx.file_id(), editor);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -445,6 +448,7 @@ fn resolve_array_of_enum_def(
|
||||||
|
|
||||||
fn build_pat(
|
fn build_pat(
|
||||||
ctx: &AssistContext<'_>,
|
ctx: &AssistContext<'_>,
|
||||||
|
make: &SyntaxFactory,
|
||||||
module: hir::Module,
|
module: hir::Module,
|
||||||
var: ExtendedVariant,
|
var: ExtendedVariant,
|
||||||
cfg: ImportPathConfig,
|
cfg: ImportPathConfig,
|
||||||
|
|
@ -455,31 +459,32 @@ fn build_pat(
|
||||||
let edition = module.krate().edition(db);
|
let edition = module.krate().edition(db);
|
||||||
let path = mod_path_to_ast(&module.find_path(db, ModuleDef::from(var), cfg)?, edition);
|
let path = mod_path_to_ast(&module.find_path(db, ModuleDef::from(var), cfg)?, edition);
|
||||||
let fields = var.fields(db);
|
let fields = var.fields(db);
|
||||||
let pat = match var.kind(db) {
|
let pat: ast::Pat = match var.kind(db) {
|
||||||
hir::StructKind::Tuple => {
|
hir::StructKind::Tuple => {
|
||||||
let mut name_generator = suggest_name::NameGenerator::new();
|
let mut name_generator = suggest_name::NameGenerator::new();
|
||||||
let pats = fields.into_iter().map(|f| {
|
let pats = fields.into_iter().map(|f| {
|
||||||
let name = name_generator.for_type(&f.ty(db), db, edition);
|
let name = name_generator.for_type(&f.ty(db), db, edition);
|
||||||
match name {
|
match name {
|
||||||
Some(name) => make::ext::simple_ident_pat(make::name(&name)).into(),
|
Some(name) => make::ext::simple_ident_pat(make.name(&name)).into(),
|
||||||
None => make::wildcard_pat().into(),
|
None => make.wildcard_pat().into(),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
make::tuple_struct_pat(path, pats).into()
|
make.tuple_struct_pat(path, pats).into()
|
||||||
}
|
}
|
||||||
hir::StructKind::Record => {
|
hir::StructKind::Record => {
|
||||||
let pats = fields
|
let fields = fields
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|f| make::name(f.name(db).as_str()))
|
.map(|f| make.name_ref(f.name(db).as_str()))
|
||||||
.map(|name| make::ext::simple_ident_pat(name).into());
|
.map(|name_ref| make.record_pat_field_shorthand(name_ref));
|
||||||
make::record_pat(path, pats).into()
|
let fields = make.record_pat_field_list(fields, None);
|
||||||
|
make.record_pat_with_fields(path, fields).into()
|
||||||
}
|
}
|
||||||
hir::StructKind::Unit => make::path_pat(path),
|
hir::StructKind::Unit => make.path_pat(path),
|
||||||
};
|
};
|
||||||
Some(pat)
|
Some(pat)
|
||||||
}
|
}
|
||||||
ExtendedVariant::True => Some(ast::Pat::from(make::literal_pat("true"))),
|
ExtendedVariant::True => Some(ast::Pat::from(make.literal_pat("true"))),
|
||||||
ExtendedVariant::False => Some(ast::Pat::from(make::literal_pat("false"))),
|
ExtendedVariant::False => Some(ast::Pat::from(make.literal_pat("false"))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,12 @@ use std::collections::VecDeque;
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
assists::GroupLabel,
|
assists::GroupLabel,
|
||||||
famous_defs::FamousDefs,
|
famous_defs::FamousDefs,
|
||||||
source_change::SourceChangeBuilder,
|
|
||||||
syntax_helpers::node_ext::{for_each_tail_expr, walk_expr},
|
syntax_helpers::node_ext::{for_each_tail_expr, walk_expr},
|
||||||
};
|
};
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, make, AstNode, Expr::BinExpr, HasArgList},
|
ast::{self, syntax_factory::SyntaxFactory, AstNode, Expr::BinExpr, HasArgList},
|
||||||
ted, SyntaxKind, T,
|
syntax_editor::{Position, SyntaxEditor},
|
||||||
|
SyntaxKind, SyntaxNode, T,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists};
|
use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists};
|
||||||
|
|
@ -58,9 +58,12 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let demorganed = bin_expr.clone_subtree().clone_for_update();
|
let make = SyntaxFactory::new();
|
||||||
|
|
||||||
|
let demorganed = bin_expr.clone_subtree();
|
||||||
|
let mut editor = SyntaxEditor::new(demorganed.syntax().clone());
|
||||||
|
editor.replace(demorganed.op_token()?, make.token(inv_token));
|
||||||
|
|
||||||
ted::replace(demorganed.op_token()?, ast::make::token(inv_token));
|
|
||||||
let mut exprs = VecDeque::from([
|
let mut exprs = VecDeque::from([
|
||||||
(bin_expr.lhs()?, demorganed.lhs()?),
|
(bin_expr.lhs()?, demorganed.lhs()?),
|
||||||
(bin_expr.rhs()?, demorganed.rhs()?),
|
(bin_expr.rhs()?, demorganed.rhs()?),
|
||||||
|
|
@ -70,35 +73,39 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
|
||||||
if let BinExpr(bin_expr) = &expr {
|
if let BinExpr(bin_expr) = &expr {
|
||||||
if let BinExpr(cbin_expr) = &dm {
|
if let BinExpr(cbin_expr) = &dm {
|
||||||
if op == bin_expr.op_kind()? {
|
if op == bin_expr.op_kind()? {
|
||||||
ted::replace(cbin_expr.op_token()?, ast::make::token(inv_token));
|
editor.replace(cbin_expr.op_token()?, make.token(inv_token));
|
||||||
exprs.push_back((bin_expr.lhs()?, cbin_expr.lhs()?));
|
exprs.push_back((bin_expr.lhs()?, cbin_expr.lhs()?));
|
||||||
exprs.push_back((bin_expr.rhs()?, cbin_expr.rhs()?));
|
exprs.push_back((bin_expr.rhs()?, cbin_expr.rhs()?));
|
||||||
} else {
|
} else {
|
||||||
let mut inv = invert_boolean_expression(expr);
|
let mut inv = invert_boolean_expression(&make, expr);
|
||||||
if inv.needs_parens_in(dm.syntax().parent()?) {
|
if needs_parens_in_place_of(&inv, &dm.syntax().parent()?, &dm) {
|
||||||
inv = ast::make::expr_paren(inv).clone_for_update();
|
inv = make.expr_paren(inv).into();
|
||||||
}
|
}
|
||||||
ted::replace(dm.syntax(), inv.syntax());
|
editor.replace(dm.syntax(), inv.syntax());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let mut inv = invert_boolean_expression(dm.clone_subtree()).clone_for_update();
|
let mut inv = invert_boolean_expression(&make, dm.clone());
|
||||||
if inv.needs_parens_in(dm.syntax().parent()?) {
|
if needs_parens_in_place_of(&inv, &dm.syntax().parent()?, &dm) {
|
||||||
inv = ast::make::expr_paren(inv).clone_for_update();
|
inv = make.expr_paren(inv).into();
|
||||||
}
|
}
|
||||||
ted::replace(dm.syntax(), inv.syntax());
|
editor.replace(dm.syntax(), inv.syntax());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
editor.add_mappings(make.finish_with_mappings());
|
||||||
|
let edit = editor.finish();
|
||||||
|
let demorganed = ast::Expr::cast(edit.new_root().clone())?;
|
||||||
|
|
||||||
acc.add_group(
|
acc.add_group(
|
||||||
&GroupLabel("Apply De Morgan's law".to_owned()),
|
&GroupLabel("Apply De Morgan's law".to_owned()),
|
||||||
AssistId("apply_demorgan", AssistKind::RefactorRewrite),
|
AssistId("apply_demorgan", AssistKind::RefactorRewrite),
|
||||||
"Apply De Morgan's law",
|
"Apply De Morgan's law",
|
||||||
op_range,
|
op_range,
|
||||||
|edit| {
|
|builder| {
|
||||||
let demorganed = ast::Expr::BinExpr(demorganed);
|
let make = SyntaxFactory::new();
|
||||||
let paren_expr = bin_expr.syntax().parent().and_then(ast::ParenExpr::cast);
|
let paren_expr = bin_expr.syntax().parent().and_then(ast::ParenExpr::cast);
|
||||||
let neg_expr = paren_expr
|
let neg_expr = paren_expr
|
||||||
.clone()
|
.clone()
|
||||||
|
|
@ -107,24 +114,32 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
|
||||||
.filter(|prefix_expr| matches!(prefix_expr.op_kind(), Some(ast::UnaryOp::Not)))
|
.filter(|prefix_expr| matches!(prefix_expr.op_kind(), Some(ast::UnaryOp::Not)))
|
||||||
.map(ast::Expr::PrefixExpr);
|
.map(ast::Expr::PrefixExpr);
|
||||||
|
|
||||||
|
let mut editor;
|
||||||
if let Some(paren_expr) = paren_expr {
|
if let Some(paren_expr) = paren_expr {
|
||||||
if let Some(neg_expr) = neg_expr {
|
if let Some(neg_expr) = neg_expr {
|
||||||
cov_mark::hit!(demorgan_double_negation);
|
cov_mark::hit!(demorgan_double_negation);
|
||||||
let parent = neg_expr.syntax().parent();
|
let parent = neg_expr.syntax().parent();
|
||||||
|
editor = builder.make_editor(neg_expr.syntax());
|
||||||
|
|
||||||
if parent.is_some_and(|parent| demorganed.needs_parens_in(parent)) {
|
if parent.is_some_and(|parent| demorganed.needs_parens_in(parent)) {
|
||||||
cov_mark::hit!(demorgan_keep_parens_for_op_precedence2);
|
cov_mark::hit!(demorgan_keep_parens_for_op_precedence2);
|
||||||
edit.replace_ast(neg_expr, make::expr_paren(demorganed));
|
editor.replace(neg_expr.syntax(), make.expr_paren(demorganed).syntax());
|
||||||
} else {
|
} else {
|
||||||
edit.replace_ast(neg_expr, demorganed);
|
editor.replace(neg_expr.syntax(), demorganed.syntax());
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
cov_mark::hit!(demorgan_double_parens);
|
cov_mark::hit!(demorgan_double_parens);
|
||||||
edit.replace_ast(paren_expr.into(), add_bang_paren(demorganed));
|
editor = builder.make_editor(paren_expr.syntax());
|
||||||
|
|
||||||
|
editor.replace(paren_expr.syntax(), add_bang_paren(&make, demorganed).syntax());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
edit.replace_ast(bin_expr.into(), add_bang_paren(demorganed));
|
editor = builder.make_editor(bin_expr.syntax());
|
||||||
|
editor.replace(bin_expr.syntax(), add_bang_paren(&make, demorganed).syntax());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
editor.add_mappings(make.finish_with_mappings());
|
||||||
|
builder.add_file_edits(ctx.file_id(), editor);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -161,7 +176,7 @@ pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_>
|
||||||
let (name, arg_expr) = validate_method_call_expr(ctx, &method_call)?;
|
let (name, arg_expr) = validate_method_call_expr(ctx, &method_call)?;
|
||||||
|
|
||||||
let ast::Expr::ClosureExpr(closure_expr) = arg_expr else { return None };
|
let ast::Expr::ClosureExpr(closure_expr) = arg_expr else { return None };
|
||||||
let closure_body = closure_expr.body()?;
|
let closure_body = closure_expr.body()?.clone_for_update();
|
||||||
|
|
||||||
let op_range = method_call.syntax().text_range();
|
let op_range = method_call.syntax().text_range();
|
||||||
let label = format!("Apply De Morgan's law to `Iterator::{}`", name.text().as_str());
|
let label = format!("Apply De Morgan's law to `Iterator::{}`", name.text().as_str());
|
||||||
|
|
@ -170,18 +185,19 @@ pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_>
|
||||||
AssistId("apply_demorgan_iterator", AssistKind::RefactorRewrite),
|
AssistId("apply_demorgan_iterator", AssistKind::RefactorRewrite),
|
||||||
label,
|
label,
|
||||||
op_range,
|
op_range,
|
||||||
|edit| {
|
|builder| {
|
||||||
|
let make = SyntaxFactory::new();
|
||||||
|
let mut editor = builder.make_editor(method_call.syntax());
|
||||||
// replace the method name
|
// replace the method name
|
||||||
let new_name = match name.text().as_str() {
|
let new_name = match name.text().as_str() {
|
||||||
"all" => make::name_ref("any"),
|
"all" => make.name_ref("any"),
|
||||||
"any" => make::name_ref("all"),
|
"any" => make.name_ref("all"),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
};
|
||||||
.clone_for_update();
|
editor.replace(name.syntax(), new_name.syntax());
|
||||||
edit.replace_ast(name, new_name);
|
|
||||||
|
|
||||||
// negate all tail expressions in the closure body
|
// negate all tail expressions in the closure body
|
||||||
let tail_cb = &mut |e: &_| tail_cb_impl(edit, e);
|
let tail_cb = &mut |e: &_| tail_cb_impl(&mut editor, &make, e);
|
||||||
walk_expr(&closure_body, &mut |expr| {
|
walk_expr(&closure_body, &mut |expr| {
|
||||||
if let ast::Expr::ReturnExpr(ret_expr) = expr {
|
if let ast::Expr::ReturnExpr(ret_expr) = expr {
|
||||||
if let Some(ret_expr_arg) = &ret_expr.expr() {
|
if let Some(ret_expr_arg) = &ret_expr.expr() {
|
||||||
|
|
@ -198,15 +214,15 @@ pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_>
|
||||||
.and_then(ast::PrefixExpr::cast)
|
.and_then(ast::PrefixExpr::cast)
|
||||||
.filter(|prefix_expr| matches!(prefix_expr.op_kind(), Some(ast::UnaryOp::Not)))
|
.filter(|prefix_expr| matches!(prefix_expr.op_kind(), Some(ast::UnaryOp::Not)))
|
||||||
{
|
{
|
||||||
edit.delete(
|
editor.delete(
|
||||||
prefix_expr
|
prefix_expr.op_token().expect("prefix expression always has an operator"),
|
||||||
.op_token()
|
|
||||||
.expect("prefix expression always has an operator")
|
|
||||||
.text_range(),
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
edit.insert(method_call.syntax().text_range().start(), "!");
|
editor.insert(Position::before(method_call.syntax()), make.token(SyntaxKind::BANG));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
editor.add_mappings(make.finish_with_mappings());
|
||||||
|
builder.add_file_edits(ctx.file_id(), editor);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -233,26 +249,50 @@ fn validate_method_call_expr(
|
||||||
it_type.impls_trait(sema.db, iter_trait, &[]).then_some((name_ref, arg_expr))
|
it_type.impls_trait(sema.db, iter_trait, &[]).then_some((name_ref, arg_expr))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tail_cb_impl(edit: &mut SourceChangeBuilder, e: &ast::Expr) {
|
fn tail_cb_impl(editor: &mut SyntaxEditor, make: &SyntaxFactory, e: &ast::Expr) {
|
||||||
match e {
|
match e {
|
||||||
ast::Expr::BreakExpr(break_expr) => {
|
ast::Expr::BreakExpr(break_expr) => {
|
||||||
if let Some(break_expr_arg) = break_expr.expr() {
|
if let Some(break_expr_arg) = break_expr.expr() {
|
||||||
for_each_tail_expr(&break_expr_arg, &mut |e| tail_cb_impl(edit, e))
|
for_each_tail_expr(&break_expr_arg, &mut |e| tail_cb_impl(editor, make, e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::Expr::ReturnExpr(_) => {
|
ast::Expr::ReturnExpr(_) => {
|
||||||
// all return expressions have already been handled by the walk loop
|
// all return expressions have already been handled by the walk loop
|
||||||
}
|
}
|
||||||
e => {
|
e => {
|
||||||
let inverted_body = invert_boolean_expression(e.clone());
|
let inverted_body = invert_boolean_expression(make, e.clone());
|
||||||
edit.replace(e.syntax().text_range(), inverted_body.syntax().text());
|
editor.replace(e.syntax(), inverted_body.syntax());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add bang and parentheses to the expression.
|
/// Add bang and parentheses to the expression.
|
||||||
fn add_bang_paren(expr: ast::Expr) -> ast::Expr {
|
fn add_bang_paren(make: &SyntaxFactory, expr: ast::Expr) -> ast::Expr {
|
||||||
make::expr_prefix(T![!], make::expr_paren(expr)).into()
|
make.expr_prefix(T![!], make.expr_paren(expr).into()).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn needs_parens_in_place_of(
|
||||||
|
this: &ast::Expr,
|
||||||
|
parent: &SyntaxNode,
|
||||||
|
in_place_of: &ast::Expr,
|
||||||
|
) -> bool {
|
||||||
|
assert_eq!(Some(parent), in_place_of.syntax().parent().as_ref());
|
||||||
|
|
||||||
|
let child_idx = parent
|
||||||
|
.children()
|
||||||
|
.enumerate()
|
||||||
|
.find_map(|(i, it)| if &it == in_place_of.syntax() { Some(i) } else { None })
|
||||||
|
.unwrap();
|
||||||
|
let parent = parent.clone_subtree();
|
||||||
|
let subtree_place = parent.children().nth(child_idx).unwrap();
|
||||||
|
|
||||||
|
let mut editor = SyntaxEditor::new(parent);
|
||||||
|
editor.replace(subtree_place, this.syntax());
|
||||||
|
let edit = editor.finish();
|
||||||
|
|
||||||
|
let replaced = edit.new_root().children().nth(child_idx).unwrap();
|
||||||
|
let replaced = ast::Expr::cast(replaced).unwrap();
|
||||||
|
replaced.needs_parens_in(edit.new_root().clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ use syntax::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
utils::{invert_boolean_expression, unwrap_trivial_block},
|
utils::{invert_boolean_expression_legacy, unwrap_trivial_block},
|
||||||
AssistContext, AssistId, AssistKind, Assists,
|
AssistContext, AssistId, AssistKind, Assists,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -119,7 +119,7 @@ pub(crate) fn convert_if_to_bool_then(acc: &mut Assists, ctx: &AssistContext<'_>
|
||||||
| ast::Expr::WhileExpr(_)
|
| ast::Expr::WhileExpr(_)
|
||||||
| ast::Expr::YieldExpr(_)
|
| ast::Expr::YieldExpr(_)
|
||||||
);
|
);
|
||||||
let cond = if invert_cond { invert_boolean_expression(cond) } else { cond };
|
let cond = if invert_cond { invert_boolean_expression_legacy(cond) } else { cond };
|
||||||
let cond = if parenthesize { make::expr_paren(cond) } else { cond };
|
let cond = if parenthesize { make::expr_paren(cond) } else { cond };
|
||||||
let arg_list = make::arg_list(Some(make::expr_closure(None, closure_body)));
|
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);
|
let mcall = make::expr_method_call(cond, make::name_ref("then"), arg_list);
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ use syntax::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
assist_context::{AssistContext, Assists},
|
assist_context::{AssistContext, Assists},
|
||||||
utils::invert_boolean_expression,
|
utils::invert_boolean_expression_legacy,
|
||||||
AssistId, AssistKind,
|
AssistId, AssistKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -139,7 +139,7 @@ fn if_expr_to_guarded_return(
|
||||||
let new_expr = {
|
let new_expr = {
|
||||||
let then_branch =
|
let then_branch =
|
||||||
make::block_expr(once(make::expr_stmt(early_expression).into()), None);
|
make::block_expr(once(make::expr_stmt(early_expression).into()), None);
|
||||||
let cond = invert_boolean_expression(cond_expr);
|
let cond = invert_boolean_expression_legacy(cond_expr);
|
||||||
make::expr_if(cond, then_branch, None).indent(if_indent_level)
|
make::expr_if(cond, then_branch, None).indent(if_indent_level)
|
||||||
};
|
};
|
||||||
new_expr.syntax().clone_for_update()
|
new_expr.syntax().clone_for_update()
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ use syntax::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
assist_context::{AssistContext, Assists},
|
assist_context::{AssistContext, Assists},
|
||||||
utils::invert_boolean_expression,
|
utils::invert_boolean_expression_legacy,
|
||||||
AssistId, AssistKind,
|
AssistId, AssistKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -63,7 +63,7 @@ pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext<'_>)
|
||||||
let stmts = iter::once(make::expr_stmt(if_expr.into()).into());
|
let stmts = iter::once(make::expr_stmt(if_expr.into()).into());
|
||||||
make::block_expr(stmts, None)
|
make::block_expr(stmts, None)
|
||||||
} else {
|
} else {
|
||||||
let if_cond = invert_boolean_expression(while_cond);
|
let if_cond = invert_boolean_expression_legacy(while_cond);
|
||||||
let if_expr = make::expr_if(if_cond, break_block, None).syntax().clone().into();
|
let if_expr = make::expr_if(if_cond, break_block, None).syntax().clone().into();
|
||||||
let elements = while_body.stmt_list().map_or_else(
|
let elements = while_body.stmt_list().map_or_else(
|
||||||
|| Either::Left(iter::empty()),
|
|| Either::Left(iter::empty()),
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use syntax::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
assist_context::{AssistContext, Assists},
|
assist_context::{AssistContext, Assists},
|
||||||
utils::invert_boolean_expression,
|
utils::invert_boolean_expression_legacy,
|
||||||
AssistId, AssistKind,
|
AssistId, AssistKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -48,7 +48,7 @@ pub(crate) fn invert_if(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()
|
||||||
};
|
};
|
||||||
|
|
||||||
acc.add(AssistId("invert_if", AssistKind::RefactorRewrite), "Invert if", if_range, |edit| {
|
acc.add(AssistId("invert_if", AssistKind::RefactorRewrite), "Invert if", if_range, |edit| {
|
||||||
let flip_cond = invert_boolean_expression(cond.clone());
|
let flip_cond = invert_boolean_expression_legacy(cond.clone());
|
||||||
edit.replace_ast(cond, flip_cond);
|
edit.replace_ast(cond, flip_cond);
|
||||||
|
|
||||||
let else_node = else_block.syntax();
|
let else_node = else_block.syntax();
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,9 @@ use syntax::{
|
||||||
self,
|
self,
|
||||||
edit::{AstNodeEdit, IndentLevel},
|
edit::{AstNodeEdit, IndentLevel},
|
||||||
edit_in_place::{AttrsOwnerEdit, Indent, Removable},
|
edit_in_place::{AttrsOwnerEdit, Indent, Removable},
|
||||||
make, HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace,
|
make,
|
||||||
|
syntax_factory::SyntaxFactory,
|
||||||
|
HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace,
|
||||||
},
|
},
|
||||||
ted, AstNode, AstToken, Direction, Edition, NodeOrToken, SourceFile,
|
ted, AstNode, AstToken, Direction, Edition, NodeOrToken, SourceFile,
|
||||||
SyntaxKind::*,
|
SyntaxKind::*,
|
||||||
|
|
@ -245,11 +247,79 @@ pub(crate) fn vis_offset(node: &SyntaxNode) -> TextSize {
|
||||||
.unwrap_or_else(|| node.text_range().start())
|
.unwrap_or_else(|| node.text_range().start())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn invert_boolean_expression(expr: ast::Expr) -> ast::Expr {
|
pub(crate) fn invert_boolean_expression(make: &SyntaxFactory, expr: ast::Expr) -> ast::Expr {
|
||||||
invert_special_case(&expr).unwrap_or_else(|| make::expr_prefix(T![!], expr).into())
|
invert_special_case(make, &expr).unwrap_or_else(|| make.expr_prefix(T![!], expr).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
|
// FIXME: Migrate usages of this function to the above function and remove this.
|
||||||
|
pub(crate) fn invert_boolean_expression_legacy(expr: ast::Expr) -> ast::Expr {
|
||||||
|
invert_special_case_legacy(&expr).unwrap_or_else(|| make::expr_prefix(T![!], expr).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn invert_special_case(make: &SyntaxFactory, expr: &ast::Expr) -> Option<ast::Expr> {
|
||||||
|
match expr {
|
||||||
|
ast::Expr::BinExpr(bin) => {
|
||||||
|
let op_kind = bin.op_kind()?;
|
||||||
|
let rev_kind = match op_kind {
|
||||||
|
ast::BinaryOp::CmpOp(ast::CmpOp::Eq { negated }) => {
|
||||||
|
ast::BinaryOp::CmpOp(ast::CmpOp::Eq { negated: !negated })
|
||||||
|
}
|
||||||
|
ast::BinaryOp::CmpOp(ast::CmpOp::Ord { ordering: ast::Ordering::Less, strict }) => {
|
||||||
|
ast::BinaryOp::CmpOp(ast::CmpOp::Ord {
|
||||||
|
ordering: ast::Ordering::Greater,
|
||||||
|
strict: !strict,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ast::BinaryOp::CmpOp(ast::CmpOp::Ord {
|
||||||
|
ordering: ast::Ordering::Greater,
|
||||||
|
strict,
|
||||||
|
}) => ast::BinaryOp::CmpOp(ast::CmpOp::Ord {
|
||||||
|
ordering: ast::Ordering::Less,
|
||||||
|
strict: !strict,
|
||||||
|
}),
|
||||||
|
// Parenthesize other expressions before prefixing `!`
|
||||||
|
_ => {
|
||||||
|
return Some(
|
||||||
|
make.expr_prefix(T![!], make.expr_paren(expr.clone()).into()).into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(make.expr_bin(bin.lhs()?, rev_kind, bin.rhs()?).into())
|
||||||
|
}
|
||||||
|
ast::Expr::MethodCallExpr(mce) => {
|
||||||
|
let receiver = mce.receiver()?;
|
||||||
|
let method = mce.name_ref()?;
|
||||||
|
let arg_list = mce.arg_list()?;
|
||||||
|
|
||||||
|
let method = match method.text().as_str() {
|
||||||
|
"is_some" => "is_none",
|
||||||
|
"is_none" => "is_some",
|
||||||
|
"is_ok" => "is_err",
|
||||||
|
"is_err" => "is_ok",
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(make.expr_method_call(receiver, make.name_ref(method), arg_list).into())
|
||||||
|
}
|
||||||
|
ast::Expr::PrefixExpr(pe) if pe.op_kind()? == ast::UnaryOp::Not => match pe.expr()? {
|
||||||
|
ast::Expr::ParenExpr(parexpr) => {
|
||||||
|
parexpr.expr().map(|e| e.clone_subtree().clone_for_update())
|
||||||
|
}
|
||||||
|
_ => pe.expr().map(|e| e.clone_subtree().clone_for_update()),
|
||||||
|
},
|
||||||
|
ast::Expr::Literal(lit) => match lit.kind() {
|
||||||
|
ast::LiteralKind::Bool(b) => match b {
|
||||||
|
true => Some(ast::Expr::Literal(make.expr_literal("false"))),
|
||||||
|
false => Some(ast::Expr::Literal(make.expr_literal("true"))),
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn invert_special_case_legacy(expr: &ast::Expr) -> Option<ast::Expr> {
|
||||||
match expr {
|
match expr {
|
||||||
ast::Expr::BinExpr(bin) => {
|
ast::Expr::BinExpr(bin) => {
|
||||||
let bin = bin.clone_for_update();
|
let bin = bin.clone_for_update();
|
||||||
|
|
|
||||||
|
|
@ -784,7 +784,7 @@ pub fn record_pat_field_shorthand(name_ref: ast::NameRef) -> ast::RecordPatField
|
||||||
ast_from_text(&format!("fn f(S {{ {name_ref} }}: ()))"))
|
ast_from_text(&format!("fn f(S {{ {name_ref} }}: ()))"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a `BindPat` if the path has just one segment, a `PathPat` otherwise.
|
/// Returns a `IdentPat` if the path has just one segment, a `PathPat` otherwise.
|
||||||
pub fn path_pat(path: ast::Path) -> ast::Pat {
|
pub fn path_pat(path: ast::Path) -> ast::Pat {
|
||||||
return from_text(&path.to_string());
|
return from_text(&path.to_string());
|
||||||
fn from_text(text: &str) -> ast::Pat {
|
fn from_text(text: &str) -> ast::Pat {
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,11 @@ impl SyntaxFactory {
|
||||||
self.mappings.unwrap_or_default().into_inner()
|
self.mappings.unwrap_or_default().into_inner()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Take all of the tracked syntax mappings, leaving `SyntaxMapping::default()` in its place, if any.
|
||||||
|
pub fn take(&self) -> SyntaxMapping {
|
||||||
|
self.mappings.as_ref().map(|mappings| mappings.take()).unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
fn mappings(&self) -> Option<RefMut<'_, SyntaxMapping>> {
|
fn mappings(&self) -> Option<RefMut<'_, SyntaxMapping>> {
|
||||||
self.mappings.as_ref().map(|it| it.borrow_mut())
|
self.mappings.as_ref().map(|it| it.borrow_mut())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -156,6 +156,32 @@ impl SyntaxFactory {
|
||||||
make::literal_pat(text).clone_for_update()
|
make::literal_pat(text).clone_for_update()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn slice_pat(&self, pats: impl IntoIterator<Item = ast::Pat>) -> ast::SlicePat {
|
||||||
|
let (pats, input) = iterator_input(pats);
|
||||||
|
let ast = make::slice_pat(pats).clone_for_update();
|
||||||
|
|
||||||
|
if let Some(mut mapping) = self.mappings() {
|
||||||
|
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
|
||||||
|
builder.map_children(input.into_iter(), ast.pats().map(|it| it.syntax().clone()));
|
||||||
|
builder.finish(&mut mapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
ast
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tuple_pat(&self, pats: impl IntoIterator<Item = ast::Pat>) -> ast::TuplePat {
|
||||||
|
let (pats, input) = iterator_input(pats);
|
||||||
|
let ast = make::tuple_pat(pats).clone_for_update();
|
||||||
|
|
||||||
|
if let Some(mut mapping) = self.mappings() {
|
||||||
|
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
|
||||||
|
builder.map_children(input.into_iter(), ast.fields().map(|it| it.syntax().clone()));
|
||||||
|
builder.finish(&mut mapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
ast
|
||||||
|
}
|
||||||
|
|
||||||
pub fn tuple_struct_pat(
|
pub fn tuple_struct_pat(
|
||||||
&self,
|
&self,
|
||||||
path: ast::Path,
|
path: ast::Path,
|
||||||
|
|
@ -174,6 +200,96 @@ impl SyntaxFactory {
|
||||||
ast
|
ast
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn record_pat_with_fields(
|
||||||
|
&self,
|
||||||
|
path: ast::Path,
|
||||||
|
fields: ast::RecordPatFieldList,
|
||||||
|
) -> ast::RecordPat {
|
||||||
|
let ast = make::record_pat_with_fields(path.clone(), fields.clone()).clone_for_update();
|
||||||
|
|
||||||
|
if let Some(mut mapping) = self.mappings() {
|
||||||
|
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
|
||||||
|
builder.map_node(path.syntax().clone(), ast.path().unwrap().syntax().clone());
|
||||||
|
builder.map_node(
|
||||||
|
fields.syntax().clone(),
|
||||||
|
ast.record_pat_field_list().unwrap().syntax().clone(),
|
||||||
|
);
|
||||||
|
builder.finish(&mut mapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
ast
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn record_pat_field_list(
|
||||||
|
&self,
|
||||||
|
fields: impl IntoIterator<Item = ast::RecordPatField>,
|
||||||
|
rest_pat: Option<ast::RestPat>,
|
||||||
|
) -> ast::RecordPatFieldList {
|
||||||
|
let (fields, input) = iterator_input(fields);
|
||||||
|
let ast = make::record_pat_field_list(fields, rest_pat.clone()).clone_for_update();
|
||||||
|
|
||||||
|
if let Some(mut mapping) = self.mappings() {
|
||||||
|
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
|
||||||
|
builder.map_children(input.into_iter(), ast.fields().map(|it| it.syntax().clone()));
|
||||||
|
if let Some(rest_pat) = rest_pat {
|
||||||
|
builder
|
||||||
|
.map_node(rest_pat.syntax().clone(), ast.rest_pat().unwrap().syntax().clone());
|
||||||
|
}
|
||||||
|
builder.finish(&mut mapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
ast
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn record_pat_field(self, name_ref: ast::NameRef, pat: ast::Pat) -> ast::RecordPatField {
|
||||||
|
let ast = make::record_pat_field(name_ref.clone(), pat.clone()).clone_for_update();
|
||||||
|
|
||||||
|
if let Some(mut mapping) = self.mappings() {
|
||||||
|
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
|
||||||
|
builder.map_node(name_ref.syntax().clone(), ast.name_ref().unwrap().syntax().clone());
|
||||||
|
builder.map_node(pat.syntax().clone(), ast.pat().unwrap().syntax().clone());
|
||||||
|
builder.finish(&mut mapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
ast
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn record_pat_field_shorthand(&self, name_ref: ast::NameRef) -> ast::RecordPatField {
|
||||||
|
let ast = make::record_pat_field_shorthand(name_ref.clone()).clone_for_update();
|
||||||
|
|
||||||
|
if let Some(mut mapping) = self.mappings() {
|
||||||
|
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
|
||||||
|
builder.map_node(name_ref.syntax().clone(), ast.pat().unwrap().syntax().clone());
|
||||||
|
builder.finish(&mut mapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
ast
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn path_pat(&self, path: ast::Path) -> ast::Pat {
|
||||||
|
let ast = make::path_pat(path.clone()).clone_for_update();
|
||||||
|
|
||||||
|
match &ast {
|
||||||
|
ast::Pat::PathPat(ast) => {
|
||||||
|
if let Some(mut mapping) = self.mappings() {
|
||||||
|
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
|
||||||
|
builder.map_node(path.syntax().clone(), ast.path().unwrap().syntax().clone());
|
||||||
|
builder.finish(&mut mapping)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ast::Pat::IdentPat(ast) => {
|
||||||
|
if let Some(mut mapping) = self.mappings() {
|
||||||
|
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
|
||||||
|
builder.map_node(path.syntax().clone(), ast.name().unwrap().syntax().clone());
|
||||||
|
builder.finish(&mut mapping)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
ast
|
||||||
|
}
|
||||||
|
|
||||||
pub fn block_expr(
|
pub fn block_expr(
|
||||||
&self,
|
&self,
|
||||||
statements: impl IntoIterator<Item = ast::Stmt>,
|
statements: impl IntoIterator<Item = ast::Stmt>,
|
||||||
|
|
@ -214,6 +330,21 @@ impl SyntaxFactory {
|
||||||
make::expr_empty_block().clone_for_update()
|
make::expr_empty_block().clone_for_update()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn expr_paren(&self, expr: ast::Expr) -> ast::ParenExpr {
|
||||||
|
// FIXME: `make::expr_paren` should return a `ParenExpr`, not just an `Expr`
|
||||||
|
let ast::Expr::ParenExpr(ast) = make::expr_paren(expr.clone()).clone_for_update() else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(mut mapping) = self.mappings() {
|
||||||
|
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
|
||||||
|
builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone());
|
||||||
|
builder.finish(&mut mapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
ast
|
||||||
|
}
|
||||||
|
|
||||||
pub fn expr_tuple(&self, fields: impl IntoIterator<Item = ast::Expr>) -> ast::TupleExpr {
|
pub fn expr_tuple(&self, fields: impl IntoIterator<Item = ast::Expr>) -> ast::TupleExpr {
|
||||||
let (fields, input) = iterator_input(fields);
|
let (fields, input) = iterator_input(fields);
|
||||||
let ast = make::expr_tuple(fields).clone_for_update();
|
let ast = make::expr_tuple(fields).clone_for_update();
|
||||||
|
|
@ -292,6 +423,31 @@ impl SyntaxFactory {
|
||||||
ast
|
ast
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn expr_method_call(
|
||||||
|
&self,
|
||||||
|
receiver: ast::Expr,
|
||||||
|
method: ast::NameRef,
|
||||||
|
arg_list: ast::ArgList,
|
||||||
|
) -> ast::MethodCallExpr {
|
||||||
|
// FIXME: `make::expr_method_call` should return a `MethodCallExpr`, not just an `Expr`
|
||||||
|
let ast::Expr::MethodCallExpr(ast) =
|
||||||
|
make::expr_method_call(receiver.clone(), method.clone(), arg_list.clone())
|
||||||
|
.clone_for_update()
|
||||||
|
else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(mut mapping) = self.mappings() {
|
||||||
|
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
|
||||||
|
builder.map_node(receiver.syntax().clone(), ast.receiver().unwrap().syntax().clone());
|
||||||
|
builder.map_node(method.syntax().clone(), ast.name_ref().unwrap().syntax().clone());
|
||||||
|
builder.map_node(arg_list.syntax().clone(), ast.arg_list().unwrap().syntax().clone());
|
||||||
|
builder.finish(&mut mapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
ast
|
||||||
|
}
|
||||||
|
|
||||||
pub fn arg_list(&self, args: impl IntoIterator<Item = ast::Expr>) -> ast::ArgList {
|
pub fn arg_list(&self, args: impl IntoIterator<Item = ast::Expr>) -> ast::ArgList {
|
||||||
let (args, input) = iterator_input(args);
|
let (args, input) = iterator_input(args);
|
||||||
let ast = make::arg_list(args).clone_for_update();
|
let ast = make::arg_list(args).clone_for_update();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue