Repository: rust-lang/rust-analyzer
Pull request #18413 (2024-10-25 18:18:14)

fix: Allow public re-export of `extern crate` import
MCVE:

```rust
//- /main.rs crate:main deps:test_crate
use test_crate::foo; // not resolving this!

use foo::Foo;

//- /lib.rs crate:test_crate deps:test_crate2
extern crate test_crate2;

pub use test_crate2 as foo;

//- /lib2.rs crate:test_crate2
pub struct Foo;
```

It seems that I should exempt min visibility for `extern crate`, as it seems to be exceptional in rustc 🤔

_Originally posted by @ShoyuVanilla in https://github.com/rust-lang/rust-analyzer/issues/18390#issuecomment-2438253336_

Should fix the regression made by #18390 
Thank you @lnicola for pointing out this regression 👍 

ShoyuVanilla: Oh, I found another bug - preexisting before #18390 - related to extern crate that should be dealt with this together.
I'll handle it as well with this

Patch:
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index 6117664c64..b87e94f9c5 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/body/lower.rs
@@ -1598,10 +1598,6 @@ impl ExprCollector<'_> {
                 for (id, _) in current_is_used.into_iter() {
                     binding_list.check_is_used(self, id);
                 }
-                if let &[pat] = &*pats {
-                    // Leading pipe without real OR pattern. Leaving an one-item OR pattern may confuse later stages.
-                    return pat;
-                }
                 Pat::Or(pats.into())
             }
             ast::Pat::ParenPat(p) => return self.collect_pat_opt(p.pat(), binding_list),
diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs
index 91a8cbab1a..dba11d4b76 100644
--- a/crates/hir-def/src/item_tree.rs
+++ b/crates/hir-def/src/item_tree.rs
@@ -967,6 +967,11 @@ impl UseTree {
         self.expand_impl(None, &mut cb)
     }
 
