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

Issue:
Landing page for VSCode extension on install
We should open an information tab when a user installs the VSCode extension, showing a bunch of useful features and configurations the user might be interested in. This should help with discoverability for features that we do not want to enable by default.

Came up in https://rust-lang.zulipchat.com/#narrow/stream/122651-general/topic/RA.20appreciation.20thread/near/302443767

Pull request:
feat: Add landing/faq walkthrough pages
This is a basic implementation of a landing and FAQ page; I've included the bare-bones information as well as a [recommended section on inlay hints](https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer/topic/Landing.20Page/near/446135321).

I've also added `rust-analyzer: Open Walkthrough` and `rust-analyzer: Open FAQ` commands for ease of access.

I am hoping to create a small list of FAQ to include that might be useful as well as any other information I may have missed in the landing page. Feel free to share any suggestions!

![landing/faq page demo](https://github.com/rust-lang/rust-analyzer/assets/100006388/4644e4f0-4555-4b29-83c1-b048084ad63d)

cc #13351 

Patch:
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index f3ad4e6869..96431ba4ce 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -835,7 +835,11 @@ impl<'a> InferenceContext<'a> {
         let return_ty = if let Some(rpits) = self.db.return_type_impl_traits(func) {
             // RPIT opaque types use substitution of their parent function.
             let fn_placeholders = TyBuilder::placeholder_subst(self.db, func);
-            let result = self.insert_inference_vars_for_impl_trait(return_ty, fn_placeholders);
+            let result = self.insert_inference_vars_for_impl_trait(
+                return_ty,
+                rpits.clone(),
+                fn_placeholders,
+            );
             let rpits = rpits.skip_binders();
             for (id, _) in rpits.impl_traits.iter() {
                 if let Entry::Vacant(e) = self.result.type_of_rpit.entry(id) {
@@ -858,7 +862,12 @@ impl<'a> InferenceContext<'a> {
         self.insert_atpit_coercion_table(params_and_ret_tys.iter());
     }
 
-    fn insert_inference_vars_for_impl_trait<T>(&mut self, t: T, placeholders: Substitution) -> T
+    fn insert_inference_vars_for_impl_trait<T>(
+        &mut self,
+        t: T,
+        rpits: Arc<chalk_ir::Binders<crate::ImplTraits>>,
+        placeholders: Substitution,
+    ) -> T
     where
         T: crate::HasInterner<Interner = Interner> + crate::TypeFoldable<Interner>,
     {
@@ -869,21 +878,13 @@ impl<'a> InferenceContext<'a> {
                     TyKind::OpaqueType(opaque_ty_id, _) => *opaque_ty_id,
                     _ => return ty,
                 };
-                let (impl_traits, idx) =
-                    match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) {
-                        ImplTraitId::ReturnTypeImplTrait(def, idx) => {
-                            (self.db.return_type_impl_traits(def), idx)
-                        }
-                        ImplTraitId::AssociatedTypeImplTrait(def, idx) => {
-                            (self.db.type_alias_impl_traits(def), idx)
-                        }
-                        _ => unreachable!(),
-                    };
-                let Some(impl_traits) = impl_traits else {
-                    return ty;
+                let idx = match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) {
+                    ImplTraitId::ReturnTypeImplTrait(_, idx) => idx,
+                    ImplTraitId::AssociatedTypeImplTrait(_, idx) => idx,
+                    _ => unreachable!(),
                 };
-                let bounds = (*impl_traits)
-                    .map_ref(|rpits| rpits.impl_traits[idx].bounds.map_ref(|it| it.iter()));
+                let bounds =
+                    (*rpits).map_ref(|rpits| rpits.impl_traits[idx].bounds.map_ref(|it| it.iter()));
                 let var = self.table.new_type_var();
                 let var_subst = Substitution::from1(Interner, var.clone());
                 for bound in bounds {
@@ -891,8 +892,11 @@ impl<'a> InferenceContext<'a> {
                     let (var_predicate, binders) =
                         predicate.substitute(Interner, &var_subst).into_value_and_skipped_binders();
                     always!(binders.is_empty(Interner)); // quantified where clauses not yet handled
-                    let var_predicate = self
-                        .insert_inference_vars_for_impl_trait(var_predicate, placeholders.clone());
+                    let var_predicate = self.insert_inference_vars_for_impl_trait(
+                        var_predicate,
+                        rpits.clone(),
+                        placeholders.clone(),
+                    );
                     self.push_obligation(var_predicate.cast(Interner));
                 }
                 self.result.type_of_rpit.insert(idx, var.clone());
@@ -979,8 +983,16 @@ impl<'a> InferenceContext<'a> {
                     self.db.lookup_intern_impl_trait_id(opaque_ty_id.into())
                 {
                     if assoc_tys.contains(&alias_id) {
+                        let atpits = self
+                            .db
+                            .type_alias_impl_traits(alias_id)
+                            .expect("Marked as ATPIT but no impl traits!");
                         let alias_placeholders = TyBuilder::placeholder_subst(self.db, alias_id);
-                        let ty = self.insert_inference_vars_for_impl_trait(ty, alias_placeholders);
+                        let ty = self.insert_inference_vars_for_impl_trait(
+                            ty,
+                            atpits,
+                            alias_placeholders,
+                        );
                         return Some((opaque_ty_id, ty));
                     }
                 }
diff --git a/editors/code/package.json b/editors/code/package.json
index 0da5b920a7..99759330e6 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -305,6 +305,16 @@
                 "command": "rust-analyzer.toggleLSPLogs",
                 "title": "Toggle LSP Logs",
                 "category": "rust-analyzer"
+            },
+            {
+                "command": "rust-analyzer.openWalkthrough",
+                "title": "Open Walkthrough",
+                "category": "rust-analyzer"
+            },
+            {
+                "command": "rust-analyzer.openFAQ",
+                "title": "Open FAQ",
+                "category": "rust-analyzer"
             }
         ],
         "keybindings": [
@@ -3132,6 +3142,14 @@
                 {
                     "command": "rust-analyzer.toggleLSPLogs",
                     "when": "inRustProject"
+                },
+                {
+                    "command": "rust-analyzer.openWalkthrough",
+                    "when": "inRustProject"
+                },
+                {
+                    "command": "rust-analyzer.openFAQ",
+                    "when": "inRustProject"
                 }
             ],
             "editor/context": [
@@ -3161,6 +3179,94 @@
                 "fileMatch": "rust-project.json",
                 "url": "https://json.schemastore.org/rust-project.json"
             }
+        ],
+        "walkthroughs": [
+            {
+                "id": "landing",
+                "title": "Learn about rust-analyzer",
+                "description": "A brief introduction to get started with rust-analyzer. Learn about key features and resources to help you get the most out of the extension.",
+                "steps": [
+                    {
+                        "id": "docs",
+                        "title": "Visit the docs!",
+                        "description": "Confused about configurations? Want to learn more about rust-analyzer? Visit the [User Manual](https://rust-analyzer.github.io/manual.html)!",
+                        "media": {
+                            "image": "./icon.png",
+                            "altText": "rust-analyzer logo"
+                        },
+                        "completionEvents": [
+                            "onLink:https://rust-analyzer.github.io/manual.html"
+                        ]
+                    },
+                    {
+                        "id": "faq",
+                        "title": "FAQ",
+                        "description": "Have questions about rust-analyzer? Check out the [FAQ Walkthrough](command:rust-analyzer.openFAQ)!",
+                        "media": {
+                            "image": "icon.png",
+                            "altText": "rust-analyzer logo"
+                        }
+                    },
+                    {
+                        "id": "changelog",
+                        "title": "Changelog",
+                        "description": "Stay up-to-date with the latest changes in rust-analyzer. Check out the changelog [here](https://rust-analyzer.github.io/thisweek)!",
+                        "media": {
+                            "image": "icon.png",
+                            "altText": "rust-analyzer logo"
+                        },
+                        "completionEvents": [
+                            "onLink:https://rust-analyzer.github.io/thisweek"
+                        ]
+                    },
+                    {
+                        "id": "revisit",
+                        "title": "Want to revisit a walkthrough?",
+                        "description": "Use the ``Welcome: Open Walkthrough`` command to revisit any walkthrough!",
+                        "media": {
+                            "image": "icon.png",
+                            "altText": "rust-analyzer logo"
+                        }
+                    }
+                ]
+            },
+            {
+                "id": "faq",
+                "title": "FAQ",
+                "description": "Here are some frequently asked questions about rust-analyzer.",
+                "steps": [
+                    {
+                        "id": "faq1",
+                        "title": "What is rust-analyzer?",
+                        "description": "rust-analyzer is a language server for Rust. It provides features like code completion, find references, and more.",
+                        "media": {
+                            "image": "icon.png",
+                            "altText": "rust-analyzer logo"
+                        }
+                    },
+                    {
+                        "id": "faq2",
+                        "title": "Why are all of these type hints showing up in my code?",
+                        "description": "By default, rust-analyzer displays __inlay hints__ to help you understand your code better. You can disable them in your settings.json file with ``\"editor.inlayHints.enabled\": \"off\"``",
+                        "media": {
+                            "image": "icon.png",
+                            "altText": "rust-analyzer logo"
+                        }
+                    },
+                    {
+                        "id": "faq3",
+                        "title": "Where can I find more information about rust-analyzer?",
+                        "description": "You can find more information about rust-analyzer in the [User Manual](https://rust-analyzer.github.io/manual.html).",
+                        "media": {
+                            "image": "icon.png",
+                            "altText": "rust-analyzer logo"
+                        },
+                        "completionEvents": [
+                            "onLink:https://rust-analyzer.github.io/manual.html"
+                        ]
+                    }
+                ]
+            }
         ]
     }
 }
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index f0f9fab1c6..2b0b300106 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -1502,3 +1502,23 @@ export function toggleLSPLogs(ctx: Ctx): Cmd {
         }
     };
 }
