diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/hir/src/diagnostics.rs | 2 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics.rs | 24 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics/expr.rs | 27 | ||||
-rw-r--r-- | crates/ide/src/diagnostics.rs | 3 | ||||
-rw-r--r-- | crates/ide/src/diagnostics/fixes.rs | 26 |
5 files changed, 74 insertions, 8 deletions
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index eaf1a14ec..0f2ed4bb1 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs | |||
@@ -5,5 +5,5 @@ pub use hir_expand::diagnostics::{ | |||
5 | }; | 5 | }; |
6 | pub use hir_ty::diagnostics::{ | 6 | pub use hir_ty::diagnostics::{ |
7 | IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, | 7 | IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, |
8 | NoSuchField, | 8 | NoSuchField, RemoveThisSemicolon |
9 | }; | 9 | }; |
diff --git a/crates/hir_ty/src/diagnostics.rs b/crates/hir_ty/src/diagnostics.rs index b58fe0ed7..e59487e54 100644 --- a/crates/hir_ty/src/diagnostics.rs +++ b/crates/hir_ty/src/diagnostics.rs | |||
@@ -216,6 +216,30 @@ impl Diagnostic for MissingOkInTailExpr { | |||
216 | } | 216 | } |
217 | } | 217 | } |
218 | 218 | ||
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 | |||
219 | // Diagnostic: break-outside-of-loop | 243 | // Diagnostic: break-outside-of-loop |
220 | // | 244 | // |
221 | // This diagnostic is triggered if `break` keyword is used outside of a loop. | 245 | // This diagnostic is triggered if `break` keyword is used outside of a loop. |
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs index 434b19354..313422968 100644 --- a/crates/hir_ty/src/diagnostics/expr.rs +++ b/crates/hir_ty/src/diagnostics/expr.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | use std::sync::Arc; | 3 | use std::sync::Arc; |
4 | 4 | ||
5 | use hir_def::{path::path, resolver::HasResolver, AdtId, DefWithBodyId}; | 5 | use hir_def::{AdtId, DefWithBodyId, expr::Statement, path::path, resolver::HasResolver}; |
6 | use hir_expand::diagnostics::DiagnosticSink; | 6 | use hir_expand::diagnostics::DiagnosticSink; |
7 | use rustc_hash::FxHashSet; | 7 | use rustc_hash::FxHashSet; |
8 | use syntax::{ast, AstPtr}; | 8 | use syntax::{ast, AstPtr}; |
@@ -23,6 +23,8 @@ pub(crate) use hir_def::{ | |||
23 | LocalFieldId, VariantId, | 23 | LocalFieldId, VariantId, |
24 | }; | 24 | }; |
25 | 25 | ||
26 | use super::RemoveThisSemicolon; | ||
27 | |||
26 | pub(super) struct ExprValidator<'a, 'b: 'a> { | 28 | pub(super) struct ExprValidator<'a, 'b: 'a> { |
27 | owner: DefWithBodyId, | 29 | owner: DefWithBodyId, |
28 | infer: Arc<InferenceResult>, | 30 | infer: Arc<InferenceResult>, |
@@ -78,6 +80,12 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
78 | let body_expr = &body[body.body_expr]; | 80 | let body_expr = &body[body.body_expr]; |
79 | if let Expr::Block { tail: Some(t), .. } = body_expr { | 81 | if let Expr::Block { tail: Some(t), .. } = body_expr { |
80 | self.validate_results_in_tail_expr(body.body_expr, *t, db); | 82 | self.validate_results_in_tail_expr(body.body_expr, *t, db); |
83 | } else { | ||
84 | if let Expr::Block { statements, .. } = body_expr { | ||
85 | if let Some(Statement::Expr(id)) = statements.last() { | ||
86 | self.validate_missing_tail_expr(body.body_expr, *id, db); | ||
87 | } | ||
88 | } | ||
81 | } | 89 | } |
82 | } | 90 | } |
83 | 91 | ||
@@ -317,6 +325,23 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
317 | } | 325 | } |
318 | } | 326 | } |
319 | } | 327 | } |
328 | |||
329 | fn validate_missing_tail_expr(&mut self, body_id: ExprId, possible_tail_id: ExprId, db: &dyn HirDatabase) { | ||
330 | let mismatch = match self.infer.type_mismatch_for_expr(body_id) { | ||
331 | Some(m) => m, | ||
332 | None => return, | ||
333 | }; | ||
334 | |||
335 | if let Some(possible_tail_ty) = self.infer.type_of_expr.get(possible_tail_id) { | ||
336 | if mismatch.actual == Ty::unit() && mismatch.expected == *possible_tail_ty { | ||
337 | let (_, source_map) = db.body_with_source_map(self.owner.into()); | ||
338 | |||
339 | if let Ok(source_ptr) = source_map.expr_syntax(possible_tail_id) { | ||
340 | self.sink.push(RemoveThisSemicolon { file: source_ptr.file_id, expr: source_ptr.value }); | ||
341 | } | ||
342 | } | ||
343 | } | ||
344 | } | ||
320 | } | 345 | } |
321 | 346 | ||
322 | pub fn record_literal_missing_fields( | 347 | pub fn record_literal_missing_fields( |
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index c8453edb3..9157704dc 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs | |||
@@ -131,6 +131,9 @@ pub(crate) fn diagnostics( | |||
131 | .on::<hir::diagnostics::NoSuchField, _>(|d| { | 131 | .on::<hir::diagnostics::NoSuchField, _>(|d| { |
132 | res.borrow_mut().push(diagnostic_with_fix(d, &sema)); | 132 | res.borrow_mut().push(diagnostic_with_fix(d, &sema)); |
133 | }) | 133 | }) |
134 | .on::<hir::diagnostics::RemoveThisSemicolon, _>(|d| { | ||
135 | res.borrow_mut().push(diagnostic_with_fix(d, &sema)); | ||
136 | }) | ||
134 | .on::<hir::diagnostics::IncorrectCase, _>(|d| { | 137 | .on::<hir::diagnostics::IncorrectCase, _>(|d| { |
135 | res.borrow_mut().push(warning_with_fix(d, &sema)); | 138 | res.borrow_mut().push(warning_with_fix(d, &sema)); |
136 | }) | 139 | }) |
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs index d275dd75b..cb4d49ad9 100644 --- a/crates/ide/src/diagnostics/fixes.rs +++ b/crates/ide/src/diagnostics/fixes.rs | |||
@@ -4,7 +4,7 @@ use hir::{ | |||
4 | db::AstDatabase, | 4 | db::AstDatabase, |
5 | diagnostics::{ | 5 | diagnostics::{ |
6 | Diagnostic, IncorrectCase, MissingFields, MissingOkInTailExpr, NoSuchField, | 6 | Diagnostic, IncorrectCase, MissingFields, MissingOkInTailExpr, NoSuchField, |
7 | UnresolvedModule, | 7 | RemoveThisSemicolon, UnresolvedModule, |
8 | }, | 8 | }, |
9 | HasSource, HirDisplay, Semantics, VariantDef, | 9 | HasSource, HirDisplay, Semantics, VariantDef, |
10 | }; | 10 | }; |
@@ -13,11 +13,7 @@ use ide_db::{ | |||
13 | source_change::{FileSystemEdit, SourceFileEdit}, | 13 | source_change::{FileSystemEdit, SourceFileEdit}, |
14 | RootDatabase, | 14 | RootDatabase, |
15 | }; | 15 | }; |
16 | use syntax::{ | 16 | use syntax::{AstNode, Direction, T, algo, ast::{self, ExprStmt, edit::IndentLevel, make}}; |
17 | algo, | ||
18 | ast::{self, edit::IndentLevel, make}, | ||
19 | AstNode, | ||
20 | }; | ||
21 | use text_edit::TextEdit; | 17 | use text_edit::TextEdit; |
22 | 18 | ||
23 | use crate::{diagnostics::Fix, references::rename::rename_with_semantics, FilePosition}; | 19 | use crate::{diagnostics::Fix, references::rename::rename_with_semantics, FilePosition}; |
@@ -102,6 +98,24 @@ impl DiagnosticWithFix for MissingOkInTailExpr { | |||
102 | } | 98 | } |
103 | } | 99 | } |
104 | 100 | ||
101 | impl DiagnosticWithFix for RemoveThisSemicolon { | ||
102 | fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> { | ||
103 | let root = sema.db.parse_or_expand(self.file)?; | ||
104 | |||
105 | let semicolon = self.expr.to_node(&root) | ||
106 | .syntax() | ||
107 | .siblings_with_tokens(Direction::Next) | ||
108 | .filter_map(|it| it.into_token()) | ||
109 | .find(|it| it.kind() == T![;])? | ||
110 | .text_range(); | ||
111 | |||
112 | let edit = TextEdit::delete(semicolon); | ||
113 | let source_change = SourceFileEdit { file_id: self.file.original_file(sema.db), edit }.into(); | ||
114 | |||
115 | Some(Fix::new("Remove this semicolon", source_change, semicolon)) | ||
116 | } | ||
117 | } | ||
118 | |||
105 | impl DiagnosticWithFix for IncorrectCase { | 119 | impl DiagnosticWithFix for IncorrectCase { |
106 | fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> { | 120 | fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> { |
107 | let root = sema.db.parse_or_expand(self.file)?; | 121 | let root = sema.db.parse_or_expand(self.file)?; |