Skip to content

Commit

Permalink
collect doc alias as tips during resolution
Browse files Browse the repository at this point in the history
  • Loading branch information
bvanjoi committed Jul 14, 2024
1 parent a241cf1 commit d59e6fa
Show file tree
Hide file tree
Showing 9 changed files with 237 additions and 32 deletions.
26 changes: 26 additions & 0 deletions compiler/rustc_attr/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1239,3 +1239,29 @@ pub fn parse_confusables(attr: &Attribute) -> Option<Vec<Symbol>> {

return Some(candidates);
}

pub fn collect_doc_alias_symbol_from_attrs<'tcx>(
attrs: impl Iterator<Item = &'tcx ast::Attribute>,
) -> Vec<Symbol> {
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
}
1 change: 1 addition & 0 deletions compiler/rustc_expand/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1062,6 +1062,7 @@ pub trait ResolverExpand {
fn declare_proc_macro(&mut self, id: NodeId);

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

/// Tools registered with `#![register_tool]` and used by tool attributes and lints.
fn registered_tools(&self) -> &RegisteredTools;
Expand Down
25 changes: 23 additions & 2 deletions compiler/rustc_expand/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use rustc_ast::{ForeignItemKind, HasAttrs, HasNodeId};
use rustc_ast::{Inline, ItemKind, MacStmtStyle, MetaItemKind, ModKind};
use rustc_ast::{NestedMetaItem, NodeId, PatKind, StmtKind, TyKind};
use rustc_ast_pretty::pprust;
use rustc_attr::collect_doc_alias_symbol_from_attrs;
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
use rustc_data_structures::sync::Lrc;
use rustc_errors::PResult;
Expand Down Expand Up @@ -2021,7 +2022,17 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
}
None => {
match Node::wrap_flat_map_node_noop_flat_map(node, self, |mut node, this| {
assign_id!(this, node.node_id_mut(), || node.noop_flat_map(this))
assign_id!(this, node.node_id_mut(), || {
let symbols = collect_doc_alias_symbol_from_attrs(node.attrs().iter());
if !symbols.is_empty() {
this.cx.resolver.append_confusable_attr_symbols(
this.cx.current_expansion.lint_node_id,
symbols,
);
}

node.noop_flat_map(this)
})
}) {
Ok(output) => output,
Err(returned_node) => {
Expand Down Expand Up @@ -2069,7 +2080,17 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
}
None if node.delegation().is_some() => unreachable!(),
None => {
assign_id!(self, node.node_id_mut(), || node.noop_visit(self))
assign_id!(self, node.node_id_mut(), || {
let symbols = collect_doc_alias_symbol_from_attrs(node.attrs().iter());
if !symbols.is_empty() {
self.cx.resolver.append_confusable_attr_symbols(
self.cx.current_expansion.lint_node_id,
symbols,
);
}

node.noop_visit(self)
})
}
};
}
Expand Down
40 changes: 11 additions & 29 deletions compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
};
Expand All @@ -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
}

Expand Down
64 changes: 63 additions & 1 deletion compiler/rustc_resolve/src/late/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -776,6 +789,55 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
return (false, candidates);
}

