Skip to main content

solar_cli/commands/
compile.rs

1use solar_config::{CompileOpts, CompilerOutput};
2use solar_interface::{Result, Session};
3use solar_sema::{CompilerRef, ParsingContext};
4use std::{ops::ControlFlow, process::ExitCode};
5
6pub(super) fn run(opts: CompileOpts) -> ExitCode {
7    match run_compiler_args(opts) {
8        Ok(()) => ExitCode::SUCCESS,
9        Err(_) => ExitCode::FAILURE,
10    }
11}
12
13pub fn run_compiler_args(opts: CompileOpts) -> Result {
14    if opts.standard_json {
15        crate::standard_json::run(opts)
16            .map_err(|_e| solar_interface::diagnostics::ErrorGuaranteed::new_unchecked())?;
17        return Ok(());
18    }
19
20    run_compiler_with(opts, run_default)
21}
22
23fn run_default(compiler: &mut CompilerRef<'_>) -> Result {
24    run_pipeline(
25        compiler,
26        |pcx| {
27            // Partition arguments into three categories:
28            // - `stdin`: `-`, occurrences after the first are ignored
29            // - remappings: `[context:]prefix=path`, already parsed as part of `CompileOpts`
30            // - paths: everything else
31            let mut seen_stdin = false;
32            let mut paths = Vec::new();
33            for arg in pcx.sess.opts.input.clone() {
34                if arg == "-" {
35                    if !seen_stdin {
36                        pcx.load_stdin()?;
37                    }
38                    seen_stdin = true;
39                    continue;
40                }
41
42                if arg.contains('=') {
43                    continue;
44                }
45
46                paths.push(arg);
47            }
48
49            pcx.par_load_files(paths)
50        },
51        |_| {},
52    )
53    .map(|_| ())
54}
55
56pub(crate) fn run_pipeline(
57    compiler: &mut CompilerRef<'_>,
58    load_sources: impl FnOnce(&mut ParsingContext<'_>) -> Result,
59    after_parsing: impl FnOnce(&CompilerRef<'_>),
60) -> Result<ControlFlow<()>> {
61    let sess = compiler.gcx().sess;
62    if sess.opts.language.is_yul() && !sess.opts.unstable.parse_yul {
63        return Err(sess.dcx.err("Yul is not supported yet").emit());
64    }
65
66    let mut pcx = compiler.parse();
67    load_sources(&mut pcx)?;
68    pcx.parse();
69
70    if compiler.gcx().sources.is_empty() {
71        let msg = "no files found";
72        let note = "if you wish to use the standard input, please specify `-` explicitly";
73        return Err(sess.dcx.err(msg).note(note).emit());
74    }
75
76    compiler.sources_mut().topo_sort();
77    after_parsing(compiler);
78
79    let ControlFlow::Continue(()) = compiler.lower_asts()? else {
80        return Ok(ControlFlow::Break(()));
81    };
82    compiler.drop_asts();
83    let ControlFlow::Continue(()) = compiler.analysis()? else {
84        return Ok(ControlFlow::Break(()));
85    };
86
87    // Code generation (MIR and bytecode) is experimental and not part of the
88    // stable, solc-compatible pipeline yet, so it is gated behind `-Zcodegen`.
89    let needs_codegen = sess.opts.emit.iter().any(|e| {
90        matches!(e, CompilerOutput::Mir | CompilerOutput::Bin | CompilerOutput::BinRuntime)
91    });
92    if needs_codegen && !sess.opts.unstable.codegen {
93        return Err(sess
94            .dcx
95            .err("code generation is experimental")
96            .help("pass `-Zcodegen` to emit MIR or bytecode")
97            .emit());
98    }
99
100    crate::emit::emit_requested(compiler)?;
101
102    Ok(ControlFlow::Continue(()))
103}
104
105fn run_compiler_with(
106    opts: CompileOpts,
107    f: impl FnOnce(&mut CompilerRef<'_>) -> Result + Send,
108) -> Result {
109    let mut sess = Session::new(opts);
110    sess.infer_language();
111    run_compiler_session_with(sess, f, true)
112}
113
114pub(crate) fn run_compiler_session_with(
115    sess: Session,
116    f: impl FnOnce(&mut CompilerRef<'_>) -> Result + Send,
117    finish: bool,
118) -> Result {
119    sess.validate()?;
120    let mut compiler = solar_sema::Compiler::new(sess);
121    compiler.enter_mut(|compiler| {
122        let mut r = f(compiler);
123        if finish {
124            r = r.and(finish_diagnostics(compiler.gcx().sess));
125        }
126        r
127    })
128}
129
130fn finish_diagnostics(sess: &Session) -> Result {
131    sess.dcx.print_error_count()
132}