+    /// The [`UseTreeKind`] of this `UseTree`.
+    pub fn kind(&self) -> &UseTreeKind {
+        &self.kind
+    }
+
     fn expand_impl(
         &self,
         prefix: Option<ModPath>,
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs
index b9aec93690..a37e3c70e2 100644
--- a/crates/hir-def/src/nameres/collector.rs
+++ b/crates/hir-def/src/nameres/collector.rs
@@ -31,7 +31,7 @@ use crate::{
     item_scope::{ImportId, ImportOrExternCrate, ImportType, PerNsGlobImports},
     item_tree::{
         self, AttrOwner, FieldsShape, FileItemTreeId, ImportKind, ItemTree, ItemTreeId,
-        ItemTreeNode, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId,
+        ItemTreeNode, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId, UseTreeKind,
     },
     macro_call_as_call_id, macro_call_as_call_id_with_eager,
     nameres::{
@@ -1058,8 +1058,33 @@ impl DefCollector<'_> {
         vis: Visibility,
         def_import_type: Option<ImportType>,
     ) -> bool {
-        if let Some((_, v, _)) = defs.types.as_mut() {
-            *v = v.min(vis, &self.def_map).unwrap_or(vis);
+        // `extern crate crate_name` things can be re-exported as `pub use crate_name`.
+        // But they cannot be re-exported as `pub use self::crate_name`, `pub use crate::crate_name`
+        // or `pub use ::crate_name`.
+        //
+        // This has been historically allowed, but may be not allowed in future
+        // https://github.com/rust-lang/rust/issues/127909
+        if let Some((_, v, it)) = defs.types.as_mut() {
+            let is_extern_crate_reimport_without_prefix = || {
+                let Some(ImportOrExternCrate::ExternCrate(_)) = it else {
+                    return false;
+                };
+                let Some(ImportType::Import(id)) = def_import_type else {
+                    return false;
+                };
+                let use_id = id.import.lookup(self.db).id;
+                let item_tree = use_id.item_tree(self.db);
+                let use_kind = item_tree[use_id.value].use_tree.kind();
+                let UseTreeKind::Single { path, .. } = use_kind else {
+                    return false;
+                };
+                path.segments().len() < 2
+            };
+            if is_extern_crate_reimport_without_prefix() {
+                *v = vis;
+            } else {
+                *v = v.min(vis, &self.def_map).unwrap_or(vis);
+            }
         }
         if let Some((_, v, _)) = defs.values.as_mut() {
             *v = v.min(vis, &self.def_map).unwrap_or(vis);
diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs
index d491042eab..266851e3c0 100644
--- a/crates/hir-def/src/resolver.rs
+++ b/crates/hir-def/src/resolver.rs
@@ -1043,12 +1043,12 @@ impl HasResolver for ModuleId {
     fn resolver(self, db: &dyn DefDatabase) -> Resolver {
         let mut def_map = self.def_map(db);
         let mut module_id = self.local_id;
+        let mut modules: SmallVec<[_; 1]> = smallvec![];
 
         if !self.is_block_module() {
             return Resolver { scopes: vec![], module_scope: ModuleItemMap { def_map, module_id } };
         }
 
-        let mut modules: SmallVec<[_; 1]> = smallvec![];
         while let Some(parent) = def_map.parent() {
             let block_def_map = mem::replace(&mut def_map, parent.def_map(db));
             modules.push(block_def_map);
diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs
index 7d2f556406..5d5f72490d 100644
--- a/crates/hir-expand/src/lib.rs
+++ b/crates/hir-expand/src/lib.rs
@@ -165,73 +165,40 @@ pub enum ExpandErrorKind {
 }
 
 impl ExpandError {
-    pub fn render_to_string(&self, db: &dyn ExpandDatabase) -> RenderedExpandError {
+    pub fn render_to_string(&self, db: &dyn ExpandDatabase) -> (String, bool) {
         self.inner.0.render_to_string(db)
     }
 }
 
-pub struct RenderedExpandError {
-    pub message: String,
-    pub error: bool,
-    pub kind: &'static str,
-}
-
-impl RenderedExpandError {
-    const GENERAL_KIND: &str = "macro-error";
-}
-
 impl ExpandErrorKind {
-    pub fn render_to_string(&self, db: &dyn ExpandDatabase) -> RenderedExpandError {
+    pub fn render_to_string(&self, db: &dyn ExpandDatabase) -> (String, bool) {
         match self {
-            ExpandErrorKind::ProcMacroAttrExpansionDisabled => RenderedExpandError {
-                message: "procedural attribute macro expansion is disabled".to_owned(),
-                error: false,
-                kind: "proc-macros-disabled",
-            },
-            ExpandErrorKind::MacroDisabled => RenderedExpandError {
-                message: "proc-macro is explicitly disabled".to_owned(),
-                error: false,
-                kind: "proc-macro-disabled",
-            },
+            ExpandErrorKind::ProcMacroAttrExpansionDisabled => {
+                ("procedural attribute macro expansion is disabled".to_owned(), false)
+            }
+            ExpandErrorKind::MacroDisabled => {
+                ("proc-macro is explicitly disabled".to_owned(), false)
+            }
             &ExpandErrorKind::MissingProcMacroExpander(def_crate) => {
                 match db.proc_macros().get_error_for_crate(def_crate) {
-                    Some((e, hard_err)) => RenderedExpandError {
-                        message: e.to_owned(),
-                        error: hard_err,
-                        kind: RenderedExpandError::GENERAL_KIND,
-                    },
-                    None => RenderedExpandError {
-                        message: format!("internal error: proc-macro map is missing error entry for crate {def_crate:?}"),
-                        error: true,
-                        kind: RenderedExpandError::GENERAL_KIND,
-                    },
+                    Some((e, hard_err)) => (e.to_owned(), hard_err),
+                    None => (
+                        format!(
+                            "internal error: proc-macro map is missing error entry for crate {def_crate:?}"
+                        ),
+                        true,
+                    ),
                 }
             }
-            ExpandErrorKind::MacroDefinition => RenderedExpandError {
-                message: "macro definition has parse errors".to_owned(),
-                error: true,
-                kind: RenderedExpandError::GENERAL_KIND,
-            },
-            ExpandErrorKind::Mbe(e) => RenderedExpandError {
-                message: e.to_string(),
-                error: true,
-                kind: RenderedExpandError::GENERAL_KIND,
-            },
-            ExpandErrorKind::RecursionOverflow => RenderedExpandError {
-                message: "overflow expanding the original macro".to_owned(),
-                error: true,
-                kind: RenderedExpandError::GENERAL_KIND,
-            },
-            ExpandErrorKind::Other(e) => RenderedExpandError {
-                message: (**e).to_owned(),
-                error: true,
-                kind: RenderedExpandError::GENERAL_KIND,
-            },
-            ExpandErrorKind::ProcMacroPanic(e) => RenderedExpandError {
-                message: format!("proc-macro panicked: {e}"),
-                error: true,
-                kind: RenderedExpandError::GENERAL_KIND,
-            },
+            ExpandErrorKind::MacroDefinition => {
+                ("macro definition has parse errors".to_owned(), true)
+            }
+            ExpandErrorKind::Mbe(e) => (e.to_string(), true),
+            ExpandErrorKind::RecursionOverflow => {
+                ("overflow expanding the original macro".to_owned(), true)
+            }
+            ExpandErrorKind::Other(e) => ((**e).to_owned(), true),
+            ExpandErrorKind::ProcMacroPanic(e) => (format!("proc-macro panicked: {e}"), true),
         }
     }
 }
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 8297acde85..2fedffe047 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -165,7 +165,6 @@ pub struct MacroError {
     pub precise_location: Option<TextRange>,
     pub message: String,
     pub error: bool,
-    pub kind: &'static str,
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index f9b51b6570..4a18795e7d 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -58,8 +58,7 @@ use hir_def::{
     TypeOrConstParamId, TypeParamId, UnionId,
 };
 use hir_expand::{
-    attrs::collect_attrs, proc_macro::ProcMacroKind, AstId, MacroCallKind, RenderedExpandError,
-    ValueResult,
+    attrs::collect_attrs, proc_macro::ProcMacroKind, AstId, MacroCallKind, ValueResult,
 };
 use hir_ty::{
     all_super_traits, autoderef, check_orphan_rules,
@@ -839,7 +838,7 @@ fn macro_call_diagnostics(
         let file_id = loc.kind.file_id();
         let node =
             InFile::new(file_id, db.ast_id_map(file_id).get_erased(loc.kind.erased_ast_id()));
-        let RenderedExpandError { message, error, kind } = err.render_to_string(db.upcast());
+        let (message, error) = err.render_to_string(db.upcast());
         let precise_location = if err.span().anchor.file_id == file_id {
             Some(
                 err.span().range
@@ -851,7 +850,7 @@ fn macro_call_diagnostics(
         } else {
             None
         };
-        acc.push(MacroError { node, precise_location, message, error, kind }.into());
+        acc.push(MacroError { node, precise_location, message, error }.into());
     }
 
     if !parse_errors.is_empty() {
@@ -917,14 +916,13 @@ fn emit_def_diagnostic_(
 
         DefDiagnosticKind::MacroError { ast, path, err } => {
             let item = ast.to_ptr(db.upcast());
-            let RenderedExpandError { message, error, kind } = err.render_to_string(db.upcast());
+            let (message, error) = err.render_to_string(db.upcast());
             acc.push(
                 MacroError {
                     node: InFile::new(ast.file_id, item.syntax_node_ptr()),
                     precise_location: None,
                     message: format!("{}: {message}", path.display(db.upcast(), edition)),
                     error,
-                    kind,
                 }
                 .into(),
             )
@@ -1813,8 +1811,7 @@ impl DefWithBody {
                     InactiveCode { node: *node, cfg: cfg.clone(), opts: opts.clone() }.into()
                 }
                 BodyDiagnostic::MacroError { node, err } => {
-                    let RenderedExpandError { message, error, kind } =
-                        err.render_to_string(db.upcast());
+                    let (message, error) = err.render_to_string(db.upcast());
 
                     let precise_location = if err.span().anchor.file_id == node.file_id {
                         Some(
@@ -1832,7 +1829,6 @@ impl DefWithBody {
                         precise_location,
                         message,
                         error,
-                        kind,
                     }
                     .into()
                 }
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 6e298d9586..860754d5e7 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -936,7 +936,16 @@ impl<'db> SemanticsImpl<'db> {
             }
         }
 
+        let (file_id, tokens) = stack.first()?;
+        // make sure we pick the token in the expanded include if we encountered an include,
+        // otherwise we'll get the wrong semantics
+        let sa =
+            tokens.first()?.0.parent().and_then(|parent| {
+                self.analyze_impl(InFile::new(*file_id, &parent), None, false)
+            })?;
+
         let mut m_cache = self.macro_call_cache.borrow_mut();
+        let def_map = sa.resolver.def_map();
 
         // Filters out all tokens that contain the given range (usually the macro call), any such
         // token is redundant as the corresponding macro call has already been processed
@@ -945,10 +954,6 @@ impl<'db> SemanticsImpl<'db> {
         };
 
         while let Some((expansion, ref mut tokens)) = stack.pop() {
-            // Reverse the tokens so we prefer first tokens (to accommodate for popping from the
-            // back)
-            // alternatively we could pop from the front but that would shift the content on every pop
-            tokens.reverse();
             while let Some((token, ctx)) = tokens.pop() {
                 let was_not_remapped = (|| {
                     // First expand into attribute invocations
@@ -1019,16 +1024,8 @@ impl<'db> SemanticsImpl<'db> {
                                         ) {
                                         call.as_macro_file()
                                     } else {
-                                        token
-                                            .parent()
-                                            .and_then(|parent| {
-                                                self.analyze_impl(
-                                                    InFile::new(expansion, &parent),
-                                                    None,
-                                                    false,
-                                                )
-                                            })?
-                                            .expand(self.db, mcall.as_ref())?
+                                        // FIXME: This is wrong, the SourceAnalyzer might be invalid here
+                                        sa.expand(self.db, mcall.as_ref())?
                                     };
                                     m_cache.insert(mcall, it);
                                     it
@@ -1098,16 +1095,9 @@ impl<'db> SemanticsImpl<'db> {
                                 attr.path().and_then(|it| it.as_single_name_ref())?.as_name();
                             // Not an attribute, nor a derive, so it's either an intert attribute or a derive helper
                             // Try to resolve to a derive helper and downmap
-                            let resolver = &token
-                                .parent()
-                                .and_then(|parent| {
-                                    self.analyze_impl(InFile::new(expansion, &parent), None, false)
-                                })?
-                                .resolver;
                             let id = self.db.ast_id_map(expansion).ast_id(&adt);
-                            let helpers = resolver
-                                .def_map()
-                                .derive_helpers_in_scope(InFile::new(expansion, id))?;
+                            let helpers =
+                                def_map.derive_helpers_in_scope(InFile::new(expansion, id))?;
 
                             if !helpers.is_empty() {
                                 let text_range = attr.syntax().text_range();
diff --git a/crates/ide-assists/src/handlers/remove_dbg.rs b/crates/ide-assists/src/handlers/remove_dbg.rs
index 94274f6d17..0e9c463e02 100644
--- a/crates/ide-assists/src/handlers/remove_dbg.rs
+++ b/crates/ide-assists/src/handlers/remove_dbg.rs
@@ -41,7 +41,7 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
         macro_calls.into_iter().filter_map(compute_dbg_replacement).collect::<Vec<_>>();
 
     acc.add(
-        AssistId("remove_dbg", AssistKind::QuickFix),
+        AssistId("remove_dbg", AssistKind::Refactor),
         "Remove dbg!()",
         replacements.iter().map(|&(range, _)| range).reduce(|acc, range| acc.cover(range))?,
         |builder| {
diff --git a/crates/ide-assists/src/handlers/unmerge_match_arm.rs b/crates/ide-assists/src/handlers/unmerge_match_arm.rs
index 648bf358b4..db789cfa33 100644
--- a/crates/ide-assists/src/handlers/unmerge_match_arm.rs
+++ b/crates/ide-assists/src/handlers/unmerge_match_arm.rs
@@ -34,9 +34,6 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
 pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
     let pipe_token = ctx.find_token_syntax_at_offset(T![|])?;
     let or_pat = ast::OrPat::cast(pipe_token.parent()?)?.clone_for_update();
-    if or_pat.leading_pipe().is_some_and(|it| it == pipe_token) {
-        return None;
-    }
     let match_arm = ast::MatchArm::cast(or_pat.syntax().parent()?)?;
     let match_arm_body = match_arm.expr()?;
 
diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs
index 8aaf5d6fff..22620816d5 100644
--- a/crates/ide-assists/src/lib.rs
+++ b/crates/ide-assists/src/lib.rs
@@ -301,7 +301,6 @@ mod handlers {
             inline_call::inline_into_callers,
             inline_const_as_literal::inline_const_as_literal,
             inline_local_variable::inline_local_variable,
-            inline_macro::inline_macro,
             inline_type_alias::inline_type_alias,
             inline_type_alias::inline_type_alias_uses,
             into_to_qualified_from::into_to_qualified_from,
@@ -327,7 +326,6 @@ mod handlers {
             raw_string::add_hash,
             raw_string::make_usual_string,
             raw_string::remove_hash,
-            remove_dbg::remove_dbg,
             remove_mut::remove_mut,
             remove_unused_imports::remove_unused_imports,
             remove_unused_param::remove_unused_param,
@@ -383,6 +381,9 @@ mod handlers {
             generate_getter_or_setter::generate_setter,
             generate_delegate_methods::generate_delegate_methods,
             generate_deref::generate_deref,
+            //
+            remove_dbg::remove_dbg,
+            inline_macro::inline_macro,
             // Are you sure you want to add new assist here, and not to the
             // sorted list above?
         ]
diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs
index b764f852f0..aed093f0eb 100644
--- a/crates/ide-db/src/lib.rs
+++ b/crates/ide-db/src/lib.rs
@@ -293,35 +293,3 @@ impl SnippetCap {
         }
     }
 }
-
-pub struct Ranker<'a> {
-    pub kind: parser::SyntaxKind,
-    pub text: &'a str,
-    pub ident_kind: bool,
-}
-
-impl<'a> Ranker<'a> {
-    pub const MAX_RANK: usize = 0b1110;
-
-    pub fn from_token(token: &'a syntax::SyntaxToken) -> Self {
-        let kind = token.kind();
-        Ranker { kind, text: token.text(), ident_kind: kind.is_any_identifier() }
-    }
-
-    /// A utility function that ranks a token again a given kind and text, returning a number that
-    /// represents how close the token is to the given kind and text.
-    pub fn rank_token(&self, tok: &syntax::SyntaxToken) -> usize {
-        let tok_kind = tok.kind();
-
-        let exact_same_kind = tok_kind == self.kind;
-        let both_idents = exact_same_kind || (tok_kind.is_any_identifier() && self.ident_kind);
-        let same_text = tok.text() == self.text;
-        // anything that mapped into a token tree has likely no semantic information
-        let no_tt_parent =
-            tok.parent().map_or(false, |it| it.kind() != parser::SyntaxKind::TOKEN_TREE);
-        (both_idents as usize)
-            | ((exact_same_kind as usize) << 1)
-            | ((same_text as usize) << 2)
-            | ((no_tt_parent as usize) << 3)
-    }
-}
diff --git a/crates/ide-diagnostics/src/handlers/macro_error.rs b/crates/ide-diagnostics/src/handlers/macro_error.rs
index e177b72e4d..6a976697c8 100644
--- a/crates/ide-diagnostics/src/handlers/macro_error.rs
+++ b/crates/ide-diagnostics/src/handlers/macro_error.rs
@@ -3,19 +3,14 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity};
 // Diagnostic: macro-error
 //
 // This diagnostic is shown for macro expansion errors.
-
-// Diagnostic: proc-macros-disabled
-//
-// This diagnostic is shown for proc macros where proc macros have been disabled.
-
-// Diagnostic: proc-macro-disabled
-//
-// This diagnostic is shown for proc macros that has been specifically disabled via `rust-analyzer.procMacro.ignored`.
 pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) -> Diagnostic {
     // Use more accurate position if available.
     let display_range = ctx.resolve_precise_location(&d.node, d.precise_location);
     Diagnostic::new(
-        DiagnosticCode::Ra(d.kind, if d.error { Severity::Error } else { Severity::WeakWarning }),
+        DiagnosticCode::Ra(
+            "macro-error",
+            if d.error { Severity::Error } else { Severity::WeakWarning },
+        ),
         d.message.clone(),
         display_range,
     )
diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs
index e5b4ed17b2..1b82c00d1d 100644
--- a/crates/ide/src/call_hierarchy.rs
+++ b/crates/ide/src/call_hierarchy.rs
@@ -510,7 +510,6 @@ fn caller$0() {
             expect![[]],
         );
     }
-
     #[test]
     fn test_call_hierarchy_in_macros_incoming_different_files() {
         check_hierarchy(
@@ -592,9 +591,9 @@ macro_rules! call {
 "#,
             expect!["callee Function FileId(0) 22..37 30..36"],
             expect![[r#"
+                callee Function FileId(0) 38..52 44..50 : FileId(0):44..50
                 caller Function FileId(0) 38..52 : FileId(0):44..50
-                caller Function FileId(1) 130..136 130..136 : FileId(0):44..50
-                callee Function FileId(0) 38..52 44..50 : FileId(0):44..50"#]],
+                caller Function FileId(1) 130..136 130..136 : FileId(0):44..50"#]],
             expect![[]],
         );
     }
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 6cac4f1ee4..124db2985b 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -11,7 +11,7 @@ use ide_db::{
     defs::{Definition, IdentClass, NameRefClass, OperatorClass},
     famous_defs::FamousDefs,
     helpers::pick_best_token,
-    FileRange, FxIndexSet, Ranker, RootDatabase,
+    FileRange, FxIndexSet, RootDatabase,
 };
 use itertools::{multizip, Itertools};
 use span::Edition;
@@ -182,13 +182,27 @@ fn hover_offset(
     // equivalency is more important
     let mut descended = sema.descend_into_macros(original_token.clone());
 
-    let ranker = Ranker::from_token(&original_token);
-
-    descended.sort_by_cached_key(|tok| !ranker.rank_token(tok));
+    let kind = original_token.kind();
+    let text = original_token.text();
+    let ident_kind = kind.is_any_identifier();
+
+    descended.sort_by_cached_key(|tok| {
+        let tok_kind = tok.kind();
+
+        let exact_same_kind = tok_kind == kind;
+        let both_idents = exact_same_kind || (tok_kind.is_any_identifier() && ident_kind);
+        let same_text = tok.text() == text;
+        // anything that mapped into a token tree has likely no semantic information
+        let no_tt_parent = tok.parent().map_or(false, |it| it.kind() != TOKEN_TREE);
+        !((both_idents as usize)
+            | ((exact_same_kind as usize) << 1)
+            | ((same_text as usize) << 2)
+            | ((no_tt_parent as usize) << 3))
+    });
 
     let mut res = vec![];
     for token in descended {
-        let is_same_kind = token.kind() == ranker.kind;
+        let is_same_kind = token.kind() == kind;
         let lint_hover = (|| {
             // FIXME: Definition should include known lints and the like instead of having this special case here
             let attr = token.parent_ancestors().find_map(ast::Attr::cast)?;
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 339315db57..e7cb8a253f 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -1701,14 +1701,14 @@ fn f() {
 }
             "#,
             expect![[r#"
-                func Function FileId(0) 137..146 140..144 module
+                func Function FileId(0) 137..146 140..144
 
-                FileId(0) 181..185
+                FileId(0) 161..165
 
 
-                func Function FileId(0) 137..146 140..144
+                func Function FileId(0) 137..146 140..144 module
 
-                FileId(0) 161..165
+                FileId(0) 181..185
             "#]],
         )
     }
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index 0747d1b404..961b2a4c93 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -16,7 +16,7 @@ mod tests;
 use std::ops::ControlFlow;
 
 use hir::{InRealFile, Name, Semantics};
-use ide_db::{FxHashMap, Ranker, RootDatabase, SymbolKind};
+use ide_db::{FxHashMap, RootDatabase, SymbolKind};
 use span::EditionedFileId;
 use syntax::{
     ast::{self, IsString},
@@ -397,12 +397,13 @@ fn traverse(
                 Some(AttrOrDerive::Derive(_)) => inside_attribute,
                 None => false,
             };
-
         let descended_element = if in_macro {
             // Attempt to descend tokens into macro-calls.
             let res = match element {
                 NodeOrToken::Token(token) if token.kind() != COMMENT => {
-                    let ranker = Ranker::from_token(&token);
+                    let kind = token.kind();
+                    let text = token.text();
+                    let ident_kind = kind.is_any_identifier();
 
                     let mut t = None;
                     let mut r = 0;
@@ -411,9 +412,21 @@ fn traverse(
                         |tok, _ctx| {
                             // FIXME: Consider checking ctx transparency for being opaque?
                             let tok = tok.value;
-                            let my_rank = ranker.rank_token(&tok);
-
-                            if my_rank >= Ranker::MAX_RANK {
+                            let tok_kind = tok.kind();
+
+                            let exact_same_kind = tok_kind == kind;
+                            let both_idents =
+                                exact_same_kind || (tok_kind.is_any_identifier() && ident_kind);
+                            let same_text = tok.text() == text;
+                            // anything that mapped into a token tree has likely no semantic information
+                            let no_tt_parent =
+                                tok.parent().map_or(false, |it| it.kind() != TOKEN_TREE);
+                            let my_rank = (both_idents as usize)
+                                | ((exact_same_kind as usize) << 1)
+                                | ((same_text as usize) << 2)
+                                | ((no_tt_parent as usize) << 3);
+
+                            if my_rank > 0b1110 {
                                 // a rank of 0b1110 means that we have found a maximally interesting
                                 // token so stop early.
                                 t = Some(tok);
diff --git a/crates/parser/src/grammar/patterns.rs b/crates/parser/src/grammar/patterns.rs
index ed01fca2ac..3db1a10ca5 100644
--- a/crates/parser/src/grammar/patterns.rs
+++ b/crates/parser/src/grammar/patterns.rs
@@ -21,8 +21,7 @@ const RANGE_PAT_END_FIRST: TokenSet =
     expressions::LITERAL_FIRST.union(paths::PATH_FIRST).union(TokenSet::new(&[T![-], T![const]]));
 
 pub(crate) fn pattern(p: &mut Parser<'_>) {
-    let m = p.start();
-    pattern_r(p, m, false, PAT_RECOVERY_SET);
+    pattern_r(p, PAT_RECOVERY_SET);
 }
 
 /// Parses a pattern list separated by pipes `|`.
@@ -37,9 +36,8 @@ pub(crate) fn pattern_single(p: &mut Parser<'_>) {
 /// Parses a pattern list separated by pipes `|`
 /// using the given `recovery_set`.
 pub(super) fn pattern_top_r(p: &mut Parser<'_>, recovery_set: TokenSet) {
-    let m = p.start();
-    let has_leading_pipe = p.eat(T![|]);
-    pattern_r(p, m, has_leading_pipe, recovery_set);
+    p.eat(T![|]);
+    pattern_r(p, recovery_set);
 }
 
 // test or_pattern
@@ -53,10 +51,11 @@ pub(super) fn pattern_top_r(p: &mut Parser<'_>, recovery_set: TokenSet) {
 // }
 /// Parses a pattern list separated by pipes `|`, with no leading `|`,using the
 /// given `recovery_set`.
-fn pattern_r(p: &mut Parser<'_>, m: Marker, has_leading_pipe: bool, recovery_set: TokenSet) {
+fn pattern_r(p: &mut Parser<'_>, recovery_set: TokenSet) {
+    let m = p.start();
     pattern_single_r(p, recovery_set);
 
-    if !p.at(T![|]) && !has_leading_pipe {
+    if !p.at(T![|]) {
         m.abandon(p);
         return;
     }
diff --git a/crates/parser/src/lexed_str.rs b/crates/parser/src/lexed_str.rs
index 3c0eb1b42a..5322463a71 100644
--- a/crates/parser/src/lexed_str.rs
+++ b/crates/parser/src/lexed_str.rs
@@ -39,9 +39,7 @@ impl<'a> LexedStr<'a> {
             conv.offset = shebang_len;
         };
 
-        // Re-create the tokenizer from scratch every token because `GuardedStrPrefix` is one token in the lexer
-        // but we want to split it to two in edition <2024.
-        while let Some(token) = rustc_lexer::tokenize(&text[conv.offset..]).next() {
+        for token in rustc_lexer::tokenize(&text[conv.offset..]) {
             let token_text = &text[conv.offset..][..token.len as usize];
 
             conv.extend_token(&token.kind, token_text);
@@ -160,7 +158,7 @@ impl<'a> Converter<'a> {
         }
     }
 
-    fn extend_token(&mut self, kind: &rustc_lexer::TokenKind, mut token_text: &str) {
+    fn extend_token(&mut self, kind: &rustc_lexer::TokenKind, token_text: &str) {
         // A note on an intended tradeoff:
         // We drop some useful information here (see patterns with double dots `..`)
         // Storing that info in `SyntaxKind` is not possible due to its layout requirements of
@@ -191,15 +189,10 @@ impl<'a> Converter<'a> {
                 rustc_lexer::TokenKind::RawIdent => IDENT,
 
                 rustc_lexer::TokenKind::GuardedStrPrefix if self.edition.at_least_2024() => {
-                    // FIXME: rustc does something better for recovery.
                     err = "Invalid string literal (reserved syntax)";
                     ERROR
                 }
-                rustc_lexer::TokenKind::GuardedStrPrefix => {
-                    // The token is `#"` or `##`, split it into two.
-                    token_text = &token_text[1..];
-                    POUND
-                }
+                rustc_lexer::TokenKind::GuardedStrPrefix => POUND,
 
                 rustc_lexer::TokenKind::Literal { kind, .. } => {
                     self.extend_literal(token_text.len(), kind);
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs
index 22910ee4c6..bcdd045d70 100644
--- a/crates/rust-analyzer/src/diagnostics.rs
+++ b/crates/rust-analyzer/src/diagnostics.rs
@@ -173,6 +173,21 @@ pub(crate) fn fetch_native_diagnostics(
     let _p = tracing::info_span!("fetch_native_diagnostics").entered();
     let _ctx = stdx::panic_context::enter("fetch_native_diagnostics".to_owned());
 
+    let convert_diagnostic =
+        |line_index: &crate::line_index::LineIndex, d: ide::Diagnostic| lsp_types::Diagnostic {
+            range: lsp::to_proto::range(line_index, d.range.range),
+            severity: Some(lsp::to_proto::diagnostic_severity(d.severity)),
+            code: Some(lsp_types::NumberOrString::String(d.code.as_str().to_owned())),
+            code_description: Some(lsp_types::CodeDescription {
+                href: lsp_types::Url::parse(&d.code.url()).unwrap(),
+            }),
+            source: Some("rust-analyzer".to_owned()),
+            message: d.message,
+            related_information: None,
+            tags: d.unused.then(|| vec![lsp_types::DiagnosticTag::UNNECESSARY]),
+            data: None,
+        };
+
     // the diagnostics produced may point to different files not requested by the concrete request,
     // put those into here and filter later
     let mut odd_ones = Vec::new();
@@ -233,22 +248,3 @@ pub(crate) fn fetch_native_diagnostics(
     }
     diagnostics
 }
-
-pub(crate) fn convert_diagnostic(
-    line_index: &crate::line_index::LineIndex,
-    d: ide::Diagnostic,
-) -> lsp_types::Diagnostic {
-    lsp_types::Diagnostic {
-        range: lsp::to_proto::range(line_index, d.range.range),
-        severity: Some(lsp::to_proto::diagnostic_severity(d.severity)),
-        code: Some(lsp_types::NumberOrString::String(d.code.as_str().to_owned())),
-        code_description: Some(lsp_types::CodeDescription {
-            href: lsp_types::Url::parse(&d.code.url()).unwrap(),
-        }),
-        source: Some("rust-analyzer".to_owned()),
-        message: d.message,
-        related_information: None,
-        tags: d.unused.then(|| vec![lsp_types::DiagnosticTag::UNNECESSARY]),
-        data: None,
-    }
-}
diff --git a/crates/rust-analyzer/src/handlers/dispatch.rs b/crates/rust-analyzer/src/handlers/dispatch.rs
index 03759b036b..ed7bf27843 100644
--- a/crates/rust-analyzer/src/handlers/dispatch.rs
+++ b/crates/rust-analyzer/src/handlers/dispatch.rs
@@ -5,7 +5,7 @@ use std::{
 };
 
 use ide::Cancelled;
-use lsp_server::{ExtractError, Response, ResponseError};
+use lsp_server::ExtractError;
 use serde::{de::DeserializeOwned, Serialize};
 use stdx::thread::ThreadIntent;
 
@@ -117,36 +117,7 @@ impl RequestDispatcher<'_> {
             }
             return self;
         }
-        self.on_with_thread_intent::<true, ALLOW_RETRYING, R>(
-            ThreadIntent::Worker,
-            f,
-            Self::content_modified_error,
-        )
-    }
-
-    /// Dispatches a non-latency-sensitive request onto the thread pool. When the VFS is marked not
-    /// ready this will return a `default` constructed [`R::Result`].
-    pub(crate) fn on_with<R>(
-        &mut self,
-        f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result<R::Result>,
-        default: impl FnOnce() -> R::Result,
-        on_cancelled: fn() -> ResponseError,
-    ) -> &mut Self
-    where
-        R: lsp_types::request::Request<
-                Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug,
-                Result: Serialize,
-            > + 'static,
-    {
-        if !self.global_state.vfs_done {
-            if let Some(lsp_server::Request { id, .. }) =
-                self.req.take_if(|it| it.method == R::METHOD)
-            {
-                self.global_state.respond(lsp_server::Response::new_ok(id, default()));
-            }
-            return self;
-        }
-        self.on_with_thread_intent::<true, false, R>(ThreadIntent::Worker, f, on_cancelled)
+        self.on_with_thread_intent::<true, ALLOW_RETRYING, R>(ThreadIntent::Worker, f)
     }
 
     /// Dispatches a non-latency-sensitive request onto the thread pool. When the VFS is marked not
@@ -165,11 +136,7 @@ impl RequestDispatcher<'_> {
             }
             return self;
         }
-        self.on_with_thread_intent::<true, ALLOW_RETRYING, R>(
-            ThreadIntent::Worker,
-            f,
-            Self::content_modified_error,
-        )
+        self.on_with_thread_intent::<true, ALLOW_RETRYING, R>(ThreadIntent::Worker, f)
     }
 
     /// Dispatches a latency-sensitive request onto the thread pool. When the VFS is marked not
@@ -192,11 +159,7 @@ impl RequestDispatcher<'_> {
             }
             return self;
         }
-        self.on_with_thread_intent::<true, ALLOW_RETRYING, R>(
-            ThreadIntent::LatencySensitive,
-            f,
-            Self::content_modified_error,
-        )
+        self.on_with_thread_intent::<true, ALLOW_RETRYING, R>(ThreadIntent::LatencySensitive, f)
     }
 
     /// Formatting requests should never block on waiting a for task thread to open up, editors will wait
@@ -211,11 +174,7 @@ impl RequestDispatcher<'_> {
         R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug,
         R::Result: Serialize,
     {
-        self.on_with_thread_intent::<false, false, R>(
-            ThreadIntent::LatencySensitive,
-            f,
-            Self::content_modified_error,
-        )
+        self.on_with_thread_intent::<false, false, R>(ThreadIntent::LatencySensitive, f)
     }
 
     pub(crate) fn finish(&mut self) {
@@ -234,7 +193,6 @@ impl RequestDispatcher<'_> {
         &mut self,
         intent: ThreadIntent,
         f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result<R::Result>,
-        on_cancelled: fn() -> ResponseError,
     ) -> &mut Self
     where
         R: lsp_types::request::Request + 'static,
@@ -263,10 +221,11 @@ impl RequestDispatcher<'_> {
             match thread_result_to_response::<R>(req.id.clone(), result) {
                 Ok(response) => Task::Response(response),
                 Err(_cancelled) if ALLOW_RETRYING => Task::Retry(req),
-                Err(_cancelled) => {
-                    let error = on_cancelled();
-                    Task::Response(Response { id: req.id, result: None, error: Some(error) })
-                }
+                Err(_cancelled) => Task::Response(lsp_server::Response::new_err(
+                    req.id,
+                    lsp_server::ErrorCode::ContentModified as i32,
+                    "content modified".to_owned(),
+                )),
             }
         });
 
@@ -297,14 +256,6 @@ impl RequestDispatcher<'_> {
             }
         }
     }
-
-    fn content_modified_error() -> ResponseError {
-        ResponseError {
-            code: lsp_server::ErrorCode::ContentModified as i32,
-            message: "content modified".to_owned(),
-            data: None,
-        }
-    }
 }
 
 fn thread_result_to_response<R>(
diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs
index fa584ab4d2..5b4b678c0c 100644
--- a/crates/rust-analyzer/src/handlers/request.rs
+++ b/crates/rust-analyzer/src/handlers/request.rs
@@ -4,7 +4,6 @@
 use std::{
     fs,
     io::Write as _,
-    ops::Not,
     process::{self, Stdio},
 };
 
@@ -15,7 +14,7 @@ use ide::{
     FilePosition, FileRange, HoverAction, HoverGotoTypeData, InlayFieldsToResolve, Query,
     RangeInfo, ReferenceCategory, Runnable, RunnableKind, SingleResolve, SourceChange, TextEdit,
 };
-use ide_db::{FxHashMap, SymbolKind};
+use ide_db::SymbolKind;
 use itertools::Itertools;
 use lsp_server::ErrorCode;
 use lsp_types::{
@@ -37,7 +36,6 @@ use vfs::{AbsPath, AbsPathBuf, FileId, VfsPath};
 
 use crate::{
     config::{Config, RustfmtConfig, WorkspaceSymbolConfig},
-    diagnostics::convert_diagnostic,
     global_state::{FetchWorkspaceRequest, GlobalState, GlobalStateSnapshot},
     hack_recover_crate_name,
     line_index::LineEndings,
@@ -475,74 +473,6 @@ pub(crate) fn handle_on_type_formatting(
     Ok(Some(change))
 }
 
-pub(crate) fn handle_document_diagnostics(
-    snap: GlobalStateSnapshot,
-    params: lsp_types::DocumentDiagnosticParams,
-) -> anyhow::Result<lsp_types::DocumentDiagnosticReportResult> {
-    const EMPTY: lsp_types::DocumentDiagnosticReportResult =
-        lsp_types::DocumentDiagnosticReportResult::Report(
-            lsp_types::DocumentDiagnosticReport::Full(
-                lsp_types::RelatedFullDocumentDiagnosticReport {
-                    related_documents: None,
-                    full_document_diagnostic_report: lsp_types::FullDocumentDiagnosticReport {
-                        result_id: None,
-                        items: vec![],
-                    },
-                },
-            ),
-        );
-
-    let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
-    let source_root = snap.analysis.source_root_id(file_id)?;
-    if !snap.analysis.is_local_source_root(source_root)? {
-        return Ok(EMPTY);
-    }
-    let config = snap.config.diagnostics(Some(source_root));
-    if !config.enabled {
-        return Ok(EMPTY);
-    }
-    let line_index = snap.file_line_index(file_id)?;
-    let supports_related = snap.config.text_document_diagnostic_related_document_support();
-
-    let mut related_documents = FxHashMap::default();
-    let diagnostics = snap
-        .analysis
-        .full_diagnostics(&config, AssistResolveStrategy::None, file_id)?
-        .into_iter()
-        .filter_map(|d| {
-            let file = d.range.file_id;
-            let diagnostic = convert_diagnostic(&line_index, d);
-            if file == file_id {
-                return Some(diagnostic);
-            }
-            if supports_related {
-                related_documents.entry(file).or_insert_with(Vec::new).push(diagnostic);
-            }
-            None
-        });
-    Ok(lsp_types::DocumentDiagnosticReportResult::Report(
-        lsp_types::DocumentDiagnosticReport::Full(lsp_types::RelatedFullDocumentDiagnosticReport {
-            full_document_diagnostic_report: lsp_types::FullDocumentDiagnosticReport {
-                result_id: None,
-                items: diagnostics.collect(),
-            },
-            related_documents: related_documents.is_empty().not().then(|| {
-                related_documents
-                    .into_iter()
-                    .map(|(id, items)| {
-                        (
-                            to_proto::url(&snap, id),
-                            lsp_types::DocumentDiagnosticReportKind::Full(
-                                lsp_types::FullDocumentDiagnosticReport { result_id: None, items },
-                            ),
-                        )
-                    })
-                    .collect()
-            }),
-        }),
-    ))
-}
-
 pub(crate) fn handle_document_symbol(
     snap: GlobalStateSnapshot,
     params: lsp_types::DocumentSymbolParams,
diff --git a/crates/rust-analyzer/src/lsp/capabilities.rs b/crates/rust-analyzer/src/lsp/capabilities.rs
index 271a9c0f3d..939593dd45 100644
--- a/crates/rust-analyzer/src/lsp/capabilities.rs
+++ b/crates/rust-analyzer/src/lsp/capabilities.rs
@@ -155,15 +155,7 @@ pub fn server_capabilities(config: &Config) -> ServerCapabilities {
             "ssr": true,
             "workspaceSymbolScopeKindFiltering": true,
         })),
-        diagnostic_provider: Some(lsp_types::DiagnosticServerCapabilities::Options(
-            lsp_types::DiagnosticOptions {
-                identifier: None,
-                inter_file_dependencies: true,
-                // FIXME
-                workspace_diagnostics: false,
-                work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
-            },
-        )),
+        diagnostic_provider: None,
         inline_completion_provider: None,
     }
 }
@@ -388,15 +380,6 @@ impl ClientCapabilities {
         .unwrap_or_default()
     }
 
-    pub fn text_document_diagnostic(&self) -> bool {
-        (|| -> _ { self.0.text_document.as_ref()?.diagnostic.as_ref() })().is_some()
-    }
-
-    pub fn text_document_diagnostic_related_document_support(&self) -> bool {
-        (|| -> _ { self.0.text_document.as_ref()?.diagnostic.as_ref()?.related_document_support })()
-            == Some(true)
-    }
-
     pub fn code_action_group(&self) -> bool {
         self.experimental_bool("codeActionGroup")
     }
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 9a51df80fe..20be38a9e4 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -417,8 +417,6 @@ impl GlobalState {
                 }
             }
 
-            let supports_diagnostic_pull_model = self.config.text_document_diagnostic();
-
             let client_refresh = became_quiescent || state_changed;
             if client_refresh {
                 // Refresh semantic tokens if the client supports it.
@@ -436,21 +434,11 @@ impl GlobalState {
                 if self.config.inlay_hints_refresh() {
                     self.send_request::<lsp_types::request::InlayHintRefreshRequest>((), |_, _| ());
                 }
-
-                if supports_diagnostic_pull_model {
-                    self.send_request::<lsp_types::request::WorkspaceDiagnosticRefresh>(
-                        (),
-                        |_, _| (),
-                    );
-                }
             }
 
             let project_or_mem_docs_changed =
                 became_quiescent || state_changed || memdocs_added_or_removed;
-            if project_or_mem_docs_changed
-                && !supports_diagnostic_pull_model
-                && self.config.publish_diagnostics(None)
-            {
+            if project_or_mem_docs_changed && self.config.publish_diagnostics(None) {
                 self.update_diagnostics();
             }
             if project_or_mem_docs_changed && self.config.test_explorer() {
@@ -1092,23 +1080,6 @@ impl GlobalState {
             .on_latency_sensitive::<NO_RETRY, lsp_request::SemanticTokensRangeRequest>(handlers::handle_semantic_tokens_range)
             // FIXME: Some of these NO_RETRY could be retries if the file they are interested didn't change.
             // All other request handlers
-            .on_with::<lsp_request::DocumentDiagnosticRequest>(handlers::handle_document_diagnostics, || lsp_types::DocumentDiagnosticReportResult::Report(
-                lsp_types::DocumentDiagnosticReport::Full(
-                    lsp_types::RelatedFullDocumentDiagnosticReport {
-                        related_documents: None,
-                        full_document_diagnostic_report: lsp_types::FullDocumentDiagnosticReport {
-                            result_id: None,
-                            items: vec![],
-                        },
-                    },
-                ),
-            ), || lsp_server::ResponseError {
-                code: lsp_server::ErrorCode::ServerCancelled as i32,
-                message: "server cancelled the request".to_owned(),
-                data: serde_json::to_value(lsp_types::DiagnosticServerCancellationData {
-                    retrigger_request: true
-                }).ok(),
-            })
             .on::<RETRY, lsp_request::DocumentSymbolRequest>(handlers::handle_document_symbol)
             .on::<RETRY, lsp_request::FoldingRangeRequest>(handlers::handle_folding_range)
             .on::<NO_RETRY, lsp_request::SignatureHelpRequest>(handlers::handle_signature_help)
diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram
index 02c59646a9..3609da0bb2 100644
--- a/crates/syntax/rust.ungram
+++ b/crates/syntax/rust.ungram
@@ -736,7 +736,7 @@ PathPat =
   Path
 
 OrPat =
-  '|'? (Pat ('|' Pat)*)
+  (Pat ('|' Pat)* '|'?)
 
 BoxPat =
   'box' Pat
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index 23d2b355a9..b9eb1abb11 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -1283,8 +1283,6 @@ pub struct OrPat {
 impl OrPat {
     #[inline]
     pub fn pats(&self) -> AstChildren<Pat> { support::children(&self.syntax) }
-    #[inline]
-    pub fn pipe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![|]) }
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 6ec73e76f7..11f5e662e3 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -1140,13 +1140,3 @@ impl From<ast::AssocItem> for ast::AnyHasAttrs {
         Self::new(node)
     }
 }
-
-impl ast::OrPat {
-    pub fn leading_pipe(&self) -> Option<SyntaxToken> {
-        self.syntax
-            .children_with_tokens()
-            .find(|it| !it.kind().is_trivia())
-            .and_then(NodeOrToken::into_token)
-            .filter(|it| it.kind() == T![|])
-    }
-}
diff --git a/editors/code/.vscodeignore b/editors/code/.vscodeignore
index 1712a1477e..09dc27056b 100644
--- a/editors/code/.vscodeignore
+++ b/editors/code/.vscodeignore
@@ -12,4 +12,3 @@
 !ra_syntax_tree.tmGrammar.json
 !server
 !README.md
-!walkthrough-setup-tips.md
diff --git a/editors/code/package.json b/editors/code/package.json
index 6eebdf9f01..e55eceff78 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -3224,9 +3224,10 @@
                     {
                         "id": "setup",
                         "title": "Useful Setup Tips",
-                        "description": "There are a couple of things you might want to configure upfront to your tastes. We'll name a few here but be sure to check out the docs linked below!\n\n**Marking library sources as readonly**\n\nAdding the snippet on the right to your settings.json will mark all Rust library sources as readonly.\n\n**Check on Save**\n\nBy default, rust-analyzer will run ``cargo check`` on your codebase when you save a file, rendering diagnostics emitted by ``cargo check`` within your code. This can potentially collide with other ``cargo`` commands running concurrently, blocking them from running for a certain amount of time. In these cases it is recommended to disable the ``rust-analyzer.checkOnSave`` configuration and running the ``rust-analyzer: Run flycheck`` command on-demand instead.",
+                        "description": "There are a couple of things you might want to configure upfront to your tastes. We'll name a few here but be sure to check out the docs linked below!\n\n**Marking library sources as readonly**\n\nAdding the following to your settings.json will mark all Rust library sources as readonly:\n```json\n\"files.readonlyInclude\": {\n    \"**/.cargo/registry/src/**/*.rs\": true,\n    \"**/lib/rustlib/src/rust/library/**/*.rs\": true,\n},\n```\n\n**Check on Save**\n\nBy default, rust-analyzer will run `cargo check` on your codebase when you save a file, rendering diagnostics emitted by `cargo check` within your code. This can potentially collide with other `cargo` commands running concurrently, blocking them from running for a certain amount of time. In these cases it is recommended to disable the `rust-analyzer.checkOnSave` configuration and running the `rust-analyzer: Run flycheck` command on-demand instead.",
                         "media": {
-                            "markdown": "./walkthrough-setup-tips.md"
+                            "image": "./icon.png",
+                            "altText": "rust-analyzer logo"
                         }
                     },
                     {
@@ -3244,7 +3245,7 @@
                     {
                         "id": "faq",
                         "title": "FAQ",
-                        "description": "What are these code hints that are being inserted into my code?\n\nThese hints are called inlay hints which rust-analyzer support and are enabled by default in VSCode. If you wish to disable them you can do so via the ``editor.inlayHints.enabled`` setting.",
+                        "description": "What are these code hints that are being inserted into my code?\n\nThese hints are called inlay hints which rust-analyzer support and are enabled by default in VSCode. If you wish to disable them you can do so via the `editor.inlayHints.enabled` setting.",
                         "media": {
                             "image": "icon.png",
                             "altText": "rust-analyzer logo"
diff --git a/editors/code/walkthrough-setup-tips.md b/editors/code/walkthrough-setup-tips.md
deleted file mode 100644
index fda4ac8002..0000000000
--- a/editors/code/walkthrough-setup-tips.md
+++ /dev/null
@@ -1,10 +0,0 @@
-# Settings Example
-
-Add the following to settings.json to mark Rust library sources as read-only:
-
-```json
-"files.readonlyInclude": {
-  "**/.cargo/registry/src/**/*.rs": true,
-  "**/lib/rustlib/src/rust/library/**/*.rs": true,
-},
-```
diff --git a/rust-bors.toml b/rust-bors.toml
new file mode 100644
index 0000000000..c31ba66c50
--- /dev/null
+++ b/rust-bors.toml
@@ -0,0 +1,1 @@
+timeout = 3600
diff --git a/xtask/src/release/changelog.rs b/xtask/src/release/changelog.rs
index 343a9efbbc..086a4d463e 100644
--- a/xtask/src/release/changelog.rs
+++ b/xtask/src/release/changelog.rs
@@ -128,10 +128,9 @@ fn unescape(s: &str) -> String {
 }
 
 fn parse_pr_number(s: &str) -> Option<u32> {
-    const GITHUB_PREFIX: &str = "Merge pull request #";
+    const BORS_PREFIX: &str = "Merge #";
     const HOMU_PREFIX: &str = "Auto merge of #";
-    if let Some(s) = s.strip_prefix(GITHUB_PREFIX) {
-        let s = if let Some(space) = s.find(' ') { &s[..space] } else { s };
+    if let Some(s) = s.strip_prefix(BORS_PREFIX) {
         s.parse().ok()
     } else if let Some(s) = s.strip_prefix(HOMU_PREFIX) {
         if let Some(space) = s.find(' ') {


Test patch:
diff --git a/crates/hir-def/src/macro_expansion_tests/mod.rs b/crates/hir-def/src/macro_expansion_tests/mod.rs
index d5b94f0ae4..450a15bd66 100644
--- a/crates/hir-def/src/macro_expansion_tests/mod.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mod.rs
@@ -122,7 +122,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
 
         let mut expn_text = String::new();
         if let Some(err) = exp.err {
-            format_to!(expn_text, "/* error: {} */", err.render_to_string(&db).message);
+            format_to!(expn_text, "/* error: {} */", err.render_to_string(&db).0);
         }
         let (parse, token_map) = exp.value;
         if expect_errors {
diff --git a/crates/hir-def/src/nameres/tests.rs b/crates/hir-def/src/nameres/tests.rs
index 7b02a89e5d..e1e30e5cec 100644
--- a/crates/hir-def/src/nameres/tests.rs
+++ b/crates/hir-def/src/nameres/tests.rs
@@ -385,6 +385,52 @@ pub struct Arc;
     );
 }
 
+#[test]
+fn extern_crate_reexport() {
+    check(
+        r#"
+//- /main.rs crate:main deps:importer
+use importer::*;
+use importer::extern_crate1::exported::*;
+use importer::allowed_reexport::*;
+use importer::extern_crate2::*;
+use importer::not_allowed_reexport1;
+use importer::not_allowed_reexport2;
+
+//- /importer.rs crate:importer deps:extern_crate1,extern_crate2
+extern crate extern_crate1;
+extern crate extern_crate2;
+
+pub use extern_crate1;
+pub use extern_crate1 as allowed_reexport;
+
+pub use ::extern_crate;
+pub use self::extern_crate as not_allowed_reexport1;
+pub use crate::extern_crate as not_allowed_reexport2;
+
+//- /extern_crate1.rs crate:extern_crate1
+pub mod exported {
+    pub struct PublicItem;
+    struct PrivateItem;
+}
+
+pub struct Exported;
+
+//- /extern_crate2.rs crate:extern_crate2
+pub struct NotExported;
+"#,
+        expect![[r#"
+            crate
+            Exported: t v
+            PublicItem: t v
+            allowed_reexport: t
+            exported: t
+            not_allowed_reexport1: _
+            not_allowed_reexport2: _
+        "#]],
+    );
+}
+
 #[test]
 fn extern_crate_rename_2015_edition() {
     check(
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 3e40263041..ef835f5bef 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -289,7 +289,7 @@ m!(ab$0c);
             *abc*
 
             ```rust
-            test
+            test::module
             ```
 
             ```rust
@@ -298,11 +298,11 @@ m!(ab$0c);
 
             ---
 
-            Outer
+            Inner
             ---
 
             ```rust
-            test::module
+            test
             ```
 
             ```rust
@@ -311,7 +311,7 @@ m!(ab$0c);
 
             ---
 
-            Inner
+            Outer
         "#]],
     );
 }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html b/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html
index 361dcd1bc3..d07ba74db2 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html
@@ -50,4 +50,4 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="brace">}</span>
 
 <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="module attribute crate_root library">proc_macros</span><span class="operator attribute">::</span><span class="attribute attribute library">issue_18089</span><span class="attribute_bracket attribute">]</span>
-<span class="keyword">fn</span> <span class="macro declaration">template</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span></code></pre>
\ No newline at end of file
+<span class="keyword">fn</span> <span class="function declaration">template</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span></code></pre>
\ No newline at end of file
diff --git a/crates/parser/src/tests.rs b/crates/parser/src/tests.rs
index 4b19ddc752..e7bccb6685 100644
--- a/crates/parser/src/tests.rs
+++ b/crates/parser/src/tests.rs
@@ -15,20 +15,11 @@ use crate::{Edition, LexedStr, TopEntryPoint};
 #[path = "../test_data/generated/runner.rs"]
 mod runner;
 
-fn infer_edition(file_path: &Path) -> Edition {
-    let file_content = std::fs::read_to_string(file_path).unwrap();
-    if let Some(edition) = file_content.strip_prefix("//@ edition: ") {
-        edition[..4].parse().expect("invalid edition directive")
-    } else {
-        Edition::CURRENT
-    }
-}
-
 #[test]
 fn lex_ok() {
     for case in TestCase::list("lexer/ok") {
         let _guard = stdx::panic_context::enter(format!("{:?}", case.rs));
-        let actual = lex(&case.text, infer_edition(&case.rs));
+        let actual = lex(&case.text);
         expect_file![case.rast].assert_eq(&actual)
     }
 }
@@ -37,13 +28,13 @@ fn lex_ok() {
 fn lex_err() {
     for case in TestCase::list("lexer/err") {
         let _guard = stdx::panic_context::enter(format!("{:?}", case.rs));
-        let actual = lex(&case.text, infer_edition(&case.rs));
+        let actual = lex(&case.text);
         expect_file![case.rast].assert_eq(&actual)
     }
 }
 
-fn lex(text: &str, edition: Edition) -> String {
-    let lexed = LexedStr::new(edition, text);
+fn lex(text: &str) -> String {
+    let lexed = LexedStr::new(Edition::CURRENT, text);
 
     let mut res = String::new();
     for i in 0..lexed.len() {
diff --git a/crates/parser/src/tests/top_entries.rs b/crates/parser/src/tests/top_entries.rs
index 7076e03ba4..c56bf0b644 100644
--- a/crates/parser/src/tests/top_entries.rs
+++ b/crates/parser/src/tests/top_entries.rs
@@ -195,38 +195,6 @@ fn macro_pattern() {
             error 0: expected pattern
         "#]],
     );
-
-    check(
-        TopEntryPoint::Pattern,
-        "| 42 | 43",
-        expect![[r#"
-            OR_PAT
-              PIPE "|"
-              WHITESPACE " "
-              LITERAL_PAT
-                LITERAL
-                  INT_NUMBER "42"
-              WHITESPACE " "
-              PIPE "|"
-              WHITESPACE " "
-              LITERAL_PAT
-                LITERAL
-                  INT_NUMBER "43"
-        "#]],
-    );
-
-    check(
-        TopEntryPoint::Pattern,
-        "| 42",
-        expect![[r#"
-            OR_PAT
-              PIPE "|"
-              WHITESPACE " "
-              LITERAL_PAT
-                LITERAL
-                  INT_NUMBER "42"
-        "#]],
-    );
 }
 
 #[test]
diff --git a/crates/parser/test_data/lexer/ok/guarded_str_prefix_edition_2021.rast b/crates/parser/test_data/lexer/ok/guarded_str_prefix_edition_2021.rast
deleted file mode 100644
index 1bdd672044..0000000000
--- a/crates/parser/test_data/lexer/ok/guarded_str_prefix_edition_2021.rast
+++ /dev/null
@@ -1,4 +0,0 @@
-COMMENT "//@ edition: 2021"
-WHITESPACE "\n\n"
-POUND "#"
-STRING "\"foo\""
diff --git a/crates/parser/test_data/lexer/ok/guarded_str_prefix_edition_2021.rs b/crates/parser/test_data/lexer/ok/guarded_str_prefix_edition_2021.rs
deleted file mode 100644
index f00f949f0d..0000000000
--- a/crates/parser/test_data/lexer/ok/guarded_str_prefix_edition_2021.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-//@ edition: 2021
-
-#"foo"
\ No newline at end of file
diff --git a/crates/parser/test_data/parser/inline/ok/match_arm.rast b/crates/parser/test_data/parser/inline/ok/match_arm.rast
index 9221028162..8189cf0a8e 100644
--- a/crates/parser/test_data/parser/inline/ok/match_arm.rast
+++ b/crates/parser/test_data/parser/inline/ok/match_arm.rast
@@ -102,9 +102,9 @@ SOURCE_FILE
                 COMMA ","
               WHITESPACE "\n        "
               MATCH_ARM
+                PIPE "|"
+                WHITESPACE " "
                 OR_PAT
-                  PIPE "|"
-                  WHITESPACE " "
                   IDENT_PAT
                     NAME
                       IDENT "X"
@@ -132,12 +132,11 @@ SOURCE_FILE
                 COMMA ","
               WHITESPACE "\n        "
               MATCH_ARM
-                OR_PAT
-                  PIPE "|"
-                  WHITESPACE " "
-                  IDENT_PAT
-                    NAME
-                      IDENT "X"
+                PIPE "|"
+                WHITESPACE " "
+                IDENT_PAT
+                  NAME
+                    IDENT "X"
                 WHITESPACE " "
                 FAT_ARROW "=>"
                 WHITESPACE " "
diff --git a/crates/parser/test_data/parser/inline/ok/slice_pat.rast b/crates/parser/test_data/parser/inline/ok/slice_pat.rast
index 06c30bba59..dff72ba886 100644
--- a/crates/parser/test_data/parser/inline/ok/slice_pat.rast
+++ b/crates/parser/test_data/parser/inline/ok/slice_pat.rast
@@ -43,12 +43,11 @@ SOURCE_FILE
           WHITESPACE " "
           SLICE_PAT
             L_BRACK "["
-            OR_PAT
-              PIPE "|"
-              WHITESPACE " "
-              IDENT_PAT
-                NAME
-                  IDENT "a"
+            PIPE "|"
+            WHITESPACE " "
+            IDENT_PAT
+              NAME
+                IDENT "a"
             COMMA ","
             WHITESPACE " "
             REST_PAT
diff --git a/crates/parser/test_data/parser/inline/ok/tuple_pat.rast b/crates/parser/test_data/parser/inline/ok/tuple_pat.rast
index c7cd11f774..1a01e0f693 100644
--- a/crates/parser/test_data/parser/inline/ok/tuple_pat.rast
+++ b/crates/parser/test_data/parser/inline/ok/tuple_pat.rast
@@ -91,9 +91,9 @@ SOURCE_FILE
           WHITESPACE " "
           TUPLE_PAT
             L_PAREN "("
+            PIPE "|"
+            WHITESPACE " "
             OR_PAT
-              PIPE "|"
-              WHITESPACE " "
               IDENT_PAT
                 NAME
                   IDENT "a"
@@ -105,12 +105,11 @@ SOURCE_FILE
                   IDENT "a"
             COMMA ","
             WHITESPACE " "
-            OR_PAT
-              PIPE "|"
-              WHITESPACE " "
-              IDENT_PAT
-                NAME
-                  IDENT "b"
+            PIPE "|"
+            WHITESPACE " "
+            IDENT_PAT
+              NAME
+                IDENT "b"
             R_PAREN ")"
           WHITESPACE " "
           EQ "="
diff --git a/crates/parser/test_data/parser/inline/ok/tuple_pat_fields.rast b/crates/parser/test_data/parser/inline/ok/tuple_pat_fields.rast
index 96353f4697..55baf2fdcb 100644
--- a/crates/parser/test_data/parser/inline/ok/tuple_pat_fields.rast
+++ b/crates/parser/test_data/parser/inline/ok/tuple_pat_fields.rast
@@ -110,12 +110,11 @@ SOURCE_FILE
                 NAME_REF
                   IDENT "S"
             L_PAREN "("
-            OR_PAT
-              PIPE "|"
-              WHITESPACE " "
-              IDENT_PAT
-                NAME
-                  IDENT "a"
+            PIPE "|"
+            WHITESPACE " "
+            IDENT_PAT
+              NAME
+                IDENT "a"
             R_PAREN ")"
           WHITESPACE " "
           EQ "="