#![feature(rustc_private)]
extern crate rustc_driver;
extern crate rustc_interface;
extern crate rustc_middle;
extern crate rustc_span;
use no_alloc_analysis::roots::DiscoveredRoot;
use no_alloc_report::{Report, ReportFragment, RootVerdict};
use rustc_driver::{Callbacks, Compilation};
use rustc_interface::interface;
use rustc_middle::ty::TyCtxt;
use std::path::{Path, PathBuf};
const REQUIRED_FLAGS: &[&str] = &[
"--cfg=no_alloc_check",
"--check-cfg=cfg(no_alloc_check)",
"-Zcrate-attr=feature(register_tool)",
"-Zcrate-attr=register_tool(no_alloc_tool)",
"-Zalways-encode-mir",
];
struct NoAllocCallbacks;
impl Callbacks for NoAllocCallbacks {
fn after_analysis<'tcx>(
&mut self,
_compiler: &interface::Compiler,
tcx: TyCtxt<'tcx>,
) -> Compilation {
let warn_only = std::env::var("NO_ALLOC_WARN_ONLY").as_deref() == Ok("1");
let discovery = no_alloc_analysis::roots::discover(tcx);
let mut report = Report {
roots: Vec::new(),
selection_errors: discovery.selection_errors,
};
let mut any_hard_error = false;
for root in discovery.roots {
match root {
DiscoveredRoot::NotInstantiated { root } => {
let root_path = no_alloc_analysis::roots::root_path(tcx, root);
report.roots.push(RootVerdict {
root: root_path.clone(),
instance: root_path,
verdict: no_alloc_report::Verdict::NotInstantiated,
});
}
DiscoveredRoot::Instance { root, instance } => {
let root_path = no_alloc_analysis::roots::root_path(tcx, root);
let checked = no_alloc_analysis::traversal::check_instance(tcx, instance);
if no_alloc_analysis::diagnostics::emit(tcx, &checked, warn_only) {
any_hard_error = true;
}
report.roots.push(RootVerdict {
root: root_path,
instance: instance.to_string(),
verdict: checked.verdict,
});
}
}
}
let fragment_dir = std::env::var_os("NO_ALLOC_FRAGMENT_DIR")
.map(PathBuf::from)
.unwrap_or_else(|| PathBuf::from("target/no-alloc/fragments"));
let crate_name = tcx.crate_name(rustc_span::def_id::LOCAL_CRATE);
let nanos = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or_default();
let fragment_path =
fragment_dir.join(format!("{crate_name}-{}-{nanos}.json", std::process::id()));
let fragment = ReportFragment {
report,
matched_root_specs: discovery.matched_root_specs,
};
if let Err(error) = fragment.write_to_file(&fragment_path) {
tcx.dcx().err(format!(
"no_alloc: failed to write report fragment: {error}"
));
any_hard_error = true;
}
if any_hard_error {
Compilation::Stop
} else {
Compilation::Continue
}
}
}
fn main() -> std::process::ExitCode {
tracing_subscriber::fmt()
.with_writer(std::io::stderr)
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_env("NO_ALLOC_LOG")
.unwrap_or_else(|_| "off".into()),
)
.init();
let mut args: Vec<String> = std::env::args().collect();
if args.len() <= 1 {
tracing::error!("missing rustc invocation");
return std::process::ExitCode::FAILURE;
}
if Path::new(&args[1]).file_stem().and_then(|s| s.to_str()) == Some("rustc") {
args.remove(1);
}
for flag in REQUIRED_FLAGS {
if !args.iter().any(|a| a == flag) {
args.push((*flag).to_string());
}
}
let mut callbacks = NoAllocCallbacks;
rustc_driver::catch_with_exit_code(|| {
rustc_driver::run_compiler(&args, &mut callbacks);
})
}