Skip to content

Commit ce781c4

Browse files
committed
collect doc alias as tips during resolution
1 parent 414482f commit ce781c4

File tree

7 files changed

+347
-32
lines changed

7 files changed

+347
-32
lines changed

compiler/rustc_attr_parsing/src/attributes/util.rs

+30
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,33 @@ pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool {
2626
pub fn find_crate_name(attrs: &[impl AttributeExt]) -> Option<Symbol> {
2727
first_attr_value_str_by_name(attrs, sym::crate_name)
2828
}
29+
30+
pub fn is_doc_alias_attrs_contain_symbol<'tcx, T: AttributeExt + 'tcx>(
31+
attrs: impl Iterator<Item = &'tcx T>,
32+
symbol: Symbol,
33+
) -> bool {
34+
let doc_attrs = attrs.filter(|attr| attr.has_name(sym::doc));
35+
for attr in doc_attrs {
36+
let Some(values) = attr.meta_item_list() else {
37+
continue;
38+
};
39+
let alias_values = values.iter().filter(|v| v.has_name(sym::alias));
40+
for v in alias_values {
41+
if let Some(nested) = v.meta_item_list() {
42+
// #[doc(alias("foo", "bar"))]
43+
let mut iter = nested.iter().filter_map(|item| item.lit()).map(|item| item.symbol);
44+
if iter.any(|s| s == symbol) {
45+
return true;
46+
}
47+
} else if let Some(meta) = v.meta_item()
48+
&& let Some(lit) = meta.name_value_literal()
49+
{
50+
// #[doc(alias = "foo")]
51+
if lit.symbol == symbol {
52+
return true;
53+
}
54+
}
55+
}
56+
}
57+
false
58+
}

compiler/rustc_attr_parsing/src/lib.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,9 @@ pub mod parser;
8989
mod session_diagnostics;
9090

9191
pub use attributes::cfg::*;
92-
pub use attributes::util::{find_crate_name, is_builtin_attr, parse_version};
92+
pub use attributes::util::{
93+
find_crate_name, is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version,
94+
};
9395
pub use context::{AttributeParser, OmitDoc};
9496
pub use rustc_attr_data_structures::*;
9597

compiler/rustc_hir_typeck/src/method/probe.rs

+7-30
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::cell::{Cell, RefCell};
22
use std::cmp::max;
33
use std::ops::Deref;
44

5+
use rustc_attr_parsing::is_doc_alias_attrs_contain_symbol;
56
use rustc_data_structures::fx::FxHashSet;
67
use rustc_data_structures::sso::SsoHashSet;
78
use rustc_errors::Applicability;
@@ -2333,10 +2334,13 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
23332334
};
23342335
let hir_id = self.fcx.tcx.local_def_id_to_hir_id(local_def_id);
23352336
let attrs = self.fcx.tcx.hir_attrs(hir_id);
2337+
2338+
if is_doc_alias_attrs_contain_symbol(attrs.into_iter(), method.name) {
2339+
return true;
2340+
}
2341+
23362342
for attr in attrs {
2337-
if attr.has_name(sym::doc) {
2338-
// do nothing
2339-
} else if attr.has_name(sym::rustc_confusables) {
2343+
if attr.has_name(sym::rustc_confusables) {
23402344
let Some(confusables) = attr.meta_item_list() else {
23412345
continue;
23422346
};
@@ -2348,33 +2352,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
23482352
return true;
23492353
}
23502354
}
2351-
continue;
2352-
} else {
2353-
continue;
2354-
};
2355-
let Some(values) = attr.meta_item_list() else {
2356-
continue;
2357-
};
2358-
for v in values {
2359-
if !v.has_name(sym::alias) {
2360-
continue;
2361-
}
2362-
if let Some(nested) = v.meta_item_list() {
2363-
// #[doc(alias("foo", "bar"))]
2364-
for n in nested {
2365-
if let Some(lit) = n.lit()
2366-
&& method.name == lit.symbol
2367-
{
2368-
return true;
2369-
}
2370-
}
2371-
} else if let Some(meta) = v.meta_item()
2372-
&& let Some(lit) = meta.name_value_literal()
2373-
&& method.name == lit.symbol
2374-
{
2375-
// #[doc(alias = "foo")]
2376-
return true;
2377-
}
23782355
}
23792356
}
23802357
false

