solar_config/
opts.rs

1//! Solar CLI arguments.
2
3use 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/// Blazingly fast Solidity compiler.
12#[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    /// Files to compile or import remappings.
23    #[cfg_attr(feature = "clap", arg(value_hint = ValueHint::FilePath))]
24    pub input: Vec<PathBuf>,
25    /// Directory to search for files.
26    #[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    /// Map to search for files. Can also be provided as a positional argument.
29    #[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    /// Source code language. Only Solidity is currently implemented.
35    #[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    /// Number of threads to use. Zero specifies the number of logical cores.
42    #[cfg_attr(feature = "clap", arg(long, short = 'j', visible_alias = "jobs", default_value_t))]
43    pub threads: Threads,
44    /// EVM version.
45    #[cfg_attr(feature = "clap", arg(long, value_enum, default_value_t))]
46    pub evm_version: EvmVersion,
47    /// Stop execution after the given compiler stage.
48    #[cfg_attr(feature = "clap", arg(long, value_enum))]
49    pub stop_after: Option<CompilerStage>,
50
51    /// Directory to write output files.
52    #[cfg_attr(feature = "clap", arg(long, value_hint = ValueHint::DirPath))]
53    pub out_dir: Option<PathBuf>,
54    /// Comma separated list of types of output for the compiler to emit.
55    #[cfg_attr(feature = "clap", arg(long, value_delimiter = ','))]
56    pub emit: Vec<CompilerOutput>,
57
58    /// Coloring.
59    #[cfg(feature = "clap")] // TODO
60    #[cfg_attr(
61        feature = "clap",
62        arg(help_heading = "Display options", long, value_enum, default_value = "auto")
63    )]
64    pub color: ColorChoice,
65    /// Use verbose output.
66    #[cfg_attr(feature = "clap", arg(help_heading = "Display options", long, short))]
67    pub verbose: bool,
68    /// Pretty-print JSON output.
69    ///
70    /// Does not include errors. See `--pretty-json-err`.
71    #[cfg_attr(feature = "clap", arg(help_heading = "Display options", long))]
72    pub pretty_json: bool,
73    /// Pretty-print error JSON output.
74    #[cfg_attr(feature = "clap", arg(help_heading = "Display options", long))]
75    pub pretty_json_err: bool,
76    /// How errors and other messages are produced.
77    #[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    /// Unstable flags. WARNING: these are completely unstable, and may change at any time.
84    ///
85    /// See `-Zhelp` for more details.
86    #[doc(hidden)]
87    #[cfg_attr(feature = "clap", arg(id = "unstable-features", value_name = "FLAG", short = 'Z'))]
88    pub _unstable: Vec<String>,
89
90    /// Parsed unstable flags.
91    #[cfg_attr(feature = "clap", arg(skip))]
92    pub unstable: UnstableOpts,
93
94    // Allows `Opts { x: y, ..Default::default() }`.
95    #[doc(hidden)]
96    #[cfg_attr(feature = "clap", arg(skip))]
97    pub _non_exhaustive: (),
98}
99
100impl Opts {
101    /// Returns the number of threads to use.
102    #[inline]
103    pub fn threads(&self) -> NonZeroUsize {
104        self.threads.0
105    }
106
107    /// Finishes argument parsing.
108    ///
109    /// This currently only parses the `-Z` arguments into the `unstable` field, but may be extended
110    /// in the future.
111    #[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/// Internal options.
123#[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        // TODO: This is pretty hard to fix, as we don't have access to the `Command` in the derive macros.
131        "   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    /// Enables UI testing mode.
138    #[cfg_attr(feature = "clap", arg(long))]
139    pub ui_testing: bool,
140
141    /// Prints a note for every diagnostic that is emitted with the creation and emission location.
142    ///
143    /// This is enabled by default on debug builds.
144    #[cfg_attr(feature = "clap", arg(long))]
145    pub track_diagnostics: bool,
146
147    /// Enables parsing Yul files for testing.
148    #[cfg_attr(feature = "clap", arg(long))]
149    pub parse_yul: bool,
150
151    /// Print additional information about the compiler's internal state.
152    ///
153    /// Valid kinds are `ast` and `hir`.
154    #[cfg_attr(feature = "clap", arg(long, value_name = "KIND[=PATHS...]"))]
155    pub dump: Option<Dump>,
156
157    /// Print AST stats.
158    #[cfg_attr(feature = "clap", arg(long))]
159    pub ast_stats: bool,
160
161    /// Print help.
162    #[cfg_attr(feature = "clap", arg(long, action = clap::ArgAction::Help))]
163    pub help: (),
164
165    // Allows `UnstableOpts { x: y, ..Default::default() }`.
166    #[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}