From 762307dce8b5e836c1b52780d27881c5978b84ab Mon Sep 17 00:00:00 2001 From: bohan Date: Sun, 14 Jul 2024 23:05:14 +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 34c24a26f7b13..a38c7cbe69706 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -1239,3 +1239,29 @@ pub fn parse_confusables(attr: &Attribute) -> Option> { return 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 6a7af5510e074..134bb48b9153e 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -4,6 +4,7 @@ use super::MethodError; use super::NoMatchData; use crate::FnCtxt; +use rustc_attr::collect_doc_alias_symbol_from_attrs; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir as hir; @@ -1827,9 +1828,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.as_str() == name.as_str()) + { + 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; }; @@ -1841,35 +1849,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() - && name.as_str() == lit.symbol.as_str() - { - return true; - } - } - } else if let Some(meta) = v.meta_item() - && let Some(lit) = meta.name_value_literal() - && name.as_str() == lit.symbol.as_str() - { - // #[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 941fb6436df92..1629e88cab9a5 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -4,9 +4,10 @@ use crate::diagnostics::{ImportSuggestion, LabelSuggestion, TypoSuggestion}; use crate::late::{AliasPossibility, LateResolutionVisitor, RibKind}; use crate::late::{LifetimeBinderKind, LifetimeRes, LifetimeRibKind, LifetimeUseSet}; use crate::ty::fast_reject::SimplifiedType; -use crate::{errors, path_names_to_string}; +use crate::{errors, path_names_to_string, Resolver}; use crate::{Module, ModuleKind, ModuleOrUniformRoot}; use crate::{PathResult, PathSource, Segment}; +use rustc_attr::collect_doc_alias_symbol_from_attrs; use rustc_hir::def::Namespace::{self, *}; use rustc_ast::ptr::P; @@ -452,6 +453,18 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, '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, @@ -776,6 +789,50 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { return (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<'a, '_>, m: Module<'a>| { + 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`.