diff --git a/Cargo.toml b/Cargo.toml
index 90e59143279..16dd5103899 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,7 +4,7 @@ exclude = ["crates/proc-macro-srv/proc-macro-test/imp"]
 resolver = "2"
 
 [workspace.package]
-rust-version = "1.74"
+rust-version = "1.76"
 edition = "2021"
 license = "MIT OR Apache-2.0"
 authors = ["rust-analyzer team"]
diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs
index 7d637bac096..c7502890ef4 100644
--- a/crates/hir/src/attrs.rs
+++ b/crates/hir/src/attrs.rs
@@ -124,7 +124,7 @@ fn resolve_doc_path_on_(
         AttrDefId::GenericParamId(_) => return None,
     };
 
-    let mut modpath = modpath_from_str(link)?;
+    let mut modpath = doc_modpath_from_str(link)?;
 
     let resolved = resolver.resolve_module_path_in_items(db.upcast(), &modpath);
     if resolved.is_none() {
@@ -299,7 +299,7 @@ fn as_module_def_if_namespace_matches(
     (ns.unwrap_or(expected_ns) == expected_ns).then_some(DocLinkDef::ModuleDef(def))
 }
 
-fn modpath_from_str(link: &str) -> Option<ModPath> {
+fn doc_modpath_from_str(link: &str) -> Option<ModPath> {
     // FIXME: this is not how we should get a mod path here.
     let try_get_modpath = |link: &str| {
         let mut parts = link.split("::");
@@ -327,7 +327,9 @@ fn modpath_from_str(link: &str) -> Option<ModPath> {
         };
         let parts = first_segment.into_iter().chain(parts).map(|segment| match segment.parse() {
             Ok(idx) => Name::new_tuple_field(idx),
-            Err(_) => Name::new_text_dont_use(segment.into()),
+            Err(_) => {
+                Name::new_text_dont_use(segment.split_once('<').map_or(segment, |it| it.0).into())
+            }
         });
         Some(ModPath::from_segments(kind, parts))
     };
diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs
index 1b6ff8bad53..33970de1e4b 100644
--- a/crates/ide-db/src/defs.rs
+++ b/crates/ide-db/src/defs.rs
@@ -721,7 +721,7 @@ impl NameRefClass {
 
 impl_from!(
     Field, Module, Function, Adt, Variant, Const, Static, Trait, TraitAlias, TypeAlias, BuiltinType, Local,
-    GenericParam, Label, Macro
+    GenericParam, Label, Macro, ExternCrateDecl
     for Definition
 );
 
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index 18821bd78bf..d10bdca50d8 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -233,21 +233,22 @@ pub(crate) fn doc_attributes(
 ) -> Option<(hir::AttrsWithOwner, Definition)> {
     match_ast! {
         match node {
-            ast::SourceFile(it)  => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Module(def))),
-            ast::Module(it)      => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Module(def))),
-            ast::Fn(it)          => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Function(def))),
-            ast::Struct(it)      => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Adt(hir::Adt::Struct(def)))),
-            ast::Union(it)       => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Adt(hir::Adt::Union(def)))),
-            ast::Enum(it)        => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Adt(hir::Adt::Enum(def)))),
-            ast::Variant(it)     => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Variant(def))),
-            ast::Trait(it)       => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Trait(def))),
-            ast::Static(it)      => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Static(def))),
-            ast::Const(it)       => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Const(def))),
-            ast::TypeAlias(it)   => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::TypeAlias(def))),
-            ast::Impl(it)        => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::SelfType(def))),
-            ast::RecordField(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Field(def))),
-            ast::TupleField(it)  => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Field(def))),
-            ast::Macro(it)       => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Macro(def))),
+            ast::SourceFile(it)  => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::from(def))),
+            ast::Module(it)      => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::from(def))),
+            ast::Fn(it)          => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::from(def))),
+            ast::Struct(it)      => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::from(hir::Adt::Struct(def)))),
+            ast::Union(it)       => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::from(hir::Adt::Union(def)))),
+            ast::Enum(it)        => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::from(hir::Adt::Enum(def)))),
+            ast::Variant(it)     => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::from(def))),
+            ast::Trait(it)       => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::from(def))),
+            ast::Static(it)      => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::from(def))),
+            ast::Const(it)       => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::from(def))),
+            ast::TypeAlias(it)   => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::from(def))),
+            ast::Impl(it)        => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::from(def))),
+            ast::RecordField(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::from(def))),
+            ast::TupleField(it)  => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::from(def))),
+            ast::Macro(it)       => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::from(def))),
+            ast::ExternCrate(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::from(def))),
             // ast::Use(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
             _ => None
         }
diff --git a/crates/ide/src/doc_links/intra_doc_links.rs b/crates/ide/src/doc_links/intra_doc_links.rs
index 13088bdc3b3..ebdd4add177 100644
--- a/crates/ide/src/doc_links/intra_doc_links.rs
+++ b/crates/ide/src/doc_links/intra_doc_links.rs
@@ -1,10 +1,10 @@
 //! Helper tools for intra doc links.
 
