Skip to content

Commit 1f6d11c

Browse files
committed
make rustc_public a crates.io crate
1 parent 2f3f59d commit 1f6d11c

File tree

15 files changed

+1091
-11
lines changed

15 files changed

+1091
-11
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ resolver = "2"
44
members = [
55
"tools/compiletest",
66
"tools/test-drive",
7+
"rustc_public",
78
]
89

910
exclude = [

rustc_public/Cargo.toml

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,38 @@
11
[package]
22
name = "rustc_public"
33
version = "0.1.0-preview"
4+
authors = ["rustc_public team"]
5+
description = "Define compiler intermediate representation usable by external tools"
6+
license = "MIT OR Apache-2.0"
7+
repository = "https://github.com/rust-lang/project-stable-mir"
48
edition = "2024"
59

10+
[lib]
11+
path = "src/lib.rs"
12+
613
[dependencies]
7-
# tidy-alphabetical-start
8-
rustc_abi = { path = "../rustc_abi" }
9-
rustc_hir = { path = "../rustc_hir" }
10-
rustc_middle = { path = "../rustc_middle" }
11-
rustc_public_bridge = { path = "../rustc_public_bridge" }
12-
rustc_session = { path = "../rustc_session" }
13-
rustc_span = { path = "../rustc_span" }
14-
rustc_target = { path = "../rustc_target" }
1514
scoped-tls = "1.0"
1615
serde = { version = "1.0.125", features = [ "derive" ] }
1716
tracing = "0.1"
18-
# tidy-alphabetical-end
17+
18+
[build-dependencies]
19+
rustversion = "1"
20+
21+
[dev-dependencies]
22+
regex = "1.5.5"
23+
ui_test = "0.30.2"
24+
25+
[[test]]
26+
name = "compiletest"
27+
harness = false
1928

2029
[features]
21-
# tidy-alphabetical-start
2230
# Provides access to APIs that expose internals of the rust compiler.
2331
# APIs enabled by this feature are unstable. They can be removed or modified
2432
# at any point and they are not included in the crate's semantic versioning.
2533
rustc_internal = []
26-
# tidy-alphabetical-end
34+
35+
[package.metadata.rust-analyzer]
36+
# This crate uses #[feature(rustc_private)].
37+
# See https://github.com/rust-analyzer/rust-analyzer/pull/7891
38+
rustc_private = true

rustc_public/build.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
use std::process::{self, Command};
2+
use std::{env, str};
3+
4+
use rustversion;
5+
6+
const MSRV: &str = "2025-08-09";
7+
const SUPPORTED: bool = rustversion::cfg!(since(2025-08-09));
8+
9+
fn main() {
10+
if !SUPPORTED {
11+
let current = rustc_version().unwrap_or(String::from("unknown"));
12+
eprintln!(
13+
"\nERROR: rustc_public requires rustc nightly-{MSRV} or newer\n\
14+
current: {current}\n\
15+
help: run `rustup update nightly`.\n"
16+
);
17+
process::exit(1);
18+
}
19+
println!("cargo:rerun-if-changed=build.rs");
20+
}
21+
22+
fn rustc_version() -> Option<String> {
23+
let rustc = env::var_os("RUSTC").unwrap_or_else(|| {
24+
eprintln!("RUSTC is not set during build script execution.\n");
25+
process::exit(1);
26+
});
27+
let output = Command::new(rustc).arg("--version").output().ok()?;
28+
let version = str::from_utf8(&output.stdout).ok()?;
29+
version.parse().ok()
30+
}

rustc_public/src/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
//!
77
//! This API is still completely unstable and subject to change.
88
9+
#![feature(rustc_private)]
910
#![allow(rustc::usage_of_ty_tykind)]
1011
#![doc(
1112
html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/",
@@ -19,6 +20,15 @@
1920
//! The goal is to eventually be published on
2021
//! [crates.io](https://crates.io).
2122
23+
extern crate rustc_abi;
24+
extern crate rustc_driver;
25+
extern crate rustc_hir;
26+
extern crate rustc_middle;
27+
extern crate rustc_public_bridge;
28+
extern crate rustc_session;
29+
extern crate rustc_span;
30+
extern crate rustc_target;
31+
2232
use std::fmt::Debug;
2333
use std::{fmt, io};
2434

rustc_public/tests/compiletest.rs

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
use std::env;
2+
use std::ffi::OsString;
3+
use std::path::PathBuf;
4+
use std::sync::OnceLock;
5+
6+
use regex::bytes::Regex;
7+
use ui_test::color_eyre::eyre::Result;
8+
use ui_test::spanned::Spanned;
9+
use ui_test::status_emitter::StatusEmitter;
10+
use ui_test::{
11+
Args, CommandBuilder, Config, Match, bless_output_files, error_on_output_conflict,
12+
run_tests_generic,
13+
};
14+
15+
#[derive(Copy, Clone, Debug)]
16+
/// Decides what is expected of each test's exit status.
17+
enum Mode {
18+
/// The test passes a full execution of the rustc driver.
19+
Pass,
20+
/// The rustc driver should emit an error.
21+
Fail,
22+
/// The test is currently failing but is expected to succeed.
23+
/// This is used to add test cases that reproduce an existing bug. This help us identify issues
24+
/// that may be "accidentally" fixed.
25+
FixMe,
26+
/// The test passes a full execution of `cargo build`
27+
CargoPass,
28+
}
29+
30+
#[derive(Debug)]
31+
struct TestCx {
32+
/// Path for the rustc_public driver.
33+
driver_path: PathBuf,
34+
/// Arguments from the `cargo test` command.
35+
args: Args,
36+
}
37+
38+
impl TestCx {
39+
fn new() -> Result<Self> {
40+
let driver_path: PathBuf = PathBuf::from(
41+
env::var("RP_TEST_DRIVER_PATH")
42+
.expect("RP_TEST_DRIVER_PATH must be set to run rustc_public test suites"),
43+
);
44+
let args = Args::test()?;
45+
Ok(Self { driver_path, args })
46+
}
47+
48+
fn run_test(&mut self, mode: Mode, test_dir: &str) -> Result<()> {
49+
let config = config(mode, test_dir, self);
50+
eprintln!("Compiler: {}", config.program.display());
51+
run_tests_generic(
52+
vec![config],
53+
ui_test::default_file_filter,
54+
ui_test::default_per_file_config,
55+
Box::<dyn StatusEmitter>::from(self.args.format),
56+
)?;
57+
58+
Ok(())
59+
}
60+
}
61+
62+
fn config(mode: Mode, test_dir: &str, cx: &mut TestCx) -> Config {
63+
let config = if matches!(mode, Mode::CargoPass) {
64+
cargo_config(mode, test_dir, cx)
65+
} else {
66+
driver_config(mode, test_dir, cx)
67+
};
68+
cx.args.bless |= env::var("RP_TEST_DRIVER_BLESS").is_ok_and(|v| v != "0");
69+
70+
common_settings(config, mode, cx.args.bless)
71+
}
72+
73+
/// Configure cargo tests that will run the test-driver instead of rustc.
74+
fn cargo_config(mode: Mode, test_dir: &str, cx: &TestCx) -> Config {
75+
let mut config = Config::cargo(test_dir);
76+
config
77+
.program
78+
.envs
79+
.push(("RUST".into(), Some(cx.driver_path.clone().into_os_string())));
80+
config.program.envs.push((
81+
"CARGO_ENCODED_RUSTFLAGS".into(),
82+
Some(rustc_flags(mode).join(&OsString::from("\x1f")).into()),
83+
));
84+
config
85+
}
86+
87+
/// Configure tests that will invoke the test-driver directly as rustc.
88+
fn driver_config(mode: Mode, test_dir: &str, cx: &TestCx) -> Config {
89+
let mut config = Config::rustc(test_dir);
90+
config.program = CommandBuilder::rustc();
91+
config.program.program = cx.driver_path.clone();
92+
config.program.args = rustc_flags(mode);
93+
config
94+
}
95+
96+
fn common_settings(mut config: Config, mode: Mode, bless: bool) -> Config {
97+
// Recommend that users should use this command to bless failing tests.
98+
config.bless_command = Some("./x test --bless".into());
99+
config.output_conflict_handling = if bless {
100+
bless_output_files
101+
} else {
102+
error_on_output_conflict
103+
};
104+
config.comment_defaults.base().exit_status = match mode {
105+
Mode::Pass | Mode::CargoPass => Some(0),
106+
Mode::Fail | Mode::FixMe => Some(1),
107+
}
108+
.map(Spanned::dummy)
109+
.into();
110+
config.comment_defaults.base().require_annotations =
111+
Spanned::dummy(matches!(mode, Mode::Fail)).into();
112+
config.comment_defaults.base().normalize_stderr = stderr_filters()
113+
.iter()
114+
.map(|(m, p)| (m.clone(), p.to_vec()))
115+
.collect();
116+
config.comment_defaults.base().normalize_stdout = stdout_filters()
117+
.iter()
118+
.map(|(m, p)| (m.clone(), p.to_vec()))
119+
.collect();
120+
config.out_dir = PathBuf::from(env!("CARGO_TARGET_TMPDIR")).join("rp_tests");
121+
config
122+
}
123+
124+
fn rustc_flags(mode: Mode) -> Vec<OsString> {
125+
let mut flags = vec!["--smir-check".into()];
126+
let verbose = env::var("RP_TEST_DRIVER_VERBOSE").is_ok_and(|v| v != "0");
127+
if verbose {
128+
flags.push("--smir-verbose".into());
129+
}
130+
if matches!(mode, Mode::FixMe) {
131+
// Enable checks that should pass but may trigger an existing issue.
132+
flags.push("--smir-fixme".into());
133+
}
134+
flags
135+
}
136+
137+
fn run_all(mut cx: TestCx) -> Result<()> {
138+
cx.run_test(Mode::Pass, "tests/sanity-checks")?;
139+
cx.run_test(Mode::Pass, "tests/print")?;
140+
cx.run_test(Mode::FixMe, "tests/fixme")?;
141+
cx.run_test(Mode::Fail, "tests/fail")?;
142+
Ok(())
143+
}
144+
145+
fn main() -> Result<()> {
146+
let cx = TestCx::new()?;
147+
run_all(cx)?;
148+
Ok(())
149+
}
150+
151+
macro_rules! regexes {
152+
($name:ident: $($regex:expr => $replacement:expr,)*) => {
153+
fn $name() -> &'static [(Match, &'static [u8])] {
154+
static S: OnceLock<Vec<(Match, &'static [u8])>> = OnceLock::new();
155+
S.get_or_init(|| vec![
156+
$((Regex::new($regex).unwrap().into(), $replacement.as_bytes()),)*
157+
])
158+
}
159+
};
160+
}
161+
162+
regexes! {
163+
stdout_filters:
164+
// Windows file paths
165+
r"\\" => "/",
166+
// erase borrow tags
167+
"<[0-9]+>" => "<TAG>",
168+
"<[0-9]+=" => "<TAG=",
169+
// erase test paths
170+
"tests/print" => "$$DIR",
171+
}
172+
173+
regexes! {
174+
stderr_filters:
175+
// erase line and column info
176+
r"\.rs:[0-9]+:[0-9]+(: [0-9]+:[0-9]+)?" => ".rs:LL:CC",
177+
// erase alloc ids
178+
"alloc[0-9]+" => "ALLOC",
179+
// erase thread ids
180+
r"unnamed-[0-9]+" => "unnamed-ID",
181+
r"thread '(?P<name>.*?)' \(\d+\) panicked" => "thread '$name' ($$TID) panicked",
182+
// erase borrow tags
183+
"<[0-9]+>" => "<TAG>",
184+
"<[0-9]+=" => "<TAG=",
185+
// normalize width of Tree Borrows diagnostic borders (which otherwise leak borrow tag info)
186+
"(─{50})─+" => "$1",
187+
// erase whitespace that differs between platforms
188+
r" +at (.*\.rs)" => " at $1",
189+
// erase generics in backtraces
190+
"([0-9]+: .*)::<.*>" => "$1",
191+
// erase long hexadecimals
192+
r"0x[0-9a-fA-F]+[0-9a-fA-F]{2,2}" => "$$HEX",
193+
// erase specific alignments
194+
"alignment [0-9]+" => "alignment ALIGN",
195+
"[0-9]+ byte alignment but found [0-9]+" => "ALIGN byte alignment but found ALIGN",
196+
// erase thread caller ids
197+
r"call [0-9]+" => "call ID",
198+
// erase platform module paths
199+
r"\bsys::([a-z_]+)::[a-z]+::" => "sys::$1::PLATFORM::",
200+
// Windows file paths
201+
r"\\" => "/",
202+
// test-drive tags
203+
r"\[T-DRIVE\].*\n" => "",
204+
// erase Rust stdlib path
205+
"[^ \n`]*/(rust[^/]*|checkout)/library/" => "RUSTLIB/",
206+
// erase rustc path
207+
"[^ \n`]*/(rust[^/]*|checkout)/compiler/" => "RUSTC/",
208+
// erase platform file paths
209+
r"\bsys/([a-z_]+)/[a-z]+\b" => "sys/$1/PLATFORM",
210+
// erase paths into the crate registry
211+
r"[^ ]*/\.?cargo/registry/.*/(.*\.rs)" => "CARGO_REGISTRY/.../$1",
212+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//@compile-flags: -Copt-level=1
2+
3+
#![allow(dead_code, unused_variables)]
4+
use std::fmt::Debug;
5+
6+
pub trait Meow<A: Clone + Debug> {
7+
fn foo(&self, a: Option<&A>) -> A;
8+
9+
fn fzz(&self) -> A {
10+
self.foo(None)
11+
}
12+
}
13+
14+
fn main() {}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
thread 'rustc' ($TID) panicked at rustc_public/src/alloc.rs:LL:CC:
3+
Failed to convert: Scalar($HEX) to std::option::Option<&'{erased} A/#1>
4+
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
5+
Test sanity_checks::test_all_fns: Failed:
6+
- Panic!
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//@ compile-flags: -Z unpretty=stable-mir --crate-type lib -C panic=abort -Zmir-opt-level=0 -Ztrim-diagnostic-paths=true
2+
//@check-pass
3+
//@ edition: 2024
4+
5+
#![allow(dead_code, unused_variables)]
6+
7+
pub fn foo() {
8+
let y = 0;
9+
let x = async || {
10+
let y = y;
11+
};
12+
}

0 commit comments

Comments
 (0)