+
+export function openWalkthrough(_: Ctx): Cmd {
+    return async () => {
+        await vscode.commands.executeCommand(
+            "workbench.action.openWalkthrough",
+            "rust-lang.rust-analyzer#landing",
+            false,
+        );
+    };
+}
+
+export function openFAQ(_: Ctx): Cmd {
+    return async () => {
+        await vscode.commands.executeCommand(
+            "workbench.action.openWalkthrough",
+            "rust-lang.rust-analyzer#faq",
+            true,
+        );
+    };
+}
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index ff67bb7bd5..c96f2ae869 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -178,6 +178,8 @@ function createCommands(): Record<string, CommandFactory> {
         viewMemoryLayout: { enabled: commands.viewMemoryLayout },
         toggleCheckOnSave: { enabled: commands.toggleCheckOnSave },
         toggleLSPLogs: { enabled: commands.toggleLSPLogs },
+        openWalkthrough: { enabled: commands.openWalkthrough },
+        openFAQ: { enabled: commands.openFAQ },
         // Internal commands which are invoked by the server.
         applyActionGroup: { enabled: commands.applyActionGroup },
         applySnippetWorkspaceEdit: { enabled: commands.applySnippetWorkspaceEditCommand },


Test patch:
diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs
index 4fcb6062b6..3aa94be755 100644
--- a/crates/hir-ty/src/tests/regression.rs
+++ b/crates/hir-ty/src/tests/regression.rs
@@ -1999,22 +1999,3 @@ where
 "#,
     );
 }
-
-#[test]
-fn tait_async_stack_overflow_17199() {
-    check_types(
-        r#"
-    //- minicore: fmt, future
-    type Foo = impl core::fmt::Debug;
-
-    async fn foo() -> Foo {
-        ()
-    }
-
-    async fn test() {
-        let t = foo().await;
-         // ^ impl Debug
-    }
-"#,
-    );
-}