Repository: rust-lang/rust-analyzer (rust)

Issue:
Duplicate insertion of struct's field completion
<!--
Troubleshooting guide: https://rust-analyzer.github.io/manual.html#troubleshooting
Forum for questions: https://users.rust-lang.org/c/ide/14

Before submitting, please make sure that you're not running into one of these known issues:

 1. on-the-fly diagnostics are mostly unimplemented (`cargo check` diagnostics will be shown when saving a file): #3107

Otherwise please try to provide information which will help us to fix the issue faster. Minimal reproducible examples with few dependencies are especially lovely <3.
-->

**rust-analyzer version**: rust-analyzer version: 0.3.1940-standalone (f216be4a0 2024-04-27)

**rustc version**: rustc 1.77.2 (25ef9e3d8 2024-04-09)

**editor or extension**: vscode v0.3.1940

**relevant settings**: none

**repository link (if public, optional)**: none

**code snippet to reproduce**: 

```rust
struct S(i32);
fn main() {
    let s = S(1);
    s.0 // choose 0 in completion
}
```

choosing completion of 0 gets 0 inserted again:


https://github.com/rust-lang/rust-analyzer/assets/71200607/20301f17-c563-48c7-8bb4-b5faa38b0a98

expect behavior: nothing is inserted


<!-- TRIAGEBOT_START -->

<!-- TRIAGEBOT_ASSIGN_START -->

<!-- TRIAGEBOT_ASSIGN_DATA_START$${"user":"roife"}$$TRIAGEBOT_ASSIGN_DATA_END -->

<!-- TRIAGEBOT_ASSIGN_END -->
<!-- TRIAGEBOT_END -->

Pull request:
Fix source_range for INT_NUMBER in completion
fix #17179.

Previously r-a use `TextRange::empty(self.position.offset)` as `source_range` for `INT_NUMBER`, so the `text_edit` would always be an insertion, which results in #17179.

This PR changed it by using `text_range` of `original_token` (same as `IDENT`).

Patch:
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index 04ace38202..f1315f6c81 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -1311,10 +1311,11 @@ impl<'a> TyLoweringContext<'a> {
                 bounds,
                 lifetime: match lifetime {
                     Some(it) => match it.bound_var(Interner) {
-                        Some(bound_var) => bound_var
-                            .shifted_out_to(DebruijnIndex::new(2))
-                            .map(|bound_var| LifetimeData::BoundVar(bound_var).intern(Interner))
-                            .unwrap_or(it),
+                        Some(bound_var) => LifetimeData::BoundVar(BoundVar::new(
+                            DebruijnIndex::INNERMOST,
+                            bound_var.index,
+                        ))
+                        .intern(Interner),
                         None => it,
                     },
                     None => static_lifetime(),
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index 8b435f419c..db34beadc0 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -466,7 +466,7 @@ impl CompletionContext<'_> {
                 cov_mark::hit!(completes_if_lifetime_without_idents);
                 TextRange::at(self.original_token.text_range().start(), TextSize::from(1))
             }
-            IDENT | LIFETIME_IDENT | UNDERSCORE => self.original_token.text_range(),
+            IDENT | LIFETIME_IDENT | UNDERSCORE | INT_NUMBER => self.original_token.text_range(),
             _ if kind.is_keyword() => self.original_token.text_range(),
             _ => TextRange::empty(self.position.offset),
         }
diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs
index 0f13bea7b6..db69b22078 100644
--- a/crates/ide-completion/src/render.rs
+++ b/crates/ide-completion/src/render.rs
@@ -1730,6 +1730,51 @@ fn foo(a: A) { B { bar: a.$0 }; }
         )
     }
 
+    #[test]
+    fn tuple_field_detail() {
+        check(
+            r#"
+struct S(i32);
+
+fn f() -> i32 {
+    let s = S(0);
+    s.0$0
+}
+"#,
+            SymbolKind::Field,
+            expect![[r#"
+                [
+                    CompletionItem {
+                        label: "0",
+                        source_range: 56..57,
+                        delete: 56..57,
+                        insert: "0",
+                        kind: SymbolKind(
+                            Field,
+                        ),
+                        detail: "i32",
+                        relevance: CompletionRelevance {
+                            exact_name_match: false,
+                            type_match: Some(
+                                Exact,
+                            ),
+                            is_local: false,
+                            is_item_from_trait: false,
+                            is_item_from_notable_trait: false,
+                            is_name_already_imported: false,
+                            requires_import: false,
+                            is_op_method: false,
+                            is_private_editable: false,
+                            postfix_match: None,
+                            is_definite: false,
+                            function: None,
+                        },
+                    },
+                ]
+            "#]],
+        );
+    }
+
     #[test]
     fn record_field_and_call_relevances() {
         check_relevance(


Test patch:
diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs
index 18fc8afd18..7a318877b7 100644
--- a/crates/hir-ty/src/tests/traits.rs
+++ b/crates/hir-ty/src/tests/traits.rs
@@ -4803,24 +4803,3 @@ fn foo() {
 "#,
     );
 }
-
-#[test]
-fn dyn_trait_with_lifetime_in_rpit() {
-    check_types(
-        r#"
-//- minicore: future
-pub struct Box<T> {}
-
-trait Trait {}
-
-pub async fn foo_async<'a>() -> Box<dyn Trait + 'a>  {
-    Box {}
-}
-
-fn foo() {
-    foo_async();
-  //^^^^^^^^^^^impl Future<Output = Box<dyn Trait>> + ?Sized
-}
-"#,
-    )
-}