fn lookup_doc_alias_name(&mut self, path: &[Segment], ns: Namespace) -> Option<DefId> {
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;
};
let symbols = if let Some(local_id) = did.as_local() {
r.def_id_to_node_id.get(local_id).and_then(|node_id| {
r.confusable_attr_symbols.get(node_id).map(Cow::Borrowed)
})
} else {
let attrs: Vec<_> = r.tcx.get_attrs(did, sym::doc).collect();
(attrs.is_empty())
.then(|| Cow::Owned(collect_doc_alias_symbol_from_attrs(attrs.into_iter())))
};
let Some(symbols) = symbols else {
continue;
};
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<'_>,
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_resolve/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1160,6 +1160,8 @@ pub struct Resolver<'a, 'tcx> {
/// Whether lifetime elision was successful.
lifetime_elision_allowed: FxHashSet<NodeId>,

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

Expand Down Expand Up @@ -1512,6 +1514,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
proc_macros: Default::default(),
confused_type_with_std_module: Default::default(),
lifetime_elision_allowed: Default::default(),
confusable_attr_symbols: Default::default(),
stripped_cfg_items: Default::default(),
effective_visibilities: Default::default(),
doc_link_resolutions: Default::default(),
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_resolve/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,10 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> {
self.stripped_cfg_items.push(StrippedCfgItem { parent_module: parent_node, name, cfg });
}

fn append_confusable_attr_symbols(&mut self, node_id: NodeId, symbols: Vec<Symbol>) {
self.confusable_attr_symbols.insert(node_id, symbols);
}

fn registered_tools(&self) -> &RegisteredTools {
self.registered_tools
}
Expand Down
40 changes: 40 additions & 0 deletions tests/ui/attributes/use-doc-alias-name.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// issue#124273

#[doc(alias="DocAliasS1")]
pub struct S1;
//~^ NOTE: `S1` has a name defined in the doc alias attribute as `DocAliasS1`

#[doc(alias="DocAliasS2")]
#[doc(alias("DocAliasS3", "DocAliasS4"))]
pub struct S2;
//~^ NOTE: `S2` has a name defined in the doc alias attribute as `DocAliasS2`
//~| NOTE: `S2` has a name defined in the doc alias attribute as `DocAliasS3`
//~| NOTE: `S2` has a name defined in the doc alias attribute as `DocAliasS4`

#[doc(alias("doc_alias_f1", "doc_alias_f2"))]
pub fn f() {}
//~^ NOTE: `f` has a name defined in the doc alias attribute as `doc_alias_f1`
//~| NOTE: `f` has a name defined in the doc alias attribute as `doc_alias_f2`

mod m {
#[doc(alias="DocAliasS5")]
pub struct S5;
//~^ NOTE: `S5` has a name defined in the doc alias attribute as `DocAliasS5`
}

fn main() {
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`
}
66 changes: 66 additions & 0 deletions tests/ui/attributes/use-doc-alias-name.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
error[E0425]: cannot find value `DocAliasS1` in this scope
--> $DIR/use-doc-alias-name.rs:26:3
|
LL | pub struct S1;
| -------------- `S1` has a name defined in the doc alias attribute as `DocAliasS1`
...
LL | DocAliasS1;
| ^^^^^^^^^^

error[E0425]: cannot find value `DocAliasS2` in this scope
--> $DIR/use-doc-alias-name.rs:28:3
|
LL | pub struct S2;
| -------------- `S2` has a name defined in the doc alias attribute as `DocAliasS2`
...
LL | DocAliasS2;
| ^^^^^^^^^^

error[E0425]: cannot find value `DocAliasS3` in this scope
--> $DIR/use-doc-alias-name.rs:30:3
|
LL | pub struct S2;
| -------------- `S2` has a name defined in the doc alias attribute as `DocAliasS3`
...
LL | DocAliasS3;
| ^^^^^^^^^^

error[E0425]: cannot find value `DocAliasS4` in this scope
--> $DIR/use-doc-alias-name.rs:32:3
|
LL | pub struct S2;
| -------------- `S2` has a name defined in the doc alias attribute as `DocAliasS4`
...
LL | DocAliasS4;
| ^^^^^^^^^^

error[E0425]: cannot find value `DocAliasS5` in module `m`
--> $DIR/use-doc-alias-name.rs:38:6
|
LL | pub struct S5;
| -------------- `S5` has a name defined in the doc alias attribute as `DocAliasS5`
...
LL | m::DocAliasS5;
| ^^^^^^^^^^

error[E0425]: cannot find function `doc_alias_f1` in this scope
--> $DIR/use-doc-alias-name.rs:34:3
|
LL | pub fn f() {}
| ------------- `f` has a name defined in the doc alias attribute as `doc_alias_f1`
...
LL | doc_alias_f1();
| ^^^^^^^^^^^^

error[E0425]: cannot find function `doc_alias_f2` in this scope
--> $DIR/use-doc-alias-name.rs:36:3
|
LL | pub fn f() {}
| ------------- `f` has a name defined in the doc alias attribute as `doc_alias_f2`
...
LL | doc_alias_f2();
| ^^^^^^^^^^^^

error: aborting due to 7 previous errors

For more information about this error, try `rustc --explain E0425`.

0 comments on commit d59e6fa

Please sign in to comment.