From 64c11b445c1b8589a2a32ae4508ec34e25b8a642 Mon Sep 17 00:00:00 2001 From: Usman Akinyemi Date: Sun, 5 Apr 2026 03:36:20 +0530 Subject: [PATCH] resolve: use `#[doc(alias)]` to improve function resolution diagnostics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parse `#[doc(alias)]` attributes for local functions and store them in the resolver. Use these aliases during name resolution diagnostics to suggest the original function when an alias is used in its place. This enables cases like calling `bar()` to suggest `foo()` when `foo` is annotated with `#[doc(alias = "bar")]`. Includes UI tests for function and method cases(which has already being supported). Helped-by: Esteban Küber Signed-off-by: Usman Akinyemi --- .../rustc_resolve/src/build_reduced_graph.rs | 52 +++++++++++++++++ .../rustc_resolve/src/late/diagnostics.rs | 13 +++-- compiler/rustc_resolve/src/lib.rs | 3 + tests/ui/attributes/doc-alias-attr.rs | 33 +++++++++++ tests/ui/attributes/doc-alias-attr.stderr | 58 +++++++++++++++++++ 5 files changed, 154 insertions(+), 5 deletions(-) create mode 100644 tests/ui/attributes/doc-alias-attr.rs create mode 100644 tests/ui/attributes/doc-alias-attr.stderr diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 29b5cdb2c8999..5513b19881035 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -14,6 +14,7 @@ use rustc_ast::{ }; use rustc_attr_parsing as attr; use rustc_attr_parsing::AttributeParser; +use rustc_data_structures::fx::FxHashSet; use rustc_expand::base::ResolverExpand; use rustc_expand::expand::AstFragment; use rustc_hir::Attribute; @@ -569,6 +570,52 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { } } + /// Extracts `#[doc(alias = "...")]` and `#[doc(alias(...))]` from attributes. + /// + /// Uses a lightweight, ad-hoc approach instead of the full attribute parsing + /// machinery to collect aliases for later use in diagnostics. + fn parse_doc_aliases(&self, attrs: &[ast::Attribute]) -> FxHashSet { + let mut aliases = FxHashSet::default(); + + for attr in attrs { + if !attr.has_name(sym::doc) { + continue; + } + + // Get #[doc(...)] list + let Some(items) = attr.meta_item_list() else { + continue; + }; + + for item in items { + let Some(meta) = item.meta_item() else { + continue; + }; + + if !meta.has_name(sym::alias) { + continue; + } + + // Case 1: #[doc(alias = "foo")] + if let Some(value) = meta.value_str() { + aliases.insert(value); + continue; + } + + // Case 2: #[doc(alias("foo", "bar"))] + if let Some(nested) = meta.meta_item_list() { + for nested_item in nested { + if let Some(lit) = nested_item.lit() { + aliases.insert(lit.symbol); + } + } + } + } + } + + aliases + } + fn build_reduced_graph_for_use_tree( &mut self, // This particular use tree @@ -875,6 +922,11 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { ItemKind::Fn(box Fn { ident, .. }) => { self.r.define_local(parent, ident, ValueNS, res, vis, sp, expansion); + if !item.attrs.is_empty() { + // for better error reporting in doc alias for function + let aliases = self.parse_doc_aliases(&item.attrs); + self.r.doc_aliases.insert(local_def_id, aliases); + } // Functions introducing procedural macros reserve a slot // in the macro namespace as well (see #52225). self.define_macro(item); diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 7539e3c4f499f..ae8481731f0f3 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1112,11 +1112,14 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { else { continue; }; - if did.is_local() { - // We don't record the doc alias name in the local crate - // because the people who write doc alias are usually not - // confused by them. - continue; + if let Some(local) = did.as_local() { + if let Some(aliases) = r.doc_aliases.get(&local) + && aliases.contains(&item_name) + { + return Some(did); + } else { + continue; + } } if let Some(d) = hir::find_attr!(r.tcx, did, Doc(d) => d) && d.aliases.contains_key(&item_name) diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index d75f2981a7724..e77919cd6acf0 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1404,6 +1404,9 @@ pub struct Resolver<'ra, 'tcx> { mods_with_parse_errors: FxHashSet = default::fx_hash_set(), + // for better error reporting in doc alias for function + doc_aliases: FxHashMap> = default::fx_hash_map(), + /// Whether `Resolver::register_macros_for_all_crates` has been called once already, as we /// don't need to run it more than once. all_crate_macros_already_registered: bool = false, diff --git a/tests/ui/attributes/doc-alias-attr.rs b/tests/ui/attributes/doc-alias-attr.rs new file mode 100644 index 0000000000000..76105073876de --- /dev/null +++ b/tests/ui/attributes/doc-alias-attr.rs @@ -0,0 +1,33 @@ +#[doc(alias = "bar")] +fn foo() {} + +#[doc(alias("sum", "plus"))] +fn net() {} + +struct S; + +impl S { + #[doc(alias("bar"))] + fn foo() {} + + #[doc(alias= "baz")] + fn qux(&self, x: i32) {} +} + + +fn main() { + S::bar(); + //~^ ERROR no associated function or constant named `bar` found for struct `S` in the current scope + //~| HELP there is an associated function `foo` with a similar name + + let s = S; + s.baz(10); + //~^ ERROR no method named `baz` + //~| HELP there is a method `qux` with a similar name + + sum(); //~ ERROR: cannot find function `sum` in this scope + //~| HELP: `net` has a name defined in the doc alias attribute as `sum` + + bar(); //~ ERROR: cannot find function `bar` in this scope + //~| HELP: `foo` has a name defined in the doc alias attribute as `bar` +} diff --git a/tests/ui/attributes/doc-alias-attr.stderr b/tests/ui/attributes/doc-alias-attr.stderr new file mode 100644 index 0000000000000..8576864e3a12f --- /dev/null +++ b/tests/ui/attributes/doc-alias-attr.stderr @@ -0,0 +1,58 @@ +error[E0599]: no associated function or constant named `bar` found for struct `S` in the current scope + --> $DIR/doc-alias-attr.rs:19:8 + | +LL | struct S; + | -------- associated function or constant `bar` not found for this struct +... +LL | S::bar(); + | ^^^ associated function or constant not found in `S` + | +help: there is an associated function `foo` with a similar name + | +LL - S::bar(); +LL + S::foo(); + | + +error[E0599]: no method named `baz` found for struct `S` in the current scope + --> $DIR/doc-alias-attr.rs:24:7 + | +LL | struct S; + | -------- method `baz` not found for this struct +... +LL | s.baz(10); + | ^^^ + | +help: there is a method `qux` with a similar name + | +LL - s.baz(10); +LL + s.qux(10); + | + +error[E0425]: cannot find function `sum` in this scope + --> $DIR/doc-alias-attr.rs:28:5 + | +LL | sum(); + | ^^^ + | +help: `net` has a name defined in the doc alias attribute as `sum` + | +LL - sum(); +LL + net(); + | + +error[E0425]: cannot find function `bar` in this scope + --> $DIR/doc-alias-attr.rs:31:5 + | +LL | bar(); + | ^^^ + | +help: `foo` has a name defined in the doc alias attribute as `bar` + | +LL - bar(); +LL + foo(); + | + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0425, E0599. +For more information about an error, try `rustc --explain E0425`.