diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-10-23 11:38:30 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2020-10-23 11:38:30 +0100 |
commit | 81609960fa30ea92e37b23dc7b025d1939626812 (patch) | |
tree | 835866b358bd597e1a2c481b93641418c2562c86 /crates/cfg/src/cfg_expr.rs | |
parent | 8b3c851dd37f39f79e7e8807378f45fdde7f6411 (diff) | |
parent | a246d4f599dcf2612fa99720fb4ca98101ed93fb (diff) |
Merge #6324
6324: Improve #[cfg] diagnostics r=jonas-schievink a=jonas-schievink
Unfortunately I ran into https://github.com/rust-analyzer/rust-analyzer/issues/4058 while testing this on https://github.com/nrf-rs/nrf-hal/, so I didn't see much of it in action yet, but it does seem to work.
Co-authored-by: Jonas Schievink <[email protected]>
Co-authored-by: Jonas Schievink <[email protected]>
Diffstat (limited to 'crates/cfg/src/cfg_expr.rs')
-rw-r--r-- | crates/cfg/src/cfg_expr.rs | 113 |
1 files changed, 55 insertions, 58 deletions
diff --git a/crates/cfg/src/cfg_expr.rs b/crates/cfg/src/cfg_expr.rs index 336fe25bc..42327f1e1 100644 --- a/crates/cfg/src/cfg_expr.rs +++ b/crates/cfg/src/cfg_expr.rs | |||
@@ -2,30 +2,77 @@ | |||
2 | //! | 2 | //! |
3 | //! See: https://doc.rust-lang.org/reference/conditional-compilation.html#conditional-compilation | 3 | //! See: https://doc.rust-lang.org/reference/conditional-compilation.html#conditional-compilation |
4 | 4 | ||
5 | use std::slice::Iter as SliceIter; | 5 | use std::{fmt, slice::Iter as SliceIter}; |
6 | 6 | ||
7 | use tt::SmolStr; | 7 | use tt::SmolStr; |
8 | 8 | ||
9 | /// A simple configuration value passed in from the outside. | ||
10 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] | ||
11 | pub enum CfgAtom { | ||
12 | /// eg. `#[cfg(test)]` | ||
13 | Flag(SmolStr), | ||
14 | /// eg. `#[cfg(target_os = "linux")]` | ||
15 | /// | ||
16 | /// Note that a key can have multiple values that are all considered "active" at the same time. | ||
17 | /// For example, `#[cfg(target_feature = "sse")]` and `#[cfg(target_feature = "sse2")]`. | ||
18 | KeyValue { key: SmolStr, value: SmolStr }, | ||
19 | } | ||
20 | |||
21 | impl CfgAtom { | ||
22 | /// Returns `true` when the atom comes from the target specification. | ||
23 | /// | ||
24 | /// If this returns `true`, then changing this atom requires changing the compilation target. If | ||
25 | /// it returns `false`, the atom might come from a build script or the build system. | ||
26 | pub fn is_target_defined(&self) -> bool { | ||
27 | match self { | ||
28 | CfgAtom::Flag(flag) => matches!(&**flag, "unix" | "windows"), | ||
29 | CfgAtom::KeyValue { key, value: _ } => matches!( | ||
30 | &**key, | ||
31 | "target_arch" | ||
32 | | "target_os" | ||
33 | | "target_env" | ||
34 | | "target_family" | ||
35 | | "target_endian" | ||
36 | | "target_pointer_width" | ||
37 | | "target_vendor" // NOTE: `target_feature` is left out since it can be configured via `-Ctarget-feature` | ||
38 | ), | ||
39 | } | ||
40 | } | ||
41 | } | ||
42 | |||
43 | impl fmt::Display for CfgAtom { | ||
44 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
45 | match self { | ||
46 | CfgAtom::Flag(name) => write!(f, "{}", name), | ||
47 | CfgAtom::KeyValue { key, value } => write!(f, "{} = {:?}", key, value), | ||
48 | } | ||
49 | } | ||
50 | } | ||
51 | |||
9 | #[derive(Debug, Clone, PartialEq, Eq)] | 52 | #[derive(Debug, Clone, PartialEq, Eq)] |
10 | pub enum CfgExpr { | 53 | pub enum CfgExpr { |
11 | Invalid, | 54 | Invalid, |
12 | Atom(SmolStr), | 55 | Atom(CfgAtom), |
13 | KeyValue { key: SmolStr, value: SmolStr }, | ||
14 | All(Vec<CfgExpr>), | 56 | All(Vec<CfgExpr>), |
15 | Any(Vec<CfgExpr>), | 57 | Any(Vec<CfgExpr>), |
16 | Not(Box<CfgExpr>), | 58 | Not(Box<CfgExpr>), |
17 | } | 59 | } |
18 | 60 | ||
61 | impl From<CfgAtom> for CfgExpr { | ||
62 | fn from(atom: CfgAtom) -> Self { | ||
63 | CfgExpr::Atom(atom) | ||
64 | } | ||
65 | } | ||
66 | |||
19 | impl CfgExpr { | 67 | impl CfgExpr { |
20 | pub fn parse(tt: &tt::Subtree) -> CfgExpr { | 68 | pub fn parse(tt: &tt::Subtree) -> CfgExpr { |
21 | next_cfg_expr(&mut tt.token_trees.iter()).unwrap_or(CfgExpr::Invalid) | 69 | next_cfg_expr(&mut tt.token_trees.iter()).unwrap_or(CfgExpr::Invalid) |
22 | } | 70 | } |
23 | /// Fold the cfg by querying all basic `Atom` and `KeyValue` predicates. | 71 | /// Fold the cfg by querying all basic `Atom` and `KeyValue` predicates. |
24 | pub fn fold(&self, query: &dyn Fn(&SmolStr, Option<&SmolStr>) -> bool) -> Option<bool> { | 72 | pub fn fold(&self, query: &dyn Fn(&CfgAtom) -> bool) -> Option<bool> { |
25 | match self { | 73 | match self { |
26 | CfgExpr::Invalid => None, | 74 | CfgExpr::Invalid => None, |
27 | CfgExpr::Atom(name) => Some(query(name, None)), | 75 | CfgExpr::Atom(atom) => Some(query(atom)), |
28 | CfgExpr::KeyValue { key, value } => Some(query(key, Some(value))), | ||
29 | CfgExpr::All(preds) => { | 76 | CfgExpr::All(preds) => { |
30 | preds.iter().try_fold(true, |s, pred| Some(s && pred.fold(query)?)) | 77 | preds.iter().try_fold(true, |s, pred| Some(s && pred.fold(query)?)) |
31 | } | 78 | } |
@@ -54,7 +101,7 @@ fn next_cfg_expr(it: &mut SliceIter<tt::TokenTree>) -> Option<CfgExpr> { | |||
54 | // FIXME: escape? raw string? | 101 | // FIXME: escape? raw string? |
55 | let value = | 102 | let value = |
56 | SmolStr::new(literal.text.trim_start_matches('"').trim_end_matches('"')); | 103 | SmolStr::new(literal.text.trim_start_matches('"').trim_end_matches('"')); |
57 | CfgExpr::KeyValue { key: name, value } | 104 | CfgAtom::KeyValue { key: name, value }.into() |
58 | } | 105 | } |
59 | _ => return Some(CfgExpr::Invalid), | 106 | _ => return Some(CfgExpr::Invalid), |
60 | } | 107 | } |
@@ -70,7 +117,7 @@ fn next_cfg_expr(it: &mut SliceIter<tt::TokenTree>) -> Option<CfgExpr> { | |||
70 | _ => CfgExpr::Invalid, | 117 | _ => CfgExpr::Invalid, |
71 | } | 118 | } |
72 | } | 119 | } |
73 | _ => CfgExpr::Atom(name), | 120 | _ => CfgAtom::Flag(name).into(), |
74 | }; | 121 | }; |
75 | 122 | ||
76 | // Eat comma separator | 123 | // Eat comma separator |
@@ -81,53 +128,3 @@ fn next_cfg_expr(it: &mut SliceIter<tt::TokenTree>) -> Option<CfgExpr> { | |||
81 | } | 128 | } |
82 | Some(ret) | 129 | Some(ret) |
83 | } | 130 | } |
84 | |||
85 | #[cfg(test)] | ||
86 | mod tests { | ||
87 | use super::*; | ||
88 | |||
89 | use mbe::ast_to_token_tree; | ||
90 | use syntax::ast::{self, AstNode}; | ||
91 | |||
92 | fn assert_parse_result(input: &str, expected: CfgExpr) { | ||
93 | let (tt, _) = { | ||
94 | let source_file = ast::SourceFile::parse(input).ok().unwrap(); | ||
95 | let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); | ||
96 | ast_to_token_tree(&tt).unwrap() | ||
97 | }; | ||
98 | let cfg = CfgExpr::parse(&tt); | ||
99 | assert_eq!(cfg, expected); | ||
100 | } | ||
101 | |||
102 | #[test] | ||
103 | fn test_cfg_expr_parser() { | ||
104 | assert_parse_result("#![cfg(foo)]", CfgExpr::Atom("foo".into())); | ||
105 | assert_parse_result("#![cfg(foo,)]", CfgExpr::Atom("foo".into())); | ||
106 | assert_parse_result( | ||
107 | "#![cfg(not(foo))]", | ||
108 | CfgExpr::Not(Box::new(CfgExpr::Atom("foo".into()))), | ||
109 | ); | ||
110 | assert_parse_result("#![cfg(foo(bar))]", CfgExpr::Invalid); | ||
111 | |||
112 | // Only take the first | ||
113 | assert_parse_result(r#"#![cfg(foo, bar = "baz")]"#, CfgExpr::Atom("foo".into())); | ||
114 | |||
115 | assert_parse_result( | ||
116 | r#"#![cfg(all(foo, bar = "baz"))]"#, | ||
117 | CfgExpr::All(vec![ | ||
118 | CfgExpr::Atom("foo".into()), | ||
119 | CfgExpr::KeyValue { key: "bar".into(), value: "baz".into() }, | ||
120 | ]), | ||
121 | ); | ||
122 | |||
123 | assert_parse_result( | ||
124 | r#"#![cfg(any(not(), all(), , bar = "baz",))]"#, | ||
125 | CfgExpr::Any(vec![ | ||
126 | CfgExpr::Not(Box::new(CfgExpr::Invalid)), | ||
127 | CfgExpr::All(vec![]), | ||
128 | CfgExpr::Invalid, | ||
129 | CfgExpr::KeyValue { key: "bar".into(), value: "baz".into() }, | ||
130 | ]), | ||
131 | ); | ||
132 | } | ||
133 | } | ||