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 #[cfg_attr(feature = "clap", arg(help_heading = "Display options", long))]
92 pub no_warnings: bool,
93
94 #[doc(hidden)]
98 #[cfg_attr(feature = "clap", arg(id = "unstable-features", value_name = "FLAG", short = 'Z'))]
99 pub _unstable: Vec<String>,
100
101 #[cfg_attr(feature = "clap", arg(skip))]
103 pub unstable: UnstableOpts,
104
105 #[doc(hidden)]
107 #[cfg_attr(feature = "clap", arg(skip))]
108 pub _non_exhaustive: (),
109}
110
111impl Opts {
112 #[inline]
114 pub fn threads(&self) -> NonZeroUsize {
115 self.threads.0
116 }
117
118 #[cfg(feature = "clap")]
123 pub fn finish(&mut self) -> Result<(), clap::Error> {
124 if !self._unstable.is_empty() {
125 let hack = self._unstable.iter().map(|s| format!("--{s}"));
126 self.unstable =
127 UnstableOpts::try_parse_from(std::iter::once(String::new()).chain(hack))?;
128 }
129 Ok(())
130 }
131}
132
133#[derive(Clone, Debug, Default)]
135#[cfg_attr(feature = "clap", derive(Parser))]
136#[cfg_attr(feature = "clap", clap(
137 disable_help_flag = true,
138 before_help = concat!(
139 "List of all unstable flags.\n",
140 "WARNING: these are completely unstable, and may change at any time!\n",
141 " NOTE: the following flags should be passed on the command-line using `-Z`, not `--`",
143 ),
144 help_template = "{before-help}{all-args}"
145))]
146#[allow(clippy::manual_non_exhaustive)]
147pub struct UnstableOpts {
148 #[cfg_attr(feature = "clap", arg(long))]
150 pub ui_testing: bool,
151
152 #[cfg_attr(feature = "clap", arg(long))]
156 pub track_diagnostics: bool,
157
158 #[cfg_attr(feature = "clap", arg(long))]
160 pub parse_yul: bool,
161
162 #[cfg_attr(feature = "clap", arg(long, value_name = "KIND[=PATHS...]"))]
166 pub dump: Option<Dump>,
167
168 #[cfg_attr(feature = "clap", arg(long))]
170 pub ast_stats: bool,
171
172 #[cfg_attr(feature = "clap", arg(long))]
174 pub span_visitor: bool,
175
176 #[cfg_attr(feature = "clap", arg(long, action = clap::ArgAction::Help))]
178 pub help: (),
179
180 #[doc(hidden)]
182 #[cfg_attr(feature = "clap", arg(skip))]
183 pub _non_exhaustive: (),
184
185 #[cfg(test)]
186 #[cfg_attr(feature = "clap", arg(long))]
187 pub test_bool: bool,
188 #[cfg(test)]
189 #[cfg_attr(feature = "clap", arg(long))]
190 pub test_value: Option<usize>,
191}
192
193#[cfg(all(test, feature = "clap"))]
194mod tests {
195 use super::*;
196 use clap::CommandFactory;
197
198 #[test]
199 fn verify_cli() {
200 Opts::command().debug_assert();
201 let _ = Opts::default();
202 let _ = Opts { evm_version: EvmVersion::Berlin, ..Default::default() };
203
204 UnstableOpts::command().debug_assert();
205 let _ = UnstableOpts::default();
206 let _ = UnstableOpts { ast_stats: false, ..Default::default() };
207 }
208
209 #[test]
210 fn unstable_features() {
211 fn parse(args: &[&str]) -> Result<UnstableOpts, impl std::fmt::Debug> {
212 struct UnwrapDisplay<T>(T);
213 impl<T: std::fmt::Display> std::fmt::Debug for UnwrapDisplay<T> {
214 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
215 write!(f, "\n{}", self.0)
216 }
217 }
218 (|| {
219 let mut opts = Opts::try_parse_from(args)?;
220 opts.finish()?;
221 Ok::<_, clap::Error>(opts.unstable)
222 })()
223 .map_err(|e| UnwrapDisplay(e.render().ansi().to_string()))
224 }
225
226 let unstable = parse(&["solar", "a.sol"]).unwrap();
227 assert!(!unstable.test_bool);
228
229 let unstable = parse(&["solar", "-Ztest-bool", "a.sol"]).unwrap();
230 assert!(unstable.test_bool);
231 let unstable = parse(&["solar", "-Z", "test-bool", "a.sol"]).unwrap();
232 assert!(unstable.test_bool);
233
234 assert!(parse(&["solar", "-Ztest-value", "a.sol"]).is_err());
235 assert!(parse(&["solar", "-Z", "test-value", "a.sol"]).is_err());
236 assert!(parse(&["solar", "-Ztest-value", "2", "a.sol"]).is_err());
237 let unstable = parse(&["solar", "-Ztest-value=2", "a.sol"]).unwrap();
238 assert_eq!(unstable.test_value, Some(2));
239 let unstable = parse(&["solar", "-Z", "test-value=2", "a.sol"]).unwrap();
240 assert_eq!(unstable.test_value, Some(2));
241
242 let unstable = parse(&["solar", "-Zast-stats", "a.sol"]).unwrap();
243 assert!(unstable.ast_stats);
244 }
245}