1use crate::{CompilerOutput, CompilerStage, Dump, ErrorFormat, EvmVersion, Language, Threads};
4use std::{num::NonZeroUsize, path::PathBuf};
5
6#[cfg(feature = "clap")]
7use clap::{ColorChoice, Parser, ValueHint};
8
9#[derive(Clone, Debug, Default)]
11#[cfg_attr(feature = "clap", derive(Parser))]
12#[cfg_attr(feature = "clap", command(
13 name = "solar",
14 version = crate::version::SHORT_VERSION,
15 long_version = crate::version::LONG_VERSION,
16 arg_required_else_help = true,
17))]
18#[allow(clippy::manual_non_exhaustive)]
19pub struct Opts {
20 #[cfg_attr(feature = "clap", arg(value_hint = ValueHint::FilePath))]
27 pub input: Vec<String>,
28 #[cfg_attr(
32 feature = "clap",
33 arg(
34 help_heading = "Input options",
35 long,
36 short = 'I',
37 alias = "import-path",
38 value_hint = ValueHint::DirPath,
39 )
40 )]
41 pub include_path: Vec<PathBuf>,
42 #[cfg_attr(
44 feature = "clap",
45 arg(help_heading = "Input options", long, value_enum, default_value_t, hide = true)
46 )]
47 pub language: Language,
48
49 #[cfg_attr(feature = "clap", arg(long, short = 'j', visible_alias = "jobs", default_value_t))]
51 pub threads: Threads,
52 #[cfg_attr(feature = "clap", arg(long, value_enum, default_value_t))]
54 pub evm_version: EvmVersion,
55 #[cfg_attr(feature = "clap", arg(long, value_enum))]
57 pub stop_after: Option<CompilerStage>,
58
59 #[cfg_attr(feature = "clap", arg(long, value_hint = ValueHint::DirPath))]
61 pub out_dir: Option<PathBuf>,
62 #[cfg_attr(feature = "clap", arg(long, value_delimiter = ','))]
64 pub emit: Vec<CompilerOutput>,
65
66 #[cfg(feature = "clap")] #[cfg_attr(
69 feature = "clap",
70 arg(help_heading = "Display options", long, value_enum, default_value = "auto")
71 )]
72 pub color: ColorChoice,
73 #[cfg_attr(feature = "clap", arg(help_heading = "Display options", long, short))]
75 pub verbose: bool,
76 #[cfg_attr(feature = "clap", arg(help_heading = "Display options", long))]
80 pub pretty_json: bool,
81 #[cfg_attr(feature = "clap", arg(help_heading = "Display options", long))]
83 pub pretty_json_err: bool,
84 #[cfg_attr(
86 feature = "clap",
87 arg(help_heading = "Display options", long, value_enum, default_value_t)
88 )]
89 pub error_format: ErrorFormat,
90
91 #[doc(hidden)]
95 #[cfg_attr(feature = "clap", arg(id = "unstable-features", value_name = "FLAG", short = 'Z'))]
96 pub _unstable: Vec<String>,
97
98 #[cfg_attr(feature = "clap", arg(skip))]
100 pub unstable: UnstableOpts,
101
102 #[doc(hidden)]
104 #[cfg_attr(feature = "clap", arg(skip))]
105 pub _non_exhaustive: (),
106}
107
108impl Opts {
109 #[inline]
111 pub fn threads(&self) -> NonZeroUsize {
112 self.threads.0
113 }
114
115 #[cfg(feature = "clap")]
120 pub fn finish(&mut self) -> Result<(), clap::Error> {
121 if !self._unstable.is_empty() {
122 let hack = self._unstable.iter().map(|s| format!("--{s}"));
123 self.unstable =
124 UnstableOpts::try_parse_from(std::iter::once(String::new()).chain(hack))?;
125 }
126 Ok(())
127 }
128}
129
130#[derive(Clone, Debug, Default)]
132#[cfg_attr(feature = "clap", derive(Parser))]
133#[cfg_attr(feature = "clap", clap(
134 disable_help_flag = true,
135 before_help = concat!(
136 "List of all unstable flags.\n",
137 "WARNING: these are completely unstable, and may change at any time!\n",
138 " NOTE: the following flags should be passed on the command-line using `-Z`, not `--`",
140 ),
141 help_template = "{before-help}{all-args}"
142))]
143#[allow(clippy::manual_non_exhaustive)]
144pub struct UnstableOpts {
145 #[cfg_attr(feature = "clap", arg(long))]
147 pub ui_testing: bool,
148
149 #[cfg_attr(feature = "clap", arg(long))]
153 pub track_diagnostics: bool,
154
155 #[cfg_attr(feature = "clap", arg(long))]
157 pub parse_yul: bool,
158
159 #[cfg_attr(feature = "clap", arg(long, value_name = "KIND[=PATHS...]"))]
163 pub dump: Option<Dump>,
164
165 #[cfg_attr(feature = "clap", arg(long))]
167 pub ast_stats: bool,
168
169 #[cfg_attr(feature = "clap", arg(long, action = clap::ArgAction::Help))]
171 pub help: (),
172
173 #[doc(hidden)]
175 #[cfg_attr(feature = "clap", arg(skip))]
176 pub _non_exhaustive: (),
177
178 #[cfg(test)]
179 #[cfg_attr(feature = "clap", arg(long))]
180 pub test_bool: bool,
181 #[cfg(test)]
182 #[cfg_attr(feature = "clap", arg(long))]
183 pub test_value: Option<usize>,
184}
185
186#[cfg(all(test, feature = "clap"))]
187mod tests {
188 use super::*;
189 use clap::CommandFactory;
190
191 #[test]
192 fn verify_cli() {
193 Opts::command().debug_assert();
194 let _ = Opts::default();
195 UnstableOpts::command().debug_assert();
196 let _ = UnstableOpts::default();
197 }
198
199 #[test]
200 fn unstable_features() {
201 fn parse(args: &[&str]) -> Result<UnstableOpts, impl std::fmt::Debug> {
202 struct UnwrapDisplay<T>(T);
203 impl<T: std::fmt::Display> std::fmt::Debug for UnwrapDisplay<T> {
204 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
205 write!(f, "\n{}", self.0)
206 }
207 }
208 (|| {
209 let mut opts = Opts::try_parse_from(args)?;
210 opts.finish()?;
211 Ok::<_, clap::Error>(opts.unstable)
212 })()
213 .map_err(|e| UnwrapDisplay(e.render().ansi().to_string()))
214 }
215
216 let unstable = parse(&["solar", "a.sol"]).unwrap();
217 assert!(!unstable.test_bool);
218
219 let unstable = parse(&["solar", "-Ztest-bool", "a.sol"]).unwrap();
220 assert!(unstable.test_bool);
221 let unstable = parse(&["solar", "-Z", "test-bool", "a.sol"]).unwrap();
222 assert!(unstable.test_bool);
223
224 assert!(parse(&["solar", "-Ztest-value", "a.sol"]).is_err());
225 assert!(parse(&["solar", "-Z", "test-value", "a.sol"]).is_err());
226 assert!(parse(&["solar", "-Ztest-value", "2", "a.sol"]).is_err());
227 let unstable = parse(&["solar", "-Ztest-value=2", "a.sol"]).unwrap();
228 assert_eq!(unstable.test_value, Some(2));
229 let unstable = parse(&["solar", "-Z", "test-value=2", "a.sol"]).unwrap();
230 assert_eq!(unstable.test_value, Some(2));
231
232 let unstable = parse(&["solar", "-Zast-stats", "a.sol"]).unwrap();
233 assert!(unstable.ast_stats);
234 }
235}