Repository: rust-analyzer/rust-analyzer
Pull request #5189 (2020-07-02 10:43:05)

Record and suggest assoc. items of traits via ImportMap
Fixes https://github.com/rust-analyzer/rust-analyzer/issues/5115


Patch:
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 7f4e753410..ed9191c49c 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -116,9 +116,9 @@ jobs:
     - run: npm ci
       working-directory: ./editors/code
 
-#    - run: npm audit || { sleep 10 && npm audit; } || { sleep 30 && npm audit; }
-#      if: runner.os == 'Linux'
-#      working-directory: ./editors/code
+    - run: npm audit || { sleep 10 && npm audit; } || { sleep 30 && npm audit; }
+      if: runner.os == 'Linux'
+      working-directory: ./editors/code
 
     - run: npm run lint
       working-directory: ./editors/code
diff --git a/Cargo.lock b/Cargo.lock
index 61ae8157a7..e1d2475e22 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1248,7 +1248,6 @@ dependencies = [
  "ra_syntax",
  "ra_text_edit",
  "rustc-hash",
- "test_utils",
 ]
 
 [[package]]
diff --git a/Cargo.toml b/Cargo.toml
index 189f4ea2bd..5278b5a167 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -24,8 +24,6 @@ opt-level = 0
 opt-level = 0
 [profile.release.package.salsa-macros]
 opt-level = 0
-[profile.release.package.tracing-attributes]
-opt-level = 0
 [profile.release.package.xtask]
 opt-level = 0
 
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs
index 7b6499a088..4cd77adbfe 100644
--- a/crates/ra_assists/src/handlers/auto_import.rs
+++ b/crates/ra_assists/src/handlers/auto_import.rs
@@ -810,6 +810,146 @@ fn main() {
         );
     }
 
