Remember names of cfg
-ed out items to mention them in diagnostics
`#[cfg]`s are frequently used to gate crate content behind cargo features. This can lead to very confusing errors when features are missing. For example, `serde` doesn't have the `derive` feature by default. Therefore, `serde::Serialize` fails to resolve with a generic error, even though the macro is present in the docs. This commit adds a list of all stripped item names to metadata. This is filled during macro expansion and then, through a fed query, persisted in metadata. The downstream resolver can then access the metadata to look at possible candidates for mentioning in the errors. This slightly increases metadata (800k->809k for the feature-heavy windows crate), but not enough to really matter.
This commit is contained in:
parent
642c92e630
commit
a647ba250a
30 changed files with 599 additions and 84 deletions
|
@ -947,6 +947,8 @@ pub trait ResolverExpand {
|
|||
/// HIR proc macros items back to their harness items.
|
||||
fn declare_proc_macro(&mut self, id: NodeId);
|
||||
|
||||
fn append_stripped_cfg_item(&mut self, parent_node: NodeId, name: Ident, cfg: ast::MetaItem);
|
||||
|
||||
/// Tools registered with `#![register_tool]` and used by tool attributes and lints.
|
||||
fn registered_tools(&self) -> &RegisteredTools;
|
||||
}
|
||||
|
@ -965,7 +967,7 @@ pub trait LintStoreExpand {
|
|||
|
||||
type LintStoreExpandDyn<'a> = Option<&'a (dyn LintStoreExpand + 'a)>;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ModuleData {
|
||||
/// Path to the module starting from the crate name, like `my_crate::foo::bar`.
|
||||
pub mod_path: Vec<Ident>,
|
||||
|
|
|
@ -416,20 +416,28 @@ impl<'a> StripUnconfigured<'a> {
|
|||
|
||||
/// Determines if a node with the given attributes should be included in this configuration.
|
||||
fn in_cfg(&self, attrs: &[Attribute]) -> bool {
|
||||
attrs.iter().all(|attr| !is_cfg(attr) || self.cfg_true(attr))
|
||||
attrs.iter().all(|attr| !is_cfg(attr) || self.cfg_true(attr).0)
|
||||
}
|
||||
|
||||
pub(crate) fn cfg_true(&self, attr: &Attribute) -> bool {
|
||||
pub(crate) fn cfg_true(&self, attr: &Attribute) -> (bool, Option<MetaItem>) {
|
||||
let meta_item = match validate_attr::parse_meta(&self.sess.parse_sess, attr) {
|
||||
Ok(meta_item) => meta_item,
|
||||
Err(mut err) => {
|
||||
err.emit();
|
||||
return true;
|
||||
return (true, None);
|
||||
}
|
||||
};
|
||||
parse_cfg(&meta_item, &self.sess).map_or(true, |meta_item| {
|
||||
attr::cfg_matches(&meta_item, &self.sess.parse_sess, self.lint_node_id, self.features)
|
||||
})
|
||||
(
|
||||
parse_cfg(&meta_item, &self.sess).map_or(true, |meta_item| {
|
||||
attr::cfg_matches(
|
||||
&meta_item,
|
||||
&self.sess.parse_sess,
|
||||
self.lint_node_id,
|
||||
self.features,
|
||||
)
|
||||
}),
|
||||
Some(meta_item),
|
||||
)
|
||||
}
|
||||
|
||||
/// If attributes are not allowed on expressions, emit an error for `attr`
|
||||
|
|
|
@ -1042,6 +1042,12 @@ trait InvocationCollectorNode: HasAttrs + HasNodeId + Sized {
|
|||
fn expand_cfg_false(&mut self, collector: &mut InvocationCollector<'_, '_>, span: Span) {
|
||||
collector.cx.emit_err(RemoveNodeNotSupported { span, descr: Self::descr() });
|
||||
}
|
||||
|
||||
/// All of the names (items) declared by this node.
|
||||
/// This is an approximation and should only be used for diagnostics.
|
||||
fn declared_names(&self) -> Vec<Ident> {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
impl InvocationCollectorNode for P<ast::Item> {
|
||||
|
@ -1148,6 +1154,27 @@ impl InvocationCollectorNode for P<ast::Item> {
|
|||
collector.cx.current_expansion.module = orig_module;
|
||||
res
|
||||
}
|
||||
fn declared_names(&self) -> Vec<Ident> {
|
||||
if let ItemKind::Use(ut) = &self.kind {
|
||||
fn collect_use_tree_leaves(ut: &ast::UseTree, idents: &mut Vec<Ident>) {
|
||||
match &ut.kind {
|
||||
ast::UseTreeKind::Glob => {}
|
||||
ast::UseTreeKind::Simple(_) => idents.push(ut.ident()),
|
||||
ast::UseTreeKind::Nested(nested) => {
|
||||
for (ut, _) in nested {
|
||||
collect_use_tree_leaves(&ut, idents);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut idents = Vec::new();
|
||||
collect_use_tree_leaves(&ut, &mut idents);
|
||||
return idents;
|
||||
}
|
||||
|
||||
vec![self.ident]
|
||||
}
|
||||
}
|
||||
|
||||
struct TraitItemTag;
|
||||
|
@ -1685,8 +1712,8 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
|
|||
node: &mut impl HasAttrs,
|
||||
attr: ast::Attribute,
|
||||
pos: usize,
|
||||
) -> bool {
|
||||
let res = self.cfg().cfg_true(&attr);
|
||||
) -> (bool, Option<ast::MetaItem>) {
|
||||
let (res, meta_item) = self.cfg().cfg_true(&attr);
|
||||
if res {
|
||||
// FIXME: `cfg(TRUE)` attributes do not currently remove themselves during expansion,
|
||||
// and some tools like rustdoc and clippy rely on that. Find a way to remove them
|
||||
|
@ -1694,7 +1721,8 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
|
|||
self.cx.expanded_inert_attrs.mark(&attr);
|
||||
node.visit_attrs(|attrs| attrs.insert(pos, attr));
|
||||
}
|
||||
res
|
||||
|
||||
(res, meta_item)
|
||||
}
|
||||
|
||||
fn expand_cfg_attr(&self, node: &mut impl HasAttrs, attr: &ast::Attribute, pos: usize) {
|
||||
|
@ -1715,9 +1743,20 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
|
|||
return match self.take_first_attr(&mut node) {
|
||||
Some((attr, pos, derives)) => match attr.name_or_empty() {
|
||||
sym::cfg => {
|
||||
if self.expand_cfg_true(&mut node, attr, pos) {
|
||||
let (res, meta_item) = self.expand_cfg_true(&mut node, attr, pos);
|
||||
if res {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(meta_item) = meta_item {
|
||||
for name in node.declared_names() {
|
||||
self.cx.resolver.append_stripped_cfg_item(
|
||||
self.cx.current_expansion.lint_node_id,
|
||||
name,
|
||||
meta_item.clone(),
|
||||
)
|
||||
}
|
||||
}
|
||||
Default::default()
|
||||
}
|
||||
sym::cfg_attr => {
|
||||
|
@ -1761,7 +1800,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
|
|||
Some((attr, pos, derives)) => match attr.name_or_empty() {
|
||||
sym::cfg => {
|
||||
let span = attr.span;
|
||||
if self.expand_cfg_true(node, attr, pos) {
|
||||
if self.expand_cfg_true(node, attr, pos).0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue