diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/completion/src/completions/unqualified_path.rs | 12 | ||||
-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 | 39 | ||||
-rw-r--r-- | crates/ide/src/diagnostics.rs | 3 | ||||
-rw-r--r-- | crates/ide/src/diagnostics/fixes.rs | 23 | ||||
-rw-r--r-- | crates/syntax/src/algo.rs | 22 |
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 | }; |
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..849415706 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::{expr::Statement, path::path, resolver::HasResolver, AdtId, DefWithBodyId}; |
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}; |
@@ -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 | ||
322 | pub fn record_literal_missing_fields( | 355 | pub 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 | ||
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 | |||
108 | impl DiagnosticWithFix for IncorrectCase { | 129 | impl 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 | |||
610 | fn 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 | } |