+    #[test]
+    fn trait_method_cross_crate() {
+        check_assist(
+            auto_import,
+            r"
+            //- /main.rs crate:main deps:dep
+            fn main() {
+                let test_struct = dep::test_mod::TestStruct {};
+                test_struct.test_meth<|>od()
+            }
+            //- /dep.rs crate:dep
+            pub mod test_mod {
+                pub trait TestTrait {
+                    fn test_method(&self);
+                }
+                pub struct TestStruct {}
+                impl TestTrait for TestStruct {
+                    fn test_method(&self) {}
+                }
+            }
+            ",
+            r"
+            use dep::test_mod::TestTrait;
+
+            fn main() {
+                let test_struct = dep::test_mod::TestStruct {};
+                test_struct.test_method()
+            }
+            ",
+        );
+    }
+
+    #[test]
+    fn assoc_fn_cross_crate() {
+        check_assist(
+            auto_import,
+            r"
+            //- /main.rs crate:main deps:dep
+            fn main() {
+                dep::test_mod::TestStruct::test_func<|>tion
+            }
+            //- /dep.rs crate:dep
+            pub mod test_mod {
+                pub trait TestTrait {
+                    fn test_function();
+                }
+                pub struct TestStruct {}
+                impl TestTrait for TestStruct {
+                    fn test_function() {}
+                }
+            }
+            ",
+            r"
+            use dep::test_mod::TestTrait;
+
+            fn main() {
+                dep::test_mod::TestStruct::test_function
+            }
+            ",
+        );
+    }
+
+    #[test]
+    fn assoc_const_cross_crate() {
+        check_assist(
+            auto_import,
+            r"
+            //- /main.rs crate:main deps:dep
+            fn main() {
+                dep::test_mod::TestStruct::CONST<|>
+            }
+            //- /dep.rs crate:dep
+            pub mod test_mod {
+                pub trait TestTrait {
+                    const CONST: bool;
+                }
+                pub struct TestStruct {}
+                impl TestTrait for TestStruct {
+                    const CONST: bool = true;
+                }
+            }
+            ",
+            r"
+            use dep::test_mod::TestTrait;
+
+            fn main() {
+                dep::test_mod::TestStruct::CONST
+            }
+            ",
+        );
+    }
+
+    #[test]
+    fn assoc_fn_as_method_cross_crate() {
+        check_assist_not_applicable(
+            auto_import,
+            r"
+            //- /main.rs crate:main deps:dep
+            fn main() {
+                let test_struct = dep::test_mod::TestStruct {};
+                test_struct.test_func<|>tion()
+            }
+            //- /dep.rs crate:dep
+            pub mod test_mod {
+                pub trait TestTrait {
+                    fn test_function();
+                }
+                pub struct TestStruct {}
+                impl TestTrait for TestStruct {
+                    fn test_function() {}
+                }
+            }
+            ",
+        );
+    }
+
+    #[test]
+    fn private_trait_cross_crate() {
+        check_assist_not_applicable(
+            auto_import,
+            r"
+            //- /main.rs crate:main deps:dep
+            fn main() {
+                let test_struct = dep::test_mod::TestStruct {};
+                test_struct.test_meth<|>od()
+            }
+            //- /dep.rs crate:dep
+            pub mod test_mod {
+                trait TestTrait {
+                    fn test_method(&self);
+                }
+                pub struct TestStruct {}
+                impl TestTrait for TestStruct {
+                    fn test_method(&self) {}
+                }
+            }
+            ",
+        );
+    }
+
     #[test]
     fn not_applicable_for_imported_trait_for_method() {
         check_assist_not_applicable(
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index 1b3525011d..cc72964ffd 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -1189,7 +1189,7 @@ impl Type {
             None => return false,
         };
 
-        let canonical_ty = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
+        let canonical_ty = Canonical { value: self.ty.value.clone(), num_vars: 0 };
         method_resolution::implements_trait(
             &canonical_ty,
             db,
@@ -1213,7 +1213,7 @@ impl Type {
                 self.ty.environment.clone(),
                 hir_ty::Obligation::Trait(trait_ref),
             ),
-            kinds: Arc::new([]),
+            num_vars: 0,
         };
 
         db.trait_solve(self.krate, goal).is_some()
@@ -1288,7 +1288,7 @@ impl Type {
     pub fn autoderef<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Type> + 'a {
         // There should be no inference vars in types passed here
         // FIXME check that?
-        let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
+        let canonical = Canonical { value: self.ty.value.clone(), num_vars: 0 };
         let environment = self.ty.environment.clone();
         let ty = InEnvironment { value: canonical, environment };
         autoderef(db, Some(self.krate), ty)
@@ -1329,7 +1329,7 @@ impl Type {
         // There should be no inference vars in types passed here
         // FIXME check that?
         // FIXME replace Unknown by bound vars here
-        let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
+        let canonical = Canonical { value: self.ty.value.clone(), num_vars: 0 };
 
         let env = self.ty.environment.clone();
         let krate = krate.id;
@@ -1360,7 +1360,7 @@ impl Type {
         // There should be no inference vars in types passed here
         // FIXME check that?
         // FIXME replace Unknown by bound vars here
-        let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
+        let canonical = Canonical { value: self.ty.value.clone(), num_vars: 0 };
 
         let env = self.ty.environment.clone();
         let krate = krate.id;
diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs
index 68e20d06b8..869f3ca5a8 100644
--- a/crates/ra_hir_def/src/import_map.rs
+++ b/crates/ra_hir_def/src/import_map.rs
@@ -5,14 +5,16 @@ use std::{cmp::Ordering, fmt, hash::BuildHasherDefault, sync::Arc};
 use fst::{self, Streamer};
 use indexmap::{map::Entry, IndexMap};
 use ra_db::CrateId;
-use rustc_hash::FxHasher;
+use ra_syntax::SmolStr;
+use rustc_hash::{FxHashMap, FxHasher};
+use smallvec::SmallVec;
 
 use crate::{
     db::DefDatabase,
     item_scope::ItemInNs,
     path::{ModPath, PathKind},
     visibility::Visibility,
-    ModuleDefId, ModuleId,
+    AssocItemId, ModuleDefId, ModuleId, TraitId,
 };
 
 type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
@@ -34,6 +36,7 @@ pub struct ImportInfo {
 ///
 /// Note that all paths are relative to the containing crate's root, so the crate name still needs
 /// to be prepended to the `ModPath` before the path is valid.
+#[derive(Default)]
 pub struct ImportMap {
     map: FxIndexMap<ItemInNs, ImportInfo>,
 
@@ -45,13 +48,17 @@ pub struct ImportMap {
     /// the index of the first one.
     importables: Vec<ItemInNs>,
     fst: fst::Map<Vec<u8>>,
+
+    /// Maps names of associated items to the item's ID. Only includes items whose defining trait is
+    /// exported.
+    assoc_map: FxHashMap<SmolStr, SmallVec<[AssocItemId; 1]>>,
 }
 
 impl ImportMap {
     pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> {
         let _p = ra_prof::profile("import_map_query");
         let def_map = db.crate_def_map(krate);
-        let mut import_map = FxIndexMap::with_capacity_and_hasher(64, Default::default());
+        let mut import_map = Self::default();
 
         // We look only into modules that are public(ly reexported), starting with the crate root.
         let empty = ModPath { kind: PathKind::Plain, segments: vec![] };
@@ -85,7 +92,7 @@ impl ImportMap {
 
                 for item in per_ns.iter_items() {
                     let path = mk_path();
-                    match import_map.entry(item) {
+                    match import_map.map.entry(item) {
                         Entry::Vacant(entry) => {
                             entry.insert(ImportInfo { path, container: module });
                         }
@@ -105,11 +112,16 @@ impl ImportMap {
                     if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() {
                         worklist.push((mod_id, mk_path()));
                     }
+
+                    // If we've added a path to a trait, add the trait's methods to the method map.
+                    if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() {
+                        import_map.collect_trait_methods(db, tr);
+                    }
                 }
             }
         }
 
-        let mut importables = import_map.iter().collect::<Vec<_>>();
+        let mut importables = import_map.map.iter().collect::<Vec<_>>();
 
         importables.sort_by(cmp);
 
@@ -133,10 +145,10 @@ impl ImportMap {
             builder.insert(key, start as u64).unwrap();
         }
 
-        let fst = fst::Map::new(builder.into_inner().unwrap()).unwrap();
-        let importables = importables.iter().map(|(item, _)| **item).collect();
+        import_map.fst = fst::Map::new(builder.into_inner().unwrap()).unwrap();
+        import_map.importables = importables.iter().map(|(item, _)| **item).collect();
 
-        Arc::new(Self { map: import_map, fst, importables })
+        Arc::new(import_map)
     }
 
     /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root.
@@ -147,6 +159,13 @@ impl ImportMap {
     pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> {
         self.map.get(&item)
     }
+
+    fn collect_trait_methods(&mut self, db: &dyn DefDatabase, tr: TraitId) {
+        let data = db.trait_data(tr);
+        for (name, item) in data.items.iter() {
+            self.assoc_map.entry(name.to_string().into()).or_default().push(*item);
+        }
+    }
 }
 
 impl PartialEq for ImportMap {
@@ -290,13 +309,26 @@ pub fn search_dependencies<'a>(
         }
     }
 
+    // Add all exported associated items whose names match the query (exactly).
+    for map in &import_maps {
+        if let Some(v) = map.assoc_map.get(&*query.query) {
+            res.extend(v.iter().map(|&assoc| {
+                ItemInNs::Types(match assoc {
+                    AssocItemId::FunctionId(it) => it.into(),
+                    AssocItemId::ConstId(it) => it.into(),
+                    AssocItemId::TypeAliasId(it) => it.into(),
+                })
+            }));
+        }
+    }
+
     res
 }
 
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::test_db::TestDB;
+    use crate::{test_db::TestDB, AssocContainerId, Lookup};
     use insta::assert_snapshot;
     use itertools::Itertools;
     use ra_db::fixture::WithFixture;
@@ -339,6 +371,7 @@ mod tests {
                     ItemInNs::Values(_) => "v",
                     ItemInNs::Macros(_) => "m",
                 };
+                let item = assoc_to_trait(&db, item);
                 item.krate(db.upcast()).map(|krate| {
                     let map = db.import_map(krate);
                     let path = map.path_of(item).unwrap();
@@ -353,6 +386,29 @@ mod tests {
             .join("\n")
     }
 
+    fn assoc_to_trait(db: &dyn DefDatabase, item: ItemInNs) -> ItemInNs {
+        let assoc: AssocItemId = match item {
+            ItemInNs::Types(it) | ItemInNs::Values(it) => match it {
+                ModuleDefId::TypeAliasId(it) => it.into(),
+                ModuleDefId::FunctionId(it) => it.into(),
+                ModuleDefId::ConstId(it) => it.into(),
+                _ => return item,
+            },
+            _ => return item,
+        };
+
+        let container = match assoc {
+            AssocItemId::FunctionId(it) => it.lookup(db).container,
+            AssocItemId::ConstId(it) => it.lookup(db).container,
+            AssocItemId::TypeAliasId(it) => it.lookup(db).container,
+        };
+
+        match container {
+            AssocContainerId::TraitId(it) => ItemInNs::Types(it.into()),
+            _ => item,
+        }
+    }
+
     #[test]
     fn smoke() {
         let map = import_map(
@@ -610,6 +666,7 @@ mod tests {
         dep::Fmt (m)
         dep::fmt::Display (t)
         dep::format (v)
+        dep::fmt::Display (t)
         "###);
 
         let res = search_dependencies_of(ra_fixture, "main", Query::new("fmt").anchor_end());
@@ -618,6 +675,7 @@ mod tests {
         dep::Fmt (t)
         dep::Fmt (v)
         dep::Fmt (m)
+        dep::fmt::Display (t)
         "###);
     }
 
diff --git a/crates/ra_hir_ty/src/autoderef.rs b/crates/ra_hir_ty/src/autoderef.rs
index c727012c69..1b0f84c5c7 100644
--- a/crates/ra_hir_ty/src/autoderef.rs
+++ b/crates/ra_hir_ty/src/autoderef.rs
@@ -37,7 +37,7 @@ pub(crate) fn deref(
     ty: InEnvironment<&Canonical<Ty>>,
 ) -> Option<Canonical<Ty>> {
     if let Some(derefed) = ty.value.value.builtin_deref() {
-        Some(Canonical { value: derefed, kinds: ty.value.kinds.clone() })
+        Some(Canonical { value: derefed, num_vars: ty.value.num_vars })
     } else {
         deref_by_trait(db, krate, ty)
     }
@@ -68,8 +68,8 @@ fn deref_by_trait(
 
     // Check that the type implements Deref at all
     let trait_ref = TraitRef { trait_: deref_trait, substs: parameters.clone() };
-    let implements_goal = Canonical {
-        kinds: ty.value.kinds.clone(),
+    let implements_goal = super::Canonical {
+        num_vars: ty.value.num_vars,
         value: InEnvironment {
             value: Obligation::Trait(trait_ref),
             environment: ty.environment.clone(),
@@ -81,7 +81,7 @@ fn deref_by_trait(
 
     // Now do the assoc type projection
     let projection = super::traits::ProjectionPredicate {
-        ty: Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, ty.value.kinds.len())),
+        ty: Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, ty.value.num_vars)),
         projection_ty: super::ProjectionTy { associated_ty: target, parameters },
     };
 
@@ -89,8 +89,7 @@ fn deref_by_trait(
 
     let in_env = InEnvironment { value: obligation, environment: ty.environment };
 
-    let canonical =
-        Canonical::new(in_env, ty.value.kinds.iter().copied().chain(Some(super::TyKind::General)));
+    let canonical = super::Canonical { num_vars: 1 + ty.value.num_vars, value: in_env };
 
     let solution = db.trait_solve(krate, canonical)?;
 
@@ -111,7 +110,7 @@ fn deref_by_trait(
             // assumptions will be broken. We would need to properly introduce
             // new variables in that case
 
-            for i in 1..vars.0.kinds.len() {
+            for i in 1..vars.0.num_vars {
                 if vars.0.value[i - 1] != Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, i - 1))
                 {
                     warn!("complex solution for derefing {:?}: {:?}, ignoring", ty.value, solution);
@@ -120,7 +119,7 @@ fn deref_by_trait(
             }
             Some(Canonical {
                 value: vars.0.value[vars.0.value.len() - 1].clone(),
-                kinds: vars.0.kinds.clone(),
+                num_vars: vars.0.num_vars,
             })
         }
         Solution::Ambig(_) => {
diff --git a/crates/ra_hir_ty/src/infer/unify.rs b/crates/ra_hir_ty/src/infer/unify.rs
index 2e895d9116..269495ca0b 100644
--- a/crates/ra_hir_ty/src/infer/unify.rs
+++ b/crates/ra_hir_ty/src/infer/unify.rs
@@ -9,7 +9,7 @@ use test_utils::mark;
 use super::{InferenceContext, Obligation};
 use crate::{
     BoundVar, Canonical, DebruijnIndex, GenericPredicate, InEnvironment, InferTy, Substs, Ty,
-    TyKind, TypeCtor, TypeWalk,
+    TypeCtor, TypeWalk,
 };
 
 impl<'a> InferenceContext<'a> {
@@ -86,20 +86,10 @@ where
     }
 
     fn into_canonicalized<T>(self, result: T) -> Canonicalized<T> {
-        let kinds = self
-            .free_vars
-            .iter()
-            .map(|v| match v {
-                // mapping MaybeNeverTypeVar to the same kind as general ones
-                // should be fine, because as opposed to int or float type vars,
-                // they don't restrict what kind of type can go into them, they
-                // just affect fallback.
-                InferTy::TypeVar(_) | InferTy::MaybeNeverTypeVar(_) => TyKind::General,
-                InferTy::IntVar(_) => TyKind::Integer,
-                InferTy::FloatVar(_) => TyKind::Float,
-            })
-            .collect();
-        Canonicalized { value: Canonical { value: result, kinds }, free_vars: self.free_vars }
+        Canonicalized {
+            value: Canonical { value: result, num_vars: self.free_vars.len() },
+            free_vars: self.free_vars,
+        }
     }
 
     pub(crate) fn canonicalize_ty(mut self, ty: Ty) -> Canonicalized<Ty> {
@@ -141,41 +131,26 @@ impl<T> Canonicalized<T> {
         ty
     }
 
-    pub fn apply_solution(&self, ctx: &mut InferenceContext<'_>, solution: Canonical<Substs>) {
+    pub fn apply_solution(&self, ctx: &mut InferenceContext<'_>, solution: Canonical<Vec<Ty>>) {
         // the solution may contain new variables, which we need to convert to new inference vars
-        let new_vars = Substs(
-            solution
-                .kinds
-                .iter()
-                .map(|k| match k {
-                    TyKind::General => ctx.table.new_type_var(),
-                    TyKind::Integer => ctx.table.new_integer_var(),
-                    TyKind::Float => ctx.table.new_float_var(),
-                })
-                .collect(),
-        );
+        let new_vars = Substs((0..solution.num_vars).map(|_| ctx.table.new_type_var()).collect());
         for (i, ty) in solution.value.into_iter().enumerate() {
             let var = self.free_vars[i];
             // eagerly replace projections in the type; we may be getting types
             // e.g. from where clauses where this hasn't happened yet
-            let ty = ctx.normalize_associated_types_in(ty.clone().subst_bound_vars(&new_vars));
+            let ty = ctx.normalize_associated_types_in(ty.subst_bound_vars(&new_vars));
             ctx.table.unify(&Ty::Infer(var), &ty);
         }
     }
 }
 
-pub fn unify(tys: &Canonical<(Ty, Ty)>) -> Option<Substs> {
+pub fn unify(ty1: &Canonical<Ty>, ty2: &Canonical<Ty>) -> Option<Substs> {
     let mut table = InferenceTable::new();
-    let vars = Substs(
-        tys.kinds
-            .iter()
-            // we always use type vars here because we want everything to
-            // fallback to Unknown in the end (kind of hacky, as below)
-            .map(|_| table.new_type_var())
-            .collect(),
-    );
-    let ty1_with_vars = tys.value.0.clone().subst_bound_vars(&vars);
-    let ty2_with_vars = tys.value.1.clone().subst_bound_vars(&vars);
+    let num_vars = ty1.num_vars.max(ty2.num_vars);
+    let vars =
+        Substs::builder(num_vars).fill(std::iter::repeat_with(|| table.new_type_var())).build();
+    let ty1_with_vars = ty1.value.clone().subst_bound_vars(&vars);
+    let ty2_with_vars = ty2.value.clone().subst_bound_vars(&vars);
     if !table.unify(&ty1_with_vars, &ty2_with_vars) {
         return None;
     }
@@ -187,7 +162,7 @@ pub fn unify(tys: &Canonical<(Ty, Ty)>) -> Option<Substs> {
         }
     }
     Some(
-        Substs::builder(tys.kinds.len())
+        Substs::builder(ty1.num_vars)
             .fill(vars.iter().map(|v| table.resolve_ty_completely(v.clone())))
             .build(),
     )
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs
index 7f3f5e7715..c9513b752a 100644
--- a/crates/ra_hir_ty/src/lib.rs
+++ b/crates/ra_hir_ty/src/lib.rs
@@ -662,27 +662,13 @@ impl TypeWalk for GenericPredicate {
 
 /// Basically a claim (currently not validated / checked) that the contained
 /// type / trait ref contains no inference variables; any inference variables it
-/// contained have been replaced by bound variables, and `kinds` tells us how
-/// many there are and whether they were normal or float/int variables. This is
-/// used to erase irrelevant differences between types before using them in
-/// queries.
+/// contained have been replaced by bound variables, and `num_vars` tells us how
+/// many there are. This is used to erase irrelevant differences between types
+/// before using them in queries.
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Canonical<T> {
     pub value: T,
-    pub kinds: Arc<[TyKind]>,
-}
-
-impl<T> Canonical<T> {
-    pub fn new(value: T, kinds: impl IntoIterator<Item = TyKind>) -> Self {
-        Self { value, kinds: kinds.into_iter().collect() }
-    }
-}
-
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
-pub enum TyKind {
-    General,
-    Integer,
-    Float,
+    pub num_vars: usize,
 }
 
 /// A function signature as seen by type inference: Several parameter types and
diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs
index a45febbf74..5dbabd12b3 100644
--- a/crates/ra_hir_ty/src/method_resolution.rs
+++ b/crates/ra_hir_ty/src/method_resolution.rs
@@ -2,7 +2,7 @@
 //! For details about how this works in rustc, see the method lookup page in the
 //! [rustc guide](https://rust-lang.github.io/rustc-guide/method-lookup.html)
 //! and the corresponding code mostly in librustc_typeck/check/method/probe.rs.
-use std::{iter, sync::Arc};
+use std::sync::Arc;
 
 use arrayvec::ArrayVec;
 use hir_def::{
@@ -17,8 +17,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
 use super::Substs;
 use crate::{
     autoderef, db::HirDatabase, primitive::FloatBitness, utils::all_super_traits, ApplicationTy,
-    Canonical, DebruijnIndex, InEnvironment, TraitEnvironment, TraitRef, Ty, TyKind, TypeCtor,
-    TypeWalk,
+    Canonical, DebruijnIndex, InEnvironment, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk,
 };
 
 /// This is used as a key for indexing impls.
@@ -373,7 +372,7 @@ fn iterate_method_candidates_with_autoref(
         return true;
     }
     let refed = Canonical {
-        kinds: deref_chain[0].kinds.clone(),
+        num_vars: deref_chain[0].num_vars,
         value: Ty::apply_one(TypeCtor::Ref(Mutability::Shared), deref_chain[0].value.clone()),
     };
     if iterate_method_candidates_by_receiver(
@@ -389,7 +388,7 @@ fn iterate_method_candidates_with_autoref(
         return true;
     }
     let ref_muted = Canonical {
-        kinds: deref_chain[0].kinds.clone(),
+        num_vars: deref_chain[0].num_vars,
         value: Ty::apply_one(TypeCtor::Ref(Mutability::Mut), deref_chain[0].value.clone()),
     };
     if iterate_method_candidates_by_receiver(
@@ -608,19 +607,18 @@ pub(crate) fn inherent_impl_substs(
     // we create a var for each type parameter of the impl; we need to keep in
     // mind here that `self_ty` might have vars of its own
     let vars = Substs::build_for_def(db, impl_id)
-        .fill_with_bound_vars(DebruijnIndex::INNERMOST, self_ty.kinds.len())
+        .fill_with_bound_vars(DebruijnIndex::INNERMOST, self_ty.num_vars)
         .build();
     let self_ty_with_vars = db.impl_self_ty(impl_id).subst(&vars);
-    let mut kinds = self_ty.kinds.to_vec();
-    kinds.extend(iter::repeat(TyKind::General).take(vars.len()));
-    let tys = Canonical { kinds: kinds.into(), value: (self_ty_with_vars, self_ty.value.clone()) };
-    let substs = super::infer::unify(&tys);
+    let self_ty_with_vars =
+        Canonical { num_vars: vars.len() + self_ty.num_vars, value: self_ty_with_vars };
+    let substs = super::infer::unify(&self_ty_with_vars, self_ty);
     // We only want the substs for the vars we added, not the ones from self_ty.
     // Also, if any of the vars we added are still in there, we replace them by
     // Unknown. I think this can only really happen if self_ty contained
     // Unknown, and in that case we want the result to contain Unknown in those
     // places again.
-    substs.map(|s| fallback_bound_vars(s.suffix(vars.len()), self_ty.kinds.len()))
+    substs.map(|s| fallback_bound_vars(s.suffix(vars.len()), self_ty.num_vars))
 }
 
 /// This replaces any 'free' Bound vars in `s` (i.e. those with indices past
@@ -680,15 +678,15 @@ fn generic_implements_goal(
     trait_: TraitId,
     self_ty: Canonical<Ty>,
 ) -> Canonical<InEnvironment<super::Obligation>> {
-    let mut kinds = self_ty.kinds.to_vec();
+    let num_vars = self_ty.num_vars;
     let substs = super::Substs::build_for_def(db, trait_)
         .push(self_ty.value)
-        .fill_with_bound_vars(DebruijnIndex::INNERMOST, kinds.len())
+        .fill_with_bound_vars(DebruijnIndex::INNERMOST, num_vars)
         .build();
-    kinds.extend(iter::repeat(TyKind::General).take(substs.len() - 1));
+    let num_vars = substs.len() - 1 + self_ty.num_vars;
     let trait_ref = TraitRef { trait_, substs };
     let obligation = super::Obligation::Trait(trait_ref);
-    Canonical { kinds: kinds.into(), value: InEnvironment::new(env, obligation) }
+    Canonical { num_vars, value: InEnvironment::new(env, obligation) }
 }
 
 fn autoderef_method_receiver(
@@ -701,9 +699,9 @@ fn autoderef_method_receiver(
     if let Some(Ty::Apply(ApplicationTy { ctor: TypeCtor::Array, parameters })) =
         deref_chain.last().map(|ty| &ty.value)
     {
-        let kinds = deref_chain.last().unwrap().kinds.clone();
+        let num_vars = deref_chain.last().unwrap().num_vars;
         let unsized_ty = Ty::apply(TypeCtor::Slice, parameters.clone());
-        deref_chain.push(Canonical { value: unsized_ty, kinds })
+        deref_chain.push(Canonical { value: unsized_ty, num_vars })
     }
     deref_chain
 }
diff --git a/crates/ra_hir_ty/src/traits.rs b/crates/ra_hir_ty/src/traits.rs
index 2a6d7faefe..6f43c3a22e 100644
--- a/crates/ra_hir_ty/src/traits.rs
+++ b/crates/ra_hir_ty/src/traits.rs
@@ -1,5 +1,5 @@
 //! Trait solving using Chalk.
-use std::sync::Arc;
+use std::{panic, sync::Arc};
 
 use chalk_ir::cast::Cast;
 use hir_def::{
@@ -8,7 +8,7 @@ use hir_def::{
 use ra_db::{impl_intern_key, salsa, CrateId};
 use ra_prof::profile;
 
-use crate::{db::HirDatabase, DebruijnIndex, Substs};
+use crate::{db::HirDatabase, DebruijnIndex};
 
 use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk};
 
@@ -190,7 +190,15 @@ fn solution_from_chalk(
     solution: chalk_solve::Solution<Interner>,
 ) -> Solution {
     let convert_subst = |subst: chalk_ir::Canonical<chalk_ir::Substitution<Interner>>| {
-        let result = from_chalk(db, subst);
+        let value = subst
+            .value
+            .iter(&Interner)
+            .map(|p| match p.ty(&Interner) {
+                Some(ty) => from_chalk(db, ty.clone()),
+                None => unimplemented!(),
+            })
+            .collect();
+        let result = Canonical { value, num_vars: subst.binders.len(&Interner) };
         SolutionVariables(result)
     };
     match solution {
@@ -214,7 +222,7 @@ fn solution_from_chalk(
 }
 
 #[derive(Clone, Debug, PartialEq, Eq)]
-pub struct SolutionVariables(pub Canonical<Substs>);
+pub struct SolutionVariables(pub Canonical<Vec<Ty>>);
 
 #[derive(Clone, Debug, PartialEq, Eq)]
 /// A (possible) solution for a proposed goal.
diff --git a/crates/ra_hir_ty/src/traits/chalk/mapping.rs b/crates/ra_hir_ty/src/traits/chalk/mapping.rs
index 433d6aa03d..ac82ea8310 100644
--- a/crates/ra_hir_ty/src/traits/chalk/mapping.rs
+++ b/crates/ra_hir_ty/src/traits/chalk/mapping.rs
@@ -17,7 +17,7 @@ use crate::{
     primitive::{FloatBitness, FloatTy, IntBitness, IntTy, Signedness},
     traits::{builtin, AssocTyValue, Canonical, Impl, Obligation},
     ApplicationTy, CallableDef, GenericPredicate, InEnvironment, OpaqueTy, OpaqueTyId,
-    ProjectionPredicate, ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TyKind, TypeCtor,
+    ProjectionPredicate, ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor,
 };
 
 use super::interner::*;
@@ -555,39 +555,22 @@ where
     type Chalk = chalk_ir::Canonical<T::Chalk>;
 
     fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Canonical<T::Chalk> {
-        let kinds = self
-            .kinds
-            .iter()
-            .map(|k| match k {
-                TyKind::General => chalk_ir::TyKind::General,
-                TyKind::Integer => chalk_ir::TyKind::Integer,
-                TyKind::Float => chalk_ir::TyKind::Float,
-            })
-            .map(|tk| {
-                chalk_ir::CanonicalVarKind::new(
-                    chalk_ir::VariableKind::Ty(tk),
-                    chalk_ir::UniverseIndex::ROOT,
-                )
-            });
+        let parameter = chalk_ir::CanonicalVarKind::new(
+            chalk_ir::VariableKind::Ty(chalk_ir::TyKind::General),
+            chalk_ir::UniverseIndex::ROOT,
+        );
         let value = self.value.to_chalk(db);
-        chalk_ir::Canonical { value, binders: chalk_ir::CanonicalVarKinds::from(&Interner, kinds) }
+        chalk_ir::Canonical {
+            value,
+            binders: chalk_ir::CanonicalVarKinds::from(&Interner, vec![parameter; self.num_vars]),
+        }
     }
 
     fn from_chalk(db: &dyn HirDatabase, canonical: chalk_ir::Canonical<T::Chalk>) -> Canonical<T> {
-        let kinds = canonical
-            .binders
-            .iter(&Interner)
-            .map(|k| match k.kind {
-                chalk_ir::VariableKind::Ty(tk) => match tk {
-                    chalk_ir::TyKind::General => TyKind::General,
-                    chalk_ir::TyKind::Integer => TyKind::Integer,
-                    chalk_ir::TyKind::Float => TyKind::Float,
-                },
-                chalk_ir::VariableKind::Lifetime => panic!("unexpected lifetime from Chalk"),
-                chalk_ir::VariableKind::Const(_) => panic!("unexpected const from Chalk"),
-            })
-            .collect();
-        Canonical { kinds, value: from_chalk(db, canonical.value) }
+        Canonical {
+            num_vars: canonical.binders.len(&Interner),
+            value: from_chalk(db, canonical.value),
+        }
     }
 }
 
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
index 9ebb8ebb7c..69ea754b3d 100644
--- a/crates/ra_ide/src/completion.rs
+++ b/crates/ra_ide/src/completion.rs
@@ -63,11 +63,11 @@ pub use crate::completion::{
 // There also snippet completions:
 //
 // .Expressions
-// - `pd` -> `eprintln!(" = {:?}", );")`
-// - `ppd` -> `eprintln!(" = {:#?}", );`
+// - `pd` -> `println!("{:?}")`
+// - `ppd` -> `println!("{:#?}")`
 //
 // .Items
-// - `tfn` -> `#[test] fn feature(){}`
+// - `tfn` -> `#[test] fn f(){}`
 // - `tmod` ->
 // ```rust
 // #[cfg(test)]
@@ -75,7 +75,7 @@ pub use crate::completion::{
 //     use super::*;
 //
 //     #[test]
-//     fn test_name() {}
+//     fn test_fn() {}
 // }
 // ```
 
diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs
index e599cc3d16..3b174f9161 100644
--- a/crates/ra_ide/src/completion/complete_keyword.rs
+++ b/crates/ra_ide/src/completion/complete_keyword.rs
@@ -1,6 +1,6 @@
 //! FIXME: write short doc here
 
-use ra_syntax::{ast, SyntaxKind};
+use ra_syntax::ast;
 
 use crate::completion::{
     CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
@@ -37,10 +37,6 @@ pub(super) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
 }
 
 pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
-    if ctx.token.kind() == SyntaxKind::COMMENT {
-        return;
-    }
-
     let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent;
     if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling {
         add_keyword(ctx, acc, "where", "where ");
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index b18279746e..4fdc2f0bbc 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -1516,54 +1516,4 @@ mod tests {
         "###
         );
     }
-
-    #[test]
-    fn no_keyword_autocompletion_on_line_comments() {
-        assert_debug_snapshot!(
-        do_completion(
-                r"
-                    fn test() {
-                        let x = 2; // A comment<|>
-                    }
-                    ",
-                CompletionKind::Keyword
-        ),
-            @r###"
-            []
-            "###
-        );
-    }
-
-    #[test]
-    fn no_keyword_autocompletion_on_multi_line_comments() {
-        assert_debug_snapshot!(
-        do_completion(
-                r"
-                    /*
-                    Some multi-line comment<|>
-                    */
-                    ",
-                CompletionKind::Keyword
-        ),
-            @r###"
-            []
-            "###
-        );
-    }
-
-    #[test]
-    fn no_keyword_autocompletion_on_doc_comments() {
-        assert_debug_snapshot!(
-        do_completion(
-                r"
-                    /// Some doc comment
-                    /// let test<|> = 1
-                    ",
-                CompletionKind::Keyword
-        ),
-            @r###"
-            []
-            "###
-        );
-    }
 }
diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs
index b3e9e5dfe1..9f8e540c02 100644
--- a/crates/ra_ide/src/ssr.rs
+++ b/crates/ra_ide/src/ssr.rs
@@ -10,18 +10,6 @@ use ra_ssr::{MatchFinder, SsrError, SsrRule};
 // The syntax for a structural search replace command is `<search_pattern> ==>> <replace_pattern>`.
 // A `$<name>` placeholder in the search pattern will match any AST node and `$<name>` will reference it in the replacement.
 // Within a macro call, a placeholder will match up until whatever token follows the placeholder.
-//
-// Placeholders may be given constraints by writing them as `${<name>:<constraint1>:<constraint2>...}`.
-//
-// Supported constraints:
-//
-// |===
-// | Constraint    | Restricts placeholder
-//
-// | kind(literal) | Is a literal (e.g. `42` or `"forty two"`)
-// | not(a)        | Negates the constraint `a`
-// |===
-//
 // Available via the command `rust-analyzer.ssr`.
 //
 // ```rust
diff --git a/crates/ra_ssr/Cargo.toml b/crates/ra_ssr/Cargo.toml
index fe098aaee3..3c2f15a833 100644
--- a/crates/ra_ssr/Cargo.toml
+++ b/crates/ra_ssr/Cargo.toml
@@ -17,4 +17,3 @@ ra_db = { path = "../ra_db" }
 ra_ide_db = { path = "../ra_ide_db" }
 hir = { path = "../ra_hir", package = "ra_hir" }
 rustc-hash = "1.1.0"
-test_utils = { path = "../test_utils" }
diff --git a/crates/ra_ssr/src/matching.rs b/crates/ra_ssr/src/matching.rs
index ce53d46d29..53d802e775 100644
--- a/crates/ra_ssr/src/matching.rs
+++ b/crates/ra_ssr/src/matching.rs
@@ -2,7 +2,7 @@
 //! process of matching, placeholder values are recorded.
 
 use crate::{
-    parsing::{Constraint, NodeKind, Placeholder, SsrTemplate},
+    parsing::{Placeholder, SsrTemplate},
     SsrMatches, SsrPattern, SsrRule,
 };
 use hir::Semantics;
@@ -11,7 +11,6 @@ use ra_syntax::ast::{AstNode, AstToken};
 use ra_syntax::{ast, SyntaxElement, SyntaxElementChildren, SyntaxKind, SyntaxNode, SyntaxToken};
 use rustc_hash::FxHashMap;
 use std::{cell::Cell, iter::Peekable};
-use test_utils::mark;
 
 // Creates a match error. If we're currently attempting to match some code that we thought we were
 // going to match, as indicated by the --debug-snippet flag, then populate the reason field.
@@ -170,9 +169,6 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
         if let Some(placeholder) =
             match_inputs.get_placeholder(&SyntaxElement::Node(pattern.clone()))
         {
-            for constraint in &placeholder.constraints {
-                self.check_constraint(constraint, code)?;
-            }
             if self.match_out.is_none() {
                 return Ok(());
             }
@@ -296,24 +292,6 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
         Ok(())
     }
 
-    fn check_constraint(
-        &self,
-        constraint: &Constraint,
-        code: &SyntaxNode,
-    ) -> Result<(), MatchFailed> {
-        match constraint {
-            Constraint::Kind(kind) => {
-                kind.matches(code)?;
-            }
-            Constraint::Not(sub) => {
-                if self.check_constraint(&*sub, code).is_ok() {
-                    fail_match!("Constraint {:?} failed for '{}'", constraint, code.text());
-                }
-            }
-        }
-        Ok(())
-    }
-
     /// We want to allow the records to match in any order, so we have special matching logic for
     /// them.
     fn attempt_match_record_field_list(
@@ -537,21 +515,6 @@ impl SsrPattern {
     }
 }
 
-impl NodeKind {
-    fn matches(&self, node: &SyntaxNode) -> Result<(), MatchFailed> {
-        let ok = match self {
-            Self::Literal => {
-                mark::hit!(literal_constraint);
-                ast::Literal::can_cast(node.kind())
-            }
-        };
-        if !ok {
-            fail_match!("Code '{}' isn't of kind {:?}", node.text(), self);
-        }
-        Ok(())
-    }
-}
-
 // If `node` contains nothing but an ident then return it, otherwise return None.
 fn only_ident(element: SyntaxElement) -> Option<SyntaxToken> {
     match element {
diff --git a/crates/ra_ssr/src/parsing.rs b/crates/ra_ssr/src/parsing.rs
index 5ea1256165..04d46bd326 100644
--- a/crates/ra_ssr/src/parsing.rs
+++ b/crates/ra_ssr/src/parsing.rs
@@ -6,7 +6,7 @@
 //! e.g. expressions, type references etc.
 
 use crate::{SsrError, SsrPattern, SsrRule};
-use ra_syntax::{ast, AstNode, SmolStr, SyntaxKind, T};
+use ra_syntax::{ast, AstNode, SmolStr, SyntaxKind};
 use rustc_hash::{FxHashMap, FxHashSet};
 use std::str::FromStr;
 
@@ -39,18 +39,6 @@ pub(crate) struct Placeholder {
     pub(crate) ident: SmolStr,
     /// A unique name used in place of this placeholder when we parse the pattern as Rust code.
     stand_in_name: String,
-    pub(crate) constraints: Vec<Constraint>,
-}
-
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub(crate) enum Constraint {
-    Kind(NodeKind),
-    Not(Box<Constraint>),
-}
-
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub(crate) enum NodeKind {
-    Literal,
 }
 
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -161,7 +149,7 @@ fn parse_pattern(pattern_str: &str) -> Result<Vec<PatternElement>, SsrError> {
     let mut placeholder_names = FxHashSet::default();
     let mut tokens = tokenize(pattern_str)?.into_iter();
     while let Some(token) = tokens.next() {
-        if token.kind == T![$] {
+        if token.kind == SyntaxKind::DOLLAR {
             let placeholder = parse_placeholder(&mut tokens)?;
             if !placeholder_names.insert(placeholder.ident.clone()) {
                 bail!("Name `{}` repeats more than once", placeholder.ident);
@@ -189,9 +177,6 @@ fn validate_rule(rule: &SsrRule) -> Result<(), SsrError> {
             if !defined_placeholders.contains(&placeholder.ident) {
                 undefined.push(format!("${}", placeholder.ident));
             }
-            if !placeholder.constraints.is_empty() {
-                bail!("Replacement placeholders cannot have constraints");
-            }
         }
     }
     if !undefined.is_empty() {
@@ -220,90 +205,23 @@ fn tokenize(source: &str) -> Result<Vec<Token>, SsrError> {
 
 fn parse_placeholder(tokens: &mut std::vec::IntoIter<Token>) -> Result<Placeholder, SsrError> {
     let mut name = None;
-    let mut constraints = Vec::new();
     if let Some(token) = tokens.next() {
         match token.kind {
             SyntaxKind::IDENT => {
                 name = Some(token.text);
             }
-            T!['{'] => {
-                let token =
-                    tokens.next().ok_or_else(|| SsrError::new("Unexpected end of placeholder"))?;
-                if token.kind == SyntaxKind::IDENT {
-                    name = Some(token.text);
-                }
-                loop {
-                    let token = tokens
-                        .next()
-                        .ok_or_else(|| SsrError::new("Placeholder is missing closing brace '}'"))?;
-                    match token.kind {
-                        T![:] => {
-                            constraints.push(parse_constraint(tokens)?);
-                        }
-                        T!['}'] => break,
-                        _ => bail!("Unexpected token while parsing placeholder: '{}'", token.text),
-                    }
-                }
-            }
             _ => {
-                bail!("Placeholders should either be $name or ${name:constraints}");
+                bail!("Placeholders should be $name");
             }
         }
     }
     let name = name.ok_or_else(|| SsrError::new("Placeholder ($) with no name"))?;
-    Ok(Placeholder::new(name, constraints))
-}
-
-fn parse_constraint(tokens: &mut std::vec::IntoIter<Token>) -> Result<Constraint, SsrError> {
-    let constraint_type = tokens
-        .next()
-        .ok_or_else(|| SsrError::new("Found end of placeholder while looking for a constraint"))?
-        .text
-        .to_string();
-    match constraint_type.as_str() {
-        "kind" => {
-            expect_token(tokens, "(")?;
-            let t = tokens.next().ok_or_else(|| {
-                SsrError::new("Unexpected end of constraint while looking for kind")
-            })?;
-            if t.kind != SyntaxKind::IDENT {
-                bail!("Expected ident, found {:?} while parsing kind constraint", t.kind);
-            }
-            expect_token(tokens, ")")?;
-            Ok(Constraint::Kind(NodeKind::from(&t.text)?))
-        }
-        "not" => {
-            expect_token(tokens, "(")?;
-            let sub = parse_constraint(tokens)?;
-            expect_token(tokens, ")")?;
-            Ok(Constraint::Not(Box::new(sub)))
-        }
-        x => bail!("Unsupported constraint type '{}'", x),
-    }
-}
-
-fn expect_token(tokens: &mut std::vec::IntoIter<Token>, expected: &str) -> Result<(), SsrError> {
-    if let Some(t) = tokens.next() {
-        if t.text == expected {
-            return Ok(());
-        }
-        bail!("Expected {} found {}", expected, t.text);
-    }
-    bail!("Expected {} found end of stream");
-}
-
-impl NodeKind {
-    fn from(name: &SmolStr) -> Result<NodeKind, SsrError> {
-        Ok(match name.as_str() {
-            "literal" => NodeKind::Literal,
-            _ => bail!("Unknown node kind '{}'", name),
-        })
-    }
+    Ok(Placeholder::new(name))
 }
 
 impl Placeholder {
-    fn new(name: SmolStr, constraints: Vec<Constraint>) -> Self {
-        Self { stand_in_name: format!("__placeholder_{}", name), constraints, ident: name }
+    fn new(name: SmolStr) -> Self {
+        Self { stand_in_name: format!("__placeholder_{}", name), ident: name }
     }
 }
 
@@ -323,31 +241,31 @@ mod tests {
             PatternElement::Token(Token { kind, text: SmolStr::new(text) })
         }
         fn placeholder(name: &str) -> PatternElement {
-            PatternElement::Placeholder(Placeholder::new(SmolStr::new(name), Vec::new()))
+            PatternElement::Placeholder(Placeholder::new(SmolStr::new(name)))
         }
         let result: SsrRule = "foo($a, $b) ==>> bar($b, $a)".parse().unwrap();
         assert_eq!(
             result.pattern.raw.tokens,
             vec![
                 token(SyntaxKind::IDENT, "foo"),
-                token(T!['('], "("),
+                token(SyntaxKind::L_PAREN, "("),
                 placeholder("a"),
-                token(T![,], ","),
+                token(SyntaxKind::COMMA, ","),
                 token(SyntaxKind::WHITESPACE, " "),
                 placeholder("b"),
-                token(T![')'], ")"),
+                token(SyntaxKind::R_PAREN, ")"),
             ]
         );
         assert_eq!(
             result.template.tokens,
             vec![
                 token(SyntaxKind::IDENT, "bar"),
-                token(T!['('], "("),
+                token(SyntaxKind::L_PAREN, "("),
                 placeholder("b"),
-                token(T![,], ","),
+                token(SyntaxKind::COMMA, ","),
                 token(SyntaxKind::WHITESPACE, " "),
                 placeholder("a"),
-                token(T![')'], ")"),
+                token(SyntaxKind::R_PAREN, ")"),
             ]
         );
     }
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index a1521a93be..2982b9cbf6 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -19,16 +19,6 @@ let ctx: Ctx | undefined;
 const RUST_PROJECT_CONTEXT_NAME = "inRustProject";
 
 export async function activate(context: vscode.ExtensionContext) {
-    // For some reason vscode not always shows pop-up error notifications
-    // when an extension fails to activate, so we do it explicitly by ourselves.
-    // FIXME: remove this bit of code once vscode fixes this issue: https://github.com/microsoft/vscode/issues/101242
-    await tryActivate(context).catch(err => {
-        void vscode.window.showErrorMessage(`Cannot activate rust-analyzer: ${err.message}`);
-        throw err;
-    });
-}
-
-async function tryActivate(context: vscode.ExtensionContext) {
     // Register a "dumb" onEnter command for the case where server fails to
     // start.
     //
@@ -68,7 +58,9 @@ async function tryActivate(context: vscode.ExtensionContext) {
 
     const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
     if (workspaceFolder === undefined) {
-        throw new Error("no folder is opened");
+        const err = "Cannot activate rust-analyzer when no folder is opened";
+        void vscode.window.showErrorMessage(err);
+        throw new Error(err);
     }
 
     // Note: we try to start the server before we activate type hints so that it
@@ -160,17 +152,13 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi
         return;
     };
 
+    const lastCheck = state.lastCheck;
     const now = Date.now();
-    if (config.package.releaseTag === NIGHTLY_TAG) {
-        // Check if we should poll github api for the new nightly version
-        // if we haven't done it during the past hour
-        const lastCheck = state.lastCheck;
 
-        const anHour = 60 * 60 * 1000;
-        const shouldCheckForNewNightly = state.releaseId === undefined || (now - (lastCheck ?? 0)) > anHour;
+    const anHour = 60 * 60 * 1000;
+    const shouldDownloadNightly = state.releaseId === undefined || (now - (lastCheck ?? 0)) > anHour;
 
-        if (!shouldCheckForNewNightly) return;
-    }
+    if (!shouldDownloadNightly) return;
 
     const release = await fetchRelease("nightly").catch((e) => {
         log.error(e);


Test patch:
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs
index 766790576d..01c919a7ec 100644
--- a/crates/ra_hir_ty/src/tests/traits.rs
+++ b/crates/ra_hir_ty/src/tests/traits.rs
@@ -3029,21 +3029,3 @@ fn infer_dyn_fn_output() {
     "###
     );
 }
-
-#[test]
-fn variable_kinds() {
-    check_types(
-        r#"
-trait Trait<T> { fn get(self, t: T) -> T; }
-struct S;
-impl Trait<u128> for S {}
-impl Trait<f32> for S {}
-fn test() {
-    S.get(1);
-  //^^^^^^^^ u128
-    S.get(1.);
-  //^^^^^^^^ f32
-}
-        "#,
-    );
-}
diff --git a/crates/ra_ssr/src/tests.rs b/crates/ra_ssr/src/tests.rs
index 9568d44322..c692c97e2e 100644
--- a/crates/ra_ssr/src/tests.rs
+++ b/crates/ra_ssr/src/tests.rs
@@ -1,6 +1,5 @@
 use crate::{MatchFinder, SsrRule};
 use ra_db::{FileId, SourceDatabaseExt};
-use test_utils::mark;
 
 fn parse_error_text(query: &str) -> String {
     format!("{}", query.parse::<SsrRule>().unwrap_err())
@@ -302,22 +301,6 @@ fn match_pattern() {
     assert_matches("Some($a)", "fn f() {if let Some(x) = foo() {}}", &["Some(x)"]);
 }
 
-#[test]
-fn literal_constraint() {
-    mark::check!(literal_constraint);
-    let code = r#"
-        fn f1() {
-            let x1 = Some(42);
-            let x2 = Some("foo");
-            let x3 = Some(x1);
-            let x4 = Some(40 + 2);
-            let x5 = Some(true);
-        }
-        "#;
-    assert_matches("Some(${a:kind(literal)})", code, &["Some(42)", "Some(\"foo\")", "Some(true)"]);
-    assert_matches("Some(${a:not(kind(literal))})", code, &["Some(x1)", "Some(40 + 2)"]);
-}
-
 #[test]
 fn match_reordered_struct_instantiation() {
     assert_matches(