From c5f9423041a11659dcef853964ce845d70921c7f Mon Sep 17 00:00:00 2001 From: bohan Date: Mon, 14 Oct 2024 00:36:33 +0800 Subject: [PATCH] collect doc alias as tips during resolution --- compiler/rustc_attr/src/builtin.rs | 26 ++++++ compiler/rustc_hir_typeck/src/method/probe.rs | 40 +++------ .../rustc_resolve/src/late/diagnostics.rs | 59 ++++++++++++- .../auxiliary/use-doc-alias-name-extern.rs | 14 +++ tests/ui/attributes/use-doc-alias-name.rs | 36 ++++++++ tests/ui/attributes/use-doc-alias-name.stderr | 86 +++++++++++++++++++ 6 files changed, 231 insertions(+), 30 deletions(-) create mode 100644 tests/ui/attributes/auxiliary/use-doc-alias-name-extern.rs create mode 100644 tests/ui/attributes/use-doc-alias-name.rs create mode 100644 tests/ui/attributes/use-doc-alias-name.stderr diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index c1db4d07dfc86..bd94e79969744 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -1285,3 +1285,29 @@ pub fn parse_confusables(attr: &Attribute) -> Option> { Some(candidates) } + +pub fn collect_doc_alias_symbol_from_attrs<'tcx>( + attrs: impl Iterator, +) -> Vec { + let doc_attrs = attrs.filter(|attr| attr.name_or_empty() == sym::doc); + let mut symbols = vec![]; + for attr in doc_attrs { + let Some(values) = attr.meta_item_list() else { + continue; + }; + let alias_values = values.iter().filter(|v| v.name_or_empty() == sym::alias); + for v in alias_values { + if let Some(nested) = v.meta_item_list() { + // #[doc(alias("foo", "bar"))] + let iter = nested.iter().filter_map(|item| item.lit()).map(|item| item.symbol); + symbols.extend(iter); + } else if let Some(meta) = v.meta_item() + && let Some(lit) = meta.name_value_literal() + { + // #[doc(alias = "foo")] + symbols.push(lit.symbol); + } + } + } + symbols +} diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index ba6bfd3a5e940..28680ffaf7159 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -3,6 +3,7 @@ use std::cmp::max; use std::iter; use std::ops::Deref; +use rustc_attr::collect_doc_alias_symbol_from_attrs; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir as hir; @@ -1921,9 +1922,16 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { }; let hir_id = self.fcx.tcx.local_def_id_to_hir_id(local_def_id); let attrs = self.fcx.tcx.hir().attrs(hir_id); + + if collect_doc_alias_symbol_from_attrs(attrs.into_iter()) + .iter() + .any(|alias| *alias == method.name) + { + return true; + } + for attr in attrs { - if sym::doc == attr.name_or_empty() { - } else if sym::rustc_confusables == attr.name_or_empty() { + if sym::rustc_confusables == attr.name_or_empty() { let Some(confusables) = attr.meta_item_list() else { continue; }; @@ -1935,35 +1943,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { return true; } } - continue; - } else { - continue; - }; - let Some(values) = attr.meta_item_list() else { - continue; - }; - for v in values { - if v.name_or_empty() != sym::alias { - continue; - } - if let Some(nested) = v.meta_item_list() { - // #[doc(alias("foo", "bar"))] - for n in nested { - if let Some(lit) = n.lit() - && method.name == lit.symbol - { - return true; - } - } - } else if let Some(meta) = v.meta_item() - && let Some(lit) = meta.name_value_literal() - && method.name == lit.symbol - { - // #[doc(alias = "foo")] - return true; - } } } + false } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index fce5ec36c661b..bef9cd6f8f2a0 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -11,6 +11,7 @@ use rustc_ast::{ Item, ItemKind, MethodCall, NodeId, Path, Ty, TyKind, }; use rustc_ast_pretty::pprust::where_bound_predicate_to_string; +use rustc_attr::collect_doc_alias_symbol_from_attrs; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{ @@ -40,7 +41,7 @@ use crate::late::{ }; use crate::ty::fast_reject::SimplifiedType; use crate::{ - Module, ModuleKind, ModuleOrUniformRoot, PathResult, PathSource, Segment, errors, + Module, ModuleKind, ModuleOrUniformRoot, PathResult, PathSource, Resolver, Segment, errors, path_names_to_string, }; @@ -459,6 +460,18 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { return (err, Vec::new()); } + if let Some(did) = self.lookup_doc_alias_name(path, source.namespace()) { + err.span_label( + self.r.def_span(did), + format!( + "`{}` has a name defined in the doc alias attribute as `{}`", + self.r.tcx.item_name(did), + path.last().unwrap().ident.as_str() + ), + ); + return (err, Vec::new()); + }; + let (found, mut candidates) = self.try_lookup_name_relaxed( &mut err, source, @@ -783,6 +796,50 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { (false, candidates) } + fn lookup_doc_alias_name(&mut self, path: &[Segment], ns: Namespace) -> Option { + let item_str = path.last().unwrap().ident; + + let find_doc_alias_name = |r: &mut Resolver<'ra, '_>, m: Module<'ra>| { + for resolution in r.resolutions(m).borrow().values() { + let Some(did) = + resolution.borrow().binding.and_then(|binding| binding.res().opt_def_id()) + 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; + } + let symbols = collect_doc_alias_symbol_from_attrs(r.tcx.get_attrs(did, sym::doc)); + if symbols.contains(&item_str.name) { + return Some(did); + } + } + None + }; + + if path.len() == 1 { + for rib in self.ribs[ns].iter().rev() { + if let RibKind::Module(module) = rib.kind + && let Some(did) = find_doc_alias_name(self.r, module) + { + return Some(did); + } + } + } else { + let mod_path = &path[..path.len() - 1]; + if let PathResult::Module(ModuleOrUniformRoot::Module(module)) = + self.resolve_path(mod_path, Some(TypeNS), None) + && let Some(did) = find_doc_alias_name(self.r, module) + { + return Some(did); + } + } + None + } + fn suggest_trait_and_bounds( &mut self, err: &mut Diag<'_>, diff --git a/tests/ui/attributes/auxiliary/use-doc-alias-name-extern.rs b/tests/ui/attributes/auxiliary/use-doc-alias-name-extern.rs new file mode 100644 index 0000000000000..0af4b3ed09c9b --- /dev/null +++ b/tests/ui/attributes/auxiliary/use-doc-alias-name-extern.rs @@ -0,0 +1,14 @@ +#[doc(alias="DocAliasS1")] +pub struct S1; + +#[doc(alias="DocAliasS2")] +#[doc(alias("DocAliasS3", "DocAliasS4"))] +pub struct S2; + +#[doc(alias("doc_alias_f1", "doc_alias_f2"))] +pub fn f() {} + +pub mod m { + #[doc(alias="DocAliasS5")] + pub struct S5; +} diff --git a/tests/ui/attributes/use-doc-alias-name.rs b/tests/ui/attributes/use-doc-alias-name.rs new file mode 100644 index 0000000000000..dce093acf5209 --- /dev/null +++ b/tests/ui/attributes/use-doc-alias-name.rs @@ -0,0 +1,36 @@ +//@ aux-build: use-doc-alias-name-extern.rs +//@ error-pattern: `S1` has a name defined in the doc alias attribute as `DocAliasS1` +//@ error-pattern: `S2` has a name defined in the doc alias attribute as `DocAliasS2` +//@ error-pattern: `S2` has a name defined in the doc alias attribute as `DocAliasS3` +//@ error-pattern: `S2` has a name defined in the doc alias attribute as `DocAliasS4` +//@ error-pattern: `f` has a name defined in the doc alias attribute as `doc_alias_f1` +//@ error-pattern: `f` has a name defined in the doc alias attribute as `doc_alias_f2` +//@ error-pattern: `S5` has a name defined in the doc alias attribute as `DocAliasS5` + +// issue#124273 + +extern crate use_doc_alias_name_extern; + +use use_doc_alias_name_extern::*; + +#[doc(alias="LocalDocAliasS")] +struct S; + +fn main() { + LocalDocAliasS; + //~^ ERROR: cannot find value `LocalDocAliasS` in this scope + DocAliasS1; + //~^ ERROR: cannot find value `DocAliasS1` in this scope + DocAliasS2; + //~^ ERROR: cannot find value `DocAliasS2` in this scope + DocAliasS3; + //~^ ERROR: cannot find value `DocAliasS3` in this scope + DocAliasS4; + //~^ ERROR: cannot find value `DocAliasS4` in this scope + doc_alias_f1(); + //~^ ERROR: cannot find function `doc_alias_f1` in this scope + doc_alias_f2(); + //~^ ERROR: cannot find function `doc_alias_f2` in this scope + m::DocAliasS5; + //~^ ERROR: cannot find value `DocAliasS5` in module `m` +} diff --git a/tests/ui/attributes/use-doc-alias-name.stderr b/tests/ui/attributes/use-doc-alias-name.stderr new file mode 100644 index 0000000000000..fc1cb431991d9 --- /dev/null +++ b/tests/ui/attributes/use-doc-alias-name.stderr @@ -0,0 +1,86 @@ +error[E0425]: cannot find value `LocalDocAliasS` in this scope + --> $DIR/use-doc-alias-name.rs:20:5 + | +LL | LocalDocAliasS; + | ^^^^^^^^^^^^^^ not found in this scope + +error[E0425]: cannot find value `DocAliasS1` in this scope + --> $DIR/use-doc-alias-name.rs:22:5 + | +LL | DocAliasS1; + | ^^^^^^^^^^ + | + ::: $DIR/auxiliary/use-doc-alias-name-extern.rs:2:1 + | +LL | pub struct S1; + | ------------- `S1` has a name defined in the doc alias attribute as `DocAliasS1` + +error[E0425]: cannot find value `DocAliasS2` in this scope + --> $DIR/use-doc-alias-name.rs:24:5 + | +LL | DocAliasS2; + | ^^^^^^^^^^ + | + ::: $DIR/auxiliary/use-doc-alias-name-extern.rs:6:1 + | +LL | pub struct S2; + | ------------- `S2` has a name defined in the doc alias attribute as `DocAliasS2` + +error[E0425]: cannot find value `DocAliasS3` in this scope + --> $DIR/use-doc-alias-name.rs:26:5 + | +LL | DocAliasS3; + | ^^^^^^^^^^ + | + ::: $DIR/auxiliary/use-doc-alias-name-extern.rs:6:1 + | +LL | pub struct S2; + | ------------- `S2` has a name defined in the doc alias attribute as `DocAliasS3` + +error[E0425]: cannot find value `DocAliasS4` in this scope + --> $DIR/use-doc-alias-name.rs:28:5 + | +LL | DocAliasS4; + | ^^^^^^^^^^ + | + ::: $DIR/auxiliary/use-doc-alias-name-extern.rs:6:1 + | +LL | pub struct S2; + | ------------- `S2` has a name defined in the doc alias attribute as `DocAliasS4` + +error[E0425]: cannot find value `DocAliasS5` in module `m` + --> $DIR/use-doc-alias-name.rs:34:8 + | +LL | m::DocAliasS5; + | ^^^^^^^^^^ + | + ::: $DIR/auxiliary/use-doc-alias-name-extern.rs:13:5 + | +LL | pub struct S5; + | ------------- `S5` has a name defined in the doc alias attribute as `DocAliasS5` + +error[E0425]: cannot find function `doc_alias_f1` in this scope + --> $DIR/use-doc-alias-name.rs:30:5 + | +LL | doc_alias_f1(); + | ^^^^^^^^^^^^ + | + ::: $DIR/auxiliary/use-doc-alias-name-extern.rs:9:1 + | +LL | pub fn f() {} + | ---------- `f` has a name defined in the doc alias attribute as `doc_alias_f1` + +error[E0425]: cannot find function `doc_alias_f2` in this scope + --> $DIR/use-doc-alias-name.rs:32:5 + | +LL | doc_alias_f2(); + | ^^^^^^^^^^^^ + | + ::: $DIR/auxiliary/use-doc-alias-name-extern.rs:9:1 + | +LL | pub fn f() {} + | ---------- `f` has a name defined in the doc alias attribute as `doc_alias_f2` + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0425`.