From 2987fac76fdc09056670180c5f2e2357d9af9247 Mon Sep 17 00:00:00 2001 From: davidsemakula Date: Tue, 30 Jan 2024 19:57:36 +0300 Subject: [PATCH 1/5] diagnostic to remove trailing return --- crates/hir-ty/src/diagnostics/expr.rs | 31 +++ crates/hir/src/diagnostics.rs | 18 ++ .../src/handlers/remove_trailing_return.rs | 262 ++++++++++++++++++ .../src/handlers/type_mismatch.rs | 7 +- crates/ide-diagnostics/src/lib.rs | 2 + 5 files changed, 318 insertions(+), 2 deletions(-) create mode 100644 crates/ide-diagnostics/src/handlers/remove_trailing_return.rs diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs index c09351390af..af3ca3c082d 100644 --- a/crates/hir-ty/src/diagnostics/expr.rs +++ b/crates/hir-ty/src/diagnostics/expr.rs @@ -44,6 +44,9 @@ pub enum BodyValidationDiagnostic { match_expr: ExprId, uncovered_patterns: String, }, + RemoveTrailingReturn { + return_expr: ExprId, + }, RemoveUnnecessaryElse { if_expr: ExprId, }, @@ -75,6 +78,10 @@ fn validate_body(&mut self, db: &dyn HirDatabase) { let body = db.body(self.owner); let mut filter_map_next_checker = None; + if matches!(self.owner, DefWithBodyId::FunctionId(_)) { + self.check_for_trailing_return(body.body_expr, &body); + } + for (id, expr) in body.exprs.iter() { if let Some((variant, missed_fields, true)) = record_literal_missing_fields(db, &self.infer, id, expr) @@ -93,12 +100,16 @@ fn validate_body(&mut self, db: &dyn HirDatabase) { Expr::Call { .. } | Expr::MethodCall { .. } => { self.validate_call(db, id, expr, &mut filter_map_next_checker); } + Expr::Closure { body: body_expr, .. } => { + self.check_for_trailing_return(*body_expr, &body); + } Expr::If { .. } => { self.check_for_unnecessary_else(id, expr, &body); } _ => {} } } + for (id, pat) in body.pats.iter() { if let Some((variant, missed_fields, true)) = record_pattern_missing_fields(db, &self.infer, id, pat) @@ -244,6 +255,26 @@ fn lower_pattern<'p>( pattern } + fn check_for_trailing_return(&mut self, body_expr: ExprId, body: &Body) { + match &body.exprs[body_expr] { + Expr::Block { statements, tail, .. } => { + let last_stmt = tail.or_else(|| match statements.last()? { + Statement::Expr { expr, .. } => Some(*expr), + _ => None, + }); + if let Some(last_stmt) = last_stmt { + self.check_for_trailing_return(last_stmt, body); + } + } + Expr::Return { .. } => { + self.diagnostics.push(BodyValidationDiagnostic::RemoveTrailingReturn { + return_expr: body_expr, + }); + } + _ => (), + } + } + fn check_for_unnecessary_else(&mut self, id: ExprId, expr: &Expr, body: &Body) { if let Expr::If { condition: _, then_branch, else_branch } = expr { if else_branch.is_none() { diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index 487e0c8f7a5..cec6be8e892 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -68,6 +68,7 @@ fn from(d: $diag) -> AnyDiagnostic { PrivateAssocItem, PrivateField, ReplaceFilterMapNextWithFindMap, + RemoveTrailingReturn, RemoveUnnecessaryElse, TraitImplIncorrectSafety, TraitImplMissingAssocItems, @@ -343,6 +344,12 @@ pub struct TraitImplRedundantAssocItems { pub assoc_item: (Name, AssocItem), } +#[derive(Debug)] +pub struct RemoveTrailingReturn { + pub file_id: HirFileId, + pub return_expr: AstPtr, +} + #[derive(Debug)] pub struct RemoveUnnecessaryElse { pub if_expr: InFile>, @@ -450,6 +457,17 @@ pub(crate) fn body_validation_diagnostic( Err(SyntheticSyntax) => (), } } + BodyValidationDiagnostic::RemoveTrailingReturn { return_expr } => { + if let Ok(source_ptr) = source_map.expr_syntax(return_expr) { + return Some( + RemoveTrailingReturn { + file_id: source_ptr.file_id, + return_expr: source_ptr.value, + } + .into(), + ); + } + } BodyValidationDiagnostic::RemoveUnnecessaryElse { if_expr } => { if let Ok(source_ptr) = source_map.expr_syntax(if_expr) { if let Some(ptr) = source_ptr.value.cast::() { diff --git a/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs b/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs new file mode 100644 index 00000000000..6cb5911096f --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs @@ -0,0 +1,262 @@ +use hir::{db::ExpandDatabase, diagnostics::RemoveTrailingReturn, HirFileIdExt, InFile}; +use ide_db::{assists::Assist, source_change::SourceChange}; +use syntax::{ast, AstNode, SyntaxNodePtr}; +use text_edit::TextEdit; + +use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: remove-trailing-return +// +// This diagnostic is triggered when there is a redundant `return` at the end of a function +// or closure. +pub(crate) fn remove_trailing_return( + ctx: &DiagnosticsContext<'_>, + d: &RemoveTrailingReturn, +) -> Diagnostic { + let display_range = ctx.sema.diagnostics_display_range(InFile { + file_id: d.file_id, + value: expr_stmt(ctx, d) + .as_ref() + .map(|stmt| SyntaxNodePtr::new(stmt.syntax())) + .unwrap_or_else(|| d.return_expr.into()), + }); + Diagnostic::new( + DiagnosticCode::Clippy("needless_return"), + "replace return ; with ", + display_range, + ) + .with_fixes(fixes(ctx, d)) +} + +fn fixes(ctx: &DiagnosticsContext<'_>, d: &RemoveTrailingReturn) -> Option> { + let return_expr = return_expr(ctx, d)?; + let stmt = expr_stmt(ctx, d); + + let range = stmt.as_ref().map_or(return_expr.syntax(), AstNode::syntax).text_range(); + let replacement = + return_expr.expr().map_or_else(String::new, |expr| format!("{}", expr.syntax().text())); + + let edit = TextEdit::replace(range, replacement); + let source_change = SourceChange::from_text_edit(d.file_id.original_file(ctx.sema.db), edit); + + Some(vec![fix( + "remove_trailing_return", + "Replace return ; with ", + source_change, + range, + )]) +} + +fn return_expr(ctx: &DiagnosticsContext<'_>, d: &RemoveTrailingReturn) -> Option { + let root = ctx.sema.db.parse_or_expand(d.file_id); + let expr = d.return_expr.to_node(&root); + ast::ReturnExpr::cast(expr.syntax().clone()) +} + +fn expr_stmt(ctx: &DiagnosticsContext<'_>, d: &RemoveTrailingReturn) -> Option { + let return_expr = return_expr(ctx, d)?; + return_expr.syntax().parent().and_then(ast::ExprStmt::cast) +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_diagnostics, check_fix}; + + #[test] + fn remove_trailing_return() { + check_diagnostics( + r#" +fn foo() -> u8 { + return 2; +} //^^^^^^^^^ 💡 weak: replace return ; with +"#, + ); + } + + #[test] + fn remove_trailing_return_inner_function() { + check_diagnostics( + r#" +fn foo() -> u8 { + fn bar() -> u8 { + return 2; + } //^^^^^^^^^ 💡 weak: replace return ; with + bar() +} +"#, + ); + } + + #[test] + fn remove_trailing_return_closure() { + check_diagnostics( + r#" +fn foo() -> u8 { + let bar = || return 2; + bar() //^^^^^^^^ 💡 weak: replace return ; with +} +"#, + ); + check_diagnostics( + r#" +fn foo() -> u8 { + let bar = || { + return 2; + };//^^^^^^^^^ 💡 weak: replace return ; with + bar() +} +"#, + ); + } + + #[test] + fn remove_trailing_return_unit() { + check_diagnostics( + r#" +fn foo() { + return +} //^^^^^^ 💡 weak: replace return ; with +"#, + ); + } + + #[test] + fn remove_trailing_return_no_semi() { + check_diagnostics( + r#" +fn foo() -> u8 { + return 2 +} //^^^^^^^^ 💡 weak: replace return ; with +"#, + ); + } + + #[test] + fn no_diagnostic_if_no_return_keyword() { + check_diagnostics( + r#" +fn foo() -> u8 { + 3 +} +"#, + ); + } + + #[test] + fn no_diagnostic_if_not_last_statement() { + check_diagnostics( + r#" +fn foo() -> u8 { + if true { return 2; } + 3 +} +"#, + ); + } + + #[test] + fn replace_with_expr() { + check_fix( + r#" +fn foo() -> u8 { + return$0 2; +} +"#, + r#" +fn foo() -> u8 { + 2 +} +"#, + ); + } + + #[test] + fn replace_with_unit() { + check_fix( + r#" +fn foo() { + return$0/*ensure tidy is happy*/ +} +"#, + r#" +fn foo() { + /*ensure tidy is happy*/ +} +"#, + ); + } + + #[test] + fn replace_with_expr_no_semi() { + check_fix( + r#" +fn foo() -> u8 { + return$0 2 +} +"#, + r#" +fn foo() -> u8 { + 2 +} +"#, + ); + } + + #[test] + fn replace_in_inner_function() { + check_fix( + r#" +fn foo() -> u8 { + fn bar() -> u8 { + return$0 2; + } + bar() +} +"#, + r#" +fn foo() -> u8 { + fn bar() -> u8 { + 2 + } + bar() +} +"#, + ); + } + + #[test] + fn replace_in_closure() { + check_fix( + r#" +fn foo() -> u8 { + let bar = || return$0 2; + bar() +} +"#, + r#" +fn foo() -> u8 { + let bar = || 2; + bar() +} +"#, + ); + check_fix( + r#" +fn foo() -> u8 { + let bar = || { + return$0 2; + }; + bar() +} +"#, + r#" +fn foo() -> u8 { + let bar = || { + 2 + }; + bar() +} +"#, + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs index 750189beecb..eec8efe785a 100644 --- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -186,7 +186,9 @@ fn str_ref_to_owned( #[cfg(test)] mod tests { - use crate::tests::{check_diagnostics, check_fix, check_no_fix}; + use crate::tests::{ + check_diagnostics, check_diagnostics_with_disabled, check_fix, check_no_fix, + }; #[test] fn missing_reference() { @@ -718,7 +720,7 @@ struct Bar { #[test] fn return_no_value() { - check_diagnostics( + check_diagnostics_with_disabled( r#" fn f() -> i32 { return; @@ -727,6 +729,7 @@ fn f() -> i32 { } fn g() { return; } "#, + std::iter::once("needless_return".to_string()), ); } diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index 7423de0be74..7c5cf673303 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -43,6 +43,7 @@ mod handlers { pub(crate) mod no_such_field; pub(crate) mod private_assoc_item; pub(crate) mod private_field; + pub(crate) mod remove_trailing_return; pub(crate) mod remove_unnecessary_else; pub(crate) mod replace_filter_map_next_with_find_map; pub(crate) mod trait_impl_incorrect_safety; @@ -383,6 +384,7 @@ pub fn diagnostics( AnyDiagnostic::UnusedVariable(d) => handlers::unused_variables::unused_variables(&ctx, &d), AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d), AnyDiagnostic::MismatchedTupleStructPatArgCount(d) => handlers::mismatched_arg_count::mismatched_tuple_struct_pat_arg_count(&ctx, &d), + AnyDiagnostic::RemoveTrailingReturn(d) => handlers::remove_trailing_return::remove_trailing_return(&ctx, &d), AnyDiagnostic::RemoveUnnecessaryElse(d) => handlers::remove_unnecessary_else::remove_unnecessary_else(&ctx, &d), }; res.push(d) From cad222ff1bdf438aaf638811f53e0be33b32fcf3 Mon Sep 17 00:00:00 2001 From: davidsemakula Date: Wed, 31 Jan 2024 06:46:48 +0300 Subject: [PATCH 2/5] remove trailing return in trailing if expression --- crates/hir-ty/src/diagnostics/expr.rs | 6 ++ .../src/handlers/remove_trailing_return.rs | 60 +++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs index af3ca3c082d..50312af4f21 100644 --- a/crates/hir-ty/src/diagnostics/expr.rs +++ b/crates/hir-ty/src/diagnostics/expr.rs @@ -266,6 +266,12 @@ fn check_for_trailing_return(&mut self, body_expr: ExprId, body: &Body) { self.check_for_trailing_return(last_stmt, body); } } + Expr::If { then_branch, else_branch, .. } => { + self.check_for_trailing_return(*then_branch, body); + if let Some(else_branch) = else_branch { + self.check_for_trailing_return(*else_branch, body); + } + } Expr::Return { .. } => { self.diagnostics.push(BodyValidationDiagnostic::RemoveTrailingReturn { return_expr: body_expr, diff --git a/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs b/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs index 6cb5911096f..ceef9da16c5 100644 --- a/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs +++ b/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs @@ -131,6 +131,22 @@ fn foo() -> u8 { ); } + #[test] + fn remove_trailing_return_in_if() { + check_diagnostics( + r#" +fn foo(x: usize) -> u8 { + if x > 0 { + return 1; + //^^^^^^^^^ 💡 weak: replace return ; with + } else { + return 0; + } //^^^^^^^^^ 💡 weak: replace return ; with +} +"#, + ); + } + #[test] fn no_diagnostic_if_no_return_keyword() { check_diagnostics( @@ -256,6 +272,50 @@ fn foo() -> u8 { }; bar() } +"#, + ); + } + + #[test] + fn replace_in_if() { + check_fix( + r#" +fn foo(x: usize) -> u8 { + if x > 0 { + return$0 1; + } else { + 0 + } +} +"#, + r#" +fn foo(x: usize) -> u8 { + if x > 0 { + 1 + } else { + 0 + } +} +"#, + ); + check_fix( + r#" +fn foo(x: usize) -> u8 { + if x > 0 { + 1 + } else { + return$0 0; + } +} +"#, + r#" +fn foo(x: usize) -> u8 { + if x > 0 { + 1 + } else { + 0 + } +} "#, ); } From 98e6f43a2f8073a4647d8609c9f23873090cd794 Mon Sep 17 00:00:00 2001 From: davidsemakula Date: Wed, 31 Jan 2024 07:04:49 +0300 Subject: [PATCH 3/5] remove trailing return in trailing match expression --- crates/hir-ty/src/diagnostics/expr.rs | 6 ++ crates/hir/src/diagnostics.rs | 19 ++++--- .../src/handlers/remove_trailing_return.rs | 55 +++++++++++++++++++ 3 files changed, 72 insertions(+), 8 deletions(-) diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs index 50312af4f21..7f8fb7f4b52 100644 --- a/crates/hir-ty/src/diagnostics/expr.rs +++ b/crates/hir-ty/src/diagnostics/expr.rs @@ -272,6 +272,12 @@ fn check_for_trailing_return(&mut self, body_expr: ExprId, body: &Body) { self.check_for_trailing_return(*else_branch, body); } } + Expr::Match { arms, .. } => { + for arm in arms.iter() { + let MatchArm { expr, .. } = arm; + self.check_for_trailing_return(*expr, body); + } + } Expr::Return { .. } => { self.diagnostics.push(BodyValidationDiagnostic::RemoveTrailingReturn { return_expr: body_expr, diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index cec6be8e892..b9f86f34156 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -11,7 +11,7 @@ use either::Either; use hir_def::{body::SyntheticSyntax, hir::ExprOrPatId, path::ModPath, AssocItemId, DefWithBodyId}; use hir_expand::{name::Name, HirFileId, InFile}; -use syntax::{ast, AstPtr, SyntaxError, SyntaxNodePtr, TextRange}; +use syntax::{ast, AstNode, AstPtr, SyntaxError, SyntaxNodePtr, TextRange}; use crate::{AssocItem, Field, Local, MacroKind, Trait, Type}; @@ -459,13 +459,16 @@ pub(crate) fn body_validation_diagnostic( } BodyValidationDiagnostic::RemoveTrailingReturn { return_expr } => { if let Ok(source_ptr) = source_map.expr_syntax(return_expr) { - return Some( - RemoveTrailingReturn { - file_id: source_ptr.file_id, - return_expr: source_ptr.value, - } - .into(), - ); + // Filters out desugared return expressions (e.g. desugared try operators). + if ast::ReturnExpr::can_cast(source_ptr.value.kind()) { + return Some( + RemoveTrailingReturn { + file_id: source_ptr.file_id, + return_expr: source_ptr.value, + } + .into(), + ); + } } } BodyValidationDiagnostic::RemoveUnnecessaryElse { if_expr } => { diff --git a/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs b/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs index ceef9da16c5..6c4ec7c132f 100644 --- a/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs +++ b/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs @@ -147,6 +147,21 @@ fn foo(x: usize) -> u8 { ); } + #[test] + fn remove_trailing_return_in_match() { + check_diagnostics( + r#" +fn foo(x: Result) -> u8 { + match x { + Ok(_) => return 1, + //^^^^^^^^ 💡 weak: replace return ; with + Err(_) => return 0, + } //^^^^^^^^ 💡 weak: replace return ; with +} +"#, + ); + } + #[test] fn no_diagnostic_if_no_return_keyword() { check_diagnostics( @@ -316,6 +331,46 @@ fn foo(x: usize) -> u8 { 0 } } +"#, + ); + } + + #[test] + fn replace_in_match() { + check_fix( + r#" +fn foo(x: Result) -> u8 { + match x { + Ok(_) => return$0 1, + Err(_) => 0, + } +} +"#, + r#" +fn foo(x: Result) -> u8 { + match x { + Ok(_) => 1, + Err(_) => 0, + } +} +"#, + ); + check_fix( + r#" +fn foo(x: Result) -> u8 { + match x { + Ok(_) => 1, + Err(_) => return$0 0, + } +} +"#, + r#" +fn foo(x: Result) -> u8 { + match x { + Ok(_) => 1, + Err(_) => 0, + } +} "#, ); } From a250c2dde032c90ca6b4e789921831c085b0bc85 Mon Sep 17 00:00:00 2001 From: davidsemakula Date: Wed, 31 Jan 2024 15:04:41 +0300 Subject: [PATCH 4/5] refactor remove trailing return diagnostic --- crates/hir/src/diagnostics.rs | 10 ++-- .../src/handlers/remove_trailing_return.rs | 46 ++++++++----------- 2 files changed, 24 insertions(+), 32 deletions(-) diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index b9f86f34156..e07c48aa8ac 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -11,7 +11,7 @@ use either::Either; use hir_def::{body::SyntheticSyntax, hir::ExprOrPatId, path::ModPath, AssocItemId, DefWithBodyId}; use hir_expand::{name::Name, HirFileId, InFile}; -use syntax::{ast, AstNode, AstPtr, SyntaxError, SyntaxNodePtr, TextRange}; +use syntax::{ast, AstPtr, SyntaxError, SyntaxNodePtr, TextRange}; use crate::{AssocItem, Field, Local, MacroKind, Trait, Type}; @@ -346,8 +346,7 @@ pub struct TraitImplRedundantAssocItems { #[derive(Debug)] pub struct RemoveTrailingReturn { - pub file_id: HirFileId, - pub return_expr: AstPtr, + pub return_expr: InFile>, } #[derive(Debug)] @@ -460,11 +459,10 @@ pub(crate) fn body_validation_diagnostic( BodyValidationDiagnostic::RemoveTrailingReturn { return_expr } => { if let Ok(source_ptr) = source_map.expr_syntax(return_expr) { // Filters out desugared return expressions (e.g. desugared try operators). - if ast::ReturnExpr::can_cast(source_ptr.value.kind()) { + if let Some(ptr) = source_ptr.value.cast::() { return Some( RemoveTrailingReturn { - file_id: source_ptr.file_id, - return_expr: source_ptr.value, + return_expr: InFile::new(source_ptr.file_id, ptr), } .into(), ); diff --git a/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs b/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs index 6c4ec7c132f..276ac0d15d9 100644 --- a/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs +++ b/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs @@ -1,9 +1,9 @@ -use hir::{db::ExpandDatabase, diagnostics::RemoveTrailingReturn, HirFileIdExt, InFile}; -use ide_db::{assists::Assist, source_change::SourceChange}; -use syntax::{ast, AstNode, SyntaxNodePtr}; +use hir::{db::ExpandDatabase, diagnostics::RemoveTrailingReturn}; +use ide_db::{assists::Assist, base_db::FileRange, source_change::SourceChange}; +use syntax::{ast, AstNode}; use text_edit::TextEdit; -use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext}; +use crate::{adjusted_display_range, fix, Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: remove-trailing-return // @@ -13,12 +13,12 @@ pub(crate) fn remove_trailing_return( ctx: &DiagnosticsContext<'_>, d: &RemoveTrailingReturn, ) -> Diagnostic { - let display_range = ctx.sema.diagnostics_display_range(InFile { - file_id: d.file_id, - value: expr_stmt(ctx, d) - .as_ref() - .map(|stmt| SyntaxNodePtr::new(stmt.syntax())) - .unwrap_or_else(|| d.return_expr.into()), + let display_range = adjusted_display_range(ctx, d.return_expr, &|return_expr| { + return_expr + .syntax() + .parent() + .and_then(ast::ExprStmt::cast) + .map(|stmt| stmt.syntax().text_range()) }); Diagnostic::new( DiagnosticCode::Clippy("needless_return"), @@ -29,15 +29,20 @@ pub(crate) fn remove_trailing_return( } fn fixes(ctx: &DiagnosticsContext<'_>, d: &RemoveTrailingReturn) -> Option> { - let return_expr = return_expr(ctx, d)?; - let stmt = expr_stmt(ctx, d); + let root = ctx.sema.db.parse_or_expand(d.return_expr.file_id); + let return_expr = d.return_expr.value.to_node(&root); + let stmt = return_expr.syntax().parent().and_then(ast::ExprStmt::cast); + + let FileRange { range, file_id } = + ctx.sema.original_range_opt(stmt.as_ref().map_or(return_expr.syntax(), AstNode::syntax))?; + if Some(file_id) != d.return_expr.file_id.file_id() { + return None; + } - let range = stmt.as_ref().map_or(return_expr.syntax(), AstNode::syntax).text_range(); let replacement = return_expr.expr().map_or_else(String::new, |expr| format!("{}", expr.syntax().text())); - let edit = TextEdit::replace(range, replacement); - let source_change = SourceChange::from_text_edit(d.file_id.original_file(ctx.sema.db), edit); + let source_change = SourceChange::from_text_edit(file_id, edit); Some(vec![fix( "remove_trailing_return", @@ -47,17 +52,6 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &RemoveTrailingReturn) -> Option, d: &RemoveTrailingReturn) -> Option { - let root = ctx.sema.db.parse_or_expand(d.file_id); - let expr = d.return_expr.to_node(&root); - ast::ReturnExpr::cast(expr.syntax().clone()) -} - -fn expr_stmt(ctx: &DiagnosticsContext<'_>, d: &RemoveTrailingReturn) -> Option { - let return_expr = return_expr(ctx, d)?; - return_expr.syntax().parent().and_then(ast::ExprStmt::cast) -} - #[cfg(test)] mod tests { use crate::tests::{check_diagnostics, check_fix}; From 602acfcb70f5b0af1157268196eba440cf064b25 Mon Sep 17 00:00:00 2001 From: davidsemakula Date: Thu, 8 Feb 2024 19:29:27 +0300 Subject: [PATCH 5/5] fix conflicting "unnecessary else" and "trailing return" diagnostics tests --- crates/hir/src/diagnostics.rs | 2 +- .../src/handlers/remove_trailing_return.rs | 10 +++++-- .../src/handlers/remove_unnecessary_else.rs | 14 ++++++---- crates/ide-diagnostics/src/tests.rs | 28 +++++++++++++++++-- 4 files changed, 42 insertions(+), 12 deletions(-) diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index e07c48aa8ac..08843a6c999 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -67,9 +67,9 @@ fn from(d: $diag) -> AnyDiagnostic { NoSuchField, PrivateAssocItem, PrivateField, - ReplaceFilterMapNextWithFindMap, RemoveTrailingReturn, RemoveUnnecessaryElse, + ReplaceFilterMapNextWithFindMap, TraitImplIncorrectSafety, TraitImplMissingAssocItems, TraitImplOrphan, diff --git a/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs b/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs index 276ac0d15d9..605e8baba0a 100644 --- a/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs +++ b/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs @@ -54,7 +54,9 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &RemoveTrailingReturn) -> Option u8 { #[test] fn remove_trailing_return_in_if() { - check_diagnostics( + check_diagnostics_with_disabled( r#" fn foo(x: usize) -> u8 { if x > 0 { @@ -138,6 +140,7 @@ fn foo(x: usize) -> u8 { } //^^^^^^^^^ 💡 weak: replace return ; with } "#, + std::iter::once("remove-unnecessary-else".to_string()), ); } @@ -287,7 +290,7 @@ fn foo() -> u8 { #[test] fn replace_in_if() { - check_fix( + check_fix_with_disabled( r#" fn foo(x: usize) -> u8 { if x > 0 { @@ -306,6 +309,7 @@ fn foo(x: usize) -> u8 { } } "#, + std::iter::once("remove-unnecessary-else".to_string()), ); check_fix( r#" diff --git a/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs b/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs index c6c85256f93..124086c8fa1 100644 --- a/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs +++ b/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs @@ -87,11 +87,15 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &RemoveUnnecessaryElse) -> Option) #[track_caller] fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) { + let mut config = DiagnosticsConfig::test_sample(); + config.expr_fill_default = ExprFillDefaultMode::Default; + check_nth_fix_with_config(config, nth, ra_fixture_before, ra_fixture_after) +} + +#[track_caller] +pub(crate) fn check_fix_with_disabled( + ra_fixture_before: &str, + ra_fixture_after: &str, + disabled: impl Iterator, +) { + let mut config = DiagnosticsConfig::test_sample(); + config.expr_fill_default = ExprFillDefaultMode::Default; + config.disabled.extend(disabled); + check_nth_fix_with_config(config, 0, ra_fixture_before, ra_fixture_after) +} + +#[track_caller] +fn check_nth_fix_with_config( + config: DiagnosticsConfig, + nth: usize, + ra_fixture_before: &str, + ra_fixture_after: &str, +) { let after = trim_indent(ra_fixture_after); let (db, file_position) = RootDatabase::with_position(ra_fixture_before); - let mut conf = DiagnosticsConfig::test_sample(); - conf.expr_fill_default = ExprFillDefaultMode::Default; let diagnostic = - super::diagnostics(&db, &conf, &AssistResolveStrategy::All, file_position.file_id) + super::diagnostics(&db, &config, &AssistResolveStrategy::All, file_position.file_id) .pop() .expect("no diagnostics"); let fix = &diagnostic