Repository: rust-analyzer/rust-analyzer
Pull request #10384 (2021-09-28 17:18:54)

feat: Add vscode native onEnterRules
This PR copy onEnterRules configurations [from vscode-rust](https://github.com/rust-lang/vscode-rust/blob/master/src/extension.ts#L287) based on discussion in #6254 

I understand that the ideal way is to use parser data for this, and probably all other things that language-config do. But since it can't be enabled (or at least it isn't enabled) by default in vscode, I think this is needed (until onEnter becomes official LSP and get vscode support).

People can still configure shortcut and use the parser based onEnter, so it wouldn't harm anyone.



Patch:
diff --git a/crates/hir_def/src/generics.rs b/crates/hir_def/src/generics.rs
index 8c5313fa45..0921ecea6f 100644
--- a/crates/hir_def/src/generics.rs
+++ b/crates/hir_def/src/generics.rs
@@ -7,15 +7,13 @@ use base_db::FileId;
 use either::Either;
 use hir_expand::{
     name::{AsName, Name},
-    ExpandResult, HirFileId, InFile,
+    HirFileId, InFile,
 };
 use la_arena::{Arena, ArenaMap};
-use once_cell::unsync::Lazy;
-use std::ops::DerefMut;
 use syntax::ast::{self, HasGenericParams, HasName, HasTypeBounds};
 
 use crate::{
-    body::{Expander, LowerCtx},
+    body::LowerCtx,
     child_by_source::ChildBySource,
     db::DefDatabase,
     dyn_map::DynMap,
@@ -23,8 +21,8 @@ use crate::{
     keys,
     src::{HasChildSource, HasSource},
     type_ref::{LifetimeRef, TypeBound, TypeRef},
-    AdtId, ConstParamId, GenericDefId, HasModule, LifetimeParamId, LocalConstParamId,
-    LocalLifetimeParamId, LocalTypeParamId, Lookup, TypeParamId,
+    AdtId, ConstParamId, GenericDefId, LifetimeParamId, LocalConstParamId, LocalLifetimeParamId,
+    LocalTypeParamId, Lookup, TypeParamId,
 };
 
 /// Data about a generic type parameter (to a function, struct, impl, ...).
@@ -101,23 +99,10 @@ impl GenericParams {
 
         match def {
             GenericDefId::FunctionId(id) => {
-                let loc = id.lookup(db);
-                let tree = loc.id.item_tree(db);
-                let item = &tree[loc.id.value];
-
-                let mut generic_params = GenericParams::clone(&item.explicit_generic_params);
-
-                let module = loc.container.module(db);
-                let func_data = db.function_data(id);
-
-                // Don't create an `Expander` nor call `loc.source(db)` if not needed since this
-                // causes a reparse after the `ItemTree` has been created.
-                let mut expander = Lazy::new(|| Expander::new(db, loc.source(db).file_id, module));
-                for param in &func_data.params {
-                    generic_params.fill_implicit_impl_trait_args(db, &mut expander, param);
-                }
-
-                Interned::new(generic_params)
+                let id = id.lookup(db).id;
+                let tree = id.item_tree(db);
+                let item = &tree[id.value];
+                item.generic_params.clone()
             }
             GenericDefId::AdtId(AdtId::StructId(id)) => {
                 let id = id.lookup(db).id;
@@ -274,12 +259,7 @@ impl GenericParams {
         self.where_predicates.push(predicate);
     }
 
-    pub(crate) fn fill_implicit_impl_trait_args(
-        &mut self,
-        db: &dyn DefDatabase,
-        expander: &mut impl DerefMut<Target = Expander>,
-        type_ref: &TypeRef,
-    ) {
+    pub(crate) fn fill_implicit_impl_trait_args(&mut self, type_ref: &TypeRef) {
         type_ref.walk(&mut |type_ref| {
             if let TypeRef::ImplTrait(bounds) = type_ref {
                 let param = TypeParamData {
@@ -295,18 +275,6 @@ impl GenericParams {
                     });
                 }
             }
-            if let TypeRef::Macro(mc) = type_ref {
-                let macro_call = mc.to_node(db.upcast());
-                match expander.enter_expand::<ast::Type>(db, macro_call) {
-                    Ok(ExpandResult { value: Some((mark, expanded)), .. }) => {
-                        let ctx = LowerCtx::new(db, mc.file_id);
-                        let type_ref = TypeRef::from_ast(&ctx, expanded);
-                        self.fill_implicit_impl_trait_args(db, expander, &type_ref);
-                        expander.exit(db, mark);
-                    }
-                    _ => {}
-                }
-            }
         });
     }
 
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index 3900e7e97c..df46ceeaee 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -605,7 +605,7 @@ pub struct ExternBlock {
 pub struct Function {
     pub name: Name,
     pub visibility: RawVisibilityId,
-    pub explicit_generic_params: Interned<GenericParams>,
+    pub generic_params: Interned<GenericParams>,
     pub abi: Option<Interned<str>>,
     pub params: IdRange<Param>,
     pub ret_type: Interned<TypeRef>,
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs
index 9c278f5ac6..6d34556b3e 100644
--- a/crates/hir_def/src/item_tree/lower.rs
+++ b/crates/hir_def/src/item_tree/lower.rs
@@ -401,7 +401,7 @@ impl<'a> Ctx<'a> {
         let mut res = Function {
             name,
             visibility,
-            explicit_generic_params: Interned::new(GenericParams::default()),
+            generic_params: Interned::new(GenericParams::default()),
             abi,
             params,
             ret_type: Interned::new(ret_type),
@@ -409,8 +409,7 @@ impl<'a> Ctx<'a> {
             ast_id,
             flags,
         };
-        res.explicit_generic_params =
-            self.lower_generic_params(GenericsOwner::Function(&res), func);
+        res.generic_params = self.lower_generic_params(GenericsOwner::Function(&res), func);
 
         Some(id(self.data().functions.alloc(res)))
     }
@@ -665,8 +664,16 @@ impl<'a> Ctx<'a> {
     ) -> Interned<GenericParams> {
         let mut generics = GenericParams::default();
         match owner {
-            GenericsOwner::Function(_)
-            | GenericsOwner::Struct
+            GenericsOwner::Function(func) => {
+                generics.fill(&self.body_ctx, node);
+                // lower `impl Trait` in arguments
+                for id in func.params.clone() {
+                    if let Param::Normal(ty) = &self.data().params[id] {
+                        generics.fill_implicit_impl_trait_args(ty);
+                    }
+                }
+            }
+            GenericsOwner::Struct
             | GenericsOwner::Enum
             | GenericsOwner::Union
             | GenericsOwner::TypeAlias => {
diff --git a/crates/hir_def/src/item_tree/pretty.rs b/crates/hir_def/src/item_tree/pretty.rs
index 49dc1eef18..3e33b0c46c 100644
--- a/crates/hir_def/src/item_tree/pretty.rs
+++ b/crates/hir_def/src/item_tree/pretty.rs
@@ -234,7 +234,7 @@ impl<'a> Printer<'a> {
                 let Function {
                     name,
                     visibility,
-                    explicit_generic_params,
+                    generic_params,
                     abi,
                     params,
                     ret_type,
@@ -250,7 +250,7 @@ impl<'a> Printer<'a> {
                     w!(self, "extern \"{}\" ", abi);
                 }
                 w!(self, "fn {}", name);
-                self.print_generic_params(explicit_generic_params);
+                self.print_generic_params(generic_params);
                 w!(self, "(");
                 if !params.is_empty() {
                     self.indented(|this| {
@@ -271,7 +271,7 @@ impl<'a> Printer<'a> {
                 }
                 w!(self, ") -> ");
                 self.print_type_ref(ret_type);
-                self.print_where_clause(explicit_generic_params);
+                self.print_where_clause(generic_params);
                 wln!(self, ";");
             }
             ModItem::Struct(it) => {
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index a595814635..da04eec92b 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -63,6 +63,8 @@ async function tryActivate(context: vscode.ExtensionContext) {
     activateInlayHints(ctx);
     warnAboutExtensionConflicts();
 
+    ctx.pushCleanup(configureLanguage());
+
     vscode.workspace.onDidChangeConfiguration(
         _ => ctx?.client?.sendNotification('workspace/didChangeConfiguration', { settings: "" }),
         null,
@@ -474,3 +476,54 @@ function warnAboutExtensionConflicts() {
             .then(() => { }, console.error);
     };
 }
+
+/**
+ * Sets up additional language configuration that's impossible to do via a
+ * separate language-configuration.json file. See [1] for more information.
+ *
+ * [1]: https://github.com/Microsoft/vscode/issues/11514#issuecomment-244707076
+ */
+function configureLanguage(): vscode.Disposable {
+    const indentAction = vscode.IndentAction.None;
+    return vscode.languages.setLanguageConfiguration('rust', {
+        onEnterRules: [
+            {
+                // Doc single-line comment
+                // e.g. ///|
+                beforeText: /^\s*\/{3}.*$/,
+                action: { indentAction, appendText: '/// ' },
+            },
+            {
+                // Parent doc single-line comment
+                // e.g. //!|
+                beforeText: /^\s*\/{2}\!.*$/,
+                action: { indentAction, appendText: '//! ' },
+            },
+            {
+                // Begins an auto-closed multi-line comment (standard or parent doc)
+                // e.g. /** | */ or /*! | */
+                beforeText: /^\s*\/\*(\*|\!)(?!\/)([^\*]|\*(?!\/))*$/,
+                afterText: /^\s*\*\/$/,
+                action: { indentAction: vscode.IndentAction.IndentOutdent, appendText: ' * ' },
+            },
+            {
+                // Begins a multi-line comment (standard or parent doc)
+                // e.g. /** ...| or /*! ...|
+                beforeText: /^\s*\/\*(\*|\!)(?!\/)([^\*]|\*(?!\/))*$/,
+                action: { indentAction, appendText: ' * ' },
+            },
+            {
+                // Continues a multi-line comment
+                // e.g.  * ...|
+                beforeText: /^(\ \ )*\ \*(\ ([^\*]|\*(?!\/))*)?$/,
+                action: { indentAction, appendText: '* ' },
+            },
+            {
+                // Dedents after closing a multi-line comment
+                // e.g.  */|
+                beforeText: /^(\ \ )*\ \*\/\s*$/,
+                action: { indentAction, removeText: 1 },
+            },
+        ],
+    });
+}


Test patch:
diff --git a/crates/hir_def/src/item_tree/tests.rs b/crates/hir_def/src/item_tree/tests.rs
index 7671ccefd1..a153e17b17 100644
--- a/crates/hir_def/src/item_tree/tests.rs
+++ b/crates/hir_def/src/item_tree/tests.rs
@@ -340,11 +340,12 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
                 T: 'b
             {
                 // flags = 0x2
-                pub(self) fn f<G>(
+                pub(self) fn f<G, _anon_1>(
                     _: impl Copy,
                 ) -> impl Copy
                 where
-                    G: 'a;
+                    G: 'a,
+                    _anon_1: Copy;
             }
 
             pub(self) enum Enum<'a, T, const U: u8> {
diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs
index 2f38d584a1..618499fdc1 100644
--- a/crates/hir_ty/src/tests/regression.rs
+++ b/crates/hir_ty/src/tests/regression.rs
@@ -1177,24 +1177,3 @@ fn multiexp_inner() {
         "#,
     );
 }
-
-#[test]
-fn macro_expands_to_impl_trait() {
-    check_no_mismatches(
-        r#"
-trait Foo {}
-
-macro_rules! ty {
-    () => {
-        impl Foo
-    }
-}
-
-fn foo(_: ty!()) {}
-
-fn bar() {
-    foo(());
-}
-    "#,
-    )
-}