mirror of https://github.com/rust-lang/rust.git
Implement RFC 2523, `#[cfg(version(..))]`
This commit is contained in:
parent
f05a524044
commit
90aa62a1bf
|
@ -3593,6 +3593,7 @@ dependencies = [
|
||||||
"rustc_session",
|
"rustc_session",
|
||||||
"rustc_span",
|
"rustc_span",
|
||||||
"serialize",
|
"serialize",
|
||||||
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
# `cfg_version`
|
||||||
|
|
||||||
|
The tracking issue for this feature is: [#64796]
|
||||||
|
|
||||||
|
[#64796]: https://github.com/rust-lang/rust/issues/64796
|
||||||
|
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
The `cfg_version` feature makes it possible to execute different code
|
||||||
|
depending on the compiler version.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#![feature(cfg_version)]
|
||||||
|
|
||||||
|
#[cfg(version("1.42"))]
|
||||||
|
fn a() {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(version("1.42")))]
|
||||||
|
fn a() {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
fn b() {
|
||||||
|
if cfg!(version("1.42")) {
|
||||||
|
// ...
|
||||||
|
} else {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
|
@ -19,3 +19,4 @@ rustc_feature = { path = "../librustc_feature" }
|
||||||
rustc_macros = { path = "../librustc_macros" }
|
rustc_macros = { path = "../librustc_macros" }
|
||||||
rustc_session = { path = "../librustc_session" }
|
rustc_session = { path = "../librustc_session" }
|
||||||
rustc_ast = { path = "../librustc_ast" }
|
rustc_ast = { path = "../librustc_ast" }
|
||||||
|
version_check = "0.9"
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use super::{find_by_name, mark_used};
|
use super::{find_by_name, mark_used};
|
||||||
|
|
||||||
use rustc_ast::ast::{self, Attribute, MetaItem, MetaItemKind, NestedMetaItem};
|
use rustc_ast::ast::{self, Attribute, Lit, LitKind, MetaItem, MetaItemKind, NestedMetaItem};
|
||||||
use rustc_ast_pretty::pprust;
|
use rustc_ast_pretty::pprust;
|
||||||
use rustc_errors::{struct_span_err, Applicability, Handler};
|
use rustc_errors::{struct_span_err, Applicability, Handler};
|
||||||
use rustc_feature::{find_gated_cfg, is_builtin_attr_name, Features, GatedCfg};
|
use rustc_feature::{find_gated_cfg, is_builtin_attr_name, Features, GatedCfg};
|
||||||
|
@ -11,6 +11,7 @@ use rustc_session::parse::{feature_err, ParseSess};
|
||||||
use rustc_span::hygiene::Transparency;
|
use rustc_span::hygiene::Transparency;
|
||||||
use rustc_span::{symbol::sym, symbol::Symbol, Span};
|
use rustc_span::{symbol::sym, symbol::Symbol, Span};
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
|
use version_check::Version;
|
||||||
|
|
||||||
pub fn is_builtin_attr(attr: &Attribute) -> bool {
|
pub fn is_builtin_attr(attr: &Attribute) -> bool {
|
||||||
attr.is_doc_comment() || attr.ident().filter(|ident| is_builtin_attr_name(ident.name)).is_some()
|
attr.is_doc_comment() || attr.ident().filter(|ident| is_builtin_attr_name(ident.name)).is_some()
|
||||||
|
@ -568,11 +569,8 @@ pub fn find_crate_name(attrs: &[Attribute]) -> Option<Symbol> {
|
||||||
|
|
||||||
/// Tests if a cfg-pattern matches the cfg set
|
/// Tests if a cfg-pattern matches the cfg set
|
||||||
pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Features>) -> bool {
|
pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Features>) -> bool {
|
||||||
eval_condition(cfg, sess, &mut |cfg| {
|
eval_condition(cfg, sess, features, &mut |cfg| {
|
||||||
let gate = find_gated_cfg(|sym| cfg.check_name(sym));
|
try_gate_cfg(cfg, sess, features);
|
||||||
if let (Some(feats), Some(gated_cfg)) = (features, gate) {
|
|
||||||
gate_cfg(&gated_cfg, cfg.span, sess, feats);
|
|
||||||
}
|
|
||||||
let error = |span, msg| {
|
let error = |span, msg| {
|
||||||
sess.span_diagnostic.span_err(span, msg);
|
sess.span_diagnostic.span_err(span, msg);
|
||||||
true
|
true
|
||||||
|
@ -603,6 +601,13 @@ pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Feat
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn try_gate_cfg(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Features>) {
|
||||||
|
let gate = find_gated_cfg(|sym| cfg.check_name(sym));
|
||||||
|
if let (Some(feats), Some(gated_cfg)) = (features, gate) {
|
||||||
|
gate_cfg(&gated_cfg, cfg.span, sess, feats);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &ParseSess, features: &Features) {
|
fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &ParseSess, features: &Features) {
|
||||||
let (cfg, feature, has_feature) = gated_cfg;
|
let (cfg, feature, has_feature) = gated_cfg;
|
||||||
if !has_feature(features) && !cfg_span.allows_unstable(*feature) {
|
if !has_feature(features) && !cfg_span.allows_unstable(*feature) {
|
||||||
|
@ -616,9 +621,42 @@ fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &ParseSess, features: &F
|
||||||
pub fn eval_condition(
|
pub fn eval_condition(
|
||||||
cfg: &ast::MetaItem,
|
cfg: &ast::MetaItem,
|
||||||
sess: &ParseSess,
|
sess: &ParseSess,
|
||||||
|
features: Option<&Features>,
|
||||||
eval: &mut impl FnMut(&ast::MetaItem) -> bool,
|
eval: &mut impl FnMut(&ast::MetaItem) -> bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match cfg.kind {
|
match cfg.kind {
|
||||||
|
ast::MetaItemKind::List(ref mis) if cfg.name_or_empty() == sym::version => {
|
||||||
|
try_gate_cfg(cfg, sess, features);
|
||||||
|
let (min_version, span) = match &mis[..] {
|
||||||
|
[NestedMetaItem::Literal(Lit { kind: LitKind::Str(sym, ..), span, .. })] => {
|
||||||
|
(sym, span)
|
||||||
|
}
|
||||||
|
[NestedMetaItem::Literal(Lit { span, .. })
|
||||||
|
| NestedMetaItem::MetaItem(MetaItem { span, .. })] => {
|
||||||
|
sess.span_diagnostic
|
||||||
|
.struct_span_err(*span, &*format!("expected string literal"))
|
||||||
|
.emit();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
[..] => {
|
||||||
|
sess.span_diagnostic
|
||||||
|
.struct_span_err(cfg.span, "expected single string literal")
|
||||||
|
.emit();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let min_version = match Version::parse(&min_version.as_str()) {
|
||||||
|
Some(ver) => ver,
|
||||||
|
None => {
|
||||||
|
sess.span_diagnostic.struct_span_err(*span, "invalid version string").emit();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let version = option_env!("CFG_VERSION").unwrap_or("unknown rustc version");
|
||||||
|
let version = Version::parse(version).unwrap();
|
||||||
|
|
||||||
|
version >= min_version
|
||||||
|
}
|
||||||
ast::MetaItemKind::List(ref mis) => {
|
ast::MetaItemKind::List(ref mis) => {
|
||||||
for mi in mis.iter() {
|
for mi in mis.iter() {
|
||||||
if !mi.is_meta_item() {
|
if !mi.is_meta_item() {
|
||||||
|
@ -634,12 +672,12 @@ pub fn eval_condition(
|
||||||
// The unwraps below may look dangerous, but we've already asserted
|
// The unwraps below may look dangerous, but we've already asserted
|
||||||
// that they won't fail with the loop above.
|
// that they won't fail with the loop above.
|
||||||
match cfg.name_or_empty() {
|
match cfg.name_or_empty() {
|
||||||
sym::any => {
|
sym::any => mis
|
||||||
mis.iter().any(|mi| eval_condition(mi.meta_item().unwrap(), sess, eval))
|
.iter()
|
||||||
}
|
.any(|mi| eval_condition(mi.meta_item().unwrap(), sess, features, eval)),
|
||||||
sym::all => {
|
sym::all => mis
|
||||||
mis.iter().all(|mi| eval_condition(mi.meta_item().unwrap(), sess, eval))
|
.iter()
|
||||||
}
|
.all(|mi| eval_condition(mi.meta_item().unwrap(), sess, features, eval)),
|
||||||
sym::not => {
|
sym::not => {
|
||||||
if mis.len() != 1 {
|
if mis.len() != 1 {
|
||||||
struct_span_err!(
|
struct_span_err!(
|
||||||
|
@ -652,7 +690,7 @@ pub fn eval_condition(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
!eval_condition(mis[0].meta_item().unwrap(), sess, eval)
|
!eval_condition(mis[0].meta_item().unwrap(), sess, features, eval)
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
struct_span_err!(
|
struct_span_err!(
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
//! The goal is to move the definition of `MetaItem` and things that don't need to be in `syntax`
|
//! The goal is to move the definition of `MetaItem` and things that don't need to be in `syntax`
|
||||||
//! to this crate.
|
//! to this crate.
|
||||||
|
|
||||||
|
#![feature(or_patterns)]
|
||||||
|
|
||||||
mod builtin;
|
mod builtin;
|
||||||
|
|
||||||
pub use builtin::*;
|
pub use builtin::*;
|
||||||
|
|
|
@ -562,6 +562,9 @@ declare_features! (
|
||||||
/// Allows the use of `#[target_feature]` on safe functions.
|
/// Allows the use of `#[target_feature]` on safe functions.
|
||||||
(active, target_feature_11, "1.45.0", Some(69098), None),
|
(active, target_feature_11, "1.45.0", Some(69098), None),
|
||||||
|
|
||||||
|
/// Allow conditional compilation depending on rust version
|
||||||
|
(active, cfg_version, "1.45.0", Some(64796), None),
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// feature-group-end: actual feature gates
|
// feature-group-end: actual feature gates
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
|
|
@ -26,6 +26,7 @@ const GATED_CFGS: &[GatedCfg] = &[
|
||||||
(sym::target_has_atomic, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)),
|
(sym::target_has_atomic, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)),
|
||||||
(sym::target_has_atomic_load_store, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)),
|
(sym::target_has_atomic_load_store, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)),
|
||||||
(sym::sanitize, sym::cfg_sanitize, cfg_fn!(cfg_sanitize)),
|
(sym::sanitize, sym::cfg_sanitize, cfg_fn!(cfg_sanitize)),
|
||||||
|
(sym::version, sym::cfg_version, cfg_fn!(cfg_version)),
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Find a gated cfg determined by the `pred`icate which is given the cfg's name.
|
/// Find a gated cfg determined by the `pred`icate which is given the cfg's name.
|
||||||
|
|
|
@ -192,6 +192,7 @@ symbols! {
|
||||||
cfg_target_has_atomic,
|
cfg_target_has_atomic,
|
||||||
cfg_target_thread_local,
|
cfg_target_thread_local,
|
||||||
cfg_target_vendor,
|
cfg_target_vendor,
|
||||||
|
cfg_version,
|
||||||
char,
|
char,
|
||||||
clippy,
|
clippy,
|
||||||
clone,
|
clone,
|
||||||
|
@ -805,6 +806,7 @@ symbols! {
|
||||||
var,
|
var,
|
||||||
vec,
|
vec,
|
||||||
Vec,
|
Vec,
|
||||||
|
version,
|
||||||
vis,
|
vis,
|
||||||
visible_private_types,
|
visible_private_types,
|
||||||
volatile,
|
volatile,
|
||||||
|
|
|
@ -81,7 +81,7 @@ impl<'tcx> OnUnimplementedDirective {
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
attr::eval_condition(cond, &tcx.sess.parse_sess, &mut |_| true);
|
attr::eval_condition(cond, &tcx.sess.parse_sess, Some(tcx.features()), &mut |_| true);
|
||||||
Some(cond.clone())
|
Some(cond.clone())
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -208,11 +208,16 @@ impl<'tcx> OnUnimplementedDirective {
|
||||||
|
|
||||||
for command in self.subcommands.iter().chain(Some(self)).rev() {
|
for command in self.subcommands.iter().chain(Some(self)).rev() {
|
||||||
if let Some(ref condition) = command.condition {
|
if let Some(ref condition) = command.condition {
|
||||||
if !attr::eval_condition(condition, &tcx.sess.parse_sess, &mut |c| {
|
if !attr::eval_condition(
|
||||||
|
condition,
|
||||||
|
&tcx.sess.parse_sess,
|
||||||
|
Some(tcx.features()),
|
||||||
|
&mut |c| {
|
||||||
c.ident().map_or(false, |ident| {
|
c.ident().map_or(false, |ident| {
|
||||||
options.contains(&(ident.name, c.value_str().map(|s| s.to_string())))
|
options.contains(&(ident.name, c.value_str().map(|s| s.to_string())))
|
||||||
})
|
})
|
||||||
}) {
|
},
|
||||||
|
) {
|
||||||
debug!("evaluate: skipping {:?} due to condition", command);
|
debug!("evaluate: skipping {:?} due to condition", command);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
#[cfg(version("1.44"))]
|
||||||
|
//~^ ERROR `cfg(version)` is experimental and subject to change
|
||||||
|
fn foo() -> bool { true }
|
||||||
|
#[cfg(not(version("1.44")))]
|
||||||
|
//~^ ERROR `cfg(version)` is experimental and subject to change
|
||||||
|
fn foo() -> bool { false }
|
||||||
|
|
||||||
|
#[cfg(version("1.43", "1.44", "1.45"))] //~ ERROR: expected single string literal
|
||||||
|
//~^ ERROR `cfg(version)` is experimental and subject to change
|
||||||
|
fn bar() -> bool { false }
|
||||||
|
#[cfg(version(false))] //~ ERROR: expected string literal
|
||||||
|
//~^ ERROR `cfg(version)` is experimental and subject to change
|
||||||
|
fn bar() -> bool { false }
|
||||||
|
#[cfg(version("foo"))] //~ ERROR: invalid version string
|
||||||
|
//~^ ERROR `cfg(version)` is experimental and subject to change
|
||||||
|
fn bar() -> bool { false }
|
||||||
|
#[cfg(version("999"))]
|
||||||
|
//~^ ERROR `cfg(version)` is experimental and subject to change
|
||||||
|
fn bar() -> bool { false }
|
||||||
|
#[cfg(version("0"))]
|
||||||
|
//~^ ERROR `cfg(version)` is experimental and subject to change
|
||||||
|
fn bar() -> bool { true }
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
assert!(foo());
|
||||||
|
assert!(bar());
|
||||||
|
assert!(cfg!(version("1.42"))); //~ ERROR `cfg(version)` is experimental and subject to change
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
error[E0658]: `cfg(version)` is experimental and subject to change
|
||||||
|
--> $DIR/feature-gate-cfg-version.rs:1:7
|
||||||
|
|
|
||||||
|
LL | #[cfg(version("1.44"))]
|
||||||
|
| ^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #64796 <https://github.com/rust-lang/rust/issues/64796> for more information
|
||||||
|
= help: add `#![feature(cfg_version)]` to the crate attributes to enable
|
||||||
|
|
||||||
|
error[E0658]: `cfg(version)` is experimental and subject to change
|
||||||
|
--> $DIR/feature-gate-cfg-version.rs:4:11
|
||||||
|
|
|
||||||
|
LL | #[cfg(not(version("1.44")))]
|
||||||
|
| ^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #64796 <https://github.com/rust-lang/rust/issues/64796> for more information
|
||||||
|
= help: add `#![feature(cfg_version)]` to the crate attributes to enable
|
||||||
|
|
||||||
|
error[E0658]: `cfg(version)` is experimental and subject to change
|
||||||
|
--> $DIR/feature-gate-cfg-version.rs:8:7
|
||||||
|
|
|
||||||
|
LL | #[cfg(version("1.43", "1.44", "1.45"))]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #64796 <https://github.com/rust-lang/rust/issues/64796> for more information
|
||||||
|
= help: add `#![feature(cfg_version)]` to the crate attributes to enable
|
||||||
|
|
||||||
|
error: expected single string literal
|
||||||
|
--> $DIR/feature-gate-cfg-version.rs:8:7
|
||||||
|
|
|
||||||
|
LL | #[cfg(version("1.43", "1.44", "1.45"))]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error[E0658]: `cfg(version)` is experimental and subject to change
|
||||||
|
--> $DIR/feature-gate-cfg-version.rs:11:7
|
||||||
|
|
|
||||||
|
LL | #[cfg(version(false))]
|
||||||
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #64796 <https://github.com/rust-lang/rust/issues/64796> for more information
|
||||||
|
= help: add `#![feature(cfg_version)]` to the crate attributes to enable
|
||||||
|
|
||||||
|
error: expected string literal
|
||||||
|
--> $DIR/feature-gate-cfg-version.rs:11:15
|
||||||
|
|
|
||||||
|
LL | #[cfg(version(false))]
|
||||||
|
| ^^^^^
|
||||||
|
|
||||||
|
error[E0658]: `cfg(version)` is experimental and subject to change
|
||||||
|
--> $DIR/feature-gate-cfg-version.rs:14:7
|
||||||
|
|
|
||||||
|
LL | #[cfg(version("foo"))]
|
||||||
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #64796 <https://github.com/rust-lang/rust/issues/64796> for more information
|
||||||
|
= help: add `#![feature(cfg_version)]` to the crate attributes to enable
|
||||||
|
|
||||||
|
error: invalid version string
|
||||||
|
--> $DIR/feature-gate-cfg-version.rs:14:15
|
||||||
|
|
|
||||||
|
LL | #[cfg(version("foo"))]
|
||||||
|
| ^^^^^
|
||||||
|
|
||||||
|
error[E0658]: `cfg(version)` is experimental and subject to change
|
||||||
|
--> $DIR/feature-gate-cfg-version.rs:17:7
|
||||||
|
|
|
||||||
|
LL | #[cfg(version("999"))]
|
||||||
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #64796 <https://github.com/rust-lang/rust/issues/64796> for more information
|
||||||
|
= help: add `#![feature(cfg_version)]` to the crate attributes to enable
|
||||||
|
|
||||||
|
error[E0658]: `cfg(version)` is experimental and subject to change
|
||||||
|
--> $DIR/feature-gate-cfg-version.rs:20:7
|
||||||
|
|
|
||||||
|
LL | #[cfg(version("0"))]
|
||||||
|
| ^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #64796 <https://github.com/rust-lang/rust/issues/64796> for more information
|
||||||
|
= help: add `#![feature(cfg_version)]` to the crate attributes to enable
|
||||||
|
|
||||||
|
error[E0658]: `cfg(version)` is experimental and subject to change
|
||||||
|
--> $DIR/feature-gate-cfg-version.rs:27:18
|
||||||
|
|
|
||||||
|
LL | assert!(cfg!(version("1.42")));
|
||||||
|
| ^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #64796 <https://github.com/rust-lang/rust/issues/64796> for more information
|
||||||
|
= help: add `#![feature(cfg_version)]` to the crate attributes to enable
|
||||||
|
|
||||||
|
error: aborting due to 11 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0658`.
|
Loading…
Reference in New Issue