compiler/rustc_resolve/src/late/diagnostics.rs

+66-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use rustc_ast::{
1111
Item, ItemKind, MethodCall, NodeId, Path, PathSegment, Ty, TyKind,
1212
};
1313
use rustc_ast_pretty::pprust::where_bound_predicate_to_string;
14+
use rustc_attr_parsing::is_doc_alias_attrs_contain_symbol;
1415
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
1516
use rustc_errors::codes::*;
1617
use rustc_errors::{
@@ -39,7 +40,7 @@ use crate::late::{
3940
};
4041
use crate::ty::fast_reject::SimplifiedType;
4142
use crate::{
42-
Module, ModuleKind, ModuleOrUniformRoot, PathResult, PathSource, Segment, errors,
43+
Module, ModuleKind, ModuleOrUniformRoot, PathResult, PathSource, Resolver, Segment, errors,
4344
path_names_to_string,
4445
};
4546

@@ -477,6 +478,19 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
477478
return (err, Vec::new());
478479
}
479480

481+
if let Some((did, item)) = self.lookup_doc_alias_name(path, source.namespace()) {
482+
let item_name = item.name;
483+
let suggestion_name = self.r.tcx.item_name(did);
484+
err.span_suggestion(
485+
item.span,
486+
format!("`{suggestion_name}` has a name defined in the doc alias attribute as `{item_name}`"),
487+
suggestion_name,
488+
Applicability::MaybeIncorrect
489+
);
490+
491+
return (err, Vec::new());
492+
};
493+
480494
let (found, suggested_candidates, mut candidates) = self.try_lookup_name_relaxed(
481495
&mut err,
482496
source,
@@ -852,6 +866,57 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
852866
(false, suggested_candidates, candidates)
853867
}
854868

