about summary refs log tree commit diff
diff options
context:
space:
mode:
authorclubby789 <jamie@hill-daniel.co.uk>2023-02-11 00:00:18 +0000
committerclubby789 <jamie@hill-daniel.co.uk>2023-02-11 12:28:35 +0000
commitef8de38c8498885a46119f214e17f0237dab6251 (patch)
tree3073aa0e7a810e7d58790983fc9a8a2bd0ca32a1
parent2773383a314a4b8f481ce2bed12c32de794ffbe9 (diff)
downloadrust-ef8de38c8498885a46119f214e17f0237dab6251.tar.gz
rust-ef8de38c8498885a46119f214e17f0237dab6251.zip
rustdoc: Don't resolve link to field on different variant
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs29
-rw-r--r--tests/rustdoc-ui/intra-doc/errors.rs16
-rw-r--r--tests/rustdoc-ui/intra-doc/errors.stderr14
3 files changed, 54 insertions, 5 deletions
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 8435972bb11..1ee98a79b05 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -338,7 +338,8 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
         match ty_res {
             Res::Def(DefKind::Enum, did) => match tcx.type_of(did).kind() {
                 ty::Adt(def, _) if def.is_enum() => {
-                    if let Some(field) = def.all_fields().find(|f| f.name == variant_field_name) {
+                    if let Some(variant) = def.variants().iter().find(|v| v.name == variant_name)
+                        && let Some(field) = variant.fields.iter().find(|f| f.name == variant_field_name) {
                         Ok((ty_res, field.did))
                     } else {
                         Err(UnresolvedPath {
@@ -1768,15 +1769,35 @@ fn resolution_failure(
 
                     // Otherwise, it must be an associated item or variant
                     let res = partial_res.expect("None case was handled by `last_found_module`");
-                    let kind = match res {
-                        Res::Def(kind, _) => Some(kind),
+                    let kind_did = match res {
+                        Res::Def(kind, did) => Some((kind, did)),
                         Res::Primitive(_) => None,
                     };
-                    let path_description = if let Some(kind) = kind {
+                    let is_struct_variant = |did| {
+                        if let ty::Adt(def, _) = tcx.type_of(did).kind()
+                        && def.is_enum()
+                        && let Some(variant) = def.variants().iter().find(|v| v.name == res.name(tcx)) {
+                            // ctor is `None` if variant is a struct
+                            variant.ctor.is_none()
+                        } else {
+                            false
+                        }
+                    };
+                    let path_description = if let Some((kind, did)) = kind_did {
                         match kind {
                             Mod | ForeignMod => "inner item",
                             Struct => "field or associated item",
                             Enum | Union => "variant or associated item",
+                            Variant if is_struct_variant(did) => {
+                                let variant = res.name(tcx);
+                                let note = format!("variant `{variant}` has no such field");
+                                if let Some(span) = sp {
+                                    diag.span_label(span, &note);
+                                } else {
+                                    diag.note(&note);
+                                }
+                                return;
+                            }
                             Variant
                             | Field
                             | Closure
diff --git a/tests/rustdoc-ui/intra-doc/errors.rs b/tests/rustdoc-ui/intra-doc/errors.rs
index b29f7c29b5d..95dd2b98e03 100644
--- a/tests/rustdoc-ui/intra-doc/errors.rs
+++ b/tests/rustdoc-ui/intra-doc/errors.rs
@@ -103,3 +103,19 @@ pub trait T {
 macro_rules! m {
     () => {};
 }
+
+///[`TestEnum::Variant1::field_name`]
+//~^ ERROR unresolved link
+//~| NOTE variant `Variant1` has no such field
+pub enum TestEnum {
+    Variant1 {},
+    Variant2 { field_name: u64 },
+}
+
+///[`TestEnumNoFields::Variant1::field_name`]
+//~^ ERROR unresolved link
+//~| NOTE `Variant1` is a variant, not a module or type, and cannot have associated items
+pub enum TestEnumNoFields {
+    Variant1 (),
+    Variant2 {},
+}
diff --git a/tests/rustdoc-ui/intra-doc/errors.stderr b/tests/rustdoc-ui/intra-doc/errors.stderr
index 9a1896fb0cd..1b2416d7da7 100644
--- a/tests/rustdoc-ui/intra-doc/errors.stderr
+++ b/tests/rustdoc-ui/intra-doc/errors.stderr
@@ -142,6 +142,18 @@ error: unresolved link to `T::h`
 LL | /// [T::h!]
    |      ^^^^^ the trait `T` has no macro named `h`
 
+error: unresolved link to `TestEnum::Variant1::field_name`
+  --> $DIR/errors.rs:107:6
+   |
+LL | ///[`TestEnum::Variant1::field_name`]
+   |      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ variant `Variant1` has no such field
+
+error: unresolved link to `TestEnumNoFields::Variant1::field_name`
+  --> $DIR/errors.rs:115:6
+   |
+LL | ///[`TestEnumNoFields::Variant1::field_name`]
+   |      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Variant1` is a variant, not a module or type, and cannot have associated items
+
 error: unresolved link to `m`
   --> $DIR/errors.rs:98:6
    |
@@ -153,5 +165,5 @@ help: to link to the macro, add an exclamation mark
 LL | /// [m!()]
    |       +
 
-error: aborting due to 20 previous errors
+error: aborting due to 22 previous errors