Repository: rust-analyzer/rust-analyzer
Pull request #10807 (2021-11-19 12:19:59)

fix: Diagnose invalid derive attribute input
Doesn't yet diagnose incorrect syntax between the `(`, `)` braces as we discard those problems currently.
bors r+


Patch:
diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs
index 2f6eed82cb..c7a8463a3c 100644
--- a/crates/hir/src/attrs.rs
+++ b/crates/hir/src/attrs.rs
@@ -147,7 +147,7 @@ fn resolve_doc_path(
         AttrDefId::MacroDefId(_) => return None,
     };
     let path = ast::Path::parse(link).ok()?;
-    let modpath = ModPath::from_src(db.upcast(), path, &Hygiene::new_unhygienic())?;
+    let modpath = ModPath::from_src(db.upcast(), path, &Hygiene::new_unhygienic()).unwrap();
     let resolved = resolver.resolve_module_path_in_items(db.upcast(), &modpath);
     let resolved = if resolved == PerNs::none() {
         resolver.resolve_module_path_in_trait_assoc_items(db.upcast(), &modpath)?
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 5c4104baae..83c6f2c3b8 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -34,6 +34,7 @@ diagnostics![
     IncorrectCase,
     InvalidDeriveTarget,
     MacroError,
+    MalformedDerive,
     MismatchedArgCount,
     MissingFields,
     MissingMatchArms,
@@ -104,6 +105,11 @@ pub struct InvalidDeriveTarget {
     pub node: InFile<SyntaxNodePtr>,
 }
 
+#[derive(Debug)]
+pub struct MalformedDerive {
+    pub node: InFile<SyntaxNodePtr>,
+}
+
 #[derive(Debug)]
 pub struct NoSuchField {
     pub field: InFile<AstPtr<ast::RecordExprField>>,
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index cca9b08f5b..baa58c0824 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -83,10 +83,11 @@ pub use crate::{
     attrs::{HasAttrs, Namespace},
     diagnostics::{
         AddReferenceHere, AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, IncorrectCase,
-        InvalidDeriveTarget, MacroError, MismatchedArgCount, MissingFields, MissingMatchArms,
-        MissingOkOrSomeInTailExpr, MissingUnsafe, NoSuchField, RemoveThisSemicolon,
-        ReplaceFilterMapNextWithFindMap, UnimplementedBuiltinMacro, UnresolvedExternCrate,
-        UnresolvedImport, UnresolvedMacroCall, UnresolvedModule, UnresolvedProcMacro,
+        InvalidDeriveTarget, MacroError, MalformedDerive, MismatchedArgCount, MissingFields,
+        MissingMatchArms, MissingOkOrSomeInTailExpr, MissingUnsafe, NoSuchField,
+        RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap, UnimplementedBuiltinMacro,
+        UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall, UnresolvedModule,
+        UnresolvedProcMacro,
     },
     has_source::HasSource,
     semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo},
@@ -669,6 +670,21 @@ impl Module {
                         None => stdx::never!("derive diagnostic on item without derive attribute"),
                     }
                 }
+                DefDiagnosticKind::MalformedDerive { ast, id } => {
+                    let node = ast.to_node(db.upcast());
+                    let derive = node.attrs().nth(*id as usize);
+                    match derive {
+                        Some(derive) => {
+                            acc.push(
+                                MalformedDerive {
+                                    node: ast.with_value(SyntaxNodePtr::from(AstPtr::new(&derive))),
+                                }
+                                .into(),
+                            );
+                        }
+                        None => stdx::never!("derive diagnostic on item without derive attribute"),
+                    }
+                }
             }
         }
         for decl in self.declarations(db) {
@@ -1472,10 +1488,6 @@ impl Const {
         db.const_data(self.id).name.clone()
     }
 
-    pub fn value(self, db: &dyn HirDatabase) -> Option<ast::Expr> {
-        self.source(db)?.value.body()
-    }
-
     pub fn ty(self, db: &dyn HirDatabase) -> Type {
         let data = db.const_data(self.id);
         let resolver = self.id.resolver(db.upcast());
@@ -1512,10 +1524,6 @@ impl Static {
         db.static_data(self.id).mutable
     }
 
-    pub fn value(self, db: &dyn HirDatabase) -> Option<ast::Expr> {
-        self.source(db)?.value.body()
-    }
-
     pub fn ty(self, db: &dyn HirDatabase) -> Type {
         let data = db.static_data(self.id);
         let resolver = self.id.resolver(db.upcast());
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 343e189046..5673bef38b 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -8,7 +8,6 @@ use std::iter;
 use base_db::{CrateId, Edition, FileId, ProcMacroId};
 use cfg::{CfgExpr, CfgOptions};
 use hir_expand::{
-    ast_id_map::FileAstId,
     builtin_attr_macro::find_builtin_attr,
     builtin_derive_macro::find_builtin_derive,
     builtin_fn_macro::find_builtin_macro,
@@ -1081,8 +1080,10 @@ impl DefCollector<'_> {
                         return false;
                     }
                 }
-                MacroDirectiveKind::Attr { ast_id, mod_item, attr } => {
-                    let file_id = ast_id.ast_id.file_id;
+                MacroDirectiveKind::Attr { ast_id: file_ast_id, mod_item, attr } => {
+                    let &AstIdWithPath { ast_id, ref path } = file_ast_id;
+                    let file_id = ast_id.file_id;
+
                     let mut recollect_without = |collector: &mut Self, item_tree| {
                         // Remove the original directive since we resolved it.
                         let mod_dir = collector.mod_dirs[&directive.module_id].clone();
@@ -1100,8 +1101,8 @@ impl DefCollector<'_> {
                         false
                     };
 
-                    if let Some(ident) = ast_id.path.as_ident() {
-                        if let Some(helpers) = self.derive_helpers_in_scope.get(&ast_id.ast_id) {
+                    if let Some(ident) = path.as_ident() {
+                        if let Some(helpers) = self.derive_helpers_in_scope.get(&ast_id) {
                             if helpers.contains(ident) {
                                 cov_mark::hit!(resolved_derive_helper);
                                 // Resolved to derive helper. Collect the item's attributes again,
@@ -1112,7 +1113,7 @@ impl DefCollector<'_> {
                         }
                     }
 
-                    let def = resolver(ast_id.path.clone()).filter(MacroDefId::is_attribute);
+                    let def = resolver(path.clone()).filter(MacroDefId::is_attribute);
                     if matches!(
                         def,
                         Some(MacroDefId {  kind:MacroDefKind::BuiltInAttr(expander, _),.. })
@@ -1121,26 +1122,23 @@ impl DefCollector<'_> {
                         // Resolved to `#[derive]`
                         let item_tree = self.db.file_item_tree(file_id);
 
-                        let ast_id: FileAstId<ast::Item> = match *mod_item {
-                            ModItem::Struct(it) => item_tree[it].ast_id.upcast(),
-                            ModItem::Union(it) => item_tree[it].ast_id.upcast(),
-                            ModItem::Enum(it) => item_tree[it].ast_id.upcast(),
+                        match mod_item {
+                            ModItem::Struct(_) | ModItem::Union(_) | ModItem::Enum(_) => (),
                             _ => {
                                 let diag = DefDiagnostic::invalid_derive_target(
                                     directive.module_id,
-                                    ast_id.ast_id,
+                                    ast_id,
                                     attr.id,
                                 );
                                 self.def_map.diagnostics.push(diag);
-                                res = ReachedFixedPoint::No;
-                                return false;
+                                return recollect_without(self, &item_tree);
                             }
-                        };
+                        }
 
                         match attr.parse_derive() {
                             Some(derive_macros) => {
                                 for path in derive_macros {
-                                    let ast_id = AstIdWithPath::new(file_id, ast_id, path);
+                                    let ast_id = AstIdWithPath::new(file_id, ast_id.value, path);
                                     self.unresolved_macros.push(MacroDirective {
                                         module_id: directive.module_id,
                                         depth: directive.depth + 1,
@@ -1152,8 +1150,12 @@ impl DefCollector<'_> {
                                 }
                             }
                             None => {
-                                // FIXME: diagnose
-                                tracing::debug!("malformed derive: {:?}", attr);
+                                let diag = DefDiagnostic::malformed_derive(
+                                    directive.module_id,
+                                    ast_id,
+                                    attr.id,
+                                );
+                                self.def_map.diagnostics.push(diag);
                             }
                         }
 
@@ -1165,7 +1167,8 @@ impl DefCollector<'_> {
                     }
 
                     // Not resolved to a derive helper or the derive attribute, so try to resolve as a normal attribute.
-                    match attr_macro_as_call_id(ast_id, attr, self.db, self.def_map.krate, def) {
+                    match attr_macro_as_call_id(file_ast_id, attr, self.db, self.def_map.krate, def)
+                    {
                         Ok(call_id) => {
                             let loc: MacroCallLoc = self.db.lookup_intern_macro_call(call_id);
 
@@ -1198,7 +1201,7 @@ impl DefCollector<'_> {
 
                             self.def_map.modules[directive.module_id]
                                 .scope
-                                .add_attr_macro_invoc(ast_id.ast_id, call_id);
+                                .add_attr_macro_invoc(ast_id, call_id);
 
                             resolved.push((directive.module_id, call_id, directive.depth));
                             res = ReachedFixedPoint::No;
diff --git a/crates/hir_def/src/nameres/diagnostics.rs b/crates/hir_def/src/nameres/diagnostics.rs
index 1eb3844731..51e3f18c11 100644
--- a/crates/hir_def/src/nameres/diagnostics.rs
+++ b/crates/hir_def/src/nameres/diagnostics.rs
@@ -32,6 +32,8 @@ pub enum DefDiagnosticKind {
     UnimplementedBuiltinMacro { ast: AstId<ast::Macro> },
 
     InvalidDeriveTarget { ast: AstId<ast::Item>, id: u32 },
+
+    MalformedDerive { ast: AstId<ast::Item>, id: u32 },
 }
 
 #[derive(Debug, PartialEq, Eq)]
@@ -116,4 +118,15 @@ impl DefDiagnostic {
             kind: DefDiagnosticKind::InvalidDeriveTarget { ast, id: id.ast_index },
         }
     }
+
+    pub(super) fn malformed_derive(
+        container: LocalModuleId,
+        ast: AstId<ast::Item>,
+        id: AttrId,
+    ) -> Self {
+        Self {
+            in_module: container,
+            kind: DefDiagnosticKind::MalformedDerive { ast, id: id.ast_index },
+        }
+    }
 }
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 8d24bdcf4f..f4fb52647e 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -11,7 +11,7 @@ use ide_db::{
     base_db::FileRange,
     defs::Definition,
     helpers::{pick_best_token, FamousDefs},
-    FxIndexSet, RootDatabase,
+    RootDatabase,
 };
 use itertools::Itertools;
 use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxNode, SyntaxToken, T};
@@ -69,7 +69,7 @@ impl HoverAction {
     }
 }
 
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
+#[derive(Debug, Clone, Eq, PartialEq)]
 pub struct HoverGotoTypeData {
     pub mod_path: String,
     pub nav: NavigationTarget,
@@ -136,12 +136,11 @@ pub(crate) fn hover(
         .flatten()
         .unique_by(|&(def, _)| def)
         .filter_map(|(def, node)| hover_for_definition(sema, file_id, def, &node, config))
-        .reduce(|mut acc: HoverResult, HoverResult { markup, actions }| {
+        .reduce(|mut acc, HoverResult { markup, actions }| {
             acc.actions.extend(actions);
             acc.markup = Markup::from(format!("{}\n---\n{}", acc.markup, markup));
             acc
         });
-
     if result.is_none() {
         // fallbacks, show keywords or types
         if let Some(res) = render::keyword(sema, config, &original_token) {
@@ -153,10 +152,7 @@ pub(crate) fn hover(
             return res;
         }
     }
-    result.map(|mut res: HoverResult| {
-        res.actions = dedupe_or_merge_hover_actions(res.actions);
-        RangeInfo::new(original_token.text_range(), res)
-    })
+    result.map(|res| RangeInfo::new(original_token.text_range(), res))
 }
 
 pub(crate) fn hover_for_definition(
@@ -318,7 +314,6 @@ fn goto_type_action_for_def(db: &RootDatabase, def: Definition) -> Option<HoverA
             Definition::Local(it) => it.ty(db),
             Definition::GenericParam(hir::GenericParam::ConstParam(it)) => it.ty(db),
             Definition::Field(field) => field.ty(db),
-            Definition::Function(function) => function.ret_type(db),
             _ => return None,
         };
 
@@ -345,43 +340,3 @@ fn walk_and_push_ty(
         }
     });
 }
-
-fn dedupe_or_merge_hover_actions(actions: Vec<HoverAction>) -> Vec<HoverAction> {
-    let mut deduped_actions = Vec::with_capacity(actions.len());
-    let mut go_to_type_targets = FxIndexSet::default();
-
-    let mut seen_implementation = false;
-    let mut seen_reference = false;
-    let mut seen_runnable = false;
-    for action in actions {
-        match action {
-            HoverAction::GoToType(targets) => {
-                go_to_type_targets.extend(targets);
-            }
-            HoverAction::Implementation(..) => {
-                if !seen_implementation {
-                    seen_implementation = true;
-                    deduped_actions.push(action);
-                }
-            }
-            HoverAction::Reference(..) => {
-                if !seen_reference {
-                    seen_reference = true;
-                    deduped_actions.push(action);
-                }
-            }
-            HoverAction::Runnable(..) => {
-                if !seen_runnable {
-                    seen_runnable = true;
-                    deduped_actions.push(action);
-                }
-            }
-        };
-    }
-
-    if !go_to_type_targets.is_empty() {
-        deduped_actions.push(HoverAction::GoToType(go_to_type_targets.into_iter().collect()));
-    }
-
-    deduped_actions
-}
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index e872d9b1fb..59068028ed 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -1,6 +1,4 @@
 //! Logic for rendering the different hover messages
-use std::fmt::Display;
-
 use either::Either;
 use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo};
 use ide_db::{
@@ -354,8 +352,8 @@ pub(super) fn definition(
         Definition::Function(it) => label_and_docs(db, it),
         Definition::Adt(it) => label_and_docs(db, it),
         Definition::Variant(it) => label_and_docs(db, it),
-        Definition::Const(it) => label_value_and_docs(db, it, |it| it.value(db)),
-        Definition::Static(it) => label_value_and_docs(db, it, |it| it.value(db)),
+        Definition::Const(it) => label_and_docs(db, it),
+        Definition::Static(it) => label_and_docs(db, it),
         Definition::Trait(it) => label_and_docs(db, it),
         Definition::TypeAlias(it) => label_and_docs(db, it),
         Definition::BuiltinType(it) => {
@@ -383,25 +381,6 @@ where
     (label, docs)
 }
 
-fn label_value_and_docs<D, E, V>(
-    db: &RootDatabase,
-    def: D,
-    value_extractor: E,
-) -> (String, Option<hir::Documentation>)
-where
-    D: HasAttrs + HirDisplay,
-    E: Fn(&D) -> Option<V>,
-    V: Display,
-{
-    let label = if let Some(value) = (value_extractor)(&def) {
-        format!("{} = {}", def.display(db), value)
-    } else {
-        def.display(db).to_string()
-    };
-    let docs = def.attrs(db).docs();
-    (label, docs)
-}
-
 fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> {
     if let Definition::GenericParam(_) = def {
         return None;
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 94af017d64..f3c5dce7c3 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -441,25 +441,15 @@ fn is_argument_similar_to_param_name(argument: &ast::Expr, param_name: &str) ->
         None => return false,
     };
 
-    // std is honestly too panic happy...
-    let str_split_at = |str: &str, at| str.is_char_boundary(at).then(|| argument.split_at(at));
-
     let param_name = param_name.trim_start_matches('_');
     let argument = argument.trim_start_matches('_');
-
-    match str_split_at(argument, param_name.len()) {
-        Some((prefix, rest)) if prefix.eq_ignore_ascii_case(param_name) => {
-            return rest.is_empty() || rest.starts_with('_');
-        }
-        _ => (),
+    if argument.strip_prefix(param_name).map_or(false, |s| s.starts_with('_')) {
+        return true;
     }
-    match argument.len().checked_sub(param_name.len()).and_then(|at| str_split_at(argument, at)) {
-        Some((rest, suffix)) if param_name.eq_ignore_ascii_case(suffix) => {
-            return rest.is_empty() || rest.ends_with('_');
-        }
-        _ => (),
+    if argument.strip_suffix(param_name).map_or(false, |s| s.ends_with('_')) {
+        return true;
     }
-    false
+    argument == param_name
 }
 
 /// Hide the parameter name of a unary function if it is a `_` - prefixed suffix of the function's name, or equal.
@@ -474,13 +464,9 @@ fn is_param_name_suffix_of_fn_name(
     match (callable.n_params(), fn_name) {
         (1, Some(function)) => {
             function == param_name
-                || function
-                    .len()
-                    .checked_sub(param_name.len())
-                    .and_then(|at| function.is_char_boundary(at).then(|| function.split_at(at)))
-                    .map_or(false, |(prefix, suffix)| {
-                        suffix.eq_ignore_ascii_case(param_name) && prefix.ends_with('_')
-                    })
+                || (function.len() > param_name.len()
+                    && function.ends_with(param_name)
+                    && function[..function.len() - param_name.len()].ends_with('_'))
         }
         _ => false,
     }
@@ -866,9 +852,6 @@ enum CompletionKind {
 fn non_ident_pat((a, b): (u32, u32)) {}
 
 fn main() {
-    const PARAM: u32 = 0;
-    foo(PARAM);
-
     check("");
 
     map(0);
diff --git a/crates/ide_diagnostics/src/handlers/invalid_derive_target.rs b/crates/ide_diagnostics/src/handlers/invalid_derive_target.rs
index 556d2dc0cb..c779266bc9 100644
--- a/crates/ide_diagnostics/src/handlers/invalid_derive_target.rs
+++ b/crates/ide_diagnostics/src/handlers/invalid_derive_target.rs
@@ -8,7 +8,6 @@ pub(crate) fn invalid_derive_target(
     ctx: &DiagnosticsContext<'_>,
     d: &hir::InvalidDeriveTarget,
 ) -> Diagnostic {
-    // Use more accurate position if available.
     let display_range = ctx.sema.diagnostics_display_range(d.node.clone()).range;
 
     Diagnostic::new(
diff --git a/crates/ide_diagnostics/src/handlers/malformed_derive.rs b/crates/ide_diagnostics/src/handlers/malformed_derive.rs
new file mode 100644
index 0000000000..cd48bdba07
--- /dev/null
+++ b/crates/ide_diagnostics/src/handlers/malformed_derive.rs
@@ -0,0 +1,37 @@
+use crate::{Diagnostic, DiagnosticsContext, Severity};
+
+// Diagnostic: malformed-derive
+//
+// This diagnostic is shown when the derive attribute has invalid input.
+pub(crate) fn malformed_derive(
+    ctx: &DiagnosticsContext<'_>,
+    d: &hir::MalformedDerive,
+) -> Diagnostic {
+    let display_range = ctx.sema.diagnostics_display_range(d.node.clone()).range;
+
+    Diagnostic::new(
+        "malformed-derive",
+        "malformed derive input, derive attributes are of the form `#[derive(Derive1, Derive2, ...)]`",
+        display_range,
+    )
+    .severity(Severity::Error)
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::check_diagnostics;
+
+    #[test]
+    fn invalid_input() {
+        check_diagnostics(
+            r#"
+//- minicore:derive
+mod __ {
+    #[derive = "aaaa"]
+  //^^^^^^^^^^^^^^^^^^ error: malformed derive input, derive attributes are of the form `#[derive(Derive1, Derive2, ...)]`
+    struct Foo;
+}
+            "#,
+        );
+    }
+}
diff --git a/crates/ide_diagnostics/src/lib.rs b/crates/ide_diagnostics/src/lib.rs
index 5070f24003..c921d989bd 100644
--- a/crates/ide_diagnostics/src/lib.rs
+++ b/crates/ide_diagnostics/src/lib.rs
@@ -30,6 +30,7 @@ mod handlers {
     pub(crate) mod incorrect_case;
     pub(crate) mod invalid_derive_target;
     pub(crate) mod macro_error;
+    pub(crate) mod malformed_derive;
     pub(crate) mod mismatched_arg_count;
     pub(crate) mod missing_fields;
     pub(crate) mod missing_match_arms;
@@ -182,6 +183,7 @@ pub fn diagnostics(
             AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
             AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d),
             AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d),
+            AnyDiagnostic::MalformedDerive(d) => handlers::malformed_derive::malformed_derive(&ctx, &d),
             AnyDiagnostic::MismatchedArgCount(d) => handlers::mismatched_arg_count::mismatched_arg_count(&ctx, &d),
             AnyDiagnostic::MissingFields(d) => handlers::missing_fields::missing_fields(&ctx, &d),
             AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d),
diff --git a/crates/parser/src/grammar/patterns.rs b/crates/parser/src/grammar/patterns.rs
index 70ff87b238..9da94ce162 100644
--- a/crates/parser/src/grammar/patterns.rs
+++ b/crates/parser/src/grammar/patterns.rs
@@ -74,11 +74,6 @@ fn pattern_single_r(p: &mut Parser, recovery_set: TokenSet) {
         //         Some(0) | None => (),
         //         Some(1..) => ()
         //     }
-        //
-        //     match (10 as u8, 5 as u8) {
-        //         (0, _) => (),
-        //         (1.., _) => ()
-        //     }
         // }
 
         // FIXME: support half_open_range_patterns (`..=2`),
@@ -90,7 +85,7 @@ fn pattern_single_r(p: &mut Parser, recovery_set: TokenSet) {
 
                 // `0 .. =>` or `let 0 .. =` or `Some(0 .. )`
                 //       ^                ^                ^
-                if p.at(T![=]) | p.at(T![')']) | p.at(T![,]) {
+                if p.at(T![=]) | p.at(T![')']) {
                     // test half_open_range_pat
                     // fn f() { let 0 .. = 1u32; }
                 } else {
diff --git a/crates/proc_macro_srv/src/abis/mod.rs b/crates/proc_macro_srv/src/abis/mod.rs
index 0de15b4832..5d7c623b0a 100644
--- a/crates/proc_macro_srv/src/abis/mod.rs
+++ b/crates/proc_macro_srv/src/abis/mod.rs
@@ -69,26 +69,22 @@ impl Abi {
         symbol_name: String,
         info: RustCInfo,
     ) -> Result<Abi, LoadProcMacroDylibError> {
-        // FIXME: this should use exclusive ranges when they're stable
-        // https://github.com/rust-lang/rust/issues/37854
-        match (info.version.0, info.version.1) {
-            (1, 47..=54) => {
-                let inner = unsafe { Abi_1_47::from_lib(lib, symbol_name) }?;
-                Ok(Abi::Abi1_47(inner))
-            }
-            (1, 55..=55) => {
-                let inner = unsafe { Abi_1_55::from_lib(lib, symbol_name) }?;
-                Ok(Abi::Abi1_55(inner))
-            }
-            (1, 56..=57) => {
-                let inner = unsafe { Abi_1_56::from_lib(lib, symbol_name) }?;
-                Ok(Abi::Abi1_56(inner))
-            }
-            (1, 58..) => {
-                let inner = unsafe { Abi_1_58::from_lib(lib, symbol_name) }?;
-                Ok(Abi::Abi1_58(inner))
-            }
-            _ => Err(LoadProcMacroDylibError::UnsupportedABI),
+        if info.version.0 != 1 {
+            Err(LoadProcMacroDylibError::UnsupportedABI)
+        } else if info.version.1 < 47 {
+            Err(LoadProcMacroDylibError::UnsupportedABI)
+        } else if info.version.1 < 54 {
+            let inner = unsafe { Abi_1_47::from_lib(lib, symbol_name) }?;
+            Ok(Abi::Abi1_47(inner))
+        } else if info.version.1 < 56 {
+            let inner = unsafe { Abi_1_55::from_lib(lib, symbol_name) }?;
+            Ok(Abi::Abi1_55(inner))
+        } else if info.version.1 < 57 {
+            let inner = unsafe { Abi_1_56::from_lib(lib, symbol_name) }?;
+            Ok(Abi::Abi1_56(inner))
+        } else {
+            let inner = unsafe { Abi_1_58::from_lib(lib, symbol_name) }?;
+            Ok(Abi::Abi1_58(inner))
         }
     }
 


Test patch:
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 5db6fd7974..2aa54fc33f 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -173,7 +173,7 @@ m!(ab$0c);
             ---
 
             Outer
-        "#]],
+            "#]],
     );
 }
 
@@ -503,52 +503,30 @@ fn hover_const_static() {
     check(
         r#"const foo$0: u32 = 123;"#,
         expect![[r#"
-            *foo*
-
-            ```rust
-            test
-            ```
-
-            ```rust
-            const foo: u32 = 123
-            ```
-        "#]],
-    );
-    check(
-        r#"
-const foo$0: u32 = {
-    let x = foo();
-    x + 100
-};"#,
-        expect![[r#"
-            *foo*
+                *foo*
 
-            ```rust
-            test
-            ```
+                ```rust
+                test
+                ```
 
-            ```rust
-            const foo: u32 = {
-                let x = foo();
-                x + 100
-            }
-            ```
-        "#]],
+                ```rust
+                const foo: u32
+                ```
+            "#]],
     );
-
     check(
         r#"static foo$0: u32 = 456;"#,
         expect![[r#"
-            *foo*
+                *foo*
 
-            ```rust
-            test
-            ```
+                ```rust
+                test
+                ```
 
-            ```rust
-            static foo: u32 = 456
-            ```
-        "#]],
+                ```rust
+                static foo: u32
+                ```
+            "#]],
     );
 }
 
@@ -810,16 +788,16 @@ fn main() {
 }
 "#,
         expect![[r#"
-            *C*
+                *C*
 
-            ```rust
-            test
-            ```
+                ```rust
+                test
+                ```
 
-            ```rust
-            const C: u32 = 1
-            ```
-        "#]],
+                ```rust
+                const C: u32
+                ```
+            "#]],
     )
 }
 
@@ -1135,39 +1113,6 @@ fn foo() {
     );
 }
 
-#[test]
-fn test_hover_multiple_actions() {
-    check_actions(
-        r#"
-struct Bar;
-struct Foo { bar: Bar }
-
-fn foo(Foo { b$0ar }: &Foo) {}
-        "#,
-        expect![[r#"
-            [
-                GoToType(
-                    [
-                        HoverGotoTypeData {
-                            mod_path: "test::Bar",
-                            nav: NavigationTarget {
-                                file_id: FileId(
-                                    0,
-                                ),
-                                full_range: 0..11,
-                                focus_range: 7..10,
-                                name: "Bar",
-                                kind: Struct,
-                                description: "struct Bar",
-                            },
-                        },
-                    ],
-                ),
-            ]
-        "#]],
-    )
-}
-
 #[test]
 fn test_hover_through_literal_string_in_builtin_macro() {
     check_hover_no_result(
@@ -1751,40 +1696,40 @@ fn test_hover_test_has_action() {
 fn foo_$0test() {}
 "#,
         expect![[r#"
-            [
-                Reference(
-                    FilePosition {
-                        file_id: FileId(
-                            0,
-                        ),
-                        offset: 11,
-                    },
-                ),
-                Runnable(
-                    Runnable {
-                        use_name_in_title: false,
-                        nav: NavigationTarget {
+                [
+                    Reference(
+                        FilePosition {
                             file_id: FileId(
                                 0,
                             ),
-                            full_range: 0..24,
-                            focus_range: 11..19,
-                            name: "foo_test",
-                            kind: Function,
+                            offset: 11,
                         },
-                        kind: Test {
-                            test_id: Path(
-                                "foo_test",
-                            ),
-                            attr: TestAttr {
-                                ignore: false,
+                    ),
+                    Runnable(
+                        Runnable {
+                            use_name_in_title: false,
+                            nav: NavigationTarget {
+                                file_id: FileId(
+                                    0,
+                                ),
+                                full_range: 0..24,
+                                focus_range: 11..19,
+                                name: "foo_test",
+                                kind: Function,
                             },
+                            kind: Test {
+                                test_id: Path(
+                                    "foo_test",
+                                ),
+                                attr: TestAttr {
+                                    ignore: false,
+                                },
+                            },
+                            cfg: None,
                         },
-                        cfg: None,
-                    },
-                ),
-            ]
-        "#]],
+                    ),
+                ]
+            "#]],
     );
 }
 
@@ -2779,21 +2724,21 @@ fn main() {
 }
 "#,
         expect![[r#"
-            *f*
+                *f*
 
-            ```rust
-            f: &i32
-            ```
-            ---
+                ```rust
+                f: &i32
+                ```
+                ---
 
-            ```rust
-            test::S
-            ```
+                ```rust
+                test::S
+                ```
 
-            ```rust
-            f: i32
-            ```
-        "#]],
+                ```rust
+                f: i32
+                ```
+            "#]],
     );
 }
 
@@ -3231,20 +3176,20 @@ fn foo() {
 }
 "#,
         expect![[r#"
-            *FOO*
+                *FOO*
 
-            ```rust
-            test
-            ```
+                ```rust
+                test
+                ```
 
-            ```rust
-            const FOO: usize = 3
-            ```
+                ```rust
+                const FOO: usize
+                ```
 
-            ---
+                ---
 
-            This is a doc
-        "#]],
+                This is a doc
+            "#]],
     );
 }
 
diff --git a/crates/syntax/test_data/parser/inline/ok/0058_range_pat.rast b/crates/syntax/test_data/parser/inline/ok/0058_range_pat.rast
index 0eb5af54b8..5d5803f751 100644
--- a/crates/syntax/test_data/parser/inline/ok/0058_range_pat.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0058_range_pat.rast
@@ -1,5 +1,5 @@
-SOURCE_FILE@0..307
-  FN@0..306
+SOURCE_FILE@0..223
+  FN@0..222
     FN_KW@0..2 "fn"
     WHITESPACE@2..3 " "
     NAME@3..7
@@ -8,8 +8,8 @@ SOURCE_FILE@0..307
       L_PAREN@7..8 "("
       R_PAREN@8..9 ")"
     WHITESPACE@9..10 " "
-    BLOCK_EXPR@10..306
-      STMT_LIST@10..306
+    BLOCK_EXPR@10..222
+      STMT_LIST@10..222
         L_CURLY@10..11 "{"
         WHITESPACE@11..16 "\n    "
         EXPR_STMT@16..132
@@ -96,156 +96,80 @@ SOURCE_FILE@0..307
               WHITESPACE@126..131 "\n    "
               R_CURLY@131..132 "}"
         WHITESPACE@132..138 "\n\n    "
-        EXPR_STMT@138..220
-          MATCH_EXPR@138..220
-            MATCH_KW@138..143 "match"
-            WHITESPACE@143..144 " "
-            CALL_EXPR@144..158
-              PATH_EXPR@144..148
-                PATH@144..148
-                  PATH_SEGMENT@144..148
-                    NAME_REF@144..148
-                      IDENT@144..148 "Some"
-              ARG_LIST@148..158
-                L_PAREN@148..149 "("
-                CAST_EXPR@149..157
-                  LITERAL@149..151
-                    INT_NUMBER@149..151 "10"
-                  WHITESPACE@151..152 " "
-                  AS_KW@152..154 "as"
-                  WHITESPACE@154..155 " "
-                  PATH_TYPE@155..157
-                    PATH@155..157
-                      PATH_SEGMENT@155..157
-                        NAME_REF@155..157
-                          IDENT@155..157 "u8"
-                R_PAREN@157..158 ")"
-            WHITESPACE@158..159 " "
-            MATCH_ARM_LIST@159..220
-              L_CURLY@159..160 "{"
-              WHITESPACE@160..169 "\n        "
-              MATCH_ARM@169..190
-                OR_PAT@169..183
-                  TUPLE_STRUCT_PAT@169..176
-                    PATH@169..173
-                      PATH_SEGMENT@169..173
-                        NAME_REF@169..173
-                          IDENT@169..173 "Some"
-                    L_PAREN@173..174 "("
-                    LITERAL_PAT@174..175
-                      LITERAL@174..175
-                        INT_NUMBER@174..175 "0"
-                    R_PAREN@175..176 ")"
-                  WHITESPACE@176..177 " "
-                  PIPE@177..178 "|"
-                  WHITESPACE@178..179 " "
-                  IDENT_PAT@179..183
-                    NAME@179..183
-                      IDENT@179..183 "None"
-                WHITESPACE@183..184 " "
-                FAT_ARROW@184..186 "=>"
-                WHITESPACE@186..187 " "
-                TUPLE_EXPR@187..189
-                  L_PAREN@187..188 "("
-                  R_PAREN@188..189 ")"
-                COMMA@189..190 ","
-              WHITESPACE@190..199 "\n        "
-              MATCH_ARM@199..214
-                TUPLE_STRUCT_PAT@199..208
-                  PATH@199..203
-                    PATH_SEGMENT@199..203
-                      NAME_REF@199..203
-                        IDENT@199..203 "Some"
-                  L_PAREN@203..204 "("
-                  RANGE_PAT@204..207
-                    LITERAL_PAT@204..205
-                      LITERAL@204..205
-                        INT_NUMBER@204..205 "1"
-                    DOT2@205..207 ".."
-                  R_PAREN@207..208 ")"
-                WHITESPACE@208..209 " "
-                FAT_ARROW@209..211 "=>"
-                WHITESPACE@211..212 " "
-                TUPLE_EXPR@212..214
-                  L_PAREN@212..213 "("
-                  R_PAREN@213..214 ")"
-              WHITESPACE@214..219 "\n    "
-              R_CURLY@219..220 "}"
-        WHITESPACE@220..226 "\n\n    "
-        MATCH_EXPR@226..304
-          MATCH_KW@226..231 "match"
-          WHITESPACE@231..232 " "
-          TUPLE_EXPR@232..251
-            L_PAREN@232..233 "("
-            CAST_EXPR@233..241
-              LITERAL@233..235
-                INT_NUMBER@233..235 "10"
-              WHITESPACE@235..236 " "
-              AS_KW@236..238 "as"
-              WHITESPACE@238..239 " "
-              PATH_TYPE@239..241
-                PATH@239..241
-                  PATH_SEGMENT@239..241
-                    NAME_REF@239..241
-                      IDENT@239..241 "u8"
-            COMMA@241..242 ","
-            WHITESPACE@242..243 " "
-            CAST_EXPR@243..250
-              LITERAL@243..244
-                INT_NUMBER@243..244 "5"
-              WHITESPACE@244..245 " "
-              AS_KW@245..247 "as"
-              WHITESPACE@247..248 " "
-              PATH_TYPE@248..250
-                PATH@248..250
-                  PATH_SEGMENT@248..250
-                    NAME_REF@248..250
-                      IDENT@248..250 "u8"
-            R_PAREN@250..251 ")"
-          WHITESPACE@251..252 " "
-          MATCH_ARM_LIST@252..304
-            L_CURLY@252..253 "{"
-            WHITESPACE@253..262 "\n        "
-            MATCH_ARM@262..275
-              TUPLE_PAT@262..268
-                L_PAREN@262..263 "("
-                LITERAL_PAT@263..264
-                  LITERAL@263..264
-                    INT_NUMBER@263..264 "0"
-                COMMA@264..265 ","
-                WHITESPACE@265..266 " "
-                WILDCARD_PAT@266..267
-                  UNDERSCORE@266..267 "_"
-                R_PAREN@267..268 ")"
-              WHITESPACE@268..269 " "
-              FAT_ARROW@269..271 "=>"
-              WHITESPACE@271..272 " "
-              TUPLE_EXPR@272..274
-                L_PAREN@272..273 "("
-                R_PAREN@273..274 ")"
-              COMMA@274..275 ","
-            WHITESPACE@275..284 "\n        "
-            MATCH_ARM@284..298
-              TUPLE_PAT@284..292
-                L_PAREN@284..285 "("
-                RANGE_PAT@285..288
-                  LITERAL_PAT@285..286
-                    LITERAL@285..286
-                      INT_NUMBER@285..286 "1"
-                  DOT2@286..288 ".."
-                COMMA@288..289 ","
-                WHITESPACE@289..290 " "
-                WILDCARD_PAT@290..291
-                  UNDERSCORE@290..291 "_"
-                R_PAREN@291..292 ")"
-              WHITESPACE@292..293 " "
-              FAT_ARROW@293..295 "=>"
-              WHITESPACE@295..296 " "
-              TUPLE_EXPR@296..298
-                L_PAREN@296..297 "("
-                R_PAREN@297..298 ")"
-            WHITESPACE@298..303 "\n    "
-            R_CURLY@303..304 "}"
-        WHITESPACE@304..305 "\n"
-        R_CURLY@305..306 "}"
-  WHITESPACE@306..307 "\n"
+        MATCH_EXPR@138..220
+          MATCH_KW@138..143 "match"
+          WHITESPACE@143..144 " "
+          CALL_EXPR@144..158
+            PATH_EXPR@144..148
+              PATH@144..148
+                PATH_SEGMENT@144..148
+                  NAME_REF@144..148
+                    IDENT@144..148 "Some"
+            ARG_LIST@148..158
+              L_PAREN@148..149 "("
+              CAST_EXPR@149..157
+                LITERAL@149..151
+                  INT_NUMBER@149..151 "10"
+                WHITESPACE@151..152 " "
+                AS_KW@152..154 "as"
+                WHITESPACE@154..155 " "
+                PATH_TYPE@155..157
+                  PATH@155..157
+                    PATH_SEGMENT@155..157
+                      NAME_REF@155..157
+                        IDENT@155..157 "u8"
+              R_PAREN@157..158 ")"
+          WHITESPACE@158..159 " "
+          MATCH_ARM_LIST@159..220
+            L_CURLY@159..160 "{"
+            WHITESPACE@160..169 "\n        "
+            MATCH_ARM@169..190
+              OR_PAT@169..183
+                TUPLE_STRUCT_PAT@169..176
+                  PATH@169..173
+                    PATH_SEGMENT@169..173
+                      NAME_REF@169..173
+                        IDENT@169..173 "Some"
+                  L_PAREN@173..174 "("
+                  LITERAL_PAT@174..175
+                    LITERAL@174..175
+                      INT_NUMBER@174..175 "0"
+                  R_PAREN@175..176 ")"
+                WHITESPACE@176..177 " "
+                PIPE@177..178 "|"
+                WHITESPACE@178..179 " "
+                IDENT_PAT@179..183
+                  NAME@179..183
+                    IDENT@179..183 "None"
+              WHITESPACE@183..184 " "
+              FAT_ARROW@184..186 "=>"
+              WHITESPACE@186..187 " "
+              TUPLE_EXPR@187..189
+                L_PAREN@187..188 "("
+                R_PAREN@188..189 ")"
+              COMMA@189..190 ","
+            WHITESPACE@190..199 "\n        "
+            MATCH_ARM@199..214
+              TUPLE_STRUCT_PAT@199..208
+                PATH@199..203
+                  PATH_SEGMENT@199..203
+                    NAME_REF@199..203
+                      IDENT@199..203 "Some"
+                L_PAREN@203..204 "("
+                RANGE_PAT@204..207
+                  LITERAL_PAT@204..205
+                    LITERAL@204..205
+                      INT_NUMBER@204..205 "1"
+                  DOT2@205..207 ".."
+                R_PAREN@207..208 ")"
+              WHITESPACE@208..209 " "
+              FAT_ARROW@209..211 "=>"
+              WHITESPACE@211..212 " "
+              TUPLE_EXPR@212..214
+                L_PAREN@212..213 "("
+                R_PAREN@213..214 ")"
+            WHITESPACE@214..219 "\n    "
+            R_CURLY@219..220 "}"
+        WHITESPACE@220..221 "\n"
+        R_CURLY@221..222 "}"
+  WHITESPACE@222..223 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0058_range_pat.rs b/crates/syntax/test_data/parser/inline/ok/0058_range_pat.rs
index 6c586a8956..fa91efd212 100644
--- a/crates/syntax/test_data/parser/inline/ok/0058_range_pat.rs
+++ b/crates/syntax/test_data/parser/inline/ok/0058_range_pat.rs
@@ -10,9 +10,4 @@ fn main() {
         Some(0) | None => (),
         Some(1..) => ()
     }
-
-    match (10 as u8, 5 as u8) {
-        (0, _) => (),
-        (1.., _) => ()
-    }
 }