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::{OwnedTargetIsa, 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<OwnedTargetIsa>),
23 }
24 
25 impl IsaSpec {
26     /// If the `IsaSpec` contains exactly 1 `TargetIsa` we return a reference to it
unique_isa(&self) -> Option<&dyn TargetIsa>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 {
from(err: ParseOptionError) -> Self62     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 ///
91 /// Note that parsing terminates after the first error is encountered.
parse_options<'a, I>( iter: I, config: &mut dyn Configurable, loc: Location, ) -> Result<(), ParseOptionError> where I: Iterator<Item = &'a str>,92 pub fn parse_options<'a, I>(
93     iter: I,
94     config: &mut dyn Configurable,
95     loc: Location,
96 ) -> Result<(), ParseOptionError>
97 where
98     I: Iterator<Item = &'a str>,
99 {
100     for opt in iter {
101         parse_option(opt, config, loc)?;
102     }
103     Ok(())
104 }
105 
106 /// Parse an single command line options and apply it to `config`.
parse_option( opt: &str, config: &mut dyn Configurable, loc: Location, ) -> Result<(), ParseOptionError>107 pub fn parse_option(
108     opt: &str,
109     config: &mut dyn Configurable,
110     loc: Location,
111 ) -> Result<(), ParseOptionError> {
112     match TestOption::new(opt) {
113         TestOption::Flag(name) => match config.enable(name) {
114             Ok(_) => Ok(()),
115             Err(SetError::BadName(name)) => Err(ParseOptionError::UnknownFlag { loc, name }),
116             Err(_) => option_err!(loc, "not a boolean flag: '{}'", opt),
117         },
118         TestOption::Value(name, value) => match config.set(name, value) {
119             Ok(_) => Ok(()),
120             Err(SetError::BadName(name)) => Err(ParseOptionError::UnknownValue {
121                 loc,
122                 name,
123                 value: value.to_string(),
124             }),
125             Err(SetError::BadType) => option_err!(loc, "invalid setting type: '{}'", opt),
126             Err(SetError::BadValue(expected)) => {
127                 option_err!(
128                     loc,
129                     "invalid setting value for '{}', expected {}",
130                     opt,
131                     expected
132                 )
133             }
134         },
135     }
136 }
137