about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-10-25 13:24:07 +0000
committerGitHub <noreply@github.com>2021-10-25 13:24:07 +0000
commit142b6dc650deed819d52602f2a30105d668d7308 (patch)
treeed95cd1ac6afcf1c9538e5b58c4a2f6b83fcba83
parente81e3c39806a43804d215ba33f1354fb78e79a4b (diff)
parenta932935d4e63bdda44c590791184071c53b9ca68 (diff)
downloadrust-142b6dc650deed819d52602f2a30105d668d7308.tar.gz
rust-142b6dc650deed819d52602f2a30105d668d7308.zip
Merge #10631
10631: fix: Fix postfix completions panicking r=Veykril a=Veykril

Fixes https://github.com/rust-analyzer/rust-analyzer/issues/10243, I couldn't reproduce the panic with the given snippet, but this change should still guard against it.
bors r+

Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
-rw-r--r--crates/ide_completion/src/completions/postfix.rs49
-rw-r--r--crates/ide_completion/src/completions/postfix/format_like.rs5
2 files changed, 36 insertions, 18 deletions
diff --git a/crates/ide_completion/src/completions/postfix.rs b/crates/ide_completion/src/completions/postfix.rs
index b35d97e4252..4ace346768a 100644
--- a/crates/ide_completion/src/completions/postfix.rs
+++ b/crates/ide_completion/src/completions/postfix.rs
@@ -55,7 +55,10 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
         None => return,
     };
 
-    let postfix_snippet = build_postfix_snippet_builder(ctx, cap, &dot_receiver);
+    let postfix_snippet = match build_postfix_snippet_builder(ctx, cap, &dot_receiver) {
+        Some(it) => it,
+        None => return,
+    };
 
     if !ctx.config.snippets.is_empty() {
         add_custom_postfix_completions(acc, ctx, &postfix_snippet, &receiver_text);
@@ -123,7 +126,10 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
     // so it's better to consider references now to avoid breaking the compilation
     let dot_receiver = include_references(dot_receiver);
     let receiver_text = get_receiver_text(&dot_receiver, receiver_is_ambiguous_float_literal);
-    let postfix_snippet = build_postfix_snippet_builder(ctx, cap, &dot_receiver);
+    let postfix_snippet = match build_postfix_snippet_builder(ctx, cap, &dot_receiver) {
+        Some(it) => it,
+        None => return,
+    };
 
     match try_enum {
         Some(try_enum) => match try_enum {
@@ -200,27 +206,36 @@ fn include_references(initial_element: &ast::Expr) -> ast::Expr {
     resulting_element
 }
 
-fn build_postfix_snippet_builder<'a>(
-    ctx: &'a CompletionContext,
+fn build_postfix_snippet_builder<'ctx>(
+    ctx: &'ctx CompletionContext,
     cap: SnippetCap,
-    receiver: &'a ast::Expr,
-) -> impl Fn(&str, &str, &str) -> Builder + 'a {
+    receiver: &'ctx ast::Expr,
+) -> Option<impl Fn(&str, &str, &str) -> Builder + 'ctx> {
     let receiver_syntax = receiver.syntax();
-    let receiver_range = ctx.sema.original_range(receiver_syntax).range;
+    let receiver_range = ctx.sema.original_range_opt(receiver_syntax)?.range;
     let delete_range = TextRange::new(receiver_range.start(), ctx.source_range().end());
 
-    move |label, detail, snippet| {
-        let edit = TextEdit::replace(delete_range, snippet.to_string());
-        let mut item = CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label);
-        item.detail(detail).kind(CompletionItemKind::Snippet).snippet_edit(cap, edit);
-        if ctx.original_token.text() == label {
-            let relevance =
-                CompletionRelevance { exact_postfix_snippet_match: true, ..Default::default() };
-            item.set_relevance(relevance);
-        }
+    // Wrapping impl Fn in an option ruins lifetime inference for the parameters in a way that
+    // can't be annotated for the closure, hence fix it by constructing it without the Option first
+    fn build<'ctx>(
+        ctx: &'ctx CompletionContext,
+        cap: SnippetCap,
+        delete_range: TextRange,
+    ) -> impl Fn(&str, &str, &str) -> Builder + 'ctx {
+        move |label, detail, snippet| {
+            let edit = TextEdit::replace(delete_range, snippet.to_string());
+            let mut item = CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label);
+            item.detail(detail).kind(CompletionItemKind::Snippet).snippet_edit(cap, edit);
+            if ctx.original_token.text() == label {
+                let relevance =
+                    CompletionRelevance { exact_postfix_snippet_match: true, ..Default::default() };
+                item.set_relevance(relevance);
+            }
 
-        item
+            item
+        }
     }
+    Some(build(ctx, cap, delete_range))
 }
 
 fn add_custom_postfix_completions(
diff --git a/crates/ide_completion/src/completions/postfix/format_like.rs b/crates/ide_completion/src/completions/postfix/format_like.rs
index 4fd8d038e75..8098045b1e5 100644
--- a/crates/ide_completion/src/completions/postfix/format_like.rs
+++ b/crates/ide_completion/src/completions/postfix/format_like.rs
@@ -49,7 +49,10 @@ pub(crate) fn add_format_like_completions(
         None => return,
     };
 
-    let postfix_snippet = build_postfix_snippet_builder(ctx, cap, dot_receiver);
+    let postfix_snippet = match build_postfix_snippet_builder(ctx, cap, dot_receiver) {
+        Some(it) => it,
+        None => return,
+    };
     let mut parser = FormatStrParser::new(input);
 
     if parser.parse().is_ok() {