-const TYPES: ([&str; 9], [&str; 0]) =
-    (["type", "struct", "enum", "mod", "trait", "union", "module", "prim", "primitive"], []);
-const VALUES: ([&str; 8], [&str; 1]) =
-    (["value", "function", "fn", "method", "const", "static", "mod", "module"], ["()"]);
-const MACROS: ([&str; 2], [&str; 1]) = (["macro", "derive"], ["!"]);
+const TYPES: (&[&str], &[&str]) =
+    (&["type", "struct", "enum", "mod", "trait", "union", "module", "prim", "primitive"], &[]);
+const VALUES: (&[&str], &[&str]) =
+    (&["value", "function", "fn", "method", "const", "static", "mod", "module"], &["()"]);
+const MACROS: (&[&str], &[&str]) = (&["macro", "derive"], &["!"]);
 
 /// Extract the specified namespace from an intra-doc-link if one exists.
 ///
@@ -17,42 +17,38 @@ pub(super) fn parse_intra_doc_link(s: &str) -> (&str, Option<hir::Namespace>) {
     let s = s.trim_matches('`');
 
     [
-        (hir::Namespace::Types, (TYPES.0.iter(), TYPES.1.iter())),
-        (hir::Namespace::Values, (VALUES.0.iter(), VALUES.1.iter())),
-        (hir::Namespace::Macros, (MACROS.0.iter(), MACROS.1.iter())),
+        (hir::Namespace::Types, TYPES),
+        (hir::Namespace::Values, VALUES),
+        (hir::Namespace::Macros, MACROS),
     ]
     .into_iter()
-    .find_map(|(ns, (mut prefixes, mut suffixes))| {
-        if let Some(prefix) = prefixes.find(|&&prefix| {
+    .find_map(|(ns, (prefixes, suffixes))| {
+        if let Some(prefix) = prefixes.iter().find(|&&prefix| {
             s.starts_with(prefix)
                 && s.chars().nth(prefix.len()).map_or(false, |c| c == '@' || c == ' ')
         }) {
             Some((&s[prefix.len() + 1..], ns))
         } else {
-            suffixes.find_map(|&suffix| s.strip_suffix(suffix).zip(Some(ns)))
+            suffixes.iter().find_map(|&suffix| s.strip_suffix(suffix).zip(Some(ns)))
         }
     })
     .map_or((s, None), |(s, ns)| (s, Some(ns)))
 }
 
 pub(super) fn strip_prefixes_suffixes(s: &str) -> &str {
-    [
-        (TYPES.0.iter(), TYPES.1.iter()),
-        (VALUES.0.iter(), VALUES.1.iter()),
-        (MACROS.0.iter(), MACROS.1.iter()),
-    ]
-    .into_iter()
-    .find_map(|(mut prefixes, mut suffixes)| {
-        if let Some(prefix) = prefixes.find(|&&prefix| {
-            s.starts_with(prefix)
-                && s.chars().nth(prefix.len()).map_or(false, |c| c == '@' || c == ' ')
-        }) {
-            Some(&s[prefix.len() + 1..])
-        } else {
-            suffixes.find_map(|&suffix| s.strip_suffix(suffix))
-        }
-    })
-    .unwrap_or(s)
+    [TYPES, VALUES, MACROS]
+        .into_iter()
+        .find_map(|(prefixes, suffixes)| {
+            if let Some(prefix) = prefixes.iter().find(|&&prefix| {
+                s.starts_with(prefix)
+                    && s.chars().nth(prefix.len()).map_or(false, |c| c == '@' || c == ' ')
+            }) {
+                Some(&s[prefix.len() + 1..])
+            } else {
+                suffixes.iter().find_map(|&suffix| s.strip_suffix(suffix))
+            }
+        })
+        .unwrap_or(s)
 }
 
 #[cfg(test)]
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index ead4f91595f..b9ae89cc18d 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -6103,6 +6103,31 @@ pub struct Foo(i32);
     );
 }
 
+#[test]
+fn hover_intra_generics() {
+    check(
+        r#"
+/// Doc comment for [`Foo$0<T>`]
+pub struct Foo<T>(T);
+"#,
+        expect![[r#"
+            *[`Foo<T>`]*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            pub struct Foo<T>(T);
+            ```
+
+            ---
+
+            Doc comment for [`Foo<T>`](https://docs.rs/test/*/test/struct.Foo.html)
+        "#]],
+    );
+}
+
 #[test]
 fn hover_inert_attr() {
     check(
diff --git a/crates/syntax/fuzz/Cargo.toml b/crates/syntax/fuzz/Cargo.toml
index ebf538aa247..a235e3e17ce 100644
--- a/crates/syntax/fuzz/Cargo.toml
+++ b/crates/syntax/fuzz/Cargo.toml
@@ -3,7 +3,7 @@ name = "syntax-fuzz"
 version = "0.0.1"
 publish = false
 edition = "2021"
-rust-version = "1.66.1"
+rust-version = "1.76"
 
 [package.metadata]
 cargo-fuzz = true
@@ -26,4 +26,4 @@ name = "reparse"
 path = "fuzz_targets/reparse.rs"
 
 [lints]
-workspace = true
\ No newline at end of file
+workspace = true