Skip to content

Commit d59e6fa

Browse files
committed
collect doc alias as tips during resolution
1 parent a241cf1 commit d59e6fa

File tree

9 files changed

+237
-32
lines changed

9 files changed

+237
-32
lines changed

compiler/rustc_attr/src/builtin.rs

+26
Original file line numberDiff line numberDiff line change
@@ -1239,3 +1239,29 @@ pub fn parse_confusables(attr: &Attribute) -> Option<Vec<Symbol>> {
12391239

12401240
return Some(candidates);
12411241
}
1242+
1243+
pub fn collect_doc_alias_symbol_from_attrs<'tcx>(
1244+
attrs: impl Iterator<Item = &'tcx ast::Attribute>,
1245+
) -> Vec<Symbol> {
1246+
let doc_attrs = attrs.filter(|attr| attr.name_or_empty() == sym::doc);
1247+
let mut symbols = vec![];
1248+
for attr in doc_attrs {
1249+
let Some(values) = attr.meta_item_list() else {
1250+
continue;
1251+
};
1252+
let alias_values = values.iter().filter(|v| v.name_or_empty() == sym::alias);
1253+
for v in alias_values {
1254+
if let Some(nested) = v.meta_item_list() {
1255+
// #[doc(alias("foo", "bar"))]
1256+
let iter = nested.iter().filter_map(|item| item.lit()).map(|item| item.symbol);
1257+
symbols.extend(iter);
1258+
} else if let Some(meta) = v.meta_item()
1259+
&& let Some(lit) = meta.name_value_literal()
1260+
{
1261+
// #[doc(alias = "foo")]
1262+
symbols.push(lit.symbol);
1263+
}
1264+
}
1265+
}
1266+
symbols
1267+
}

compiler/rustc_expand/src/base.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1062,6 +1062,7 @@ pub trait ResolverExpand {
10621062
fn declare_proc_macro(&mut self, id: NodeId);
10631063

10641064
fn append_stripped_cfg_item(&mut self, parent_node: NodeId, name: Ident, cfg: ast::MetaItem);
1065+
fn append_confusable_attr_symbols(&mut self, node_id: NodeId, symbols: Vec<Symbol>);
10651066

10661067
/// Tools registered with `#![register_tool]` and used by tool attributes and lints.
10671068
fn registered_tools(&self) -> &RegisteredTools;

compiler/rustc_expand/src/expand.rs

+23-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use rustc_ast::{ForeignItemKind, HasAttrs, HasNodeId};
2020
use rustc_ast::{Inline, ItemKind, MacStmtStyle, MetaItemKind, ModKind};
2121
use rustc_ast::{NestedMetaItem, NodeId, PatKind, StmtKind, TyKind};
2222
use rustc_ast_pretty::pprust;
23+
use rustc_attr::collect_doc_alias_symbol_from_attrs;
2324
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
2425
use rustc_data_structures::sync::Lrc;
2526
use rustc_errors::PResult;
@@ -2021,7 +2022,17 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
20212022
}
20222023
None => {
20232024
match Node::wrap_flat_map_node_noop_flat_map(node, self, |mut node, this| {
2024-
assign_id!(this, node.node_id_mut(), || node.noop_flat_map(this))
2025+
assign_id!(this, node.node_id_mut(), || {
2026+
let symbols = collect_doc_alias_symbol_from_attrs(node.attrs().iter());
2027+
if !symbols.is_empty() {
2028+
this.cx.resolver.append_confusable_attr_symbols(
2029+
this.cx.current_expansion.lint_node_id,
2030+
symbols,
2031+
);
2032+
}
2033+
2034+
node.noop_flat_map(this)
2035+
})
20252036
}) {
20262037
Ok(output) => output,
20272038
Err(returned_node) => {
@@ -2069,7 +2080,17 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
20692080
}
20702081
None if node.delegation().is_some() => unreachable!(),
20712082
None => {
2072-
assign_id!(self, node.node_id_mut(), || node.noop_visit(self))
2083+
assign_id!(self, node.node_id_mut(), || {
2084+
let symbols = collect_doc_alias_symbol_from_attrs(node.attrs().iter());
2085+
if !symbols.is_empty() {
2086+
self.cx.resolver.append_confusable_attr_symbols(
2087+
self.cx.current_expansion.lint_node_id,
2088+
symbols,
2089+
);
2090+
}
2091+
2092+
node.noop_visit(self)
2093+
})
20732094
}
20742095
};
20752096
}

