diff --git a/README.md b/README.md index 260c4359b..b08fda988 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.10.0-nightly-2024-05-20" +rustc_plugin = "=0.11.0-nightly-2024-12-01" ``` 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. @@ -55,7 +55,7 @@ Normally, Rust libraries have a [minimum supported Rust version][msrv] because t [Aquascope]: https://github.com/cognitive-engineering-lab/aquascope [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.10.0-nightly-2024-05-20/rustc_plugin/ -[docs-utils]: https://cognitive-engineering-lab.github.io/rustc_plugin/v0.10.0-nightly-2024-05-20/rustc_utils/ +[docs]: https://cognitive-engineering-lab.github.io/rustc_plugin/v0.11.0-nightly-2024-12-01/rustc_plugin/ +[docs-utils]: https://cognitive-engineering-lab.github.io/rustc_plugin/v0.11.0-nightly-2024-12-01/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 b87b5bdc4..fa1ccf96a 100644 --- a/crates/rustc_plugin/Cargo.toml +++ b/crates/rustc_plugin/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustc_plugin" -version = "0.10.0-nightly-2024-05-20" +version = "0.11.0-nightly-2024-12-01" edition = "2021" authors = ["Will Crichton "] description = "A framework for writing plugins that integrate with the Rust compiler" diff --git a/crates/rustc_plugin/examples/print-all-items/src/bin/cargo-print-all-items.rs b/crates/rustc_plugin/examples/print-all-items/src/bin/cargo-print-all-items.rs index a82ec6a63..299b20f02 100644 --- a/crates/rustc_plugin/examples/print-all-items/src/bin/cargo-print-all-items.rs +++ b/crates/rustc_plugin/examples/print-all-items/src/bin/cargo-print-all-items.rs @@ -1,3 +1,5 @@ +#![feature(rustc_private)] + fn main() { env_logger::init(); rustc_plugin::cli_main(print_all_items::PrintAllItemsPlugin); diff --git a/crates/rustc_plugin/examples/print-all-items/src/bin/print-all-items-driver.rs b/crates/rustc_plugin/examples/print-all-items/src/bin/print-all-items-driver.rs index 173fee1c2..1d173af8e 100644 --- a/crates/rustc_plugin/examples/print-all-items/src/bin/print-all-items-driver.rs +++ b/crates/rustc_plugin/examples/print-all-items/src/bin/print-all-items-driver.rs @@ -1,3 +1,5 @@ +#![feature(rustc_private)] + fn main() { env_logger::init(); rustc_plugin::driver_main(print_all_items::PrintAllItemsPlugin); 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 37c618799..882d1e489 100644 --- a/crates/rustc_plugin/examples/print-all-items/src/lib.rs +++ b/crates/rustc_plugin/examples/print-all-items/src/lib.rs @@ -78,14 +78,10 @@ impl rustc_driver::Callbacks for PrintAllItemsCallbacks { fn after_analysis<'tcx>( &mut self, _compiler: &rustc_interface::interface::Compiler, - queries: &'tcx rustc_interface::Queries<'tcx>, + tcx: TyCtxt<'tcx>, ) -> rustc_driver::Compilation { - // We extract a key data structure, the `TyCtxt`, which is all we need - // for our simple task of printing out item names. - queries - .global_ctxt() - .unwrap() - .enter(|tcx| print_all_items(tcx, &self.args)); + // We call our top-level function with access to the type context `tcx` and the CLI arguments. + print_all_items(tcx, &self.args); // Note that you should generally allow compilation to continue. If // your plugin is being invoked on a dependency, then you need to ensure diff --git a/crates/rustc_plugin/src/cli.rs b/crates/rustc_plugin/src/cli.rs index 657d644a1..c8d6e8068 100644 --- a/crates/rustc_plugin/src/cli.rs +++ b/crates/rustc_plugin/src/cli.rs @@ -209,7 +209,7 @@ fn only_run_on_file( CompileKind::ProcMacro => {} } - cmd.env(SPECIFIC_CRATE, &pkg.name.replace('-', "_")); + cmd.env(SPECIFIC_CRATE, pkg.name.replace('-', "_")); cmd.env(SPECIFIC_TARGET, kind_str); log::debug!( diff --git a/crates/rustc_utils/Cargo.toml b/crates/rustc_utils/Cargo.toml index 14ec68634..da9a7ac8f 100644 --- a/crates/rustc_utils/Cargo.toml +++ b/crates/rustc_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustc_utils" -version = "0.10.0-nightly-2024-05-20" +version = "0.11.0-nightly-2024-12-01" edition = "2021" authors = ["Will Crichton "] description = "Utilities for working with the Rust compiler" diff --git a/crates/rustc_utils/src/hir/ty.rs b/crates/rustc_utils/src/hir/ty.rs index 1a7d5b501..22bb45157 100644 --- a/crates/rustc_utils/src/hir/ty.rs +++ b/crates/rustc_utils/src/hir/ty.rs @@ -3,8 +3,9 @@ use rustc_data_structures::captures::Captures; use rustc_hir::def_id::DefId; use rustc_infer::infer::TyCtxtInferExt; -use rustc_middle::ty::{GenericArgKind, ParamEnv, Region, Ty, TyCtxt}; +use rustc_middle::ty::{GenericArgKind, ParamEnv, Region, Ty, TyCtxt, TypingEnv}; use rustc_trait_selection::infer::InferCtxtExt; +use rustc_type_ir::TypingMode; /// Extension trait for [`Ty`]. pub trait TyExt<'tcx> { @@ -24,12 +25,14 @@ pub trait TyExt<'tcx> { ) -> bool; /// Returns true if a type implements `Copy`. - fn is_copyable(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> bool; + fn is_copyable(&self, tcx: TyCtxt<'tcx>, typing_env: TypingEnv<'tcx>) -> bool; } impl<'tcx> TyExt<'tcx> for Ty<'tcx> { - type AllRegionsIter<'a> = impl Iterator> + Captures<'tcx> + 'a - where Self: 'a; + type AllRegionsIter<'a> + = impl Iterator> + Captures<'tcx> + 'a + where + Self: 'a; fn inner_regions(&self) -> Self::AllRegionsIter<'_> { self.walk().filter_map(|part| match part.unpack() { @@ -46,7 +49,7 @@ impl<'tcx> TyExt<'tcx> for Ty<'tcx> { ) -> bool { use rustc_infer::traits::EvaluationResult; - let infcx = tcx.infer_ctxt().build(); + let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); let ty = tcx.erase_regions(*self); let result = infcx.type_implements_trait(trait_def_id, [ty], param_env); matches!( @@ -55,15 +58,15 @@ impl<'tcx> TyExt<'tcx> for Ty<'tcx> { ) } - fn is_copyable(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> bool { + fn is_copyable(&self, tcx: TyCtxt<'tcx>, typing_env: TypingEnv<'tcx>) -> bool { let ty = tcx.erase_regions(*self); - ty.is_copy_modulo_regions(tcx, param_env) + ty.is_copy_modulo_regions(tcx, typing_env) } } #[cfg(test)] mod test { - use rustc_middle::ty::ParamEnv; + use rustc_middle::ty::TypingEnv; use super::TyExt; use crate::{test_utils, BodyExt}; @@ -84,8 +87,8 @@ fn main() { assert_eq!(x.ty.inner_regions().count(), 1); assert_eq!(y.ty.inner_regions().count(), 0); - assert!(!x.ty.is_copyable(tcx, ParamEnv::empty())); - assert!(y.ty.is_copyable(tcx, ParamEnv::empty())); + assert!(!x.ty.is_copyable(tcx, TypingEnv::fully_monomorphized())); + assert!(y.ty.is_copyable(tcx, TypingEnv::fully_monomorphized())); }); } } diff --git a/crates/rustc_utils/src/lib.rs b/crates/rustc_utils/src/lib.rs index 0f58b7174..f89961d50 100644 --- a/crates/rustc_utils/src/lib.rs +++ b/crates/rustc_utils/src/lib.rs @@ -15,7 +15,6 @@ negative_impls, // for !Send min_specialization, // for rustc_index::newtype_index type_alias_impl_trait, // for iterators in traits - lazy_cell, // for global constants w/ heap allocation box_patterns, // for ergonomics let_chains, // for places_conflict module exact_size_is_empty, // for graphviz module diff --git a/crates/rustc_utils/src/mir/body.rs b/crates/rustc_utils/src/mir/body.rs index 395f63036..8e1326f12 100644 --- a/crates/rustc_utils/src/mir/body.rs +++ b/crates/rustc_utils/src/mir/body.rs @@ -7,6 +7,7 @@ use std::{ }; use anyhow::{ensure, Result}; +use pretty::PrettyPrintMirOptions; use rustc_data_structures::{captures::Captures, fx::FxHashMap as HashMap}; use rustc_hir::{def_id::DefId, CoroutineDesugaring, CoroutineKind, HirId}; use rustc_middle::{ @@ -84,7 +85,10 @@ pub trait BodyExt<'tcx> { } impl<'tcx> BodyExt<'tcx> for Body<'tcx> { - type AllReturnsIter<'a> = impl Iterator + Captures<'tcx> + 'a where Self: 'a; + type AllReturnsIter<'a> + = impl Iterator + Captures<'tcx> + 'a + where + Self: 'a; fn all_returns(&self) -> Self::AllReturnsIter<'_> { self .basic_blocks @@ -98,7 +102,10 @@ impl<'tcx> BodyExt<'tcx> for Body<'tcx> { }) } - type AllLocationsIter<'a> = impl Iterator + Captures<'tcx> + 'a where Self: 'a; + type AllLocationsIter<'a> + = impl Iterator + Captures<'tcx> + 'a + where + Self: 'a; fn all_locations(&self) -> Self::AllLocationsIter<'_> { self .basic_blocks @@ -133,7 +140,15 @@ impl<'tcx> BodyExt<'tcx> for Body<'tcx> { fn to_string(&self, tcx: TyCtxt<'tcx>) -> Result { let mut buffer = Vec::new(); - write_mir_fn(tcx, self, &mut |_, _| Ok(()), &mut buffer)?; + write_mir_fn( + tcx, + self, + &mut |_, _| Ok(()), + &mut buffer, + PrettyPrintMirOptions { + include_extra_comments: false, + }, + )?; Ok(String::from_utf8(buffer)?) } @@ -166,13 +181,17 @@ impl<'tcx> BodyExt<'tcx> for Body<'tcx> { } } - type ArgRegionsIter<'a> = impl Iterator> + Captures<'tcx> + 'a - where Self: 'a; + type ArgRegionsIter<'a> + = impl Iterator> + Captures<'tcx> + 'a + where + Self: 'a; type ReturnRegionsIter = impl Iterator>; - type PlacesIter<'a> = impl Iterator> + Captures<'tcx> + 'a - where Self: 'a; + type PlacesIter<'a> + = impl Iterator> + Captures<'tcx> + 'a + where + Self: 'a; fn regions_in_args(&self) -> Self::ArgRegionsIter<'_> { self diff --git a/crates/rustc_utils/src/mir/borrowck_facts.rs b/crates/rustc_utils/src/mir/borrowck_facts.rs index 55c9db4ba..8a7a7cb47 100644 --- a/crates/rustc_utils/src/mir/borrowck_facts.rs +++ b/crates/rustc_utils/src/mir/borrowck_facts.rs @@ -6,7 +6,7 @@ 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, MirPass, StatementKind, TerminatorKind}, + mir::{Body, BorrowCheckResult, StatementKind, TerminatorKind}, ty::TyCtxt, util::Providers, }; @@ -17,40 +17,37 @@ use crate::{block_timer, cache::Cache, BodyExt}; /// /// This pass helps reduce the number of intermediates during dataflow analysis, which /// reduces memory usage. -pub struct SimplifyMir; -impl<'tcx> MirPass<'tcx> for SimplifyMir { - fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let return_blocks = body - .all_returns() - .filter_map(|loc| { - let bb = &body.basic_blocks[loc.block]; - (bb.statements.len() == 0).then_some(loc.block) - }) - .collect::>(); - - for block in body.basic_blocks_mut() { - block.statements.retain(|stmt| { - !matches!( - stmt.kind, - StatementKind::StorageLive(..) | StatementKind::StorageDead(..) - ) - }); - - let terminator = block.terminator_mut(); - terminator.kind = match terminator.kind { - TerminatorKind::FalseEdge { real_target, .. } => TerminatorKind::Goto { - target: real_target, - }, - TerminatorKind::FalseUnwind { real_target, .. } => TerminatorKind::Goto { - target: real_target, - }, - // Ensures that control dependencies can determine the independence of differnet - // return paths - TerminatorKind::Goto { target } if return_blocks.contains(&target) => { - TerminatorKind::Return - } - _ => continue, +pub fn simplify_mir(body: &mut Body<'_>) { + let return_blocks = body + .all_returns() + .filter_map(|loc| { + let bb = &body.basic_blocks[loc.block]; + (bb.statements.len() == 0).then_some(loc.block) + }) + .collect::>(); + + for block in body.basic_blocks_mut() { + block.statements.retain(|stmt| { + !matches!( + stmt.kind, + StatementKind::StorageLive(..) | StatementKind::StorageDead(..) + ) + }); + + let terminator = block.terminator_mut(); + terminator.kind = match terminator.kind { + TerminatorKind::FalseEdge { real_target, .. } => TerminatorKind::Goto { + target: real_target, + }, + TerminatorKind::FalseUnwind { real_target, .. } => TerminatorKind::Goto { + target: real_target, + }, + // Ensures that control dependencies can determine the independence of differnet + // return paths + TerminatorKind::Goto { target } if return_blocks.contains(&target) => { + TerminatorKind::Return } + _ => continue, } } } @@ -86,7 +83,7 @@ fn mir_borrowck(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &BorrowCheckResult<'_> { ); if SIMPLIFY_MIR.load(Ordering::SeqCst) { - SimplifyMir.run_pass(tcx, &mut body_with_facts.body); + simplify_mir(&mut body_with_facts.body); } // SAFETY: The reader casts the 'static lifetime to 'tcx before using it. diff --git a/crates/rustc_utils/src/mir/control_dependencies.rs b/crates/rustc_utils/src/mir/control_dependencies.rs index c329ffc98..e7e0aaa49 100644 --- a/crates/rustc_utils/src/mir/control_dependencies.rs +++ b/crates/rustc_utils/src/mir/control_dependencies.rs @@ -10,7 +10,7 @@ use std::fmt; use rustc_data_structures::graph::{dominators::Dominators, vec_graph::VecGraph, *}; use rustc_index::{ - bit_set::{BitSet, HybridBitSet, SparseBitMatrix}, + bit_set::{BitSet, ChunkedBitSet, SparseBitMatrix}, Idx, }; use smallvec::SmallVec; @@ -191,7 +191,7 @@ impl ControlDependencies { } /// Returns the set of all node that are control-dependent on the given `node`. - pub fn dependent_on(&self, node: Node) -> Option<&HybridBitSet> { + pub fn dependent_on(&self, node: Node) -> Option<&ChunkedBitSet> { self.0.row(node) } } diff --git a/crates/rustc_utils/src/mir/place.rs b/crates/rustc_utils/src/mir/place.rs index 91c8052f1..513020c7e 100644 --- a/crates/rustc_utils/src/mir/place.rs +++ b/crates/rustc_utils/src/mir/place.rs @@ -9,17 +9,17 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::{ mir::{ visit::{PlaceContext, Visitor}, - Body, HasLocalDecls, Local, Location, MirPass, Mutability, Place, PlaceElem, - PlaceRef, ProjectionElem, StatementKind, TerminatorKind, VarDebugInfo, - VarDebugInfoContents, RETURN_PLACE, + Body, HasLocalDecls, Local, Location, Mutability, Place, PlaceElem, PlaceRef, + ProjectionElem, VarDebugInfo, VarDebugInfoContents, RETURN_PLACE, }, traits::ObligationCause, ty::{self, AdtKind, Region, RegionKind, RegionVid, Ty, TyCtxt, TyKind, TypeVisitor}, }; use rustc_target::abi::{FieldIdx, VariantIdx}; use rustc_trait_selection::traits::NormalizeExt; +use rustc_type_ir::TypingMode; -use crate::{AdtDefExt, BodyExt, SpanExt}; +use crate::{AdtDefExt, SpanExt}; /// A MIR [`Visitor`] which collects all [`Place`]s that appear in the visited object. #[derive(Default)] @@ -36,48 +36,6 @@ impl<'tcx> Visitor<'tcx> for PlaceCollector<'tcx> { } } -/// MIR pass to remove instructions not important for Flowistry. -/// -/// This pass helps reduce the number of intermediates during dataflow analysis, which -/// reduces memory usage. -pub struct SimplifyMir; -impl<'tcx> MirPass<'tcx> for SimplifyMir { - fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let return_blocks = body - .all_returns() - .filter_map(|loc| { - let bb = &body.basic_blocks[loc.block]; - bb.statements.is_empty().then_some(loc.block) - }) - .collect::>(); - - for block in body.basic_blocks_mut() { - block.statements.retain(|stmt| { - !matches!( - stmt.kind, - StatementKind::StorageLive(..) | StatementKind::StorageDead(..) - ) - }); - - let terminator = block.terminator_mut(); - terminator.kind = match terminator.kind { - TerminatorKind::FalseEdge { real_target, .. } => TerminatorKind::Goto { - target: real_target, - }, - TerminatorKind::FalseUnwind { real_target, .. } => TerminatorKind::Goto { - target: real_target, - }, - // Ensures that control dependencies can determine the independence of differnet - // return paths - TerminatorKind::Goto { target } if return_blocks.contains(&target) => { - TerminatorKind::Return - } - _ => continue, - } - } - } -} - /// Extension trait for [`Place`]. pub trait PlaceExt<'tcx> { /// Creates a new [`Place`]. @@ -184,7 +142,10 @@ impl<'tcx> PlaceExt<'tcx> for Place<'tcx> { || self.refs_in_projection(body, tcx).next().is_none() } - type RefsInProjectionIter<'a> = impl Iterator, &'tcx [PlaceElem<'tcx>])> + 'a where Self: 'a; + type RefsInProjectionIter<'a> + = impl Iterator, &'tcx [PlaceElem<'tcx>])> + 'a + where + Self: 'a; fn refs_in_projection( &self, body: &Body<'tcx>, @@ -379,7 +340,10 @@ impl<'tcx> PlaceExt<'tcx> for Place<'tcx> { fn normalize(&self, tcx: TyCtxt<'tcx>, def_id: DefId) -> Place<'tcx> { let param_env = tcx.param_env(def_id); let place = tcx.erase_regions(*self); - let infcx = tcx.infer_ctxt().build(); + let infcx = tcx.infer_ctxt().build(TypingMode::post_borrowck_analysis( + tcx, + def_id.expect_local(), + )); let place = infcx .at(&ObligationCause::dummy(), param_env) .normalize(place) @@ -440,15 +404,6 @@ impl<'tcx> RegionVisitorDispatcher<'tcx> for VisitedPlacesCollector<'tcx> { } } -#[derive(Default)] -struct VisitedTypesCollector<'tcx>(HashSet>); - -impl<'tcx> RegionVisitorDispatcher<'tcx> for VisitedTypesCollector<'tcx> { - fn on_visit_type(&mut self, ty: Ty<'tcx>) { - self.0.insert(ty); - } -} - #[derive(Default)] struct RegionMemberCollector<'tcx>(HashMap, Mutability)>>); @@ -530,7 +485,7 @@ impl<'tcx, Dispatcher: RegionVisitorDispatcher<'tcx>> TypeVisitor> match ty.kind() { _ if ty.is_box() => { self.place_stack.push(ProjectionElem::Deref); - self.visit_ty(ty.boxed_ty()); + self.visit_ty(ty.boxed_ty().expect("Cannot unbox boxed type??")); self.place_stack.pop(); } @@ -706,8 +661,10 @@ mod test { ty::TyCtxt, }; - use super::{BodyExt, PlaceExt}; - use crate::test_utils::{self, compare_sets, Placer}; + use crate::{ + test_utils::{self, compare_sets, Placer}, + BodyExt, PlaceExt, + }; #[test] fn test_place_arg_direct() { diff --git a/crates/rustc_utils/src/source_map/range.rs b/crates/rustc_utils/src/source_map/range.rs index 02f47d05a..9ff96d2b0 100644 --- a/crates/rustc_utils/src/source_map/range.rs +++ b/crates/rustc_utils/src/source_map/range.rs @@ -283,7 +283,8 @@ impl ByteRange { let external = file.external_src.borrow(); let _src = file .src - .as_ref() + .as_deref() + .map(|s| s.as_str()) .unwrap_or_else(|| external.get_source().as_ref().unwrap()); let byte_start = BytePos(span.lo().0 as usize); 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 326309e9e..7eae818d8 100644 --- a/crates/rustc_utils/src/source_map/spanner/hir_span.rs +++ b/crates/rustc_utils/src/source_map/spanner/hir_span.rs @@ -16,7 +16,7 @@ struct ChildExprSpans { spans: Vec, item_span: Span, } -impl<'hir> HirVisitor<'hir> for ChildExprSpans { +impl HirVisitor<'_> for ChildExprSpans { fn visit_expr(&mut self, ex: &hir::Expr) { match ex.kind { // Don't take the span for the whole block, since we want to leave @@ -84,7 +84,7 @@ macro_rules! try_span { }; } -impl<'tcx> Spanner<'tcx> { +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)); diff --git a/crates/rustc_utils/src/test_utils.rs b/crates/rustc_utils/src/test_utils.rs index 12ec2ad24..74d53c9f3 100644 --- a/crates/rustc_utils/src/test_utils.rs +++ b/crates/rustc_utils/src/test_utils.rs @@ -350,7 +350,7 @@ pub struct PlaceBuilder<'a, 'tcx> { place: Place<'tcx>, } -impl<'a, 'tcx> PlaceBuilder<'a, 'tcx> { +impl<'tcx> PlaceBuilder<'_, 'tcx> { pub fn field(mut self, i: usize) -> Self { let f = FieldIdx::from_usize(i); let ty = self diff --git a/rust-toolchain.toml b/rust-toolchain.toml index f47c0184c..1d3f18d37 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2024-05-20" +channel = "nightly-2024-12-01" components = ["clippy", "rust-src", "rustc-dev", "llvm-tools-preview"]