Repository: rust-lang/rust-analyzer
Pull request #12188 (2022-05-07 13:23:39)

fix: Allow auto importing starting segments of use items

Veykril: @bors r+
bors: :pushpin: Commit e0b46eca5422f5d686151251fb2ad407245d63f5 has been approved by `Veykril`

<!-- @bors r=Veykril e0b46eca5422f5d686151251fb2ad407245d63f5 -->
<!-- homu: {"type":"Approved","sha":"e0b46eca5422f5d686151251fb2ad407245d63f5","approver":"Veykril"} -->
bors: :hourglass: Testing commit e0b46eca5422f5d686151251fb2ad407245d63f5 with merge d650ea931b8cb9c1b03b14f31003b4bf0e429889...
<!-- homu: {"type":"BuildStarted","head_sha":"e0b46eca5422f5d686151251fb2ad407245d63f5","merge_sha":"d650ea931b8cb9c1b03b14f31003b4bf0e429889"} -->
bors: :broken_heart: Test failed - [checks-actions](https://github.com/rust-lang/rust-analyzer/runs/6334715865?check_suite_focus=true)
<!-- homu: {"type":"BuildFailed","builder_url":"https://github.com/rust-lang/rust-analyzer/runs/6334715865?check_suite_focus=true","builder_name":"checks-actions"} -->

Patch:
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index 84662aa277..e8303ec40f 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/body/lower.rs
@@ -972,7 +972,7 @@ impl From<ast::LiteralKind> for Literal {
                 }
             }
             LiteralKind::FloatNumber(lit) => {
-                let ty = lit.suffix().and_then(|s| BuiltinFloat::from_suffix(&s));
+                let ty = lit.suffix().and_then(BuiltinFloat::from_suffix);
                 Literal::Float(Default::default(), ty)
             }
             LiteralKind::ByteString(bs) => {
diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs
index 687c549748..8d2352f06e 100644
--- a/crates/hir-expand/src/builtin_fn_macro.rs
+++ b/crates/hir-expand/src/builtin_fn_macro.rs
@@ -4,7 +4,10 @@ use base_db::{AnchoredPath, Edition, FileId};
 use cfg::CfgExpr;
 use either::Either;
 use mbe::{parse_exprs_with_sep, parse_to_token_tree};
-use syntax::{ast, SmolStr};
+use syntax::{
+    ast::{self, AstToken},
+    SmolStr,
+};
 
 use crate::{db::AstDatabase, name, quote, ExpandError, ExpandResult, MacroCallId, MacroCallLoc};
 
@@ -355,7 +358,14 @@ fn unreachable_expand(
 }
 
 fn unquote_str(lit: &tt::Literal) -> Option<String> {
-    let token = ast::make::literal(&lit.to_string()).as_string()?;
+    let lit = ast::make::tokens::literal(&lit.to_string());
+    let token = ast::String::cast(lit)?;
+    token.value().map(|it| it.into_owned())
+}
+
+fn unquote_byte_string(lit: &tt::Literal) -> Option<Vec<u8>> {
+    let lit = ast::make::tokens::literal(&lit.to_string());
+    let token = ast::ByteString::cast(lit)?;
     token.value().map(|it| it.into_owned())
 }
 
@@ -432,16 +442,12 @@ fn concat_bytes_expand(
     for (i, t) in tt.token_trees.iter().enumerate() {
         match t {
             tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => {
-                let lit = ast::make::literal(&lit.to_string());
-                match lit.kind() {
-                    ast::LiteralKind::ByteString(s) => {
-                        s.value()
-                            .unwrap_or_default()
-                            .into_iter()
-                            .for_each(|x| bytes.push(x.to_string()));
-                    }
-                    ast::LiteralKind::Byte(_) => {
-                        bytes.push(lit.to_string());
+                let token = ast::make::tokens::literal(&lit.to_string());
+                match token.kind() {
+                    syntax::SyntaxKind::BYTE => bytes.push(token.text().to_string()),
+                    syntax::SyntaxKind::BYTE_STRING => {
+                        let components = unquote_byte_string(lit).unwrap_or_else(Vec::new);
+                        components.into_iter().for_each(|x| bytes.push(x.to_string()));
                     }
                     _ => {
                         err.get_or_insert(mbe::ExpandError::UnexpectedToken.into());
@@ -475,10 +481,10 @@ fn concat_bytes_expand_subtree(
     for (ti, tt) in tree.token_trees.iter().enumerate() {
         match tt {
             tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => {
-                let lit = ast::make::literal(&lit.to_string());
+                let lit = ast::make::tokens::literal(&lit.to_string());
                 match lit.kind() {
-                    ast::LiteralKind::IntNumber(_) | ast::LiteralKind::Byte(_) => {
-                        bytes.push(lit.to_string());
+                    syntax::SyntaxKind::BYTE | syntax::SyntaxKind::INT_NUMBER => {
+                        bytes.push(lit.text().to_string())
                     }
                     _ => {
                         return Err(mbe::ExpandError::UnexpectedToken.into());
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 496b4168c0..c91fbce5ea 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -88,7 +88,7 @@ pub use crate::{
         UnresolvedModule, UnresolvedProcMacro,
     },
     has_source::HasSource,
-    semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits},
+    semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo},
 };
 
 // Be careful with these re-exports.
@@ -1370,12 +1370,11 @@ impl Function {
         None
     }
 
-    pub fn has_self_param(self, db: &dyn HirDatabase) -> bool {
-        db.function_data(self.id).has_self_param()
-    }
-
     pub fn self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> {
-        self.has_self_param(db).then(|| SelfParam { func: self.id })
+        if !db.function_data(self.id).has_self_param() {
+            return None;
+        }
+        Some(SelfParam { func: self.id })
     }
 
     pub fn assoc_fn_params(self, db: &dyn HirDatabase) -> Vec<Param> {
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index d887dae99c..8b565ef1fa 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -1370,10 +1370,10 @@ impl<'a> SemanticsScope<'a> {
         &self.resolver
     }
 
-    /// Note: `VisibleTraits` should be treated as an opaque type, passed into `Type
-    pub fn visible_traits(&self) -> VisibleTraits {
+    /// Note: `FxHashSet<TraitId>` should be treated as an opaque type, passed into `Type
+    pub fn visible_traits(&self) -> FxHashSet<TraitId> {
         let resolver = &self.resolver;
-        VisibleTraits(resolver.traits_in_scope(self.db.upcast()))
+        resolver.traits_in_scope(self.db.upcast())
     }
 
     pub fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
@@ -1424,5 +1424,3 @@ impl<'a> SemanticsScope<'a> {
         )
     }
 }
-
-pub struct VisibleTraits(pub FxHashSet<TraitId>);
diff --git a/crates/ide-assists/src/handlers/auto_import.rs b/crates/ide-assists/src/handlers/auto_import.rs
index 874563c6f7..0a0dafb35e 100644
--- a/crates/ide-assists/src/handlers/auto_import.rs
+++ b/crates/ide-assists/src/handlers/auto_import.rs
@@ -373,19 +373,6 @@ mod baz {
         );
     }
 
-    #[test]
-    fn not_applicable_in_import_statements() {
-        check_assist_not_applicable(
-            auto_import,
-            r"
-            use PubStruct$0;
-
-            pub mod PubMod {
-                pub struct PubStruct;
-            }",
-        );
-    }
-
     #[test]
     fn function_import() {
         check_assist(
@@ -1121,4 +1108,43 @@ struct Foo;
 "#,
         );
     }
+
+    #[test]
+    fn works_in_use_start() {
+        check_assist(
+            auto_import,
+            r#"
+mod bar {
+    pub mod foo {
+        pub struct Foo;
+    }
+}
+use foo$0::Foo;
+"#,
+            r#"
+mod bar {
+    pub mod foo {
+        pub struct Foo;
+    }
+}
+use bar::foo;
+use foo::Foo;
+"#,
+        );
+    }
+
+    #[test]
+    fn not_applicable_in_non_start_use() {
+        check_assist_not_applicable(
+            auto_import,
+            r"
+mod bar {
+    pub mod foo {
+        pub struct Foo;
+    }
+}
+use foo::Foo$0;
+",
+        );
+    }
 }
diff --git a/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs b/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs
index 82a67fb51d..711b914561 100644
--- a/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs
+++ b/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs
@@ -148,13 +148,14 @@ fn is_ref_and_impls_iter_method(
     let ty = sema.type_of_expr(&expr_behind_ref)?.adjusted();
     let scope = sema.scope(iterable.syntax())?;
     let krate = scope.krate();
+    let traits_in_scope = scope.visible_traits();
     let iter_trait = FamousDefs(sema, krate).core_iter_Iterator()?;
 
     let has_wanted_method = ty
         .iterate_method_candidates(
             sema.db,
             &scope,
-            &scope.visible_traits().0,
+            &traits_in_scope,
             None,
             Some(&wanted_method),
             |func| {
diff --git a/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs b/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs
index 062b12a50b..1ad1cea43f 100644
--- a/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs
+++ b/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs
@@ -95,14 +95,10 @@ fn get_impl_method(
 
     let scope = ctx.sema.scope(impl_.syntax())?;
     let ty = impl_def.self_ty(db);
-    ty.iterate_method_candidates(
-        db,
-        &scope,
-        &scope.visible_traits().0,
-        None,
-        Some(fn_name),
-        |func| Some(func),
-    )
+    let traits_in_scope = scope.visible_traits();
+    ty.iterate_method_candidates(db, &scope, &traits_in_scope, None, Some(fn_name), |func| {
+        Some(func)
+    })
 }
 
 #[cfg(test)]
diff --git a/crates/ide-assists/src/handlers/qualify_path.rs b/crates/ide-assists/src/handlers/qualify_path.rs
index 5deb60f57b..8d2293d224 100644
--- a/crates/ide-assists/src/handlers/qualify_path.rs
+++ b/crates/ide-assists/src/handlers/qualify_path.rs
@@ -381,20 +381,6 @@ pub mod PubMod {
         check_assist_not_applicable(qualify_path, r#"PubStruct$0"#);
     }
 
-    #[test]
-    fn not_applicable_in_import_statements() {
-        check_assist_not_applicable(
-            qualify_path,
-            r#"
-use PubStruct$0;
-
-pub mod PubMod {
-    pub struct PubStruct;
-}
-"#,
-        );
-    }
-
     #[test]
     fn qualify_function() {
         check_assist(
@@ -1270,4 +1256,42 @@ struct Foo;
 "#,
         );
     }
+
+    #[test]
+    fn works_in_use_start() {
+        check_assist(
+            qualify_path,
+            r#"
+mod bar {
+    pub mod foo {
+        pub struct Foo;
+    }
+}
+use foo$0::Foo;
+"#,
+            r#"
+mod bar {
+    pub mod foo {
+        pub struct Foo;
+    }
+}
+use bar::foo::Foo;
+"#,
+        );
+    }
+
+    #[test]
+    fn not_applicable_in_non_start_use() {
+        check_assist_not_applicable(
+            qualify_path,
+            r"
+mod bar {
+    pub mod foo {
+        pub struct Foo;
+    }
+}
+use foo::Foo$0;
+",
+        );
+    }
 }
diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs
index 931b92dec3..36cee48aed 100644
--- a/crates/ide-completion/src/completions.rs
+++ b/crates/ide-completion/src/completions.rs
@@ -2,32 +2,32 @@
 
 pub(crate) mod attribute;
 pub(crate) mod dot;
-pub(crate) mod expr;
 pub(crate) mod extern_abi;
 pub(crate) mod flyimport;
 pub(crate) mod fn_param;
 pub(crate) mod format_string;
-pub(crate) mod item_list;
 pub(crate) mod keyword;
 pub(crate) mod lifetime;
 pub(crate) mod mod_;
 pub(crate) mod pattern;
 pub(crate) mod postfix;
+pub(crate) mod qualified_path;
 pub(crate) mod record;
 pub(crate) mod snippet;
 pub(crate) mod trait_impl;
-pub(crate) mod r#type;
+pub(crate) mod unqualified_path;
 pub(crate) mod use_;
 pub(crate) mod vis;
 
 use std::iter;
 
-use hir::{db::HirDatabase, known, ScopeDef};
+use hir::{db::HirDatabase, known, HirDisplay, ScopeDef};
 use ide_db::SymbolKind;
 
 use crate::{
     context::Visible,
     item::Builder,
+    patterns::{ImmediateLocation, TypeAnnotation},
     render::{
         const_::render_const,
         function::{render_fn, render_method},
@@ -35,6 +35,7 @@ use crate::{
         macro_::render_macro,
         pattern::{render_struct_pat, render_variant_pat},
         render_field, render_resolution, render_resolution_simple, render_tuple_field,
+        render_type_inference,
         type_alias::{render_type_alias, render_type_alias_with_eq},
         union_literal::render_union_literal,
         RenderContext,
@@ -399,3 +400,19 @@ fn enum_variants_with_paths(
         }
     }
 }
+
+pub(crate) fn inferred_type(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
+    use TypeAnnotation::*;
+    let pat = match &ctx.completion_location {
+        Some(ImmediateLocation::TypeAnnotation(t)) => t,
+        _ => return None,
+    };
+    let x = match pat {
+        Let(pat) | FnParam(pat) => ctx.sema.type_of_pat(pat.as_ref()?),
+        Const(exp) | RetType(exp) => ctx.sema.type_of_expr(exp.as_ref()?),
+    }?
+    .adjusted();
+    let ty_string = x.display_source_code(ctx.db, ctx.module.into()).ok()?;
+    acc.add(render_type_inference(ty_string, ctx));
+    None
+}
diff --git a/crates/ide-completion/src/completions/attribute.rs b/crates/ide-completion/src/completions/attribute.rs
index fef177d2dd..09ba672a14 100644
--- a/crates/ide-completion/src/completions/attribute.rs
+++ b/crates/ide-completion/src/completions/attribute.rs
@@ -71,7 +71,7 @@ pub(crate) fn complete_known_attribute_input(
 pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) {
     let (is_absolute_path, qualifier, is_inner, annotated_item_kind) = match ctx.path_context {
         Some(PathCompletionCtx {
-            kind: PathKind::Attr { kind, annotated_item_kind },
+            kind: Some(PathKind::Attr { kind, annotated_item_kind }),
             is_absolute_path,
             ref qualifier,
             ..
diff --git a/crates/ide-completion/src/completions/attribute/derive.rs b/crates/ide-completion/src/completions/attribute/derive.rs
index d827d781a3..4f524dd73b 100644
--- a/crates/ide-completion/src/completions/attribute/derive.rs
+++ b/crates/ide-completion/src/completions/attribute/derive.rs
@@ -13,7 +13,10 @@ use crate::{
 pub(crate) fn complete_derive(acc: &mut Completions, ctx: &CompletionContext) {
     let (qualifier, is_absolute_path) = match ctx.path_context {
         Some(PathCompletionCtx {
-            kind: PathKind::Derive, ref qualifier, is_absolute_path, ..
+            kind: Some(PathKind::Derive),
+            ref qualifier,
+            is_absolute_path,
+            ..
         }) => (qualifier, is_absolute_path),
         _ => return,
     };
diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs
index 33af76c710..01a5f856c1 100644
--- a/crates/ide-completion/src/completions/dot.rs
+++ b/crates/ide-completion/src/completions/dot.rs
@@ -2,11 +2,7 @@
 
 use ide_db::FxHashSet;
 
-use crate::{
-    context::{CompletionContext, PathCompletionCtx, PathKind},
-    patterns::ImmediateLocation,
-    Completions,
-};
+use crate::{context::CompletionContext, patterns::ImmediateLocation, Completions};
 
 /// Complete dot accesses, i.e. fields or methods.
 pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
@@ -38,16 +34,9 @@ fn complete_undotted_self(acc: &mut Completions, ctx: &CompletionContext) {
     if !ctx.config.enable_self_on_the_fly {
         return;
     }
-    match ctx.path_context {
-        Some(PathCompletionCtx {
-            is_absolute_path: false,
-            qualifier: None,
-            kind: PathKind::Expr { .. },
-            ..
-        }) if !ctx.is_path_disallowed() => {}
-        _ => return,
+    if ctx.is_non_trivial_path() || ctx.is_path_disallowed() || !ctx.expects_expression() {
+        return;
     }
-
     if let Some(func) = ctx.function_def.as_ref().and_then(|fn_| ctx.sema.to_def(fn_)) {
         if let Some(self_) = func.self_param(ctx.db) {
             let ty = self_.ty(ctx.db);
@@ -89,10 +78,18 @@ fn complete_methods(
     mut f: impl FnMut(hir::Function),
 ) {
     let mut seen_methods = FxHashSet::default();
+    let mut traits_in_scope = ctx.scope.visible_traits();
+
+    // Remove drop from the environment as calling `Drop::drop` is not allowed
+    if let Some(drop_trait) = ctx.famous_defs().core_ops_Drop() {
+        cov_mark::hit!(dot_remove_drop_trait);
+        traits_in_scope.remove(&drop_trait.into());
+    }
+
     receiver.iterate_method_candidates(
         ctx.db,
         &ctx.scope,
-        &ctx.traits_in_scope().0,
+        &traits_in_scope,
         Some(ctx.module),
         None,
         |func| {
@@ -761,6 +758,7 @@ fn main() {
 
     #[test]
     fn postfix_drop_completion() {
+        cov_mark::check!(dot_remove_drop_trait);
         cov_mark::check!(postfix_drop_completion);
         check_edit(
             "drop",
@@ -787,24 +785,4 @@ fn main() {
 ",
         )
     }
-
-    #[test]
-    fn tuple_index_completion() {
-        check(
-            r#"
-struct I;
-impl I {
-    fn i_method(&self) {}
-}
-struct S((), I);
-
-fn f(s: S) {
-    s.1.$0
-}
-"#,
-            expect![[r#"
-                me i_method() fn(&self)
-            "#]],
-        );
-    }
 }
diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs
deleted file mode 100644
index b7e2b88657..0000000000
--- a/crates/ide-completion/src/completions/expr.rs
+++ /dev/null
@@ -1,179 +0,0 @@
-//! Completion of names from the current scope in expression position.
-
-use hir::ScopeDef;
-use ide_db::FxHashSet;
-
-use crate::{
-    context::{PathCompletionCtx, PathKind, PathQualifierCtx},
-    CompletionContext, Completions,
-};
-
-pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext) {
-    let _p = profile::span("complete_expr_path");
-    if ctx.is_path_disallowed() {
-        return;
-    }
-
-    let (&is_absolute_path, qualifier) = match &ctx.path_context {
-        Some(PathCompletionCtx {
-            kind: PathKind::Expr { .. },
-            is_absolute_path,
-            qualifier,
-            ..
-        }) => (is_absolute_path, qualifier),
-        _ => return,
-    };
-
-    let scope_def_applicable = |def| {
-        use hir::{GenericParam::*, ModuleDef::*};
-        match def {
-            ScopeDef::GenericParam(LifetimeParam(_)) | ScopeDef::Label(_) => false,
-            // Don't suggest attribute macros and derives.
-            ScopeDef::ModuleDef(Macro(mac)) => mac.is_fn_like(ctx.db),
-            _ => true,
-        }
-    };
-
-    match qualifier {
-        Some(PathQualifierCtx { is_infer_qualifier, resolution, .. }) => {
-            if *is_infer_qualifier {
-                ctx.traits_in_scope()
-                    .0
-                    .into_iter()
-                    .flat_map(|it| hir::Trait::from(it).items(ctx.sema.db))
-                    .for_each(|item| add_assoc_item(acc, ctx, item));
-                return;
-            }
-            let resolution = match resolution {
-                Some(it) => it,
-                None => return,
-            };
-            // Add associated types on type parameters and `Self`.
-            ctx.scope.assoc_type_shorthand_candidates(resolution, |_, alias| {
-                acc.add_type_alias(ctx, alias);
-                None::<()>
-            });
-            match resolution {
-                hir::PathResolution::Def(hir::ModuleDef::Module(module)) => {
-                    let module_scope = module.scope(ctx.db, Some(ctx.module));
-                    for (name, def) in module_scope {
-                        if scope_def_applicable(def) {
-                            acc.add_resolution(ctx, name, def);
-                        }
-                    }
-                }
-
-                hir::PathResolution::Def(
-                    def @ (hir::ModuleDef::Adt(_)
-                    | hir::ModuleDef::TypeAlias(_)
-                    | hir::ModuleDef::BuiltinType(_)),
-                ) => {
-                    if let &hir::ModuleDef::Adt(hir::Adt::Enum(e)) = def {
-                        add_enum_variants(acc, ctx, e);
-                    }
-                    let ty = match def {
-                        hir::ModuleDef::Adt(adt) => adt.ty(ctx.db),
-                        hir::ModuleDef::TypeAlias(a) => {
-                            let ty = a.ty(ctx.db);
-                            if let Some(hir::Adt::Enum(e)) = ty.as_adt() {
-                                cov_mark::hit!(completes_variant_through_alias);
-                                add_enum_variants(acc, ctx, e);
-                            }
-                            ty
-                        }
-                        hir::ModuleDef::BuiltinType(builtin) => {
-                            cov_mark::hit!(completes_primitive_assoc_const);
-                            builtin.ty(ctx.db)
-                        }
-                        _ => unreachable!(),
-                    };
-
-                    // XXX: For parity with Rust bug #22519, this does not complete Ty::AssocType.
-                    // (where AssocType is defined on a trait, not an inherent impl)
-
-                    ty.iterate_path_candidates(
-                        ctx.db,
-                        &ctx.scope,
-                        &ctx.traits_in_scope().0,
-                        Some(ctx.module),
-                        None,
-                        |item| {
-                            add_assoc_item(acc, ctx, item);
-                            None::<()>
-                        },
-                    );
-
-                    // Iterate assoc types separately
-                    ty.iterate_assoc_items(ctx.db, ctx.krate, |item| {
-                        if let hir::AssocItem::TypeAlias(ty) = item {
-                            acc.add_type_alias(ctx, ty)
-                        }
-                        None::<()>
-                    });
-                }
-                hir::PathResolution::Def(hir::ModuleDef::Trait(t)) => {
-                    // Handles `Trait::assoc` as well as `<Ty as Trait>::assoc`.
-                    for item in t.items(ctx.db) {
-                        add_assoc_item(acc, ctx, item);
-                    }
-                }
-                hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_) => {
-                    let ty = match resolution {
-                        hir::PathResolution::TypeParam(param) => param.ty(ctx.db),
-                        hir::PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db),
-                        _ => return,
-                    };
-
-                    if let Some(hir::Adt::Enum(e)) = ty.as_adt() {
-                        add_enum_variants(acc, ctx, e);
-                    }
-                    let mut seen = FxHashSet::default();
-                    ty.iterate_path_candidates(
-                        ctx.db,
-                        &ctx.scope,
-                        &ctx.traits_in_scope().0,
-                        Some(ctx.module),
-                        None,
-                        |item| {
-                            // We might iterate candidates of a trait multiple times here, so deduplicate
-                            // them.
-                            if seen.insert(item) {
-                                add_assoc_item(acc, ctx, item);
-                            }
-                            None::<()>
-                        },
-                    );
-                }
-                _ => (),
-            }
-        }
-        None if is_absolute_path => acc.add_crate_roots(ctx),
-        None => {
-            acc.add_nameref_keywords_with_colon(ctx);
-            if let Some(hir::Adt::Enum(e)) =
-                ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())
-            {
-                super::enum_variants_with_paths(acc, ctx, e, |acc, ctx, variant, path| {
-                    acc.add_qualified_enum_variant(ctx, variant, path)
-                });
-            }
-            ctx.process_all_names(&mut |name, def| {
-                if scope_def_applicable(def) {
-                    acc.add_resolution(ctx, name, def);
-                }
-            });
-        }
-    }
-}
-
-fn add_assoc_item(acc: &mut Completions, ctx: &CompletionContext, item: hir::AssocItem) {
-    match item {
-        hir::AssocItem::Function(func) => acc.add_function(ctx, func, None),
-        hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
-        hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
-    }
-}
-
-fn add_enum_variants(acc: &mut Completions, ctx: &CompletionContext, e: hir::Enum) {
-    e.variants(ctx.db).into_iter().for_each(|variant| acc.add_enum_variant(ctx, variant, None));
-}
diff --git a/crates/ide-completion/src/completions/flyimport.rs b/crates/ide-completion/src/completions/flyimport.rs
index 7a90126916..7a02a3b333 100644
--- a/crates/ide-completion/src/completions/flyimport.rs
+++ b/crates/ide-completion/src/completions/flyimport.rs
@@ -161,12 +161,12 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
             (_, ItemInNs::Types(hir::ModuleDef::Module(_))) => true,
             // and so are macros(except for attributes)
             (
-                PathKind::Expr { .. } | PathKind::Type | PathKind::Item { .. } | PathKind::Pat,
+                PathKind::Expr | PathKind::Type | PathKind::Item | PathKind::Pat,
                 ItemInNs::Macros(mac),
             ) => mac.is_fn_like(ctx.db),
-            (PathKind::Item { .. }, _) => true,
+            (PathKind::Item, _) => true,
 
-            (PathKind::Expr { .. }, ItemInNs::Types(_) | ItemInNs::Values(_)) => true,
+            (PathKind::Expr, ItemInNs::Types(_) | ItemInNs::Values(_)) => true,
 
             (PathKind::Pat, ItemInNs::Types(_)) => true,
             (PathKind::Pat, ItemInNs::Values(def)) => matches!(def, hir::ModuleDef::Const(_)),
diff --git a/crates/ide-completion/src/completions/item_list.rs b/crates/ide-completion/src/completions/item_list.rs
deleted file mode 100644
index df03120dfe..0000000000
--- a/crates/ide-completion/src/completions/item_list.rs
+++ /dev/null
@@ -1,51 +0,0 @@
-//! Completion of paths and keywords at item list position.
-
-use crate::{
-    completions::module_or_fn_macro,
-    context::{PathCompletionCtx, PathKind, PathQualifierCtx},
-    CompletionContext, Completions,
-};
-
-pub(crate) fn complete_item_list(acc: &mut Completions, ctx: &CompletionContext) {
-    let _p = profile::span("complete_item_list");
-    if ctx.is_path_disallowed() || ctx.has_unfinished_impl_or_trait_prev_sibling() {
-        return;
-    }
-
-    let (&is_absolute_path, qualifier) = match &ctx.path_context {
-        Some(PathCompletionCtx {
-            kind: PathKind::Item { .. },
-            is_absolute_path,
-            qualifier,
-            ..
-        }) => (is_absolute_path, qualifier),
-        _ => return,
-    };
-
-    match qualifier {
-        Some(PathQualifierCtx { resolution, is_super_chain, .. }) => {
-            if let Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) = resolution {
-                for (name, def) in module.scope(ctx.db, Some(ctx.module)) {
-                    if let Some(def) = module_or_fn_macro(ctx.db, def) {
-                        acc.add_resolution(ctx, name, def);
-                    }
-                }
-            }
-
-            if *is_super_chain {
-                acc.add_keyword(ctx, "super::");
-            }
-        }
-        None if is_absolute_path => {
-            acc.add_crate_roots(ctx);
-        }
-        None => {
-            ctx.process_all_names(&mut |name, def| {
-                if let Some(def) = module_or_fn_macro(ctx.db, def) {
-                    acc.add_resolution(ctx, name, def);
-                }
-            });
-            acc.add_nameref_keywords_with_colon(ctx);
-        }
-    }
-}
diff --git a/crates/ide-completion/src/completions/keyword.rs b/crates/ide-completion/src/completions/keyword.rs
index 557992d14a..07cfad81d2 100644
--- a/crates/ide-completion/src/completions/keyword.rs
+++ b/crates/ide-completion/src/completions/keyword.rs
@@ -40,7 +40,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
     if let Some(PathKind::Vis { .. }) = ctx.path_kind() {
         return;
     }
-    if ctx.has_unfinished_impl_or_trait_prev_sibling() {
+    if ctx.has_impl_or_trait_prev_sibling() {
         add_keyword("where", "where");
         if ctx.has_impl_prev_sibling() {
             add_keyword("for", "for");
@@ -125,11 +125,9 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
     }
 
     let (can_be_stmt, in_loop_body) = match ctx.path_context {
-        Some(PathCompletionCtx {
-            is_absolute_path: false,
-            kind: PathKind::Expr { in_block_expr, in_loop_body, .. },
-            ..
-        }) => (in_block_expr, in_loop_body),
+        Some(PathCompletionCtx { is_absolute_path: false, can_be_stmt, in_loop_body, .. }) => {
+            (can_be_stmt, in_loop_body)
+        }
         _ => return,
     };
 
diff --git a/crates/ide-completion/src/completions/pattern.rs b/crates/ide-completion/src/completions/pattern.rs
index 963ee30959..16666eba45 100644
--- a/crates/ide-completion/src/completions/pattern.rs
+++ b/crates/ide-completion/src/completions/pattern.rs
@@ -163,11 +163,12 @@ fn pattern_path_completion(
                         _ => return,
                     };
 
+                    let traits_in_scope = ctx.scope.visible_traits();
                     let mut seen = FxHashSet::default();
                     ty.iterate_path_candidates(
                         ctx.db,
                         &ctx.scope,
-                        &ctx.scope.visible_traits().0,
+                        &traits_in_scope,
                         Some(ctx.module),
                         None,
                         |item| {
@@ -195,13 +196,12 @@ fn pattern_path_completion(
         // qualifier can only be none here if we are in a TuplePat or RecordPat in which case special characters have to follow the path
         None if *is_absolute_path => acc.add_crate_roots(ctx),
         None => {
+            cov_mark::hit!(unqualified_path_only_modules_in_import);
             ctx.process_all_names(&mut |name, res| {
-                // FIXME: properly filter here
-                if let ScopeDef::ModuleDef(_) = res {
+                if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res {
                     acc.add_resolution(ctx, name, res);
                 }
             });
-
             acc.add_nameref_keywords_with_colon(ctx);
         }
     }
diff --git a/crates/ide-completion/src/completions/postfix.rs b/crates/ide-completion/src/completions/postfix.rs
index cc2a443700..7b936f0ff7 100644
--- a/crates/ide-completion/src/completions/postfix.rs
+++ b/crates/ide-completion/src/completions/postfix.rs
@@ -5,7 +5,7 @@ mod format_like;
 use hir::{Documentation, HasAttrs};
 use ide_db::{imports::insert_use::ImportScope, ty_filter::TryEnum, SnippetCap};
 use syntax::{
-    ast::{self, AstNode, LiteralKind},
+    ast::{self, AstNode, AstToken},
     SyntaxKind::{EXPR_STMT, STMT_LIST},
     TextRange, TextSize,
 };
@@ -191,7 +191,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
     }
 
     if let ast::Expr::Literal(literal) = dot_receiver.clone() {
-        if let LiteralKind::String(literal_text) = literal.kind() {
+        if let Some(literal_text) = ast::String::cast(literal.token()) {
             add_format_like_completions(acc, ctx, &dot_receiver, cap, &literal_text);
         }
     }
diff --git a/crates/ide-completion/src/completions/qualified_path.rs b/crates/ide-completion/src/completions/qualified_path.rs
new file mode 100644
index 0000000000..ad92820bca
--- /dev/null
+++ b/crates/ide-completion/src/completions/qualified_path.rs
@@ -0,0 +1,685 @@
+//! Completion of paths, i.e. `some::prefix::$0`.
+
+use hir::{ScopeDef, Trait};
+use ide_db::FxHashSet;
+use syntax::ast;
+
+use crate::{
+    completions::module_or_fn_macro,
+    context::{PathCompletionCtx, PathKind},
+    patterns::ImmediateLocation,
+    CompletionContext, Completions,
+};
+
+pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) {
+    if ctx.is_path_disallowed() || ctx.has_impl_or_trait_prev_sibling() {
+        return;
+    }
+    if ctx.pattern_ctx.is_some() {
+        return;
+    }
+    let (qualifier, kind) = match ctx.path_context {
+        // let ... else, syntax would come in really handy here right now
+        Some(PathCompletionCtx { qualifier: Some(ref qualifier), kind, .. }) => (qualifier, kind),
+        _ => return,
+    };
+    let traits_in_scope = |ctx: &CompletionContext| {
+        let mut traits_in_scope = ctx.scope.visible_traits();
+        if let Some(drop) = ctx.famous_defs().core_ops_Drop() {
+            traits_in_scope.remove(&drop.into());
+        }
+        traits_in_scope
+    };
+
+    // special case `<_>::$0` as this doesn't resolve to anything.
+    if qualifier.path.qualifier().is_none() {
+        if matches!(
+            qualifier.path.segment().and_then(|it| it.kind()),
+            Some(ast::PathSegmentKind::Type {
+                type_ref: Some(ast::Type::InferType(_)),
+                trait_ref: None,
+            })
+        ) {
+            cov_mark::hit!(completion_type_anchor_empty);
+            traits_in_scope(ctx)
+                .into_iter()
+                .flat_map(|it| Trait::from(it).items(ctx.sema.db))
+                .for_each(|item| add_assoc_item(acc, ctx, item));
+            return;
+        }
+    }
+
+    let resolution = match &qualifier.resolution {
+        Some(res) => res,
+        None => return,
+    };
+
+    match ctx.completion_location {
+        Some(ImmediateLocation::ItemList | ImmediateLocation::Trait | ImmediateLocation::Impl) => {
+            if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
+                for (name, def) in module.scope(ctx.db, Some(ctx.module)) {
+                    if let Some(def) = module_or_fn_macro(ctx.db, def) {
+                        acc.add_resolution(ctx, name, def);
+                    }
+                }
+            }
+            return;
+        }
+        _ => (),
+    }
+
+    match kind {
+        Some(
+            PathKind::Pat
+            | PathKind::Attr { .. }
+            | PathKind::Vis { .. }
+            | PathKind::Use
+            | PathKind::Derive,
+        ) => {
+            return;
+        }
+        _ => {
+            // Add associated types on type parameters and `Self`.
+            ctx.scope.assoc_type_shorthand_candidates(resolution, |_, alias| {
+                acc.add_type_alias(ctx, alias);
+                None::<()>
+            });
+        }
+    }
+
+    match resolution {
+        hir::PathResolution::Def(hir::ModuleDef::Module(module)) => {
+            let module_scope = module.scope(ctx.db, Some(ctx.module));
+            for (name, def) in module_scope {
+                let add_resolution = match def {
+                    // Don't suggest attribute macros and derives.
+                    ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => mac.is_fn_like(ctx.db),
+                    // no values in type places
+                    ScopeDef::ModuleDef(
+                        hir::ModuleDef::Function(_)
+                        | hir::ModuleDef::Variant(_)
+                        | hir::ModuleDef::Static(_),
+                    )
+                    | ScopeDef::Local(_) => !ctx.expects_type(),
+                    // unless its a constant in a generic arg list position
+                    ScopeDef::ModuleDef(hir::ModuleDef::Const(_)) => {
+                        !ctx.expects_type() || ctx.expects_generic_arg()
+                    }
+                    _ => true,
+                };
+
+                if add_resolution {
+                    acc.add_resolution(ctx, name, def);
+                }
+            }
+        }
+        hir::PathResolution::Def(
+            def @ (hir::ModuleDef::Adt(_)
+            | hir::ModuleDef::TypeAlias(_)
+            | hir::ModuleDef::BuiltinType(_)),
+        ) => {
+            if let &hir::ModuleDef::Adt(hir::Adt::Enum(e)) = def {
+                add_enum_variants(acc, ctx, e);
+            }
+            let ty = match def {
+                hir::ModuleDef::Adt(adt) => adt.ty(ctx.db),
+                hir::ModuleDef::TypeAlias(a) => {
+                    let ty = a.ty(ctx.db);
+                    if let Some(hir::Adt::Enum(e)) = ty.as_adt() {
+                        cov_mark::hit!(completes_variant_through_alias);
+                        add_enum_variants(acc, ctx, e);
+                    }
+                    ty
+                }
+                hir::ModuleDef::BuiltinType(builtin) => {
+                    cov_mark::hit!(completes_primitive_assoc_const);
+                    builtin.ty(ctx.db)
+                }
+                _ => unreachable!(),
+            };
+
+            // XXX: For parity with Rust bug #22519, this does not complete Ty::AssocType.
+            // (where AssocType is defined on a trait, not an inherent impl)
+
+            let traits_in_scope = traits_in_scope(ctx);
+            ty.iterate_path_candidates(
+                ctx.db,
+                &ctx.scope,
+                &traits_in_scope,
+                Some(ctx.module),
+                None,
+                |item| {
+                    add_assoc_item(acc, ctx, item);
+                    None::<()>
+                },
+            );
+
+            // Iterate assoc types separately
+            ty.iterate_assoc_items(ctx.db, ctx.krate, |item| {
+                if let hir::AssocItem::TypeAlias(ty) = item {
+                    acc.add_type_alias(ctx, ty)
+                }
+                None::<()>
+            });
+        }
+        hir::PathResolution::Def(hir::ModuleDef::Trait(t)) => {
+            // Handles `Trait::assoc` as well as `<Ty as Trait>::assoc`.
+            for item in t.items(ctx.db) {
+                add_assoc_item(acc, ctx, item);
+            }
+        }
+        hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_) => {
+            let ty = match resolution {
+                hir::PathResolution::TypeParam(param) => param.ty(ctx.db),
+                hir::PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db),
+                _ => return,
+            };
+
+            if let Some(hir::Adt::Enum(e)) = ty.as_adt() {
+                add_enum_variants(acc, ctx, e);
+            }
+            let traits_in_scope = traits_in_scope(ctx);
+            let mut seen = FxHashSet::default();
+            ty.iterate_path_candidates(
+                ctx.db,
+                &ctx.scope,
+                &traits_in_scope,
+                Some(ctx.module),
+                None,
+                |item| {
+                    // We might iterate candidates of a trait multiple times here, so deduplicate
+                    // them.
+                    if seen.insert(item) {
+                        add_assoc_item(acc, ctx, item);
+                    }
+                    None::<()>
+                },
+            );
+        }
+        _ => {}
+    }
+}
+
+fn add_assoc_item(acc: &mut Completions, ctx: &CompletionContext, item: hir::AssocItem) {
+    match item {
+        hir::AssocItem::Function(func) if !ctx.expects_type() => acc.add_function(ctx, func, None),
+        hir::AssocItem::Const(ct) if !ctx.expects_type() || ctx.expects_generic_arg() => {
+            acc.add_const(ctx, ct)
+        }
+        hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
+        _ => (),
+    }
+}
+
+fn add_enum_variants(acc: &mut Completions, ctx: &CompletionContext, e: hir::Enum) {
+    if ctx.expects_type() {
+        return;
+    }
+    e.variants(ctx.db).into_iter().for_each(|variant| acc.add_enum_variant(ctx, variant, None));
+}
+
+#[cfg(test)]
+mod tests {
+    use expect_test::{expect, Expect};
+
+    use crate::tests::{check_edit, completion_list_no_kw};
+
+    fn check(ra_fixture: &str, expect: Expect) {
+        let actual = completion_list_no_kw(ra_fixture);
+        expect.assert_eq(&actual);
+    }
+
+    #[test]
+    fn associated_item_visibility() {
+        check(
+            r#"
+//- /lib.rs crate:lib new_source_root:library
+pub struct S;
+
+impl S {
+    pub fn public_method() { }
+    fn private_method() { }
+    pub type PublicType = u32;
+    type PrivateType = u32;
+    pub const PUBLIC_CONST: u32 = 1;
+    const PRIVATE_CONST: u32 = 1;
+}
+
+//- /main.rs crate:main deps:lib new_source_root:local
+fn foo() { let _ = lib::S::$0 }
+"#,
+            expect![[r#"
+                ct PUBLIC_CONST    pub const PUBLIC_CONST: u32
+                fn public_method() fn()
+                ta PublicType      pub type PublicType = u32
+            "#]],
+        );
+    }
+
+    #[test]
+    fn completes_union_associated_method() {
+        check(
+            r#"
+union U {};
+impl U { fn m() { } }
+
+fn foo() { let _ = U::$0 }
+"#,
+            expect![[r#"
+                fn m() fn()
+            "#]],
+        );
+    }
+
+    #[test]
+    fn completes_trait_associated_method_1() {
+        check(
+            r#"
+trait Trait { fn m(); }
+
+fn foo() { let _ = Trait::$0 }
+"#,
+            expect![[r#"
+                fn m() (as Trait) fn()
+            "#]],
+        );
+    }
+
+    #[test]
+    fn completes_trait_associated_method_2() {
+        check(
+            r#"
+trait Trait { fn m(); }
+
+struct S;
+impl Trait for S {}
+
+fn foo() { let _ = S::$0 }
+"#,
+            expect![[r#"
+                fn m() (as Trait) fn()
+            "#]],
+        );
+    }
+
+    #[test]
+    fn completes_trait_associated_method_3() {
+        check(
+            r#"
+trait Trait { fn m(); }
+
+struct S;
+impl Trait for S {}
+
+fn foo() { let _ = <S as Trait>::$0 }
+"#,
+            expect![[r#"
+                fn m() (as Trait) fn()
+            "#]],
+        );
+    }
+
+    #[test]
+    fn completes_ty_param_assoc_ty() {
+        check(
+            r#"
+trait Super {
+    type Ty;
+    const CONST: u8;
+    fn func() {}
+    fn method(&self) {}
+}
+
+trait Sub: Super {
+    type SubTy;
+    const C2: ();
+    fn subfunc() {}
+    fn submethod(&self) {}
+}
+
+fn foo<T: Sub>() { T::$0 }
+"#,
+            expect![[r#"
+                ct C2 (as Sub)           const C2: ()
+                ct CONST (as Super)      const CONST: u8
+                fn func() (as Super)     fn()
+                fn subfunc() (as Sub)    fn()
+                ta SubTy (as Sub)        type SubTy
+                ta Ty (as Super)         type Ty
+                me method(…) (as Super)  fn(&self)
+                me submethod(…) (as Sub) fn(&self)
+            "#]],
+        );
+    }
+
+    #[test]
+    fn completes_self_param_assoc_ty() {
+        check(
+            r#"
+trait Super {
+    type Ty;
+    const CONST: u8 = 0;
+    fn func() {}
+    fn method(&self) {}
+}
+
+trait Sub: Super {
+    type SubTy;
+    const C2: () = ();
+    fn subfunc() {}
+    fn submethod(&self) {}
+}
+
+struct Wrap<T>(T);
+impl<T> Super for Wrap<T> {}
+impl<T> Sub for Wrap<T> {
+    fn subfunc() {
+        // Should be able to assume `Self: Sub + Super`
+        Self::$0
+    }
+}
+"#,
+            expect![[r#"
+                ct C2 (as Sub)           const C2: ()
+                ct CONST (as Super)      const CONST: u8
+                fn func() (as Super)     fn()
+                fn subfunc() (as Sub)    fn()
+                ta SubTy (as Sub)        type SubTy
+                ta Ty (as Super)         type Ty
+                me method(…) (as Super)  fn(&self)
+                me submethod(…) (as Sub) fn(&self)
+            "#]],
+        );
+    }
+
+    #[test]
+    fn completes_type_alias() {
+        check(
+            r#"
+struct S;
+impl S { fn foo() {} }
+type T = S;
+impl T { fn bar() {} }
+
+fn main() { T::$0; }
+"#,
+            expect![[r#"
+                fn bar() fn()
+                fn foo() fn()
+            "#]],
+        );
+    }
+
+    #[test]
+    fn completes_qualified_macros() {
+        check(
+            r#"
+#[macro_export]
+macro_rules! foo { () => {} }
+
+fn main() { let _ = crate::$0 }
+"#,
+            expect![[r#"
+                fn main()  fn()
+                ma foo!(…) macro_rules! foo
+            "#]],
+        );
+    }
+
+    #[test]
+    fn does_not_complete_non_fn_macros() {
+        check(
+            r#"
+mod m {
+    #[rustc_builtin_macro]
+    pub macro Clone {}
+}
+
+fn f() {m::$0}
+"#,
+            expect![[r#""#]],
+        );
+        check(
+            r#"
+mod m {
+    #[rustc_builtin_macro]
+    pub macro bench {}
+}
+
+fn f() {m::$0}
+"#,
+            expect![[r#""#]],
+        );
+    }
+
+    #[test]
+    fn completes_reexported_items_under_correct_name() {
+        check(
+            r#"
+fn foo() { self::m::$0 }
+
+mod m {
+    pub use super::p::wrong_fn as right_fn;
+    pub use super::p::WRONG_CONST as RIGHT_CONST;
+    pub use super::p::WrongType as RightType;
+}
+mod p {
+    fn wrong_fn() {}
+    const WRONG_CONST: u32 = 1;
+    struct WrongType {};
+}
+"#,
+            expect![[r#"
+                ct RIGHT_CONST
+                fn right_fn()  fn()
+                st RightType
+            "#]],
+        );
+
+        check_edit(
+            "RightType",
+            r#"
+fn foo() { self::m::$0 }
+
+mod m {
+    pub use super::p::wrong_fn as right_fn;
+    pub use super::p::WRONG_CONST as RIGHT_CONST;
+    pub use super::p::WrongType as RightType;
+}
+mod p {
+    fn wrong_fn() {}
+    const WRONG_CONST: u32 = 1;
+    struct WrongType {};
+}
+"#,
+            r#"
+fn foo() { self::m::RightType }
+
+mod m {
+    pub use super::p::wrong_fn as right_fn;
+    pub use super::p::WRONG_CONST as RIGHT_CONST;
+    pub use super::p::WrongType as RightType;
+}
+mod p {
+    fn wrong_fn() {}
+    const WRONG_CONST: u32 = 1;
+    struct WrongType {};
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn completes_in_simple_macro_call() {
+        check(
+            r#"
+macro_rules! m { ($e:expr) => { $e } }
+fn main() { m!(self::f$0); }
+fn foo() {}
+"#,
+            expect![[r#"
+                fn foo()  fn()
+                fn main() fn()
+            "#]],
+        );
+    }
+
+    #[test]
+    fn function_mod_share_name() {
+        check(
+            r#"
+fn foo() { self::m::$0 }
+
+mod m {
+    pub mod z {}
+    pub fn z() {}
+}
+"#,
+            expect![[r#"
+                fn z() fn()
+                md z
+            "#]],
+        );
+    }
+
+    #[test]
+    fn completes_hashmap_new() {
+        check(
+            r#"
+struct RandomState;
+struct HashMap<K, V, S = RandomState> {}
+
+impl<K, V> HashMap<K, V, RandomState> {
+    pub fn new() -> HashMap<K, V, RandomState> { }
+}
+fn foo() {
+    HashMap::$0
+}
+"#,
+            expect![[r#"
+                fn new() fn() -> HashMap<K, V, RandomState>
+            "#]],
+        );
+    }
+
+    #[test]
+    fn completes_variant_through_self() {
+        check(
+            r#"
+enum Foo {
+    Bar,
+    Baz,
+}
+
+impl Foo {
+    fn foo(self) {
+        Self::$0
+    }
+}
+"#,
+            expect![[r#"
+                ev Bar    Bar
+                ev Baz    Baz
+                me foo(…) fn(self)
+            "#]],
+        );
+    }
+
+    #[test]
+    fn completes_primitive_assoc_const() {
+        cov_mark::check!(completes_primitive_assoc_const);
+        check(
+            r#"
+//- /lib.rs crate:lib deps:core
+fn f() {
+    u8::$0
+}
+
+//- /core.rs crate:core
+#[lang = "u8"]
+impl u8 {
+    pub const MAX: Self = 255;
+
+    pub fn func(self) {}
+}
+"#,
+            expect![[r#"
+                ct MAX     pub const MAX: Self
+                me func(…) fn(self)
+            "#]],
+        );
+    }
+
+    #[test]
+    fn completes_variant_through_alias() {
+        cov_mark::check!(completes_variant_through_alias);
+        check(
+            r#"
+enum Foo {
+    Bar
+}
+type Foo2 = Foo;
+fn main() {
+    Foo2::$0
+}
+"#,
+            expect![[r#"
+                ev Bar Bar
+            "#]],
+        );
+    }
+
+    #[test]
+    fn respects_doc_hidden() {
+        cov_mark::check!(qualified_path_doc_hidden);
+        check(
+            r#"
+//- /lib.rs crate:lib deps:dep
+fn f() {
+    dep::$0
+}
+
+//- /dep.rs crate:dep
+#[doc(hidden)]
+#[macro_export]
+macro_rules! m {
+    () => {}
+}
+
+#[doc(hidden)]
+pub fn f() {}
+
+#[doc(hidden)]
+pub struct S;
+
+#[doc(hidden)]
+pub mod m {}
+            "#,
+            expect![[r#""#]],
+        )
+    }
+
+    #[test]
+    fn type_anchor_empty() {
+        cov_mark::check!(completion_type_anchor_empty);
+        check(
+            r#"
+trait Foo {
+    fn foo() -> Self;
+}
+struct Bar;
+impl Foo for Bar {
+    fn foo() -> {
+        Bar
+    }
+}
+fn bar() -> Bar {
+    <_>::$0
+}
+"#,
+            expect![[r#"
+                fn foo() (as Foo) fn() -> Self
+            "#]],
+        )
+    }
+}
diff --git a/crates/ide-completion/src/completions/snippet.rs b/crates/ide-completion/src/completions/snippet.rs
index 38136db4a9..c00de6ff50 100644
--- a/crates/ide-completion/src/completions/snippet.rs
+++ b/crates/ide-completion/src/completions/snippet.rs
@@ -5,9 +5,8 @@ use ide_db::{imports::insert_use::ImportScope, SnippetCap};
 use syntax::T;
 
 use crate::{
-    context::{ItemListKind, PathCompletionCtx, PathKind},
-    item::Builder,
-    CompletionContext, CompletionItem, CompletionItemKind, Completions, SnippetScope,
+    context::PathCompletionCtx, item::Builder, CompletionContext, CompletionItem,
+    CompletionItemKind, Completions, SnippetScope,
 };
 
 fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder {
@@ -17,13 +16,14 @@ fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str)
 }
 
 pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) {
+    if ctx.function_def.is_none() {
+        return;
+    }
+
     let can_be_stmt = match ctx.path_context {
         Some(PathCompletionCtx {
-            is_absolute_path: false,
-            qualifier: None,
-            kind: PathKind::Expr { in_block_expr, .. },
-            ..
-        }) => in_block_expr,
+            is_absolute_path: false, qualifier: None, can_be_stmt, ..
+        }) => can_be_stmt,
         _ => return,
     };
 
@@ -43,16 +43,11 @@ pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionConte
 }
 
 pub(crate) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) {
-    let path_kind = match ctx.path_context {
-        Some(PathCompletionCtx {
-            is_absolute_path: false,
-            qualifier: None,
-            kind: kind @ (PathKind::Item { .. } | PathKind::Expr { in_block_expr: true, .. }),
-            ..
-        }) => kind,
-        _ => return,
-    };
-    if ctx.previous_token_is(T![unsafe]) || ctx.has_unfinished_impl_or_trait_prev_sibling() {
+    if !(ctx.expects_item() || ctx.has_block_expr_parent())
+        || ctx.previous_token_is(T![unsafe])
+        || ctx.path_qual().is_some()
+        || ctx.has_impl_or_trait_prev_sibling()
+    {
         return;
     }
     if ctx.has_visibility_prev_sibling() {
@@ -69,8 +64,7 @@ pub(crate) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionConte
     }
 
     // Test-related snippets shouldn't be shown in blocks.
-    if let PathKind::Item { kind: ItemListKind::SourceFile | ItemListKind::Module, .. } = path_kind
-    {
+    if !ctx.has_block_expr_parent() {
         let mut item = snippet(
             ctx,
             cap,
@@ -102,22 +96,19 @@ fn ${1:feature}() {
         item.lookup_by("tfn");
         item.add_to(acc);
     }
-    if let PathKind::Item { kind: ItemListKind::SourceFile | ItemListKind::Module, .. }
-    | PathKind::Expr { .. } = path_kind
-    {
-        let item = snippet(
-            ctx,
-            cap,
-            "macro_rules",
-            "\
+
+    let item = snippet(
+        ctx,
+        cap,
+        "macro_rules",
+        "\
 macro_rules! $1 {
     ($2) => {
         $0
     };
 }",
-        );
-        item.add_to(acc);
-    }
+    );
+    item.add_to(acc);
 }
 
 fn add_custom_completions(
diff --git a/crates/ide-completion/src/completions/trait_impl.rs b/crates/ide-completion/src/completions/trait_impl.rs
index e3c15038d0..80fbaffd71 100644
--- a/crates/ide-completion/src/completions/trait_impl.rs
+++ b/crates/ide-completion/src/completions/trait_impl.rs
@@ -56,17 +56,19 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext
     if let Some((kind, replacement_range, impl_def)) = completion_match(ctx) {
         if let Some(hir_impl) = ctx.sema.to_def(&impl_def) {
             get_missing_assoc_items(&ctx.sema, &impl_def).into_iter().for_each(|item| {
-                use self::ImplCompletionKind::*;
                 match (item, kind) {
-                    (hir::AssocItem::Function(func), All | Fn) => {
-                        add_function_impl(acc, ctx, replacement_range, func, hir_impl)
-                    }
-                    (hir::AssocItem::TypeAlias(type_alias), All | TypeAlias) => {
-                        add_type_alias_impl(acc, ctx, replacement_range, type_alias)
-                    }
-                    (hir::AssocItem::Const(const_), All | Const) => {
-                        add_const_impl(acc, ctx, replacement_range, const_, hir_impl)
-                    }
+                    (
+                        hir::AssocItem::Function(fn_item),
+                        ImplCompletionKind::All | ImplCompletionKind::Fn,
+                    ) => add_function_impl(acc, ctx, replacement_range, fn_item, hir_impl),
+                    (
+                        hir::AssocItem::TypeAlias(type_item),
+                        ImplCompletionKind::All | ImplCompletionKind::TypeAlias,
+                    ) => add_type_alias_impl(acc, ctx, replacement_range, type_item),
+                    (
+                        hir::AssocItem::Const(const_item),
+                        ImplCompletionKind::All | ImplCompletionKind::Const,
+                    ) => add_const_impl(acc, ctx, replacement_range, const_item, hir_impl),
                     _ => {}
                 }
             });
@@ -74,7 +76,6 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext
     }
 }
 
-// FIXME: This should be lifted out so that we can do proper smart item keyword completions
 fn completion_match(ctx: &CompletionContext) -> Option<(ImplCompletionKind, TextRange, ast::Impl)> {
     let token = ctx.token.clone();
 
@@ -151,15 +152,15 @@ fn add_function_impl(
     func: hir::Function,
     impl_def: hir::Impl,
 ) {
-    let fn_name = func.name(ctx.db);
+    let fn_name = func.name(ctx.db).to_smol_str();
 
-    let label = format!(
-        "fn {}({})",
-        fn_name,
-        if func.assoc_fn_params(ctx.db).is_empty() { "" } else { ".." }
-    );
+    let label = if func.assoc_fn_params(ctx.db).is_empty() {
+        format!("fn {}()", fn_name)
+    } else {
+        format!("fn {}(..)", fn_name)
+    };
 
-    let completion_kind = if func.has_self_param(ctx.db) {
+    let completion_kind = if func.self_param(ctx.db).is_some() {
         CompletionItemKind::Method
     } else {
         CompletionItemKind::SymbolKind(SymbolKind::Function)
@@ -546,7 +547,12 @@ impl Test for T {
     type Test = fn $0;
 }
 ",
-            expect![[r#""#]],
+            expect![[r#"
+                sp Self
+                st T
+                tt Test
+                bt u32
+            "#]],
         );
     }
 
diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs
deleted file mode 100644
index a8d71fd868..0000000000
--- a/crates/ide-completion/src/completions/type.rs
+++ /dev/null
@@ -1,210 +0,0 @@
-//! Completion of names from the current scope in type position.
-
-use hir::{HirDisplay, ScopeDef};
-use ide_db::FxHashSet;
-use syntax::{ast, AstNode};
-
-use crate::{
-    context::{PathCompletionCtx, PathKind, PathQualifierCtx},
-    patterns::{ImmediateLocation, TypeAnnotation},
-    render::render_type_inference,
-    CompletionContext, Completions,
-};
-
-pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext) {
-    let _p = profile::span("complete_type_path");
-    if ctx.is_path_disallowed() {
-        return;
-    }
-
-    let (&is_absolute_path, qualifier) = match &ctx.path_context {
-        Some(PathCompletionCtx { kind: PathKind::Type, is_absolute_path, qualifier, .. }) => {
-            (is_absolute_path, qualifier)
-        }
-        _ => return,
-    };
-
-    let scope_def_applicable = |def| {
-        use hir::{GenericParam::*, ModuleDef::*};
-        match def {
-            ScopeDef::GenericParam(LifetimeParam(_)) | ScopeDef::Label(_) => false,
-            // no values in type places
-            ScopeDef::ModuleDef(Function(_) | Variant(_) | Static(_)) | ScopeDef::Local(_) => false,
-            // unless its a constant in a generic arg list position
-            ScopeDef::ModuleDef(Const(_)) | ScopeDef::GenericParam(ConstParam(_)) => {
-                ctx.expects_generic_arg()
-            }
-            ScopeDef::ImplSelfType(_) => {
-                !ctx.previous_token_is(syntax::T![impl]) && !ctx.previous_token_is(syntax::T![for])
-            }
-            // Don't suggest attribute macros and derives.
-            ScopeDef::ModuleDef(Macro(mac)) => mac.is_fn_like(ctx.db),
-            // Type things are fine
-            ScopeDef::ModuleDef(BuiltinType(_) | Adt(_) | Module(_) | Trait(_) | TypeAlias(_))
-            | ScopeDef::AdtSelfType(_)
-            | ScopeDef::Unknown
-            | ScopeDef::GenericParam(TypeParam(_)) => true,
-        }
-    };
-
-    match qualifier {
-        Some(PathQualifierCtx { is_infer_qualifier, resolution, .. }) => {
-            if *is_infer_qualifier {
-                ctx.traits_in_scope()
-                    .0
-                    .into_iter()
-                    .flat_map(|it| hir::Trait::from(it).items(ctx.sema.db))
-                    .for_each(|item| add_assoc_item(acc, ctx, item));
-                return;
-            }
-            let resolution = match resolution {
-                Some(it) => it,
-                None => return,
-            };
-            // Add associated types on type parameters and `Self`.
-            ctx.scope.assoc_type_shorthand_candidates(resolution, |_, alias| {
-                acc.add_type_alias(ctx, alias);
-                None::<()>
-            });
-
-            match resolution {
-                hir::PathResolution::Def(hir::ModuleDef::Module(module)) => {
-                    let module_scope = module.scope(ctx.db, Some(ctx.module));
-                    for (name, def) in module_scope {
-                        if scope_def_applicable(def) {
-                            acc.add_resolution(ctx, name, def);
-                        }
-                    }
-                }
-                hir::PathResolution::Def(
-                    def @ (hir::ModuleDef::Adt(_)
-                    | hir::ModuleDef::TypeAlias(_)
-                    | hir::ModuleDef::BuiltinType(_)),
-                ) => {
-                    let ty = match def {
-                        hir::ModuleDef::Adt(adt) => adt.ty(ctx.db),
-                        hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db),
-                        hir::ModuleDef::BuiltinType(builtin) => builtin.ty(ctx.db),
-                        _ => unreachable!(),
-                    };
-
-                    // XXX: For parity with Rust bug #22519, this does not complete Ty::AssocType.
-                    // (where AssocType is defined on a trait, not an inherent impl)
-
-                    ty.iterate_path_candidates(
-                        ctx.db,
-                        &ctx.scope,
-                        &ctx.traits_in_scope().0,
-                        Some(ctx.module),
-                        None,
-                        |item| {
-                            add_assoc_item(acc, ctx, item);
-                            None::<()>
-                        },
-                    );
-
-                    // Iterate assoc types separately
-                    ty.iterate_assoc_items(ctx.db, ctx.krate, |item| {
-                        if let hir::AssocItem::TypeAlias(ty) = item {
-                            acc.add_type_alias(ctx, ty)
-                        }
-                        None::<()>
-                    });
-                }
-                hir::PathResolution::Def(hir::ModuleDef::Trait(t)) => {
-                    // Handles `Trait::assoc` as well as `<Ty as Trait>::assoc`.
-                    for item in t.items(ctx.db) {
-                        add_assoc_item(acc, ctx, item);
-                    }
-                }
-                hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_) => {
-                    let ty = match resolution {
-                        hir::PathResolution::TypeParam(param) => param.ty(ctx.db),
-                        hir::PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db),
-                        _ => return,
-                    };
-
-                    let mut seen = FxHashSet::default();
-                    ty.iterate_path_candidates(
-                        ctx.db,
-                        &ctx.scope,
-                        &ctx.traits_in_scope().0,
-                        Some(ctx.module),
-                        None,
-                        |item| {
-                            // We might iterate candidates of a trait multiple times here, so deduplicate
-                            // them.
-                            if seen.insert(item) {
-                                add_assoc_item(acc, ctx, item);
-                            }
-                            None::<()>
-                        },
-                    );
-                }
-                _ => (),
-            }
-        }
-        None if is_absolute_path => acc.add_crate_roots(ctx),
-        None => {
-            acc.add_nameref_keywords_with_colon(ctx);
-            if let Some(ImmediateLocation::TypeBound) = &ctx.completion_location {
-                ctx.process_all_names(&mut |name, res| {
-                    let add_resolution = match res {
-                        ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => mac.is_fn_like(ctx.db),
-                        ScopeDef::ModuleDef(
-                            hir::ModuleDef::Trait(_) | hir::ModuleDef::Module(_),
-                        ) => true,
-                        _ => false,
-                    };
-                    if add_resolution {
-                        acc.add_resolution(ctx, name, res);
-                    }
-                });
-                return;
-            }
-            if let Some(ImmediateLocation::GenericArgList(arg_list)) = &ctx.completion_location {
-                if let Some(path_seg) = arg_list.syntax().parent().and_then(ast::PathSegment::cast)
-                {
-                    if let Some(hir::PathResolution::Def(hir::ModuleDef::Trait(trait_))) =
-                        ctx.sema.resolve_path(&path_seg.parent_path())
-                    {
-                        trait_.items(ctx.sema.db).into_iter().for_each(|it| {
-                            if let hir::AssocItem::TypeAlias(alias) = it {
-                                acc.add_type_alias_with_eq(ctx, alias)
-                            }
-                        });
-                    }
-                }
-            }
-            ctx.process_all_names(&mut |name, def| {
-                if scope_def_applicable(def) {
-                    acc.add_resolution(ctx, name, def);
-                }
-            });
-        }
-    }
-}
-
-pub(crate) fn complete_inferred_type(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
-    use TypeAnnotation::*;
-    let pat = match &ctx.completion_location {
-        Some(ImmediateLocation::TypeAnnotation(t)) => t,
-        _ => return None,
-    };
-    let x = match pat {
-        Let(pat) | FnParam(pat) => ctx.sema.type_of_pat(pat.as_ref()?),
-        Const(exp) | RetType(exp) => ctx.sema.type_of_expr(exp.as_ref()?),
-    }?
-    .adjusted();
-    let ty_string = x.display_source_code(ctx.db, ctx.module.into()).ok()?;
-    acc.add(render_type_inference(ty_string, ctx));
-    None
-}
-
-fn add_assoc_item(acc: &mut Completions, ctx: &CompletionContext, item: hir::AssocItem) {
-    match item {
-        hir::AssocItem::Const(ct) if ctx.expects_generic_arg() => acc.add_const(ctx, ct),
-        hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => (),
-        hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
-    }
-}
diff --git a/crates/ide-completion/src/completions/unqualified_path.rs b/crates/ide-completion/src/completions/unqualified_path.rs
new file mode 100644
index 0000000000..c4d4c5549e
--- /dev/null
+++ b/crates/ide-completion/src/completions/unqualified_path.rs
@@ -0,0 +1,298 @@
+//! Completion of names from the current scope, e.g. locals and imported items.
+
+use hir::ScopeDef;
+use syntax::{ast, AstNode};
+
+use crate::{
+    completions::module_or_fn_macro,
+    context::{PathCompletionCtx, PathKind},
+    patterns::ImmediateLocation,
+    CompletionContext, Completions,
+};
+
+pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
+    let _p = profile::span("complete_unqualified_path");
+    if ctx.is_path_disallowed() || ctx.has_impl_or_trait_prev_sibling() {
+        return;
+    }
+    match ctx.path_context {
+        Some(PathCompletionCtx {
+            is_absolute_path: false,
+            qualifier: None,
+            kind: None | Some(PathKind::Expr | PathKind::Type | PathKind::Item),
+            ..
+        }) => (),
+        _ => return,
+    }
+
+    acc.add_nameref_keywords(ctx);
+
+    match &ctx.completion_location {
+        Some(ImmediateLocation::ItemList | ImmediateLocation::Trait | ImmediateLocation::Impl) => {
+            // only show macros in {Assoc}ItemList
+            ctx.process_all_names(&mut |name, def| {
+                if let Some(def) = module_or_fn_macro(ctx.db, def) {
+                    acc.add_resolution(ctx, name, def);
+                }
+            });
+            return;
+        }
+        Some(ImmediateLocation::TypeBound) => {
+            ctx.process_all_names(&mut |name, res| {
+                let add_resolution = match res {
+                    ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => mac.is_fn_like(ctx.db),
+                    ScopeDef::ModuleDef(hir::ModuleDef::Trait(_) | hir::ModuleDef::Module(_)) => {
+                        true
+                    }
+                    _ => false,
+                };
+                if add_resolution {
+                    acc.add_resolution(ctx, name, res);
+                }
+            });
+            return;
+        }
+        _ => (),
+    }
+
+    if !ctx.expects_type() {
+        if let Some(hir::Adt::Enum(e)) =
+            ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())
+        {
+            super::enum_variants_with_paths(acc, ctx, e, |acc, ctx, variant, path| {
+                acc.add_qualified_enum_variant(ctx, variant, path)
+            });
+        }
+    }
+
+    if let Some(ImmediateLocation::GenericArgList(arg_list)) = &ctx.completion_location {
+        if let Some(path_seg) = arg_list.syntax().parent().and_then(ast::PathSegment::cast) {
+            if let Some(hir::PathResolution::Def(hir::ModuleDef::Trait(trait_))) =
+                ctx.sema.resolve_path(&path_seg.parent_path())
+            {
+                trait_.items(ctx.sema.db).into_iter().for_each(|it| {
+                    if let hir::AssocItem::TypeAlias(alias) = it {
+                        acc.add_type_alias_with_eq(ctx, alias)
+                    }
+                });
+            }
+        }
+    }
+
+    ctx.process_all_names(&mut |name, res| {
+        let add_resolution = match res {
+            ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) | ScopeDef::Label(_) => {
+                cov_mark::hit!(unqualified_skip_lifetime_completion);
+                return;
+            }
+            ScopeDef::ImplSelfType(_) => {
+                !ctx.previous_token_is(syntax::T![impl]) && !ctx.previous_token_is(syntax::T![for])
+            }
+            // Don't suggest attribute macros and derives.
+            ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => mac.is_fn_like(ctx.db),
+            // no values in type places
+            ScopeDef::ModuleDef(
+                hir::ModuleDef::Function(_)
+                | hir::ModuleDef::Variant(_)
+                | hir::ModuleDef::Static(_),
+            )
+            | ScopeDef::Local(_) => !ctx.expects_type(),
+            // unless its a constant in a generic arg list position
+            ScopeDef::ModuleDef(hir::ModuleDef::Const(_))
+            | ScopeDef::GenericParam(hir::GenericParam::ConstParam(_)) => {
+                !ctx.expects_type() || ctx.expects_generic_arg()
+            }
+            _ => true,
+        };
+        if add_resolution {
+            acc.add_resolution(ctx, name, res);
+        }
+    });
+}
+
+#[cfg(test)]
+mod tests {
+    use expect_test::{expect, Expect};
+
+    use crate::tests::{check_edit, completion_list_no_kw};
+
+    fn check(ra_fixture: &str, expect: Expect) {
+        let actual = completion_list_no_kw(ra_fixture);
+        expect.assert_eq(&actual)
+    }
+
+    #[test]
+    fn completes_if_prefix_is_keyword() {
+        check_edit(
+            "wherewolf",
+            r#"
+fn main() {
+    let wherewolf = 92;
+    drop(where$0)
+}
+"#,
+            r#"
+fn main() {
+    let wherewolf = 92;
+    drop(wherewolf)
+}
+"#,
+        )
+    }
+
+    /// Regression test for issue #6091.
+    #[test]
+    fn correctly_completes_module_items_prefixed_with_underscore() {
+        check_edit(
+            "_alpha",
+            r#"
+fn main() {
+    _$0
+}
+fn _alpha() {}
+"#,
+            r#"
+fn main() {
+    _alpha()$0
+}
+fn _alpha() {}
+"#,
+        )
+    }
+
+    #[test]
+    fn completes_prelude() {
+        check(
+            r#"
+//- /main.rs crate:main deps:std
+fn foo() { let x: $0 }
+
+//- /std/lib.rs crate:std
+pub mod prelude {
+    pub mod rust_2018 {
+        pub struct Option;
+    }
+}
+"#,
+            expect![[r#"
+                md std
+                st Option
+                bt u32
+            "#]],
+        );
+    }
+
+    #[test]
+    fn completes_prelude_macros() {
+        check(
+            r#"
+//- /main.rs crate:main deps:std
+fn f() {$0}
+
+//- /std/lib.rs crate:std
+pub mod prelude {
+    pub mod rust_2018 {
+        pub use crate::concat;
+    }
+}
+
+mod macros {
+    #[rustc_builtin_macro]
+    #[macro_export]
+    macro_rules! concat { }
+}
+"#,
+            expect![[r#"
+                fn f()        fn()
+                ma concat!(…) macro_rules! concat
+                md std
+                bt u32
+            "#]],
+        );
+    }
+
+    #[test]
+    fn completes_std_prelude_if_core_is_defined() {
+        check(
+            r#"
+//- /main.rs crate:main deps:core,std
+fn foo() { let x: $0 }
+
+//- /core/lib.rs crate:core
+pub mod prelude {
+    pub mod rust_2018 {
+        pub struct Option;
+    }
+}
+
+//- /std/lib.rs crate:std deps:core
+pub mod prelude {
+    pub mod rust_2018 {
+        pub struct String;
+    }
+}
+"#,
+            expect![[r#"
+                md core
+                md std
+                st String
+                bt u32
+            "#]],
+        );
+    }
+
+    #[test]
+    fn respects_doc_hidden() {
+        check(
+            r#"
+//- /lib.rs crate:lib deps:std
+fn f() {
+    format_$0
+}
+
+//- /std.rs crate:std
+#[doc(hidden)]
+#[macro_export]
+macro_rules! format_args_nl {
+    () => {}
+}
+
+pub mod prelude {
+    pub mod rust_2018 {}
+}
+            "#,
+            expect![[r#"
+                fn f() fn()
+                md std
+                bt u32
+            "#]],
+        );
+    }
+
+    #[test]
+    fn respects_doc_hidden_in_assoc_item_list() {
+        check(
+            r#"
+//- /lib.rs crate:lib deps:std
+struct S;
+impl S {
+    format_$0
+}
+
+//- /std.rs crate:std
+#[doc(hidden)]
+#[macro_export]
+macro_rules! format_args_nl {
+    () => {}
+}
+
+pub mod prelude {
+    pub mod rust_2018 {}
+}
+            "#,
+            expect![[r#"
+                md std
+            "#]],
+        );
+    }
+}
diff --git a/crates/ide-completion/src/completions/use_.rs b/crates/ide-completion/src/completions/use_.rs
index a8d301da17..25eb3fb908 100644
--- a/crates/ide-completion/src/completions/use_.rs
+++ b/crates/ide-completion/src/completions/use_.rs
@@ -12,14 +12,14 @@ use crate::{
 
 pub(crate) fn complete_use_tree(acc: &mut Completions, ctx: &CompletionContext) {
     let (&is_absolute_path, qualifier) = match &ctx.path_context {
-        Some(PathCompletionCtx { kind: PathKind::Use, is_absolute_path, qualifier, .. }) => {
-            (is_absolute_path, qualifier)
-        }
+        Some(PathCompletionCtx {
+            kind: Some(PathKind::Use), is_absolute_path, qualifier, ..
+        }) => (is_absolute_path, qualifier),
         _ => return,
     };
 
     match qualifier {
-        Some(PathQualifierCtx { path, resolution, is_super_chain, use_tree_parent, .. }) => {
+        Some(PathQualifierCtx { path, resolution, is_super_chain, use_tree_parent }) => {
             if *is_super_chain {
                 acc.add_keyword(ctx, "super::");
             }
diff --git a/crates/ide-completion/src/completions/vis.rs b/crates/ide-completion/src/completions/vis.rs
index 50560c9992..7315a488b8 100644
--- a/crates/ide-completion/src/completions/vis.rs
+++ b/crates/ide-completion/src/completions/vis.rs
@@ -10,7 +10,7 @@ use crate::{
 pub(crate) fn complete_vis(acc: &mut Completions, ctx: &CompletionContext) {
     let (&is_absolute_path, qualifier, &has_in_token) = match &ctx.path_context {
         Some(PathCompletionCtx {
-            kind: PathKind::Vis { has_in_token },
+            kind: Some(PathKind::Vis { has_in_token }),
             is_absolute_path,
             qualifier,
             ..
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index 802e48f23a..a41d89859a 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -45,10 +45,7 @@ pub(crate) enum Visible {
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub(super) enum PathKind {
-    Expr {
-        in_block_expr: bool,
-        in_loop_body: bool,
-    },
+    Expr,
     Type,
     Attr {
         kind: AttrKind,
@@ -56,9 +53,7 @@ pub(super) enum PathKind {
     },
     Derive,
     /// Path in item position, that is inside an (Assoc)ItemList
-    Item {
-        kind: ItemListKind,
-    },
+    Item,
     Pat,
     Vis {
         has_in_token: bool,
@@ -66,15 +61,6 @@ pub(super) enum PathKind {
     Use,
 }
 
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub(super) enum ItemListKind {
-    SourceFile,
-    Module,
-    Impl,
-    Trait,
-    ExternBlock,
-}
-
 #[derive(Debug)]
 pub(crate) struct PathCompletionCtx {
     /// If this is a call with () already there (or {} in case of record patterns)
@@ -85,13 +71,12 @@ pub(crate) struct PathCompletionCtx {
     pub(super) is_absolute_path: bool,
     /// The qualifier of the current path if it exists.
     pub(super) qualifier: Option<PathQualifierCtx>,
-    #[allow(dead_code)]
-    // FIXME: use this
-    /// The parent of the path we are completing.
-    pub(super) parent: Option<ast::Path>,
-    pub(super) kind: PathKind,
+    pub(super) kind: Option<PathKind>,
     /// Whether the path segment has type args or not.
     pub(super) has_type_args: bool,
+    /// `true` if we are a statement or a last expr in the block.
+    pub(super) can_be_stmt: bool,
+    pub(super) in_loop_body: bool,
 }
 
 #[derive(Debug)]
@@ -102,8 +87,6 @@ pub(crate) struct PathQualifierCtx {
     pub(crate) is_super_chain: bool,
     /// Whether the qualifier comes from a use tree parent or not
     pub(crate) use_tree_parent: bool,
-    /// <_>
-    pub(crate) is_infer_qualifier: bool,
 }
 
 #[derive(Debug)]
@@ -287,17 +270,13 @@ impl<'a> CompletionContext<'a> {
             || matches!(self.name_ctx, Some(NameContext::RecordField))
     }
 
-    /// Whether the cursor is right after a trait or impl header.
-    /// trait Foo ident$0
-    // FIXME: This probably shouldn't exist
-    pub(crate) fn has_unfinished_impl_or_trait_prev_sibling(&self) -> bool {
+    pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool {
         matches!(
             self.prev_sibling,
             Some(ImmediatePrevSibling::ImplDefType | ImmediatePrevSibling::TraitDefName)
         )
     }
 
-    // FIXME: This probably shouldn't exist
     pub(crate) fn has_impl_prev_sibling(&self) -> bool {
         matches!(self.prev_sibling, Some(ImmediatePrevSibling::ImplDefType))
     }
@@ -310,7 +289,6 @@ impl<'a> CompletionContext<'a> {
         matches!(self.prev_sibling, Some(ImmediatePrevSibling::IfExpr))
     }
 
-    // FIXME: This shouldn't exist
     pub(crate) fn is_path_disallowed(&self) -> bool {
         self.previous_token_is(T![unsafe])
             || matches!(
@@ -325,11 +303,11 @@ impl<'a> CompletionContext<'a> {
     }
 
     pub(crate) fn expects_expression(&self) -> bool {
-        matches!(self.path_context, Some(PathCompletionCtx { kind: PathKind::Expr { .. }, .. }))
+        matches!(self.path_context, Some(PathCompletionCtx { kind: Some(PathKind::Expr), .. }))
     }
 
     pub(crate) fn expects_type(&self) -> bool {
-        matches!(self.path_context, Some(PathCompletionCtx { kind: PathKind::Type, .. }))
+        matches!(self.path_context, Some(PathCompletionCtx { kind: Some(PathKind::Type), .. }))
     }
 
     pub(crate) fn path_is_call(&self) -> bool {
@@ -351,7 +329,7 @@ impl<'a> CompletionContext<'a> {
     }
 
     pub(crate) fn path_kind(&self) -> Option<PathKind> {
-        self.path_context.as_ref().map(|it| it.kind)
+        self.path_context.as_ref().and_then(|it| it.kind)
     }
 
     pub(crate) fn is_immediately_after_macro_bang(&self) -> bool {
@@ -391,15 +369,6 @@ impl<'a> CompletionContext<'a> {
         }
     }
 
-    /// Returns the traits in scope, with the [`Drop`] trait removed.
-    pub(crate) fn traits_in_scope(&self) -> hir::VisibleTraits {
-        let mut traits_in_scope = self.scope.visible_traits();
-        if let Some(drop) = self.famous_defs().core_ops_Drop() {
-            traits_in_scope.0.remove(&drop.into());
-        }
-        traits_in_scope
-    }
-
     /// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items.
     pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
         let _p = profile::span("CompletionContext::process_all_names");
@@ -847,7 +816,7 @@ impl<'a> CompletionContext<'a> {
                     Self::classify_name_ref(&self.sema, &original_file, name_ref)
                 {
                     self.path_context =
-                        Some(PathCompletionCtx { kind: PathKind::Derive, ..path_ctx });
+                        Some(PathCompletionCtx { kind: Some(PathKind::Derive), ..path_ctx });
                 }
             }
             return;
@@ -975,22 +944,16 @@ impl<'a> CompletionContext<'a> {
 
         let mut path_ctx = PathCompletionCtx {
             has_call_parens: false,
-            has_macro_bang: false,
             is_absolute_path: false,
             qualifier: None,
-            parent: path.parent_path(),
-            kind: PathKind::Item { kind: ItemListKind::SourceFile },
             has_type_args: false,
+            can_be_stmt: false,
+            in_loop_body: false,
+            has_macro_bang: false,
+            kind: None,
         };
         let mut pat_ctx = None;
-
-        let is_in_block = |it: &SyntaxNode| {
-            it.parent()
-                .map(|node| {
-                    ast::ExprStmt::can_cast(node.kind()) || ast::StmtList::can_cast(node.kind())
-                })
-                .unwrap_or(false)
-        };
+        path_ctx.in_loop_body = is_in_loop_body(name_ref.syntax());
 
         path_ctx.kind = path.syntax().ancestors().find_map(|it| {
             // using Option<Option<PathKind>> as extra controlflow
@@ -999,10 +962,7 @@ impl<'a> CompletionContext<'a> {
                     ast::PathType(_) => Some(PathKind::Type),
                     ast::PathExpr(it) => {
                         path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind()));
-                        let in_block_expr = is_in_block(it.syntax());
-                        let in_loop_body = is_in_loop_body(it.syntax());
-
-                        Some(PathKind::Expr { in_block_expr, in_loop_body })
+                        Some(PathKind::Expr)
                     },
                     ast::TupleStructPat(it) => {
                         path_ctx.has_call_parens = true;
@@ -1020,25 +980,17 @@ impl<'a> CompletionContext<'a> {
                     },
                     ast::MacroCall(it) => {
                         path_ctx.has_macro_bang = it.excl_token().is_some();
-                        let parent = it.syntax().parent();
-                        match parent.as_ref().map(|it| it.kind()) {
+                        match it.syntax().parent().map(|it| it.kind()) {
                             Some(SyntaxKind::MACRO_PAT) => Some(PathKind::Pat),
                             Some(SyntaxKind::MACRO_TYPE) => Some(PathKind::Type),
-                            Some(SyntaxKind::ITEM_LIST) => Some(PathKind::Item { kind: ItemListKind::Module }),
-                            Some(SyntaxKind::ASSOC_ITEM_LIST) => Some(PathKind::Item { kind: match parent.and_then(|it| it.parent()).map(|it| it.kind()) {
-                                Some(SyntaxKind::TRAIT) => ItemListKind::Trait,
-                                Some(SyntaxKind::IMPL) => ItemListKind::Impl,
-                                _ => return Some(None),
-                            } }),
-                            Some(SyntaxKind::EXTERN_ITEM_LIST) => Some(PathKind::Item { kind: ItemListKind::ExternBlock }),
-                            Some(SyntaxKind::SOURCE_FILE) => Some(PathKind::Item { kind: ItemListKind::SourceFile }),
-                            _ => {
-                               return Some(parent.and_then(ast::MacroExpr::cast).map(|it| {
-                                    let in_loop_body = is_in_loop_body(it.syntax());
-                                    let in_block_expr = is_in_block(it.syntax());
-                                    PathKind::Expr { in_block_expr, in_loop_body }
-                                }));
-                            },
+                            Some(SyntaxKind::MACRO_EXPR) => Some(PathKind::Expr),
+                            Some(
+                                SyntaxKind::ITEM_LIST
+                                | SyntaxKind::ASSOC_ITEM_LIST
+                                | SyntaxKind::EXTERN_ITEM_LIST
+                                | SyntaxKind::SOURCE_FILE
+                            ) => Some(PathKind::Item),
+                            _ => return Some(None),
                         }
                     },
                     ast::Meta(meta) => (|| {
@@ -1059,21 +1011,15 @@ impl<'a> CompletionContext<'a> {
                     })(),
                     ast::Visibility(it) => Some(PathKind::Vis { has_in_token: it.in_token().is_some() }),
                     ast::UseTree(_) => Some(PathKind::Use),
-                    ast::ItemList(_) => Some(PathKind::Item { kind: ItemListKind::Module }),
-                    ast::AssocItemList(it) => Some(PathKind::Item { kind: {
-                            match it.syntax().parent()?.kind() {
-                                SyntaxKind::TRAIT => ItemListKind::Trait,
-                                SyntaxKind::IMPL => ItemListKind::Impl,
-                                _ => return None,
-                            }
-                        }}),
-                    ast::ExternItemList(_) => Some(PathKind::Item { kind: ItemListKind::ExternBlock }),
-                    ast::SourceFile(_) => Some(PathKind::Item { kind: ItemListKind::SourceFile }),
+                    ast::ItemList(_) => Some(PathKind::Item),
+                    ast::AssocItemList(_) => Some(PathKind::Item),
+                    ast::ExternItemList(_) => Some(PathKind::Item),
+                    ast::SourceFile(_) => Some(PathKind::Item),
                     _ => return None,
                 }
             };
             Some(kind)
-        }).flatten()?;
+        }).flatten();
         path_ctx.has_type_args = segment.generic_arg_list().is_some();
 
         if let Some((path, use_tree_parent)) = path_or_use_tree_qualifier(&path) {
@@ -1090,24 +1036,7 @@ impl<'a> CompletionContext<'a> {
                 let res = sema.resolve_path(&path);
                 let is_super_chain = iter::successors(Some(path.clone()), |p| p.qualifier())
                     .all(|p| p.segment().and_then(|s| s.super_token()).is_some());
-
-                // `<_>::$0`
-                let is_infer_qualifier = path.qualifier().is_none()
-                    && matches!(
-                        path.segment().and_then(|it| it.kind()),
-                        Some(ast::PathSegmentKind::Type {
-                            type_ref: Some(ast::Type::InferType(_)),
-                            trait_ref: None,
-                        })
-                    );
-
-                PathQualifierCtx {
-                    path,
-                    resolution: res,
-                    is_super_chain,
-                    use_tree_parent,
-                    is_infer_qualifier,
-                }
+                PathQualifierCtx { path, resolution: res, is_super_chain, use_tree_parent }
             });
             return Some((path_ctx, pat_ctx));
         }
@@ -1119,6 +1048,24 @@ impl<'a> CompletionContext<'a> {
             }
         }
 
+        // Find either enclosing expr statement (thing with `;`) or a
+        // block. If block, check that we are the last expr.
+        path_ctx.can_be_stmt = name_ref
+            .syntax()
+            .ancestors()
+            .find_map(|node| {
+                if let Some(stmt) = ast::ExprStmt::cast(node.clone()) {
+                    return Some(stmt.syntax().text_range() == name_ref.syntax().text_range());
+                }
+                if let Some(stmt_list) = ast::StmtList::cast(node) {
+                    return Some(
+                        stmt_list.tail_expr().map(|e| e.syntax().text_range())
+                            == Some(name_ref.syntax().text_range()),
+                    );
+                }
+                None
+            })
+            .unwrap_or(false);
         Some((path_ctx, pat_ctx))
     }
 }
diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs
index 6e5c2a9386..fe1ad2bf82 100644
--- a/crates/ide-completion/src/item.rs
+++ b/crates/ide-completion/src/item.rs
@@ -26,7 +26,7 @@ pub struct CompletionItem {
     /// It should be used primarily for UI, but we also use this to convert
     /// generic TextEdit into LSP's completion edit (see conv.rs).
     ///
-    /// `source_range` must contain the completion offset. `text_edit` should
+    /// `source_range` must contain the completion offset. `insert_text` should
     /// start with what `source_range` points to, or VSCode will filter out the
     /// completion silently.
     source_range: TextRange,
@@ -149,7 +149,7 @@ pub struct CompletionRelevance {
     pub is_private_editable: bool,
     /// Set for postfix snippet item completions
     pub postfix_match: Option<CompletionRelevancePostfixMatch>,
-    /// This is set for type inference results
+    /// This is setted for type inference results
     pub is_definite: bool,
 }
 
diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs
index 6695fcdc19..6924ba1db6 100644
--- a/crates/ide-completion/src/lib.rs
+++ b/crates/ide-completion/src/lib.rs
@@ -144,37 +144,33 @@ pub fn completions(
     config: &CompletionConfig,
     position: FilePosition,
 ) -> Option<Completions> {
-    let ctx = &CompletionContext::new(db, position, config)?;
-    let mut acc = Completions::default();
+    let ctx = CompletionContext::new(db, position, config)?;
 
-    {
-        let acc = &mut acc;
-        completions::attribute::complete_attribute(acc, ctx);
-        completions::attribute::complete_derive(acc, ctx);
-        completions::attribute::complete_known_attribute_input(acc, ctx);
-        completions::dot::complete_dot(acc, ctx);
-        completions::expr::complete_expr_path(acc, ctx);
-        completions::extern_abi::complete_extern_abi(acc, ctx);
-        completions::flyimport::import_on_the_fly(acc, ctx);
-        completions::fn_param::complete_fn_param(acc, ctx);
-        completions::format_string::format_string(acc, ctx);
-        completions::item_list::complete_item_list(acc, ctx);
-        completions::keyword::complete_expr_keyword(acc, ctx);
-        completions::lifetime::complete_label(acc, ctx);
-        completions::lifetime::complete_lifetime(acc, ctx);
-        completions::mod_::complete_mod(acc, ctx);
-        completions::pattern::complete_pattern(acc, ctx);
-        completions::postfix::complete_postfix(acc, ctx);
-        completions::record::complete_record_literal(acc, ctx);
-        completions::record::complete_record(acc, ctx);
-        completions::snippet::complete_expr_snippet(acc, ctx);
-        completions::snippet::complete_item_snippet(acc, ctx);
-        completions::trait_impl::complete_trait_impl(acc, ctx);
-        completions::r#type::complete_type_path(acc, ctx);
-        completions::r#type::complete_inferred_type(acc, ctx);
-        completions::use_::complete_use_tree(acc, ctx);
-        completions::vis::complete_vis(acc, ctx);
-    }
+    let mut acc = Completions::default();
+    completions::attribute::complete_attribute(&mut acc, &ctx);
+    completions::attribute::complete_derive(&mut acc, &ctx);
+    completions::attribute::complete_known_attribute_input(&mut acc, &ctx);
+    completions::dot::complete_dot(&mut acc, &ctx);
+    completions::extern_abi::complete_extern_abi(&mut acc, &ctx);
+    completions::flyimport::import_on_the_fly(&mut acc, &ctx);
+    completions::fn_param::complete_fn_param(&mut acc, &ctx);
+    completions::format_string::format_string(&mut acc, &ctx);
+    completions::inferred_type(&mut acc, &ctx);
+    completions::keyword::complete_expr_keyword(&mut acc, &ctx);
+    completions::lifetime::complete_label(&mut acc, &ctx);
+    completions::lifetime::complete_lifetime(&mut acc, &ctx);
+    completions::mod_::complete_mod(&mut acc, &ctx);
+    completions::pattern::complete_pattern(&mut acc, &ctx);
+    completions::postfix::complete_postfix(&mut acc, &ctx);
+    completions::qualified_path::complete_qualified_path(&mut acc, &ctx);
+    completions::record::complete_record_literal(&mut acc, &ctx);
+    completions::record::complete_record(&mut acc, &ctx);
+    completions::snippet::complete_expr_snippet(&mut acc, &ctx);
+    completions::snippet::complete_item_snippet(&mut acc, &ctx);
+    completions::trait_impl::complete_trait_impl(&mut acc, &ctx);
+    completions::unqualified_path::complete_unqualified_path(&mut acc, &ctx);
+    completions::use_::complete_use_tree(&mut acc, &ctx);
+    completions::vis::complete_vis(&mut acc, &ctx);
 
     Some(acc)
 }
diff --git a/crates/ide-completion/src/patterns.rs b/crates/ide-completion/src/patterns.rs
index c1db8ce4a6..6fdec78385 100644
--- a/crates/ide-completion/src/patterns.rs
+++ b/crates/ide-completion/src/patterns.rs
@@ -230,7 +230,7 @@ pub(crate) fn determine_location(
                 let receiver = find_in_original_file(it.expr(), original_file);
                 let receiver_is_ambiguous_float_literal = if let Some(ast::Expr::Literal(l)) = &receiver {
                     match l.kind() {
-                        ast::LiteralKind::FloatNumber { .. } => l.to_string().ends_with('.'),
+                        ast::LiteralKind::FloatNumber { .. } => l.token().text().ends_with('.'),
                         _ => false,
                     }
                 } else {
diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs
index a59c8e8ee2..836b14bc2c 100644
--- a/crates/ide-completion/src/render.rs
+++ b/crates/ide-completion/src/render.rs
@@ -273,7 +273,7 @@ fn render_resolution_simple_(
     // Add `<>` for generic types
     let type_path_no_ty_args = matches!(
         ctx.completion.path_context,
-        Some(PathCompletionCtx { kind: PathKind::Type, has_type_args: false, .. })
+        Some(PathCompletionCtx { kind: Some(PathKind::Type), has_type_args: false, .. })
     ) && ctx.completion.config.add_call_parenthesis;
     if type_path_no_ty_args {
         if let Some(cap) = ctx.snippet_cap() {
diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs
index 0117d869ea..d8183660e8 100644
--- a/crates/ide-completion/src/render/function.rs
+++ b/crates/ide-completion/src/render/function.rs
@@ -197,10 +197,10 @@ fn should_add_parens(ctx: &CompletionContext) -> bool {
     }
 
     match ctx.path_context {
-        Some(PathCompletionCtx { kind: PathKind::Expr { .. }, has_call_parens: true, .. }) => {
+        Some(PathCompletionCtx { kind: Some(PathKind::Expr), has_call_parens: true, .. }) => {
             return false
         }
-        Some(PathCompletionCtx { kind: PathKind::Use | PathKind::Type, .. }) => {
+        Some(PathCompletionCtx { kind: Some(PathKind::Use | PathKind::Type), .. }) => {
             cov_mark::hit!(no_parens_in_use_item);
             return false;
         }
diff --git a/crates/ide-completion/src/render/macro_.rs b/crates/ide-completion/src/render/macro_.rs
index 22df7132f0..cbf51af7cc 100644
--- a/crates/ide-completion/src/render/macro_.rs
+++ b/crates/ide-completion/src/render/macro_.rs
@@ -35,7 +35,7 @@ fn render(
 
     let needs_bang = match completion.path_context {
         Some(PathCompletionCtx { kind, has_macro_bang, .. }) => {
-            is_fn_like && kind != PathKind::Use && !has_macro_bang
+            is_fn_like && kind != Some(PathKind::Use) && !has_macro_bang
         }
         _ => is_fn_like,
     };
diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs
index f8cfe15c89..81467ab07a 100644
--- a/crates/ide-db/src/imports/import_assets.rs
+++ b/crates/ide-db/src/imports/import_assets.rs
@@ -114,8 +114,13 @@ impl ImportAssets {
         sema: &Semantics<RootDatabase>,
     ) -> Option<Self> {
         let candidate_node = fully_qualified_path.syntax().clone();
-        if candidate_node.ancestors().find_map(ast::Use::cast).is_some() {
-            return None;
+        if let Some(use_tree) = candidate_node.ancestors().find_map(ast::UseTree::cast) {
+            // Path is inside a use tree, then only continue if it is the first segment of a use statement.
+            if use_tree.syntax().parent().and_then(ast::Use::cast).is_none()
+                || fully_qualified_path.qualifier().is_some()
+            {
+                return None;
+            }
         }
         Some(Self {
             import_candidate: ImportCandidate::for_regular_path(sema, fully_qualified_path)?,
diff --git a/crates/ide-ssr/src/resolving.rs b/crates/ide-ssr/src/resolving.rs
index f3b1af8e77..d46ca05e55 100644
--- a/crates/ide-ssr/src/resolving.rs
+++ b/crates/ide-ssr/src/resolving.rs
@@ -230,7 +230,7 @@ impl<'db> ResolutionScope<'db> {
             adt.ty(self.scope.db).iterate_path_candidates(
                 self.scope.db,
                 &self.scope,
-                &self.scope.visible_traits().0,
+                &self.scope.visible_traits(),
                 Some(module),
                 None,
                 |assoc_item| {
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index bafbf6d9cc..e04fd5a7c7 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -30,15 +30,7 @@ pub(super) fn token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Optio
         INT_NUMBER if token.ancestors().nth(1).map(|it| it.kind()) == Some(FIELD_EXPR) => {
             SymbolKind::Field.into()
         }
-        INT_NUMBER | FLOAT_NUMBER_PART | FLOAT_NUMBER_START_0 | FLOAT_NUMBER_START_1
-        | FLOAT_NUMBER_START_2 => HlTag::NumericLiteral.into(),
-        DOT if matches!(
-            token.prev_token().map(|n| n.kind()),
-            Some(FLOAT_NUMBER_START_1 | FLOAT_NUMBER_START_2)
-        ) =>
-        {
-            HlTag::NumericLiteral.into()
-        }
+        INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(),
         BYTE => HlTag::ByteLiteral.into(),
         CHAR => HlTag::CharLiteral.into(),
         IDENT if token.parent().and_then(ast::TokenTree::cast).is_some() => {
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs
index fb6f8d66c6..21a0aa4284 100644
--- a/crates/mbe/src/syntax_bridge.rs
+++ b/crates/mbe/src/syntax_bridge.rs
@@ -260,33 +260,6 @@ fn convert_tokens<C: TokenConvertor>(conv: &mut C) -> tt::Subtree {
                 IDENT => make_leaf!(Ident),
                 UNDERSCORE => make_leaf!(Ident),
                 k if k.is_keyword() => make_leaf!(Ident),
-                FLOAT_NUMBER_START_0 | FLOAT_NUMBER_START_1 | FLOAT_NUMBER_START_2 => {
-                    // Reassemble a split-up float token.
-                    let mut range = range;
-                    let mut text = token.to_text(conv).to_string();
-                    if kind == FLOAT_NUMBER_START_1 || kind == FLOAT_NUMBER_START_2 {
-                        let (dot, dot_range) = conv.bump().unwrap();
-                        assert_eq!(dot.kind(conv), DOT);
-                        text += &*dot.to_text(conv);
-                        range = TextRange::new(range.start(), dot_range.end());
-
-                        if kind == FLOAT_NUMBER_START_2 {
-                            let (tail, tail_range) = conv.bump().unwrap();
-                            assert_eq!(tail.kind(conv), FLOAT_NUMBER_PART);
-                            text += &*tail.to_text(conv);
-                            range = TextRange::new(range.start(), tail_range.end());
-                        }
-                    }
-
-                    result.push(
-                        tt::Leaf::from(tt::Literal {
-                            id: conv.id_alloc().alloc(range, synth_id),
-                            text: text.into(),
-                        })
-                        .into(),
-                    );
-                    continue;
-                }
                 k if k.is_literal() => make_leaf!(Literal),
                 LIFETIME_IDENT => {
                     let char_unit = TextSize::of('\'');
@@ -740,7 +713,6 @@ struct TtTreeSink<'a> {
     text_pos: TextSize,
     inner: SyntaxTreeBuilder,
     token_map: TokenMap,
-    remaining_float_lit_text: String,
 }
 
 impl<'a> TtTreeSink<'a> {
@@ -752,7 +724,6 @@ impl<'a> TtTreeSink<'a> {
             text_pos: 0.into(),
             inner: SyntaxTreeBuilder::default(),
             token_map: TokenMap::default(),
-            remaining_float_lit_text: String::new(),
         }
     }
 
@@ -779,54 +750,6 @@ impl<'a> TtTreeSink<'a> {
             n_tokens = 2;
         }
 
-        // We need to split a float `tt::Literal` into up to 3 tokens consumed by the parser.
-        match self.cursor.token_tree() {
-            Some(tt::buffer::TokenTreeRef::Subtree(sub, _)) if sub.delimiter.is_none() => {
-                self.cursor = self.cursor.subtree().unwrap()
-            }
-            _ => {}
-        }
-        let literal = match self.cursor.token_tree() {
-            Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Literal(lit), _)) => Some(lit),
-            _ => None,
-        };
-        if matches!(
-            kind,
-            FLOAT_NUMBER_PART | FLOAT_NUMBER_START_0 | FLOAT_NUMBER_START_1 | FLOAT_NUMBER_START_2
-        ) {
-            if self.remaining_float_lit_text.is_empty() {
-                always!(
-                    literal.is_some(),
-                    "kind={:?}, cursor tt={:?}",
-                    kind,
-                    self.cursor.token_tree()
-                );
-                let text = literal.map_or(String::new(), |lit| lit.to_string());
-                self.cursor = self.cursor.bump();
-                match text.split_once('.') {
-                    Some((start, end)) => {
-                        self.inner.token(kind, start);
-                        self.remaining_float_lit_text = format!(".{end}");
-                        return;
-                    }
-                    None => {
-                        self.inner.token(kind, &text);
-                        return;
-                    }
-                }
-            } else {
-                self.inner.token(kind, &self.remaining_float_lit_text);
-                self.remaining_float_lit_text.clear();
-                return;
-            }
-        }
-        if kind == DOT && !self.remaining_float_lit_text.is_empty() {
-            always!(self.remaining_float_lit_text.chars().next() == Some('.'));
-            self.inner.token(kind, ".");
-            self.remaining_float_lit_text = self.remaining_float_lit_text[1..].to_string();
-            return;
-        }
-
         let mut last = self.cursor;
         for _ in 0..n_tokens {
             let tmp: u8;
diff --git a/crates/mbe/src/to_parser_input.rs b/crates/mbe/src/to_parser_input.rs
index 958f643320..6faa147218 100644
--- a/crates/mbe/src/to_parser_input.rs
+++ b/crates/mbe/src/to_parser_input.rs
@@ -35,13 +35,15 @@ pub(crate) fn to_parser_input(buffer: &TokenBuffer) -> parser::Input {
                         let is_negated = lit.text.starts_with('-');
                         let inner_text = &lit.text[if is_negated { 1 } else { 0 }..];
 
-                        let lexed_str = parser::LexedStr::new(inner_text);
-                        if lexed_str.is_empty() {
-                            panic!("failed to convert literal: {:?}", lit);
-                        }
-                        for i in 0..lexed_str.len() {
-                            res.push(lexed_str.kind(i));
-                        }
+                        let kind = parser::LexedStr::single_token(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![_]),
diff --git a/crates/mbe/src/tt_iter.rs b/crates/mbe/src/tt_iter.rs
index 2dbf1f3198..fc5590b718 100644
--- a/crates/mbe/src/tt_iter.rs
+++ b/crates/mbe/src/tt_iter.rs
@@ -90,20 +90,9 @@ impl<'a> TtIter<'a> {
 
         let mut cursor = buffer.begin();
         let mut error = false;
-        let mut float_fragments_to_skip = 0;
         for step in tree_traversal.iter() {
             match step {
                 parser::Step::Token { kind, mut n_input_tokens } => {
-                    if float_fragments_to_skip > 0 {
-                        float_fragments_to_skip -= 1;
-                        n_input_tokens = 0;
-                    }
-                    match kind {
-                        SyntaxKind::LIFETIME_IDENT => n_input_tokens = 2,
-                        SyntaxKind::FLOAT_NUMBER_START_1 => float_fragments_to_skip = 1,
-                        SyntaxKind::FLOAT_NUMBER_START_2 => float_fragments_to_skip = 2,
-                        _ => {}
-                    }
                     if kind == SyntaxKind::LIFETIME_IDENT {
                         n_input_tokens = 2;
                     }
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs
index f68d7196c8..4efbf9a606 100644
--- a/crates/parser/src/grammar.rs
+++ b/crates/parser/src/grammar.rs
@@ -39,7 +39,6 @@ mod generic_params;
 mod types;
 
 use crate::{
-    grammar::expressions::FLOAT_LITERAL_FIRST,
     parser::{CompletedMarker, Marker, Parser},
     SyntaxKind::{self, *},
     TokenSet, T,
@@ -319,15 +318,9 @@ fn name_ref(p: &mut Parser) {
 }
 
 fn name_ref_or_index(p: &mut Parser) {
-    assert!(
-        p.at(IDENT) || p.at(INT_NUMBER) || p.at(FLOAT_NUMBER_PART) || p.at_ts(FLOAT_LITERAL_FIRST)
-    );
+    assert!(p.at(IDENT) || p.at(INT_NUMBER));
     let m = p.start();
-    if p.at(FLOAT_NUMBER_PART) || p.at_ts(FLOAT_LITERAL_FIRST) {
-        p.bump_remap(INT_NUMBER);
-    } else {
-        p.bump_any();
-    }
+    p.bump_any();
     m.complete(p, NAME_REF);
 }
 
diff --git a/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs
index 290083b343..b063c73a9d 100644
--- a/crates/parser/src/grammar/expressions.rs
+++ b/crates/parser/src/grammar/expressions.rs
@@ -3,7 +3,7 @@ mod atom;
 use super::*;
 
 pub(crate) use self::atom::{block_expr, match_arm_list};
-pub(super) use self::atom::{literal, FLOAT_LITERAL_FIRST, LITERAL_FIRST};
+pub(super) use self::atom::{literal, LITERAL_FIRST};
 
 #[derive(PartialEq, Eq)]
 pub(super) enum Semicolon {
@@ -452,9 +452,6 @@ fn index_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
 // fn foo() {
 //     x.foo();
 //     y.bar::<T>(1, 2,);
-//
-//     0e0.sin();
-//     0e0f32.sin();
 // }
 fn method_call_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
     assert!(p.at(T![.]) && p.nth(1) == IDENT && (p.nth(2) == T!['('] || p.nth_at(2, T![::])));
@@ -472,16 +469,17 @@ fn method_call_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
 // fn foo() {
 //     x.foo;
 //     x.0.bar;
-//     x.0. bar;
-//     x.0.1;
 //     x.0();
 // }
 fn field_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
     assert!(p.at(T![.]));
     let m = lhs.precede(p);
     p.bump(T![.]);
-    if p.at(IDENT) || p.at(INT_NUMBER) || p.at(FLOAT_NUMBER_PART) || p.at_ts(FLOAT_LITERAL_FIRST) {
+    if p.at(IDENT) || p.at(INT_NUMBER) {
         name_ref_or_index(p);
+    } else if p.at(FLOAT_NUMBER) {
+        // FIXME: How to recover and instead parse INT + T![.]?
+        p.bump_any();
     } else {
         p.error("expected field name or number");
     }
diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs
index 5ab148ff28..10e5d897e0 100644
--- a/crates/parser/src/grammar/expressions/atom.rs
+++ b/crates/parser/src/grammar/expressions/atom.rs
@@ -17,58 +17,22 @@ pub(crate) const LITERAL_FIRST: TokenSet = TokenSet::new(&[
     T![true],
     T![false],
     INT_NUMBER,
-    FLOAT_NUMBER_START_0,
-    FLOAT_NUMBER_START_1,
-    FLOAT_NUMBER_START_2,
+    FLOAT_NUMBER,
     BYTE,
     CHAR,
     STRING,
     BYTE_STRING,
 ]);
 
-pub(crate) const FLOAT_LITERAL_FIRST: TokenSet =
-    TokenSet::new(&[FLOAT_NUMBER_START_0, FLOAT_NUMBER_START_1, FLOAT_NUMBER_START_2]);
-
 pub(crate) fn literal(p: &mut Parser) -> Option<CompletedMarker> {
     if !p.at_ts(LITERAL_FIRST) {
         return None;
     }
     let m = p.start();
-    if p.at_ts(FLOAT_LITERAL_FIRST) {
-        float_literal(p);
-    } else {
-        // Everything else is just one token.
-        p.bump_any();
-    }
+    p.bump_any();
     Some(m.complete(p, LITERAL))
 }
 
-// test float_literal
-// fn f() {
-//     0.0;
-//     1.;
-//     0e0;
-//     0e0f32;
-//     1.23f64;
-// }
-pub(crate) fn float_literal(p: &mut Parser) {
-    // Floats can be up to 3 tokens. The first token indicates how many there are.
-    let f = p.start();
-    if p.at(FLOAT_NUMBER_START_0) {
-        p.bump(FLOAT_NUMBER_START_0);
-    } else if p.at(FLOAT_NUMBER_START_1) {
-        p.bump(FLOAT_NUMBER_START_1);
-        p.bump(DOT);
-    } else if p.at(FLOAT_NUMBER_START_2) {
-        p.bump(FLOAT_NUMBER_START_2);
-        p.bump(DOT);
-        p.bump(FLOAT_NUMBER_PART);
-    } else {
-        unreachable!();
-    }
-    f.complete(p, FLOAT_LITERAL);
-}
-
 // E.g. for after the break in `if break {}`, this should not match
 pub(super) const ATOM_EXPR_FIRST: TokenSet =
     LITERAL_FIRST.union(paths::PATH_FIRST).union(TokenSet::new(&[
diff --git a/crates/parser/src/grammar/patterns.rs b/crates/parser/src/grammar/patterns.rs
index c16bd8d0c7..1f622b32e5 100644
--- a/crates/parser/src/grammar/patterns.rs
+++ b/crates/parser/src/grammar/patterns.rs
@@ -140,7 +140,7 @@ fn atom_pat(p: &mut Parser, recovery_set: TokenSet) -> Option<CompletedMarker> {
 }
 
 fn is_literal_pat_start(p: &Parser) -> bool {
-    p.at(T![-]) && (p.nth(1) == INT_NUMBER || p.nth(1) == FLOAT_NUMBER_PART)
+    p.at(T![-]) && (p.nth(1) == INT_NUMBER || p.nth(1) == FLOAT_NUMBER)
         || p.at_ts(expressions::LITERAL_FIRST)
 }
 
diff --git a/crates/parser/src/lexed_str.rs b/crates/parser/src/lexed_str.rs
index 9887960871..f4b9988eac 100644
--- a/crates/parser/src/lexed_str.rs
+++ b/crates/parser/src/lexed_str.rs
@@ -177,7 +177,7 @@ impl<'a> Converter<'a> {
 
                 rustc_lexer::TokenKind::RawIdent => IDENT,
                 rustc_lexer::TokenKind::Literal { kind, .. } => {
-                    self.extend_literal(token_text, kind);
+                    self.extend_literal(token_text.len(), kind);
                     return;
                 }
 
@@ -223,7 +223,7 @@ impl<'a> Converter<'a> {
         self.push(syntax_kind, token_text.len(), err);
     }
 
-    fn extend_literal(&mut self, token_text: &str, kind: &rustc_lexer::LiteralKind) {
+    fn extend_literal(&mut self, len: usize, kind: &rustc_lexer::LiteralKind) {
         let mut err = "";
 
         let syntax_kind = match *kind {
@@ -237,27 +237,7 @@ impl<'a> Converter<'a> {
                 if empty_exponent {
                     err = "Missing digits after the exponent symbol";
                 }
-
-                // In order to correctly parse nested tuple accesses like `tup.0.0`, where the `0.0`
-                // is lexed as a float, we split floats that contain a `.` into 3 tokens.
-                // To ensure that later stages can always reconstruct the token correctly, the first
-                // token in the sequence indicates the number of following tokens that are part of
-                // the float literal.
-                if let Some((before, after)) = token_text.split_once('.') {
-                    let err = if err.is_empty() { None } else { Some(err) };
-
-                    assert!(!before.is_empty());
-                    let tok =
-                        if after.is_empty() { FLOAT_NUMBER_START_1 } else { FLOAT_NUMBER_START_2 };
-                    self.push(tok, before.len(), None);
-                    self.push(DOT, 1, None);
-                    if !after.is_empty() {
-                        self.push(FLOAT_NUMBER_PART, after.len(), err);
-                    }
-                    return;
-                }
-
-                FLOAT_NUMBER_START_0
+                FLOAT_NUMBER
             }
             rustc_lexer::LiteralKind::Char { terminated } => {
                 if !terminated {
@@ -315,6 +295,6 @@ impl<'a> Converter<'a> {
         };
 
         let err = if err.is_empty() { None } else { Some(err) };
-        self.push(syntax_kind, token_text.len(), err);
+        self.push(syntax_kind, len, err);
     }
 }
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs
index 5ddcd6ad6a..628fa745e7 100644
--- a/crates/parser/src/syntax_kind/generated.rs
+++ b/crates/parser/src/syntax_kind/generated.rs
@@ -110,10 +110,7 @@ pub enum SyntaxKind {
     RAW_KW,
     MACRO_RULES_KW,
     INT_NUMBER,
-    FLOAT_NUMBER_START_0,
-    FLOAT_NUMBER_START_1,
-    FLOAT_NUMBER_START_2,
-    FLOAT_NUMBER_PART,
+    FLOAT_NUMBER,
     CHAR,
     BYTE,
     STRING,
@@ -230,7 +227,6 @@ pub enum SyntaxKind {
     PATH,
     PATH_SEGMENT,
     LITERAL,
-    FLOAT_LITERAL,
     RENAME,
     VISIBILITY,
     WHERE_CLAUSE,
@@ -290,8 +286,7 @@ impl SyntaxKind {
     }
     pub fn is_literal(self) -> bool {
         match self {
-            INT_NUMBER | FLOAT_NUMBER_START_0 | FLOAT_NUMBER_START_1 | FLOAT_NUMBER_START_2
-            | FLOAT_NUMBER_PART | CHAR | BYTE | STRING | BYTE_STRING => true,
+            INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE | STRING | BYTE_STRING => true,
             _ => false,
         }
     }
@@ -391,5 +386,5 @@ impl SyntaxKind {
     }
 }
 #[macro_export]
-macro_rules ! T { [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [existential] => { $ crate :: SyntaxKind :: EXISTENTIAL_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; [float_number_part] => { $ crate :: SyntaxKind :: FLOAT_NUMBER_PART } ; [float_number_start_0] => { $ crate :: SyntaxKind :: FLOAT_NUMBER_START_0 } ; [float_number_start_1] => { $ crate :: SyntaxKind :: FLOAT_NUMBER_START_1 } ; [float_number_start_2] => { $ crate :: SyntaxKind :: FLOAT_NUMBER_START_2 } ; }
+macro_rules ! T { [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [existential] => { $ crate :: SyntaxKind :: EXISTENTIAL_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; }
 pub use T;
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index c2a521bbf5..c2a6a3fe30 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -55,13 +55,10 @@ impl GlobalState {
             self.reload_flycheck();
         }
 
-        if self.analysis_host.raw_database().enable_proc_attr_macros()
-            != self.config.expand_proc_attr_macros()
-        {
-            self.analysis_host
-                .raw_database_mut()
-                .set_enable_proc_attr_macros(self.config.expand_proc_attr_macros());
-        }
+        // Apply experimental feature flags.
+        self.analysis_host
+            .raw_database_mut()
+            .set_enable_proc_attr_macros(self.config.expand_proc_attr_macros());
     }
 
     pub(crate) fn current_status(&self) -> lsp_ext::ServerStatusParams {
diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram
index 85270dee62..62aa478399 100644
--- a/crates/syntax/rust.ungram
+++ b/crates/syntax/rust.ungram
@@ -365,20 +365,13 @@ MacroExpr =
 
 Literal =
   Attr* value:(
-    'int_number' | FloatLiteral
+    'int_number' | 'float_number'
   | 'string' | 'raw_string'
   | 'byte_string' | 'raw_byte_string'
   | 'true' | 'false'
   | 'char' | 'byte'
   )
 
-FloatLiteral =
-  'float_number_start_0'?
-  'float_number_start_1'?
-  'float_number_start_2'?
-  '.'?
-  'float_number_part'?
-
 PathExpr =
   Attr* Path
 
diff --git a/crates/syntax/src/ast/expr_ext.rs b/crates/syntax/src/ast/expr_ext.rs
index 11d81d4778..17785152bc 100644
--- a/crates/syntax/src/ast/expr_ext.rs
+++ b/crates/syntax/src/ast/expr_ext.rs
@@ -8,7 +8,7 @@ use crate::{
         operators::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp},
         support, AstChildren, AstNode,
     },
-    AstToken, SyntaxElement,
+    AstToken,
     SyntaxKind::*,
     SyntaxNode, SyntaxToken, T,
 };
@@ -282,32 +282,30 @@ pub enum LiteralKind {
     String(ast::String),
     ByteString(ast::ByteString),
     IntNumber(ast::IntNumber),
-    FloatNumber(ast::FloatLiteral),
+    FloatNumber(ast::FloatNumber),
     Char(ast::Char),
     Byte(ast::Byte),
     Bool(bool),
 }
 
 impl ast::Literal {
-    pub fn value(&self) -> SyntaxElement {
+    pub fn token(&self) -> SyntaxToken {
         self.syntax()
             .children_with_tokens()
             .find(|e| e.kind() != ATTR && !e.kind().is_trivia())
+            .and_then(|e| e.into_token())
             .unwrap()
     }
+
     pub fn kind(&self) -> LiteralKind {
-        let token = match self.value() {
-            rowan::NodeOrToken::Node(node) => {
-                return LiteralKind::FloatNumber(
-                    ast::FloatLiteral::cast(node).expect("unreachable"),
-                );
-            }
-            rowan::NodeOrToken::Token(token) => token,
-        };
+        let token = self.token();
 
         if let Some(t) = ast::IntNumber::cast(token.clone()) {
             return LiteralKind::IntNumber(t);
         }
+        if let Some(t) = ast::FloatNumber::cast(token.clone()) {
+            return LiteralKind::FloatNumber(t);
+        }
         if let Some(t) = ast::String::cast(token.clone()) {
             return LiteralKind::String(t);
         }
@@ -327,26 +325,6 @@ impl ast::Literal {
             _ => unreachable!(),
         }
     }
-
-    pub fn as_string(&self) -> Option<ast::String> {
-        match self.kind() {
-            LiteralKind::String(it) => Some(it),
-            _ => None,
-        }
-    }
-
-    pub fn as_byte_string(&self) -> Option<ast::ByteString> {
-        match self.kind() {
-            LiteralKind::ByteString(it) => Some(it),
-            _ => None,
-        }
-    }
-}
-
-impl ast::FloatLiteral {
-    pub fn suffix(&self) -> Option<String> {
-        ast::FloatNumberPart::cast(self.syntax().last_token()?)?.suffix().map(|s| s.to_string())
-    }
 }
 
 pub enum BlockModifier {
@@ -386,7 +364,7 @@ impl ast::BlockExpr {
 fn test_literal_with_attr() {
     let parse = ast::SourceFile::parse(r#"const _: &str = { #[attr] "Hello" };"#);
     let lit = parse.tree().syntax().descendants().find_map(ast::Literal::cast).unwrap();
-    assert_eq!(lit.value().to_string(), r#""Hello""#);
+    assert_eq!(lit.token().text(), r#""Hello""#);
 }
 
 impl ast::RecordExprField {
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index 9d5af8e63c..cf90ba64cf 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -1085,26 +1085,6 @@ impl UnderscoreExpr {
     pub fn underscore_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![_]) }
 }
 
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct FloatLiteral {
-    pub(crate) syntax: SyntaxNode,
-}
-impl FloatLiteral {
-    pub fn float_number_start_0_token(&self) -> Option<SyntaxToken> {
-        support::token(&self.syntax, T![float_number_start_0])
-    }
-    pub fn float_number_start_1_token(&self) -> Option<SyntaxToken> {
-        support::token(&self.syntax, T![float_number_start_1])
-    }
-    pub fn float_number_start_2_token(&self) -> Option<SyntaxToken> {
-        support::token(&self.syntax, T![float_number_start_2])
-    }
-    pub fn dot_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![.]) }
-    pub fn float_number_part_token(&self) -> Option<SyntaxToken> {
-        support::token(&self.syntax, T![float_number_part])
-    }
-}
-
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct StmtList {
     pub(crate) syntax: SyntaxNode,
@@ -2739,17 +2719,6 @@ impl AstNode for UnderscoreExpr {
     }
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
-impl AstNode for FloatLiteral {
-    fn can_cast(kind: SyntaxKind) -> bool { kind == FLOAT_LITERAL }
-    fn cast(syntax: SyntaxNode) -> Option<Self> {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
-    }
-    fn syntax(&self) -> &SyntaxNode { &self.syntax }
-}
 impl AstNode for StmtList {
     fn can_cast(kind: SyntaxKind) -> bool { kind == STMT_LIST }
     fn cast(syntax: SyntaxNode) -> Option<Self> {
@@ -4639,11 +4608,6 @@ impl std::fmt::Display for UnderscoreExpr {
         std::fmt::Display::fmt(self.syntax(), f)
     }
 }
-impl std::fmt::Display for FloatLiteral {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        std::fmt::Display::fmt(self.syntax(), f)
-    }
-}
 impl std::fmt::Display for StmtList {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         std::fmt::Display::fmt(self.syntax(), f)
diff --git a/crates/syntax/src/ast/generated/tokens.rs b/crates/syntax/src/ast/generated/tokens.rs
index 1e1a55e326..a3209c5abd 100644
--- a/crates/syntax/src/ast/generated/tokens.rs
+++ b/crates/syntax/src/ast/generated/tokens.rs
@@ -112,16 +112,16 @@ impl AstToken for IntNumber {
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct FloatNumberPart {
+pub struct FloatNumber {
     pub(crate) syntax: SyntaxToken,
 }
-impl std::fmt::Display for FloatNumberPart {
+impl std::fmt::Display for FloatNumber {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         std::fmt::Display::fmt(&self.syntax, f)
     }
 }
-impl AstToken for FloatNumberPart {
-    fn can_cast(kind: SyntaxKind) -> bool { kind == FLOAT_NUMBER_PART }
+impl AstToken for FloatNumber {
+    fn can_cast(kind: SyntaxKind) -> bool { kind == FLOAT_NUMBER }
     fn cast(syntax: SyntaxToken) -> Option<Self> {
         if Self::can_cast(syntax.kind()) {
             Some(Self { syntax })
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 846f4f3c71..5908dda8e6 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -799,11 +799,6 @@ pub fn struct_(
     ))
 }
 
-pub fn literal(text: &str) -> ast::Literal {
-    assert_eq!(text.trim(), text);
-    ast_from_text(&format!("fn f() {{ let _ = {}; }}", text))
-}
-
 #[track_caller]
 fn ast_from_text<N: AstNode>(text: &str) -> N {
     let parse = SourceFile::parse(text);
@@ -832,7 +827,7 @@ pub fn token(kind: SyntaxKind) -> SyntaxToken {
 pub mod tokens {
     use once_cell::sync::Lazy;
 
-    use crate::{AstNode, Parse, SourceFile, SyntaxKind::*, SyntaxToken};
+    use crate::{ast, AstNode, Parse, SourceFile, SyntaxKind::*, SyntaxToken};
 
     pub(super) static SOURCE_FILE: Lazy<Parse<SourceFile>> = Lazy::new(|| {
         SourceFile::parse(
@@ -863,6 +858,12 @@ pub mod tokens {
         sf.syntax().first_child_or_token().unwrap().into_token().unwrap()
     }
 
+    pub fn literal(text: &str) -> SyntaxToken {
+        assert_eq!(text.trim(), text);
+        let lit: ast::Literal = super::ast_from_text(&format!("fn f() {{ let _ = {}; }}", text));
+        lit.syntax().first_child_or_token().unwrap().into_token().unwrap()
+    }
+
     pub fn single_newline() -> SyntaxToken {
         let res = SOURCE_FILE
             .tree()
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 5b19b5ed2c..f2153ca921 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -555,9 +555,7 @@ impl ast::FieldExpr {
         self.syntax
             .children_with_tokens()
             // FIXME: Accepting floats here to reject them in validation later
-            .find(|c| {
-                c.kind() == SyntaxKind::INT_NUMBER || c.kind() == SyntaxKind::FLOAT_NUMBER_PART
-            })
+            .find(|c| c.kind() == SyntaxKind::INT_NUMBER || c.kind() == SyntaxKind::FLOAT_NUMBER)
             .as_ref()
             .and_then(SyntaxElement::as_token)
             .cloned()
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs
index 5f2e7231d9..4b6dc236b5 100644
--- a/crates/syntax/src/ast/token_ext.rs
+++ b/crates/syntax/src/ast/token_ext.rs
@@ -321,7 +321,7 @@ impl ast::IntNumber {
     }
 }
 
-impl ast::FloatNumberPart {
+impl ast::FloatNumber {
     pub fn suffix(&self) -> Option<&str> {
         let text = self.text();
         let mut indices = text.char_indices();
@@ -355,24 +355,14 @@ impl Radix {
 
 #[cfg(test)]
 mod tests {
-    use crate::ast::{self, make};
+    use crate::ast::{self, make, FloatNumber, IntNumber};
 
     fn check_float_suffix<'a>(lit: &str, expected: impl Into<Option<&'a str>>) {
-        let suffix = match make::literal(lit).kind() {
-            ast::LiteralKind::FloatNumber(f) => f.suffix(),
-            // `1f32` lexes as an INT_NUMBER
-            ast::LiteralKind::IntNumber(i) => i.suffix().map(|s| s.to_string()),
-            e => unreachable!("{e:?}"),
-        };
-        assert_eq!(suffix.as_deref(), expected.into());
+        assert_eq!(FloatNumber { syntax: make::tokens::literal(lit) }.suffix(), expected.into());
     }
 
     fn check_int_suffix<'a>(lit: &str, expected: impl Into<Option<&'a str>>) {
-        let i = match make::literal(lit).kind() {
-            ast::LiteralKind::IntNumber(i) => i,
-            _ => unreachable!(),
-        };
-        assert_eq!(i.suffix(), expected.into());
+        assert_eq!(IntNumber { syntax: make::tokens::literal(lit) }.suffix(), expected.into());
     }
 
     #[test]
@@ -400,11 +390,12 @@ mod tests {
     }
 
     fn check_string_value<'a>(lit: &str, expected: impl Into<Option<&'a str>>) {
-        let s = match make::literal(&format!("\"{}\"", lit)).kind() {
-            ast::LiteralKind::String(s) => s,
-            _ => unreachable!(),
-        };
-        assert_eq!(s.value().as_deref(), expected.into());
+        assert_eq!(
+            ast::String { syntax: make::tokens::literal(&format!("\"{}\"", lit)) }
+                .value()
+                .as_deref(),
+            expected.into()
+        );
     }
 
     #[test]
diff --git a/crates/syntax/src/validation.rs b/crates/syntax/src/validation.rs
index 3edca3eb8f..c2c2c82e11 100644
--- a/crates/syntax/src/validation.rs
+++ b/crates/syntax/src/validation.rs
@@ -119,15 +119,8 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
         text.rfind(end_delimiter).and_then(|end| text.get(prefix_len..end))
     }
 
-    let token = literal.value();
-    let text;
-    let text = match &token {
-        rowan::NodeOrToken::Node(node) => {
-            text = node.text().to_string();
-            &*text
-        }
-        rowan::NodeOrToken::Token(token) => token.text(),
-    };
+    let token = literal.token();
+    let text = token.text();
 
     // FIXME: lift this lambda refactor to `fn` (https://github.com/rust-analyzer/rust-analyzer/pull/2834#discussion_r366199205)
     let mut push_err = |prefix_len, (off, err): (usize, unescape::EscapeError)| {


Test patch:
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs b/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs
index 66cc2843d6..2de10ddbdf 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs
@@ -80,7 +80,7 @@ macro_rules! f3 { ($i:_) => () }
 
 #[test]
 fn test_rustc_issue_57597() {
-    // <https://github.com/rust-lang/rust/blob/master/src/test/ui/macros/issue-57597.rs>
+    // <https://github.com/rust-lang/rust/blob/master/src/test/ui/issues/issue-57597.rs>
     check(
         r#"
 macro_rules! m0 { ($($($i:ident)?)+) => {}; }
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs b/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs
index 9e4ab043f6..84cc3f3872 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs
@@ -38,7 +38,6 @@ macro_rules! m {
         let _ = 12E+99_f64;
         let _ = "rust1";
         let _ = -92;
-        let _ = -1.3e4f32;
     }
 }
 fn f() {
@@ -53,7 +52,6 @@ macro_rules! m {
         let _ = 12E+99_f64;
         let _ = "rust1";
         let _ = -92;
-        let _ = -1.3e4f32;
     }
 }
 fn f() {
@@ -62,7 +60,6 @@ fn f() {
     let _ = 12E+99_f64;
     let _ = "rust1";
     let _ = -92;
-    let _ = -1.3e4f32;
 }
 "#]],
     );
@@ -151,57 +148,3 @@ $ = ();
 "#]],
     )
 }
-
-#[test]
-fn float_literal_in_output() {
-    check(
-        r#"
-macro_rules! constant {
-    ($e:expr ;) => {$e};
-}
-
-const _: () = constant!(0.0;);
-const _: () = constant!(0.;);
-const _: () = constant!(0e0;);
-"#,
-        expect![[r#"
-macro_rules! constant {
-    ($e:expr ;) => {$e};
-}
-
-const _: () = 0.0;
-const _: () = 0.;
-const _: () = 0e0;
-"#]],
-    );
-}
-
-#[test]
-fn float_literal_in_tt() {
-    check(
-        r#"
-macro_rules! constant {
-    ($( $ret:expr; )*) => {};
-}
-
-macro_rules! float_const_impl {
-    () => ( constant!(0.3; 3.3;); );
-}
-
-float_const_impl! {}
-"#,
-        expect![[r#"
-macro_rules! constant {
-    ($( $ret:expr; )*) => {};
-}
-
-macro_rules! float_const_impl {
-    () => ( constant!(0.3; 3.3;); );
-}
-
-constant!(0.3;
-3.3;
-);
-"#]],
-    );
-}
diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs
index 4b8a7e782d..d4d61c2167 100644
--- a/crates/hir-ty/src/tests/simple.rs
+++ b/crates/hir-ty/src/tests/simple.rs
@@ -2733,14 +2733,3 @@ fn f() {
     "#,
     );
 }
-
-#[test]
-fn nested_tuple_index() {
-    check_no_mismatches(
-        r#"
-fn main() {
-    let fld: i32 = ((0,),).0.0;
-}
-"#,
-    );
-}
diff --git a/crates/ide-completion/src/tests.rs b/crates/ide-completion/src/tests.rs
index 72b7a5584a..5387b455f0 100644
--- a/crates/ide-completion/src/tests.rs
+++ b/crates/ide-completion/src/tests.rs
@@ -10,7 +10,6 @@
 
 mod attribute;
 mod expression;
-mod flyimport;
 mod fn_param;
 mod item_list;
 mod item;
@@ -18,10 +17,10 @@ mod pattern;
 mod predicate;
 mod proc_macros;
 mod record;
-mod special;
 mod type_pos;
 mod use_tree;
 mod visibility;
+mod flyimport;
 
 use std::mem;
 
@@ -214,17 +213,7 @@ pub(crate) fn check_pattern_is_applicable(code: &str, check: impl FnOnce(SyntaxE
 
 pub(crate) fn get_all_items(config: CompletionConfig, code: &str) -> Vec<CompletionItem> {
     let (db, position) = position(code);
-    let res = crate::completions(&db, &config, position).map_or_else(Vec::default, Into::into);
-    // validate
-    res.iter().for_each(|it| {
-        let sr = it.source_range();
-        assert!(
-            sr.contains_inclusive(position.offset),
-            "source range {sr:?} does not contain the offset {:?} of the completion request: {it:?}",
-            position.offset
-        );
-    });
-    res
+    crate::completions(&db, &config, position).map_or_else(Vec::default, Into::into)
 }
 
 #[test]
diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs
index 61246b963e..a068d5bf7a 100644
--- a/crates/ide-completion/src/tests/expression.rs
+++ b/crates/ide-completion/src/tests/expression.rs
@@ -48,7 +48,7 @@ fn baz() {
             un Union
             ev TupleV(…)     TupleV(u32)
             bt u32
-            kw crate::
+            kw crate
             kw false
             kw for
             kw if
@@ -57,8 +57,8 @@ fn baz() {
             kw match
             kw mut
             kw return
-            kw self::
-            kw super::
+            kw self
+            kw super
             kw true
             kw unsafe
             kw while
@@ -91,7 +91,7 @@ fn func(param0 @ (param1, param2): (i32, i32)) {
             lc param1     i32
             lc param2     i32
             bt u32
-            kw crate::
+            kw crate
             kw false
             kw for
             kw if
@@ -99,8 +99,8 @@ fn func(param0 @ (param1, param2): (i32, i32)) {
             kw loop
             kw match
             kw return
-            kw self::
-            kw super::
+            kw self
+            kw super
             kw true
             kw unsafe
             kw while
@@ -111,6 +111,7 @@ fn func(param0 @ (param1, param2): (i32, i32)) {
 
 #[test]
 fn completes_all_the_things_in_fn_body() {
+    cov_mark::check!(unqualified_skip_lifetime_completion);
     check(
         r#"
 use non_existant::Unresolved;
@@ -145,7 +146,7 @@ impl Unit {
             ev TupleV(…)    TupleV(u32)
             bt u32
             kw const
-            kw crate::
+            kw crate
             kw enum
             kw extern
             kw false
@@ -159,10 +160,10 @@ impl Unit {
             kw match
             kw mod
             kw return
-            kw self::
+            kw self
             kw static
             kw struct
-            kw super::
+            kw super
             kw trait
             kw true
             kw type
@@ -223,7 +224,7 @@ fn complete_in_block() {
             fn foo()       fn()
             bt u32
             kw const
-            kw crate::
+            kw crate
             kw enum
             kw extern
             kw false
@@ -237,10 +238,10 @@ fn complete_in_block() {
             kw match
             kw mod
             kw return
-            kw self::
+            kw self
             kw static
             kw struct
-            kw super::
+            kw super
             kw trait
             kw true
             kw type
@@ -269,7 +270,7 @@ fn complete_after_if_expr() {
             fn foo()       fn()
             bt u32
             kw const
-            kw crate::
+            kw crate
             kw else
             kw else if
             kw enum
@@ -285,10 +286,10 @@ fn complete_after_if_expr() {
             kw match
             kw mod
             kw return
-            kw self::
+            kw self
             kw static
             kw struct
-            kw super::
+            kw super
             kw trait
             kw true
             kw type
@@ -317,7 +318,7 @@ fn complete_in_match_arm() {
         expect![[r#"
             fn foo()     fn()
             bt u32
-            kw crate::
+            kw crate
             kw false
             kw for
             kw if
@@ -325,8 +326,8 @@ fn complete_in_match_arm() {
             kw loop
             kw match
             kw return
-            kw self::
-            kw super::
+            kw self
+            kw super
             kw true
             kw unsafe
             kw while
@@ -345,7 +346,7 @@ fn completes_in_loop_ctx() {
             kw break
             kw const
             kw continue
-            kw crate::
+            kw crate
             kw enum
             kw extern
             kw false
@@ -359,10 +360,10 @@ fn completes_in_loop_ctx() {
             kw match
             kw mod
             kw return
-            kw self::
+            kw self
             kw static
             kw struct
-            kw super::
+            kw super
             kw trait
             kw true
             kw type
@@ -385,7 +386,7 @@ fn completes_in_let_initializer() {
         expect![[r#"
             fn main()    fn()
             bt u32
-            kw crate::
+            kw crate
             kw false
             kw for
             kw if
@@ -393,8 +394,8 @@ fn completes_in_let_initializer() {
             kw loop
             kw match
             kw return
-            kw self::
-            kw super::
+            kw self
+            kw super
             kw true
             kw unsafe
             kw while
@@ -420,7 +421,7 @@ fn foo() {
             fn foo()     fn()
             st Foo
             bt u32
-            kw crate::
+            kw crate
             kw false
             kw for
             kw if
@@ -428,8 +429,8 @@ fn foo() {
             kw loop
             kw match
             kw return
-            kw self::
-            kw super::
+            kw self
+            kw super
             kw true
             kw unsafe
             kw while
@@ -456,7 +457,7 @@ fn foo() {
             fn foo()     fn()
             lc bar       i32
             bt u32
-            kw crate::
+            kw crate
             kw false
             kw for
             kw if
@@ -464,8 +465,8 @@ fn foo() {
             kw loop
             kw match
             kw return
-            kw self::
-            kw super::
+            kw self
+            kw super
             kw true
             kw unsafe
             kw while
@@ -488,7 +489,7 @@ fn quux(x: i32) {
             lc x         i32
             ma m!(…)     macro_rules! m
             bt u32
-            kw crate::
+            kw crate
             kw false
             kw for
             kw if
@@ -496,8 +497,8 @@ fn quux(x: i32) {
             kw loop
             kw match
             kw return
-            kw self::
-            kw super::
+            kw self
+            kw super
             kw true
             kw unsafe
             kw while
@@ -516,7 +517,7 @@ fn quux(x: i32) {
             lc x         i32
             ma m!(…)     macro_rules! m
             bt u32
-            kw crate::
+            kw crate
             kw false
             kw for
             kw if
@@ -524,8 +525,8 @@ fn quux(x: i32) {
             kw loop
             kw match
             kw return
-            kw self::
-            kw super::
+            kw self
+            kw super
             kw true
             kw unsafe
             kw while
diff --git a/crates/ide-completion/src/tests/item.rs b/crates/ide-completion/src/tests/item.rs
index e4b70f97e3..8f2f8a548a 100644
--- a/crates/ide-completion/src/tests/item.rs
+++ b/crates/ide-completion/src/tests/item.rs
@@ -27,9 +27,9 @@ impl Tra$0
             tt Trait
             un Union
             bt u32
-            kw crate::
-            kw self::
-            kw super::
+            kw crate
+            kw self
+            kw super
         "#]],
     )
 }
@@ -50,9 +50,9 @@ impl Trait for Str$0
             tt Trait
             un Union
             bt u32
-            kw crate::
-            kw self::
-            kw super::
+            kw crate
+            kw self
+            kw super
         "#]],
     )
 }
@@ -95,7 +95,7 @@ fn after_struct_name() {
             ma makro!(…)           macro_rules! makro
             md module
             kw const
-            kw crate::
+            kw crate
             kw enum
             kw extern
             kw fn
@@ -104,10 +104,10 @@ fn after_struct_name() {
             kw pub
             kw pub(crate)
             kw pub(super)
-            kw self::
+            kw self
             kw static
             kw struct
-            kw super::
+            kw super
             kw trait
             kw type
             kw union
@@ -129,7 +129,7 @@ fn after_fn_name() {
             ma makro!(…)           macro_rules! makro
             md module
             kw const
-            kw crate::
+            kw crate
             kw enum
             kw extern
             kw fn
@@ -138,10 +138,10 @@ fn after_fn_name() {
             kw pub
             kw pub(crate)
             kw pub(super)
-            kw self::
+            kw self
             kw static
             kw struct
-            kw super::
+            kw super
             kw trait
             kw type
             kw union
diff --git a/crates/ide-completion/src/tests/item_list.rs b/crates/ide-completion/src/tests/item_list.rs
index f320812936..40d3b90da9 100644
--- a/crates/ide-completion/src/tests/item_list.rs
+++ b/crates/ide-completion/src/tests/item_list.rs
@@ -15,7 +15,7 @@ fn in_mod_item_list() {
         expect![[r#"
             ma makro!(…)           macro_rules! makro
             kw const
-            kw crate::
+            kw crate
             kw enum
             kw extern
             kw fn
@@ -24,10 +24,10 @@ fn in_mod_item_list() {
             kw pub
             kw pub(crate)
             kw pub(super)
-            kw self::
+            kw self
             kw static
             kw struct
-            kw super::
+            kw super
             kw trait
             kw type
             kw union
@@ -48,7 +48,7 @@ fn in_source_file_item_list() {
             ma makro!(…)           macro_rules! makro
             md module
             kw const
-            kw crate::
+            kw crate
             kw enum
             kw extern
             kw fn
@@ -57,10 +57,10 @@ fn in_source_file_item_list() {
             kw pub
             kw pub(crate)
             kw pub(super)
-            kw self::
+            kw self
             kw static
             kw struct
-            kw super::
+            kw super
             kw trait
             kw type
             kw union
@@ -166,13 +166,13 @@ fn in_impl_assoc_item_list() {
             ma makro!(…)  macro_rules! makro
             md module
             kw const
-            kw crate::
+            kw crate
             kw fn
             kw pub
             kw pub(crate)
             kw pub(super)
-            kw self::
-            kw super::
+            kw self
+            kw super
             kw type
             kw unsafe
         "#]],
@@ -203,10 +203,10 @@ fn in_trait_assoc_item_list() {
             ma makro!(…) macro_rules! makro
             md module
             kw const
-            kw crate::
+            kw crate
             kw fn
-            kw self::
-            kw super::
+            kw self
+            kw super
             kw type
             kw unsafe
         "#]],
@@ -240,13 +240,13 @@ impl Test for () {
             md module
             ta type Type1 =
             kw const
-            kw crate::
+            kw crate
             kw fn
             kw pub
             kw pub(crate)
             kw pub(super)
-            kw self::
-            kw super::
+            kw self
+            kw super
             kw type
             kw unsafe
         "#]],
diff --git a/crates/ide-completion/src/tests/pattern.rs b/crates/ide-completion/src/tests/pattern.rs
index 3ecb2f1637..b353883bc7 100644
--- a/crates/ide-completion/src/tests/pattern.rs
+++ b/crates/ide-completion/src/tests/pattern.rs
@@ -394,9 +394,6 @@ fn foo() {
 }
 "#,
         expect![[r#"
-            fn foo()   fn()
-            st Bar
-            bt u32
             kw crate::
             kw self::
             kw super::
@@ -406,18 +403,19 @@ fn foo() {
         r#"
 struct Foo { bar: u32 }
 fn foo() {
-    match (Foo { bar: 0 }) {
+    match Foo { bar: 0 } {
         F$0 { bar } => {}
     }
 }
 "#,
         expect![[r#"
-            fn foo()   fn()
+            fn foo()  fn()
             st Foo
             bt u32
-            kw crate::
-            kw self::
-            kw super::
+            kw crate
+            kw return
+            kw self
+            kw super
         "#]],
     );
     check_empty(
diff --git a/crates/ide-completion/src/tests/predicate.rs b/crates/ide-completion/src/tests/predicate.rs
index f2b9c061dc..cc18bf4453 100644
--- a/crates/ide-completion/src/tests/predicate.rs
+++ b/crates/ide-completion/src/tests/predicate.rs
@@ -26,9 +26,9 @@ struct Foo<'lt, T, const C: usize> where $0 {}
             tt Trait
             un Union
             bt u32
-            kw crate::
-            kw self::
-            kw super::
+            kw crate
+            kw self
+            kw super
         "#]],
     );
 }
@@ -43,9 +43,9 @@ struct Foo<'lt, T, const C: usize> where T: $0 {}
             ma makro!(…) macro_rules! makro
             md module
             tt Trait
-            kw crate::
-            kw self::
-            kw super::
+            kw crate
+            kw self
+            kw super
         "#]],
     );
 }
@@ -62,9 +62,9 @@ struct Foo<'lt, T, const C: usize> where 'lt: $0 {}
             ma makro!(…) macro_rules! makro
             md module
             tt Trait
-            kw crate::
-            kw self::
-            kw super::
+            kw crate
+            kw self
+            kw super
         "#]],
     );
 }
@@ -79,9 +79,9 @@ struct Foo<'lt, T, const C: usize> where for<'a> T: $0 {}
             ma makro!(…) macro_rules! makro
             md module
             tt Trait
-            kw crate::
-            kw self::
-            kw super::
+            kw crate
+            kw self
+            kw super
         "#]],
     );
 }
@@ -103,9 +103,9 @@ struct Foo<'lt, T, const C: usize> where for<'a> $0 {}
             tt Trait
             un Union
             bt u32
-            kw crate::
-            kw self::
-            kw super::
+            kw crate
+            kw self
+            kw super
         "#]],
     );
 }
@@ -129,9 +129,9 @@ impl Record {
             tt Trait
             un Union
             bt u32
-            kw crate::
-            kw self::
-            kw super::
+            kw crate
+            kw self
+            kw super
         "#]],
     );
 }
diff --git a/crates/ide-completion/src/tests/record.rs b/crates/ide-completion/src/tests/record.rs
index a0f8b24867..bae5ddb539 100644
--- a/crates/ide-completion/src/tests/record.rs
+++ b/crates/ide-completion/src/tests/record.rs
@@ -167,7 +167,7 @@ fn main() {
             tt Default
             tt Sized
             bt u32
-            kw crate::
+            kw crate
             kw false
             kw for
             kw if
@@ -175,8 +175,8 @@ fn main() {
             kw loop
             kw match
             kw return
-            kw self::
-            kw super::
+            kw self
+            kw super
             kw true
             kw unsafe
             kw while
diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs
deleted file mode 100644
index 6195537a18..0000000000
--- a/crates/ide-completion/src/tests/special.rs
+++ /dev/null
@@ -1,638 +0,0 @@
-//! Tests that don't fit into a specific category.
-
-use expect_test::{expect, Expect};
-
-use crate::tests::{check_edit, completion_list_no_kw};
-
-fn check(ra_fixture: &str, expect: Expect) {
-    let actual = completion_list_no_kw(ra_fixture);
-    expect.assert_eq(&actual)
-}
-
-#[test]
-fn completes_if_prefix_is_keyword() {
-    check_edit(
-        "wherewolf",
-        r#"
-fn main() {
-    let wherewolf = 92;
-    drop(where$0)
-}
-"#,
-        r#"
-fn main() {
-    let wherewolf = 92;
-    drop(wherewolf)
-}
-"#,
-    )
-}
-
-/// Regression test for issue #6091.
-#[test]
-fn correctly_completes_module_items_prefixed_with_underscore() {
-    check_edit(
-        "_alpha",
-        r#"
-fn main() {
-    _$0
-}
-fn _alpha() {}
-"#,
-        r#"
-fn main() {
-    _alpha()$0
-}
-fn _alpha() {}
-"#,
-    )
-}
-
-#[test]
-fn completes_prelude() {
-    check(
-        r#"
-//- /main.rs crate:main deps:std
-fn foo() { let x: $0 }
-
-//- /std/lib.rs crate:std
-pub mod prelude {
-    pub mod rust_2018 {
-        pub struct Option;
-    }
-}
-"#,
-        expect![[r#"
-                md std
-                st Option
-                bt u32
-            "#]],
-    );
-}
-
-#[test]
-fn completes_prelude_macros() {
-    check(
-        r#"
-//- /main.rs crate:main deps:std
-fn f() {$0}
-
-//- /std/lib.rs crate:std
-pub mod prelude {
-    pub mod rust_2018 {
-        pub use crate::concat;
-    }
-}
-
-mod macros {
-    #[rustc_builtin_macro]
-    #[macro_export]
-    macro_rules! concat { }
-}
-"#,
-        expect![[r#"
-                fn f()        fn()
-                ma concat!(…) macro_rules! concat
-                md std
-                bt u32
-            "#]],
-    );
-}
-
-#[test]
-fn completes_std_prelude_if_core_is_defined() {
-    check(
-        r#"
-//- /main.rs crate:main deps:core,std
-fn foo() { let x: $0 }
-
-//- /core/lib.rs crate:core
-pub mod prelude {
-    pub mod rust_2018 {
-        pub struct Option;
-    }
-}
-
-//- /std/lib.rs crate:std deps:core
-pub mod prelude {
-    pub mod rust_2018 {
-        pub struct String;
-    }
-}
-"#,
-        expect![[r#"
-                md core
-                md std
-                st String
-                bt u32
-            "#]],
-    );
-}
-
-#[test]
-fn respects_doc_hidden() {
-    check(
-        r#"
-//- /lib.rs crate:lib deps:std
-fn f() {
-    format_$0
-}
-
-//- /std.rs crate:std
-#[doc(hidden)]
-#[macro_export]
-macro_rules! format_args_nl {
-    () => {}
-}
-
-pub mod prelude {
-    pub mod rust_2018 {}
-}
-            "#,
-        expect![[r#"
-                fn f() fn()
-                md std
-                bt u32
-            "#]],
-    );
-}
-
-#[test]
-fn respects_doc_hidden_in_assoc_item_list() {
-    check(
-        r#"
-//- /lib.rs crate:lib deps:std
-struct S;
-impl S {
-    format_$0
-}
-
-//- /std.rs crate:std
-#[doc(hidden)]
-#[macro_export]
-macro_rules! format_args_nl {
-    () => {}
-}
-
-pub mod prelude {
-    pub mod rust_2018 {}
-}
-            "#,
-        expect![[r#"
-                md std
-            "#]],
-    );
-}
-
-#[test]
-fn associated_item_visibility() {
-    check(
-        r#"
-//- /lib.rs crate:lib new_source_root:library
-pub struct S;
-
-impl S {
-    pub fn public_method() { }
-    fn private_method() { }
-    pub type PublicType = u32;
-    type PrivateType = u32;
-    pub const PUBLIC_CONST: u32 = 1;
-    const PRIVATE_CONST: u32 = 1;
-}
-
-//- /main.rs crate:main deps:lib new_source_root:local
-fn foo() { let _ = lib::S::$0 }
-"#,
-        expect![[r#"
-                ct PUBLIC_CONST    pub const PUBLIC_CONST: u32
-                fn public_method() fn()
-                ta PublicType      pub type PublicType = u32
-            "#]],
-    );
-}
-
-#[test]
-fn completes_union_associated_method() {
-    check(
-        r#"
-union U {};
-impl U { fn m() { } }
-
-fn foo() { let _ = U::$0 }
-"#,
-        expect![[r#"
-                fn m() fn()
-            "#]],
-    );
-}
-
-#[test]
-fn completes_trait_associated_method_1() {
-    check(
-        r#"
-trait Trait { fn m(); }
-
-fn foo() { let _ = Trait::$0 }
-"#,
-        expect![[r#"
-                fn m() (as Trait) fn()
-            "#]],
-    );
-}
-
-#[test]
-fn completes_trait_associated_method_2() {
-    check(
-        r#"
-trait Trait { fn m(); }
-
-struct S;
-impl Trait for S {}
-
-fn foo() { let _ = S::$0 }
-"#,
-        expect![[r#"
-                fn m() (as Trait) fn()
-            "#]],
-    );
-}
-
-#[test]
-fn completes_trait_associated_method_3() {
-    check(
-        r#"
-trait Trait { fn m(); }
-
-struct S;
-impl Trait for S {}
-
-fn foo() { let _ = <S as Trait>::$0 }
-"#,
-        expect![[r#"
-                fn m() (as Trait) fn()
-            "#]],
-    );
-}
-
-#[test]
-fn completes_ty_param_assoc_ty() {
-    check(
-        r#"
-trait Super {
-    type Ty;
-    const CONST: u8;
-    fn func() {}
-    fn method(&self) {}
-}
-
-trait Sub: Super {
-    type SubTy;
-    const C2: ();
-    fn subfunc() {}
-    fn submethod(&self) {}
-}
-
-fn foo<T: Sub>() { T::$0 }
-"#,
-        expect![[r#"
-                ct C2 (as Sub)           const C2: ()
-                ct CONST (as Super)      const CONST: u8
-                fn func() (as Super)     fn()
-                fn subfunc() (as Sub)    fn()
-                ta SubTy (as Sub)        type SubTy
-                ta Ty (as Super)         type Ty
-                me method(…) (as Super)  fn(&self)
-                me submethod(…) (as Sub) fn(&self)
-            "#]],
-    );
-}
-
-#[test]
-fn completes_self_param_assoc_ty() {
-    check(
-        r#"
-trait Super {
-    type Ty;
-    const CONST: u8 = 0;
-    fn func() {}
-    fn method(&self) {}
-}
-
-trait Sub: Super {
-    type SubTy;
-    const C2: () = ();
-    fn subfunc() {}
-    fn submethod(&self) {}
-}
-
-struct Wrap<T>(T);
-impl<T> Super for Wrap<T> {}
-impl<T> Sub for Wrap<T> {
-    fn subfunc() {
-        // Should be able to assume `Self: Sub + Super`
-        Self::$0
-    }
-}
-"#,
-        expect![[r#"
-                ct C2 (as Sub)           const C2: ()
-                ct CONST (as Super)      const CONST: u8
-                fn func() (as Super)     fn()
-                fn subfunc() (as Sub)    fn()
-                ta SubTy (as Sub)        type SubTy
-                ta Ty (as Super)         type Ty
-                me method(…) (as Super)  fn(&self)
-                me submethod(…) (as Sub) fn(&self)
-            "#]],
-    );
-}
-
-#[test]
-fn completes_type_alias() {
-    check(
-        r#"
-struct S;
-impl S { fn foo() {} }
-type T = S;
-impl T { fn bar() {} }
-
-fn main() { T::$0; }
-"#,
-        expect![[r#"
-                fn bar() fn()
-                fn foo() fn()
-            "#]],
-    );
-}
-
-#[test]
-fn completes_qualified_macros() {
-    check(
-        r#"
-#[macro_export]
-macro_rules! foo { () => {} }
-
-fn main() { let _ = crate::$0 }
-"#,
-        expect![[r#"
-                fn main()  fn()
-                ma foo!(…) macro_rules! foo
-            "#]],
-    );
-}
-
-#[test]
-fn does_not_complete_non_fn_macros() {
-    check(
-        r#"
-mod m {
-    #[rustc_builtin_macro]
-    pub macro Clone {}
-}
-
-fn f() {m::$0}
-"#,
-        expect![[r#""#]],
-    );
-    check(
-        r#"
-mod m {
-    #[rustc_builtin_macro]
-    pub macro bench {}
-}
-
-fn f() {m::$0}
-"#,
-        expect![[r#""#]],
-    );
-}
-
-#[test]
-fn completes_reexported_items_under_correct_name() {
-    check(
-        r#"
-fn foo() { self::m::$0 }
-
-mod m {
-    pub use super::p::wrong_fn as right_fn;
-    pub use super::p::WRONG_CONST as RIGHT_CONST;
-    pub use super::p::WrongType as RightType;
-}
-mod p {
-    fn wrong_fn() {}
-    const WRONG_CONST: u32 = 1;
-    struct WrongType {};
-}
-"#,
-        expect![[r#"
-                ct RIGHT_CONST
-                fn right_fn()  fn()
-                st RightType
-            "#]],
-    );
-
-    check_edit(
-        "RightType",
-        r#"
-fn foo() { self::m::$0 }
-
-mod m {
-    pub use super::p::wrong_fn as right_fn;
-    pub use super::p::WRONG_CONST as RIGHT_CONST;
-    pub use super::p::WrongType as RightType;
-}
-mod p {
-    fn wrong_fn() {}
-    const WRONG_CONST: u32 = 1;
-    struct WrongType {};
-}
-"#,
-        r#"
-fn foo() { self::m::RightType }
-
-mod m {
-    pub use super::p::wrong_fn as right_fn;
-    pub use super::p::WRONG_CONST as RIGHT_CONST;
-    pub use super::p::WrongType as RightType;
-}
-mod p {
-    fn wrong_fn() {}
-    const WRONG_CONST: u32 = 1;
-    struct WrongType {};
-}
-"#,
-    );
-}
-
-#[test]
-fn completes_in_simple_macro_call() {
-    check(
-        r#"
-macro_rules! m { ($e:expr) => { $e } }
-fn main() { m!(self::f$0); }
-fn foo() {}
-"#,
-        expect![[r#"
-                fn foo()  fn()
-                fn main() fn()
-            "#]],
-    );
-}
-
-#[test]
-fn function_mod_share_name() {
-    check(
-        r#"
-fn foo() { self::m::$0 }
-
-mod m {
-    pub mod z {}
-    pub fn z() {}
-}
-"#,
-        expect![[r#"
-                fn z() fn()
-                md z
-            "#]],
-    );
-}
-
-#[test]
-fn completes_hashmap_new() {
-    check(
-        r#"
-struct RandomState;
-struct HashMap<K, V, S = RandomState> {}
-
-impl<K, V> HashMap<K, V, RandomState> {
-    pub fn new() -> HashMap<K, V, RandomState> { }
-}
-fn foo() {
-    HashMap::$0
-}
-"#,
-        expect![[r#"
-                fn new() fn() -> HashMap<K, V, RandomState>
-            "#]],
-    );
-}
-
-#[test]
-fn completes_variant_through_self() {
-    check(
-        r#"
-enum Foo {
-    Bar,
-    Baz,
-}
-
-impl Foo {
-    fn foo(self) {
-        Self::$0
-    }
-}
-"#,
-        expect![[r#"
-                ev Bar    Bar
-                ev Baz    Baz
-                me foo(…) fn(self)
-            "#]],
-    );
-}
-
-#[test]
-fn completes_primitive_assoc_const() {
-    cov_mark::check!(completes_primitive_assoc_const);
-    check(
-        r#"
-//- /lib.rs crate:lib deps:core
-fn f() {
-    u8::$0
-}
-
-//- /core.rs crate:core
-#[lang = "u8"]
-impl u8 {
-    pub const MAX: Self = 255;
-
-    pub fn func(self) {}
-}
-"#,
-        expect![[r#"
-                ct MAX     pub const MAX: Self
-                me func(…) fn(self)
-            "#]],
-    );
-}
-
-#[test]
-fn completes_variant_through_alias() {
-    cov_mark::check!(completes_variant_through_alias);
-    check(
-        r#"
-enum Foo {
-    Bar
-}
-type Foo2 = Foo;
-fn main() {
-    Foo2::$0
-}
-"#,
-        expect![[r#"
-                ev Bar Bar
-            "#]],
-    );
-}
-
-#[test]
-fn respects_doc_hidden2() {
-    cov_mark::check!(qualified_path_doc_hidden);
-    check(
-        r#"
-//- /lib.rs crate:lib deps:dep
-fn f() {
-    dep::$0
-}
-
-//- /dep.rs crate:dep
-#[doc(hidden)]
-#[macro_export]
-macro_rules! m {
-    () => {}
-}
-
-#[doc(hidden)]
-pub fn f() {}
-
-#[doc(hidden)]
-pub struct S;
-
-#[doc(hidden)]
-pub mod m {}
-            "#,
-        expect![[r#""#]],
-    )
-}
-
-#[test]
-fn type_anchor_empty() {
-    check(
-        r#"
-trait Foo {
-    fn foo() -> Self;
-}
-struct Bar;
-impl Foo for Bar {
-    fn foo() -> {
-        Bar
-    }
-}
-fn bar() -> Bar {
-    <_>::$0
-}
-"#,
-        expect![[r#"
-                fn foo() (as Foo) fn() -> Self
-            "#]],
-    )
-}
diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs
index 5224bc4b48..fcbb2e05a7 100644
--- a/crates/ide-completion/src/tests/type_pos.rs
+++ b/crates/ide-completion/src/tests/type_pos.rs
@@ -29,9 +29,9 @@ struct Foo<'lt, T, const C: usize> {
             tp T
             un Union
             bt u32
-            kw crate::
-            kw self::
-            kw super::
+            kw crate
+            kw self
+            kw super
         "#]],
     )
 }
@@ -55,12 +55,12 @@ struct Foo<'lt, T, const C: usize>(f$0);
             tp T
             un Union
             bt u32
-            kw crate::
+            kw crate
             kw pub
             kw pub(crate)
             kw pub(super)
-            kw self::
-            kw super::
+            kw self
+            kw super
         "#]],
     )
 }
@@ -82,9 +82,9 @@ fn x<'lt, T, const C: usize>() -> $0
             tp T
             un Union
             bt u32
-            kw crate::
-            kw self::
-            kw super::
+            kw crate
+            kw self
+            kw super
         "#]],
     );
 }
@@ -108,9 +108,9 @@ const FOO: $0 = Foo(2);
             un Union
             bt u32
             it Foo<i32>
-            kw crate::
-            kw self::
-            kw super::
+            kw crate
+            kw self
+            kw super
         "#]],
     );
 }
@@ -135,9 +135,9 @@ fn f2() {
             un Union
             bt u32
             it i32
-            kw crate::
-            kw self::
-            kw super::
+            kw crate
+            kw self
+            kw super
         "#]],
     );
 }
@@ -164,9 +164,9 @@ fn f2() {
             un Union
             bt u32
             it u64
-            kw crate::
-            kw self::
-            kw super::
+            kw crate
+            kw self
+            kw super
         "#]],
     );
 }
@@ -190,9 +190,9 @@ fn f2(x: u64) -> $0 {
             un Union
             bt u32
             it u64
-            kw crate::
-            kw self::
-            kw super::
+            kw crate
+            kw self
+            kw super
         "#]],
     );
 }
@@ -217,9 +217,9 @@ fn f2(x: $0) {
             un Union
             bt u32
             it i32
-            kw crate::
-            kw self::
-            kw super::
+            kw crate
+            kw self
+            kw super
         "#]],
     );
 }
@@ -252,9 +252,9 @@ fn foo<'lt, T, const C: usize>() {
             un Union
             bt u32
             it a::Foo<a::Foo<i32>>
-            kw crate::
-            kw self::
-            kw super::
+            kw crate
+            kw self
+            kw super
         "#]],
     );
 }
@@ -282,9 +282,9 @@ fn foo<'lt, T, const C: usize>() {
             un Union
             bt u32
             it Foo<i32>
-            kw crate::
-            kw self::
-            kw super::
+            kw crate
+            kw self
+            kw super
         "#]],
     );
 }
@@ -309,9 +309,9 @@ fn foo<'lt, T, const C: usize>() {
             tp T
             un Union
             bt u32
-            kw crate::
-            kw self::
-            kw super::
+            kw crate
+            kw self
+            kw super
         "#]],
     );
     check(
@@ -359,9 +359,9 @@ fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {}
             tp T
             un Union
             bt u32
-            kw crate::
-            kw self::
-            kw super::
+            kw crate
+            kw self
+            kw super
         "#]],
     );
     check(
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
index d7d6c79e3d..60bc290121 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -119,13 +119,13 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
     <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="numeric_literal">-</span><span class="numeric_literal">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
     <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="numeric_literal">27</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">0</span><span class="numeric_literal">.</span><span class="numeric_literal">01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">0</span><span class="numeric_literal">.</span><span class="numeric_literal">01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="numeric_literal">0</span><span class="numeric_literal">.</span><span class="numeric_literal">01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span>    <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="numeric_literal">0</span><span class="numeric_literal">.</span><span class="numeric_literal">01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span>   <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="numeric_literal">0</span><span class="numeric_literal">.</span><span class="numeric_literal">01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> prec <span class="operator">=</span> <span class="numeric_literal">5</span><span class="comma">,</span> number <span class="operator">=</span> <span class="numeric_literal">0</span><span class="numeric_literal">.</span><span class="numeric_literal">01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 fractional digits"</span><span class="comma">,</span> <span class="string_literal">"Hello"</span><span class="comma">,</span> <span class="numeric_literal">3</span><span class="comma">,</span> name<span class="operator">=</span><span class="numeric_literal">1234</span><span class="numeric_literal">.</span><span class="numeric_literal">56</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span>    <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span>   <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> prec <span class="operator">=</span> <span class="numeric_literal">5</span><span class="comma">,</span> number <span class="operator">=</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 fractional digits"</span><span class="comma">,</span> <span class="string_literal">"Hello"</span><span class="comma">,</span> <span class="numeric_literal">3</span><span class="comma">,</span> name<span class="operator">=</span><span class="numeric_literal">1234.56</span><span class="parenthesis">)</span><span class="semicolon">;</span>
     <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 characters"</span><span class="comma">,</span> <span class="string_literal">"Hello"</span><span class="comma">,</span> <span class="numeric_literal">3</span><span class="comma">,</span> name<span class="operator">=</span><span class="string_literal">"1234.56"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
     <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 right-aligned characters"</span><span class="comma">,</span> <span class="string_literal">"Hello"</span><span class="comma">,</span> <span class="numeric_literal">3</span><span class="comma">,</span> name<span class="operator">=</span><span class="string_literal">"1234.56"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
     <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello {{}}"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
diff --git a/crates/parser/test_data/lexer/err/empty_exponent.rast b/crates/parser/test_data/lexer/err/empty_exponent.rast
index 73de4cac24..af03d73ced 100644
--- a/crates/parser/test_data/lexer/err/empty_exponent.rast
+++ b/crates/parser/test_data/lexer/err/empty_exponent.rast
@@ -1,14 +1,14 @@
-FLOAT_NUMBER_START_0 "0e" error: Missing digits after the exponent symbol
+FLOAT_NUMBER "0e" error: Missing digits after the exponent symbol
 WHITESPACE "\n"
-FLOAT_NUMBER_START_0 "0E" error: Missing digits after the exponent symbol
+FLOAT_NUMBER "0E" error: Missing digits after the exponent symbol
 WHITESPACE "\n\n"
-FLOAT_NUMBER_START_0 "42e+" error: Missing digits after the exponent symbol
+FLOAT_NUMBER "42e+" error: Missing digits after the exponent symbol
 WHITESPACE "\n"
-FLOAT_NUMBER_START_0 "42e-" error: Missing digits after the exponent symbol
+FLOAT_NUMBER "42e-" error: Missing digits after the exponent symbol
 WHITESPACE "\n"
-FLOAT_NUMBER_START_0 "42E+" error: Missing digits after the exponent symbol
+FLOAT_NUMBER "42E+" error: Missing digits after the exponent symbol
 WHITESPACE "\n"
-FLOAT_NUMBER_START_0 "42E-" error: Missing digits after the exponent symbol
+FLOAT_NUMBER "42E-" error: Missing digits after the exponent symbol
 WHITESPACE "\n\n"
 INT_NUMBER "42"
 DOT "."
@@ -30,35 +30,19 @@ DOT "."
 IDENT "E"
 MINUS "-"
 WHITESPACE "\n\n"
-FLOAT_NUMBER_START_2 "42"
-DOT "."
-FLOAT_NUMBER_PART "2e+" error: Missing digits after the exponent symbol
+FLOAT_NUMBER "42.2e+" error: Missing digits after the exponent symbol
 WHITESPACE "\n"
-FLOAT_NUMBER_START_2 "42"
-DOT "."
-FLOAT_NUMBER_PART "2e-" error: Missing digits after the exponent symbol
+FLOAT_NUMBER "42.2e-" error: Missing digits after the exponent symbol
 WHITESPACE "\n"
-FLOAT_NUMBER_START_2 "42"
-DOT "."
-FLOAT_NUMBER_PART "2E+" error: Missing digits after the exponent symbol
+FLOAT_NUMBER "42.2E+" error: Missing digits after the exponent symbol
 WHITESPACE "\n"
-FLOAT_NUMBER_START_2 "42"
-DOT "."
-FLOAT_NUMBER_PART "2E-" error: Missing digits after the exponent symbol
+FLOAT_NUMBER "42.2E-" error: Missing digits after the exponent symbol
 WHITESPACE "\n\n"
-FLOAT_NUMBER_START_2 "42"
-DOT "."
-FLOAT_NUMBER_PART "2e+f32" error: Missing digits after the exponent symbol
+FLOAT_NUMBER "42.2e+f32" error: Missing digits after the exponent symbol
 WHITESPACE "\n"
-FLOAT_NUMBER_START_2 "42"
-DOT "."
-FLOAT_NUMBER_PART "2e-f32" error: Missing digits after the exponent symbol
+FLOAT_NUMBER "42.2e-f32" error: Missing digits after the exponent symbol
 WHITESPACE "\n"
-FLOAT_NUMBER_START_2 "42"
-DOT "."
-FLOAT_NUMBER_PART "2E+f32" error: Missing digits after the exponent symbol
+FLOAT_NUMBER "42.2E+f32" error: Missing digits after the exponent symbol
 WHITESPACE "\n"
-FLOAT_NUMBER_START_2 "42"
-DOT "."
-FLOAT_NUMBER_PART "2E-f32" error: Missing digits after the exponent symbol
+FLOAT_NUMBER "42.2E-f32" error: Missing digits after the exponent symbol
 WHITESPACE "\n"
diff --git a/crates/parser/test_data/lexer/ok/numbers.rast b/crates/parser/test_data/lexer/ok/numbers.rast
index 428bdf8a1f..8d13c3f610 100644
--- a/crates/parser/test_data/lexer/ok/numbers.rast
+++ b/crates/parser/test_data/lexer/ok/numbers.rast
@@ -4,8 +4,7 @@ INT_NUMBER "00"
 WHITESPACE " "
 INT_NUMBER "0_"
 WHITESPACE " "
-FLOAT_NUMBER_START_1 "0"
-DOT "."
+FLOAT_NUMBER "0."
 WHITESPACE " "
 INT_NUMBER "0z"
 WHITESPACE "\n"
@@ -21,13 +20,11 @@ INT_NUMBER "001279"
 WHITESPACE " "
 INT_NUMBER "0_1279"
 WHITESPACE " "
-FLOAT_NUMBER_START_2 "0"
-DOT "."
-FLOAT_NUMBER_PART "1279"
+FLOAT_NUMBER "0.1279"
 WHITESPACE " "
-FLOAT_NUMBER_START_0 "0e1279"
+FLOAT_NUMBER "0e1279"
 WHITESPACE " "
-FLOAT_NUMBER_START_0 "0E1279"
+FLOAT_NUMBER "0E1279"
 WHITESPACE "\n"
 INT_NUMBER "0"
 DOT "."
@@ -40,7 +37,7 @@ IDENT "foo"
 L_PAREN "("
 R_PAREN ")"
 WHITESPACE "\n"
-FLOAT_NUMBER_START_0 "0e+1"
+FLOAT_NUMBER "0e+1"
 WHITESPACE "\n"
 INT_NUMBER "0"
 DOT "."
@@ -48,19 +45,13 @@ IDENT "e"
 PLUS "+"
 INT_NUMBER "1"
 WHITESPACE "\n"
-FLOAT_NUMBER_START_2 "0"
-DOT "."
-FLOAT_NUMBER_PART "0E-2"
+FLOAT_NUMBER "0.0E-2"
 WHITESPACE "\n"
-FLOAT_NUMBER_START_2 "0___0"
-DOT "."
-FLOAT_NUMBER_PART "10000____0000e+111__"
+FLOAT_NUMBER "0___0.10000____0000e+111__"
 WHITESPACE "\n"
 INT_NUMBER "1i64"
 WHITESPACE " "
-FLOAT_NUMBER_START_2 "92"
-DOT "."
-FLOAT_NUMBER_PART "0f32"
+FLOAT_NUMBER "92.0f32"
 WHITESPACE " "
 INT_NUMBER "11__s"
 WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/err/0023_mismatched_paren.rast b/crates/parser/test_data/parser/err/0023_mismatched_paren.rast
index 70cd030da1..4064a7a1ff 100644
--- a/crates/parser/test_data/parser/err/0023_mismatched_paren.rast
+++ b/crates/parser/test_data/parser/err/0023_mismatched_paren.rast
@@ -32,9 +32,7 @@ SOURCE_FILE
               INT_NUMBER "1"
               COMMA ","
               WHITESPACE " "
-              FLOAT_NUMBER_START_2 "2"
-              DOT "."
-              FLOAT_NUMBER_PART "0"
+              FLOAT_NUMBER "2.0"
         WHITESPACE "\n    "
         R_CURLY "}"
   WHITESPACE " "
diff --git a/crates/parser/test_data/parser/inline/ok/0011_field_expr.rast b/crates/parser/test_data/parser/inline/ok/0011_field_expr.rast
index 19fab593fa..8498724b9e 100644
--- a/crates/parser/test_data/parser/inline/ok/0011_field_expr.rast
+++ b/crates/parser/test_data/parser/inline/ok/0011_field_expr.rast
@@ -40,39 +40,6 @@ SOURCE_FILE
               IDENT "bar"
           SEMICOLON ";"
         WHITESPACE "\n    "
-        EXPR_STMT
-          FIELD_EXPR
-            FIELD_EXPR
-              PATH_EXPR
-                PATH
-                  PATH_SEGMENT
-                    NAME_REF
-                      IDENT "x"
-              DOT "."
-              NAME_REF
-                INT_NUMBER "0"
-            DOT "."
-            WHITESPACE " "
-            NAME_REF
-              IDENT "bar"
-          SEMICOLON ";"
-        WHITESPACE "\n    "
-        EXPR_STMT
-          FIELD_EXPR
-            FIELD_EXPR
-              PATH_EXPR
-                PATH
-                  PATH_SEGMENT
-                    NAME_REF
-                      IDENT "x"
-              DOT "."
-              NAME_REF
-                INT_NUMBER "0"
-            DOT "."
-            NAME_REF
-              INT_NUMBER "1"
-          SEMICOLON ";"
-        WHITESPACE "\n    "
         EXPR_STMT
           CALL_EXPR
             FIELD_EXPR
diff --git a/crates/parser/test_data/parser/inline/ok/0011_field_expr.rs b/crates/parser/test_data/parser/inline/ok/0011_field_expr.rs
index 551b1ecaf0..b8da2ddc30 100644
--- a/crates/parser/test_data/parser/inline/ok/0011_field_expr.rs
+++ b/crates/parser/test_data/parser/inline/ok/0011_field_expr.rs
@@ -1,7 +1,5 @@
 fn foo() {
     x.foo;
     x.0.bar;
-    x.0. bar;
-    x.0.1;
     x.0();
 }
diff --git a/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rast b/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rast
index b3236976b9..403c265ea3 100644
--- a/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rast
+++ b/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rast
@@ -57,10 +57,7 @@ SOURCE_FILE
           EQ "="
           WHITESPACE " "
           LITERAL
-            FLOAT_LITERAL
-              FLOAT_NUMBER_START_2 "2"
-              DOT "."
-              FLOAT_NUMBER_PART "0"
+            FLOAT_NUMBER "2.0"
           SEMICOLON ";"
         WHITESPACE "\n    "
         LET_STMT
diff --git a/crates/parser/test_data/parser/inline/ok/0107_method_call_expr.rast b/crates/parser/test_data/parser/inline/ok/0107_method_call_expr.rast
index 69f1055b7e..dcbcfe1231 100644
--- a/crates/parser/test_data/parser/inline/ok/0107_method_call_expr.rast
+++ b/crates/parser/test_data/parser/inline/ok/0107_method_call_expr.rast
@@ -58,32 +58,6 @@ SOURCE_FILE
               COMMA ","
               R_PAREN ")"
           SEMICOLON ";"
-        WHITESPACE "\n\n    "
-        EXPR_STMT
-          METHOD_CALL_EXPR
-            LITERAL
-              FLOAT_LITERAL
-                FLOAT_NUMBER_START_0 "0e0"
-            DOT "."
-            NAME_REF
-              IDENT "sin"
-            ARG_LIST
-              L_PAREN "("
-              R_PAREN ")"
-          SEMICOLON ";"
-        WHITESPACE "\n    "
-        EXPR_STMT
-          METHOD_CALL_EXPR
-            LITERAL
-              FLOAT_LITERAL
-                FLOAT_NUMBER_START_0 "0e0f32"
-            DOT "."
-            NAME_REF
-              IDENT "sin"
-            ARG_LIST
-              L_PAREN "("
-              R_PAREN ")"
-          SEMICOLON ";"
         WHITESPACE "\n"
         R_CURLY "}"
   WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/0107_method_call_expr.rs b/crates/parser/test_data/parser/inline/ok/0107_method_call_expr.rs
index 3e5d464e23..1a3aa35ae8 100644
--- a/crates/parser/test_data/parser/inline/ok/0107_method_call_expr.rs
+++ b/crates/parser/test_data/parser/inline/ok/0107_method_call_expr.rs
@@ -1,7 +1,4 @@
 fn foo() {
     x.foo();
     y.bar::<T>(1, 2,);
-
-    0e0.sin();
-    0e0f32.sin();
 }
diff --git a/crates/parser/test_data/parser/inline/ok/0201_float_literal.rast b/crates/parser/test_data/parser/inline/ok/0201_float_literal.rast
deleted file mode 100644
index df4fb6eb41..0000000000
--- a/crates/parser/test_data/parser/inline/ok/0201_float_literal.rast
+++ /dev/null
@@ -1,51 +0,0 @@
-SOURCE_FILE
-  FN
-    FN_KW "fn"
-    WHITESPACE " "
-    NAME
-      IDENT "f"
-    PARAM_LIST
-      L_PAREN "("
-      R_PAREN ")"
-    WHITESPACE " "
-    BLOCK_EXPR
-      STMT_LIST
-        L_CURLY "{"
-        WHITESPACE "\n    "
-        EXPR_STMT
-          LITERAL
-            FLOAT_LITERAL
-              FLOAT_NUMBER_START_2 "0"
-              DOT "."
-              FLOAT_NUMBER_PART "0"
-          SEMICOLON ";"
-        WHITESPACE "\n    "
-        EXPR_STMT
-          LITERAL
-            FLOAT_LITERAL
-              FLOAT_NUMBER_START_1 "1"
-              DOT "."
-          SEMICOLON ";"
-        WHITESPACE "\n    "
-        EXPR_STMT
-          LITERAL
-            FLOAT_LITERAL
-              FLOAT_NUMBER_START_0 "0e0"
-          SEMICOLON ";"
-        WHITESPACE "\n    "
-        EXPR_STMT
-          LITERAL
-            FLOAT_LITERAL
-              FLOAT_NUMBER_START_0 "0e0f32"
-          SEMICOLON ";"
-        WHITESPACE "\n    "
-        EXPR_STMT
-          LITERAL
-            FLOAT_LITERAL
-              FLOAT_NUMBER_START_2 "1"
-              DOT "."
-              FLOAT_NUMBER_PART "23f64"
-          SEMICOLON ";"
-        WHITESPACE "\n"
-        R_CURLY "}"
-  WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/0201_float_literal.rs b/crates/parser/test_data/parser/inline/ok/0201_float_literal.rs
deleted file mode 100644
index 0d51ec1252..0000000000
--- a/crates/parser/test_data/parser/inline/ok/0201_float_literal.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-fn f() {
-    0.0;
-    1.;
-    0e0;
-    0e0f32;
-    1.23f64;
-}
diff --git a/crates/parser/test_data/parser/ok/0056_neq_in_type.rast b/crates/parser/test_data/parser/ok/0056_neq_in_type.rast
index b2f66e2f1e..55ce31275f 100644
--- a/crates/parser/test_data/parser/ok/0056_neq_in_type.rast
+++ b/crates/parser/test_data/parser/ok/0056_neq_in_type.rast
@@ -19,10 +19,7 @@ SOURCE_FILE
             CAST_EXPR
               METHOD_CALL_EXPR
                 LITERAL
-                  FLOAT_LITERAL
-                    FLOAT_NUMBER_START_2 "1"
-                    DOT "."
-                    FLOAT_NUMBER_PART "0f32"
+                  FLOAT_NUMBER "1.0f32"
                 DOT "."
                 NAME_REF
                   IDENT "floor"
@@ -43,10 +40,7 @@ SOURCE_FILE
             CAST_EXPR
               METHOD_CALL_EXPR
                 LITERAL
-                  FLOAT_LITERAL
-                    FLOAT_NUMBER_START_2 "1"
-                    DOT "."
-                    FLOAT_NUMBER_PART "0f32"
+                  FLOAT_NUMBER "1.0f32"
                 DOT "."
                 NAME_REF
                   IDENT "floor"
diff --git a/crates/syntax/src/tests/ast_src.rs b/crates/syntax/src/tests/ast_src.rs
index f5a78e4119..2f6932a1ad 100644
--- a/crates/syntax/src/tests/ast_src.rs
+++ b/crates/syntax/src/tests/ast_src.rs
@@ -71,17 +71,7 @@ pub(crate) const KINDS_SRC: KindsSrc = KindsSrc {
         "super", "trait", "true", "try", "type", "unsafe", "use", "where", "while", "yield",
     ],
     contextual_keywords: &["auto", "default", "existential", "union", "raw", "macro_rules"],
-    literals: &[
-        "INT_NUMBER",
-        "FLOAT_NUMBER_START_0",
-        "FLOAT_NUMBER_START_1",
-        "FLOAT_NUMBER_START_2",
-        "FLOAT_NUMBER_PART",
-        "CHAR",
-        "BYTE",
-        "STRING",
-        "BYTE_STRING",
-    ],
+    literals: &["INT_NUMBER", "FLOAT_NUMBER", "CHAR", "BYTE", "STRING", "BYTE_STRING"],
     tokens: &["ERROR", "IDENT", "WHITESPACE", "LIFETIME_IDENT", "COMMENT", "SHEBANG"],
     nodes: &[
         "SOURCE_FILE",
@@ -193,7 +183,6 @@ pub(crate) const KINDS_SRC: KindsSrc = KindsSrc {
         "PATH",
         "PATH_SEGMENT",
         "LITERAL",
-        "FLOAT_LITERAL",
         "RENAME",
         "VISIBILITY",
         "WHERE_CLAUSE",
diff --git a/crates/syntax/src/tests/sourcegen_ast.rs b/crates/syntax/src/tests/sourcegen_ast.rs
index 33bf2c0cba..4cfb8075cb 100644
--- a/crates/syntax/src/tests/sourcegen_ast.rs
+++ b/crates/syntax/src/tests/sourcegen_ast.rs
@@ -462,10 +462,6 @@ fn generate_syntax_kinds(grammar: KindsSrc<'_>) -> String {
             [lifetime_ident] => { $crate::SyntaxKind::LIFETIME_IDENT };
             [ident] => { $crate::SyntaxKind::IDENT };
             [shebang] => { $crate::SyntaxKind::SHEBANG };
-            [float_number_part] => { $crate::SyntaxKind::FLOAT_NUMBER_PART };
-            [float_number_start_0] => { $crate::SyntaxKind::FLOAT_NUMBER_START_0 };
-            [float_number_start_1] => { $crate::SyntaxKind::FLOAT_NUMBER_START_1 };
-            [float_number_start_2] => { $crate::SyntaxKind::FLOAT_NUMBER_START_2 };
         }
         pub use T;
     };
@@ -589,7 +585,7 @@ impl Field {
 
 fn lower(grammar: &Grammar) -> AstSrc {
     let mut res = AstSrc {
-        tokens: "Whitespace Comment String ByteString IntNumber FloatNumberPart Char Byte Ident"
+        tokens: "Whitespace Comment String ByteString IntNumber FloatNumber Char Byte Ident"
             .split_ascii_whitespace()
             .map(|it| it.to_string())
             .collect::<Vec<_>>(),