compiler/rustc_hir_typeck/src/method/probe.rs

+11-29
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use super::MethodError;
44
use super::NoMatchData;
55

66
use crate::FnCtxt;
7+
use rustc_attr::collect_doc_alias_symbol_from_attrs;
78
use rustc_data_structures::fx::FxHashSet;
89
use rustc_errors::Applicability;
910
use rustc_hir as hir;
@@ -1827,9 +1828,16 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
18271828
};
18281829
let hir_id = self.fcx.tcx.local_def_id_to_hir_id(local_def_id);
18291830
let attrs = self.fcx.tcx.hir().attrs(hir_id);
1831+
1832+
if collect_doc_alias_symbol_from_attrs(attrs.into_iter())
1833+
.iter()
1834+
.any(|alias| alias.as_str() == name.as_str())
1835+
{
1836+
return true;
1837+
}
1838+
18301839
for attr in attrs {
1831-
if sym::doc == attr.name_or_empty() {
1832-
} else if sym::rustc_confusables == attr.name_or_empty() {
1840+
if sym::rustc_confusables == attr.name_or_empty() {
18331841
let Some(confusables) = attr.meta_item_list() else {
18341842
continue;
18351843
};
@@ -1841,35 +1849,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
18411849
return true;
18421850
}
18431851
}
1844-
continue;
1845-
} else {
1846-
continue;
1847-
};
1848-
let Some(values) = attr.meta_item_list() else {
1849-
continue;
1850-
};
1851-
for v in values {
1852-
if v.name_or_empty() != sym::alias {
1853-
continue;
1854-
}
1855-
if let Some(nested) = v.meta_item_list() {
1856-
// #[doc(alias("foo", "bar"))]
1857-
for n in nested {
1858-
if let Some(lit) = n.lit()
1859-
&& name.as_str() == lit.symbol.as_str()
1860-
{
1861-
return true;
1862-
}
1863-
}
1864-
} else if let Some(meta) = v.meta_item()
1865-
&& let Some(lit) = meta.name_value_literal()
1866-
&& name.as_str() == lit.symbol.as_str()
1867-
{
1868-
// #[doc(alias = "foo")]
1869-
return true;
1870-
}
18711852
}
18721853
}
1854+
18731855
false
18741856
}
18751857

compiler/rustc_resolve/src/late/diagnostics.rs

+63-1
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ use crate::diagnostics::{ImportSuggestion, LabelSuggestion, TypoSuggestion};
44
use crate::late::{AliasPossibility, LateResolutionVisitor, RibKind};
55
use crate::late::{LifetimeBinderKind, LifetimeRes, LifetimeRibKind, LifetimeUseSet};
66
use crate::ty::fast_reject::SimplifiedType;
7-
use crate::{errors, path_names_to_string};
7+
use crate::{errors, path_names_to_string, Resolver};
88
use crate::{Module, ModuleKind, ModuleOrUniformRoot};
99
use crate::{PathResult, PathSource, Segment};
10+
use rustc_attr::collect_doc_alias_symbol_from_attrs;
1011
use rustc_hir::def::Namespace::{self, *};
1112

1213
use rustc_ast::ptr::P;
@@ -452,6 +453,18 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
452453
return (err, Vec::new());
453454
}
454455

456+
if let Some(did) = self.lookup_doc_alias_name(path, source.namespace()) {
457+
err.span_label(
458+
self.r.def_span(did),
459+
format!(
460+
"`{}` has a name defined in the doc alias attribute as `{}`",
461+
self.r.tcx.item_name(did),
462+
path.last().unwrap().ident.as_str()
463+
),
464+
);
465+
return (err, Vec::new());
466+
};
467+
455468
let (found, mut candidates) = self.try_lookup_name_relaxed(
456469
&mut err,
457470
source,
@@ -776,6 +789,55 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
776789
return (false, candidates);
777790
}
778791

