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
91    /// Unstable flags. WARNING: these are completely unstable, and may change at any time.
92    ///
93    /// See `-Zhelp` for more details.
94    #[doc(hidden)]
95    #[cfg_attr(feature = "clap", arg(id = "unstable-features", value_name = "FLAG", short = 'Z'))]
96    pub _unstable: Vec<String>,
97
98    /// Parsed unstable flags.
99    #[cfg_attr(feature = "clap", arg(skip))]
100    pub unstable: UnstableOpts,
101
102    // Allows `Opts { x: y, ..Default::default() }`.
103    #[doc(hidden)]
104    #[cfg_attr(feature = "clap", arg(skip))]
105    pub _non_exhaustive: (),
106}
107
108impl Opts {
109    /// Returns the number of threads to use.
110    #[inline]
111    pub fn threads(&self) -> NonZeroUsize {
112        self.threads.0
113    }
114
115    /// Finishes argument parsing.
116    ///
117    /// This currently only parses the `-Z` arguments into the `unstable` field, but may be extended
118    /// in the future.
119    #[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/// Internal options.
131#[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        // TODO: This is pretty hard to fix, as we don't have access to the `Command` in the derive macros.
139        "   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    /// Enables UI testing mode.
146    #[cfg_attr(feature = "clap", arg(long))]
147    pub ui_testing: bool,
148
149    /// Prints a note for every diagnostic that is emitted with the creation and emission location.
150    ///
151    /// This is enabled by default on debug builds.
152    #[cfg_attr(feature = "clap", arg(long))]
153    pub track_diagnostics: bool,
154
155    /// Enables parsing Yul files for testing.
156    #[cfg_attr(feature = "clap", arg(long))]
157    pub parse_yul: bool,
158
159    /// Print additional information about the compiler's internal state.
160    ///
161    /// Valid kinds are `ast` and `hir`.
162    #[cfg_attr(feature = "clap", arg(long, value_name = "KIND[=PATHS...]"))]
163    pub dump: Option<Dump>,
164
165    /// Print AST stats.
166    #[cfg_attr(feature = "clap", arg(long))]
167    pub ast_stats: bool,
168
169    /// Print help.
170    #[cfg_attr(feature = "clap", arg(long, action = clap::ArgAction::Help))]
171    pub help: (),
172
173    // Allows `UnstableOpts { x: y, ..Default::default() }`.
174    #[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}