1 use ide_db::{
2     base_db::{FileLoader, FileRange},
3     source_change::SourceChange,
4 };
5 use syntax::{TextRange, TextSize};
6 use text_edit::TextEdit;
7 
8 use crate::{fix, Assist, Diagnostic, DiagnosticsContext};
9 
10 // Diagnostic: remove-this-semicolon
11 //
12 // This diagnostic is triggered when there's an erroneous `;` at the end of the block.
remove_this_semicolon( ctx: &DiagnosticsContext<'_>, d: &hir::RemoveThisSemicolon, ) -> Diagnostic13 pub(crate) fn remove_this_semicolon(
14     ctx: &DiagnosticsContext<'_>,
15     d: &hir::RemoveThisSemicolon,
16 ) -> Diagnostic {
17     Diagnostic::new(
18         "remove-this-semicolon",
19         "remove this semicolon",
20         semicolon_range(ctx, d).unwrap_or_else(|it| it).range,
21     )
22     .with_fixes(fixes(ctx, d))
23 }
24 
semicolon_range( ctx: &DiagnosticsContext<'_>, d: &hir::RemoveThisSemicolon, ) -> Result<FileRange, FileRange>25 fn semicolon_range(
26     ctx: &DiagnosticsContext<'_>,
27     d: &hir::RemoveThisSemicolon,
28 ) -> Result<FileRange, FileRange> {
29     let expr_range = ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into()));
30     let file_text = ctx.sema.db.file_text(expr_range.file_id);
31     let range_end: usize = expr_range.range.end().into();
32     // FIXME: This doesn't handle whitespace and comments, but handling those in
33     // the presence of macros might prove tricky...
34     if file_text[range_end..].starts_with(';') {
35         Ok(FileRange {
36             file_id: expr_range.file_id,
37             range: TextRange::at(expr_range.range.end(), TextSize::of(';')),
38         })
39     } else {
40         Err(expr_range)
41     }
42 }
43 
fixes(ctx: &DiagnosticsContext<'_>, d: &hir::RemoveThisSemicolon) -> Option<Vec<Assist>>44 fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::RemoveThisSemicolon) -> Option<Vec<Assist>> {
45     let semicolon_range = semicolon_range(ctx, d).ok()?;
46 
47     let edit = TextEdit::delete(semicolon_range.range);
48     let source_change = SourceChange::from_text_edit(semicolon_range.file_id, edit);
49 
50     Some(vec![fix(
51         "remove_semicolon",
52         "Remove this semicolon",
53         source_change,
54         semicolon_range.range,
55     )])
56 }
57 
58 #[cfg(test)]
59 mod tests {
60     use crate::tests::{check_diagnostics, check_fix};
61 
62     #[test]
missing_semicolon()63     fn missing_semicolon() {
64         check_diagnostics(
65             r#"
66 fn test() -> i32 { 123; }
67                     //^ �� error: remove this semicolon
68 "#,
69         );
70     }
71 
72     #[test]
remove_semicolon()73     fn remove_semicolon() {
74         check_fix(r#"fn f() -> i32 { 92$0; }"#, r#"fn f() -> i32 { 92 }"#);
75     }
76 }
77