aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/hir/src/diagnostics.rs2
-rw-r--r--crates/hir_ty/src/diagnostics.rs24
-rw-r--r--crates/hir_ty/src/diagnostics/expr.rs27
-rw-r--r--crates/ide/src/diagnostics.rs3
-rw-r--r--crates/ide/src/diagnostics/fixes.rs26
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};
6pub use hir_ty::diagnostics::{ 6pub 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)]
220pub struct RemoveThisSemicolon {
221 pub file: HirFileId,
222 pub expr: AstPtr<ast::Expr>,
223}
224
225impl 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
3use std::sync::Arc; 3use std::sync::Arc;
4 4
5use hir_def::{path::path, resolver::HasResolver, AdtId, DefWithBodyId}; 5use hir_def::{AdtId, DefWithBodyId, expr::Statement, path::path, resolver::HasResolver};
6use hir_expand::diagnostics::DiagnosticSink; 6use hir_expand::diagnostics::DiagnosticSink;
7use rustc_hash::FxHashSet; 7use rustc_hash::FxHashSet;
8use syntax::{ast, AstPtr}; 8use syntax::{ast, AstPtr};
@@ -23,6 +23,8 @@ pub(crate) use hir_def::{
23 LocalFieldId, VariantId, 23 LocalFieldId, VariantId,
24}; 24};
25 25
26use super::RemoveThisSemicolon;
27
26pub(super) struct ExprValidator<'a, 'b: 'a> { 28pub(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
322pub fn record_literal_missing_fields( 347pub 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};
16use syntax::{ 16use syntax::{AstNode, Direction, T, algo, ast::{self, ExprStmt, edit::IndentLevel, make}};
17 algo,
18 ast::{self, edit::IndentLevel, make},
19 AstNode,
20};
21use text_edit::TextEdit; 17use text_edit::TextEdit;
22 18
23use crate::{diagnostics::Fix, references::rename::rename_with_semantics, FilePosition}; 19use crate::{diagnostics::Fix, references::rename::rename_with_semantics, FilePosition};
@@ -102,6 +98,24 @@ impl DiagnosticWithFix for MissingOkInTailExpr {
102 } 98 }
103} 99}
104 100
101impl 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
105impl DiagnosticWithFix for IncorrectCase { 119impl 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)?;