solar_interface/
session.rsuse crate::{
diagnostics::{DiagCtxt, EmittedDiagnostics},
ColorChoice, SessionGlobals, SourceMap,
};
use solar_config::{CompilerOutput, CompilerStage, Dump, EvmVersion, Language};
use std::{collections::BTreeSet, num::NonZeroUsize, path::PathBuf, sync::Arc};
#[derive(derive_builder::Builder)]
#[builder(
pattern = "owned",
build_fn(name = "try_build", private, error = "SessionBuilderError"),
setter(strip_option)
)]
pub struct Session {
pub dcx: DiagCtxt,
#[builder(default)]
source_map: Arc<SourceMap>,
#[builder(default)]
pub evm_version: EvmVersion,
#[builder(default)]
pub language: Language,
#[builder(default)]
pub stop_after: Option<CompilerStage>,
#[builder(default)]
pub emit: BTreeSet<CompilerOutput>,
#[builder(default)]
pub out_dir: Option<PathBuf>,
#[builder(default)]
pub dump: Option<Dump>,
#[builder(default)]
pub pretty_json: bool,
#[builder(default = "NonZeroUsize::MIN")]
pub jobs: NonZeroUsize,
}
#[derive(Debug)]
struct SessionBuilderError;
impl From<derive_builder::UninitializedFieldError> for SessionBuilderError {
fn from(_value: derive_builder::UninitializedFieldError) -> Self {
Self
}
}
impl SessionBuilder {
#[inline]
pub fn with_test_emitter(self) -> Self {
self.dcx(DiagCtxt::with_test_emitter())
}
#[inline]
pub fn with_stderr_emitter(self) -> Self {
self.with_stderr_emitter_and_color(ColorChoice::Auto)
}
#[inline]
pub fn with_stderr_emitter_and_color(mut self, color_choice: ColorChoice) -> Self {
let sm = self.get_source_map();
self.dcx(DiagCtxt::with_stderr_emitter_and_color(Some(sm), color_choice))
}
#[inline]
pub fn with_buffer_emitter(mut self, color_choice: ColorChoice) -> Self {
let sm = self.get_source_map();
self.dcx(DiagCtxt::with_buffer_emitter(Some(sm), color_choice))
}
#[inline]
pub fn with_silent_emitter(self, fatal_note: Option<String>) -> Self {
self.dcx(DiagCtxt::with_silent_emitter(fatal_note))
}
fn get_source_map(&mut self) -> Arc<SourceMap> {
self.source_map.get_or_insert_with(Default::default).clone()
}
#[track_caller]
pub fn build(mut self) -> Session {
let dcx = self.dcx.as_mut().unwrap_or_else(|| panic!("diagnostics context not set"));
if self.source_map.is_none() {
self.source_map = dcx.source_map_mut().cloned();
}
let mut sess = self.try_build().unwrap();
if let Some(sm) = sess.dcx.source_map_mut() {
assert!(
Arc::ptr_eq(&sess.source_map, sm),
"session source map does not match the one in the diagnostics context"
);
}
sess
}
}
impl Session {
pub fn new(dcx: DiagCtxt, source_map: Arc<SourceMap>) -> Self {
Self::builder().dcx(dcx).source_map(source_map).build()
}
pub fn empty(dcx: DiagCtxt) -> Self {
Self::builder().dcx(dcx).build()
}
#[inline]
pub fn builder() -> SessionBuilder {
SessionBuilder::default()
}
#[inline]
pub fn emitted_diagnostics(&self) -> Option<Result<(), EmittedDiagnostics>> {
self.dcx.emitted_diagnostics()
}
#[inline]
pub fn source_map(&self) -> &SourceMap {
&self.source_map
}
#[inline]
pub fn clone_source_map(&self) -> Arc<SourceMap> {
self.source_map.clone()
}
#[inline]
pub fn stop_after(&self, stage: CompilerStage) -> bool {
self.stop_after >= Some(stage)
}
#[inline]
pub fn is_sequential(&self) -> bool {
self.jobs.get() == 1
}
#[inline]
pub fn is_parallel(&self) -> bool {
!self.is_sequential()
}
#[inline]
pub fn do_emit(&self, output: CompilerOutput) -> bool {
self.emit.contains(&output)
}
#[inline]
pub fn spawn(&self, f: impl FnOnce() + Send + 'static) {
if self.is_sequential() {
f();
} else {
rayon::spawn(f);
}
}
#[inline]
pub fn join<A, B, RA, RB>(&self, oper_a: A, oper_b: B) -> (RA, RB)
where
A: FnOnce() -> RA + Send,
B: FnOnce() -> RB + Send,
RA: Send,
RB: Send,
{
if self.is_sequential() {
(oper_a(), oper_b())
} else {
rayon::join(oper_a, oper_b)
}
}
#[inline]
pub fn scope<'scope, OP, R>(&self, op: OP) -> R
where
OP: FnOnce(solar_data_structures::sync::Scope<'_, 'scope>) -> R + Send,
R: Send,
{
solar_data_structures::sync::scope(self.is_parallel(), op)
}
#[inline]
pub fn enter<R>(&self, f: impl FnOnce() -> R) -> R {
SessionGlobals::with_or_default(|_| {
SessionGlobals::with_source_map(self.clone_source_map(), f)
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[should_panic = "diagnostics context not set"]
fn no_dcx() {
Session::builder().build();
}
#[test]
#[should_panic = "session source map does not match the one in the diagnostics context"]
fn sm_mismatch() {
let sm1 = Arc::<SourceMap>::default();
let sm2 = Arc::<SourceMap>::default();
assert!(!Arc::ptr_eq(&sm1, &sm2));
Session::builder().source_map(sm1).dcx(DiagCtxt::with_stderr_emitter(Some(sm2))).build();
}
#[test]
#[should_panic = "session source map does not match the one in the diagnostics context"]
fn sm_mismatch_non_builder() {
let sm1 = Arc::<SourceMap>::default();
let sm2 = Arc::<SourceMap>::default();
assert!(!Arc::ptr_eq(&sm1, &sm2));
Session::new(DiagCtxt::with_stderr_emitter(Some(sm2)), sm1);
}
#[test]
fn builder() {
let _ = Session::builder().with_stderr_emitter().build();
}
#[test]
fn empty() {
let _ = Session::empty(DiagCtxt::with_stderr_emitter(None));
let _ = Session::empty(DiagCtxt::with_stderr_emitter(Some(Default::default())));
}
#[test]
fn local() {
let sess = Session::builder().with_buffer_emitter(ColorChoice::Never).build();
sess.dcx.err("test").emit();
let err = sess.dcx.emitted_diagnostics().unwrap().unwrap_err();
let err = Box::new(err) as Box<dyn std::error::Error>;
assert!(err.to_string().contains("error: test"), "{err:?}");
}
}