solar_cli/
lib.rs

1#![doc = include_str!("../README.md")]
2#![doc(
3    html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/solar/main/assets/logo.png",
4    html_favicon_url = "https://raw.githubusercontent.com/paradigmxyz/solar/main/assets/favicon.ico"
5)]
6#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
7
8use clap::Parser as _;
9use solar_config::ErrorFormat;
10use solar_interface::{
11    Result, Session, SourceMap,
12    diagnostics::{DiagCtxt, DynEmitter, HumanEmitter, JsonEmitter},
13};
14use solar_sema::CompilerRef;
15use std::{ops::ControlFlow, sync::Arc};
16
17pub use solar_config::{self as config, Opts, UnstableOpts, version};
18
19pub mod utils;
20
21#[cfg(all(unix, any(target_env = "gnu", target_os = "macos")))]
22pub mod sigsegv_handler;
23
24/// Signal handler to extract a backtrace from stack overflow.
25///
26/// This is a no-op because this platform doesn't support our signal handler's requirements.
27#[cfg(not(all(unix, any(target_env = "gnu", target_os = "macos"))))]
28pub mod sigsegv_handler {
29    #[cfg(unix)]
30    use libc as _;
31
32    /// No-op function.
33    pub fn install() {}
34}
35
36// `asm` feature.
37use alloy_primitives as _;
38
39use tracing as _;
40
41pub fn parse_args<I, T>(itr: I) -> Result<Opts, clap::Error>
42where
43    I: IntoIterator<Item = T>,
44    T: Into<std::ffi::OsString> + Clone,
45{
46    let mut opts = Opts::try_parse_from(itr)?;
47    opts.finish()?;
48    Ok(opts)
49}
50
51pub fn run_compiler_args(opts: Opts) -> Result {
52    run_compiler_with(opts, run_default)
53}
54
55fn run_default(compiler: &mut CompilerRef<'_>) -> Result {
56    let sess = compiler.gcx().sess;
57    if sess.opts.language.is_yul() && !sess.opts.unstable.parse_yul {
58        return Err(sess.dcx.err("Yul is not supported yet").emit());
59    }
60
61    let mut pcx = compiler.parse();
62
63    // Partition arguments into three categories:
64    // - `stdin`: `-`, occurrences after the first are ignored
65    // - remappings: `[context:]prefix=path`, already parsed as part of `Opts`
66    // - paths: everything else
67    let mut seen_stdin = false;
68    let mut paths = Vec::new();
69    for arg in sess.opts.input.iter().map(String::as_str) {
70        if arg == "-" {
71            if !seen_stdin {
72                pcx.load_stdin()?;
73            }
74            seen_stdin = true;
75            continue;
76        }
77
78        if arg.contains('=') {
79            continue;
80        }
81
82        paths.push(arg);
83    }
84
85    pcx.par_load_files(paths)?;
86
87    pcx.parse();
88    let ControlFlow::Continue(()) = compiler.lower_asts()? else { return Ok(()) };
89    compiler.drop_asts();
90    let ControlFlow::Continue(()) = compiler.analysis()? else { return Ok(()) };
91
92    Ok(())
93}
94
95fn run_compiler_with(opts: Opts, f: impl FnOnce(&mut CompilerRef<'_>) -> Result + Send) -> Result {
96    let ui_testing = opts.unstable.ui_testing;
97    let source_map = Arc::new(SourceMap::empty());
98    let emitter: Box<DynEmitter> = match opts.error_format {
99        ErrorFormat::Human => {
100            let color = match opts.color {
101                clap::ColorChoice::Always => solar_interface::ColorChoice::Always,
102                clap::ColorChoice::Auto => solar_interface::ColorChoice::Auto,
103                clap::ColorChoice::Never => solar_interface::ColorChoice::Never,
104            };
105            let human = HumanEmitter::stderr(color)
106                .source_map(Some(source_map.clone()))
107                .ui_testing(ui_testing);
108            Box::new(human)
109        }
110        ErrorFormat::Json | ErrorFormat::RustcJson => {
111            // `io::Stderr` is not buffered.
112            let writer = Box::new(std::io::BufWriter::new(std::io::stderr()));
113            let json = JsonEmitter::new(writer, source_map.clone())
114                .pretty(opts.pretty_json_err)
115                .rustc_like(matches!(opts.error_format, ErrorFormat::RustcJson))
116                .ui_testing(ui_testing);
117            Box::new(json)
118        }
119        format => todo!("{format:?}"),
120    };
121    let dcx = DiagCtxt::new(emitter).set_flags(|flags| {
122        flags.deduplicate_diagnostics &= !ui_testing;
123        flags.track_diagnostics &= !ui_testing;
124        flags.track_diagnostics |= opts.unstable.track_diagnostics;
125        flags.can_emit_warnings |= !opts.no_warnings;
126    });
127
128    let mut sess = Session::builder().dcx(dcx).source_map(source_map).opts(opts).build();
129    sess.infer_language();
130    sess.validate()?;
131
132    let mut compiler = solar_sema::Compiler::new(sess);
133    compiler.enter_mut(|compiler| {
134        let mut r = f(compiler);
135        r = r.and(finish_diagnostics(compiler.gcx().sess));
136        r
137    })
138}
139
140fn finish_diagnostics(sess: &Session) -> Result {
141    sess.dcx.print_error_count()
142}