Skip to content

Commit 44978ac

Browse files
bors[bot]ivan770
andauthored
Merge #6769
6769: Add native "remove this semicolon" diagnostics r=matklad a=ivan770 Closes #6739 ![demo2](https://user-images.githubusercontent.com/14003886/101530533-b76c3180-399a-11eb-9d18-5c8457721655.gif) Co-authored-by: ivan770 <[email protected]>
2 parents 5a6065e + bbb0bc7 commit 44978ac

File tree

5 files changed

+86
-5
lines changed

5 files changed

+86
-5
lines changed

crates/hir/src/diagnostics.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ pub use hir_expand::diagnostics::{
55
};
66
pub use hir_ty::diagnostics::{
77
IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr,
8-
NoSuchField,
8+
NoSuchField, RemoveThisSemicolon,
99
};

crates/hir_ty/src/diagnostics.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,30 @@ impl Diagnostic for MissingOkInTailExpr {
216216
}
217217
}
218218

219+
#[derive(Debug)]
220+
pub struct RemoveThisSemicolon {
221+
pub file: HirFileId,
222+
pub expr: AstPtr<ast::Expr>,
223+
}
224+
225+
impl Diagnostic for RemoveThisSemicolon {
226+
fn code(&self) -> DiagnosticCode {
227+
DiagnosticCode("remove-this-semicolon")
228+
}
229+
230+
fn message(&self) -> String {
231+
"Remove this semicolon".to_string()
232+
}
233+
234+
fn display_source(&self) -> InFile<SyntaxNodePtr> {
235+
InFile { file_id: self.file, value: self.expr.clone().into() }
236+
}
237+
238+
fn as_any(&self) -> &(dyn Any + Send + 'static) {
239+
self
240+
}
241+
}
242+
219243
// Diagnostic: break-outside-of-loop
220244
//
221245
// This diagnostic is triggered if `break` keyword is used outside of a loop.

crates/hir_ty/src/diagnostics/expr.rs

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use std::sync::Arc;
44

5-
use hir_def::{path::path, resolver::HasResolver, AdtId, DefWithBodyId};
5+
use hir_def::{expr::Statement, path::path, resolver::HasResolver, AdtId, DefWithBodyId};
66
use hir_expand::diagnostics::DiagnosticSink;
77
use rustc_hash::FxHashSet;
88
use syntax::{ast, AstPtr};
@@ -12,6 +12,7 @@ use crate::{
1212
diagnostics::{
1313
match_check::{is_useful, MatchCheckCtx, Matrix, PatStack, Usefulness},
1414
MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, MissingPatFields,
15+
RemoveThisSemicolon,
1516
},
1617
utils::variant_data,
1718
ApplicationTy, InferenceResult, Ty, TypeCtor,
@@ -76,8 +77,12 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
7677
}
7778
}
7879
let body_expr = &body[body.body_expr];
79-
if let Expr::Block { tail: Some(t), .. } = body_expr {
80-
self.validate_results_in_tail_expr(body.body_expr, *t, db);
80+
if let Expr::Block { statements, tail, .. } = body_expr {
81+
if let Some(t) = tail {
82+
self.validate_results_in_tail_expr(body.body_expr, *t, db);
83+
} else if let Some(Statement::Expr(id)) = statements.last() {
84+
self.validate_missing_tail_expr(body.body_expr, *id, db);
85+
}
8186
}
8287
}
8388

@@ -317,6 +322,34 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
317322
}
318323
}
319324
}
325+
326+
fn validate_missing_tail_expr(
327+
&mut self,
328+
body_id: ExprId,
329+
possible_tail_id: ExprId,
330+
db: &dyn HirDatabase,
331+
) {
332+
let mismatch = match self.infer.type_mismatch_for_expr(body_id) {
333+
Some(m) => m,
334+
None => return,
335+
};
336+
337+
let possible_tail_ty = match self.infer.type_of_expr.get(possible_tail_id) {
338+
Some(ty) => ty,
339+
None => return,
340+
};
341+
342+
if mismatch.actual != Ty::unit() || mismatch.expected != *possible_tail_ty {
343+
return;
344+
}
345+
346+
let (_, source_map) = db.body_with_source_map(self.owner.into());
347+
348+
if let Ok(source_ptr) = source_map.expr_syntax(possible_tail_id) {
349+
self.sink
350+
.push(RemoveThisSemicolon { file: source_ptr.file_id, expr: source_ptr.value });
351+
}
352+
}
320353
}
321354

322355
pub fn record_literal_missing_fields(

crates/ide/src/diagnostics.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@ pub(crate) fn diagnostics(
131131
.on::<hir::diagnostics::NoSuchField, _>(|d| {
132132
res.borrow_mut().push(diagnostic_with_fix(d, &sema));
133133
})
134+
.on::<hir::diagnostics::RemoveThisSemicolon, _>(|d| {
135+
res.borrow_mut().push(diagnostic_with_fix(d, &sema));
136+
})
134137
.on::<hir::diagnostics::IncorrectCase, _>(|d| {
135138
res.borrow_mut().push(warning_with_fix(d, &sema));
136139
})

crates/ide/src/diagnostics/fixes.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use hir::{
44
db::AstDatabase,
55
diagnostics::{
66
Diagnostic, IncorrectCase, MissingFields, MissingOkInTailExpr, NoSuchField,
7-
UnresolvedModule,
7+
RemoveThisSemicolon, UnresolvedModule,
88
},
99
HasSource, HirDisplay, InFile, Semantics, VariantDef,
1010
};
@@ -105,6 +105,27 @@ impl DiagnosticWithFix for MissingOkInTailExpr {
105105
}
106106
}
107107

108+
impl DiagnosticWithFix for RemoveThisSemicolon {
109+
fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> {
110+
let root = sema.db.parse_or_expand(self.file)?;
111+
112+
let semicolon = self
113+
.expr
114+
.to_node(&root)
115+
.syntax()
116+
.parent()
117+
.and_then(ast::ExprStmt::cast)
118+
.and_then(|expr| expr.semicolon_token())?
119+
.text_range();
120+
121+
let edit = TextEdit::delete(semicolon);
122+
let source_change =
123+
SourceFileEdit { file_id: self.file.original_file(sema.db), edit }.into();
124+
125+
Some(Fix::new("Remove this semicolon", source_change, semicolon))
126+
}
127+
}
128+
108129
impl DiagnosticWithFix for IncorrectCase {
109130
fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> {
110131
let root = sema.db.parse_or_expand(self.file)?;

0 commit comments

Comments
 (0)