Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions compiler/rustc_attr_parsing/src/attributes/cfg_select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,25 +47,28 @@ pub struct CfgSelectBranches {
impl CfgSelectBranches {
/// Removes the top-most branch for which `predicate` returns `true`,
/// or the wildcard if none of the reachable branches satisfied the predicate.
pub fn pop_first_match<F>(&mut self, predicate: F) -> Option<(TokenStream, Span)>
pub fn pop_first_match<F>(&mut self, predicate: F) -> Option<(CfgEntry, TokenStream, Span)>
where
F: Fn(&CfgEntry) -> bool,
{
for (index, (cfg, _, _)) in self.reachable.iter().enumerate() {
if predicate(cfg) {
let matched = self.reachable.remove(index);
return Some((matched.1, matched.2));
return Some(self.reachable.remove(index));
}
}

self.wildcard.take().map(|(_, tts, span)| (tts, span))
self.wildcard.take().map(|(_, tts, span)| (CfgEntry::Bool(true, span), tts, span))
}

/// Consume this value and iterate over all the `TokenStream`s that it stores.
pub fn into_iter_tts(self) -> impl Iterator<Item = (TokenStream, Span)> {
let it1 = self.reachable.into_iter().map(|(_, tts, span)| (tts, span));
let it2 = self.wildcard.into_iter().map(|(_, tts, span)| (tts, span));
let it3 = self.unreachable.into_iter().map(|(_, tts, span)| (tts, span));
pub fn into_iter_tts(self) -> impl Iterator<Item = (CfgEntry, TokenStream, Span)> {
let it1 = self.reachable.into_iter();
let it2 =
self.wildcard.into_iter().map(|(_, tts, span)| (CfgEntry::Bool(true, span), tts, span));
let it3 = self
.unreachable
.into_iter()
.map(|(_, tts, span)| (CfgEntry::Bool(false, span), tts, span));

it1.chain(it2).chain(it3)
}
Expand Down
78 changes: 68 additions & 10 deletions compiler/rustc_builtin_macros/src/cfg_select.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use rustc_ast::attr::AttrIdGenerator;
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::{Expr, ast};
use rustc_ast::{AttrKind, Expr, SyntheticAttr, ast};
use rustc_attr_ir::CfgEntry;
use rustc_attr_parsing as attr;
use rustc_attr_parsing::{CfgSelectBranches, EvalConfigResult, parse_cfg_select};
use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacResult, MacroExpanderResult};
use rustc_expand::expand::DeclaredIdents;
use rustc_span::{Ident, Span, sym};
use smallvec::SmallVec;

Expand All @@ -17,6 +20,7 @@ struct CfgSelectResult<'cx, 'sess> {
selected_tts: TokenStream,
selected_span: Span,
other_branches: CfgSelectBranches,
cfg_entry: CfgEntry,
}

fn tts_to_mac_result<'cx, 'sess>(
Expand All @@ -33,23 +37,74 @@ fn tts_to_mac_result<'cx, 'sess>(
}

macro_rules! forward_to_parser_any_macro {
($method_name:ident, $ret_ty:ty) => {
($method_name:ident, $ret_ty:ty, $other:expr, $selected:expr) => {
fn $method_name(self: Box<Self>) -> Option<$ret_ty> {
let CfgSelectResult { ecx, site_span, selected_tts, selected_span, .. } = *self;
let CfgSelectResult { ecx, site_span, selected_tts, selected_span, cfg_entry, .. } =
*self;

for (tts, span) in self.other_branches.into_iter_tts() {
let _ = tts_to_mac_result(ecx, site_span, tts, span).$method_name();
for (cfg_entry, tts, span) in self.other_branches.into_iter_tts() {
let result = tts_to_mac_result(ecx, site_span, tts, span).$method_name();
$other(&mut *ecx, cfg_entry, span, result);
}

tts_to_mac_result(ecx, site_span, selected_tts, selected_span).$method_name()
tts_to_mac_result(ecx, site_span, selected_tts, selected_span)
.$method_name()
.map(|elements| $selected(&mut *ecx, cfg_entry, elements))
}
};

($method_name:ident, $ret_ty:ty) => {
forward_to_parser_any_macro!($method_name, $ret_ty, |_, _, _, _| {}, |_, _, elements| {
elements
});
};
}

/// Construct a `#[<cfg_trace>]` attribute from a `CfgEntry`. This allows us to keep track of items
/// that were behind a `cfg_select!`, which is relevant for some diagnostics.
fn mk_attr(g: &AttrIdGenerator, cfg_entry: CfgEntry) -> ast::Attribute {
let cfg_span = cfg_entry.span();
ast::Attribute {
kind: AttrKind::Synthetic(Box::new(SyntheticAttr::CfgAttrTrace(cfg_entry))),
id: g.mk_attr_id(),
style: ast::AttrStyle::Outer,
span: cfg_span,
}
}

impl<'cx, 'sess> MacResult for CfgSelectResult<'cx, 'sess> {
forward_to_parser_any_macro!(make_expr, Box<Expr>);
forward_to_parser_any_macro!(make_stmts, SmallVec<[ast::Stmt; 1]>);
forward_to_parser_any_macro!(make_items, SmallVec<[Box<ast::Item>; 1]>);
forward_to_parser_any_macro!(
make_items,
SmallVec<[Box<ast::Item>; 1]>,
|ecx: &mut ExtCtxt<'_>,
cfg_entry: CfgEntry,
_span: Span,
items: Option<SmallVec<[Box<ast::Item>; 1]>>| if let Some(items) = items {
// Register item names that were not selected for error reporting. We do this
// for `#[cfg]` too.
for item in items {
for name in item.declared_idents() {
ecx.resolver.append_stripped_cfg_item(
ecx.current_expansion.lint_node_id,
name,
cfg_entry.clone(),
cfg_entry.span(),
);
}
}
},
|ecx: &mut ExtCtxt<'_>, cfg_entry: CfgEntry, items: SmallVec<[Box<ast::Item>; 1]>| {
items
.into_iter()
.map(|mut item| {
item.attrs.push(mk_attr(&ecx.sess.psess.attr_id_generator, cfg_entry.clone()));
item
})
.collect()
}
);

forward_to_parser_any_macro!(make_impl_items, SmallVec<[Box<ast::AssocItem>; 1]>);
forward_to_parser_any_macro!(make_trait_impl_items, SmallVec<[Box<ast::AssocItem>; 1]>);
Expand All @@ -73,15 +128,18 @@ pub(super) fn expand_cfg_select<'cx>(
ecx.current_expansion.lint_node_id,
) {
Ok(mut branches) => {
if let Some((selected_tts, selected_span)) = branches.pop_first_match(|cfg| {
matches!(attr::eval_config_entry(ecx.sess, cfg), EvalConfigResult::True)
}) {
if let Some((cfg_entry, selected_tts, selected_span)) =
branches.pop_first_match(|cfg| {
matches!(attr::eval_config_entry(ecx.sess, cfg), EvalConfigResult::True)
})
{
let mac = CfgSelectResult {
ecx,
selected_tts,
selected_span,
other_branches: branches,
site_span: sp,
cfg_entry,
};
return ExpandResult::Ready(Box::new(mac));
} else {
Expand Down
45 changes: 39 additions & 6 deletions compiler/rustc_expand/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1229,7 +1229,7 @@ enum AddSemicolon {

/// A trait implemented for all `AstFragment` nodes and providing all pieces
/// of functionality used by `InvocationCollector`.
trait InvocationCollectorNode: HasAttrs + HasNodeId + Sized {
trait InvocationCollectorNode: HasAttrs + HasNodeId + Sized + DeclaredIdents {
Comment thread
petrochenkov marked this conversation as resolved.
type OutputTy = SmallVec<[Self; 1]>;
type ItemKind = ItemKind;
const KIND: AstFragmentKind;
Expand Down Expand Up @@ -1281,13 +1281,44 @@ trait InvocationCollectorNode: HasAttrs + HasNodeId + Sized {
collector.cx.dcx().emit_err(RemoveNodeNotSupported { span, descr: Self::descr() });
}

fn as_target(&self) -> Target;
}

pub trait DeclaredIdents {
Comment thread
estebank marked this conversation as resolved.
/// All of the identifiers (items) declared by this node.
/// This is an approximation and should only be used for diagnostics.
fn declared_idents(&self) -> Vec<Ident> {
vec![]
}
}

fn as_target(&self) -> Target;
macro_rules! declared_idents {
($($ty:ty),*) => {
$(impl DeclaredIdents for $ty {})*
};
}

// Use the default "empty" list of idents for the following:
declared_idents! {
AstNodeWrapper<Box<ast::AssocItem>, TraitItemTag>,
AstNodeWrapper<Box<ast::AssocItem>, ImplItemTag>,
AstNodeWrapper<Box<ast::AssocItem>, TraitImplItemTag>,
Box<ast::ForeignItem>,
ast::Variant,
ast::WherePredicate,
ast::FieldDef,
ast::PatField,
ast::ExprField,
ast::Param,
ast::GenericParam,
ast::Arm,
ast::Stmt,
ast::Crate,
ast::Ty,
ast::Pat,
ast::Expr,
AstNodeWrapper<Box<ast::Expr>, OptExprTag>,
AstNodeWrapper<ast::Expr, MethodReceiverTag>
}

impl InvocationCollectorNode for Box<ast::Item> {
Expand Down Expand Up @@ -1415,6 +1446,12 @@ impl InvocationCollectorNode for Box<ast::Item> {
res
}

fn as_target(&self) -> Target {
Target::from_ast_item(self)
}
}

impl DeclaredIdents for Box<ast::Item> {
fn declared_idents(&self) -> Vec<Ident> {
if let ItemKind::Use(ut) = &self.kind {
fn collect_use_tree_leaves(ut: &ast::UseTree, idents: &mut Vec<Ident>) {
Expand All @@ -1435,10 +1472,6 @@ impl InvocationCollectorNode for Box<ast::Item> {
self.kind.ident().into_iter().collect()
}
}

fn as_target(&self) -> Target {
Target::from_ast_item(self)
}
}

struct TraitItemTag;
Expand Down
11 changes: 10 additions & 1 deletion tests/ui/cfg/auxiliary/cfged_out.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,19 @@ pub mod inner {
pub fn uwu() {}

#[cfg(false)]
pub mod doesnt_exist {
pub mod cfgd_out {
pub fn hello() {}
}

cfg_select! {
false => {
pub mod selected_out {
pub fn hello() {}
}
}
_ => {}
}

pub mod wrong {
#[cfg(feature = "suggesting me fails the test!!")]
pub fn meow() {}
Expand Down
12 changes: 8 additions & 4 deletions tests/ui/cfg/diagnostics-cross-crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@ fn main() {
//~^ NOTE found an item that was configured out
//~| NOTE not found in `cfged_out::inner`

// The module isn't found - we would like to get a diagnostic, but currently don't due to
// the awkward way the resolver diagnostics are currently implemented.
cfged_out::inner::doesnt_exist::hello(); //~ ERROR cannot find
//~^ NOTE could not find `doesnt_exist` in `inner`
// The module isn't found - we mention that `cfgd_out` is `cfg`d out
cfged_out::inner::cfgd_out::hello(); //~ ERROR cannot find
//~^ NOTE could not find `cfgd_out` in `inner`
//~| NOTE found an item that was configured out

// The module isn't found - we mention that `selected_out` is `cfg_select`d out
cfged_out::inner::selected_out::hello(); //~ ERROR cannot find
//~^ NOTE could not find `selected_out` in `inner`
//~| NOTE found an item that was configured out

// It should find the one in the right module, not the wrong one.
Expand Down
36 changes: 25 additions & 11 deletions tests/ui/cfg/diagnostics-cross-crate.stderr
Original file line number Diff line number Diff line change
@@ -1,16 +1,30 @@
error[E0433]: cannot find `doesnt_exist` in `inner`
--> $DIR/diagnostics-cross-crate.rs:17:23
error[E0433]: cannot find `cfgd_out` in `inner`
--> $DIR/diagnostics-cross-crate.rs:16:23
|
LL | cfged_out::inner::doesnt_exist::hello();
| ^^^^^^^^^^^^ could not find `doesnt_exist` in `inner`
LL | cfged_out::inner::cfgd_out::hello();
| ^^^^^^^^ could not find `cfgd_out` in `inner`
|
note: found an item that was configured out
--> $DIR/auxiliary/cfged_out.rs:6:13
|
LL | #[cfg(false)]
| ----- the item is gated here
LL | pub mod doesnt_exist {
| ^^^^^^^^^^^^
LL | pub mod cfgd_out {
| ^^^^^^^^

error[E0433]: cannot find `selected_out` in `inner`
--> $DIR/diagnostics-cross-crate.rs:21:23
|
LL | cfged_out::inner::selected_out::hello();
| ^^^^^^^^^^^^ could not find `selected_out` in `inner`
|
note: found an item that was configured out
--> $DIR/auxiliary/cfged_out.rs:12:21
|
LL | false => {
| ----- the item is gated here
LL | pub mod selected_out {
| ^^^^^^^^^^^^

error[E0425]: cannot find function `uwu` in crate `cfged_out`
--> $DIR/diagnostics-cross-crate.rs:7:16
Expand All @@ -33,34 +47,34 @@ LL | pub fn uwu() {}
| ^^^

error[E0425]: cannot find function `meow` in module `cfged_out::inner::right`
--> $DIR/diagnostics-cross-crate.rs:22:30
--> $DIR/diagnostics-cross-crate.rs:26:30
|
LL | cfged_out::inner::right::meow();
| ^^^^ not found in `cfged_out::inner::right`
|
note: found an item that was configured out
--> $DIR/auxiliary/cfged_out.rs:17:16
--> $DIR/auxiliary/cfged_out.rs:26:16
|
LL | #[cfg(feature = "what-a-cool-feature")]
| ------------------------------- the item is gated behind the `what-a-cool-feature` feature
LL | pub fn meow() {}
| ^^^^

error[E0425]: cannot find function `vanished` in crate `cfged_out`
--> $DIR/diagnostics-cross-crate.rs:27:16
--> $DIR/diagnostics-cross-crate.rs:31:16
|
LL | cfged_out::vanished();
| ^^^^^^^^ not found in `cfged_out`
|
note: found an item that was configured out
--> $DIR/auxiliary/cfged_out.rs:22:8
--> $DIR/auxiliary/cfged_out.rs:31:8
|
LL | #[cfg(i_dont_exist_and_you_can_do_nothing_about_it)]
| -------------------------------------------- the item is gated here
LL | pub fn vanished() {}
| ^^^^^^^^

error: aborting due to 5 previous errors
error: aborting due to 6 previous errors

Some errors have detailed explanations: E0425, E0433.
For more information about an error, try `rustc --explain E0425`.
Loading
Loading