diff --git a/README.md b/README.md index 690d4b363..81907dd5c 100644 --- a/README.md +++ b/README.md @@ -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. @@ -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 @@ -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 diff --git a/crates/rustc_plugin/Cargo.toml b/crates/rustc_plugin/Cargo.toml index be3b83a88..7c0324952 100644 --- a/crates/rustc_plugin/Cargo.toml +++ b/crates/rustc_plugin/Cargo.toml @@ -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 "] description = "A framework for writing plugins that integrate with the Rust compiler" repository = "https://github.com/cognitive-engineering-lab/rustc_plugin" @@ -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" diff --git a/crates/rustc_plugin/examples/print-all-items/Cargo.toml b/crates/rustc_plugin/examples/print-all-items/Cargo.toml index 00dcde3be..2434c4a40 100644 --- a/crates/rustc_plugin/examples/print-all-items/Cargo.toml +++ b/crates/rustc_plugin/examples/print-all-items/Cargo.toml @@ -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"]} \ No newline at end of file +env_logger = { version = "0.10", default-features = false } +clap = { version = "4.4", features = ["derive"] } +serde = { version = "1", features = ["derive"] } diff --git a/crates/rustc_plugin/examples/print-all-items/README.md b/crates/rustc_plugin/examples/print-all-items/README.md index d3a789ec0..8b059ba25 100644 --- a/crates/rustc_plugin/examples/print-all-items/README.md +++ b/crates/rustc_plugin/examples/print-all-items/README.md @@ -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" -``` \ No newline at end of file +``` diff --git a/crates/rustc_plugin/examples/print-all-items/rust-toolchain.toml b/crates/rustc_plugin/examples/print-all-items/rust-toolchain.toml index be68a3656..f67f0a7f1 100644 --- a/crates/rustc_plugin/examples/print-all-items/rust-toolchain.toml +++ b/crates/rustc_plugin/examples/print-all-items/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2025-03-02" +channel = "nightly-2025-08-20" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] diff --git a/crates/rustc_plugin/examples/print-all-items/src/lib.rs b/crates/rustc_plugin/examples/print-all-items/src/lib.rs index 340bf172f..288c71e20 100644 --- a/crates/rustc_plugin/examples/print-all-items/src/lib.rs +++ b/crates/rustc_plugin/examples/print-all-items/src/lib.rs @@ -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}; @@ -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, @@ -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(); } diff --git a/crates/rustc_plugin/examples/print-all-items/test-crate/Cargo.toml b/crates/rustc_plugin/examples/print-all-items/test-crate/Cargo.toml index 22ef43c3f..54058f6e2 100644 --- a/crates/rustc_plugin/examples/print-all-items/test-crate/Cargo.toml +++ b/crates/rustc_plugin/examples/print-all-items/test-crate/Cargo.toml @@ -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 diff --git a/crates/rustc_plugin/src/cli.rs b/crates/rustc_plugin/src/cli.rs index c8d6e8068..8a39da940 100644 --- a/crates/rustc_plugin/src/cli.rs +++ b/crates/rustc_plugin/src/cli.rs @@ -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"; @@ -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(); } } } diff --git a/crates/rustc_plugin/tests/workspaces/basic/Cargo.toml b/crates/rustc_plugin/tests/workspaces/basic/Cargo.toml index 71433d2f1..3b026876f 100644 --- a/crates/rustc_plugin/tests/workspaces/basic/Cargo.toml +++ b/crates/rustc_plugin/tests/workspaces/basic/Cargo.toml @@ -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 diff --git a/crates/rustc_plugin/tests/workspaces/multi/a/Cargo.toml b/crates/rustc_plugin/tests/workspaces/multi/a/Cargo.toml index 7165a5818..354ec7263 100644 --- a/crates/rustc_plugin/tests/workspaces/multi/a/Cargo.toml +++ b/crates/rustc_plugin/tests/workspaces/multi/a/Cargo.toml @@ -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 diff --git a/crates/rustc_plugin/tests/workspaces/multi/b/Cargo.toml b/crates/rustc_plugin/tests/workspaces/multi/b/Cargo.toml index 2b1b2c2ab..16168aa00 100644 --- a/crates/rustc_plugin/tests/workspaces/multi/b/Cargo.toml +++ b/crates/rustc_plugin/tests/workspaces/multi/b/Cargo.toml @@ -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" } \ No newline at end of file +a = { path = "../a" } diff --git a/crates/rustc_utils/Cargo.toml b/crates/rustc_utils/Cargo.toml index ea7b6aea5..d08381c82 100644 --- a/crates/rustc_utils/Cargo.toml +++ b/crates/rustc_utils/Cargo.toml @@ -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 "] description = "Utilities for working with the Rust compiler" repository = "https://github.com/cognitive-engineering-lab/rustc_plugin" @@ -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 } diff --git a/crates/rustc_utils/src/hir/ty.rs b/crates/rustc_utils/src/hir/ty.rs index 0c3045e43..7d4bd0091 100644 --- a/crates/rustc_utils/src/hir/ty.rs +++ b/crates/rustc_utils/src/hir/ty.rs @@ -26,7 +26,7 @@ pub trait TyExt<'tcx> { impl<'tcx> TyExt<'tcx> for Ty<'tcx> { fn inner_regions(self) -> impl Iterator> { - self.walk().filter_map(|part| match part.unpack() { + self.walk().filter_map(|part| match part.kind() { GenericArgKind::Lifetime(region) => Some(region), _ => None, }) diff --git a/crates/rustc_utils/src/lib.rs b/crates/rustc_utils/src/lib.rs index e5d8efc56..e2568187f 100644 --- a/crates/rustc_utils/src/lib.rs +++ b/crates/rustc_utils/src/lib.rs @@ -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 @@ -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, diff --git a/crates/rustc_utils/src/mir/borrowck_facts.rs b/crates/rustc_utils/src/mir/borrowck_facts.rs index b7b3e4be6..117b7be9f 100644 --- a/crates/rustc_utils/src/mir/borrowck_facts.rs +++ b/crates/rustc_utils/src/mir/borrowck_facts.rs @@ -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. /// @@ -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 @@ -67,29 +68,33 @@ thread_local! { static MIR_BODIES: Cache> = 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; diff --git a/crates/rustc_utils/src/source_map/find_bodies.rs b/crates/rustc_utils/src/source_map/find_bodies.rs index 9217ee881..0a9d34d63 100644 --- a/crates/rustc_utils/src/source_map/find_bodies.rs +++ b/crates/rustc_utils/src/source_map/find_bodies.rs @@ -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 @@ -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 diff --git a/crates/rustc_utils/src/source_map/range.rs b/crates/rustc_utils/src/source_map/range.rs index 8350339f0..aff56f0f0 100644 --- a/crates/rustc_utils/src/source_map/range.rs +++ b/crates/rustc_utils/src/source_map/range.rs @@ -353,7 +353,7 @@ fn qpath_to_span(tcx: TyCtxt, qpath: String) -> Result { .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)); } } } diff --git a/crates/rustc_utils/src/source_map/spanner/hir_span.rs b/crates/rustc_utils/src/source_map/spanner/hir_span.rs index 7eae818d8..8967eec61 100644 --- a/crates/rustc_utils/src/source_map/spanner/hir_span.rs +++ b/crates/rustc_utils/src/source_map/spanner/hir_span.rs @@ -86,8 +86,7 @@ macro_rules! try_span { impl Spanner<'_> { pub fn hir_spans(&self, id: HirId, mode: EnclosingHirSpans) -> Option> { - 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 { diff --git a/crates/rustc_utils/src/source_map/spanner/mod.rs b/crates/rustc_utils/src/source_map/spanner/mod.rs index 368e5f25f..fab128edd 100644 --- a/crates/rustc_utils/src/source_map/spanner/mod.rs +++ b/crates/rustc_utils/src/source_map/spanner/mod.rs @@ -5,18 +5,18 @@ use log::trace; use rustc_hir::{self as hir, BodyId, ExprKind, MatchSource, Node}; use rustc_middle::{ mir::{ - self, visit::Visitor as MirVisitor, Body, StatementKind, TerminatorKind, RETURN_PLACE, + self, Body, RETURN_PLACE, StatementKind, TerminatorKind, visit::Visitor as MirVisitor, }, ty::TyCtxt, }; -use rustc_span::{source_map::Spanned, Span, SpanData}; +use rustc_span::{Span, SpanData, source_map::Spanned}; pub use self::hir_span::EnclosingHirSpans; use self::{ mir_span::{MirSpanCollector, MirSpannedPlace}, span_tree::SpanTree, }; -use crate::{mir::location_or_arg::LocationOrArg, BodyExt, SpanDataExt, SpanExt}; +use crate::{BodyExt, SpanDataExt, SpanExt, mir::location_or_arg::LocationOrArg}; mod hir_span; mod mir_span; @@ -34,10 +34,9 @@ pub struct Spanner<'tcx> { impl<'tcx> Spanner<'tcx> { pub fn new(tcx: TyCtxt<'tcx>, body_id: BodyId, body: &Body<'tcx>) -> Self { - let hir = tcx.hir(); let hir_body = tcx.hir_body(body_id); let owner = tcx.hir_body_owner(body_id); - let item_span = hir.span_with_body(owner); + let item_span = tcx.hir_span_with_body(owner); let ret_span = tcx.hir_fn_decl_by_hir_id(owner).unwrap().output.span(); let mut spanner = Spanner { @@ -50,8 +49,7 @@ impl<'tcx> Spanner<'tcx> { }; trace!( "Body span: {:?}, item span: {:?}", - spanner.body_span, - spanner.item_span + spanner.body_span, spanner.item_span ); let mut mir_collector = MirSpanCollector(&mut spanner, body); @@ -161,10 +159,9 @@ impl<'tcx> Spanner<'tcx> { kind: StatementKind::Assign(box (lhs, _)), .. })) = stmt + && lhs.local == RETURN_PLACE { - if lhs.local == RETURN_PLACE { - hir_spans.push(self.ret_span); - } + hir_spans.push(self.ret_span); } let format_spans = |spans: &[Span]| -> String { diff --git a/crates/rustc_utils/src/test_utils.rs b/crates/rustc_utils/src/test_utils.rs index 94176579f..be89b3988 100644 --- a/crates/rustc_utils/src/test_utils.rs +++ b/crates/rustc_utils/src/test_utils.rs @@ -18,7 +18,7 @@ use rustc_data_structures::fx::{FxHashMap as HashMap, FxHashSet as HashSet}; use rustc_driver::run_compiler; use rustc_hir::BodyId; use rustc_middle::{ - mir::{Body, HasLocalDecls, Local, Place}, + mir::{Body, HasLocalDecls, Local, Place, PlaceTy}, ty::TyCtxt, }; use rustc_span::source_map::FileLoader; @@ -346,10 +346,8 @@ pub struct PlaceBuilder<'a, 'tcx> { impl<'tcx> PlaceBuilder<'_, 'tcx> { pub fn field(mut self, i: usize) -> Self { let f = FieldIdx::from_usize(i); - let ty = self - .place - .ty(self.body.local_decls(), self.tcx) - .field_ty(self.tcx, f); + let place_ty = self.place.ty(self.body.local_decls(), self.tcx); + let ty = PlaceTy::field_ty(self.tcx, place_ty.ty, place_ty.variant_index, f); self.place = self.tcx.mk_place_field(self.place, f, ty); self } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 95c63d8bd..50f81edad 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2025-03-03" +channel = "nightly-2025-08-20" components = ["clippy", "rust-src", "rustc-dev", "llvm-tools-preview"]