Emit error on [target.cfg(debug_assertions).dependencies]

This commit is contained in:
Benedikt Werner 2019-12-04 09:08:43 +01:00
parent 61321125e3
commit 552ecc9438
No known key found for this signature in database
GPG key ID: 1DBFF0F8E9E121EB
5 changed files with 85 additions and 2 deletions

View file

@ -1,4 +1,4 @@
use crate::error::{ParseError, ParseErrorKind::*};
use crate::error::{ParseError, ParseErrorKind, ParseErrorKind::*};
use std::fmt;
use std::iter;
use std::str::{self, FromStr};
@ -41,6 +41,28 @@ struct Parser<'a> {
t: Tokenizer<'a>,
}
impl Cfg {
pub(crate) fn validate_as_target(&self) -> Result<(), ParseErrorKind> {
match self {
Cfg::Name(name) => match name.as_str() {
"unix" | "windows" => Ok(()),
_ => Err(InvalidCfgName(name.to_string())),
},
Cfg::KeyPair(name, _) => match name.as_str() {
"target_arch"
| "target_feature"
| "target_os"
| "target_family"
| "target_env"
| "target_endian"
| "target_pointer_width"
| "target_vendor" => Ok(()),
_ => Err(InvalidCfgKey(name.to_string())),
},
}
}
}
impl FromStr for Cfg {
type Err = ParseError;
@ -89,6 +111,19 @@ impl CfgExpr {
CfgExpr::Value(ref e) => cfg.contains(e),
}
}
pub(crate) fn validate_as_target(&self) -> Result<(), ParseErrorKind> {
match *self {
CfgExpr::Not(ref e) => e.validate_as_target()?,
CfgExpr::All(ref e) | CfgExpr::Any(ref e) => {
for e in e {
e.validate_as_target()?;
}
}
CfgExpr::Value(ref e) => e.validate_as_target()?,
}
Ok(())
}
}
impl FromStr for CfgExpr {
@ -317,3 +352,31 @@ impl<'a> Token<'a> {
}
}
}
#[test]
fn cfg_validate_as_target() {
fn p(s: &str) -> CfgExpr {
s.parse().unwrap()
}
assert!(p("unix").validate_as_target().is_ok());
assert!(p("windows").validate_as_target().is_ok());
assert!(p("any(not(unix), windows)").validate_as_target().is_ok());
assert!(p("target_arch = \"abc\"").validate_as_target().is_ok());
assert!(p("target_feature = \"abc\"").validate_as_target().is_ok());
assert!(p("target_os = \"abc\"").validate_as_target().is_ok());
assert!(p("target_family = \"abc\"").validate_as_target().is_ok());
assert!(p("target_env = \"abc\"").validate_as_target().is_ok());
assert!(p("target_endian = \"abc\"").validate_as_target().is_ok());
assert!(p("target_pointer_width = \"abc\"").validate_as_target().is_ok());
assert!(p("target_vendor = \"abc\"").validate_as_target().is_ok());
assert!(p("debug_assertions").validate_as_target().is_err());
assert!(p("foo").validate_as_target().is_err());
assert!(p("any(not(debug_assertions), windows)").validate_as_target().is_err());
assert!(p("feature = \"abc\"").validate_as_target().is_err());
assert!(p("bar = \"def\"").validate_as_target().is_err());
assert!(p("any(not(feature = \"def\"))").validate_as_target().is_err());
}

View file

@ -17,6 +17,8 @@ pub enum ParseErrorKind {
IncompleteExpr(&'static str),
UnterminatedExpression(String),
InvalidTarget(String),
InvalidCfgName(String),
InvalidCfgKey(String),
#[doc(hidden)]
__Nonexhaustive,
@ -53,6 +55,8 @@ impl fmt::Display for ParseErrorKind {
write!(f, "unexpected content `{}` found after cfg expression", s)
}
InvalidTarget(s) => write!(f, "invalid target specifier: {}", s),
InvalidCfgName(name) => write!(f, "invalid name in target cfg: {}", name),
InvalidCfgKey(name) => write!(f, "invalid key in target cfg: {}", name),
__Nonexhaustive => unreachable!(),
}
}

View file

@ -88,7 +88,10 @@ impl FromStr for Platform {
fn from_str(s: &str) -> Result<Platform, ParseError> {
if s.starts_with("cfg(") && s.ends_with(')') {
let s = &s[4..s.len() - 1];
s.parse().map(Platform::Cfg)
let cfg: CfgExpr = s.parse()?;
cfg.validate_as_target()
.map_err(|kind| ParseError::new(s, kind))?;
Ok(Platform::Cfg(cfg))
} else {
Platform::validate_named_platform(s)?;
Ok(Platform::Name(s.to_string()))

View file

@ -155,6 +155,16 @@ fn bad_target_name() {
"failed to parse `!foo` as a cfg expression: \
invalid target specifier: unexpected character ! in target name",
);
bad::<Platform>(
"cfg(debug_assertions)",
"failed to parse `debug_assertions` as a cfg expression: \
invalid name in target cfg: debug_assertions",
);
bad::<Platform>(
"cfg(feature = \"abc\")",
"failed to parse `feature = \"abc\"` as a cfg expression: \
invalid key in target cfg: feature",
);
}
#[test]

View file

@ -476,6 +476,9 @@ dependencies based on optional crate features.
Use [the `[features]` section](manifest.md#the-features-section)
instead.
The same applies to `cfg(debug_assertions)`, `cfg(test)` and `cfg(prog_macro)`.
There is currently no way to add dependencies based on these configuration values.
In addition to `#[cfg]` syntax, Cargo also supports listing out the full target
the dependencies would apply to: