Skip to content

Commit 9450706

Browse files
committed
Add support for raw-idents in cfgs
1 parent 3c3bfb0 commit 9450706

File tree

5 files changed

+153
-22
lines changed

5 files changed

+153
-22
lines changed

crates/cargo-platform/src/cfg.rs

Lines changed: 102 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::error::{ParseError, ParseErrorKind::*};
22
use std::fmt;
3+
use std::hash::{Hash, Hasher};
34
use std::iter;
45
use std::str::{self, FromStr};
56

@@ -16,16 +17,28 @@ pub enum CfgExpr {
1617
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Debug)]
1718
pub enum Cfg {
1819
/// A named cfg value, like `unix`.
19-
Name(String),
20+
Name(Ident),
2021
/// A key/value cfg pair, like `target_os = "linux"`.
21-
KeyPair(String, String),
22+
KeyPair(Ident, String),
23+
}
24+
25+
/// A identifier
26+
#[derive(Eq, Ord, PartialOrd, Clone, Debug)]
27+
pub struct Ident {
28+
/// The identifier
29+
pub name: String,
30+
/// Is this a raw ident: `r#async`
31+
///
32+
/// It's mainly used for display and doesn't take
33+
/// part in the hash or equality (`foo` == `r#foo`).
34+
pub raw: bool,
2235
}
2336

