Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
The Rust compiler's interface is not stable, so the only sensible way to develop a Rust compiler plugin is by pinning to a specific nightly. Each version of `rustc_plugin` is pinned to one nightly, and you *have* to use the same nightly version that we do. Therefore each release of `rustc_plugin` has a semantic version number (e.g. `0.1.0`) and the nightly version is added as a prerelease label (e.g. `-nightly-2023-08-25`). You can add a dependency to your `Cargo.toml` like this:

```toml
rustc_plugin = "=0.13.0-nightly-2025-03-03"
rustc_plugin = "=0.14.0-nightly-2025-08-20"
```

We will treat a change to the nightly version as a breaking change, so the semantic version will be correspondingly updated as a breaking update.
Expand Down Expand Up @@ -44,6 +44,7 @@ The `rustc_plugin` framework is responsible for marshalling arguments from the t

Normally, Rust libraries have a [minimum supported Rust version][msrv] because they promise to not use any breaking features implemented after that version. Rust compiler plugins are the opposite — they have a **maximum** supported Rust version (MaxSRV). A compiler plugin cannot analyze programs that use features implemented after the release date of the plugin's toolchain. The MaxSRV for every version of `rustc_plugin` is listed below:

* v0.14 (`nightly-2025-08-20`) - rustc 1.87
* v0.13 (`nightly-2025-03-03`) - rustc 1.86
* v0.12 (`nightly-2024-12-15`) - rustc 1.84
* v0.11 (`nightly-2024-12-01`) - rustc 1.84
Expand All @@ -60,6 +61,6 @@ Normally, Rust libraries have a [minimum supported Rust version][msrv] because t
[Argus]: https://github.com/cognitive-engineering-lab/argus
[Clippy]: https://github.com/rust-lang/rust-clippy
[example]: https://github.com/cognitive-engineering-lab/rustc_plugin/tree/main/crates/rustc_plugin/examples/print-all-items
[docs]: https://cognitive-engineering-lab.github.io/rustc_plugin/v0.13.0-nightly-2025-03-03/rustc_plugin/
[docs-utils]: https://cognitive-engineering-lab.github.io/rustc_plugin/v0.13.0-nightly-2025-03-03/rustc_utils/
[docs]: https://cognitive-engineering-lab.github.io/rustc_plugin/v0.14.0-nightly-2025-08-20/rustc_plugin/
[docs-utils]: https://cognitive-engineering-lab.github.io/rustc_plugin/v0.14.0-nightly-2025-08-20/rustc_utils/
[msrv]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-rust-version-field
6 changes: 3 additions & 3 deletions crates/rustc_plugin/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "rustc_plugin"
version = "0.13.0-nightly-2025-03-03"
edition = "2021"
version = "0.14.0-nightly-2025-08-20"
edition = "2024"
authors = ["Will Crichton <crichton.will@gmail.com>"]
description = "A framework for writing plugins that integrate with the Rust compiler"
repository = "https://github.com/cognitive-engineering-lab/rustc_plugin"
Expand All @@ -18,7 +18,7 @@ serde = "1"
serde_json = "1"

[dev-dependencies]
anyhow = {version = "1", features = ["backtrace"]}
anyhow = { version = "1", features = ["backtrace"] }

[build-dependencies]
toml = "0.7"
8 changes: 4 additions & 4 deletions crates/rustc_plugin/examples/print-all-items/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
[package]
name = "print-all-items"
version = "0.1.0"
edition = "2021"
edition = "2024"

[package.metadata.rust-analyzer]
rustc_private = true

[dependencies]
rustc_plugin = { path = "../.." }
env_logger = {version = "0.10", default-features = false}
clap = {version = "4.4", features = ["derive"]}
serde = {version = "1", features = ["derive"]}
env_logger = { version = "0.10", default-features = false }
clap = { version = "4.4", features = ["derive"] }
serde = { version = "1", features = ["derive"] }
4 changes: 2 additions & 2 deletions crates/rustc_plugin/examples/print-all-items/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ cargo print-all-items
You should see the output:

```text
There is an item "" of type "`use` import"
There is an item "std" of type "extern crate"
There is an item of type "import"
There is an item "add" of type "function"
```
```
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[toolchain]
channel = "nightly-2025-03-02"
channel = "nightly-2025-08-20"
components = ["rust-src", "rustc-dev", "llvm-tools-preview"]
30 changes: 19 additions & 11 deletions crates/rustc_plugin/examples/print-all-items/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ use std::{borrow::Cow, env, process::Command};

use clap::Parser;
use rustc_hir::{
intravisit::{self, Visitor},
Item,
intravisit::{self, Visitor},
};
use rustc_middle::ty::TyCtxt;
use rustc_plugin::{CrateFilter, RustcPlugin, RustcPluginArgs, Utf8Path};
Expand All @@ -25,7 +25,7 @@ pub struct PrintAllItemsPlugin;

// To parse CLI arguments, we use Clap for this example. But that
// detail is up to you.
#[derive(Parser, Serialize, Deserialize)]
#[derive(Parser, Serialize, Deserialize, Clone)]
pub struct PrintAllItemsPluginArgs {
#[arg(short, long)]
allcaps: bool,
Expand Down Expand Up @@ -102,20 +102,28 @@ impl rustc_driver::Callbacks for PrintAllItemsCallbacks {
// I recommend reading the Rustc Development Guide to better understand which compiler APIs
// are relevant to whatever task you have.
fn print_all_items(tcx: TyCtxt, args: PrintAllItemsPluginArgs) {
tcx.hir_visit_all_item_likes_in_crate(&mut PrintVisitor { args });
tcx.hir_visit_all_item_likes_in_crate(&mut PrintVisitor { args, tcx });
}

struct PrintVisitor {
struct PrintVisitor<'tcx> {
args: PrintAllItemsPluginArgs,
tcx: TyCtxt<'tcx>,
}

impl Visitor<'_> for PrintVisitor {
fn visit_item(&mut self, item: &Item) -> Self::Result {
let mut msg = format!(
"There is an item \"{}\" of type \"{}\"",
item.ident,
item.kind.descr()
);
impl<'tcx> Visitor<'tcx> for PrintVisitor<'tcx> {
fn visit_item(&mut self, item: &'tcx Item<'tcx>) -> Self::Result {
let mut msg = if let Some(ident) = item.kind.ident() {
format!(
"There is an item \"{}\" of type \"{}\"",
ident,
self.tcx.def_descr(item.owner_id.to_def_id())
)
} else {
format!(
"There is an item of type \"{}\"",
self.tcx.def_descr(item.owner_id.to_def_id())
)
};
if self.args.allcaps {
msg = msg.to_uppercase();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "test-crate"
version = "0.1.0"
edition = "2021"
edition = "2024"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

Expand Down
12 changes: 6 additions & 6 deletions crates/rustc_plugin/src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use std::{
env, fs,
path::PathBuf,
process::{exit, Command, Stdio},
process::{Command, Stdio, exit},
};

use cargo_metadata::camino::Utf8Path;

use super::plugin::{RustcPlugin, PLUGIN_ARGS};
use super::plugin::{PLUGIN_ARGS, RustcPlugin};
use crate::CrateFilter;

pub const RUN_ON_ALL_CRATES: &str = "RUSTC_PLUGIN_ALL_TARGETS";
Expand Down Expand Up @@ -193,10 +193,10 @@ fn only_run_on_file(
let prefix = format!("lib{}", pkg.name.replace('-', "_"));
for entry in entries {
let path = entry.unwrap().path();
if let Some(file_name) = path.file_name() {
if file_name.to_string_lossy().starts_with(&prefix) {
fs::remove_file(path).unwrap();
}
if let Some(file_name) = path.file_name()
&& file_name.to_string_lossy().starts_with(&prefix)
{
fs::remove_file(path).unwrap();
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/rustc_plugin/tests/workspaces/basic/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "basic"
version = "0.1.0"
edition = "2021"
edition = "2024"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

Expand Down
2 changes: 1 addition & 1 deletion crates/rustc_plugin/tests/workspaces/multi/a/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "a"
version = "0.1.0"
edition = "2021"
edition = "2024"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

Expand Down
4 changes: 2 additions & 2 deletions crates/rustc_plugin/tests/workspaces/multi/b/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
[package]
name = "b"
version = "0.1.0"
edition = "2021"
edition = "2024"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
a = { path = "../a" }
a = { path = "../a" }
20 changes: 11 additions & 9 deletions crates/rustc_utils/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "rustc_utils"
version = "0.13.0-nightly-2025-03-03"
edition = "2021"
version = "0.14.0-nightly-2025-08-20"
edition = "2024"
authors = ["Will Crichton <crichton.will@gmail.com>"]
description = "Utilities for working with the Rust compiler"
repository = "https://github.com/cognitive-engineering-lab/rustc_plugin"
Expand All @@ -23,13 +23,15 @@ anyhow = "1"
log = "0.4"
intervaltree = "0.2"
cfg-if = "1"
serde = {version = "1", features = ["derive"], optional = true}
textwrap = {version = "0.16", optional = true}
regex = {version = "1", optional = true}
ts-rs = {version = "7", optional = true}
indexical = {version = "0.7.0", default-features = false, features = ["rustc"], optional = true}
serde = { version = "1", features = ["derive"], optional = true }
textwrap = { version = "0.16", optional = true }
regex = { version = "1", optional = true }
ts-rs = { version = "7", optional = true }
indexical = { version = "0.7.0", default-features = false, features = [
"rustc",
], optional = true }

[dev-dependencies]
rustc_utils = {path = ".", features = ["test"]}
rustc_utils = { path = ".", features = ["test"] }
test-log = "0.2"
env_logger = {version = "0.9", default-features = false}
env_logger = { version = "0.9", default-features = false }
2 changes: 1 addition & 1 deletion crates/rustc_utils/src/hir/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub trait TyExt<'tcx> {

impl<'tcx> TyExt<'tcx> for Ty<'tcx> {
fn inner_regions(self) -> impl Iterator<Item = Region<'tcx>> {
self.walk().filter_map(|part| match part.unpack() {
self.walk().filter_map(|part| match part.kind() {
GenericArgKind::Lifetime(region) => Some(region),
_ => None,
})
Expand Down
2 changes: 0 additions & 2 deletions crates/rustc_utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
min_specialization, // for rustc_index::newtype_index
type_alias_impl_trait, // for iterators in traits
box_patterns, // for ergonomics
let_chains, // for places_conflict module
exact_size_is_empty, // for graphviz module
impl_trait_in_assoc_type,
doc_auto_cfg, // for feature gates in documentation
Expand All @@ -32,7 +31,6 @@
clippy::doc_markdown,
clippy::single_match_else,
clippy::if_not_else,
clippy::match_on_vec_items,
clippy::map_unwrap_or,
clippy::match_wildcard_for_single_variants,
clippy::items_after_statements,
Expand Down
37 changes: 21 additions & 16 deletions crates/rustc_utils/src/mir/borrowck_facts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ use rustc_borrowck::consumers::{BodyWithBorrowckFacts, ConsumerOptions};
use rustc_data_structures::fx::FxHashSet as HashSet;
use rustc_hir::def_id::LocalDefId;
use rustc_middle::{
mir::{Body, BorrowCheckResult, StatementKind, TerminatorKind},
mir::{Body, ConcreteOpaqueTypes, StatementKind, TerminatorKind},
ty::TyCtxt,
util::Providers,
};
use rustc_span::ErrorGuaranteed;

use crate::{block_timer, cache::Cache, BodyExt};
use crate::{BodyExt, block_timer, cache::Cache};

/// MIR pass to remove instructions not important for Flowistry.
///
Expand Down Expand Up @@ -39,7 +40,7 @@ pub fn simplify_mir(body: &mut Body<'_>) {
TerminatorKind::FalseEdge { real_target, .. } => TerminatorKind::Goto {
target: real_target,
},
// Ensures that control dependencies can determine the independence of differnet
// Ensures that control dependencies can determine the independence of different
// return paths
TerminatorKind::Goto { target } if return_blocks.contains(&target) => {
TerminatorKind::Return
Expand Down Expand Up @@ -67,29 +68,33 @@ thread_local! {
static MIR_BODIES: Cache<LocalDefId, BodyWithBorrowckFacts<'static>> = Cache::default();
}

fn mir_borrowck(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &BorrowCheckResult<'_> {
fn mir_borrowck(
tcx: TyCtxt<'_>,
def_id: LocalDefId,
) -> Result<&ConcreteOpaqueTypes<'_>, ErrorGuaranteed> {
block_timer!(&format!(
"get_body_with_borrowck_facts for {}",
"get_bodies_with_borrowck_facts for {}",
tcx.def_path_debug_str(def_id.to_def_id())
));

let mut body_with_facts = rustc_borrowck::consumers::get_body_with_borrowck_facts(
let mut body_with_facts = rustc_borrowck::consumers::get_bodies_with_borrowck_facts(
tcx,
def_id,
ConsumerOptions::PoloniusInputFacts,
);

if SIMPLIFY_MIR.load(Ordering::SeqCst) {
simplify_mir(&mut body_with_facts.body);
}

// SAFETY: The reader casts the 'static lifetime to 'tcx before using it.
let body_with_facts: BodyWithBorrowckFacts<'static> =
unsafe { std::mem::transmute(body_with_facts) };
MIR_BODIES.with(|cache| {
cache.get(&def_id, |_| body_with_facts);
});
for (def_id, mut body_with_facts) in body_with_facts.drain() {
if SIMPLIFY_MIR.load(Ordering::SeqCst) {
simplify_mir(&mut body_with_facts.body);
}

// SAFETY: The reader casts the 'static lifetime to 'tcx before using it.
let body_with_facts: BodyWithBorrowckFacts<'static> =
unsafe { std::mem::transmute(body_with_facts) };
MIR_BODIES.with(|cache| {
cache.get(&def_id, |_| body_with_facts);
});
}
let mut providers = Providers::default();
rustc_borrowck::provide(&mut providers);
let original_mir_borrowck = providers.mir_borrowck;
Expand Down
3 changes: 1 addition & 2 deletions crates/rustc_utils/src/source_map/find_bodies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ impl<'tcx> Visitor<'tcx> for BodyFinder<'tcx> {

fn visit_nested_body(&mut self, id: BodyId) {
let tcx = self.tcx;
let hir = tcx.hir();

// const/static items are considered to have bodies, so we want to exclude
// them from our search for functions
Expand All @@ -29,7 +28,7 @@ impl<'tcx> Visitor<'tcx> for BodyFinder<'tcx> {
let body = tcx.hir_body(id);
self.visit_body(body);

let span = hir.span_with_body(tcx.hir_body_owner(id));
let span = tcx.hir_span_with_body(tcx.hir_body_owner(id));
trace!(
"Searching body for {:?} with span {span:?} (local {:?})",
self
Expand Down
2 changes: 1 addition & 1 deletion crates/rustc_utils/src/source_map/range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ fn qpath_to_span(tcx: TyCtxt, qpath: String) -> Result<Span> {
.def_path(local_def_id.to_def_id())
.to_string_no_crate_verbose();
if function_path[2 ..] == self.qpath {
self.span = Some(self.tcx.hir().span(id.hir_id));
self.span = Some(self.tcx.hir_span(id.hir_id));
}
}
}
Expand Down
3 changes: 1 addition & 2 deletions crates/rustc_utils/src/source_map/spanner/hir_span.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,7 @@ macro_rules! try_span {

impl Spanner<'_> {
pub fn hir_spans(&self, id: HirId, mode: EnclosingHirSpans) -> Option<Vec<Span>> {
let hir = self.tcx.hir();
let span = try_span!(self, hir.span(id));
let span = try_span!(self, self.tcx.hir_span(id));
let inner_spans = match self.tcx.hir_node(id) {
Node::Expr(expr) => match expr.kind {
ExprKind::Loop(_, _, loop_source, header) => match loop_source {
Expand Down
Loading