solar_config/
opts.rs

1//! Solar CLI arguments.
2
3use 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/// Blazingly fast Solidity compiler.
10#[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    /// Files to compile, or import remappings.
21    ///
22    /// `-` specifies standard input.
23    ///
24    /// Import remappings are specified as `[context:]prefix=path`.
25    /// See <https://docs.soliditylang.org/en/latest/path-resolution.html#import-remapping>.
26    #[cfg_attr(feature = "clap", arg(value_hint = ValueHint::FilePath))]
27    pub input: Vec<String>,
28    /// Directory to search for files.
29    ///
30    /// Can be used multiple times.
31    #[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    /// Source code language. Only Solidity is currently implemented.
43    #[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    /// Number of threads to use. Zero specifies the number of logical cores.
50    #[cfg_attr(feature = "clap", arg(long, short = 'j', visible_alias = "jobs", default_value_t))]
51    pub threads: Threads,
52    /// EVM version.
53    #[cfg_attr(feature = "clap", arg(long, value_enum, default_value_t))]
54    pub evm_version: EvmVersion,
55    /// Stop execution after the given compiler stage.
56    #[cfg_attr(feature = "clap", arg(long, value_enum))]
57    pub stop_after: Option<CompilerStage>,
58
59    /// Directory to write output files.
60    #[cfg_attr(feature = "clap", arg(long, value_hint = ValueHint::DirPath))]
61    pub out_dir: Option<PathBuf>,
62    /// Comma separated list of types of output for the compiler to emit.
63    #[cfg_attr(feature = "clap", arg(long, value_delimiter = ','))]
64    pub emit: Vec<CompilerOutput>,
65
66    /// Coloring.
67    #[cfg(feature = "clap")] // TODO
68    #[cfg_attr(
69        feature = "clap",
70        arg(help_heading = "Display options", long, value_enum, default_value = "auto")
71    )]
72    pub color: ColorChoice,
73    /// Use verbose output.
74    #[cfg_attr(feature = "clap", arg(help_heading = "Display options", long, short))]
75    pub verbose: bool,
76    /// Pretty-print JSON output.
77    ///
78    /// Does not include errors. See `--pretty-json-err`.
79    #[cfg_attr(feature = "clap", arg(help_heading = "Display options", long))]
80    pub pretty_json: bool,
81    /// Pretty-print error JSON output.
82    #[cfg_attr(feature = "clap", arg(help_heading = "Display options", long))]
83    pub pretty_json_err: bool,
84    /// How errors and other messages are produced.
85    #[cfg_attr(
86        feature = "clap",
87        arg(help_heading = "Display options", long, value_enum, default_value_t)
88    )]
89    pub error_format: ErrorFormat,
90    /// Whether to disable warnings.
91    #[cfg_attr(feature = "clap", arg(help_heading = "Display options", long))]
92    pub no_warnings: bool,
93
94    /// Unstable flags. WARNING: these are completely unstable, and may change at any time.
95    ///
96    /// See `-Zhelp` for more details.
97    #[doc(hidden)]
98    #[cfg_attr(feature = "clap", arg(id = "unstable-features", value_name = "FLAG", short = 'Z'))]
99    pub _unstable: Vec<String>,
100
101    /// Parsed unstable flags.
102    #[cfg_attr(feature = "clap", arg(skip))]
103    pub unstable: UnstableOpts,
104
105    // Allows `Opts { x: y, ..Default::default() }`.
106    #[doc(hidden)]
107    #[cfg_attr(feature = "clap", arg(skip))]
108    pub _non_exhaustive: (),
109}
110
111impl Opts {
112    /// Returns the number of threads to use.
113    #[inline]
114    pub fn threads(&self) -> NonZeroUsize {
115        self.threads.0
116    }
117
118    /// Finishes argument parsing.
119    ///
120    /// This currently only parses the `-Z` arguments into the `unstable` field, but may be extended
121    /// in the future.
122    #[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/// Internal options.
134#[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        // TODO: This is pretty hard to fix, as we don't have access to the `Command` in the derive macros.
142        "   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    /// Enables UI testing mode.
149    #[cfg_attr(feature = "clap", arg(long))]
150    pub ui_testing: bool,
151
152    /// Prints a note for every diagnostic that is emitted with the creation and emission location.
153    ///
154    /// This is enabled by default on debug builds.
155    #[cfg_attr(feature = "clap", arg(long))]
156    pub track_diagnostics: bool,
157
158    /// Enables parsing Yul files for testing.
159    #[cfg_attr(feature = "clap", arg(long))]
160    pub parse_yul: bool,
161
162    /// Print additional information about the compiler's internal state.
163    ///
164    /// Valid kinds are `ast` and `hir`.
165    #[cfg_attr(feature = "clap", arg(long, value_name = "KIND[=PATHS...]"))]
166    pub dump: Option<Dump>,
167
168    /// Print AST stats.
169    #[cfg_attr(feature = "clap", arg(long))]
170    pub ast_stats: bool,
171
172    /// Print help.
173    #[cfg_attr(feature = "clap", arg(long, action = clap::ArgAction::Help))]
174    pub help: (),
175
176    // Allows `UnstableOpts { x: y, ..Default::default() }`.
177    #[doc(hidden)]
178    #[cfg_attr(feature = "clap", arg(skip))]
179    pub _non_exhaustive: (),
180
181    #[cfg(test)]
182    #[cfg_attr(feature = "clap", arg(long))]
183    pub test_bool: bool,
184    #[cfg(test)]
185    #[cfg_attr(feature = "clap", arg(long))]
186    pub test_value: Option<usize>,
187}
188
189#[cfg(all(test, feature = "clap"))]
190mod tests {
191    use super::*;
192    use clap::CommandFactory;
193
194    #[test]
195    fn verify_cli() {
196        Opts::command().debug_assert();
197        let _ = Opts::default();
198        let _ = Opts { evm_version: EvmVersion::Berlin, ..Default::default() };
199
200        UnstableOpts::command().debug_assert();
201        let _ = UnstableOpts::default();
202        let _ = UnstableOpts { ast_stats: false, ..Default::default() };
203    }
204
205    #[test]
206    fn unstable_features() {
207        fn parse(args: &[&str]) -> Result<UnstableOpts, impl std::fmt::Debug> {
208            struct UnwrapDisplay<T>(T);
209            impl<T: std::fmt::Display> std::fmt::Debug for UnwrapDisplay<T> {
210                fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
211                    write!(f, "\n{}", self.0)
212                }
213            }
214            (|| {
215                let mut opts = Opts::try_parse_from(args)?;
216                opts.finish()?;
217                Ok::<_, clap::Error>(opts.unstable)
218            })()
219            .map_err(|e| UnwrapDisplay(e.render().ansi().to_string()))
220        }
221
222        let unstable = parse(&["solar", "a.sol"]).unwrap();
223        assert!(!unstable.test_bool);
224
225        let unstable = parse(&["solar", "-Ztest-bool", "a.sol"]).unwrap();
226        assert!(unstable.test_bool);
227        let unstable = parse(&["solar", "-Z", "test-bool", "a.sol"]).unwrap();
228        assert!(unstable.test_bool);
229
230        assert!(parse(&["solar", "-Ztest-value", "a.sol"]).is_err());
231        assert!(parse(&["solar", "-Z", "test-value", "a.sol"]).is_err());
232        assert!(parse(&["solar", "-Ztest-value", "2", "a.sol"]).is_err());
233        let unstable = parse(&["solar", "-Ztest-value=2", "a.sol"]).unwrap();
234        assert_eq!(unstable.test_value, Some(2));
235        let unstable = parse(&["solar", "-Z", "test-value=2", "a.sol"]).unwrap();
236        assert_eq!(unstable.test_value, Some(2));
237
238        let unstable = parse(&["solar", "-Zast-stats", "a.sol"]).unwrap();
239        assert!(unstable.ast_stats);
240    }
241}