Rollup merge of #150883 - improve-deprecated-intra-doc-span, r=camelid

Improve span for "unresolved intra doc link" on `deprecated` attribute

Follow-up of rust-lang/rust#150721.

To make this work, I replaced the `Symbol` by an `Ident` to keep the `Span` information.

cc @folkertdev
r? @camelid
This commit is contained in:
Jonathan Brouwer 2026-01-13 09:01:30 +01:00 committed by GitHub
commit dc6afd74dd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 42 additions and 29 deletions

View file

@ -235,14 +235,14 @@ impl AttributeExt for Attribute {
}
}
fn deprecation_note(&self) -> Option<Symbol> {
fn deprecation_note(&self) -> Option<Ident> {
match &self.kind {
AttrKind::Normal(normal) if normal.item.path == sym::deprecated => {
let meta = &normal.item;
// #[deprecated = "..."]
if let Some(s) = meta.value_str() {
return Some(s);
return Some(Ident { name: s, span: meta.span() });
}
// #[deprecated(note = "...")]
@ -252,7 +252,7 @@ impl AttributeExt for Attribute {
&& mi.path == sym::note
&& let Some(s) = mi.value_str()
{
return Some(s);
return Some(Ident { name: s, span: mi.span });
}
}
}
@ -905,7 +905,7 @@ pub trait AttributeExt: Debug {
/// Returns the deprecation note if this is deprecation attribute.
/// * `#[deprecated = "note"]` returns `Some("note")`.
/// * `#[deprecated(note = "note", ...)]` returns `Some("note")`.
fn deprecation_note(&self) -> Option<Symbol>;
fn deprecation_note(&self) -> Option<Ident>;
fn is_proc_macro_attr(&self) -> bool {
[sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]

View file

@ -13,14 +13,14 @@ fn get<S: Stage>(
name: Symbol,
param_span: Span,
arg: &ArgParser,
item: &Option<Symbol>,
) -> Option<Symbol> {
item: Option<Symbol>,
) -> Option<Ident> {
if item.is_some() {
cx.duplicate_key(param_span, name);
return None;
}
if let Some(v) = arg.name_value() {
if let Some(value_str) = v.value_as_str() {
if let Some(value_str) = v.value_as_ident() {
Some(value_str)
} else {
cx.expected_string_literal(v.value_span, Some(&v.value_as_lit()));
@ -72,7 +72,7 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
let features = cx.features();
let mut since = None;
let mut note = None;
let mut note: Option<Ident> = None;
let mut suggestion = None;
let is_rustc = features.staged_api();
@ -92,10 +92,16 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
match ident_name {
Some(name @ sym::since) => {
since = Some(get(cx, name, param.span(), param.args(), &since)?);
since = Some(get(cx, name, param.span(), param.args(), since)?.name);
}
Some(name @ sym::note) => {
note = Some(get(cx, name, param.span(), param.args(), &note)?);
note = Some(get(
cx,
name,
param.span(),
param.args(),
note.map(|ident| ident.name),
)?);
}
Some(name @ sym::suggestion) => {
if !features.deprecated_suggestion() {
@ -107,7 +113,7 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
}
suggestion =
Some(get(cx, name, param.span(), param.args(), &suggestion)?);
Some(get(cx, name, param.span(), param.args(), suggestion)?.name);
}
_ => {
cx.expected_specific_argument(
@ -124,7 +130,7 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
}
}
ArgParser::NameValue(v) => {
let Some(value) = v.value_as_str() else {
let Some(value) = v.value_as_ident() else {
cx.expected_string_literal(v.value_span, Some(v.value_as_lit()));
return None;
};

View file

@ -322,6 +322,13 @@ impl NameValueParser {
self.value_as_lit().kind.str()
}
/// If the value is a string literal, it will return its value associated with its span (an
/// `Ident` in short).
pub fn value_as_ident(&self) -> Option<Ident> {
let meta_item = self.value_as_lit();
meta_item.kind.str().map(|name| Ident { name, span: meta_item.span })
}
pub fn args_span(&self) -> Span {
self.eq_span.to(self.value_span)
}

View file

@ -136,7 +136,7 @@ pub enum IntType {
pub struct Deprecation {
pub since: DeprecatedSince,
/// The note to issue a reason.
pub note: Option<Symbol>,
pub note: Option<Ident>,
/// A text snippet used to completely replace any use of the deprecated item in an expression.
///
/// This is currently unstable.

View file

@ -1410,7 +1410,7 @@ impl AttributeExt for Attribute {
}
#[inline]
fn deprecation_note(&self) -> Option<Symbol> {
fn deprecation_note(&self) -> Option<Ident> {
match &self {
Attribute::Parsed(AttributeKind::Deprecation { deprecation, .. }) => deprecation.note,
_ => None,

View file

@ -185,7 +185,7 @@ pub fn early_report_macro_deprecation(
let diag = BuiltinLintDiag::DeprecatedMacro {
suggestion: depr.suggestion,
suggestion_span: span,
note: depr.note,
note: depr.note.map(|ident| ident.name),
path,
since_kind: deprecated_since_kind(is_in_effect, depr.since),
};
@ -228,7 +228,7 @@ fn late_report_deprecation(
}),
kind: def_kind.to_owned(),
path: def_path,
note: depr.note,
note: depr.note.map(|ident| ident.name),
since_kind: deprecated_since_kind(is_in_effect, depr.since),
};
tcx.emit_node_span_lint(lint, hir_id, method_span, diag);

View file

@ -26,7 +26,7 @@ use rustc_resolve::rustdoc::{
use rustc_session::Session;
use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{Symbol, kw, sym};
use rustc_span::{DUMMY_SP, FileName, Loc, RemapPathScopeComponents};
use rustc_span::{DUMMY_SP, FileName, Ident, Loc, RemapPathScopeComponents};
use tracing::{debug, trace};
use {rustc_ast as ast, rustc_hir as hir};
@ -418,7 +418,7 @@ impl Item {
{
Some(Deprecation {
since: DeprecatedSince::Unspecified,
note: Some(note),
note: Some(Ident { name: note, span: DUMMY_SP }),
suggestion: None,
})
} else {
@ -455,7 +455,7 @@ impl Item {
.attrs
.other_attrs
.iter()
.filter_map(|attr| attr.deprecation_note().map(|_| attr.span()));
.filter_map(|attr| attr.deprecation_note().map(|note| note.span));
span_of_fragments(&self.attrs.doc_strings)
.into_iter()

View file

@ -1,8 +1,8 @@
error: unresolved link to `TypeAlias::hoge`
--> $DIR/deprecated.rs:3:1
--> $DIR/deprecated.rs:3:16
|
LL | #[deprecated = "[broken cross-reference](TypeAlias::hoge)"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: the link appears in this line:
@ -16,10 +16,10 @@ LL | #![deny(rustdoc::broken_intra_doc_links)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: unresolved link to `TypeAlias::hoge`
--> $DIR/deprecated.rs:6:1
--> $DIR/deprecated.rs:6:38
|
LL | #[deprecated(since = "0.0.0", note = "[broken cross-reference](TypeAlias::hoge)")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: the link appears in this line:
@ -28,10 +28,10 @@ LL | #[deprecated(since = "0.0.0", note = "[broken cross-reference](TypeAlias::h
= note: no item named `TypeAlias` in scope
error: unresolved link to `TypeAlias::hoge`
--> $DIR/deprecated.rs:9:1
--> $DIR/deprecated.rs:9:21
|
LL | #[deprecated(note = "[broken cross-reference](TypeAlias::hoge)", since = "0.0.0")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: the link appears in this line:

View file

@ -10,19 +10,19 @@ use ::std::prelude::rust_2015::*;
struct PlainDeprecated;
#[attr = Deprecation {deprecation: Deprecation {since: Unspecified,
note: "here's why this is deprecated"}}]
note: here's why this is deprecated#0}}]
struct DirectNote;
#[attr = Deprecation {deprecation: Deprecation {since: Unspecified,
note: "here's why this is deprecated"}}]
note: here's why this is deprecated#0}}]
struct ExplicitNote;
#[attr = Deprecation {deprecation: Deprecation {since: NonStandard("1.2.3"),
note: "here's why this is deprecated"}}]
note: here's why this is deprecated#0}}]
struct SinceAndNote;
#[attr = Deprecation {deprecation: Deprecation {since: NonStandard("1.2.3"),
note: "here's why this is deprecated"}}]
note: here's why this is deprecated#0}}]
struct FlippedOrder;
fn f() {