diff --git a/Cargo.lock b/Cargo.lock index 38a861727a9d3..ad32e67fdd7a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3584,6 +3584,7 @@ dependencies = [ "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", + "rustc_feature", "rustc_fluent_macro", "rustc_fs_util", "rustc_hir", @@ -4608,6 +4609,7 @@ dependencies = [ "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", + "rustc_feature", "rustc_fluent_macro", "rustc_hir", "rustc_infer", diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index faa2865cb9130..f89999d8764df 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -6,6 +6,10 @@ attr_parsing_deprecated_item_suggestion = .help = add `#![feature(deprecated_suggestion)]` to the crate root .note = see #94785 for more details +attr_parsing_expected_cfg_accessible_path = + expected cfg-accessible path, starting with `::`, and consisting of module names and item names + .note = cfg-accessible paths start with `::`, followed by zero, one or more (maybe reexported) module name identifiers each followed by `::` in the middle, and then followed by item name identifier at the end + attr_parsing_expected_one_cfg_pattern = expected 1 cfg-pattern @@ -21,6 +25,12 @@ attr_parsing_expects_feature_list = attr_parsing_expects_features = `{$name}` expects feature names +attr_parsing_incorrect_cfg_accessible_edition = + `cfg(accessible(..))` cannot be used in edition 2015 + +attr_parsing_incorrect_cfg_accessible_pos = + `cfg(accessible(..))` cannot be used as crate attribute + attr_parsing_incorrect_meta_item = incorrect meta item @@ -88,6 +98,9 @@ attr_parsing_multiple_stability_levels = attr_parsing_non_ident_feature = 'feature' is not an identifier +attr_parsing_nonexistent_cfg_accessible_crate = + `cfg(accessible(..))` mentioned external crate is not accessible + attr_parsing_rustc_allowed_unstable_pairing = `rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute @@ -107,6 +120,9 @@ attr_parsing_unknown_meta_item = attr_parsing_unknown_version_literal = unknown version literal format, assuming it refers to a future version +attr_parsing_unstable_cfg_accessible = + `cfg(accessible(..))` is experimental and subject to change + attr_parsing_unstable_cfg_target_compact = compact `cfg(target(..))` is experimental and subject to change diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index bb9aaaa2fea9e..a5100da07d945 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -23,42 +23,39 @@ pub struct Condition { pub span: Span, } -/// Tests if a cfg-pattern matches the cfg set -pub fn cfg_matches( - cfg: &ast::MetaItemInner, +pub fn eval_individual_cfg( + condition: Condition, sess: &Session, lint_node_id: NodeId, features: Option<&Features>, ) -> bool { - eval_condition(cfg, sess, features, &mut |cfg| { - try_gate_cfg(cfg.name, cfg.span, sess, features); - match sess.psess.check_config.expecteds.get(&cfg.name) { - Some(ExpectedValues::Some(values)) if !values.contains(&cfg.value) => { - sess.psess.buffer_lint( - UNEXPECTED_CFGS, - cfg.span, - lint_node_id, - BuiltinLintDiag::UnexpectedCfgValue( - (cfg.name, cfg.name_span), - cfg.value.map(|v| (v, cfg.value_span.unwrap())), - ), - ); - } - None if sess.psess.check_config.exhaustive_names => { - sess.psess.buffer_lint( - UNEXPECTED_CFGS, - cfg.span, - lint_node_id, - BuiltinLintDiag::UnexpectedCfgName( - (cfg.name, cfg.name_span), - cfg.value.map(|v| (v, cfg.value_span.unwrap())), - ), - ); - } - _ => { /* not unexpected */ } + try_gate_cfg(condition.name, condition.span, sess, features); + match sess.psess.check_config.expecteds.get(&condition.name) { + Some(ExpectedValues::Some(values)) if !values.contains(&condition.value) => { + sess.psess.buffer_lint( + UNEXPECTED_CFGS, + condition.span, + lint_node_id, + BuiltinLintDiag::UnexpectedCfgValue( + (condition.name, condition.name_span), + condition.value.map(|v| (v, condition.value_span.unwrap())), + ), + ); + } + None if sess.psess.check_config.exhaustive_names => { + sess.psess.buffer_lint( + UNEXPECTED_CFGS, + condition.span, + lint_node_id, + BuiltinLintDiag::UnexpectedCfgName( + (condition.name, condition.name_span), + condition.value.map(|v| (v, condition.value_span.unwrap())), + ), + ); } - sess.psess.config.contains(&(cfg.name, cfg.value)) - }) + _ => { /* not unexpected */ } + } + sess.psess.config.contains(&(condition.name, condition.value)) } fn try_gate_cfg(name: Symbol, span: Span, sess: &Session, features: Option<&Features>) { @@ -77,14 +74,33 @@ fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &Session, features: &Fea } } +pub trait CfgEval { + fn eval_individual(&mut self, condition: Condition, features: Option<&Features>) -> bool; + + fn eval_accessible_path( + &mut self, + target_crate: rustc_span::Ident, + target_path_segs: impl Iterator, + ) -> Option; +} + /// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to /// evaluate individual items. pub fn eval_condition( cfg: &ast::MetaItemInner, sess: &Session, features: Option<&Features>, - eval: &mut impl FnMut(Condition) -> bool, + eval: &mut impl CfgEval, ) -> bool { + eval_condition_inner(cfg, sess, features, eval).unwrap_or_default() +} + +fn eval_condition_inner( + cfg: &ast::MetaItemInner, + sess: &Session, + features: Option<&Features>, + eval: &mut impl CfgEval, +) -> Option { let dcx = sess.dcx(); let cfg = match cfg { @@ -104,7 +120,7 @@ pub fn eval_condition( features, ); } - return *b; + return Some(*b); } _ => { dcx.emit_err(session_diagnostics::UnsupportedLiteral { @@ -113,7 +129,7 @@ pub fn eval_condition( is_bytestr: false, start_point_span: sess.source_map().start_point(cfg.span()), }); - return false; + return None; } }; @@ -129,25 +145,25 @@ pub fn eval_condition( | MetaItemInner::MetaItem(MetaItem { span, .. }), ] => { dcx.emit_err(session_diagnostics::ExpectedVersionLiteral { span: *span }); - return false; + return None; } [..] => { dcx.emit_err(session_diagnostics::ExpectedSingleVersionLiteral { span: cfg.span, }); - return false; + return None; } }; let Some(min_version) = parse_version(*min_version) else { dcx.emit_warn(session_diagnostics::UnknownVersionLiteral { span: *span }); - return false; + return None; }; // See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details if sess.psess.assume_incomplete_release { - RustcVersion::CURRENT > min_version + Some(RustcVersion::CURRENT > min_version) } else { - RustcVersion::CURRENT >= min_version + Some(RustcVersion::CURRENT >= min_version) } } ast::MetaItemKind::List(mis) => { @@ -159,7 +175,7 @@ pub fn eval_condition( is_bytestr: false, start_point_span: sess.source_map().start_point(mi.span()), }); - return false; + return None; } } @@ -170,19 +186,23 @@ pub fn eval_condition( .iter() // We don't use any() here, because we want to evaluate all cfg condition // as eval_condition can (and does) extra checks - .fold(false, |res, mi| res | eval_condition(mi, sess, features, eval)), + .fold(Some(false), |res, mi| { + Some(res? | eval_condition_inner(mi, sess, features, eval)?) + }), sym::all => mis .iter() // We don't use all() here, because we want to evaluate all cfg condition // as eval_condition can (and does) extra checks - .fold(true, |res, mi| res & eval_condition(mi, sess, features, eval)), + .fold(Some(true), |res, mi| { + Some(res? & eval_condition_inner(mi, sess, features, eval)?) + }), sym::not => { let [mi] = mis.as_slice() else { dcx.emit_err(session_diagnostics::ExpectedOneCfgPattern { span: cfg.span }); - return false; + return None; }; - !eval_condition(mi, sess, features, eval) + Some(!eval_condition_inner(mi, sess, features, eval)?) } sym::target => { if let Some(features) = features @@ -197,38 +217,103 @@ pub fn eval_condition( .emit(); } - mis.iter().fold(true, |res, mi| { + mis.iter().fold(Some(true), |res, mi| { let Some(mut mi) = mi.meta_item().cloned() else { dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { span: mi.span(), }); - return false; + return None; }; if let [seg, ..] = &mut mi.path.segments[..] { seg.ident.name = Symbol::intern(&format!("target_{}", seg.ident.name)); } - res & eval_condition( - &ast::MetaItemInner::MetaItem(mi), - sess, - features, - eval, + Some( + res? & eval_condition_inner( + &ast::MetaItemInner::MetaItem(mi), + sess, + features, + eval, + )?, ) }) } + sym::accessible => { + if let Some(features) = features + && !features.cfg_accessible() + { + feature_err( + sess, + sym::cfg_accessible, + cfg.span, + fluent_generated::attr_parsing_unstable_cfg_accessible, + ) + .emit(); + } + + let [ + MetaItemInner::MetaItem(MetaItem { + path: accessible_path, + kind: MetaItemKind::Word, + .. + }), + ] = mis.as_slice() + else { + dcx.emit_err(session_diagnostics::ExpectedCfgAccessiblePath { + span: cfg.span, + details: (), + }); + return None; + }; + + if !accessible_path.span.at_least_rust_2018() { + dcx.emit_err(session_diagnostics::IncorrectCfgAccessibleEdition { + span: accessible_path.span, + }); + return None; + } + + if !(accessible_path.segments.len() >= 3 + && accessible_path + .segments + .first() + .is_some_and(|segment| segment.ident.name == kw::PathRoot) + && accessible_path + .segments + .iter() + .skip(1) + .all(|path_seg| path_seg.args.is_none())) + { + dcx.emit_err(session_diagnostics::ExpectedCfgAccessiblePath { + span: cfg.span, + details: (), + }); + return None; + } + + let Some(accessible) = eval.eval_accessible_path( + accessible_path.segments.iter().nth(1).unwrap().ident, + accessible_path.segments.iter().skip(2).map(|path_seg| path_seg.ident), + ) else { + // error already emitted + return None; + }; + + Some(accessible) + } _ => { dcx.emit_err(session_diagnostics::InvalidPredicate { span: cfg.span, predicate: pprust::path_to_string(&cfg.path), }); - false + None } } } ast::MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => { dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { span: cfg.path.span }); - true + None } MetaItemKind::NameValue(lit) if !lit.kind.is_str() => { dcx.emit_err(session_diagnostics::UnsupportedLiteral { @@ -237,17 +322,20 @@ pub fn eval_condition( is_bytestr: lit.kind.is_bytestr(), start_point_span: sess.source_map().start_point(lit.span), }); - true + None } ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => { let ident = cfg.ident().expect("multi-segment cfg predicate"); - eval(Condition { - name: ident.name, - name_span: ident.span, - value: cfg.value_str(), - value_span: cfg.name_value_literal_span(), - span: cfg.span, - }) + Some(eval.eval_individual( + Condition { + name: ident.name, + name_span: ident.span, + value: cfg.value_str(), + value_span: cfg.name_value_literal_span(), + span: cfg.span, + }, + features, + )) } } } diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index a1264a6875f69..ae62d297ecc3c 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -13,7 +13,7 @@ // tidy-alphabetical-end mod attributes; -mod session_diagnostics; +pub mod session_diagnostics; pub use attributes::*; pub use rustc_attr_data_structures::*; diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 92bc2a8aeb05e..5786f88d3024f 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -16,6 +16,37 @@ pub(crate) struct ExpectedOneCfgPattern { pub span: Span, } +#[derive(Diagnostic)] +#[diag(attr_parsing_expected_cfg_accessible_path)] +pub(crate) struct ExpectedCfgAccessiblePath { + #[primary_span] + pub span: Span, + + #[note] + pub details: (), +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_incorrect_cfg_accessible_pos)] +pub struct IncorrectCfgAccessiblePos { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_nonexistent_cfg_accessible_crate)] +pub struct NonexistentCfgAccessibleCrate { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_incorrect_cfg_accessible_edition)] +pub(crate) struct IncorrectCfgAccessibleEdition { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(attr_parsing_invalid_predicate, code = E0537)] pub(crate) struct InvalidPredicate { diff --git a/compiler/rustc_builtin_macros/src/cfg.rs b/compiler/rustc_builtin_macros/src/cfg.rs index 85b8ef79c0504..a2455a1842344 100644 --- a/compiler/rustc_builtin_macros/src/cfg.rs +++ b/compiler/rustc_builtin_macros/src/cfg.rs @@ -6,6 +6,7 @@ use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; use rustc_errors::PResult; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult}; +use rustc_expand::config::StripUnconfiguredCfgEval; use rustc_parse::exp; use rustc_span::Span; use {rustc_ast as ast, rustc_attr_parsing as attr}; @@ -21,11 +22,15 @@ pub(crate) fn expand_cfg( ExpandResult::Ready(match parse_cfg(cx, sp, tts) { Ok(cfg) => { - let matches_cfg = attr::cfg_matches( + let matches_cfg = attr::eval_condition( &cfg, &cx.sess, - cx.current_expansion.lint_node_id, Some(cx.ecfg.features), + &mut StripUnconfiguredCfgEval::new( + &cx.sess, + Some(cx.resolver), + cx.current_expansion.lint_node_id, + ), ); MacEager::expr(cx.expr_bool(sp, matches_cfg)) } diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index 53c61831b4229..89e107c0af20c 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -6,7 +6,7 @@ use rustc_ast::ptr::P; use rustc_ast::visit::{AssocCtxt, Visitor}; use rustc_ast::{Attribute, HasAttrs, HasTokens, NodeId, mut_visit, visit}; use rustc_errors::PResult; -use rustc_expand::base::{Annotatable, ExtCtxt}; +use rustc_expand::base::{Annotatable, ExtCtxt, ResolverExpand}; use rustc_expand::config::StripUnconfigured; use rustc_expand::configure; use rustc_feature::Features; @@ -26,7 +26,13 @@ pub(crate) fn expand( ) -> Vec { check_builtin_macro_attribute(ecx, meta_item, sym::cfg_eval); warn_on_duplicate_attribute(ecx, &annotatable, sym::cfg_eval); - vec![cfg_eval(ecx.sess, ecx.ecfg.features, annotatable, ecx.current_expansion.lint_node_id)] + vec![cfg_eval( + ecx.sess, + ecx.ecfg.features, + annotatable, + ecx.current_expansion.lint_node_id, + ecx.resolver, + )] } pub(crate) fn cfg_eval( @@ -34,10 +40,17 @@ pub(crate) fn cfg_eval( features: &Features, annotatable: Annotatable, lint_node_id: NodeId, + resolver: &mut dyn ResolverExpand, ) -> Annotatable { let features = Some(features); - CfgEval(StripUnconfigured { sess, features, config_tokens: true, lint_node_id }) - .configure_annotatable(annotatable) + CfgEval(StripUnconfigured { + sess, + features, + config_tokens: true, + lint_node_id, + resolver: Some(resolver), + }) + .configure_annotatable(annotatable) } struct CfgEval<'a>(StripUnconfigured<'a>); diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs index 2653a9f48b9b6..0f8765355cbb4 100644 --- a/compiler/rustc_builtin_macros/src/derive.rs +++ b/compiler/rustc_builtin_macros/src/derive.rs @@ -32,8 +32,10 @@ impl MultiItemModifier for Expander { } let (sess, features) = (ecx.sess, ecx.ecfg.features); - let result = - ecx.resolver.resolve_derives(ecx.current_expansion.id, ecx.force_mode, &|| { + let result = ecx.resolver.resolve_derives( + ecx.current_expansion.id, + ecx.force_mode, + &mut |resolver| { let template = AttributeTemplate { list: Some("Trait1, Trait2, ..."), ..Default::default() }; validate_attr::check_builtin_meta_item( @@ -82,6 +84,7 @@ impl MultiItemModifier for Expander { features, item.clone(), ecx.current_expansion.lint_node_id, + resolver, ); for other in others { other.item = first.item.clone(); @@ -90,7 +93,8 @@ impl MultiItemModifier for Expander { } resolutions - }); + }, + ); match result { Ok(()) => ExpandResult::Ready(vec![item]), diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 2afc1efc1b34d..83511975b64fc 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -23,6 +23,7 @@ rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } +rustc_feature = { path = "../rustc_feature" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_fs_util = { path = "../rustc_fs_util" } rustc_hir = { path = "../rustc_hir" } diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 105a4cb81f0d1..4aa30856a25cd 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -3018,8 +3018,40 @@ fn add_dynamic_crate(cmd: &mut dyn Linker, sess: &Session, cratepath: &Path) { } fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool { + struct Eval<'a> { + sess: &'a Session, + lint_node_id: rustc_ast::NodeId, + } + impl rustc_attr_parsing::CfgEval for Eval<'_> { + fn eval_individual( + &mut self, + condition: rustc_attr_parsing::Condition, + features: Option<&rustc_feature::Features>, + ) -> bool { + rustc_attr_parsing::eval_individual_cfg( + condition, + self.sess, + self.lint_node_id, + features, + ) + } + + fn eval_accessible_path( + &mut self, + _target_crate: rustc_span::Ident, + _target_path_segs: impl Iterator, + ) -> Option { + // FIXME: this seems incorrect? + Some(true) + } + } match lib.cfg { - Some(ref cfg) => rustc_attr_parsing::cfg_matches(cfg, sess, CRATE_NODE_ID, None), + Some(ref cfg) => rustc_attr_parsing::eval_condition( + cfg, + sess, + None, + &mut Eval { sess, lint_node_id: CRATE_NODE_ID }, + ), None => true, } } diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 819694d1cdc1f..2c76b0e47cc32 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1032,7 +1032,7 @@ pub trait ResolverExpand { &mut self, expn_id: LocalExpnId, force: bool, - derive_paths: &dyn Fn() -> Vec, + derive_paths: &mut dyn FnMut(&mut dyn ResolverExpand) -> Vec, ) -> Result<(), Indeterminate>; /// Take resolutions for paths inside the `#[derive(...)]` attribute with the given `ExpnId` /// back from resolver. @@ -1048,6 +1048,14 @@ pub trait ResolverExpand { expn_id: LocalExpnId, path: &ast::Path, ) -> Result; + fn cfg_accessible_crate(&mut self, crate_name: Ident) -> Result; + fn cfg_accessible_mod( + &mut self, + parent: DefId, + mod_name: Ident, + ) -> Result; + fn cfg_accessible_item(&mut self, parent: DefId, item_name: Ident) + -> Result<(), Indeterminate>; /// Decodes the proc-macro quoted span in the specified crate, with the specified id. /// No caching is performed. diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 83255b820178f..0f5ee805a30dc 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -5,9 +5,7 @@ use rustc_ast::token::{Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::{ AttrTokenStream, AttrTokenTree, LazyAttrTokenStream, Spacing, TokenTree, }; -use rustc_ast::{ - self as ast, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem, MetaItemInner, NodeId, -}; +use rustc_ast::{self as ast, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem, MetaItemInner}; use rustc_attr_parsing as attr; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_feature::{ @@ -32,11 +30,12 @@ use crate::errors::{ pub struct StripUnconfigured<'a> { pub sess: &'a Session, pub features: Option<&'a Features>, + pub resolver: Option<&'a mut dyn crate::base::ResolverExpand>, + pub lint_node_id: rustc_ast::NodeId, /// If `true`, perform cfg-stripping on attached tokens. /// This is only used for the input to derive macros, /// which needs eager expansion of `cfg` and `cfg_attr` pub config_tokens: bool, - pub lint_node_id: NodeId, } pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -> Features { @@ -140,17 +139,24 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) - } pub fn pre_configure_attrs(sess: &Session, attrs: &[Attribute]) -> ast::AttrVec { - let strip_unconfigured = StripUnconfigured { + let mut strip_unconfigured = StripUnconfigured { sess, features: None, config_tokens: false, + resolver: None, lint_node_id: ast::CRATE_NODE_ID, }; - attrs - .iter() - .flat_map(|attr| strip_unconfigured.process_cfg_attr(attr)) - .take_while(|attr| !is_cfg(attr) || strip_unconfigured.cfg_true(attr).0) - .collect() + let mut output = ast::AttrVec::default(); + 'process: for attr in attrs { + for attr in strip_unconfigured.process_cfg_attr(attr) { + if !is_cfg(&attr) || strip_unconfigured.cfg_true(&attr).0 { + output.push(attr); + continue; + } + break 'process; + } + } + output } #[macro_export] @@ -163,8 +169,27 @@ macro_rules! configure { }; } +macro_rules! as_deref_mut_reborrow_macro { + ($v:expr) => { + match $v { + Some(v) => Some(&mut **v), + None => None, + } + }; +} + +/* +// FIXME: This doesn't work if extracted as a function +fn as_deref_mut_reborrow<'a, 'b: 'a, 'c: 'b, T: ?Sized>(v: &'b mut Option<&'c mut T>) -> Option<&'a mut T> { + match v { + Some(v) => Some(&mut **v), + None => None, + } +} +*/ + impl<'a> StripUnconfigured<'a> { - pub fn configure(&self, mut node: T) -> Option { + pub fn configure(&mut self, mut node: T) -> Option { self.process_cfg_attrs(&mut node); self.in_cfg(node.attrs()).then(|| { self.try_configure_tokens(&mut node); @@ -172,7 +197,7 @@ impl<'a> StripUnconfigured<'a> { }) } - fn try_configure_tokens(&self, node: &mut T) { + fn try_configure_tokens(&mut self, node: &mut T) { if self.config_tokens { if let Some(Some(tokens)) = node.tokens_mut() { let attr_stream = tokens.to_attr_token_stream(); @@ -185,7 +210,7 @@ impl<'a> StripUnconfigured<'a> { /// This is only used during the invocation of `derive` proc-macros, /// which require that we cfg-expand their entire input. /// Normal cfg-expansion operates on parsed AST nodes via the `configure` method - fn configure_tokens(&self, stream: &AttrTokenStream) -> AttrTokenStream { + fn configure_tokens(&mut self, stream: &AttrTokenStream) -> AttrTokenStream { fn can_skip(stream: &AttrTokenStream) -> bool { stream.0.iter().all(|tree| match tree { AttrTokenTree::AttrsTarget(_) => false, @@ -251,13 +276,13 @@ impl<'a> StripUnconfigured<'a> { /// Gives compiler warnings if any `cfg_attr` does not contain any /// attributes and is in the original source code. Gives compiler errors if /// the syntax of any `cfg_attr` is incorrect. - fn process_cfg_attrs(&self, node: &mut T) { + fn process_cfg_attrs(&mut self, node: &mut T) { node.visit_attrs(|attrs| { attrs.flat_map_in_place(|attr| self.process_cfg_attr(&attr)); }); } - fn process_cfg_attr(&self, attr: &Attribute) -> Vec { + fn process_cfg_attr(&mut self, attr: &Attribute) -> Vec { if attr.has_name(sym::cfg_attr) { self.expand_cfg_attr(attr, true) } else { @@ -272,7 +297,11 @@ impl<'a> StripUnconfigured<'a> { /// Gives a compiler warning when the `cfg_attr` contains no attributes and /// is in the original source file. Gives a compiler error if the syntax of /// the attribute is incorrect. - pub(crate) fn expand_cfg_attr(&self, cfg_attr: &Attribute, recursive: bool) -> Vec { + pub(crate) fn expand_cfg_attr( + &mut self, + cfg_attr: &Attribute, + recursive: bool, + ) -> Vec { validate_attr::check_attribute_safety(&self.sess.psess, AttributeSafety::Normal, &cfg_attr); let Some((cfg_predicate, expanded_attrs)) = @@ -291,7 +320,16 @@ impl<'a> StripUnconfigured<'a> { ); } - if !attr::cfg_matches(&cfg_predicate, &self.sess, self.lint_node_id, self.features) { + if !attr::eval_condition( + &cfg_predicate, + &self.sess, + self.features, + &mut StripUnconfiguredCfgEval { + sess: &self.sess, + lint_node_id: self.lint_node_id, + resolver: as_deref_mut_reborrow_macro!(&mut self.resolver), + }, + ) { return vec![]; } @@ -375,11 +413,11 @@ 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 { + fn in_cfg(&mut self, attrs: &[Attribute]) -> bool { attrs.iter().all(|attr| !is_cfg(attr) || self.cfg_true(attr).0) } - pub(crate) fn cfg_true(&self, attr: &Attribute) -> (bool, Option) { + pub(crate) fn cfg_true(&mut self, attr: &Attribute) -> (bool, Option) { let meta_item = match validate_attr::parse_meta(&self.sess.psess, attr) { Ok(meta_item) => meta_item, Err(err) => { @@ -392,7 +430,16 @@ impl<'a> StripUnconfigured<'a> { ( parse_cfg(&meta_item, self.sess).is_none_or(|meta_item| { - attr::cfg_matches(meta_item, &self.sess, self.lint_node_id, self.features) + attr::eval_condition( + meta_item, + &self.sess, + self.features, + &mut StripUnconfiguredCfgEval { + sess: &self.sess, + lint_node_id: self.lint_node_id, + resolver: as_deref_mut_reborrow_macro!(&mut self.resolver), + }, + ) }), Some(meta_item), ) @@ -424,7 +471,7 @@ impl<'a> StripUnconfigured<'a> { } #[instrument(level = "trace", skip(self))] - pub fn configure_expr(&self, expr: &mut P, method_receiver: bool) { + pub fn configure_expr(&mut self, expr: &mut P, method_receiver: bool) { if !method_receiver { for attr in expr.attrs.iter() { self.maybe_emit_expr_attr_err(attr); @@ -447,6 +494,65 @@ impl<'a> StripUnconfigured<'a> { } } +pub struct StripUnconfiguredCfgEval<'a, 'b> { + sess: &'a Session, + #[allow(dead_code)] + resolver: Option<&'b mut dyn crate::base::ResolverExpand>, + lint_node_id: rustc_ast::NodeId, +} + +impl<'a, 'b> StripUnconfiguredCfgEval<'a, 'b> { + pub fn new( + sess: &'a Session, + resolver: Option<&'b mut dyn crate::base::ResolverExpand>, + lint_node_id: rustc_ast::NodeId, + ) -> Self { + StripUnconfiguredCfgEval { sess, resolver, lint_node_id } + } +} + +impl attr::CfgEval for StripUnconfiguredCfgEval<'_, '_> { + fn eval_individual(&mut self, condition: attr::Condition, features: Option<&Features>) -> bool { + attr::eval_individual_cfg(condition, self.sess, self.lint_node_id, features) + } + + fn eval_accessible_path( + &mut self, + target_crate: rustc_span::Ident, + target_path_segs: impl Iterator, + ) -> Option { + let Some(resolver) = &mut self.resolver else { + self.sess.dcx().emit_err(attr::session_diagnostics::IncorrectCfgAccessiblePos { + span: target_crate.span, + }); + return None; + }; + + let Ok(mut cur_mod_def) = resolver.cfg_accessible_crate(target_crate) else { + self.sess.dcx().emit_err(attr::session_diagnostics::NonexistentCfgAccessibleCrate { + span: target_crate.span, + }); + return None; + }; + let mut target_path_segs = target_path_segs.peekable(); + while let Some(next_path_seg) = target_path_segs.next() { + let is_mod = target_path_segs.peek().is_some(); + if is_mod { + let Ok(mod_def) = resolver.cfg_accessible_mod(cur_mod_def, next_path_seg) else { + return Some(false); + }; + cur_mod_def = mod_def; + continue; + } + let Ok(()) = resolver.cfg_accessible_item(cur_mod_def, next_path_seg) else { + return Some(false); + }; + return Some(true); + } + unreachable!() + } +} + pub fn parse_cfg<'a>(meta_item: &'a MetaItem, sess: &Session) -> Option<&'a MetaItemInner> { let span = meta_item.span; match meta_item.meta_item_list() { diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index e3f31ebeca3f3..5fa8a72f0cc06 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1779,11 +1779,12 @@ struct InvocationCollector<'a, 'b> { } impl<'a, 'b> InvocationCollector<'a, 'b> { - fn cfg(&self) -> StripUnconfigured<'_> { + fn cfg(&mut self) -> StripUnconfigured<'_> { StripUnconfigured { sess: self.cx.sess, features: Some(self.cx.ecfg.features), config_tokens: false, + resolver: Some(self.cx.resolver), lint_node_id: self.cx.current_expansion.lint_node_id, } } @@ -1950,7 +1951,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { (res, meta_item) } - fn expand_cfg_attr(&self, node: &mut impl HasAttrs, attr: &ast::Attribute, pos: usize) { + fn expand_cfg_attr(&mut self, node: &mut impl HasAttrs, attr: &ast::Attribute, pos: usize) { node.visit_attrs(|attrs| { // Repeated `insert` calls is inefficient, but the number of // insertions is almost always 0 or 1 in practice. diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index a638a845c07ea..a0573613f69f0 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -401,6 +401,8 @@ declare_features! ( (unstable, async_trait_bounds, "1.85.0", Some(62290)), /// Allows using C-variadics. (unstable, c_variadic, "1.34.0", Some(44930)), + /// Allows conditional compilation depending on whether certain external path is accessible + (unstable, cfg_accessible, "CURRENT_RUSTC_VERSION", Some(64797)), /// Allows the use of `#[cfg()]`. (unstable, cfg_boolean_literals, "1.83.0", Some(131204)), /// Allows the use of `#[cfg(contract_checks)` to check if contract checks are enabled. @@ -423,7 +425,7 @@ declare_features! ( (unstable, cfg_target_thread_local, "1.7.0", Some(29594)), /// Allows the use of `#[cfg(ub_checks)` to check if UB checks are enabled. (unstable, cfg_ub_checks, "1.79.0", Some(123499)), - /// Allow conditional compilation depending on rust version + /// Allows conditional compilation depending on rust version (unstable, cfg_version, "1.45.0", Some(64796)), /// Allows to use the `#[cfi_encoding = ""]` attribute. (unstable, cfi_encoding, "1.71.0", Some(89653)), diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 2a1e4b261e737..5d3eb51820b8f 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -189,8 +189,35 @@ pub(crate) fn collect(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> Vec } pub(crate) fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool { + struct Eval<'a> { + sess: &'a Session, + lint_node_id: rustc_ast::NodeId, + } + impl attr::CfgEval for Eval<'_> { + fn eval_individual( + &mut self, + condition: attr::Condition, + features: Option<&rustc_feature::Features>, + ) -> bool { + attr::eval_individual_cfg(condition, self.sess, self.lint_node_id, features) + } + + fn eval_accessible_path( + &mut self, + _target_crate: rustc_span::Ident, + _target_path_segs: impl Iterator, + ) -> Option { + // FIXME: this seems incorrect? + Some(true) + } + } match lib.cfg { - Some(ref cfg) => attr::cfg_matches(cfg, sess, CRATE_NODE_ID, None), + Some(ref cfg) => rustc_attr_parsing::eval_condition( + cfg, + sess, + None, + &mut Eval { sess, lint_node_id: CRATE_NODE_ID }, + ), None => true, } } diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index cca01a01e9877..5a16213b12716 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -361,7 +361,9 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { &mut self, expn_id: LocalExpnId, force: bool, - derive_paths: &dyn Fn() -> Vec, + derive_paths: &mut dyn FnMut( + &mut dyn rustc_expand::base::ResolverExpand, + ) -> Vec, ) -> Result<(), Indeterminate> { // Block expansion of the container until we resolve all derives in it. // This is required for two reasons: @@ -372,7 +374,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { // Temporarily take the data to avoid borrow checker conflicts. let mut derive_data = mem::take(&mut self.derive_data); let entry = derive_data.entry(expn_id).or_insert_with(|| DeriveData { - resolutions: derive_paths(), + resolutions: derive_paths(self), helper_attrs: Vec::new(), has_derive_copy: false, }); @@ -458,6 +460,82 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { self.path_accessible(expn_id, path, &[MacroNS]) } + fn cfg_accessible_crate(&mut self, crate_name: Ident) -> Result { + let this = self; + let finalize = false; + let Some(binding) = this.extern_prelude_get(crate_name, finalize) else { + return Err(Indeterminate); + }; + let Some(module) = binding.module() else { + return Err(Indeterminate); + }; + Ok(module.def_id()) + } + fn cfg_accessible_mod( + &mut self, + parent: DefId, + mod_name: Ident, + ) -> Result { + let this = self; + let parent = this.expect_module(parent); + let parent_scope = ParentScope::module(parent, this); + let finalize = None; + let ignore_binding = None; + let ignore_import = None; + let ns = Namespace::TypeNS; + let Ok(binding) = this.resolve_ident_in_module( + ModuleOrUniformRoot::Module(parent), + mod_name, + ns, + &parent_scope, + finalize, + ignore_binding, + ignore_import, + ) else { + return Err(Indeterminate); + }; + if !binding.vis.is_public() { + return Err(Indeterminate); + } + let Some(module) = binding.module() else { + return Err(Indeterminate); + }; + if !module.is_normal() { + return Err(Indeterminate); + } + Ok(module.def_id()) + } + fn cfg_accessible_item( + &mut self, + parent: DefId, + item_name: Ident, + ) -> Result<(), Indeterminate> { + let this = self; + let parent = this.expect_module(parent); + let parent_scope = ParentScope::module(parent, this); + let finalize = None; + let ignore_binding = None; + let ignore_import = None; + for ns in [Namespace::TypeNS, Namespace::ValueNS, Namespace::MacroNS] { + let Ok(binding) = this.resolve_ident_in_module( + ModuleOrUniformRoot::Module(parent), + item_name, + ns, + &parent_scope, + finalize, + ignore_binding, + ignore_import, + ) else { + continue; + }; + if !binding.vis.is_public() { + continue; + } + return Ok(()); + } + return Err(Indeterminate); + } + fn get_proc_macro_quoted_span(&self, krate: CrateNum, id: usize) -> Span { self.cstore().get_proc_macro_quoted_span_untracked(krate, id, self.tcx.sess) } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 62723e385cf64..6ffa69384b85e 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -394,6 +394,7 @@ symbols! { abi_vectorcall, abi_x86_interrupt, abort, + accessible, add, add_assign, add_with_overflow, diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml index b13a753c4ed1e..523d3bb1bd0cf 100644 --- a/compiler/rustc_trait_selection/Cargo.toml +++ b/compiler/rustc_trait_selection/Cargo.toml @@ -12,6 +12,7 @@ rustc_ast_ir = { path = "../rustc_ast_ir" } rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } +rustc_feature = { path = "../rustc_feature" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } rustc_infer = { path = "../rustc_infer" } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index 518323f6526a6..3496c3b68aadf 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -426,14 +426,60 @@ impl<'tcx> OnUnimplementedDirective { .ok_or_else(|| tcx.dcx().emit_err(EmptyOnClauseInOnUnimplemented { span }))? .meta_item_or_bool() .ok_or_else(|| tcx.dcx().emit_err(InvalidOnClauseInOnUnimplemented { span }))?; - attr::eval_condition(cond, &tcx.sess, Some(tcx.features()), &mut |cfg| { - if let Some(value) = cfg.value - && let Err(guar) = parse_value(value, cfg.span) - { - errored = Some(guar); + + struct CollectErrored<'tcx, 'a> { + errored: &'a mut Option, + tcx: TyCtxt<'tcx>, + item_def_id: DefId, + is_diagnostic_namespace_variant: bool, + } + + impl attr::CfgEval for CollectErrored<'_, '_> { + fn eval_individual( + &mut self, + condition: attr::Condition, + _features: Option<&rustc_feature::Features>, + ) -> bool { + let parse_value = |value_str, value_span| { + OnUnimplementedFormatString::try_parse( + self.tcx, + self.item_def_id, + value_str, + value_span, + self.is_diagnostic_namespace_variant, + ) + .map(Some) + }; + + if let Some(value) = condition.value + && let Err(guar) = parse_value(value, condition.span) + { + *self.errored = Some(guar); + } + true } - true - }); + + fn eval_accessible_path( + &mut self, + _target_crate: rustc_span::Ident, + _target_path_segs: impl Iterator, + ) -> Option { + // FIXME: Is this correct? + Some(true) + } + } + + attr::eval_condition( + cond, + &tcx.sess, + Some(tcx.features()), + &mut CollectErrored { + errored: &mut errored, + tcx, + item_def_id, + is_diagnostic_namespace_variant, + }, + ); Some(cond.clone()) }; @@ -708,30 +754,66 @@ impl<'tcx> OnUnimplementedDirective { let options_map: FxHashMap = options.iter().filter_map(|(k, v)| v.clone().map(|v| (*k, v))).collect(); + struct EvalIndividual<'tcx, 'a> { + tcx: TyCtxt<'tcx>, + trait_ref: ty::TraitRef<'tcx>, + options: &'a [(Symbol, Option)], + long_ty_file: &'a mut Option, + options_map: &'a FxHashMap, + } + + impl attr::CfgEval for EvalIndividual<'_, '_> { + fn eval_individual( + &mut self, + condition: attr::Condition, + _features: Option<&rustc_feature::Features>, + ) -> bool { + let value = condition.value.map(|v| { + // `with_no_visible_paths` is also used when generating the options, + // so we need to match it here. + ty::print::with_no_visible_paths!( + OnUnimplementedFormatString { + symbol: v, + span: condition.span, + is_diagnostic_namespace_variant: false + } + .format( + self.tcx, + self.trait_ref, + self.options_map, + self.long_ty_file + ) + ) + }); + + self.options.contains(&(condition.name, value)) + } + + fn eval_accessible_path( + &mut self, + _target_crate: rustc_span::Ident, + _target_path_segs: impl Iterator, + ) -> Option { + // FIXME: Is this correct? + Some(true) + } + } + for command in self.subcommands.iter().chain(Some(self)).rev() { debug!(?command); if let Some(ref condition) = command.condition - && !attr::eval_condition(condition, &tcx.sess, Some(tcx.features()), &mut |cfg| { - let value = cfg.value.map(|v| { - // `with_no_visible_paths` is also used when generating the options, - // so we need to match it here. - ty::print::with_no_visible_paths!( - OnUnimplementedFormatString { - symbol: v, - span: cfg.span, - is_diagnostic_namespace_variant: false - } - .format( - tcx, - trait_ref, - &options_map, - long_ty_file - ) - ) - }); - - options.contains(&(cfg.name, value)) - }) + && !attr::eval_condition( + condition, + &tcx.sess, + Some(tcx.features()), + &mut EvalIndividual { + tcx, + trait_ref, + options, + long_ty_file, + options_map: &options_map, + }, + ) { debug!("evaluate: skipping {:?} due to condition", command); continue; diff --git a/tests/ui/cfg/cfg-accessible-1.rs b/tests/ui/cfg/cfg-accessible-1.rs new file mode 100644 index 0000000000000..287df763efbdd --- /dev/null +++ b/tests/ui/cfg/cfg-accessible-1.rs @@ -0,0 +1,27 @@ +//@ edition: 2018 +#![feature(cfg_accessible)] + +#[cfg(accessible())] //~ ERROR: expected cfg-accessible path +fn foo() {} +#[cfg(accessible(42))] //~ ERROR: unsupported literal +fn foo() -> bool { true } +#[cfg(accessible(::std::boxed::Box))] +fn foo() -> bool { true } +#[cfg(not(accessible(::std::boxed::Box)))] +fn foo() -> bool { false } +#[cfg(accessible(::std::nonexistent::item, ::nonexistent2::item))] //~ ERROR: expected cfg-accessible path +fn bar() -> bool { false } +#[cfg(not(accessible(::std::nonexistent::item)))] +fn bar() -> bool { true } +#[cfg(accessible(::nonexistent_crate::item))] //~ ERROR: `cfg(accessible(..))` mentioned external crate is not accessible +fn baz() -> bool { false } +#[cfg(not(accessible(::nonexistent_crate::item)))] //~ ERROR: `cfg(accessible(..))` mentioned external crate is not accessible +fn baz() -> bool { true } + +fn main() { + assert!(foo()); + assert!(bar()); + baz(); //~ ERROR cannot find function + assert!(cfg!(accessible(::std::boxed::Box))); + assert!(!cfg!(accessible(::nonexistent::item))); //~ ERROR: `cfg(accessible(..))` mentioned external crate is not accessible +} diff --git a/tests/ui/cfg/cfg-accessible-1.stderr b/tests/ui/cfg/cfg-accessible-1.stderr new file mode 100644 index 0000000000000..2e4941c9d8dbe --- /dev/null +++ b/tests/ui/cfg/cfg-accessible-1.stderr @@ -0,0 +1,53 @@ +error: expected cfg-accessible path, starting with `::`, and consisting of module names and item names + --> $DIR/cfg-accessible-1.rs:4:7 + | +LL | #[cfg(accessible())] + | ^^^^^^^^^^^^ + | + = note: cfg-accessible paths start with `::`, followed by zero, one or more (maybe reexported) module name identifiers each followed by `::` in the middle, and then followed by item name identifier at the end + +error[E0565]: unsupported literal + --> $DIR/cfg-accessible-1.rs:6:18 + | +LL | #[cfg(accessible(42))] + | ^^ + +error: expected cfg-accessible path, starting with `::`, and consisting of module names and item names + --> $DIR/cfg-accessible-1.rs:12:7 + | +LL | #[cfg(accessible(::std::nonexistent::item, ::nonexistent2::item))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: cfg-accessible paths start with `::`, followed by zero, one or more (maybe reexported) module name identifiers each followed by `::` in the middle, and then followed by item name identifier at the end + +error: `cfg(accessible(..))` mentioned external crate is not accessible + --> $DIR/cfg-accessible-1.rs:16:20 + | +LL | #[cfg(accessible(::nonexistent_crate::item))] + | ^^^^^^^^^^^^^^^^^ + +error: `cfg(accessible(..))` mentioned external crate is not accessible + --> $DIR/cfg-accessible-1.rs:18:24 + | +LL | #[cfg(not(accessible(::nonexistent_crate::item)))] + | ^^^^^^^^^^^^^^^^^ + +error: `cfg(accessible(..))` mentioned external crate is not accessible + --> $DIR/cfg-accessible-1.rs:26:32 + | +LL | assert!(!cfg!(accessible(::nonexistent::item))); + | ^^^^^^^^^^^ + +error[E0425]: cannot find function `baz` in this scope + --> $DIR/cfg-accessible-1.rs:24:5 + | +LL | fn bar() -> bool { true } + | ---------------- similarly named function `bar` defined here +... +LL | baz(); + | ^^^ help: a function with a similar name exists: `bar` + +error: aborting due to 7 previous errors + +Some errors have detailed explanations: E0425, E0565. +For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/cfg/cfg-accessible-2.rs b/tests/ui/cfg/cfg-accessible-2.rs new file mode 100644 index 0000000000000..695a1a33a6a43 --- /dev/null +++ b/tests/ui/cfg/cfg-accessible-2.rs @@ -0,0 +1,16 @@ +//@ run-pass +//@ edition: 2018 +#![feature(cfg_accessible)] + +fn main() { + // accessible + assert!(cfg!(accessible(::std::boxed::Box))); + // not accessible because it's internal + assert!(!cfg!(accessible(::std::vec::RawVec))); + // not accessible because it's enum variant + assert!(!cfg!(accessible(::std::net::IpAddr::V4))); + // not accessible because it's inherent associated constant + assert!(!cfg!(accessible(::std::time::Duration::ZERO))); + // not accessible because it's trait associated constant + assert!(!cfg!(accessible(::std::default::Default::default))); +} diff --git a/tests/ui/cfg/cfg-accessible-3.rs b/tests/ui/cfg/cfg-accessible-3.rs new file mode 100644 index 0000000000000..423bd08d0cbb9 --- /dev/null +++ b/tests/ui/cfg/cfg-accessible-3.rs @@ -0,0 +1,6 @@ +//@ edition:2015 +#![feature(cfg_accessible)] + +fn main() { + assert!(cfg!(accessible(::std::boxed::Box))); //~ ERROR: `cfg(accessible(..))` cannot be used in edition 2015 +} diff --git a/tests/ui/cfg/cfg-accessible-3.stderr b/tests/ui/cfg/cfg-accessible-3.stderr new file mode 100644 index 0000000000000..516fb5cae8590 --- /dev/null +++ b/tests/ui/cfg/cfg-accessible-3.stderr @@ -0,0 +1,8 @@ +error: `cfg(accessible(..))` cannot be used in edition 2015 + --> $DIR/cfg-accessible-3.rs:5:29 + | +LL | assert!(cfg!(accessible(::std::boxed::Box))); + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/cfg-accessible-4.rs b/tests/ui/cfg/cfg-accessible-4.rs new file mode 100644 index 0000000000000..9e24317387eb8 --- /dev/null +++ b/tests/ui/cfg/cfg-accessible-4.rs @@ -0,0 +1,5 @@ +//@ edition: 2018 +#![feature(cfg_accessible)] +#![cfg(accessible(::std::boxed::Box))] //~ ERROR: `cfg(accessible(..))` cannot be used as crate attribute + +fn main() {} diff --git a/tests/ui/cfg/cfg-accessible-4.stderr b/tests/ui/cfg/cfg-accessible-4.stderr new file mode 100644 index 0000000000000..cee86a1e7c050 --- /dev/null +++ b/tests/ui/cfg/cfg-accessible-4.stderr @@ -0,0 +1,8 @@ +error: `cfg(accessible(..))` cannot be used as crate attribute + --> $DIR/cfg-accessible-4.rs:3:21 + | +LL | #![cfg(accessible(::std::boxed::Box))] + | ^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/feature-gates/feature-gate-cfg-accessible.rs b/tests/ui/feature-gates/feature-gate-cfg-accessible.rs new file mode 100644 index 0000000000000..df8b355ab0291 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-cfg-accessible.rs @@ -0,0 +1,33 @@ +//@ edition: 2018 +#[cfg(accessible())] //~ ERROR: expected cfg-accessible path +//~^ ERROR `cfg(accessible(..))` is experimental and subject to change +fn foo() {} +#[cfg(accessible(42))] //~ ERROR: unsupported literal +fn foo() -> bool { true } +#[cfg(accessible(::std::boxed::Box))] +//~^ ERROR `cfg(accessible(..))` is experimental and subject to change +fn foo() -> bool { true } +#[cfg(not(accessible(::std::boxed::Box)))] +//~^ ERROR `cfg(accessible(..))` is experimental and subject to change +fn foo() -> bool { false } +#[cfg(accessible(::std::nonexistent::item, ::nonexistent2::item))] //~ ERROR: expected cfg-accessible path +//~^ ERROR `cfg(accessible(..))` is experimental and subject to change +fn bar() -> bool { false } +#[cfg(not(accessible(::std::nonexistent::item)))] +//~^ ERROR `cfg(accessible(..))` is experimental and subject to change +fn bar() -> bool { true } +#[cfg(accessible(::nonexistent_crate::item))] //~ ERROR: `cfg(accessible(..))` mentioned external crate is not accessible +//~^ ERROR `cfg(accessible(..))` is experimental and subject to change +fn baz() -> bool { false } +#[cfg(not(accessible(::nonexistent_crate::item)))] //~ ERROR: `cfg(accessible(..))` mentioned external crate is not accessible +//~^ ERROR `cfg(accessible(..))` is experimental and subject to change +fn baz() -> bool { true } + +fn main() { + assert!(foo()); + assert!(bar()); + baz(); //~ ERROR cannot find function + assert!(cfg!(accessible(::std::boxed::Box))); //~ ERROR `cfg(accessible(..))` is experimental and subject to change + assert!(!cfg!(accessible(::nonexistent::item))); //~ ERROR: `cfg(accessible(..))` mentioned external crate is not accessible + //~^ ERROR `cfg(accessible(..))` is experimental and subject to change +} diff --git a/tests/ui/feature-gates/feature-gate-cfg-accessible.stderr b/tests/ui/feature-gates/feature-gate-cfg-accessible.stderr new file mode 100644 index 0000000000000..8e38e9c3ee2a0 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-cfg-accessible.stderr @@ -0,0 +1,143 @@ +error[E0658]: `cfg(accessible(..))` is experimental and subject to change + --> $DIR/feature-gate-cfg-accessible.rs:2:7 + | +LL | #[cfg(accessible())] + | ^^^^^^^^^^^^ + | + = note: see issue #64797 for more information + = help: add `#![feature(cfg_accessible)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: expected cfg-accessible path, starting with `::`, and consisting of module names and item names + --> $DIR/feature-gate-cfg-accessible.rs:2:7 + | +LL | #[cfg(accessible())] + | ^^^^^^^^^^^^ + | + = note: cfg-accessible paths start with `::`, followed by zero, one or more (maybe reexported) module name identifiers each followed by `::` in the middle, and then followed by item name identifier at the end + +error[E0565]: unsupported literal + --> $DIR/feature-gate-cfg-accessible.rs:5:18 + | +LL | #[cfg(accessible(42))] + | ^^ + +error[E0658]: `cfg(accessible(..))` is experimental and subject to change + --> $DIR/feature-gate-cfg-accessible.rs:7:7 + | +LL | #[cfg(accessible(::std::boxed::Box))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #64797 for more information + = help: add `#![feature(cfg_accessible)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `cfg(accessible(..))` is experimental and subject to change + --> $DIR/feature-gate-cfg-accessible.rs:10:11 + | +LL | #[cfg(not(accessible(::std::boxed::Box)))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #64797 for more information + = help: add `#![feature(cfg_accessible)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `cfg(accessible(..))` is experimental and subject to change + --> $DIR/feature-gate-cfg-accessible.rs:13:7 + | +LL | #[cfg(accessible(::std::nonexistent::item, ::nonexistent2::item))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #64797 for more information + = help: add `#![feature(cfg_accessible)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: expected cfg-accessible path, starting with `::`, and consisting of module names and item names + --> $DIR/feature-gate-cfg-accessible.rs:13:7 + | +LL | #[cfg(accessible(::std::nonexistent::item, ::nonexistent2::item))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: cfg-accessible paths start with `::`, followed by zero, one or more (maybe reexported) module name identifiers each followed by `::` in the middle, and then followed by item name identifier at the end + +error[E0658]: `cfg(accessible(..))` is experimental and subject to change + --> $DIR/feature-gate-cfg-accessible.rs:16:11 + | +LL | #[cfg(not(accessible(::std::nonexistent::item)))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #64797 for more information + = help: add `#![feature(cfg_accessible)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `cfg(accessible(..))` is experimental and subject to change + --> $DIR/feature-gate-cfg-accessible.rs:19:7 + | +LL | #[cfg(accessible(::nonexistent_crate::item))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #64797 for more information + = help: add `#![feature(cfg_accessible)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: `cfg(accessible(..))` mentioned external crate is not accessible + --> $DIR/feature-gate-cfg-accessible.rs:19:20 + | +LL | #[cfg(accessible(::nonexistent_crate::item))] + | ^^^^^^^^^^^^^^^^^ + +error[E0658]: `cfg(accessible(..))` is experimental and subject to change + --> $DIR/feature-gate-cfg-accessible.rs:22:11 + | +LL | #[cfg(not(accessible(::nonexistent_crate::item)))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #64797 for more information + = help: add `#![feature(cfg_accessible)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: `cfg(accessible(..))` mentioned external crate is not accessible + --> $DIR/feature-gate-cfg-accessible.rs:22:24 + | +LL | #[cfg(not(accessible(::nonexistent_crate::item)))] + | ^^^^^^^^^^^^^^^^^ + +error[E0658]: `cfg(accessible(..))` is experimental and subject to change + --> $DIR/feature-gate-cfg-accessible.rs:30:18 + | +LL | assert!(cfg!(accessible(::std::boxed::Box))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #64797 for more information + = help: add `#![feature(cfg_accessible)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `cfg(accessible(..))` is experimental and subject to change + --> $DIR/feature-gate-cfg-accessible.rs:31:19 + | +LL | assert!(!cfg!(accessible(::nonexistent::item))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #64797 for more information + = help: add `#![feature(cfg_accessible)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: `cfg(accessible(..))` mentioned external crate is not accessible + --> $DIR/feature-gate-cfg-accessible.rs:31:32 + | +LL | assert!(!cfg!(accessible(::nonexistent::item))); + | ^^^^^^^^^^^ + +error[E0425]: cannot find function `baz` in this scope + --> $DIR/feature-gate-cfg-accessible.rs:29:5 + | +LL | fn bar() -> bool { true } + | ---------------- similarly named function `bar` defined here +... +LL | baz(); + | ^^^ help: a function with a similar name exists: `bar` + +error: aborting due to 16 previous errors + +Some errors have detailed explanations: E0425, E0565, E0658. +For more information about an error, try `rustc --explain E0425`.