1 //! Parsed representation of `set` and `isa` commands.
2 //!
3 //! A test case file can contain `set` commands that set ISA-independent settings, and it can
4 //! contain `isa` commands that select an ISA and applies ISA-specific settings.
5 //!
6 //! If a test case file contains `isa` commands, the tests will only be run against the specified
7 //! ISAs. If the file contains no `isa` commands, the tests will be run against all supported ISAs.
8 
9 use crate::error::{Location, ParseError};
10 use crate::testcommand::TestOption;
11 use cranelift_codegen::isa::TargetIsa;
12 use cranelift_codegen::settings::{Configurable, Flags, SetError};
13 
14 /// The ISA specifications in a `.clif` file.
15 pub enum IsaSpec {
16     /// The parsed file does not contain any `isa` commands, but it may contain `set` commands
17     /// which are reflected in the finished `Flags` object.
18     None(Flags),
19 
20     /// The parsed file does contain `isa` commands.
21     /// Each `isa` command is used to configure a `TargetIsa` trait object.
22     Some(Vec<Box<dyn TargetIsa>>),
23 }
24 
25 impl IsaSpec {
26     /// If the `IsaSpec` contains exactly 1 `TargetIsa` we return a reference to it
27     pub fn unique_isa(&self) -> Option<&dyn TargetIsa> {
28         if let Self::Some(ref isa_vec) = *self {
29             if isa_vec.len() == 1 {
30                 return Some(&*isa_vec[0]);
31             }
32         }
33         None
34     }
35 }
36 
37 /// An error type returned by `parse_options`.
38 pub enum ParseOptionError {
39     /// A generic ParseError.
40     Generic(ParseError),
41 
42     /// An unknown flag was used, with the given name at the given location.
43     UnknownFlag {
44         /// Location where the flag was given.
45         loc: Location,
46         /// Name of the unknown flag.
47         name: String,
48     },
49 
50     /// An unknown value was used, with the given name at the given location.
51     UnknownValue {
52         /// Location where the flag was given.
53         loc: Location,
54         /// Name of the unknown value.
55         name: String,
56         /// Value of the unknown value.
57         value: String,
58     },
59 }
60 
61 impl From<ParseOptionError> for ParseError {
62     fn from(err: ParseOptionError) -> Self {
63         match err {
64             ParseOptionError::Generic(err) => err,
65             ParseOptionError::UnknownFlag { loc, name } => Self {
66                 location: loc,
67                 message: format!("unknown setting '{}'", name),
68                 is_warning: false,
69             },
70             ParseOptionError::UnknownValue { loc, name, value } => Self {
71                 location: loc,
72                 message: format!("unknown setting '{}={}'", name, value),
73                 is_warning: false,
74             },
75         }
76     }
77 }
78 
79 macro_rules! option_err {
80     ( $loc:expr, $fmt:expr, $( $arg:expr ),+ ) => {
81         Err($crate::ParseOptionError::Generic($crate::ParseError {
82             location: $loc.clone(),
83             message: format!( $fmt, $( $arg ),+ ),
84             is_warning: false,
85         }))
86     };
87 }
88 
89 /// Parse an iterator of command line options and apply them to `config`.
90 pub fn parse_options<'a, I>(
91     iter: I,
92     config: &mut dyn Configurable,
93     loc: Location,
94 ) -> Result<(), ParseOptionError>
95 where
96     I: Iterator<Item = &'a str>,
97 {
98     for opt in iter.map(TestOption::new) {
99         match opt {
100             TestOption::Flag(name) => match config.enable(name) {
101                 Ok(_) => {}
102                 Err(SetError::BadName(name)) => {
103                     return Err(ParseOptionError::UnknownFlag { loc, name })
104                 }
105                 Err(_) => return option_err!(loc, "not a boolean flag: '{}'", opt),
106             },
107             TestOption::Value(name, value) => match config.set(name, value) {
108                 Ok(_) => {}
109                 Err(SetError::BadName(name)) => {
110                     return Err(ParseOptionError::UnknownValue {
111                         loc,
112                         name,
113                         value: value.to_string(),
114                     })
115                 }
116                 Err(SetError::BadType) => {
117                     return option_err!(loc, "invalid setting type: '{}'", opt)
118                 }
119                 Err(SetError::BadValue(expected)) => {
120                     return option_err!(
121                         loc,
122                         "invalid setting value for '{}', expected {}",
123                         opt,
124                         expected
125                     );
126                 }
127             },
128         }
129     }
130     Ok(())
131 }
132