2437
#[derive(PartialEq)]
2538
enum Token<'a> {
2639
LeftParen,
2740
RightParen,
28-
Ident(&'a str),
41+
Ident(bool, &'a str),
2942
Comma,
3043
Equals,
3144
String(&'a str),
@@ -49,6 +62,45 @@ struct Parser<'a> {
4962
t: Tokenizer<'a>,
5063
}
5164

65+
impl Ident {
66+
pub fn as_str(&self) -> &str {
67+
&self.name
68+
}
69+
}
70+
71+
impl Hash for Ident {
72+
fn hash<H: Hasher>(&self, state: &mut H) {
73+
self.name.hash(state);
74+
}
75+
}
76+
77+
impl PartialEq<str> for Ident {
78+
fn eq(&self, other: &str) -> bool {
79+
self.name == other
80+
}
81+
}
82+
83+
impl PartialEq<&str> for Ident {
84+
fn eq(&self, other: &&str) -> bool {
85+
self.name == *other
86+
}
87+
}
88+
89+
impl PartialEq<Ident> for Ident {
90+
fn eq(&self, other: &Ident) -> bool {
91+
self.name == other.name
92+
}
93+
}
94+
95+
impl fmt::Display for Ident {
96+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97+
if self.raw {
98+
f.write_str("r#")?;
99+
}
100+
f.write_str(&*self.name)
101+
}
102+
}
103+
52104
impl FromStr for Cfg {
53105
type Err = ParseError;
54106

@@ -152,7 +204,8 @@ impl<'a> Parser<'a> {
152204

153205
fn expr(&mut self) -> Result<CfgExpr, ParseError> {
154206
match self.peek() {
155-
Some(Ok(Token::Ident(op @ "all"))) | Some(Ok(Token::Ident(op @ "any"))) => {
207+
Some(Ok(Token::Ident(false, op @ "all")))
208+
| Some(Ok(Token::Ident(false, op @ "any"))) => {
156209
self.t.next();
157210
let mut e = Vec::new();
158211
self.eat(&Token::LeftParen)?;
@@ -169,7 +222,7 @@ impl<'a> Parser<'a> {
169222
Ok(CfgExpr::Any(e))
170223
}
171224
}
172-
Some(Ok(Token::Ident("not"))) => {
225+
Some(Ok(Token::Ident(false, "not"))) => {
173226
self.t.next();
174227
self.eat(&Token::LeftParen)?;
175228
let e = self.expr()?;
@@ -187,7 +240,7 @@ impl<'a> Parser<'a> {
187240

188241
fn cfg(&mut self) -> Result<Cfg, ParseError> {
189242
match self.t.next() {
190-
Some(Ok(Token::Ident(name))) => {
243+
Some(Ok(Token::Ident(raw, name))) => {
191244
let e = if self.r#try(&Token::Equals) {
192245
let val = match self.t.next() {
193246
Some(Ok(Token::String(s))) => s,
@@ -205,9 +258,18 @@ impl<'a> Parser<'a> {
205258
return Err(ParseError::new(self.t.orig, IncompleteExpr("a string")))
206259
}
207260
};
208-
Cfg::KeyPair(name.to_string(), val.to_string())
261+
Cfg::KeyPair(
262+
Ident {
263+
name: name.to_string(),
264+
raw,
265+
},
266+
val.to_string(),
267+
)
209268
} else {
210-
Cfg::Name(name.to_string())
269+
Cfg::Name(Ident {
270+
name: name.to_string(),
271+
raw,
272+
})
211273
};
212274
Ok(e)
213275
}
@@ -287,14 +349,44 @@ impl<'a> Iterator for Tokenizer<'a> {
287349
return Some(Err(ParseError::new(self.orig, UnterminatedString)));
288350
}
289351
Some((start, ch)) if is_ident_start(ch) => {
352+
let (start, raw) = if ch == 'r' {
353+
if let Some(&(_pos, '#')) = self.s.peek() {
354+
// starts with `r#` is a raw ident
355+
self.s.next();
356+
if let Some((start, ch)) = self.s.next() {
357+
if is_ident_start(ch) {
358+
(start, true)
359+
} else {
360+
// not a starting ident character
361+
return Some(Err(ParseError::new(
362+
self.orig,
363+
UnexpectedChar(ch),
364+
)));
365+
}
366+
} else {
367+
// not followed by a ident, error out
368+
return Some(Err(ParseError::new(
369+
self.orig,
370+
IncompleteExpr("identifier"),
371+
)));
372+
}
373+
} else {
374+
// starts with `r` but not does continue with `#`
375+
// cannot be a raw ident
376+
(start, false)
377+
}
378+
} else {
379+
// do not start with `r`, cannot be a raw ident
380+
(start, false)
381+
};
290382
while let Some(&(end, ch)) = self.s.peek() {
291383
if !is_ident_rest(ch) {
292-
return Some(Ok(Token::Ident(&self.orig[start..end])));
384+
return Some(Ok(Token::Ident(raw, &self.orig[start..end])));
293385
} else {
294386
self.s.next();
295387
}
296388
}
297-
return Some(Ok(Token::Ident(&self.orig[start..])));
389+
return Some(Ok(Token::Ident(raw, &self.orig[start..])));
298390
}
299391
Some((_, ch)) => {
300392
return Some(Err(ParseError::new(self.orig, UnexpectedChar(ch))));

crates/cargo-platform/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ mod cfg;
1818
mod error;
1919

2020
use cfg::KEYWORDS;
21-
pub use cfg::{Cfg, CfgExpr};
21+
pub use cfg::{Cfg, CfgExpr, Ident};
2222
pub use error::{ParseError, ParseErrorKind};
2323

2424
/// Platform definition.

crates/cargo-platform/tests/test_cfg.rs

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,37 @@
1-
use cargo_platform::{Cfg, CfgExpr, Platform};
1+
use cargo_platform::{Cfg, CfgExpr, Ident, Platform};
22
use std::fmt;
33
use std::str::FromStr;
44

55
macro_rules! c {
66
($a:ident) => {
7-
Cfg::Name(stringify!($a).to_string())
7+
Cfg::Name(Ident {
8+
name: stringify!($a).to_string(),
9+
raw: false,
10+
})
11+
};
12+
(r # $a:ident) => {
13+
Cfg::Name(Ident {
14+
name: stringify!($a).to_string(),
15+
raw: true,
16+
})
817
};
918
($a:ident = $e:expr) => {
10-
Cfg::KeyPair(stringify!($a).to_string(), $e.to_string())
19+
Cfg::KeyPair(
20+
Ident {
21+
name: stringify!($a).to_string(),
22+
raw: false,
23+
},
24+
$e.to_string(),
25+
)
26+
};
27+
(r # $a:ident = $e:expr) => {
28+
Cfg::KeyPair(
29+
Ident {
30+
name: stringify!($a).to_string(),
31+
raw: true,
32+
},
33+
$e.to_string(),
34+
)
1135
};
1236
}
1337

@@ -56,10 +80,13 @@ fn cfg_syntax() {
5680
good("_bar", c!(_bar));
5781
good(" foo", c!(foo));
5882
good(" foo ", c!(foo));
83+
good("r#foo", c!(r # foo));
5984
good(" foo = \"bar\"", c!(foo = "bar"));
6085
good("foo=\"\"", c!(foo = ""));
86+
good("r#foo=\"\"", c!(r # foo = ""));
6187
good(" foo=\"3\" ", c!(foo = "3"));
6288
good("foo = \"3 e\"", c!(foo = "3 e"));
89+
good(" r#foo = \"3 e\"", c!(r # foo = "3 e"));
6390
}
6491

6592
#[test]
@@ -78,6 +105,10 @@ fn cfg_syntax_bad() {
78105
"foo, bar",
79106
"unexpected content `, bar` found after cfg expression",
80107
);
108+
bad::<Cfg>("r# foo", "unexpected character");
109+
bad::<Cfg>("r #foo", "unexpected content");
110+
bad::<Cfg>("r#\"foo\"", "unexpected character");
111+
bad::<Cfg>("foo = r#\"\"", "unexpected character");
81112
}
82113

83114
#[test]
@@ -126,6 +157,9 @@ fn cfg_matches() {
126157
assert!(e!(not(foo)).matches(&[]));
127158
assert!(e!(any((not(foo)), (all(foo, bar)))).matches(&[c!(bar)]));
128159
assert!(e!(any((not(foo)), (all(foo, bar)))).matches(&[c!(foo), c!(bar)]));
160+
assert!(e!(foo).matches(&[c!(r # foo)]));
161+
assert!(e!(r # foo).matches(&[c!(foo)]));
162+
assert!(e!(r # foo).matches(&[c!(r # foo)]));
129163

130164
assert!(!e!(foo).matches(&[]));
131165
assert!(!e!(foo).matches(&[c!(bar)]));

src/cargo/core/compiler/custom_build.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,9 @@ fn build_work(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResul
353353
// That is because Cargo queries rustc without any profile settings.
354354
continue;
355355
}
356-
let k = format!("CARGO_CFG_{}", super::envify(&k));
356+
// FIXME: We should handle raw-idents somehow instead of predenting they
357+
// don't exist here
358+
let k = format!("CARGO_CFG_{}", super::envify(k.as_str()));
357359
cmd.env(&k, v.join(","));
358360
}
359361

tests/testsuite/cfg.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -543,12 +543,15 @@ fn cfg_raw_idents() {
543543
.build();
544544

545545
p.cargo("check")
546-
.with_status(101)
547546
.with_stderr_data(str![[r#"
548-
[ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml`
549-
550-
Caused by:
551-
failed to parse `any(r#true, r#all, r#target_os = "<>")` as a cfg expression: unexpected character `#` in cfg, expected parens, a comma, an identifier, or a string
547+
[WARNING] [[ROOT]/foo/Cargo.toml] future-incompatibility: the meaning of `cfg(r#true)` will change in the future
548+
| Cargo is erroneously allowing `cfg(true)` and `cfg(false)`, but both forms are interpreted as false unless manually overridden with `--cfg`.
549+
| In the future these will be built-in defines that will have the corresponding true/false value.
550+
| It is recommended to avoid using these configs until they are properly supported.
551+
| See <https://github.com/rust-lang/rust/issues/131204> for more information.
552+
[LOCKING] 1 package to latest compatible version
553+
[CHECKING] foo v0.1.0 ([ROOT]/foo)
554+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
552555
553556
"#]])
554557
.run();
@@ -577,7 +580,7 @@ fn cfg_raw_idents_empty() {
577580
[ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml`
578581
579582
Caused by:
580-
failed to parse `r#)` as a cfg expression: unexpected content `#)` found after cfg expression
583+
failed to parse `r#)` as a cfg expression: unexpected character `)` in cfg, expected parens, a comma, an identifier, or a string
581584
582585
"#]])
583586
.run();
@@ -606,7 +609,7 @@ fn cfg_raw_idents_not_really() {
606609
[ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml`
607610
608611
Caused by:
609-
failed to parse `r#11)` as a cfg expression: unexpected content `#11)` found after cfg expression
612+
failed to parse `r#11)` as a cfg expression: unexpected character `1` in cfg, expected parens, a comma, an identifier, or a string
610613
611614
"#]])
612615
.run();

0 commit comments

Comments
 (0)