Skip to content

Commit

Permalink
Rollup merge of #107912 - clubby789:doc-bad-enum-field, r=camelid,Gui…
Browse files Browse the repository at this point in the history
…llaumeGomez

rustdoc: Don't resolve link to field on different variant

Fix #107903

This also gives a more specific diagnostic when the enum has any fields
  • Loading branch information
matthiaskrgr authored Feb 11, 2023
2 parents f95f68e + ef8de38 commit c8614a7
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 5 deletions.
29 changes: 25 additions & 4 deletions src/librustdoc/passes/collect_intra_doc_links.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,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 {
Expand Down Expand Up @@ -1716,15 +1717,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
Expand Down
16 changes: 16 additions & 0 deletions tests/rustdoc-ui/intra-doc/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {},
}
14 changes: 13 additions & 1 deletion tests/rustdoc-ui/intra-doc/errors.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
|
Expand All @@ -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

0 comments on commit c8614a7

Please sign in to comment.