869+
fn lookup_doc_alias_name(&mut self, path: &[Segment], ns: Namespace) -> Option<(DefId, Ident)> {
870+
let find_doc_alias_name = |r: &mut Resolver<'ra, '_>, m: Module<'ra>, item_name: Symbol| {
871+
for resolution in r.resolutions(m).borrow().values() {
872+
let Some(did) =
873+
resolution.borrow().binding.and_then(|binding| binding.res().opt_def_id())
874+
else {
875+
continue;
876+
};
877+
if did.is_local() {
878+
// We don't record the doc alias name in the local crate
879+
// because the people who write doc alias are usually not
880+
// confused by them.
881+
continue;
882+
}
883+
if is_doc_alias_attrs_contain_symbol(r.tcx.get_attrs(did, sym::doc), item_name) {
884+
return Some(did);
885+
}
886+
}
887+
None
888+
};
889+
890+
if path.len() == 1 {
891+
for rib in self.ribs[ns].iter().rev() {
892+
let item = path[0].ident;
893+
if let RibKind::Module(module) = rib.kind
894+
&& let Some(did) = find_doc_alias_name(self.r, module, item.name)
895+
{
896+
return Some((did, item));
897+
}
898+
}
899+
} else {
900+
for (idx, seg) in path.iter().enumerate().rev().skip(1) {
901+
let Some(id) = seg.id else {
902+
continue;
903+
};
904+
let Some(res) = self.r.partial_res_map.get(&id) else {
905+
continue;
906+
};
907+
if let Res::Def(DefKind::Mod, module) = res.expect_full_res()
908+
&& let Some(module) = self.r.get_module(module)
909+
&& let item = path[idx + 1].ident
910+
&& let Some(did) = find_doc_alias_name(self.r, module, item.name)
911+
{
912+
return Some((did, item));
913+
}
914+
break;
915+
}
916+
}
917+
None
918+
}
919+
855920
fn suggest_trait_and_bounds(
856921
&mut self,
857922
err: &mut Diag<'_>,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#[doc(alias="DocAliasS1")]
2+
pub struct S1;
3+
4+
#[doc(alias="DocAliasS2")]
5+
#[doc(alias("DocAliasS3", "DocAliasS4"))]
6+
pub struct S2;
7+
8+
#[doc(alias("doc_alias_f1", "doc_alias_f2"))]
9+
pub fn f() {}
10+
11+
pub mod m {
12+
#[doc(alias="DocAliasS5")]
13+
pub struct S5;
14+
15+
pub mod n {
16+
#[doc(alias("DocAliasX"))]
17+
pub mod x {
18+
pub mod y {
19+
#[doc(alias="DocAliasS6")]
20+
pub struct S6;
21+
}
22+
}
23+
}
24+
}
+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//@ aux-build: use-doc-alias-name-extern.rs
2+
3+
// issue#124273
4+
5+
extern crate use_doc_alias_name_extern;
6+
7+
use use_doc_alias_name_extern::*;
8+
9+
#[doc(alias="LocalDocAliasS")]
10+
struct S;
11+
12+
fn main() {
13+
LocalDocAliasS; // don't show help in local crate
14+
//~^ ERROR: cannot find value `LocalDocAliasS` in this scope
15+
16+
DocAliasS1;
17+
//~^ ERROR: cannot find value `DocAliasS1` in this scope
18+
//~| HELP: `S1` has a name defined in the doc alias attribute as `DocAliasS1`
19+
20+
DocAliasS2;
21+
//~^ ERROR: cannot find value `DocAliasS2` in this scope
22+
//~| HELP: `S2` has a name defined in the doc alias attribute as `DocAliasS2`
23+
24+
DocAliasS3;
25+
//~^ ERROR: cannot find value `DocAliasS3` in this scope
26+
//~| HELP: `S2` has a name defined in the doc alias attribute as `DocAliasS3`
27+
28+
DocAliasS4;
29+
//~^ ERROR: cannot find value `DocAliasS4` in this scope
30+
//~| HELP: `S2` has a name defined in the doc alias attribute as `DocAliasS4`
31+
32+
doc_alias_f1();
33+
//~^ ERROR: cannot find function `doc_alias_f1` in this scope
34+
//~| HELP: `f` has a name defined in the doc alias attribute as `doc_alias_f1`
35+
36+
doc_alias_f2();
37+
//~^ ERROR: cannot find function `doc_alias_f2` in this scope
38+
//~| HELP: `f` has a name defined in the doc alias attribute as `doc_alias_f2`
39+
40+
m::DocAliasS5;
41+
//~^ ERROR: cannot find value `DocAliasS5` in module `m`
42+
//~| HELP: `S5` has a name defined in the doc alias attribute as `DocAliasS5`
43+
44+
not_exist_module::DocAliasS1;
45+
//~^ ERROR: use of unresolved module or unlinked crate `not_exist_module`
46+
//~| HELP: you might be missing a crate named `not_exist_module`
47+
48+
use_doc_alias_name_extern::DocAliasS1;
49+
//~^ ERROR: cannot find value `DocAliasS1` in crate `use_doc_alias_name_extern
50+
//~| HELP: `S1` has a name defined in the doc alias attribute as `DocAliasS1`
51+
52+
m::n::DocAliasX::y::S6;
53+
//~^ ERROR: could not find `DocAliasX` in `n`
54+
//~| HELP: `x` has a name defined in the doc alias attribute as `DocAliasX`
55+
56+
m::n::x::y::DocAliasS6;
57+
//~^ ERROR: cannot find value `DocAliasS6` in module `m::n::x::y`
58+
//~| HELP: `S6` has a name defined in the doc alias attribute as `DocAliasS6`
59+
}
60+
61+
trait T {
62+
fn f() {
63+
DocAliasS1;
64+
//~^ ERROR: cannot find value `DocAliasS1` in this scope
65+
//~| HELP: `S1` has a name defined in the doc alias attribute as `DocAliasS1`
66+
}
67+
}

0 commit comments

Comments
 (0)