diff options
Diffstat (limited to 'src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs')
-rw-r--r-- | src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs new file mode 100644 index 000000000..9826e1c70 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs @@ -0,0 +1,131 @@ +use hir::{db::AstDatabase, InFile}; +use ide_db::source_change::SourceChange; +use syntax::{ + ast::{self, HasArgList}, + AstNode, TextRange, +}; +use text_edit::TextEdit; + +use crate::{fix, Assist, Diagnostic, DiagnosticsContext, Severity}; + +// Diagnostic: replace-filter-map-next-with-find-map +// +// This diagnostic is triggered when `.filter_map(..).next()` is used, rather than the more concise `.find_map(..)`. +pub(crate) fn replace_filter_map_next_with_find_map( + ctx: &DiagnosticsContext<'_>, + d: &hir::ReplaceFilterMapNextWithFindMap, +) -> Diagnostic { + Diagnostic::new( + "replace-filter-map-next-with-find-map", + "replace filter_map(..).next() with find_map(..)", + ctx.sema.diagnostics_display_range(InFile::new(d.file, d.next_expr.clone().into())).range, + ) + .severity(Severity::WeakWarning) + .with_fixes(fixes(ctx, d)) +} + +fn fixes( + ctx: &DiagnosticsContext<'_>, + d: &hir::ReplaceFilterMapNextWithFindMap, +) -> Option<Vec<Assist>> { + let root = ctx.sema.db.parse_or_expand(d.file)?; + let next_expr = d.next_expr.to_node(&root); + let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?; + + let filter_map_call = ast::MethodCallExpr::cast(next_call.receiver()?.syntax().clone())?; + let filter_map_name_range = filter_map_call.name_ref()?.ident_token()?.text_range(); + let filter_map_args = filter_map_call.arg_list()?; + + let range_to_replace = + TextRange::new(filter_map_name_range.start(), next_expr.syntax().text_range().end()); + let replacement = format!("find_map{}", filter_map_args.syntax().text()); + let trigger_range = next_expr.syntax().text_range(); + + let edit = TextEdit::replace(range_to_replace, replacement); + + let source_change = SourceChange::from_text_edit(d.file.original_file(ctx.sema.db), edit); + + Some(vec![fix( + "replace_with_find_map", + "Replace filter_map(..).next() with find_map()", + source_change, + trigger_range, + )]) +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_diagnostics, check_fix}; + + #[test] + fn replace_filter_map_next_with_find_map2() { + check_diagnostics( + r#" +//- minicore: iterators +fn foo() { + let m = core::iter::repeat(()).filter_map(|()| Some(92)).next(); +} //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: replace filter_map(..).next() with find_map(..) +"#, + ); + } + + #[test] + fn replace_filter_map_next_with_find_map_no_diagnostic_without_next() { + check_diagnostics( + r#" +//- minicore: iterators +fn foo() { + let m = core::iter::repeat(()) + .filter_map(|()| Some(92)) + .count(); +} +"#, + ); + } + + #[test] + fn replace_filter_map_next_with_find_map_no_diagnostic_with_intervening_methods() { + check_diagnostics( + r#" +//- minicore: iterators +fn foo() { + let m = core::iter::repeat(()) + .filter_map(|()| Some(92)) + .map(|x| x + 2) + .next(); +} +"#, + ); + } + + #[test] + fn replace_filter_map_next_with_find_map_no_diagnostic_if_not_in_chain() { + check_diagnostics( + r#" +//- minicore: iterators +fn foo() { + let m = core::iter::repeat(()) + .filter_map(|()| Some(92)); + let n = m.next(); +} +"#, + ); + } + + #[test] + fn replace_with_find_map() { + check_fix( + r#" +//- minicore: iterators +fn foo() { + let m = core::iter::repeat(()).$0filter_map(|()| Some(92)).next(); +} +"#, + r#" +fn foo() { + let m = core::iter::repeat(()).find_map(|()| Some(92)); +} +"#, + ) + } +} |