Skip to content

Commit

Permalink
Merge #26
Browse files Browse the repository at this point in the history
26: Replace version_check with our own parser to generate better errors r=taiki-e a=taiki-e

This helps us to understand what kind of error occurred at which stage of parsing. See also #24 (comment).

Co-authored-by: Taiki Endo <[email protected]>
  • Loading branch information
bors[bot] and taiki-e authored Aug 31, 2020
2 parents 4d290fd + 6df1a4b commit ce4d34c
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 86 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ jobs:
- 1.31.0
- 1.33.0
- 1.39.0
- 1.46.0
- stable
- beta
- nightly
Expand Down
3 changes: 0 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,4 @@ members = ["test_suite"]
[lib]
proc-macro = true

[build-dependencies]
version_check = "0.9.2"

[dependencies]
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ pub const fn feature() {

## Alternatives

This crate is proc-macro, but is very lightweight, depending only on [`version_check`](https://github.com/SergioBenitez/version_check) to determine the compiler version.
This crate is proc-macro, but is very lightweight, and has no dependencies.

You can manually define declarative macros with similar functionality (see [`if_rust_version`](https://github.com/ogoffart/if_rust_version#examples)), or [you can define the same function twice with different cfg](https://github.com/crossbeam-rs/crossbeam/blob/0b6ea5f69fde8768c1cfac0d3601e0b4325d7997/crossbeam-epoch/src/atomic.rs#L340-L372). (Note: the former approach requires more macros to be defined depending on the number of version requirements, the latter approach requires more functions to be maintained manually)

Expand Down
61 changes: 53 additions & 8 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
use std::{env, fs, path::PathBuf};
use std::{
env, fs,
path::{Path, PathBuf},
process::Command,
str,
};

// The rustc-cfg strings below are *not* public API. Please let us know by
// opening a GitHub issue if your build environment requires some way to enable
// these cfgs other than by executing our build script.
fn main() {
println!("cargo:rustc-cfg=const_fn_has_build_script");

let version = match Version::new() {
Some(version) => format!("{:#?}\n", version),
None => panic!("unexpected output from `rustc --version`"),
let rustc = env::var_os("RUSTC").map_or_else(|| "rustc".into(), PathBuf::from);
let version = match Version::from_rustc(&rustc) {
Ok(version) => format!("{:#?}\n", version),
Err(e) => panic!("{}", e),
};

let out_dir = env::var_os("OUT_DIR").map(PathBuf::from).expect("OUT_DIR not set");
Expand All @@ -23,11 +29,50 @@ struct Version {
nightly: bool,
}

// Based on https://github.com/cuviper/autocfg/blob/1.0.1/src/version.rs
//
// Using our own parser instead of the existing crates to generate better errors.
impl Version {
fn new() -> Option<Self> {
let (version, channel, _date) = version_check::triple()?;
let (_major, minor, patch) = version.to_mmp();
let nightly = channel.is_nightly() || channel.is_dev();
// from the verbose version output
fn from_vv(vv: &str) -> Option<Self> {
// Find the release line in the verbose version output.
let release = vv
.lines()
.find(|line| line.starts_with("release: "))
.map(|line| &line["release: ".len()..])?;

// Split the version and channel info.
let mut version_channel = release.split('-');
let version = version_channel.next().unwrap();
let channel = version_channel.next();

// Split the version into semver components.
let mut digits = version.splitn(3, '.');
let major = digits.next()?;
if major != "1" {
return None;
}
let minor = digits.next()?.parse().ok()?;
let patch = digits.next().unwrap_or("0").parse().ok()?;

let nightly = channel.map_or(false, |c| c == "dev" || c == "nightly");
Some(Version { minor, patch, nightly })
}

fn from_rustc(rustc: &Path) -> Result<Self, String> {
let output =
Command::new(rustc).args(&["--version", "--verbose"]).output().map_err(|e| {
format!("failed to run `{} --version --verbose`: {}", rustc.display(), e)
})?;
if !output.status.success() {
return Err("could not execute rustc".to_string());
}
let output = str::from_utf8(&output.stdout).map_err(|e| {
format!("failed to parse output of `{} --version --verbose`: {}", rustc.display(), e)
})?;

Self::from_vv(output).ok_or_else(|| {
format!("unexpected output from `{} --version --verbose`: {}", rustc.display(), output)
})
}
}
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
//!
//! # Alternatives
//!
//! This crate is proc-macro, but is very lightweight, depending only on [`version_check`](https://github.com/SergioBenitez/version_check) to determine the compiler version.
//! This crate is proc-macro, but is very lightweight, and has no dependencies.
//!
//! You can manually define declarative macros with similar functionality (see [`if_rust_version`](https://github.com/ogoffart/if_rust_version#examples)), or [you can define the same function twice with different cfg](https://github.com/crossbeam-rs/crossbeam/blob/0b6ea5f69fde8768c1cfac0d3601e0b4325d7997/crossbeam-epoch/src/atomic.rs#L340-L372).
//! (Note: the former approach requires more macros to be defined depending on the number of version requirements, the latter approach requires more functions to be maintained manually)
Expand Down
3 changes: 3 additions & 0 deletions test_suite/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ fn main() {
if minor >= 39 || nightly {
println!("cargo:rustc-cfg=has_const_vec_new");
}
if minor >= 46 || nightly {
println!("cargo:rustc-cfg=has_const_match");
}
if nightly {
println!("cargo:rustc-cfg=const_unstable");
}
Expand Down
124 changes: 51 additions & 73 deletions test_suite/tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,66 +4,47 @@
pub mod version {
use const_fn::const_fn;

#[test]
fn test_variables() {
assert!(const_min("variables") == "variables");
assert_eq!(const_let("variables"), "variables");
assert_eq!(const_vec_new::<u8>(), Vec::new());
assert_eq!(A::const_unstable(const_vec_new::<u8>()), A(Vec::new()));
}

// min_const_fn (rust 1.31+)
// min_const_fn (1.31+)

#[const_fn("1.31")]
const fn const_min<T>(x: T) -> T {
x
}
const _CONST_MIN: &str = const_min("min_const_fn");

const CONST_MIN: &str = const_min("min_const_fn");

#[test]
fn test_const_min() {
assert!(CONST_MIN == "min_const_fn");
assert_eq!(const_let("min_const_fn"), "min_const_fn");
assert_eq!(const_vec_new::<u8>(), Vec::new());
}

// const_let (rust 1.33+)
// const_let (1.33+)

#[allow(clippy::let_and_return)]
#[const_fn("1.33")]
const fn const_let<T>(x: T) -> T {
let y = const_min(x);
y
}

#[rustversion::since(1.33)]
const CONST_LET: &str = const_let("const_let");

#[rustversion::since(1.33)]
#[test]
fn test_const_let() {
assert!(CONST_LET == "const_let");
assert_eq!(const_vec_new::<u8>(), Vec::new());
}
const _CONST_LET: &str = const_let("const_let");

// const_vec_new (rust 1.39+)
// const_vec_new (1.39+)

#[const_fn("1.39")]
const fn const_vec_new<T>() -> Vec<T> {
Vec::new()
}

#[rustversion::since(1.39)]
const CONST_VEC_NEW: Vec<u8> = const_vec_new();
const _: Vec<u8> = const_vec_new();

#[rustversion::since(1.39)]
#[test]
fn test_const_vec_new() {
assert_eq!(CONST_VEC_NEW, Vec::new());
// const_match, const_loop (1.46+)

#[const_fn("1.46")]
const fn const_match(x: u8) -> Option<u8> {
match x {
0 => None,
x => Some(x),
}
}
#[rustversion::since(1.46)]
const _: Option<u8> = const_match(1);

// const_fn (rust nightly)
// const_fn (nightly)

#[derive(Debug, Eq, PartialEq)]
pub struct A<T>(T);
Expand All @@ -74,74 +55,63 @@ pub mod version {
A(x)
}
}

#[rustversion::nightly]
pub const CONST_UNSTABLE: A<Vec<u8>> = A::const_unstable(const_vec_new());
}

pub mod cfg {
use const_fn::const_fn;
const _: A<Vec<u8>> = A::const_unstable(const_vec_new());

#[test]
fn test_variables() {
fn test() {
assert!(const_min("variables") == "variables");
assert_eq!(const_let("variables"), "variables");
assert_eq!(const_vec_new::<u8>(), Vec::new());
assert_eq!(const_match(1), Some(1));
assert_eq!(A::const_unstable(const_vec_new::<u8>()), A(Vec::new()));
}
}

pub mod cfg {
use const_fn::const_fn;

// min_const_fn (rust 1.31+)
// min_const_fn (1.31+)

#[const_fn(cfg(has_min_const_fn))]
const fn const_min<T>(x: T) -> T {
x
}
const _CONST_MIN: &str = const_min("min_const_fn");

pub const CONST_MIN: &str = const_min("min_const_fn");

#[test]
fn test_const_min() {
assert!(CONST_MIN == "min_const_fn");
assert_eq!(const_let("min_const_fn"), "min_const_fn");
assert_eq!(const_vec_new::<u8>(), Vec::new());
}

// const_let (rust 1.33+)
// const_let (1.33+)

#[allow(clippy::let_and_return)]
#[const_fn(cfg(has_const_let))]
const fn const_let<T>(x: T) -> T {
let y = const_min(x);
y
}

#[rustversion::since(1.33)]
const CONST_LET: &str = const_let("const_let");

#[rustversion::since(1.33)]
#[test]
fn test_const_let() {
assert!(CONST_LET == "const_let");
assert_eq!(const_vec_new::<u8>(), Vec::new());
}
const _CONST_LET: &str = const_let("const_let");

// const_vec_new (rust 1.39+)
// const_vec_new (1.39+)

#[const_fn(cfg(has_const_vec_new))]
const fn const_vec_new<T>() -> Vec<T> {
Vec::new()
}

#[rustversion::since(1.39)]
const CONST_VEC_NEW: Vec<u8> = const_vec_new();
const _: Vec<u8> = const_vec_new();

#[rustversion::since(1.39)]
#[test]
fn test_const_vec_new() {
assert_eq!(CONST_VEC_NEW, Vec::new());
// const_match, const_loop (1.46+)

#[const_fn(cfg(has_const_match))]
const fn const_match(x: u8) -> Option<u8> {
match x {
0 => None,
x => Some(x),
}
}
#[rustversion::since(1.46)]
const _: Option<u8> = const_match(1);

// const_fn (rust nightly)
// const_fn (nightly)

#[derive(Debug, Eq, PartialEq)]
pub struct A<T>(T);
Expand All @@ -152,7 +122,15 @@ pub mod cfg {
A(x)
}
}

#[rustversion::nightly]
pub const CONST_UNSTABLE: A<Vec<u8>> = A::const_unstable(const_vec_new());
const _: A<Vec<u8>> = A::const_unstable(const_vec_new());

#[test]
fn test() {
assert!(const_min("variables") == "variables");
assert_eq!(const_let("variables"), "variables");
assert_eq!(const_vec_new::<u8>(), Vec::new());
assert_eq!(const_match(1), Some(1));
assert_eq!(A::const_unstable(const_vec_new::<u8>()), A(Vec::new()));
}
}

0 comments on commit ce4d34c

Please sign in to comment.