792+
fn lookup_doc_alias_name(&mut self, path: &[Segment], ns: Namespace) -> Option<DefId> {
793+
let item_str = path.last().unwrap().ident;
794+
795+
let find_doc_alias_name = |r: &mut Resolver<'a, '_>, m: Module<'a>| {
796+
for resolution in r.resolutions(m).borrow().values() {
797+
let Some(did) =
798+
resolution.borrow().binding.and_then(|binding| binding.res().opt_def_id())
799+
else {
800+
continue;
801+
};
802+
let symbols = if let Some(local_id) = did.as_local() {
803+
r.def_id_to_node_id.get(local_id).and_then(|node_id| {
804+
r.confusable_attr_symbols.get(node_id).map(Cow::Borrowed)
805+
})
806+
} else {
807+
let attrs: Vec<_> = r.tcx.get_attrs(did, sym::doc).collect();
808+
(attrs.is_empty())
809+
.then(|| Cow::Owned(collect_doc_alias_symbol_from_attrs(attrs.into_iter())))
810+
};
811+
let Some(symbols) = symbols else {
812+
continue;
813+
};
814+
if symbols.contains(&item_str.name) {
815+
return Some(did);
816+
}
817+
}
818+
None
819+
};
820+
821+
if path.len() == 1 {
822+
for rib in self.ribs[ns].iter().rev() {
823+
if let RibKind::Module(module) = rib.kind
824+
&& let Some(did) = find_doc_alias_name(self.r, module)
825+
{
826+
return Some(did);
827+
}
828+
}
829+
} else {
830+
let mod_path = &path[..path.len() - 1];
831+
if let PathResult::Module(ModuleOrUniformRoot::Module(module)) =
832+
self.resolve_path(mod_path, Some(TypeNS), None)
833+
&& let Some(did) = find_doc_alias_name(self.r, module)
834+
{
835+
return Some(did);
836+
}
837+
}
838+
None
839+
}
840+
779841
fn suggest_trait_and_bounds(
780842
&mut self,
781843
err: &mut Diag<'_>,

compiler/rustc_resolve/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1160,6 +1160,8 @@ pub struct Resolver<'a, 'tcx> {
11601160
/// Whether lifetime elision was successful.
11611161
lifetime_elision_allowed: FxHashSet<NodeId>,
11621162

1163+
/// Some attributes may cause confusion, for example: `#[doc(alias = "name")`
1164+
confusable_attr_symbols: FxHashMap<NodeId, Vec<Symbol>>,
11631165
/// Names of items that were stripped out via cfg with their corresponding cfg meta item.
11641166
stripped_cfg_items: Vec<StrippedCfgItem<NodeId>>,
11651167

@@ -1512,6 +1514,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
15121514
proc_macros: Default::default(),
15131515
confused_type_with_std_module: Default::default(),
15141516
lifetime_elision_allowed: Default::default(),
1517+
confusable_attr_symbols: Default::default(),
15151518
stripped_cfg_items: Default::default(),
15161519
effective_visibilities: Default::default(),
15171520
doc_link_resolutions: Default::default(),

compiler/rustc_resolve/src/macros.rs

+4
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,10 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> {
477477
self.stripped_cfg_items.push(StrippedCfgItem { parent_module: parent_node, name, cfg });
478478
}
479479

480+
fn append_confusable_attr_symbols(&mut self, node_id: NodeId, symbols: Vec<Symbol>) {
481+
self.confusable_attr_symbols.insert(node_id, symbols);
482+
}
483+
480484
fn registered_tools(&self) -> &RegisteredTools {
481485
self.registered_tools
482486
}
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// issue#124273
2+
3+
#[doc(alias="DocAliasS1")]
4+
pub struct S1;
5+
//~^ NOTE: `S1` has a name defined in the doc alias attribute as `DocAliasS1`
6+
7+
#[doc(alias="DocAliasS2")]
8+
#[doc(alias("DocAliasS3", "DocAliasS4"))]
9+
pub struct S2;
10+
//~^ NOTE: `S2` has a name defined in the doc alias attribute as `DocAliasS2`
11+
//~| NOTE: `S2` has a name defined in the doc alias attribute as `DocAliasS3`
12+
//~| NOTE: `S2` has a name defined in the doc alias attribute as `DocAliasS4`
13+
14+
#[doc(alias("doc_alias_f1", "doc_alias_f2"))]
15+
pub fn f() {}
16+
//~^ NOTE: `f` has a name defined in the doc alias attribute as `doc_alias_f1`
17+
//~| NOTE: `f` has a name defined in the doc alias attribute as `doc_alias_f2`
18+
19+
mod m {
20+
#[doc(alias="DocAliasS5")]
21+
pub struct S5;
22+
//~^ NOTE: `S5` has a name defined in the doc alias attribute as `DocAliasS5`
23+
}
24+
25+
fn main() {
26+
DocAliasS1;
27+
//~^ ERROR: cannot find value `DocAliasS1` in this scope
28+
DocAliasS2;
29+
//~^ ERROR: cannot find value `DocAliasS2` in this scope
30+
DocAliasS3;
31+
//~^ ERROR: cannot find value `DocAliasS3` in this scope
32+
DocAliasS4;
33+
//~^ ERROR: cannot find value `DocAliasS4` in this scope
34+
doc_alias_f1();
35+
//~^ ERROR: cannot find function `doc_alias_f1` in this scope
36+
doc_alias_f2();
37+
//~^ ERROR: cannot find function `doc_alias_f2` in this scope
38+
m::DocAliasS5;
39+
//~^ ERROR: cannot find value `DocAliasS5` in module `m`
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
error[E0425]: cannot find value `DocAliasS1` in this scope
2+
--> $DIR/use-doc-alias-name.rs:26:3
3+
|
4+
LL | pub struct S1;
5+
| -------------- `S1` has a name defined in the doc alias attribute as `DocAliasS1`
6+
...
7+
LL | DocAliasS1;
8+
| ^^^^^^^^^^
9+
10+
error[E0425]: cannot find value `DocAliasS2` in this scope
11+
--> $DIR/use-doc-alias-name.rs:28:3
12+
|
13+
LL | pub struct S2;
14+
| -------------- `S2` has a name defined in the doc alias attribute as `DocAliasS2`
15+
...
16+
LL | DocAliasS2;
17+
| ^^^^^^^^^^
18+
19+
error[E0425]: cannot find value `DocAliasS3` in this scope
20+
--> $DIR/use-doc-alias-name.rs:30:3
21+
|
22+
LL | pub struct S2;
23+
| -------------- `S2` has a name defined in the doc alias attribute as `DocAliasS3`
24+
...
25+
LL | DocAliasS3;
26+
| ^^^^^^^^^^
27+
28+
error[E0425]: cannot find value `DocAliasS4` in this scope
29+
--> $DIR/use-doc-alias-name.rs:32:3
30+
|
31+
LL | pub struct S2;
32+
| -------------- `S2` has a name defined in the doc alias attribute as `DocAliasS4`
33+
...
34+
LL | DocAliasS4;
35+
| ^^^^^^^^^^
36+
37+
error[E0425]: cannot find value `DocAliasS5` in module `m`
38+
--> $DIR/use-doc-alias-name.rs:38:6
39+
|
40+
LL | pub struct S5;
41+
| -------------- `S5` has a name defined in the doc alias attribute as `DocAliasS5`
42+
...
43+
LL | m::DocAliasS5;
44+
| ^^^^^^^^^^
45+
46+
error[E0425]: cannot find function `doc_alias_f1` in this scope
47+
--> $DIR/use-doc-alias-name.rs:34:3
48+
|
49+
LL | pub fn f() {}
50+
| ------------- `f` has a name defined in the doc alias attribute as `doc_alias_f1`
51+
...
52+
LL | doc_alias_f1();
53+
| ^^^^^^^^^^^^
54+
55+
error[E0425]: cannot find function `doc_alias_f2` in this scope
56+
--> $DIR/use-doc-alias-name.rs:36:3
57+
|
58+
LL | pub fn f() {}
59+
| ------------- `f` has a name defined in the doc alias attribute as `doc_alias_f2`
60+
...
61+
LL | doc_alias_f2();
62+
| ^^^^^^^^^^^^
63+
64+
error: aborting due to 7 previous errors
65+
66+
For more information about this error, try `rustc --explain E0425`.

0 commit comments

Comments
 (0)