aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/completion/src/completions/unqualified_path.rs12
-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.rs39
-rw-r--r--crates/ide/src/diagnostics.rs3
-rw-r--r--crates/ide/src/diagnostics/fixes.rs23
-rw-r--r--crates/syntax/src/algo.rs22
7 files changed, 105 insertions, 20 deletions
diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs
index 544886d29..2b0924ae5 100644
--- a/crates/completion/src/completions/unqualified_path.rs
+++ b/crates/completion/src/completions/unqualified_path.rs
@@ -145,13 +145,11 @@ fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()
145 }) 145 })
146 .filter(|(mod_path, _)| mod_path.len() > 1) 146 .filter(|(mod_path, _)| mod_path.len() > 1)
147 .filter_map(|(import_path, definition)| { 147 .filter_map(|(import_path, definition)| {
148 let ie = 148 render_resolution_with_import(
149 ImportEdit { import_path: import_path.clone(), import_scope: import_scope.clone() }; 149 RenderContext::new(ctx),
150 { 150 ImportEdit { import_path: import_path.clone(), import_scope: import_scope.clone() },
151 let _p = profile::span("totextedit"); 151 &definition,
152 ie.to_text_edit(ctx.config.merge); 152 )
153 }
154 render_resolution_with_import(RenderContext::new(ctx), ie, &definition)
155 }); 153 });
156 154
157 acc.add_all(possible_imports); 155 acc.add_all(possible_imports);
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index eaf1a14ec..b1c924167 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..849415706 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::{expr::Statement, path::path, resolver::HasResolver, AdtId, DefWithBodyId};
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};
@@ -12,6 +12,7 @@ use crate::{
12 diagnostics::{ 12 diagnostics::{
13 match_check::{is_useful, MatchCheckCtx, Matrix, PatStack, Usefulness}, 13 match_check::{is_useful, MatchCheckCtx, Matrix, PatStack, Usefulness},
14 MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, MissingPatFields, 14 MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, MissingPatFields,
15 RemoveThisSemicolon,
15 }, 16 },
16 utils::variant_data, 17 utils::variant_data,
17 ApplicationTy, InferenceResult, Ty, TypeCtor, 18 ApplicationTy, InferenceResult, Ty, TypeCtor,
@@ -76,8 +77,12 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
76 } 77 }
77 } 78 }
78 let body_expr = &body[body.body_expr]; 79 let body_expr = &body[body.body_expr];
79 if let Expr::Block { tail: Some(t), .. } = body_expr { 80 if let Expr::Block { statements, tail, .. } = body_expr {
80 self.validate_results_in_tail_expr(body.body_expr, *t, db); 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 }
81 } 86 }
82 } 87 }
83 88
@@ -317,6 +322,34 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
317 } 322 }
318 } 323 }
319 } 324 }
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 }
320} 353}
321 354
322pub fn record_literal_missing_fields( 355pub fn record_literal_missing_fields(
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index d09f3a0a1..049f808dc 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 a900d7bae..13240672f 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, InFile, Semantics, VariantDef, 9 HasSource, HirDisplay, InFile, Semantics, VariantDef,
10}; 10};
@@ -105,6 +105,27 @@ impl DiagnosticWithFix for MissingOkInTailExpr {
105 } 105 }
106} 106}
107 107
108impl 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
108impl DiagnosticWithFix for IncorrectCase { 129impl DiagnosticWithFix for IncorrectCase {
109 fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> { 130 fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> {
110 let root = sema.db.parse_or_expand(self.file)?; 131 let root = sema.db.parse_or_expand(self.file)?;
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs
index ee89d9867..5696c014f 100644
--- a/crates/syntax/src/algo.rs
+++ b/crates/syntax/src/algo.rs
@@ -476,7 +476,8 @@ impl<'a> SyntaxRewriter<'a> {
476 if self.f.is_none() && self.replacements.is_empty() && self.insertions.is_empty() { 476 if self.f.is_none() && self.replacements.is_empty() && self.insertions.is_empty() {
477 return node.clone(); 477 return node.clone();
478 } 478 }
479 self.rewrite_children(node) 479 let green = self.rewrite_children(node);
480 with_green(node, green)
480 } 481 }
481 482
482 pub fn rewrite_ast<N: AstNode>(self, node: &N) -> N { 483 pub fn rewrite_ast<N: AstNode>(self, node: &N) -> N {
@@ -523,7 +524,7 @@ impl<'a> SyntaxRewriter<'a> {
523 self.insertions.get(pos).map(|insertions| insertions.iter().cloned()) 524 self.insertions.get(pos).map(|insertions| insertions.iter().cloned())
524 } 525 }
525 526
526 fn rewrite_children(&self, node: &SyntaxNode) -> SyntaxNode { 527 fn rewrite_children(&self, node: &SyntaxNode) -> rowan::GreenNode {
527 let _p = profile::span("rewrite_children"); 528 let _p = profile::span("rewrite_children");
528 529
529 // FIXME: this could be made much faster. 530 // FIXME: this could be made much faster.
@@ -534,7 +535,8 @@ impl<'a> SyntaxRewriter<'a> {
534 for child in node.children_with_tokens() { 535 for child in node.children_with_tokens() {
535 self.rewrite_self(&mut new_children, &child); 536 self.rewrite_self(&mut new_children, &child);
536 } 537 }
537 with_children(node, new_children) 538
539 rowan::GreenNode::new(rowan::SyntaxKind(node.kind() as u16), new_children)
538 } 540 }
539 541
540 fn rewrite_self( 542 fn rewrite_self(
@@ -556,7 +558,7 @@ impl<'a> SyntaxRewriter<'a> {
556 match element { 558 match element {
557 NodeOrToken::Token(it) => acc.push(NodeOrToken::Token(it.green().clone())), 559 NodeOrToken::Token(it) => acc.push(NodeOrToken::Token(it.green().clone())),
558 NodeOrToken::Node(it) => { 560 NodeOrToken::Node(it) => {
559 acc.push(NodeOrToken::Node(self.rewrite_children(it).green().clone())); 561 acc.push(NodeOrToken::Node(self.rewrite_children(it)));
560 } 562 }
561 } 563 }
562 } 564 }
@@ -601,14 +603,18 @@ fn with_children(
601) -> SyntaxNode { 603) -> SyntaxNode {
602 let _p = profile::span("with_children"); 604 let _p = profile::span("with_children");
603 605
604 let len = new_children.iter().map(|it| it.text_len()).sum::<TextSize>(); 606 let new_green = rowan::GreenNode::new(rowan::SyntaxKind(parent.kind() as u16), new_children);
605 let new_node = rowan::GreenNode::new(rowan::SyntaxKind(parent.kind() as u16), new_children); 607 with_green(parent, new_green)
606 let new_root_node = parent.replace_with(new_node); 608}
609
610fn with_green(syntax_node: &SyntaxNode, green: rowan::GreenNode) -> SyntaxNode {
611 let len = green.children().map(|it| it.text_len()).sum::<TextSize>();
612 let new_root_node = syntax_node.replace_with(green);
607 let new_root_node = SyntaxNode::new_root(new_root_node); 613 let new_root_node = SyntaxNode::new_root(new_root_node);
608 614
609 // FIXME: use a more elegant way to re-fetch the node (#1185), make 615 // FIXME: use a more elegant way to re-fetch the node (#1185), make
610 // `range` private afterwards 616 // `range` private afterwards
611 let mut ptr = SyntaxNodePtr::new(parent); 617 let mut ptr = SyntaxNodePtr::new(syntax_node);
612 ptr.range = TextRange::at(ptr.range.start(), len); 618 ptr.range = TextRange::at(ptr.range.start(), len);
613 ptr.to_node(&new_root_node) 619 ptr.to_node(&new_root_node)
614} 620}