Repository: rust-analyzer/rust-analyzer
Pull request #11001 (2021-12-13 15:34:21)

minor: fixup generate assist priorities
Fixes https://github.com/rust-analyzer/rust-analyzer/issues/10996
bors r+

bors[bot]: Build failed:
  * [Rust (ubuntu-latest)](https://github.com/rust-analyzer/rust-analyzer/runs/4508282812?check_suite_focus=true)

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index dca35a212a..92f98b536c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -220,6 +220,12 @@ dependencies = [
  "tracing",
 ]
 
+[[package]]
+name = "countme"
+version = "2.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "328b822bdcba4d4e402be8d9adb6eebf269f969f8eadef977a553ff3c4fbcb58"
+
 [[package]]
 name = "countme"
 version = "3.0.0"
@@ -519,7 +525,6 @@ dependencies = [
  "cfg",
  "cov-mark",
  "either",
- "hashbrown",
  "itertools",
  "la-arena",
  "limit",
@@ -753,9 +758,9 @@ dependencies = [
 
 [[package]]
 name = "itertools"
-version = "0.10.3"
+version = "0.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
+checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf"
 dependencies = [
  "either",
 ]
@@ -804,9 +809,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "libc"
-version = "0.2.111"
+version = "0.2.108"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e167738f1866a7ec625567bae89ca0d44477232a4f7c52b1c7f2adc2c98804f"
+checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119"
 
 [[package]]
 name = "libloading"
@@ -1122,9 +1127,9 @@ checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.33"
+version = "1.0.32"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb37d2df5df740e582f28f8560cf425f52bb267d872fe58358eadb554909f07a"
+checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
 dependencies = [
  "unicode-xid",
 ]
@@ -1178,7 +1183,7 @@ name = "profile"
 version = "0.0.0"
 dependencies = [
  "cfg-if",
- "countme",
+ "countme 3.0.0",
  "la-arena",
  "libc",
  "once_cell",
@@ -1221,9 +1226,9 @@ dependencies = [
 
 [[package]]
 name = "pulldown-cmark-to-cmark"
-version = "7.1.1"
+version = "7.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8bc14bc6b9f5881f240f9766414707ab24f972bcc3388589fe15b22fb15dc60d"
+checksum = "2d85e607de0249c2b5041e38de8684b00b62a64edee60bfcd85c153031a9d658"
 dependencies = [
  "pulldown-cmark",
 ]
@@ -1297,11 +1302,11 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
 
 [[package]]
 name = "rowan"
-version = "0.15.1"
+version = "0.14.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "104e4d36da6a2a0c9485ebf1b2dfc628036c7cc9b38bd1b14fa785f1f1bdf5f0"
+checksum = "4f77412a3d1f26af0c0783c23b3555a301b1a49805cba7bf9a7827a9e9e285f0"
 dependencies = [
- "countme",
+ "countme 2.0.4",
  "hashbrown",
  "memoffset",
  "rustc-hash",
@@ -1383,9 +1388,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
 
 [[package]]
 name = "ryu"
-version = "1.0.8"
+version = "1.0.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b30e4c09749c107e83dd61baf9604198efc4542863c88af39dafcaca89c7c9f9"
+checksum = "3c9613b5a66ab9ba26415184cfc41156594925a9cf3a2057e57f31ff145f6568"
 
 [[package]]
 name = "salsa"
@@ -1448,18 +1453,18 @@ dependencies = [
 
 [[package]]
 name = "serde"
-version = "1.0.131"
+version = "1.0.130"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4ad69dfbd3e45369132cc64e6748c2d65cdfb001a2b1c232d128b4ad60561c1"
+checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.131"
+version = "1.0.130"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b710a83c4e0dff6a3d511946b95274ad9ca9e5d3ae497b63fda866ac955358d2"
+checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
 dependencies = [
  "proc-macro2",
  "quote",
diff --git a/crates/hir_expand/Cargo.toml b/crates/hir_expand/Cargo.toml
index 039861f7f7..380b7ef877 100644
--- a/crates/hir_expand/Cargo.toml
+++ b/crates/hir_expand/Cargo.toml
@@ -16,7 +16,6 @@ either = "1.5.3"
 rustc-hash = "1.0.0"
 la-arena = { version = "0.3.0", path = "../../lib/arena" }
 itertools = "0.10.0"
-hashbrown = { version = "0.11", features = ["inline-more"], default-features = false }
 
 base_db = { path = "../base_db", version = "0.0.0" }
 cfg = { path = "../cfg", version = "0.0.0" }
diff --git a/crates/hir_expand/src/ast_id_map.rs b/crates/hir_expand/src/ast_id_map.rs
index 43c8a3db5c..16cf299076 100644
--- a/crates/hir_expand/src/ast_id_map.rs
+++ b/crates/hir_expand/src/ast_id_map.rs
@@ -8,13 +8,12 @@
 use std::{
     any::type_name,
     fmt,
-    hash::{BuildHasher, BuildHasherDefault, Hash, Hasher},
+    hash::{Hash, Hasher},
     marker::PhantomData,
 };
 
 use la_arena::{Arena, Idx};
 use profile::Count;
-use rustc_hash::FxHasher;
 use syntax::{ast, match_ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr};
 
 /// `AstId` points to an AST node in a specific file.
@@ -61,28 +60,12 @@ impl<N: AstNode> FileAstId<N> {
 type ErasedFileAstId = Idx<SyntaxNodePtr>;
 
 /// Maps items' `SyntaxNode`s to `ErasedFileAstId`s and back.
-#[derive(Default)]
+#[derive(Debug, PartialEq, Eq, Default)]
 pub struct AstIdMap {
-    /// Maps stable id to unstable ptr.
     arena: Arena<SyntaxNodePtr>,
-    /// Reverse: map ptr to id.
-    map: hashbrown::HashMap<Idx<SyntaxNodePtr>, (), ()>,
     _c: Count<Self>,
 }
 
-impl fmt::Debug for AstIdMap {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("AstIdMap").field("arena", &self.arena).finish()
-    }
-}
-
-impl PartialEq for AstIdMap {
-    fn eq(&self, other: &Self) -> bool {
-        self.arena == other.arena
-    }
-}
-impl Eq for AstIdMap {}
-
 impl AstIdMap {
     pub(crate) fn from_source(node: &SyntaxNode) -> AstIdMap {
         assert!(node.parent().is_none());
@@ -106,16 +89,6 @@ impl AstIdMap {
                 }
             }
         });
-        res.map = hashbrown::HashMap::with_capacity_and_hasher(res.arena.len(), ());
-        for (idx, ptr) in res.arena.iter() {
-            let hash = hash_ptr(ptr);
-            match res.map.raw_entry_mut().from_hash(hash, |idx2| *idx2 == idx) {
-                hashbrown::hash_map::RawEntryMut::Occupied(_) => unreachable!(),
-                hashbrown::hash_map::RawEntryMut::Vacant(entry) => {
-                    entry.insert_with_hasher(hash, idx, (), |&idx| hash_ptr(&res.arena[idx]));
-                }
-            }
-        }
         res
     }
 
@@ -125,9 +98,8 @@ impl AstIdMap {
     }
     fn erased_ast_id(&self, item: &SyntaxNode) -> ErasedFileAstId {
         let ptr = SyntaxNodePtr::new(item);
-        let hash = hash_ptr(&ptr);
-        match self.map.raw_entry().from_hash(hash, |&idx| self.arena[idx] == ptr) {
-            Some((&idx, &())) => idx,
+        match self.arena.iter().find(|(_id, i)| **i == ptr) {
+            Some((it, _)) => it,
             None => panic!(
                 "Can't find {:?} in AstIdMap:\n{:?}",
                 item,
@@ -145,12 +117,6 @@ impl AstIdMap {
     }
 }
 
-fn hash_ptr(ptr: &SyntaxNodePtr) -> u64 {
-    let mut hasher = BuildHasherDefault::<FxHasher>::default().build_hasher();
-    ptr.hash(&mut hasher);
-    hasher.finish()
-}
-
 /// Walks the subtree in bdfs order, calling `f` for each node. What is bdfs
 /// order? It is a mix of breadth-first and depth first orders. Nodes for which
 /// `f` returns true are visited breadth-first, all the other nodes are explored
diff --git a/crates/hir_ty/src/diagnostics/decl_check.rs b/crates/hir_ty/src/diagnostics/decl_check.rs
index 9a0f457905..a1e7198bc2 100644
--- a/crates/hir_ty/src/diagnostics/decl_check.rs
+++ b/crates/hir_ty/src/diagnostics/decl_check.rs
@@ -181,12 +181,8 @@ impl<'a> DeclValidator<'a> {
                 AttrDefId::ExternBlockId(id) => Some(id.lookup(self.db.upcast()).container.into()),
                 // These warnings should not explore macro definitions at all
                 AttrDefId::MacroDefId(_) => None,
-                AttrDefId::AdtId(aid) => match aid {
-                    AdtId::StructId(sid) => Some(sid.lookup(self.db.upcast()).container.into()),
-                    AdtId::EnumId(eid) => Some(eid.lookup(self.db.upcast()).container.into()),
-                    // Unions aren't yet supported
-                    AdtId::UnionId(_) => None,
-                },
+                // Will never occur under an enum/struct/union/type alias
+                AttrDefId::AdtId(_) => None,
                 AttrDefId::FieldId(_) => None,
                 AttrDefId::EnumVariantId(_) => None,
                 AttrDefId::TypeAliasId(_) => None,
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 16a2b4a113..5bd9f054dd 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -555,6 +555,24 @@ impl Analysis {
             .unwrap_or_default())
     }
 
+    /// Computes assists (aka code actions aka intentions) for the given
+    /// position. If `resolve == false`, computes enough info to show the
+    /// lightbulb list in the editor, but doesn't compute actual edits, to
+    /// improve performance.
+    pub fn assists(
+        &self,
+        config: &AssistConfig,
+        resolve: AssistResolveStrategy,
+        frange: FileRange,
+    ) -> Cancellable<Vec<Assist>> {
+        self.with_db(|db| {
+            let ssr_assists = ssr::ssr_assists(db, &resolve, frange);
+            let mut acc = ide_assists::assists(db, config, resolve, frange);
+            acc.extend(ssr_assists.into_iter());
+            acc
+        })
+    }
+
     /// Computes the set of diagnostics for the given file.
     pub fn diagnostics(
         &self,
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs
index 63b0a91ce5..e2845bc58b 100644
--- a/crates/ide_assists/src/handlers/extract_function.rs
+++ b/crates/ide_assists/src/handlers/extract_function.rs
@@ -480,7 +480,7 @@ impl FunctionBody {
             .statements()
             .map(|stmt| stmt.syntax().text_range())
             .filter(|&stmt| selected.intersect(stmt).filter(|it| !it.is_empty()).is_some())
-            .reduce(|acc, stmt| acc.cover(stmt));
+            .fold1(|acc, stmt| acc.cover(stmt));
         if let Some(tail_range) = parent
             .tail_expr()
             .map(|it| it.syntax().text_range())
diff --git a/crates/ide_assists/src/handlers/generate_delegate_methods.rs b/crates/ide_assists/src/handlers/generate_delegate_methods.rs
index 090dab946a..31b438b15e 100644
--- a/crates/ide_assists/src/handlers/generate_delegate_methods.rs
+++ b/crates/ide_assists/src/handlers/generate_delegate_methods.rs
@@ -47,18 +47,18 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext)
     let strukt_name = strukt.name()?;
     let current_module = ctx.sema.scope(strukt.syntax()).module()?;
 
-    let (field_name, field_ty) = match ctx.find_node_at_offset::<ast::RecordField>() {
+    let (field_name, field_ty, target) = match ctx.find_node_at_offset::<ast::RecordField>() {
         Some(field) => {
             let field_name = field.name()?;
             let field_ty = field.ty()?;
-            (format!("{}", field_name), field_ty)
+            (format!("{}", field_name), field_ty, field.syntax().text_range())
         }
         None => {
             let field = ctx.find_node_at_offset::<ast::TupleField>()?;
             let field_list = ctx.find_node_at_offset::<ast::TupleFieldList>()?;
             let field_list_index = field_list.fields().position(|it| it == field)?;
             let field_ty = field.ty()?;
-            (format!("{}", field_list_index), field_ty)
+            (format!("{}", field_list_index), field_ty, field.syntax().text_range())
         }
     };
 
@@ -74,7 +74,6 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext)
         Option::<()>::None
     });
 
-    let target = field_ty.syntax().text_range();
     for method in methods {
         let adt = ast::Adt::Struct(strukt.clone());
         let name = method.name(ctx.db()).to_string();
diff --git a/crates/ide_assists/src/handlers/generate_documentation_template.rs b/crates/ide_assists/src/handlers/generate_documentation_template.rs
deleted file mode 100644
index cfc7e9d043..0000000000
--- a/crates/ide_assists/src/handlers/generate_documentation_template.rs
+++ /dev/null
@@ -1,1091 +0,0 @@
-use hir::{AsAssocItem, HasVisibility, ModuleDef, Visibility};
-use ide_db::assists::{AssistId, AssistKind};
-use itertools::Itertools;
-use stdx::to_lower_snake_case;
-use syntax::{
-    ast::{self, edit::IndentLevel, HasDocComments, HasName},
-    AstNode,
-};
-
-use crate::assist_context::{AssistContext, Assists};
-
-// Assist: generate_documentation_template
-//
-// Adds a documentation template above a function definition / declaration.
-//
-// ```
-// pub fn my_$0func(a: i32, b: i32) -> Result<(), std::io::Error> {
-//     unimplemented!()
-// }
-// ```
-// ->
-// ```
-// /// .
-// ///
-// /// # Examples
-// ///
-// /// ```
-// /// use test::my_func;
-// ///
-// /// assert_eq!(my_func(a, b), );
-// /// ```
-// ///
-// /// # Errors
-// ///
-// /// This function will return an error if .
-// pub fn my_func(a: i32, b: i32) -> Result<(), std::io::Error> {
-//     unimplemented!()
-// }
-// ```
-pub(crate) fn generate_documentation_template(
-    acc: &mut Assists,
-    ctx: &AssistContext,
-) -> Option<()> {
-    let name = ctx.find_node_at_offset::<ast::Name>()?;
-    let ast_func = name.syntax().parent().and_then(ast::Fn::cast)?;
-    if is_in_trait_impl(&ast_func, ctx)
-        || !is_public(&ast_func, ctx)?
-        || ast_func.doc_comments().next().is_some()
-    {
-        return None;
-    }
-
-    let parent_syntax = ast_func.syntax();
-    let text_range = parent_syntax.text_range();
-    let indent_level = IndentLevel::from_node(&parent_syntax);
-
-    acc.add(
-        AssistId("generate_documentation_template", AssistKind::Generate),
-        "Generate a documentation template",
-        text_range,
-        |builder| {
-            let mut doc_lines = Vec::new();
-            // Introduction / short function description before the sections
-            doc_lines.push(introduction_builder(&ast_func, ctx));
-            // Then come the sections
-            if let Some(mut lines) = examples_builder(&ast_func, ctx) {
-                doc_lines.push("".into());
-                doc_lines.append(&mut lines);
-            }
-            for section_builder in [panics_builder, errors_builder, safety_builder] {
-                if let Some(mut lines) = section_builder(&ast_func) {
-                    doc_lines.push("".into());
-                    doc_lines.append(&mut lines);
-                }
-            }
-            builder.insert(text_range.start(), documentation_from_lines(doc_lines, indent_level));
-        },
-    )
-}
-
-/// Builds an introduction, trying to be smart if the function is `::new()`
-fn introduction_builder(ast_func: &ast::Fn, ctx: &AssistContext) -> String {
-    || -> Option<String> {
-        let hir_func = ctx.sema.to_def(ast_func)?;
-        let container = hir_func.as_assoc_item(ctx.db())?.container(ctx.db());
-        if let hir::AssocItemContainer::Impl(implementation) = container {
-            let ret_ty = hir_func.ret_type(ctx.db());
-            let self_ty = implementation.self_ty(ctx.db());
-
-            let is_new = ast_func.name()?.to_string() == "new";
-            match is_new && ret_ty == self_ty {
-                true => Some(format!("Creates a new [`{}`].", self_type(ast_func)?)),
-                false => None,
-            }
-        } else {
-            None
-        }
-    }()
-    .unwrap_or_else(|| ".".into())
-}
-
-/// Builds an `# Examples` section. An option is returned to be able to manage an error in the AST.
-fn examples_builder(ast_func: &ast::Fn, ctx: &AssistContext) -> Option<Vec<String>> {
-    let mut lines = string_vec_from(&["# Examples", "", "```"]);
-    if is_in_trait_def(ast_func, ctx) {
-        lines.push("// Example template not implemented for trait functions".into());
-    } else {
-        lines.append(&mut gen_ex_template(ast_func, ctx)?)
-    };
-
-    lines.push("```".into());
-    Some(lines)
-}
-
-/// Builds an optional `# Panics` section
-fn panics_builder(ast_func: &ast::Fn) -> Option<Vec<String>> {
-    match can_panic(ast_func) {
-        Some(true) => Some(string_vec_from(&["# Panics", "", "Panics if ."])),
-        _ => None,
-    }
-}
-
-/// Builds an optional `# Errors` section
-fn errors_builder(ast_func: &ast::Fn) -> Option<Vec<String>> {
-    match return_type(ast_func)?.to_string().contains("Result") {
-        true => Some(string_vec_from(&["# Errors", "", "This function will return an error if ."])),
-        false => None,
-    }
-}
-
-/// Builds an optional `# Safety` section
-fn safety_builder(ast_func: &ast::Fn) -> Option<Vec<String>> {
-    let is_unsafe = ast_func.unsafe_token().is_some();
-    match is_unsafe {
-        true => Some(string_vec_from(&["# Safety", "", "."])),
-        false => None,
-    }
-}
-
-/// Generates an example template
-fn gen_ex_template(ast_func: &ast::Fn, ctx: &AssistContext) -> Option<Vec<String>> {
-    let mut lines = Vec::new();
-    let is_unsafe = ast_func.unsafe_token().is_some();
-    let param_list = ast_func.param_list()?;
-    let ref_mut_params = ref_mut_params(&param_list);
-    let self_name: Option<String> = self_name(ast_func);
-
-    lines.push(format!("use {};", build_path(ast_func, ctx)?));
-    lines.push("".into());
-    if let Some(self_definition) = self_definition(ast_func, self_name.as_deref()) {
-        lines.push(self_definition);
-    }
-    for param_name in &ref_mut_params {
-        lines.push(format!("let mut {} = ;", param_name))
-    }
-    // Call the function, check result
-    let function_call = function_call(ast_func, &param_list, self_name.as_deref(), is_unsafe)?;
-    if returns_a_value(ast_func, ctx) {
-        if count_parameters(&param_list) < 3 {
-            lines.push(format!("assert_eq!({}, );", function_call));
-        } else {
-            lines.push(format!("let result = {};", function_call));
-            lines.push("assert_eq!(result, );".into());
-        }
-    } else {
-        lines.push(format!("{};", function_call));
-    }
-    // Check the mutated values
-    if is_ref_mut_self(ast_func) == Some(true) {
-        lines.push(format!("assert_eq!({}, );", self_name?));
-    }
-    for param_name in &ref_mut_params {
-        lines.push(format!("assert_eq!({}, );", param_name));
-    }
-    Some(lines)
-}
-
-/// Checks if the function is public / exported
-fn is_public(ast_func: &ast::Fn, ctx: &AssistContext) -> Option<bool> {
-    let hir_func = ctx.sema.to_def(ast_func)?;
-    Some(
-        hir_func.visibility(ctx.db()) == Visibility::Public
-            && all_parent_mods_public(&hir_func, ctx),
-    )
-}
-
-/// Checks that all parent modules of the function are public / exported
-fn all_parent_mods_public(hir_func: &hir::Function, ctx: &AssistContext) -> bool {
-    let mut module = hir_func.module(ctx.db());
-    loop {
-        if let Some(parent) = module.parent(ctx.db()) {
-            match ModuleDef::from(module).visibility(ctx.db()) {
-                Visibility::Public => module = parent,
-                _ => break false,
-            }
-        } else {
-            break true;
-        }
-    }
-}
-
-/// Returns the name of the current crate
-fn crate_name(ast_func: &ast::Fn, ctx: &AssistContext) -> Option<String> {
-    let krate = ctx.sema.scope(&ast_func.syntax()).module()?.krate();
-    Some(krate.display_name(ctx.db())?.to_string())
-}
-
-/// `None` if function without a body; some bool to guess if function can panic
-fn can_panic(ast_func: &ast::Fn) -> Option<bool> {
-    let body = ast_func.body()?.to_string();
-    let can_panic = body.contains("panic!(")
-        // FIXME it would be better to not match `debug_assert*!` macro invocations
-        || body.contains("assert!(")
-        || body.contains(".unwrap()")
-        || body.contains(".expect(");
-    Some(can_panic)
-}
-
-/// Helper function to get the name that should be given to `self` arguments
-fn self_name(ast_func: &ast::Fn) -> Option<String> {
-    self_partial_type(ast_func).map(|name| to_lower_snake_case(&name))
-}
-
-/// Heper function to get the name of the type of `self`
-fn self_type(ast_func: &ast::Fn) -> Option<String> {
-    ast_func
-        .syntax()
-        .ancestors()
-        .find_map(ast::Impl::cast)
-        .and_then(|i| i.self_ty())
-        .map(|t| (t.to_string()))
-}
-
-/// Heper function to get the name of the type of `self` without generic arguments
-fn self_partial_type(ast_func: &ast::Fn) -> Option<String> {
-    let mut self_type = self_type(ast_func)?;
-    if let Some(idx) = self_type.find(|c| ['<', ' '].contains(&c)) {
-        self_type.truncate(idx);
-    }
-    Some(self_type)
-}
-
-/// Helper function to determine if the function is in a trait implementation
-fn is_in_trait_impl(ast_func: &ast::Fn, ctx: &AssistContext) -> bool {
-    ctx.sema
-        .to_def(ast_func)
-        .and_then(|hir_func| hir_func.as_assoc_item(ctx.db()))
-        .and_then(|assoc_item| assoc_item.containing_trait_impl(ctx.db()))
-        .is_some()
-}
-
-/// Helper function to determine if the function definition is in a trait definition
-fn is_in_trait_def(ast_func: &ast::Fn, ctx: &AssistContext) -> bool {
-    ctx.sema
-        .to_def(ast_func)
-        .and_then(|hir_func| hir_func.as_assoc_item(ctx.db()))
-        .and_then(|assoc_item| assoc_item.containing_trait(ctx.db()))
-        .is_some()
-}
-
-/// Returns `None` if no `self` at all, `Some(true)` if there is `&mut self` else `Some(false)`
-fn is_ref_mut_self(ast_func: &ast::Fn) -> Option<bool> {
-    let self_param = ast_func.param_list()?.self_param()?;
-    Some(self_param.mut_token().is_some() && self_param.amp_token().is_some())
-}
-
-/// Helper function to define an variable to be the `self` argument
-fn self_definition(ast_func: &ast::Fn, self_name: Option<&str>) -> Option<String> {
-    let definition = match is_ref_mut_self(ast_func)? {
-        true => format!("let mut {} = ;", self_name?),
-        false => format!("let {} = ;", self_name?),
-    };
-    Some(definition)
-}
-
-/// Helper function to determine if a parameter is `&mut`
-fn is_a_ref_mut_param(param: &ast::Param) -> bool {
-    match param.ty() {
-        Some(ast::Type::RefType(param_ref)) => param_ref.mut_token().is_some(),
-        _ => false,
-    }
-}
-
-/// Helper function to build the list of `&mut` parameters
-fn ref_mut_params(param_list: &ast::ParamList) -> Vec<String> {
-    param_list
-        .params()
-        .filter_map(|param| match is_a_ref_mut_param(&param) {
-            // Maybe better filter the param name (to do this maybe extract a function from
-            // `arguments_from_params`?) in case of a `mut a: &mut T`. Anyway managing most (not
-            // all) cases might be enough, the goal is just to produce a template.
-            true => Some(param.pat()?.to_string()),
-            false => None,
-        })
-        .collect()
-}
-
-/// Helper function to build the comma-separated list of arguments of the function
-fn arguments_from_params(param_list: &ast::ParamList) -> String {
-    let args_iter = param_list.params().map(|param| match param.pat() {
-        // To avoid `mut` in the function call (which would be a nonsense), `Pat` should not be
-        // written as is so its variants must be managed independently. Other variants (for
-        // instance `TuplePat`) could be managed later.
-        Some(ast::Pat::IdentPat(ident_pat)) => match ident_pat.name() {
-            Some(name) => match is_a_ref_mut_param(&param) {
-                true => format!("&mut {}", name.to_string()),
-                false => name.to_string(),
-            },
-            None => "_".to_string(),
-        },
-        _ => "_".to_string(),
-    });
-    Itertools::intersperse(args_iter, ", ".to_string()).collect()
-}
-
-/// Helper function to build a function call. `None` if expected `self_name` was not provided
-fn function_call(
-    ast_func: &ast::Fn,
-    param_list: &ast::ParamList,
-    self_name: Option<&str>,
-    is_unsafe: bool,
-) -> Option<String> {
-    let name = ast_func.name()?;
-    let arguments = arguments_from_params(&param_list);
-    let function_call = if param_list.self_param().is_some() {
-        format!("{}.{}({})", self_name?, name, arguments)
-    } else if let Some(implementation) = self_partial_type(ast_func) {
-        format!("{}::{}({})", implementation, name, arguments)
-    } else {
-        format!("{}({})", name, arguments)
-    };
-    match is_unsafe {
-        true => Some(format!("unsafe {{ {} }}", function_call)),
-        false => Some(function_call),
-    }
-}
-
-/// Helper function to count the parameters including `self`
-fn count_parameters(param_list: &ast::ParamList) -> usize {
-    param_list.params().count() + if param_list.self_param().is_some() { 1 } else { 0 }
-}
-
-/// Helper function to transform lines of documentation into a Rust code documentation
-fn documentation_from_lines(doc_lines: Vec<String>, indent_level: IndentLevel) -> String {
-    let mut result = String::new();
-    for doc_line in doc_lines {
-        result.push_str("///");
-        if !doc_line.is_empty() {
-            result.push(' ');
-            result.push_str(&doc_line);
-        }
-        result.push('\n');
-        result.push_str(&indent_level.to_string());
-    }
-    result
-}
-
-/// Helper function to transform an array of borrowed strings to an owned `Vec<String>`
-fn string_vec_from(string_array: &[&str]) -> Vec<String> {
-    string_array.iter().map(|&s| s.to_owned()).collect()
-}
-
-/// Helper function to build the path of the module in the which is the node
-fn build_path(ast_func: &ast::Fn, ctx: &AssistContext) -> Option<String> {
-    let crate_name = crate_name(ast_func, ctx)?;
-    let leaf = self_partial_type(ast_func)
-        .or_else(|| ast_func.name().map(|n| n.to_string()))
-        .unwrap_or_else(|| "*".into());
-    let module_def: ModuleDef = ctx.sema.to_def(ast_func)?.module(ctx.db()).into();
-    match module_def.canonical_path(ctx.db()) {
-        Some(path) => Some(format!("{}::{}::{}", crate_name, path, leaf)),
-        None => Some(format!("{}::{}", crate_name, leaf)),
-    }
-}
-
-/// Helper function to get the return type of a function
-fn return_type(ast_func: &ast::Fn) -> Option<ast::Type> {
-    ast_func.ret_type()?.ty()
-}
-
-/// Helper function to determine if the function returns some data
-fn returns_a_value(ast_func: &ast::Fn, ctx: &AssistContext) -> bool {
-    ctx.sema
-        .to_def(ast_func)
-        .map(|hir_func| hir_func.ret_type(ctx.db()))
-        .map(|ret_ty| !ret_ty.is_unit() && !ret_ty.is_never())
-        .unwrap_or(false)
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::tests::{check_assist, check_assist_not_applicable};
-
-    use super::*;
-
-    #[test]
-    fn not_applicable_on_function_calls() {
-        check_assist_not_applicable(
-            generate_documentation_template,
-            r#"
-fn hello_world() {}
-fn calls_hello_world() {
-    hello_world$0();
-}
-"#,
-        )
-    }
-
-    #[test]
-    fn not_applicable_in_trait_impl() {
-        check_assist_not_applicable(
-            generate_documentation_template,
-            r#"
-trait MyTrait {}
-struct MyStruct;
-impl MyTrait for MyStruct {
-    fn hello_world$0();
-}
-"#,
-        )
-    }
-
-    #[test]
-    fn not_applicable_if_function_is_private() {
-        check_assist_not_applicable(generate_documentation_template, r#"fn priv$0ate() {}"#);
-    }
-
-    #[test]
-    fn not_applicable_if_function_is_pub_crate() {
-        check_assist_not_applicable(
-            generate_documentation_template,
-            r#"pub(crate) fn pri$0vate() {}"#,
-        );
-    }
-
-    #[test]
-    fn not_applicable_if_function_is_in_private_mod() {
-        check_assist_not_applicable(
-            generate_documentation_template,
-            r#"
-mod PrivateModule {
-    pub fn pri$0vate() {}
-}"#,
-        );
-    }
-
-    #[test]
-    fn not_applicable_if_function_is_in_pub_crate_mod() {
-        check_assist_not_applicable(
-            generate_documentation_template,
-            r#"
-pub(crate) mod PrivateModule {
-    pub fn pr$0ivate() {}
-}"#,
-        );
-    }
-
-    #[test]
-    fn not_applicable_if_function_is_in_non_public_mod_is_recursive() {
-        check_assist_not_applicable(
-            generate_documentation_template,
-            r#"
-mod ParentPrivateModule {
-    pub mod PrivateModule {
-        pub fn pr$0ivate() {}
-    }
-}"#,
-        );
-    }
-
-    #[test]
-    fn not_applicable_if_function_already_documented() {
-        check_assist_not_applicable(
-            generate_documentation_template,
-            r#"
-/// Some documentation here
-pub fn $0documented_function() {}
-"#,
-        );
-    }
-
-    #[test]
-    fn supports_noop_function() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub fn no$0op() {}
-"#,
-            r#"
-/// .
-///
-/// # Examples
-///
-/// ```
-/// use test::noop;
-///
-/// noop();
-/// ```
-pub fn noop() {}
-"#,
-        );
-    }
-
-    #[test]
-    fn supports_a_parameter() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub fn no$0op_with_param(_a: i32) {}
-"#,
-            r#"
-/// .
-///
-/// # Examples
-///
-/// ```
-/// use test::noop_with_param;
-///
-/// noop_with_param(_a);
-/// ```
-pub fn noop_with_param(_a: i32) {}
-"#,
-        );
-    }
-
-    #[test]
-    fn detects_unsafe_function() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub unsafe fn no$0op_unsafe() {}
-"#,
-            r#"
-/// .
-///
-/// # Examples
-///
-/// ```
-/// use test::noop_unsafe;
-///
-/// unsafe { noop_unsafe() };
-/// ```
-///
-/// # Safety
-///
-/// .
-pub unsafe fn noop_unsafe() {}
-"#,
-        );
-    }
-
-    #[test]
-    fn guesses_panic_macro_can_panic() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub fn panic$0s_if(a: bool) {
-    if a {
-        panic!();
-    }
-}
-"#,
-            r#"
-/// .
-///
-/// # Examples
-///
-/// ```
-/// use test::panics_if;
-///
-/// panics_if(a);
-/// ```
-///
-/// # Panics
-///
-/// Panics if .
-pub fn panics_if(a: bool) {
-    if a {
-        panic!();
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn guesses_assert_macro_can_panic() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub fn $0panics_if_not(a: bool) {
-    assert!(a == true);
-}
-"#,
-            r#"
-/// .
-///
-/// # Examples
-///
-/// ```
-/// use test::panics_if_not;
-///
-/// panics_if_not(a);
-/// ```
-///
-/// # Panics
-///
-/// Panics if .
-pub fn panics_if_not(a: bool) {
-    assert!(a == true);
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn guesses_unwrap_can_panic() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub fn $0panics_if_none(a: Option<()>) {
-    a.unwrap();
-}
-"#,
-            r#"
-/// .
-///
-/// # Examples
-///
-/// ```
-/// use test::panics_if_none;
-///
-/// panics_if_none(a);
-/// ```
-///
-/// # Panics
-///
-/// Panics if .
-pub fn panics_if_none(a: Option<()>) {
-    a.unwrap();
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn guesses_expect_can_panic() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub fn $0panics_if_none2(a: Option<()>) {
-    a.expect("Bouh!");
-}
-"#,
-            r#"
-/// .
-///
-/// # Examples
-///
-/// ```
-/// use test::panics_if_none2;
-///
-/// panics_if_none2(a);
-/// ```
-///
-/// # Panics
-///
-/// Panics if .
-pub fn panics_if_none2(a: Option<()>) {
-    a.expect("Bouh!");
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn checks_output_in_example() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub fn returns_a_value$0() -> i32 {
-    0
-}
-"#,
-            r#"
-/// .
-///
-/// # Examples
-///
-/// ```
-/// use test::returns_a_value;
-///
-/// assert_eq!(returns_a_value(), );
-/// ```
-pub fn returns_a_value() -> i32 {
-    0
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn detects_result_output() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub fn returns_a_result$0() -> Result<i32, std::io::Error> {
-    Ok(0)
-}
-"#,
-            r#"
-/// .
-///
-/// # Examples
-///
-/// ```
-/// use test::returns_a_result;
-///
-/// assert_eq!(returns_a_result(), );
-/// ```
-///
-/// # Errors
-///
-/// This function will return an error if .
-pub fn returns_a_result() -> Result<i32, std::io::Error> {
-    Ok(0)
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn checks_ref_mut_in_example() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub fn modifies_a_value$0(a: &mut i32) {
-    *a = 0;
-}
-"#,
-            r#"
-/// .
-///
-/// # Examples
-///
-/// ```
-/// use test::modifies_a_value;
-///
-/// let mut a = ;
-/// modifies_a_value(&mut a);
-/// assert_eq!(a, );
-/// ```
-pub fn modifies_a_value(a: &mut i32) {
-    *a = 0;
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn stores_result_if_at_least_3_params() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub fn sum3$0(a: i32, b: i32, c: i32) -> i32 {
-    a + b + c
-}
-"#,
-            r#"
-/// .
-///
-/// # Examples
-///
-/// ```
-/// use test::sum3;
-///
-/// let result = sum3(a, b, c);
-/// assert_eq!(result, );
-/// ```
-pub fn sum3(a: i32, b: i32, c: i32) -> i32 {
-    a + b + c
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn supports_fn_in_mods() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub mod a {
-    pub mod b {
-        pub fn no$0op() {}
-    }
-}
-"#,
-            r#"
-pub mod a {
-    pub mod b {
-        /// .
-        ///
-        /// # Examples
-        ///
-        /// ```
-        /// use test::a::b::noop;
-        ///
-        /// noop();
-        /// ```
-        pub fn noop() {}
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn supports_fn_in_impl() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub struct MyStruct;
-impl MyStruct {
-    pub fn no$0op() {}
-}
-"#,
-            r#"
-pub struct MyStruct;
-impl MyStruct {
-    /// .
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use test::MyStruct;
-    ///
-    /// MyStruct::noop();
-    /// ```
-    pub fn noop() {}
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn supports_fn_in_trait() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub trait MyTrait {
-    fn fun$0ction_trait();
-}
-"#,
-            r#"
-pub trait MyTrait {
-    /// .
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// // Example template not implemented for trait functions
-    /// ```
-    fn function_trait();
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn supports_unsafe_fn_in_trait() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub trait MyTrait {
-    unsafe fn unsafe_funct$0ion_trait();
-}
-"#,
-            r#"
-pub trait MyTrait {
-    /// .
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// // Example template not implemented for trait functions
-    /// ```
-    ///
-    /// # Safety
-    ///
-    /// .
-    unsafe fn unsafe_function_trait();
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn supports_fn_in_trait_with_default_panicking() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub trait MyTrait {
-    fn function_trait_with_$0default_panicking() {
-        panic!()
-    }
-}
-"#,
-            r#"
-pub trait MyTrait {
-    /// .
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// // Example template not implemented for trait functions
-    /// ```
-    ///
-    /// # Panics
-    ///
-    /// Panics if .
-    fn function_trait_with_default_panicking() {
-        panic!()
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn supports_fn_in_trait_returning_result() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub trait MyTrait {
-    fn function_tr$0ait_returning_result() -> Result<(), std::io::Error>;
-}
-"#,
-            r#"
-pub trait MyTrait {
-    /// .
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// // Example template not implemented for trait functions
-    /// ```
-    ///
-    /// # Errors
-    ///
-    /// This function will return an error if .
-    fn function_trait_returning_result() -> Result<(), std::io::Error>;
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn detects_new() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-#[derive(Debug, PartialEq)]
-pub struct MyGenericStruct<T> {
-    pub x: T,
-}
-impl<T> MyGenericStruct<T> {
-    pub fn new$0(x: T) -> MyGenericStruct<T> {
-        MyGenericStruct { x }
-    }
-}
-"#,
-            r#"
-#[derive(Debug, PartialEq)]
-pub struct MyGenericStruct<T> {
-    pub x: T,
-}
-impl<T> MyGenericStruct<T> {
-    /// Creates a new [`MyGenericStruct<T>`].
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use test::MyGenericStruct;
-    ///
-    /// assert_eq!(MyGenericStruct::new(x), );
-    /// ```
-    pub fn new(x: T) -> MyGenericStruct<T> {
-        MyGenericStruct { x }
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn detects_new_with_self() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-#[derive(Debug, PartialEq)]
-pub struct MyGenericStruct2<T> {
-    pub x: T,
-}
-impl<T> MyGenericStruct2<T> {
-    pub fn new$0(x: T) -> Self {
-        MyGenericStruct2 { x }
-    }
-}
-"#,
-            r#"
-#[derive(Debug, PartialEq)]
-pub struct MyGenericStruct2<T> {
-    pub x: T,
-}
-impl<T> MyGenericStruct2<T> {
-    /// Creates a new [`MyGenericStruct2<T>`].
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use test::MyGenericStruct2;
-    ///
-    /// assert_eq!(MyGenericStruct2::new(x), );
-    /// ```
-    pub fn new(x: T) -> Self {
-        MyGenericStruct2 { x }
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn supports_method_call() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-impl<T> MyGenericStruct<T> {
-    pub fn co$0nsume(self) {}
-}
-"#,
-            r#"
-impl<T> MyGenericStruct<T> {
-    /// .
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use test::MyGenericStruct;
-    ///
-    /// let my_generic_struct = ;
-    /// my_generic_struct.consume();
-    /// ```
-    pub fn consume(self) {}
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn checks_modified_self_param() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-impl<T> MyGenericStruct<T> {
-    pub fn modi$0fy(&mut self, new_value: T) {
-        self.x = new_value;
-    }
-}
-"#,
-            r#"
-impl<T> MyGenericStruct<T> {
-    /// .
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use test::MyGenericStruct;
-    ///
-    /// let mut my_generic_struct = ;
-    /// my_generic_struct.modify(new_value);
-    /// assert_eq!(my_generic_struct, );
-    /// ```
-    pub fn modify(&mut self, new_value: T) {
-        self.x = new_value;
-    }
-}
-"#,
-        );
-    }
-}
diff --git a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
index 46bf4d7b46..3e33c62144 100644
--- a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -65,7 +65,7 @@ pub(crate) fn replace_derive_with_manual_impl(
     let found_traits = items_locator::items_with_name(
         &ctx.sema,
         current_crate,
-        NameToImport::exact_case_sensitive(trait_path.segments().last()?.to_string()),
+        NameToImport::Exact(trait_path.segments().last()?.to_string()),
         items_locator::AssocItemSearch::Exclude,
         Some(items_locator::DEFAULT_QUERY_SEARCH_LIMIT.inner()),
     )
diff --git a/crates/ide_assists/src/handlers/unmerge_use.rs b/crates/ide_assists/src/handlers/unmerge_use.rs
index 7d73632c79..71a816e19f 100644
--- a/crates/ide_assists/src/handlers/unmerge_use.rs
+++ b/crates/ide_assists/src/handlers/unmerge_use.rs
@@ -1,3 +1,4 @@
+use itertools::Itertools;
 use syntax::{
     ast::{self, make, HasVisibility},
     ted::{self, Position},
@@ -69,7 +70,7 @@ fn resolve_full_path(tree: &ast::UseTree) -> Option<ast::Path> {
         .filter_map(ast::UseTree::cast)
         .filter_map(|t| t.path());
 
-    let final_path = paths.reduce(|prev, next| make::path_concat(next, prev))?;
+    let final_path = paths.fold1(|prev, next| make::path_concat(next, prev))?;
     if final_path.segment().map_or(false, |it| it.self_token().is_some()) {
         final_path.qualifier()
     } else {
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs
index 61ac45637b..3086addac3 100644
--- a/crates/ide_assists/src/lib.rs
+++ b/crates/ide_assists/src/lib.rs
@@ -136,7 +136,6 @@ mod handlers {
     mod generate_default_from_new;
     mod generate_deref;
     mod generate_derive;
-    mod generate_documentation_template;
     mod generate_enum_is_method;
     mod generate_enum_projection_method;
     mod generate_from_impl_for_enum;
@@ -217,10 +216,7 @@ mod handlers {
             generate_constant::generate_constant,
             generate_default_from_enum_variant::generate_default_from_enum_variant,
             generate_default_from_new::generate_default_from_new,
-            generate_delegate_methods::generate_delegate_methods,
-            generate_deref::generate_deref,
             generate_derive::generate_derive,
-            generate_documentation_template::generate_documentation_template,
             generate_enum_is_method::generate_enum_is_method,
             generate_enum_projection_method::generate_enum_as_method,
             generate_enum_projection_method::generate_enum_try_into_method,
@@ -287,6 +283,8 @@ mod handlers {
             generate_getter::generate_getter,
             generate_getter::generate_getter_mut,
             generate_setter::generate_setter,
+            generate_delegate_methods::generate_delegate_methods,
+            generate_deref::generate_deref,
             // Are you sure you want to add new assist here, and not to the
             // sorted list above?
         ]
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs
index d92e311915..f09015efb6 100644
--- a/crates/ide_completion/src/completions/attribute.rs
+++ b/crates/ide_completion/src/completions/attribute.rs
@@ -30,12 +30,16 @@ pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext)
         None => None,
     };
     match (name_ref, attribute.token_tree()) {
-        (Some(path), Some(tt)) if tt.l_paren_token().is_some() => match path.text().as_str() {
-            "repr" => repr::complete_repr(acc, ctx, tt),
-            "derive" => derive::complete_derive(acc, ctx, &parse_tt_as_comma_sep_paths(tt)?),
-            "feature" => lint::complete_lint(acc, ctx, &parse_tt_as_comma_sep_paths(tt)?, FEATURES),
+        (Some(path), Some(token_tree)) => match path.text().as_str() {
+            "repr" => repr::complete_repr(acc, ctx, token_tree),
+            "derive" => {
+                derive::complete_derive(acc, ctx, &parse_tt_as_comma_sep_paths(token_tree)?)
+            }
+            "feature" => {
+                lint::complete_lint(acc, ctx, &parse_tt_as_comma_sep_paths(token_tree)?, FEATURES)
+            }
             "allow" | "warn" | "deny" | "forbid" => {
-                let existing_lints = parse_tt_as_comma_sep_paths(tt)?;
+                let existing_lints = parse_tt_as_comma_sep_paths(token_tree)?;
                 lint::complete_lint(acc, ctx, &existing_lints, DEFAULT_LINTS);
                 lint::complete_lint(acc, ctx, &existing_lints, CLIPPY_LINTS);
                 lint::complete_lint(acc, ctx, &existing_lints, RUSTDOC_LINTS);
@@ -45,8 +49,8 @@ pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext)
             }
             _ => (),
         },
-        (_, Some(_)) => (),
-        (_, None) => complete_new_attribute(acc, ctx, attribute),
+        (None, Some(_)) => (),
+        _ => complete_new_attribute(acc, ctx, attribute),
     }
     Some(())
 }
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs
index 8994c25475..3a9c1b3beb 100644
--- a/crates/ide_completion/src/completions/flyimport.rs
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -227,18 +227,22 @@ fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAs
         )
     } else {
         let fuzzy_name_length = fuzzy_name.len();
-        let mut assets_for_path = ImportAssets::for_fuzzy_path(
+        let assets_for_path = ImportAssets::for_fuzzy_path(
             current_module,
             ctx.path_qual().cloned(),
             fuzzy_name,
             &ctx.sema,
             ctx.token.parent()?,
         )?;
-        if fuzzy_name_length < 3 {
-            cov_mark::hit!(flyimport_exact_on_short_path);
-            assets_for_path.path_fuzzy_name_to_exact(false);
+
+        if matches!(assets_for_path.import_candidate(), ImportCandidate::Path(_))
+            && fuzzy_name_length < 2
+        {
+            cov_mark::hit!(ignore_short_input_for_path);
+            None
+        } else {
+            Some(assets_for_path)
         }
-        Some(assets_for_path)
     }
 }
 
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index d459e51161..e8566b80b6 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -561,21 +561,11 @@ impl<'a> CompletionContext<'a> {
                         })().unwrap_or((None, None))
                     },
                     ast::RecordExprField(it) => {
-                        if let Some(expr) = it.expr() {
-                            cov_mark::hit!(expected_type_struct_field_with_leading_char);
-                            (
-                                self.sema.type_of_expr(&expr).map(TypeInfo::original),
-                                it.field_name().map(NameOrNameRef::NameRef),
-                            )
-                        } else {
-                            cov_mark::hit!(expected_type_struct_field_followed_by_comma);
-                            let ty = self.sema.resolve_record_field(&it)
-                                .map(|(_, _, ty)| ty);
-                            (
-                                ty,
-                                it.field_name().map(NameOrNameRef::NameRef),
-                            )
-                        }
+                        cov_mark::hit!(expected_type_struct_field_with_leading_char);
+                        (
+                            it.expr().as_ref().and_then(|e| self.sema.type_of_expr(e)).map(TypeInfo::original),
+                            it.field_name().map(NameOrNameRef::NameRef),
+                        )
                     },
                     ast::MatchExpr(it) => {
                         cov_mark::hit!(expected_type_match_arm_without_leading_char);
@@ -1018,20 +1008,6 @@ fn foo() {
         )
     }
 
-    #[test]
-    fn expected_type_struct_field_followed_by_comma() {
-        cov_mark::check!(expected_type_struct_field_followed_by_comma);
-        check_expected_type_and_name(
-            r#"
-struct Foo { a: u32 }
-fn foo() {
-    Foo { a: $0, };
-}
-"#,
-            expect![[r#"ty: u32, name: a"#]],
-        )
-    }
-
     #[test]
     fn expected_type_generic_struct_field() {
         check_expected_type_and_name(
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
index 6a087edc4f..d9618642c4 100644
--- a/crates/ide_completion/src/lib.rs
+++ b/crates/ide_completion/src/lib.rs
@@ -195,7 +195,7 @@ pub fn resolve_completion_edits(
         let items_with_name = items_locator::items_with_name(
             &ctx.sema,
             current_crate,
-            NameToImport::exact_case_sensitive(imported_name),
+            NameToImport::Exact(imported_name),
             items_locator::AssocItemSearch::Include,
             Some(items_locator::DEFAULT_QUERY_SEARCH_LIMIT.inner()),
         );
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs
index 7e78b7136c..1b9cb7ff51 100644
--- a/crates/ide_db/src/helpers.rs
+++ b/crates/ide_db/src/helpers.rs
@@ -305,24 +305,20 @@ pub fn lint_eq_or_in_group(lint: &str, lint_is: &str) -> bool {
     }
 }
 
-/// Parses the input token tree as comma separated plain paths.
+/// Parses the input token tree as comma separated paths.
 pub fn parse_tt_as_comma_sep_paths(input: ast::TokenTree) -> Option<Vec<ast::Path>> {
-    let r_paren = input.r_paren_token();
-    let tokens =
-        input.syntax().children_with_tokens().skip(1).map_while(|it| match it.into_token() {
-            // seeing a keyword means the attribute is unclosed so stop parsing here
-            Some(tok) if tok.kind().is_keyword() => None,
-            // don't include the right token tree parenthesis if it exists
-            tok @ Some(_) if tok == r_paren => None,
-            // only nodes that we can find are other TokenTrees, those are unexpected in this parse though
-            None => None,
-            Some(tok) => Some(tok),
-        });
+    let r_paren = input.r_paren_token()?;
+    let tokens = input
+        .syntax()
+        .children_with_tokens()
+        .skip(1)
+        .take_while(|it| it.as_token() != Some(&r_paren));
     let input_expressions = tokens.into_iter().group_by(|tok| tok.kind() == T![,]);
-    let paths = input_expressions
-        .into_iter()
-        .filter_map(|(is_sep, group)| (!is_sep).then(|| group))
-        .filter_map(|mut tokens| ast::Path::parse(&tokens.join("")).ok())
-        .collect();
-    Some(paths)
+    Some(
+        input_expressions
+            .into_iter()
+            .filter_map(|(is_sep, group)| (!is_sep).then(|| group))
+            .filter_map(|mut tokens| ast::Path::parse(&tokens.join("")).ok())
+            .collect::<Vec<ast::Path>>(),
+    )
 }
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs
index d4bc7c7cf8..ac0ccfa63f 100644
--- a/crates/ide_db/src/helpers/import_assets.rs
+++ b/crates/ide_db/src/helpers/import_assets.rs
@@ -68,23 +68,17 @@ pub struct FirstSegmentUnresolved {
 /// A name that will be used during item lookups.
 #[derive(Debug, Clone)]
 pub enum NameToImport {
-    /// Requires items with names that exactly match the given string, bool indicatse case-sensitivity.
-    Exact(String, bool),
+    /// Requires items with names that exactly match the given string, case-sensitive.
+    Exact(String),
     /// Requires items with names that case-insensitively contain all letters from the string,
     /// in the same order, but not necessary adjacent.
     Fuzzy(String),
 }
 
-impl NameToImport {
-    pub fn exact_case_sensitive(s: String) -> NameToImport {
-        NameToImport::Exact(s, true)
-    }
-}
-
 impl NameToImport {
     pub fn text(&self) -> &str {
         match self {
-            NameToImport::Exact(text, _) => text.as_str(),
+            NameToImport::Exact(text) => text.as_str(),
             NameToImport::Fuzzy(text) => text.as_str(),
         }
     }
@@ -146,7 +140,7 @@ impl ImportAssets {
         if let Some(_) = path.qualifier() {
             return None;
         }
-        let name = NameToImport::exact_case_sensitive(path.segment()?.name_ref()?.to_string());
+        let name = NameToImport::Exact(path.segment()?.name_ref()?.to_string());
         let candidate_node = attr.syntax().clone();
         Some(Self {
             import_candidate: ImportCandidate::Path(PathImportCandidate { qualifier: None, name }),
@@ -236,18 +230,6 @@ impl ImportAssets {
         self.search_for(sema, None)
     }
 
-    pub fn path_fuzzy_name_to_exact(&mut self, case_sensitive: bool) {
-        if let ImportCandidate::Path(PathImportCandidate { name: to_import, .. }) =
-            &mut self.import_candidate
-        {
-            let name = match to_import {
-                NameToImport::Fuzzy(name) => std::mem::take(name),
-                _ => return,
-            };
-            *to_import = NameToImport::Exact(name, case_sensitive);
-        }
-    }
-
     fn search_for(
         &self,
         sema: &Semantics<RootDatabase>,
@@ -280,7 +262,7 @@ impl ImportAssets {
         .into_iter()
         .filter(|import| import.import_path.len() > 1)
         .filter(|import| !scope_definitions.contains(&ScopeDef::from(import.item_to_import)))
-        .sorted_by(|a, b| a.import_path.cmp(&b.import_path))
+        .sorted_by_key(|import| import.import_path.clone())
         .collect()
     }
 
@@ -309,9 +291,9 @@ fn path_applicable_imports(
                 current_crate,
                 path_candidate.name.clone(),
                 // FIXME: we could look up assoc items by the input and propose those in completion,
-                // but that requires more preparation first:
+                // but that requries more preparation first:
                 // * store non-trait assoc items in import_map to fully enable this lookup
-                // * ensure that does not degrade the performance (benchmark it)
+                // * ensure that does not degrade the performance (bencmark it)
                 // * write more logic to check for corresponding trait presence requirement (we're unable to flyimport multiple item right now)
                 // * improve the associated completion item matching and/or scoring to ensure no noisy completions appear
                 //
@@ -581,9 +563,7 @@ impl ImportCandidate {
             Some(_) => None,
             None => Some(Self::TraitMethod(TraitImportCandidate {
                 receiver_ty: sema.type_of_expr(&method_call.receiver()?)?.adjusted(),
-                assoc_item_name: NameToImport::exact_case_sensitive(
-                    method_call.name_ref()?.to_string(),
-                ),
+                assoc_item_name: NameToImport::Exact(method_call.name_ref()?.to_string()),
             })),
         }
     }
@@ -595,7 +575,7 @@ impl ImportCandidate {
         path_import_candidate(
             sema,
             path.qualifier(),
-            NameToImport::exact_case_sensitive(path.segment()?.name_ref()?.to_string()),
+            NameToImport::Exact(path.segment()?.name_ref()?.to_string()),
         )
     }
 
@@ -609,7 +589,7 @@ impl ImportCandidate {
         }
         Some(ImportCandidate::Path(PathImportCandidate {
             qualifier: None,
-            name: NameToImport::exact_case_sensitive(name.to_string()),
+            name: NameToImport::Exact(name.to_string()),
         }))
     }
 
diff --git a/crates/ide_db/src/items_locator.rs b/crates/ide_db/src/items_locator.rs
index ca8266b6d2..e0dbe6caf0 100644
--- a/crates/ide_db/src/items_locator.rs
+++ b/crates/ide_db/src/items_locator.rs
@@ -50,18 +50,16 @@ pub fn items_with_name<'a>(
     });
 
     let (mut local_query, mut external_query) = match name {
-        NameToImport::Exact(exact_name, case_sensitive) => {
+        NameToImport::Exact(exact_name) => {
             let mut local_query = symbol_index::Query::new(exact_name.clone());
             local_query.exact();
 
             let external_query = import_map::Query::new(exact_name)
                 .name_only()
-                .search_mode(import_map::SearchMode::Equals);
+                .search_mode(import_map::SearchMode::Equals)
+                .case_sensitive();
 
-            (
-                local_query,
-                if case_sensitive { external_query.case_sensitive() } else { external_query },
-            )
+            (local_query, external_query)
         }
         NameToImport::Fuzzy(fuzzy_search_string) => {
             let mut local_query = symbol_index::Query::new(fuzzy_search_string.clone());
diff --git a/crates/ide_diagnostics/src/handlers/incorrect_case.rs b/crates/ide_diagnostics/src/handlers/incorrect_case.rs
index 6a78c08d44..3206a63a90 100644
--- a/crates/ide_diagnostics/src/handlers/incorrect_case.rs
+++ b/crates/ide_diagnostics/src/handlers/incorrect_case.rs
@@ -332,15 +332,6 @@ fn main() {
         check_diagnostics(
             r#"
 #![allow(non_snake_case)]
-#![allow(non_camel_case_types)]
-
-struct S {
-    fooBar: bool,
-}
-
-enum E {
-    fooBar,
-}
 
 mod F {
     fn CheckItWorksWithCrateAttr(BAD_NAME_HI: u8) {}
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs
index 1a56878fdb..b58b86b38d 100644
--- a/crates/mbe/src/lib.rs
+++ b/crates/mbe/src/lib.rs
@@ -10,7 +10,7 @@ mod parser;
 mod expander;
 mod syntax_bridge;
 mod tt_iter;
-mod to_parser_tokens;
+mod subtree_source;
 
 #[cfg(test)]
 mod benchmark;
diff --git a/crates/mbe/src/subtree_source.rs b/crates/mbe/src/subtree_source.rs
new file mode 100644
index 0000000000..6bdd787e30
--- /dev/null
+++ b/crates/mbe/src/subtree_source.rs
@@ -0,0 +1,174 @@
+//! Our parser is generic over the source of tokens it parses.
+//!
+//! This module defines tokens sourced from declarative macros.
+
+use parser::{Token, TokenSource};
+use syntax::{lex_single_syntax_kind, SmolStr, SyntaxKind, SyntaxKind::*, T};
+use tt::buffer::TokenBuffer;
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+struct TtToken {
+    tt: Token,
+    text: SmolStr,
+}
+
+pub(crate) struct SubtreeTokenSource {
+    cached: Vec<TtToken>,
+    curr: (Token, usize),
+}
+
+impl<'a> SubtreeTokenSource {
+    pub(crate) fn new(buffer: &TokenBuffer) -> SubtreeTokenSource {
+        let mut current = buffer.begin();
+        let mut cached = Vec::with_capacity(100);
+
+        while !current.eof() {
+            let cursor = current;
+            let tt = cursor.token_tree();
+
+            // Check if it is lifetime
+            if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(punct), _)) = tt {
+                if punct.char == '\'' {
+                    let next = cursor.bump();
+                    if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Ident(ident), _)) =
+                        next.token_tree()
+                    {
+                        let text = SmolStr::new("'".to_string() + &ident.text);
+                        cached.push(TtToken {
+                            tt: Token { kind: LIFETIME_IDENT, is_jointed_to_next: false },
+                            text,
+                        });
+                        current = next.bump();
+                        continue;
+                    } else {
+                        panic!("Next token must be ident : {:#?}", next.token_tree());
+                    }
+                }
+            }
+
+            current = match tt {
+                Some(tt::buffer::TokenTreeRef::Leaf(leaf, _)) => {
+                    cached.push(convert_leaf(leaf));
+                    cursor.bump()
+                }
+                Some(tt::buffer::TokenTreeRef::Subtree(subtree, _)) => {
+                    if let Some(d) = subtree.delimiter_kind() {
+                        cached.push(convert_delim(d, false));
+                    }
+                    cursor.subtree().unwrap()
+                }
+                None => match cursor.end() {
+                    Some(subtree) => {
+                        if let Some(d) = subtree.delimiter_kind() {
+                            cached.push(convert_delim(d, true));
+                        }
+                        cursor.bump()
+                    }
+                    None => continue,
+                },
+            };
+        }
+
+        let mut res = SubtreeTokenSource {
+            curr: (Token { kind: EOF, is_jointed_to_next: false }, 0),
+            cached,
+        };
+        res.curr = (res.token(0), 0);
+        res
+    }
+
+    fn token(&self, pos: usize) -> Token {
+        match self.cached.get(pos) {
+            Some(it) => it.tt,
+            None => Token { kind: EOF, is_jointed_to_next: false },
+        }
+    }
+}
+
+impl<'a> TokenSource for SubtreeTokenSource {
+    fn current(&self) -> Token {
+        self.curr.0
+    }
+
+    /// Lookahead n token
+    fn lookahead_nth(&self, n: usize) -> Token {
+        self.token(self.curr.1 + n)
+    }
+
+    /// bump cursor to next token
+    fn bump(&mut self) {
+        if self.current().kind == EOF {
+            return;
+        }
+        self.curr = (self.token(self.curr.1 + 1), self.curr.1 + 1);
+    }
+
+    /// Is the current token a specified keyword?
+    fn is_keyword(&self, kw: &str) -> bool {
+        match self.cached.get(self.curr.1) {
+            Some(t) => t.text == *kw,
+            None => false,
+        }
+    }
+}
+
+fn convert_delim(d: tt::DelimiterKind, closing: bool) -> TtToken {
+    let (kinds, texts) = match d {
+        tt::DelimiterKind::Parenthesis => ([T!['('], T![')']], "()"),
+        tt::DelimiterKind::Brace => ([T!['{'], T!['}']], "{}"),
+        tt::DelimiterKind::Bracket => ([T!['['], T![']']], "[]"),
+    };
+
+    let idx = closing as usize;
+    let kind = kinds[idx];
+    let text = &texts[idx..texts.len() - (1 - idx)];
+    TtToken { tt: Token { kind, is_jointed_to_next: false }, text: SmolStr::new(text) }
+}
+
+fn convert_literal(l: &tt::Literal) -> TtToken {
+    let is_negated = l.text.starts_with('-');
+    let inner_text = &l.text[if is_negated { 1 } else { 0 }..];
+
+    let kind = lex_single_syntax_kind(inner_text)
+        .map(|(kind, _error)| kind)
+        .filter(|kind| {
+            kind.is_literal() && (!is_negated || matches!(kind, FLOAT_NUMBER | INT_NUMBER))
+        })
+        .unwrap_or_else(|| panic!("Fail to convert given literal {:#?}", &l));
+
+    TtToken { tt: Token { kind, is_jointed_to_next: false }, text: l.text.clone() }
+}
+
+fn convert_ident(ident: &tt::Ident) -> TtToken {
+    let kind = match ident.text.as_ref() {
+        "true" => T![true],
+        "false" => T![false],
+        "_" => UNDERSCORE,
+        i if i.starts_with('\'') => LIFETIME_IDENT,
+        _ => SyntaxKind::from_keyword(ident.text.as_str()).unwrap_or(IDENT),
+    };
+
+    TtToken { tt: Token { kind, is_jointed_to_next: false }, text: ident.text.clone() }
+}
+
+fn convert_punct(p: tt::Punct) -> TtToken {
+    let kind = match SyntaxKind::from_char(p.char) {
+        None => panic!("{:#?} is not a valid punct", p),
+        Some(kind) => kind,
+    };
+
+    let text = {
+        let mut buf = [0u8; 4];
+        let s: &str = p.char.encode_utf8(&mut buf);
+        SmolStr::new(s)
+    };
+    TtToken { tt: Token { kind, is_jointed_to_next: p.spacing == tt::Spacing::Joint }, text }
+}
+
+fn convert_leaf(leaf: &tt::Leaf) -> TtToken {
+    match leaf {
+        tt::Leaf::Literal(l) => convert_literal(l),
+        tt::Leaf::Ident(ident) => convert_ident(ident),
+        tt::Leaf::Punct(punct) => convert_punct(*punct),
+    }
+}
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs
index 28a23f6be2..0b65fa171f 100644
--- a/crates/mbe/src/syntax_bridge.rs
+++ b/crates/mbe/src/syntax_bridge.rs
@@ -12,7 +12,7 @@ use syntax::{
 use tt::buffer::{Cursor, TokenBuffer};
 
 use crate::{
-    to_parser_tokens::to_parser_tokens, tt_iter::TtIter, ExpandError, ParserEntryPoint, TokenMap,
+    subtree_source::SubtreeTokenSource, tt_iter::TtIter, ExpandError, ParserEntryPoint, TokenMap,
 };
 
 /// Convert the syntax node to a `TokenTree` (what macro
@@ -56,9 +56,9 @@ pub fn token_tree_to_syntax_node(
         }
         _ => TokenBuffer::from_subtree(tt),
     };
-    let parser_tokens = to_parser_tokens(&buffer);
+    let mut token_source = SubtreeTokenSource::new(&buffer);
     let mut tree_sink = TtTreeSink::new(buffer.begin());
-    parser::parse(&parser_tokens, &mut tree_sink, entry_point);
+    parser::parse(&mut token_source, &mut tree_sink, entry_point);
     if tree_sink.roots.len() != 1 {
         return Err(ExpandError::ConversionError);
     }
diff --git a/crates/mbe/src/to_parser_tokens.rs b/crates/mbe/src/to_parser_tokens.rs
deleted file mode 100644
index 644689f432..0000000000
--- a/crates/mbe/src/to_parser_tokens.rs
+++ /dev/null
@@ -1,99 +0,0 @@
-//! Convert macro-by-example tokens which are specific to macro expansion into a
-//! format that works for our parser.
-
-use syntax::{lex_single_syntax_kind, SyntaxKind, SyntaxKind::*, T};
-use tt::buffer::TokenBuffer;
-
-pub(crate) fn to_parser_tokens(buffer: &TokenBuffer) -> parser::Tokens {
-    let mut res = parser::Tokens::default();
-
-    let mut current = buffer.begin();
-
-    while !current.eof() {
-        let cursor = current;
-        let tt = cursor.token_tree();
-
-        // Check if it is lifetime
-        if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(punct), _)) = tt {
-            if punct.char == '\'' {
-                let next = cursor.bump();
-                match next.token_tree() {
-                    Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Ident(_ident), _)) => {
-                        res.push(LIFETIME_IDENT);
-                        current = next.bump();
-                        continue;
-                    }
-                    _ => panic!("Next token must be ident : {:#?}", next.token_tree()),
-                }
-            }
-        }
-
-        current = match tt {
-            Some(tt::buffer::TokenTreeRef::Leaf(leaf, _)) => {
-                match leaf {
-                    tt::Leaf::Literal(lit) => {
-                        let is_negated = lit.text.starts_with('-');
-                        let inner_text = &lit.text[if is_negated { 1 } else { 0 }..];
-
-                        let kind = lex_single_syntax_kind(inner_text)
-                            .map(|(kind, _error)| kind)
-                            .filter(|kind| {
-                                kind.is_literal()
-                                    && (!is_negated || matches!(kind, FLOAT_NUMBER | INT_NUMBER))
-                            })
-                            .unwrap_or_else(|| panic!("Fail to convert given literal {:#?}", &lit));
-
-                        res.push(kind);
-                    }
-                    tt::Leaf::Ident(ident) => match ident.text.as_ref() {
-                        "_" => res.push(T![_]),
-                        i if i.starts_with('\'') => res.push(LIFETIME_IDENT),
-                        _ => match SyntaxKind::from_keyword(&ident.text) {
-                            Some(kind) => res.push(kind),
-                            None => {
-                                let contextual_keyword =
-                                    SyntaxKind::from_contextual_keyword(&ident.text)
-                                        .unwrap_or(SyntaxKind::IDENT);
-                                res.push_ident(contextual_keyword);
-                            }
-                        },
-                    },
-                    tt::Leaf::Punct(punct) => {
-                        let kind = SyntaxKind::from_char(punct.char)
-                            .unwrap_or_else(|| panic!("{:#?} is not a valid punct", punct));
-                        res.push(kind);
-                        if punct.spacing == tt::Spacing::Joint {
-                            res.was_joint();
-                        }
-                    }
-                }
-                cursor.bump()
-            }
-            Some(tt::buffer::TokenTreeRef::Subtree(subtree, _)) => {
-                if let Some(d) = subtree.delimiter_kind() {
-                    res.push(match d {
-                        tt::DelimiterKind::Parenthesis => T!['('],
-                        tt::DelimiterKind::Brace => T!['{'],
-                        tt::DelimiterKind::Bracket => T!['['],
-                    });
-                }
-                cursor.subtree().unwrap()
-            }
-            None => match cursor.end() {
-                Some(subtree) => {
-                    if let Some(d) = subtree.delimiter_kind() {
-                        res.push(match d {
-                            tt::DelimiterKind::Parenthesis => T![')'],
-                            tt::DelimiterKind::Brace => T!['}'],
-                            tt::DelimiterKind::Bracket => T![']'],
-                        })
-                    }
-                    cursor.bump()
-                }
-                None => continue,
-            },
-        };
-    }
-
-    res
-}
diff --git a/crates/mbe/src/tt_iter.rs b/crates/mbe/src/tt_iter.rs
index d05e84b0f0..ff0272808b 100644
--- a/crates/mbe/src/tt_iter.rs
+++ b/crates/mbe/src/tt_iter.rs
@@ -1,7 +1,7 @@
 //! A "Parser" structure for token trees. We use this when parsing a declarative
 //! macro definition into a list of patterns and templates.
 
-use crate::{to_parser_tokens::to_parser_tokens, ExpandError, ExpandResult, ParserEntryPoint};
+use crate::{subtree_source::SubtreeTokenSource, ExpandError, ExpandResult, ParserEntryPoint};
 
 use parser::TreeSink;
 use syntax::SyntaxKind;
@@ -116,10 +116,10 @@ impl<'a> TtIter<'a> {
         }
 
         let buffer = TokenBuffer::from_tokens(self.inner.as_slice());
-        let parser_tokens = to_parser_tokens(&buffer);
+        let mut src = SubtreeTokenSource::new(&buffer);
         let mut sink = OffsetTokenSink { cursor: buffer.begin(), error: false };
 
-        parser::parse(&parser_tokens, &mut sink, entry_point);
+        parser::parse(&mut src, &mut sink, entry_point);
 
         let mut err = if !sink.cursor.is_root() || sink.error {
             Some(err!("expected {:?}", entry_point))
diff --git a/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs
index 4b9c579a05..54eb96d84e 100644
--- a/crates/parser/src/grammar/expressions.rs
+++ b/crates/parser/src/grammar/expressions.rs
@@ -296,7 +296,10 @@ fn lhs(p: &mut Parser, r: Restrictions) -> Option<(CompletedMarker, BlockLike)>
         T![&] => {
             m = p.start();
             p.bump(T![&]);
-            if p.at_contextual_kw(T![raw]) && (p.nth_at(1, T![mut]) || p.nth_at(1, T![const])) {
+            if p.at(IDENT)
+                && p.at_contextual_kw("raw")
+                && (p.nth_at(1, T![mut]) || p.nth_at(1, T![const]))
+            {
                 p.bump_remap(T![raw]);
                 p.bump_any();
             } else {
diff --git a/crates/parser/src/grammar/items.rs b/crates/parser/src/grammar/items.rs
index 896efaf375..39be0e1a19 100644
--- a/crates/parser/src/grammar/items.rs
+++ b/crates/parser/src/grammar/items.rs
@@ -122,14 +122,14 @@ pub(super) fn opt_item(p: &mut Parser, m: Marker) -> Result<(), Marker> {
         has_mods = true;
         abi(p);
     }
-    if p.at_contextual_kw(T![auto]) && p.nth(1) == T![trait] {
+    if p.at(IDENT) && p.at_contextual_kw("auto") && p.nth(1) == T![trait] {
         p.bump_remap(T![auto]);
         has_mods = true;
     }
 
     // test default_item
     // default impl T for Foo {}
-    if p.at_contextual_kw(T![default]) {
+    if p.at(IDENT) && p.at_contextual_kw("default") {
         match p.nth(1) {
             T![fn] | T![type] | T![const] | T![impl] => {
                 p.bump_remap(T![default]);
@@ -176,7 +176,7 @@ pub(super) fn opt_item(p: &mut Parser, m: Marker) -> Result<(), Marker> {
 
     // test existential_type
     // existential type Foo: Fn() -> usize;
-    if p.at_contextual_kw(T![existential]) && p.nth(1) == T![type] {
+    if p.at(IDENT) && p.at_contextual_kw("existential") && p.nth(1) == T![type] {
         p.bump_remap(T![existential]);
         has_mods = true;
     }
@@ -224,10 +224,10 @@ fn opt_item_without_modifiers(p: &mut Parser, m: Marker) -> Result<(), Marker> {
         T![type] => type_alias(p, m),
         T![struct] => adt::strukt(p, m),
         T![enum] => adt::enum_(p, m),
-        IDENT if p.at_contextual_kw(T![union]) && p.nth(1) == IDENT => adt::union(p, m),
+        IDENT if p.at_contextual_kw("union") && p.nth(1) == IDENT => adt::union(p, m),
 
         T![macro] => macro_def(p, m),
-        IDENT if p.at_contextual_kw(T![macro_rules]) && p.nth(1) == BANG => macro_rules(p, m),
+        IDENT if p.at_contextual_kw("macro_rules") && p.nth(1) == BANG => macro_rules(p, m),
 
         T![const] if (la == IDENT || la == T![_] || la == T![mut]) => consts::konst(p, m),
         T![static] => consts::static_(p, m),
@@ -319,7 +319,7 @@ pub(crate) fn extern_item_list(p: &mut Parser) {
 }
 
 fn macro_rules(p: &mut Parser, m: Marker) {
-    assert!(p.at_contextual_kw(T![macro_rules]));
+    assert!(p.at_contextual_kw("macro_rules"));
     p.bump_remap(T![macro_rules]);
     p.expect(T![!]);
 
diff --git a/crates/parser/src/grammar/items/adt.rs b/crates/parser/src/grammar/items/adt.rs
index 83b7ff0578..c5bd5b14ba 100644
--- a/crates/parser/src/grammar/items/adt.rs
+++ b/crates/parser/src/grammar/items/adt.rs
@@ -10,7 +10,7 @@ pub(super) fn strukt(p: &mut Parser, m: Marker) {
 // test union_item
 // struct U { i: i32, f: f32 }
 pub(super) fn union(p: &mut Parser, m: Marker) {
-    assert!(p.at_contextual_kw(T![union]));
+    assert!(p.at_contextual_kw("union"));
     p.bump_remap(T![union]);
     struct_or_union(p, m, false);
 }
diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs
index 2e2d96d027..51ee969204 100644
--- a/crates/parser/src/lib.rs
+++ b/crates/parser/src/lib.rs
@@ -1,11 +1,8 @@
 //! The Rust parser.
 //!
-//! NOTE: The crate is undergoing refactors, don't believe everything the docs
-//! say :-)
-//!
 //! The parser doesn't know about concrete representation of tokens and syntax
-//! trees. Abstract [`TokenSource`] and [`TreeSink`] traits are used instead. As
-//! a consequence, this crate does not contain a lexer.
+//! trees. Abstract [`TokenSource`] and [`TreeSink`] traits are used instead.
+//! As a consequence, this crate does not contain a lexer.
 //!
 //! The [`Parser`] struct from the [`parser`] module is a cursor into the
 //! sequence of tokens.  Parsing routines use [`Parser`] to inspect current
@@ -23,15 +20,40 @@ mod syntax_kind;
 mod event;
 mod parser;
 mod grammar;
-mod tokens;
 
 pub(crate) use token_set::TokenSet;
 
-pub use crate::{syntax_kind::SyntaxKind, tokens::Tokens};
+pub use syntax_kind::SyntaxKind;
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct ParseError(pub Box<String>);
 
+/// `TokenSource` abstracts the source of the tokens parser operates on.
+///
+/// Hopefully this will allow us to treat text and token trees in the same way!
+pub trait TokenSource {
+    fn current(&self) -> Token;
+
+    /// Lookahead n token
+    fn lookahead_nth(&self, n: usize) -> Token;
+
+    /// bump cursor to next token
+    fn bump(&mut self);
+
+    /// Is the current token a specified keyword?
+    fn is_keyword(&self, kw: &str) -> bool;
+}
+
+/// `Token` abstracts the cursor of `TokenSource` operates on.
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub struct Token {
+    /// What is the current token?
+    pub kind: SyntaxKind,
+
+    /// Is the current token joined to the next one (`> >` vs `>>`).
+    pub is_jointed_to_next: bool,
+}
+
 /// `TreeSink` abstracts details of a particular syntax tree implementation.
 pub trait TreeSink {
     /// Adds new token to the current branch.
@@ -70,11 +92,15 @@ pub enum ParserEntryPoint {
 }
 
 /// Parse given tokens into the given sink as a rust file.
-pub fn parse_source_file(tokens: &Tokens, tree_sink: &mut dyn TreeSink) {
-    parse(tokens, tree_sink, ParserEntryPoint::SourceFile);
+pub fn parse_source_file(token_source: &mut dyn TokenSource, tree_sink: &mut dyn TreeSink) {
+    parse(token_source, tree_sink, ParserEntryPoint::SourceFile);
 }
 
-pub fn parse(tokens: &Tokens, tree_sink: &mut dyn TreeSink, entry_point: ParserEntryPoint) {
+pub fn parse(
+    token_source: &mut dyn TokenSource,
+    tree_sink: &mut dyn TreeSink,
+    entry_point: ParserEntryPoint,
+) {
     let entry_point: fn(&'_ mut parser::Parser) = match entry_point {
         ParserEntryPoint::SourceFile => grammar::entry_points::source_file,
         ParserEntryPoint::Path => grammar::entry_points::path,
@@ -92,7 +118,7 @@ pub fn parse(tokens: &Tokens, tree_sink: &mut dyn TreeSink, entry_point: ParserE
         ParserEntryPoint::Attr => grammar::entry_points::attr,
     };
 
-    let mut p = parser::Parser::new(tokens);
+    let mut p = parser::Parser::new(token_source);
     entry_point(&mut p);
     let events = p.finish();
     event::process(tree_sink, events);
@@ -115,9 +141,9 @@ impl Reparser {
     ///
     /// Tokens must start with `{`, end with `}` and form a valid brace
     /// sequence.
-    pub fn parse(self, tokens: &Tokens, tree_sink: &mut dyn TreeSink) {
+    pub fn parse(self, token_source: &mut dyn TokenSource, tree_sink: &mut dyn TreeSink) {
         let Reparser(r) = self;
-        let mut p = parser::Parser::new(tokens);
+        let mut p = parser::Parser::new(token_source);
         r(&mut p);
         let events = p.finish();
         event::process(tree_sink, events);
diff --git a/crates/parser/src/parser.rs b/crates/parser/src/parser.rs
index 4c891108a6..44c5f8e12f 100644
--- a/crates/parser/src/parser.rs
+++ b/crates/parser/src/parser.rs
@@ -7,10 +7,9 @@ use limit::Limit;
 
 use crate::{
     event::Event,
-    tokens::Tokens,
     ParseError,
     SyntaxKind::{self, EOF, ERROR, TOMBSTONE},
-    TokenSet, T,
+    TokenSet, TokenSource, T,
 };
 
 /// `Parser` struct provides the low-level API for
@@ -23,8 +22,7 @@ use crate::{
 /// "start expression, consume number literal,
 /// finish expression". See `Event` docs for more.
 pub(crate) struct Parser<'t> {
-    tokens: &'t Tokens,
-    pos: usize,
+    token_source: &'t mut dyn TokenSource,
     events: Vec<Event>,
     steps: Cell<u32>,
 }
@@ -32,8 +30,8 @@ pub(crate) struct Parser<'t> {
 static PARSER_STEP_LIMIT: Limit = Limit::new(15_000_000);
 
 impl<'t> Parser<'t> {
-    pub(super) fn new(tokens: &'t Tokens) -> Parser<'t> {
-        Parser { tokens, pos: 0, events: Vec::new(), steps: Cell::new(0) }
+    pub(super) fn new(token_source: &'t mut dyn TokenSource) -> Parser<'t> {
+        Parser { token_source, events: Vec::new(), steps: Cell::new(0) }
     }
 
     pub(crate) fn finish(self) -> Vec<Event> {
@@ -56,7 +54,7 @@ impl<'t> Parser<'t> {
         assert!(PARSER_STEP_LIMIT.check(steps as usize).is_ok(), "the parser seems stuck");
         self.steps.set(steps + 1);
 
-        self.tokens.kind(self.pos + n)
+        self.token_source.lookahead_nth(n).kind
     }
 
     /// Checks if the current token is `kind`.
@@ -92,7 +90,7 @@ impl<'t> Parser<'t> {
             T![<<=] => self.at_composite3(n, T![<], T![<], T![=]),
             T![>>=] => self.at_composite3(n, T![>], T![>], T![=]),
 
-            _ => self.tokens.kind(self.pos + n) == kind,
+            _ => self.token_source.lookahead_nth(n).kind == kind,
         }
     }
 
@@ -131,17 +129,25 @@ impl<'t> Parser<'t> {
     }
 
     fn at_composite2(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind) -> bool {
-        self.tokens.kind(self.pos + n) == k1
-            && self.tokens.kind(self.pos + n + 1) == k2
-            && self.tokens.is_joint(self.pos + n)
+        let t1 = self.token_source.lookahead_nth(n);
+        if t1.kind != k1 || !t1.is_jointed_to_next {
+            return false;
+        }
+        let t2 = self.token_source.lookahead_nth(n + 1);
+        t2.kind == k2
     }
 
     fn at_composite3(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind, k3: SyntaxKind) -> bool {
-        self.tokens.kind(self.pos + n) == k1
-            && self.tokens.kind(self.pos + n + 1) == k2
-            && self.tokens.kind(self.pos + n + 2) == k3
-            && self.tokens.is_joint(self.pos + n)
-            && self.tokens.is_joint(self.pos + n + 1)
+        let t1 = self.token_source.lookahead_nth(n);
+        if t1.kind != k1 || !t1.is_jointed_to_next {
+            return false;
+        }
+        let t2 = self.token_source.lookahead_nth(n + 1);
+        if t2.kind != k2 || !t2.is_jointed_to_next {
+            return false;
+        }
+        let t3 = self.token_source.lookahead_nth(n + 2);
+        t3.kind == k3
     }
 
     /// Checks if the current token is in `kinds`.
@@ -150,8 +156,8 @@ impl<'t> Parser<'t> {
     }
 
     /// Checks if the current token is contextual keyword with text `t`.
-    pub(crate) fn at_contextual_kw(&self, kw: SyntaxKind) -> bool {
-        self.tokens.contextual_kind(self.pos) == kw
+    pub(crate) fn at_contextual_kw(&self, kw: &str) -> bool {
+        self.token_source.is_keyword(kw)
     }
 
     /// Starts a new node in the syntax tree. All nodes and tokens
@@ -237,7 +243,10 @@ impl<'t> Parser<'t> {
     }
 
     fn do_bump(&mut self, kind: SyntaxKind, n_raw_tokens: u8) {
-        self.pos += n_raw_tokens as usize;
+        for _ in 0..n_raw_tokens {
+            self.token_source.bump();
+        }
+
         self.push_event(Event::Token { kind, n_raw_tokens });
     }
 
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs
index 601a5792af..99e7651906 100644
--- a/crates/parser/src/syntax_kind/generated.rs
+++ b/crates/parser/src/syntax_kind/generated.rs
@@ -334,18 +334,6 @@ impl SyntaxKind {
         };
         Some(kw)
     }
-    pub fn from_contextual_keyword(ident: &str) -> Option<SyntaxKind> {
-        let kw = match ident {
-            "auto" => AUTO_KW,
-            "default" => DEFAULT_KW,
-            "existential" => EXISTENTIAL_KW,
-            "union" => UNION_KW,
-            "raw" => RAW_KW,
-            "macro_rules" => MACRO_RULES_KW,
-            _ => return None,
-        };
-        Some(kw)
-    }
     pub fn from_char(c: char) -> Option<SyntaxKind> {
         let tok = match c {
             ';' => SEMICOLON,
diff --git a/crates/parser/src/tokens.rs b/crates/parser/src/tokens.rs
deleted file mode 100644
index 1c0672492d..0000000000
--- a/crates/parser/src/tokens.rs
+++ /dev/null
@@ -1,87 +0,0 @@
-//! Input for the parser -- a sequence of tokens.
-//!
-//! As of now, parser doesn't have access to the *text* of the tokens, and makes
-//! decisions based solely on their classification.
-
-use crate::SyntaxKind;
-
-#[allow(non_camel_case_types)]
-type bits = u64;
-
-/// Main input to the parser.
-///
-/// A sequence of tokens represented internally as a struct of arrays.
-#[derive(Default)]
-pub struct Tokens {
-    kind: Vec<SyntaxKind>,
-    joint: Vec<bits>,
-    contextual_kind: Vec<SyntaxKind>,
-}
-
-/// `pub` impl used by callers to create `Tokens`.
-impl Tokens {
-    #[inline]
-    pub fn push(&mut self, kind: SyntaxKind) {
-        self.push_impl(kind, SyntaxKind::EOF)
-    }
-    #[inline]
-    pub fn push_ident(&mut self, contextual_kind: SyntaxKind) {
-        self.push_impl(SyntaxKind::IDENT, contextual_kind)
-    }
-    /// Sets jointness for the last token we've pushed.
-    ///
-    /// This is a separate API rather than an argument to the `push` to make it
-    /// convenient both for textual and mbe tokens. With text, you know whether
-    /// the *previous* token was joint, with mbe, you know whether the *current*
-    /// one is joint. This API allows for styles of usage:
-    ///
-    /// ```
-    /// // In text:
-    /// tokens.was_joint(prev_joint);
-    /// tokens.push(curr);
-    ///
-    /// // In MBE:
-    /// token.push(curr);
-    /// tokens.push(curr_joint)
-    /// ```
-    #[inline]
-    pub fn was_joint(&mut self) {
-        let n = self.len() - 1;
-        let (idx, b_idx) = self.bit_index(n);
-        self.joint[idx] |= 1 << b_idx;
-    }
-    #[inline]
-    fn push_impl(&mut self, kind: SyntaxKind, contextual_kind: SyntaxKind) {
-        let idx = self.len();
-        if idx % (bits::BITS as usize) == 0 {
-            self.joint.push(0);
-        }
-        self.kind.push(kind);
-        self.contextual_kind.push(contextual_kind);
-    }
-}
-
-/// pub(crate) impl used by the parser to consume `Tokens`.
-impl Tokens {
-    pub(crate) fn kind(&self, idx: usize) -> SyntaxKind {
-        self.kind.get(idx).copied().unwrap_or(SyntaxKind::EOF)
-    }
-    pub(crate) fn contextual_kind(&self, idx: usize) -> SyntaxKind {
-        self.contextual_kind.get(idx).copied().unwrap_or(SyntaxKind::EOF)
-    }
-    pub(crate) fn is_joint(&self, n: usize) -> bool {
-        let (idx, b_idx) = self.bit_index(n);
-        self.joint[idx] & 1 << b_idx != 0
-    }
-}
-
-impl Tokens {
-    fn bit_index(&self, n: usize) -> (usize, usize) {
-        let idx = n / (bits::BITS as usize);
-        let b_idx = n % (bits::BITS as usize);
-        (idx, b_idx)
-    }
-    fn len(&self) -> usize {
-        self.kind.len()
-    }
-}
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index 526024a836..8705fb2115 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -13,7 +13,7 @@ doctest = false
 [dependencies]
 cov-mark = "2.0.0-pre.1"
 itertools = "0.10.0"
-rowan = "0.15.0"
+rowan = "0.14.0"
 rustc_lexer = { version = "725.0.0", package = "rustc-ap-rustc_lexer" }
 rustc-hash = "1.1.0"
 once_cell = "1.3.1"
diff --git a/crates/syntax/src/parsing.rs b/crates/syntax/src/parsing.rs
index 865e146482..a45f262877 100644
--- a/crates/syntax/src/parsing.rs
+++ b/crates/syntax/src/parsing.rs
@@ -2,10 +2,12 @@
 //! incremental reparsing.
 
 pub(crate) mod lexer;
+mod text_token_source;
 mod text_tree_sink;
 mod reparsing;
 
 use parser::SyntaxKind;
+use text_token_source::TextTokenSource;
 use text_tree_sink::TextTreeSink;
 
 use crate::{syntax_node::GreenNode, AstNode, SyntaxError, SyntaxNode};
@@ -13,12 +15,12 @@ use crate::{syntax_node::GreenNode, AstNode, SyntaxError, SyntaxNode};
 pub(crate) use crate::parsing::{lexer::*, reparsing::incremental_reparse};
 
 pub(crate) fn parse_text(text: &str) -> (GreenNode, Vec<SyntaxError>) {
-    let (lexer_tokens, lexer_errors) = tokenize(text);
-    let parser_tokens = to_parser_tokens(text, &lexer_tokens);
+    let (tokens, lexer_errors) = tokenize(text);
 
-    let mut tree_sink = TextTreeSink::new(text, &lexer_tokens);
+    let mut token_source = TextTokenSource::new(text, &tokens);
+    let mut tree_sink = TextTreeSink::new(text, &tokens);
 
-    parser::parse_source_file(&parser_tokens, &mut tree_sink);
+    parser::parse_source_file(&mut token_source, &mut tree_sink);
 
     let (tree, mut parser_errors) = tree_sink.finish();
     parser_errors.extend(lexer_errors);
@@ -31,52 +33,26 @@ pub(crate) fn parse_text_as<T: AstNode>(
     text: &str,
     entry_point: parser::ParserEntryPoint,
 ) -> Result<T, ()> {
-    let (lexer_tokens, lexer_errors) = tokenize(text);
+    let (tokens, lexer_errors) = tokenize(text);
     if !lexer_errors.is_empty() {
         return Err(());
     }
 
-    let parser_tokens = to_parser_tokens(text, &lexer_tokens);
-
-    let mut tree_sink = TextTreeSink::new(text, &lexer_tokens);
+    let mut token_source = TextTokenSource::new(text, &tokens);
+    let mut tree_sink = TextTreeSink::new(text, &tokens);
 
     // TextTreeSink assumes that there's at least some root node to which it can attach errors and
     // tokens. We arbitrarily give it a SourceFile.
     use parser::TreeSink;
     tree_sink.start_node(SyntaxKind::SOURCE_FILE);
-    parser::parse(&parser_tokens, &mut tree_sink, entry_point);
+    parser::parse(&mut token_source, &mut tree_sink, entry_point);
     tree_sink.finish_node();
 
-    let (tree, parser_errors, eof) = tree_sink.finish_eof();
-    if !parser_errors.is_empty() || !eof {
+    let (tree, parser_errors) = tree_sink.finish();
+    use parser::TokenSource;
+    if !parser_errors.is_empty() || token_source.current().kind != SyntaxKind::EOF {
         return Err(());
     }
 
     SyntaxNode::new_root(tree).first_child().and_then(T::cast).ok_or(())
 }
-
-pub(crate) fn to_parser_tokens(text: &str, lexer_tokens: &[lexer::Token]) -> ::parser::Tokens {
-    let mut off = 0;
-    let mut res = parser::Tokens::default();
-    let mut was_joint = false;
-    for t in lexer_tokens {
-        if t.kind.is_trivia() {
-            was_joint = false;
-        } else {
-            if t.kind == SyntaxKind::IDENT {
-                let token_text = &text[off..][..usize::from(t.len)];
-                let contextual_kw =
-                    SyntaxKind::from_contextual_keyword(token_text).unwrap_or(SyntaxKind::IDENT);
-                res.push_ident(contextual_kw);
-            } else {
-                if was_joint {
-                    res.was_joint();
-                }
-                res.push(t.kind);
-            }
-            was_joint = true;
-        }
-        off += usize::from(t.len);
-    }
-    res
-}
diff --git a/crates/syntax/src/parsing/reparsing.rs b/crates/syntax/src/parsing/reparsing.rs
index 62f39a9347..186cc9e74c 100644
--- a/crates/syntax/src/parsing/reparsing.rs
+++ b/crates/syntax/src/parsing/reparsing.rs
@@ -12,8 +12,8 @@ use text_edit::Indel;
 use crate::{
     parsing::{
         lexer::{lex_single_syntax_kind, tokenize, Token},
+        text_token_source::TextTokenSource,
         text_tree_sink::TextTreeSink,
-        to_parser_tokens,
     },
     syntax_node::{GreenNode, GreenToken, NodeOrToken, SyntaxElement, SyntaxNode},
     SyntaxError,
@@ -91,14 +91,14 @@ fn reparse_block(
     let (node, reparser) = find_reparsable_node(root, edit.delete)?;
     let text = get_text_after_edit(node.clone().into(), edit);
 
-    let (lexer_tokens, new_lexer_errors) = tokenize(&text);
-    if !is_balanced(&lexer_tokens) {
+    let (tokens, new_lexer_errors) = tokenize(&text);
+    if !is_balanced(&tokens) {
         return None;
     }
-    let parser_tokens = to_parser_tokens(&text, &lexer_tokens);
 
-    let mut tree_sink = TextTreeSink::new(&text, &lexer_tokens);
-    reparser.parse(&parser_tokens, &mut tree_sink);
+    let mut token_source = TextTokenSource::new(&text, &tokens);
+    let mut tree_sink = TextTreeSink::new(&text, &tokens);
+    reparser.parse(&mut token_source, &mut tree_sink);
 
     let (green, mut new_parser_errors) = tree_sink.finish();
     new_parser_errors.extend(new_lexer_errors);
diff --git a/crates/syntax/src/parsing/text_token_source.rs b/crates/syntax/src/parsing/text_token_source.rs
new file mode 100644
index 0000000000..11dfc63a65
--- /dev/null
+++ b/crates/syntax/src/parsing/text_token_source.rs
@@ -0,0 +1,82 @@
+//! See `TextTokenSource` docs.
+
+use parser::TokenSource;
+
+use crate::{parsing::lexer::Token, SyntaxKind::EOF, TextRange, TextSize};
+
+/// Implementation of `parser::TokenSource` that takes tokens from source code text.
+pub(crate) struct TextTokenSource<'t> {
+    text: &'t str,
+    /// token and its start position (non-whitespace/comment tokens)
+    /// ```non-rust
+    ///  struct Foo;
+    ///  ^------^--^-
+    ///  |      |    \________
+    ///  |      \____         \
+    ///  |           \         |
+    ///  (struct, 0) (Foo, 7) (;, 10)
+    /// ```
+    /// `[(struct, 0), (Foo, 7), (;, 10)]`
+    token_offset_pairs: Vec<(Token, TextSize)>,
+
+    /// Current token and position
+    curr: (parser::Token, usize),
+}
+
+impl<'t> TokenSource for TextTokenSource<'t> {
+    fn current(&self) -> parser::Token {
+        self.curr.0
+    }
+
+    fn lookahead_nth(&self, n: usize) -> parser::Token {
+        mk_token(self.curr.1 + n, &self.token_offset_pairs)
+    }
+
+    fn bump(&mut self) {
+        if self.curr.0.kind == EOF {
+            return;
+        }
+
+        let pos = self.curr.1 + 1;
+        self.curr = (mk_token(pos, &self.token_offset_pairs), pos);
+    }
+
+    fn is_keyword(&self, kw: &str) -> bool {
+        self.token_offset_pairs
+            .get(self.curr.1)
+            .map_or(false, |(token, offset)| &self.text[TextRange::at(*offset, token.len)] == kw)
+    }
+}
+
+fn mk_token(pos: usize, token_offset_pairs: &[(Token, TextSize)]) -> parser::Token {
+    let (kind, is_jointed_to_next) = match token_offset_pairs.get(pos) {
+        Some((token, offset)) => (
+            token.kind,
+            token_offset_pairs
+                .get(pos + 1)
+                .map_or(false, |(_, next_offset)| offset + token.len == *next_offset),
+        ),
+        None => (EOF, false),
+    };
+    parser::Token { kind, is_jointed_to_next }
+}
+
+impl<'t> TextTokenSource<'t> {
+    /// Generate input from tokens(expect comment and whitespace).
+    pub(crate) fn new(text: &'t str, raw_tokens: &'t [Token]) -> TextTokenSource<'t> {
+        let token_offset_pairs: Vec<_> = raw_tokens
+            .iter()
+            .filter_map({
+                let mut len = 0.into();
+                move |token| {
+                    let pair = if token.kind.is_trivia() { None } else { Some((*token, len)) };
+                    len += token.len;
+                    pair
+                }
+            })
+            .collect();
+
+        let first = mk_token(0, &token_offset_pairs);
+        TextTokenSource { text, token_offset_pairs, curr: (first, 0) }
+    }
+}
diff --git a/crates/syntax/src/parsing/text_tree_sink.rs b/crates/syntax/src/parsing/text_tree_sink.rs
index c1792199fd..8c1de92048 100644
--- a/crates/syntax/src/parsing/text_tree_sink.rs
+++ b/crates/syntax/src/parsing/text_tree_sink.rs
@@ -104,7 +104,7 @@ impl<'a> TextTreeSink<'a> {
         }
     }
 
-    pub(super) fn finish_eof(mut self) -> (GreenNode, Vec<SyntaxError>, bool) {
+    pub(super) fn finish(mut self) -> (GreenNode, Vec<SyntaxError>) {
         match mem::replace(&mut self.state, State::Normal) {
             State::PendingFinish => {
                 self.eat_trivias();
@@ -113,15 +113,7 @@ impl<'a> TextTreeSink<'a> {
             State::PendingStart | State::Normal => unreachable!(),
         }
 
-        let (node, errors) = self.inner.finish_raw();
-        let is_eof = self.token_pos == self.tokens.len();
-
-        (node, errors, is_eof)
-    }
-
-    pub(super) fn finish(self) -> (GreenNode, Vec<SyntaxError>) {
-        let (node, errors, _eof) = self.finish_eof();
-        (node, errors)
+        self.inner.finish_raw()
     }
 
     fn eat_trivias(&mut self) {


Test patch:
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs
index 5a79979533..08947e4ecd 100644
--- a/crates/ide_assists/src/tests.rs
+++ b/crates/ide_assists/src/tests.rs
@@ -222,10 +222,10 @@ fn assist_order_field_struct() {
     let mut assists = assists.iter();
 
     assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)");
-    assert_eq!(assists.next().expect("expected assist").label, "Generate `Deref` impl using `bar`");
     assert_eq!(assists.next().expect("expected assist").label, "Generate a getter method");
     assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method");
     assert_eq!(assists.next().expect("expected assist").label, "Generate a setter method");
+    assert_eq!(assists.next().expect("expected assist").label, "Generate `Deref` impl using `bar`");
     assert_eq!(assists.next().expect("expected assist").label, "Add `#[derive]`");
 }
 
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs
index c67e15b2ce..e30f98bcd1 100644
--- a/crates/ide_assists/src/tests/generated.rs
+++ b/crates/ide_assists/src/tests/generated.rs
@@ -839,36 +839,6 @@ struct Point {
     )
 }
 
-#[test]
-fn doctest_generate_documentation_template() {
-    check_doc_test(
-        "generate_documentation_template",
-        r#####"
-pub fn my_$0func(a: i32, b: i32) -> Result<(), std::io::Error> {
-    unimplemented!()
-}
-"#####,
-        r#####"
-/// .
-///
-/// # Examples
-///
-/// ```
-/// use test::my_func;
-///
-/// assert_eq!(my_func(a, b), );
-/// ```
-///
-/// # Errors
-///
-/// This function will return an error if .
-pub fn my_func(a: i32, b: i32) -> Result<(), std::io::Error> {
-    unimplemented!()
-}
-"#####,
-    )
-}
-
 #[test]
 fn doctest_generate_enum_as_method() {
     check_doc_test(
diff --git a/crates/ide_completion/src/tests/attribute.rs b/crates/ide_completion/src/tests/attribute.rs
index c90d4966f3..c3dce61e7d 100644
--- a/crates/ide_completion/src/tests/attribute.rs
+++ b/crates/ide_completion/src/tests/attribute.rs
@@ -831,20 +831,6 @@ mod lint {
             r#"#[allow(rustdoc::bare_urls)] struct Test;"#,
         );
     }
-
-    #[test]
-    fn lint_unclosed() {
-        check_edit(
-            "deprecated",
-            r#"#[allow(dep$0 struct Test;"#,
-            r#"#[allow(deprecated struct Test;"#,
-        );
-        check_edit(
-            "bare_urls",
-            r#"#[allow(rustdoc::$0 struct Test;"#,
-            r#"#[allow(rustdoc::bare_urls struct Test;"#,
-        );
-    }
 }
 
 mod repr {
diff --git a/crates/ide_completion/src/tests/flyimport.rs b/crates/ide_completion/src/tests/flyimport.rs
index fbef6d9937..1f96e122f9 100644
--- a/crates/ide_completion/src/tests/flyimport.rs
+++ b/crates/ide_completion/src/tests/flyimport.rs
@@ -96,29 +96,25 @@ fn main() {
 
 #[test]
 fn short_paths_are_ignored() {
-    cov_mark::check!(flyimport_exact_on_short_path);
+    cov_mark::check!(ignore_short_input_for_path);
 
     check(
         r#"
 //- /lib.rs crate:dep
-pub struct Bar;
-pub struct Rcar;
-pub struct Rc;
+pub struct FirstStruct;
 pub mod some_module {
-    pub struct Bar;
-    pub struct Rcar;
-    pub struct Rc;
+    pub struct SecondStruct;
+    pub struct ThirdStruct;
 }
 
 //- /main.rs crate:main deps:dep
+use dep::{FirstStruct, some_module::SecondStruct};
+
 fn main() {
-    rc$0
+    t$0
 }
 "#,
-        expect![[r#"
-            st Rc (use dep::Rc)
-            st Rc (use dep::some_module::Rc)
-        "#]],
+        expect![[r#""#]],
     );
 }
 
@@ -776,7 +772,7 @@ mod foo {
 }
 
 fn main() {
-    TES$0
+    TE$0
 }"#,
         expect![[r#"
         ct TEST_CONST (use foo::TEST_CONST)
@@ -793,7 +789,7 @@ mod foo {
 }
 
 fn main() {
-    tes$0
+    te$0
 }"#,
         expect![[r#"
         ct TEST_CONST (use foo::TEST_CONST)
@@ -850,7 +846,7 @@ struct Foo {
     some_field: i32,
 }
 fn main() {
-    let _ = Foo { some_field: som$0 };
+    let _ = Foo { some_field: so$0 };
 }
 "#,
         expect![[r#"
diff --git a/crates/syntax/src/tests/sourcegen_ast.rs b/crates/syntax/src/tests/sourcegen_ast.rs
index c66edadc3c..dcd813bbe0 100644
--- a/crates/syntax/src/tests/sourcegen_ast.rs
+++ b/crates/syntax/src/tests/sourcegen_ast.rs
@@ -359,10 +359,6 @@ fn generate_syntax_kinds(grammar: KindsSrc<'_>) -> String {
     let full_keywords =
         full_keywords_values.iter().map(|kw| format_ident!("{}_KW", to_upper_snake_case(kw)));
 
-    let contextual_keywords_values = &grammar.contextual_keywords;
-    let contextual_keywords =
-        contextual_keywords_values.iter().map(|kw| format_ident!("{}_KW", to_upper_snake_case(kw)));
-
     let all_keywords_values =
         grammar.keywords.iter().chain(grammar.contextual_keywords.iter()).collect::<Vec<_>>();
     let all_keywords_idents = all_keywords_values.iter().map(|kw| format_ident!("{}", kw));
@@ -432,14 +428,6 @@ fn generate_syntax_kinds(grammar: KindsSrc<'_>) -> String {
                 Some(kw)
             }
 
-            pub fn from_contextual_keyword(ident: &str) -> Option<SyntaxKind> {
-                let kw = match ident {
-                    #(#contextual_keywords_values => #contextual_keywords,)*
-                    _ => return None,
-                };
-                Some(kw)
-            }
-
             pub fn from_char(c: char) -> Option<SyntaxKind> {
                 let tok = match c {
                     #(#single_byte_tokens_values => #single_byte_tokens,)*