diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index f839616ce43..b144bb33506 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -39,6 +39,7 @@ fn from(d: $diag) -> AnyDiagnostic { MissingFields, MissingUnsafe, NoSuchField, + RemoveThisSemicolon, UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, @@ -153,26 +154,7 @@ pub struct MismatchedArgCount { #[derive(Debug)] pub struct RemoveThisSemicolon { - pub file: HirFileId, - pub expr: AstPtr, -} - -impl Diagnostic for RemoveThisSemicolon { - fn code(&self) -> DiagnosticCode { - DiagnosticCode("remove-this-semicolon") - } - - fn message(&self) -> String { - "Remove this semicolon".to_string() - } - - fn display_source(&self) -> InFile { - InFile { file_id: self.file, value: self.expr.clone().into() } - } - - fn as_any(&self) -> &(dyn Any + Send + 'static) { - self - } + pub expr: InFile>, } // Diagnostic: missing-ok-or-some-in-tail-expr diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index c1af5f0978a..cb9bf60b817 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -1184,10 +1184,7 @@ pub fn diagnostics( } BodyValidationDiagnostic::RemoveThisSemicolon { expr } => { match source_map.expr_syntax(expr) { - Ok(source_ptr) => sink.push(RemoveThisSemicolon { - file: source_ptr.file_id, - expr: source_ptr.value, - }), + Ok(expr) => acc.push(RemoveThisSemicolon { expr }.into()), Err(SyntheticSyntax) => (), } } diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 4c92d0cf4c1..3ced08f303a 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs @@ -11,6 +11,7 @@ mod missing_fields; mod missing_unsafe; mod no_such_field; +mod remove_this_semicolon; mod unimplemented_builtin_macro; mod unresolved_extern_crate; mod unresolved_import; @@ -165,9 +166,6 @@ pub(crate) fn diagnostics( .on::(|d| { res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve)); }) - .on::(|d| { - res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve)); - }) .on::(|d| { res.borrow_mut().push(warning_with_fix(d, &sema, resolve)); }) @@ -223,10 +221,11 @@ pub(crate) fn diagnostics( let d = match diag { AnyDiagnostic::BreakOutsideOfLoop(d) => break_outside_of_loop::break_outside_of_loop(&ctx, &d), AnyDiagnostic::MacroError(d) => macro_error::macro_error(&ctx, &d), + AnyDiagnostic::MismatchedArgCount(d) => mismatched_arg_count::mismatched_arg_count(&ctx, &d), AnyDiagnostic::MissingFields(d) => missing_fields::missing_fields(&ctx, &d), AnyDiagnostic::MissingUnsafe(d) => missing_unsafe::missing_unsafe(&ctx, &d), - AnyDiagnostic::MismatchedArgCount(d) => mismatched_arg_count::mismatched_arg_count(&ctx, &d), AnyDiagnostic::NoSuchField(d) => no_such_field::no_such_field(&ctx, &d), + AnyDiagnostic::RemoveThisSemicolon(d) => remove_this_semicolon::remove_this_semicolon(&ctx, &d), AnyDiagnostic::UnimplementedBuiltinMacro(d) => unimplemented_builtin_macro::unimplemented_builtin_macro(&ctx, &d), AnyDiagnostic::UnresolvedExternCrate(d) => unresolved_extern_crate::unresolved_extern_crate(&ctx, &d), AnyDiagnostic::UnresolvedImport(d) => unresolved_import::unresolved_import(&ctx, &d), @@ -838,16 +837,6 @@ fn x(a: S) { ) } - #[test] - fn missing_semicolon() { - check_diagnostics( - r#" - fn test() -> i32 { 123; } - //^^^ Remove this semicolon - "#, - ); - } - #[test] fn import_extern_crate_clash_with_inner_item() { // This is more of a resolver test, but doesn't really work with the hir_def testsuite. diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs index a2e792b3b61..70f17881ec9 100644 --- a/crates/ide/src/diagnostics/fixes.rs +++ b/crates/ide/src/diagnostics/fixes.rs @@ -1,8 +1,6 @@ //! Provides a way to attach fixes to the diagnostics. //! The same module also has all curret custom fixes for the diagnostics implemented. mod change_case; -mod create_field; -mod remove_semicolon; mod replace_with_find_map; mod wrap_tail_expr; diff --git a/crates/ide/src/diagnostics/fixes/create_field.rs b/crates/ide/src/diagnostics/fixes/create_field.rs deleted file mode 100644 index 8b137891791..00000000000 --- a/crates/ide/src/diagnostics/fixes/create_field.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/crates/ide/src/diagnostics/fixes/remove_semicolon.rs b/crates/ide/src/diagnostics/fixes/remove_semicolon.rs deleted file mode 100644 index f1724d47918..00000000000 --- a/crates/ide/src/diagnostics/fixes/remove_semicolon.rs +++ /dev/null @@ -1,41 +0,0 @@ -use hir::{db::AstDatabase, diagnostics::RemoveThisSemicolon, Semantics}; -use ide_assists::{Assist, AssistResolveStrategy}; -use ide_db::{source_change::SourceChange, RootDatabase}; -use syntax::{ast, AstNode}; -use text_edit::TextEdit; - -use crate::diagnostics::{fix, DiagnosticWithFixes}; - -impl DiagnosticWithFixes for RemoveThisSemicolon { - fn fixes( - &self, - sema: &Semantics, - _resolve: &AssistResolveStrategy, - ) -> Option> { - let root = sema.db.parse_or_expand(self.file)?; - - let semicolon = self - .expr - .to_node(&root) - .syntax() - .parent() - .and_then(ast::ExprStmt::cast) - .and_then(|expr| expr.semicolon_token())? - .text_range(); - - let edit = TextEdit::delete(semicolon); - let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit); - - Some(vec![fix("remove_semicolon", "Remove this semicolon", source_change, semicolon)]) - } -} - -#[cfg(test)] -mod tests { - use crate::diagnostics::tests::check_fix; - - #[test] - fn remove_semicolon() { - check_fix(r#"fn f() -> i32 { 92$0; }"#, r#"fn f() -> i32 { 92 }"#); - } -} diff --git a/crates/ide/src/diagnostics/no_such_field.rs b/crates/ide/src/diagnostics/no_such_field.rs index 61962de28e8..edc63c2468c 100644 --- a/crates/ide/src/diagnostics/no_such_field.rs +++ b/crates/ide/src/diagnostics/no_such_field.rs @@ -17,7 +17,7 @@ pub(super) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Diagnostic { Diagnostic::new( "no-such-field", - "no such field".to_string(), + "no such field", ctx.sema.diagnostics_display_range(d.field.clone().map(|it| it.into())).range, ) .with_fixes(fixes(ctx, d)) diff --git a/crates/ide/src/diagnostics/remove_this_semicolon.rs b/crates/ide/src/diagnostics/remove_this_semicolon.rs new file mode 100644 index 00000000000..814cb0f8c28 --- /dev/null +++ b/crates/ide/src/diagnostics/remove_this_semicolon.rs @@ -0,0 +1,64 @@ +use hir::db::AstDatabase; +use ide_db::source_change::SourceChange; +use syntax::{ast, AstNode}; +use text_edit::TextEdit; + +use crate::{ + diagnostics::{fix, Diagnostic, DiagnosticsContext}, + Assist, +}; + +// Diagnostic: remove-this-semicolon +// +// This diagnostic is triggered when there's an erroneous `;` at the end of the block. +pub(super) fn remove_this_semicolon( + ctx: &DiagnosticsContext<'_>, + d: &hir::RemoveThisSemicolon, +) -> Diagnostic { + Diagnostic::new( + "remove-this-semicolon", + "remove this semicolon", + ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range, + ) + .with_fixes(fixes(ctx, d)) +} + +fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::RemoveThisSemicolon) -> Option> { + let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?; + + let semicolon = d + .expr + .value + .to_node(&root) + .syntax() + .parent() + .and_then(ast::ExprStmt::cast) + .and_then(|expr| expr.semicolon_token())? + .text_range(); + + let edit = TextEdit::delete(semicolon); + let source_change = + SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit); + + Some(vec![fix("remove_semicolon", "Remove this semicolon", source_change, semicolon)]) +} + +#[cfg(test)] +mod tests { + use crate::diagnostics::tests::{check_diagnostics, check_fix}; + + #[test] + fn missing_semicolon() { + check_diagnostics( + r#" +fn test() -> i32 { 123; } + //^^^ remove this semicolon +"#, + ); + } + + #[test] + fn remove_semicolon() { + check_fix(r#"fn f() -> i32 { 92$0; }"#, r#"fn f() -> i32 { 92 }"#); + } +}