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