diff options
795 files changed, 11101 insertions, 5594 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2d8980fcd1a..b12a0b855e2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -201,6 +201,9 @@ jobs: - name: dist-i686-linux os: ubuntu-20.04-8core-32gb env: {} + - name: dist-loongarch64-linux + os: ubuntu-20.04-8core-32gb + env: {} - name: dist-mips-linux os: ubuntu-20.04-8core-32gb env: {} diff --git a/Cargo.lock b/Cargo.lock index f4510aa9130..0369442f11c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -154,11 +154,11 @@ checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" [[package]] name = "ar_archive_writer" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0639441fd17a3197d1cbca8dc8768cc172a63b64b4bb6c372e8f41ed0acc9bb" +checksum = "74cfb39880a59e122232cb5fb06b20b4382d58c12fa9747d16f846d38a7b094c" dependencies = [ - "object", + "object 0.31.1", ] [[package]] @@ -233,7 +233,7 @@ dependencies = [ "cfg-if", "libc", "miniz_oxide", - "object", + "object 0.30.1", "rustc-demangle", ] @@ -247,6 +247,15 @@ dependencies = [ ] [[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -332,6 +341,12 @@ dependencies = [ ] [[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] name = "bytes" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -629,7 +644,7 @@ dependencies = [ "itertools", "pulldown-cmark", "quine-mc_cluskey", - "regex-syntax", + "regex-syntax 0.7.1", "rustc-semver", "semver", "serde", @@ -711,9 +726,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.91" +version = "0.1.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "571298a3cce7e2afbd3d61abb91a18667d5ab25993ec577a88ee8ac45f00cc3a" +checksum = "64518f1ae689f74db058bbfb3238dfe6eb53f59f4ae712f1ff4348628522e190" dependencies = [ "cc", "rustc-std-workspace-core", @@ -889,6 +904,16 @@ dependencies = [ ] [[package]] +name = "ctrlc" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7394a21d012ce5c850497fb774b167d81b99f060025fbf06ee92b9848bd97eb2" +dependencies = [ + "nix", + "windows-sys 0.48.0", +] + +[[package]] name = "curl" version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2289,6 +2314,7 @@ name = "miri" version = "0.1.0" dependencies = [ "colored", + "ctrlc", "env_logger 0.9.0", "getrandom", "lazy_static", @@ -2319,6 +2345,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" [[package]] +name = "nix" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" +dependencies = [ + "bitflags", + "cfg-if", + "libc", + "static_assertions", +] + +[[package]] name = "nom" version = "7.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2365,13 +2403,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d864c91689fdc196779b98dba0aceac6118594c2df6ee5d943eb6a8df4d107a" dependencies = [ "compiler_builtins", + "memchr", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + +[[package]] +name = "object" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ "crc32fast", "flate2", "hashbrown 0.13.1", "indexmap", "memchr", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", + "ruzstd", ] [[package]] @@ -2731,9 +2779,9 @@ dependencies = [ [[package]] name = "pulldown-cmark" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63" +checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" dependencies = [ "bitflags", "memchr", @@ -2866,7 +2914,7 @@ checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.6.26", ] [[package]] @@ -2875,7 +2923,7 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "regex-syntax", + "regex-syntax 0.6.26", ] [[package]] @@ -2894,6 +2942,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" [[package]] +name = "regex-syntax" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" + +[[package]] name = "remote-test-client" version = "0.1.0" @@ -3223,7 +3277,7 @@ dependencies = [ "cstr", "libc", "measureme", - "object", + "object 0.31.1", "rustc-demangle", "rustc_ast", "rustc_attr", @@ -3259,7 +3313,7 @@ dependencies = [ "itertools", "jobserver", "libc", - "object", + "object 0.31.1", "pathdiff", "regex", "rustc_arena", @@ -4329,6 +4383,7 @@ dependencies = [ name = "rustdoc-json-types" version = "0.1.0" dependencies = [ + "bincode", "rustc-hash", "serde", "serde_json", @@ -4419,6 +4474,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088" [[package]] +name = "ruzstd" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a15e661f0f9dac21f3494fe5d23a6338c0ac116a2d22c2b63010acd89467ffe" +dependencies = [ + "byteorder", + "thiserror", + "twox-hash", +] + +[[package]] name = "ryu" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4654,7 +4720,7 @@ dependencies = [ "hermit-abi 0.3.0", "libc", "miniz_oxide", - "object", + "object 0.30.1", "panic_abort", "panic_unwind", "profiler_builtins", @@ -4926,13 +4992,13 @@ dependencies = [ [[package]] name = "thorin-dwp" -version = "0.4.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da8fbf660a019b6bf11ea95762041464aa9099cc293b6a66d77cea5107619671" +checksum = "98c040e1340b889d4180c64e1d787efa9c32cb1617757e101480b61238b0d927" dependencies = [ "gimli 0.26.2", "hashbrown 0.12.3", - "object", + "object 0.31.1", "tracing", ] diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 43b429f6947..4360fbeb9bb 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2391,10 +2391,10 @@ pub struct FnDecl { impl FnDecl { pub fn has_self(&self) -> bool { - self.inputs.get(0).map_or(false, Param::is_self) + self.inputs.get(0).is_some_and(Param::is_self) } pub fn c_variadic(&self) -> bool { - self.inputs.last().map_or(false, |arg| matches!(arg.ty.kind, TyKind::CVarArgs)) + self.inputs.last().is_some_and(|arg| matches!(arg.ty.kind, TyKind::CVarArgs)) } } diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index e6c4db9e2ae..15fe29580c2 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -149,7 +149,7 @@ impl Attribute { } pub fn may_have_doc_links(&self) -> bool { - self.doc_str().map_or(false, |s| comments::may_have_doc_links(s.as_str())) + self.doc_str().is_some_and(|s| comments::may_have_doc_links(s.as_str())) } pub fn is_proc_macro_attr(&self) -> bool { @@ -441,12 +441,12 @@ impl NestedMetaItem { /// Returns `true` if this list item is a MetaItem with a name of `name`. pub fn has_name(&self, name: Symbol) -> bool { - self.meta_item().map_or(false, |meta_item| meta_item.has_name(name)) + self.meta_item().is_some_and(|meta_item| meta_item.has_name(name)) } /// Returns `true` if `self` is a `MetaItem` and the meta item is a word. pub fn is_word(&self) -> bool { - self.meta_item().map_or(false, |meta_item| meta_item.is_word()) + self.meta_item().is_some_and(|meta_item| meta_item.is_word()) } /// Gets a list of inner meta items from a list `MetaItem` type. diff --git a/compiler/rustc_ast/src/expand/allocator.rs b/compiler/rustc_ast/src/expand/allocator.rs index 35939496348..e87f6e820a1 100644 --- a/compiler/rustc_ast/src/expand/allocator.rs +++ b/compiler/rustc_ast/src/expand/allocator.rs @@ -1,20 +1,28 @@ use rustc_span::symbol::{sym, Symbol}; -#[derive(Clone, Debug, Copy, HashStable_Generic)] +#[derive(Clone, Debug, Copy, Eq, PartialEq, HashStable_Generic)] pub enum AllocatorKind { Global, Default, } -impl AllocatorKind { - pub fn fn_name(&self, base: Symbol) -> String { - match *self { - AllocatorKind::Global => format!("__rg_{base}"), - AllocatorKind::Default => format!("__rdl_{base}"), - } +pub fn global_fn_name(base: Symbol) -> String { + format!("__rust_{base}") +} + +pub fn default_fn_name(base: Symbol) -> String { + format!("__rdl_{base}") +} + +pub fn alloc_error_handler_name(alloc_error_handler_kind: AllocatorKind) -> &'static str { + match alloc_error_handler_kind { + AllocatorKind::Global => "__rg_oom", + AllocatorKind::Default => "__rdl_oom", } } +pub const NO_ALLOC_SHIM_IS_UNSTABLE: &str = "__rust_no_alloc_shim_is_unstable"; + pub enum AllocatorTy { Layout, Ptr, diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 42b843482a3..7ef39f8026b 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -607,7 +607,7 @@ impl Token { /// Returns `true` if the token is an identifier whose name is the given /// string slice. pub fn is_ident_named(&self, name: Symbol) -> bool { - self.ident().map_or(false, |(ident, _)| ident.name == name) + self.ident().is_some_and(|(ident, _)| ident.name == name) } /// Returns `true` if the token is an interpolated path. diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs index 15a54fe13d0..50eb92125b9 100644 --- a/compiler/rustc_ast/src/util/literal.rs +++ b/compiler/rustc_ast/src/util/literal.rs @@ -392,8 +392,7 @@ fn integer_lit(symbol: Symbol, suffix: Option<Symbol>) -> Result<LitKind, LitErr // Small bases are lexed as if they were base 10, e.g, the string // might be `0b10201`. This will cause the conversion above to fail, // but these kinds of errors are already reported by the lexer. - let from_lexer = - base < 10 && s.chars().any(|c| c.to_digit(10).map_or(false, |d| d >= base)); + let from_lexer = base < 10 && s.chars().any(|c| c.to_digit(10).is_some_and(|d| d >= base)); if from_lexer { LitError::LexerError } else { LitError::IntTooLarge(base) } }) } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index cd6614a54a4..211f5cb0a2a 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -58,7 +58,7 @@ use rustc_errors::{ use rustc_fluent_macro::fluent_messages; use rustc_hir as hir; use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res}; -use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID}; +use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; use rustc_hir::definitions::DefPathData; use rustc_hir::{ConstArg, GenericArg, ItemLocalId, ParamName, TraitCandidate}; use rustc_index::{Idx, IndexSlice, IndexVec}; @@ -435,6 +435,7 @@ pub fn lower_to_hir(tcx: TyCtxt<'_>, (): ()) -> hir::Crate<'_> { // Queries that borrow `resolver_for_lowering`. tcx.ensure_with_value().output_filenames(()); tcx.ensure_with_value().early_lint_checks(()); + tcx.ensure_with_value().debugger_visualizers(LOCAL_CRATE); let (mut resolver, krate) = tcx.resolver_for_lowering(()).steal(); let ast_index = index_crate(&resolver.node_id_to_def_id, &krate); diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index bf43bbdbbee..04ed2767876 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -348,7 +348,7 @@ impl<'a> AstValidator<'a> { let source_map = self.session.source_map(); let end = source_map.end_point(sp); - if source_map.span_to_snippet(end).map(|s| s == ";").unwrap_or(false) { + if source_map.span_to_snippet(end).is_ok_and(|s| s == ";") { end } else { sp.shrink_to_hi() @@ -736,11 +736,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> { this.visit_expr(&arm.body); this.visit_pat(&arm.pat); walk_list!(this, visit_attribute, &arm.attrs); - if let Some(guard) = &arm.guard && let ExprKind::Let(_, guard_expr, _) = &guard.kind { + if let Some(guard) = &arm.guard { this.with_let_management(None, |this, _| { - this.visit_expr(guard_expr) + this.visit_expr(guard) }); - return; } } } diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 3d5056d82c5..274f931e43f 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -317,8 +317,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { match i.kind { ast::ForeignItemKind::Fn(..) | ast::ForeignItemKind::Static(..) => { let link_name = attr::first_attr_value_str_by_name(&i.attrs, sym::link_name); - let links_to_llvm = - link_name.map_or(false, |val| val.as_str().starts_with("llvm.")); + let links_to_llvm = link_name.is_some_and(|val| val.as_str().starts_with("llvm.")); if links_to_llvm { gate_feature_post!( &self, diff --git a/compiler/rustc_borrowck/src/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs index 4824f6346d4..6be20b0974d 100644 --- a/compiler/rustc_borrowck/src/borrow_set.rs +++ b/compiler/rustc_borrowck/src/borrow_set.rs @@ -30,7 +30,7 @@ pub struct BorrowSet<'tcx> { /// Map from local to all the borrows on that local. pub local_map: FxIndexMap<mir::Local, FxIndexSet<BorrowIndex>>, - pub(crate) locals_state_at_exit: LocalsStateAtExit, + pub locals_state_at_exit: LocalsStateAtExit, } impl<'tcx> Index<BorrowIndex> for BorrowSet<'tcx> { @@ -153,7 +153,7 @@ impl<'tcx> BorrowSet<'tcx> { self.activation_map.get(&location).map_or(&[], |activations| &activations[..]) } - pub(crate) fn len(&self) -> usize { + pub fn len(&self) -> usize { self.location_map.len() } diff --git a/compiler/rustc_borrowck/src/consumers.rs b/compiler/rustc_borrowck/src/consumers.rs index 3451b7db8ca..d257145373f 100644 --- a/compiler/rustc_borrowck/src/consumers.rs +++ b/compiler/rustc_borrowck/src/consumers.rs @@ -3,22 +3,96 @@ //! This file provides API for compiler consumers. use rustc_hir::def_id::LocalDefId; -use rustc_index::IndexSlice; -use rustc_infer::infer::{DefiningAnchor, TyCtxtInferExt}; -use rustc_middle::mir::Body; +use rustc_index::{IndexSlice, IndexVec}; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_middle::mir::{Body, Promoted}; +use rustc_middle::traits::DefiningAnchor; use rustc_middle::ty::TyCtxt; +use std::rc::Rc; + +use crate::borrow_set::BorrowSet; pub use super::{ + constraints::OutlivesConstraint, + dataflow::{calculate_borrows_out_of_scope_at_location, BorrowIndex, Borrows}, facts::{AllFacts as PoloniusInput, RustcFacts}, location::{LocationTable, RichLocation}, nll::PoloniusOutput, - BodyWithBorrowckFacts, + place_ext::PlaceExt, + places_conflict::{places_conflict, PlaceConflictBias}, + region_infer::RegionInferenceContext, }; -/// This function computes Polonius facts for the given body. It makes a copy of -/// the body because it needs to regenerate the region identifiers. This function -/// should never be invoked during a typical compilation session due to performance -/// issues with Polonius. +/// Options determining the output behavior of [`get_body_with_borrowck_facts`]. +/// +/// If executing under `-Z polonius` the choice here has no effect, and everything as if +/// [`PoloniusOutputFacts`](ConsumerOptions::PoloniusOutputFacts) had been selected +/// will be retrieved. +#[derive(Debug, Copy, Clone)] +pub enum ConsumerOptions { + /// Retrieve the [`Body`] along with the [`BorrowSet`](super::borrow_set::BorrowSet) + /// and [`RegionInferenceContext`]. If you would like the body only, use + /// [`TyCtxt::mir_promoted`]. + /// + /// These can be used in conjunction with [`calculate_borrows_out_of_scope_at_location`]. + RegionInferenceContext, + /// The recommended option. Retrieves the maximal amount of information + /// without significant slowdowns. + /// + /// Implies [`RegionInferenceContext`](ConsumerOptions::RegionInferenceContext), + /// and additionally retrieve the [`LocationTable`] and [`PoloniusInput`] that + /// would be given to Polonius. Critically, this does not run Polonius, which + /// one may want to avoid due to performance issues on large bodies. + PoloniusInputFacts, + /// Implies [`PoloniusInputFacts`](ConsumerOptions::PoloniusInputFacts), + /// and additionally runs Polonius to calculate the [`PoloniusOutput`]. + PoloniusOutputFacts, +} + +impl ConsumerOptions { + /// Should the Polonius input facts be computed? + pub(crate) fn polonius_input(&self) -> bool { + matches!(self, Self::PoloniusInputFacts | Self::PoloniusOutputFacts) + } + /// Should we run Polonius and collect the output facts? + pub(crate) fn polonius_output(&self) -> bool { + matches!(self, Self::PoloniusOutputFacts) + } +} + +/// A `Body` with information computed by the borrow checker. This struct is +/// intended to be consumed by compiler consumers. +/// +/// We need to include the MIR body here because the region identifiers must +/// match the ones in the Polonius facts. +pub struct BodyWithBorrowckFacts<'tcx> { + /// A mir body that contains region identifiers. + pub body: Body<'tcx>, + /// The mir bodies of promoteds. + pub promoted: IndexVec<Promoted, Body<'tcx>>, + /// The set of borrows occurring in `body` with data about them. + pub borrow_set: Rc<BorrowSet<'tcx>>, + /// Context generated during borrowck, intended to be passed to + /// [`calculate_borrows_out_of_scope_at_location`]. + pub region_inference_context: Rc<RegionInferenceContext<'tcx>>, + /// The table that maps Polonius points to locations in the table. + /// Populated when using [`ConsumerOptions::PoloniusInputFacts`] + /// or [`ConsumerOptions::PoloniusOutputFacts`]. + pub location_table: Option<LocationTable>, + /// Polonius input facts. + /// Populated when using [`ConsumerOptions::PoloniusInputFacts`] + /// or [`ConsumerOptions::PoloniusOutputFacts`]. + pub input_facts: Option<Box<PoloniusInput>>, + /// Polonius output facts. Populated when using + /// [`ConsumerOptions::PoloniusOutputFacts`]. + pub output_facts: Option<Rc<PoloniusOutput>>, +} + +/// This function computes borrowck facts for the given body. The [`ConsumerOptions`] +/// determine which facts are returned. This function makes a copy of the body because +/// it needs to regenerate the region identifiers. It should never be invoked during a +/// typical compilation session due to the unnecessary overhead of returning +/// [`BodyWithBorrowckFacts`]. /// /// Note: /// * This function will panic if the required body was already stolen. This @@ -28,10 +102,14 @@ pub use super::{ /// that shows how to do this at `tests/run-make/obtain-borrowck/`. /// /// * Polonius is highly unstable, so expect regular changes in its signature or other details. -pub fn get_body_with_borrowck_facts(tcx: TyCtxt<'_>, def: LocalDefId) -> BodyWithBorrowckFacts<'_> { +pub fn get_body_with_borrowck_facts( + tcx: TyCtxt<'_>, + def: LocalDefId, + options: ConsumerOptions, +) -> BodyWithBorrowckFacts<'_> { let (input_body, promoted) = tcx.mir_promoted(def); let infcx = tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bind(def)).build(); let input_body: &Body<'_> = &input_body.borrow(); let promoted: &IndexSlice<_, _> = &promoted.borrow(); - *super::do_mir_borrowck(&infcx, input_body, promoted, true).1.unwrap() + *super::do_mir_borrowck(&infcx, input_body, promoted, Some(options)).1.unwrap() } diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index 167f245361a..2daa82aef39 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -156,10 +156,10 @@ impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> { &mut self, borrow_index: BorrowIndex, borrow_region: RegionVid, - location: Location, + first_location: Location, ) { // We visit one BB at a time. The complication is that we may start in the - // middle of the first BB visited (the one containing `location`), in which + // middle of the first BB visited (the one containing `first_location`), in which // case we may have to later on process the first part of that BB if there // is a path back to its start. @@ -168,61 +168,58 @@ impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> { // `visited` once they are added to `stack`, before they are actually // processed, because this avoids the need to look them up again on // completion. - self.visited.insert(location.block); + self.visited.insert(first_location.block); - let mut first_lo = location.statement_index; - let first_hi = self.body[location.block].statements.len(); + let first_block = first_location.block; + let mut first_lo = first_location.statement_index; + let first_hi = self.body[first_block].statements.len(); - self.visit_stack.push(StackEntry { bb: location.block, lo: first_lo, hi: first_hi }); + self.visit_stack.push(StackEntry { bb: first_block, lo: first_lo, hi: first_hi }); - while let Some(StackEntry { bb, lo, hi }) = self.visit_stack.pop() { - // If we process the first part of the first basic block (i.e. we encounter that block - // for the second time), we no longer have to visit its successors again. - let mut finished_early = bb == location.block && hi != first_hi; - for i in lo..=hi { - let location = Location { block: bb, statement_index: i }; + 'preorder: while let Some(StackEntry { bb, lo, hi }) = self.visit_stack.pop() { + if let Some(kill_stmt) = + self.regioncx.first_non_contained_inclusive(borrow_region, bb, lo, hi) + { + let kill_location = Location { block: bb, statement_index: kill_stmt }; // If region does not contain a point at the location, then add to list and skip // successor locations. - if !self.regioncx.region_contains(borrow_region, location) { - debug!("borrow {:?} gets killed at {:?}", borrow_index, location); - self.borrows_out_of_scope_at_location - .entry(location) - .or_default() - .push(borrow_index); - finished_early = true; - break; - } + debug!("borrow {:?} gets killed at {:?}", borrow_index, kill_location); + self.borrows_out_of_scope_at_location + .entry(kill_location) + .or_default() + .push(borrow_index); + continue 'preorder; } - if !finished_early { - // Add successor BBs to the work list, if necessary. - let bb_data = &self.body[bb]; - debug_assert!(hi == bb_data.statements.len()); - for succ_bb in bb_data.terminator().successors() { - if !self.visited.insert(succ_bb) { - if succ_bb == location.block && first_lo > 0 { - // `succ_bb` has been seen before. If it wasn't - // fully processed, add its first part to `stack` - // for processing. - self.visit_stack.push(StackEntry { - bb: succ_bb, - lo: 0, - hi: first_lo - 1, - }); - - // And update this entry with 0, to represent the - // whole BB being processed. - first_lo = 0; - } - } else { - // succ_bb hasn't been seen before. Add it to - // `stack` for processing. - self.visit_stack.push(StackEntry { - bb: succ_bb, - lo: 0, - hi: self.body[succ_bb].statements.len(), - }); + // If we process the first part of the first basic block (i.e. we encounter that block + // for the second time), we no longer have to visit its successors again. + if bb == first_block && hi != first_hi { + continue; + } + + // Add successor BBs to the work list, if necessary. + let bb_data = &self.body[bb]; + debug_assert!(hi == bb_data.statements.len()); + for succ_bb in bb_data.terminator().successors() { + if !self.visited.insert(succ_bb) { + if succ_bb == first_block && first_lo > 0 { + // `succ_bb` has been seen before. If it wasn't + // fully processed, add its first part to `stack` + // for processing. + self.visit_stack.push(StackEntry { bb: succ_bb, lo: 0, hi: first_lo - 1 }); + + // And update this entry with 0, to represent the + // whole BB being processed. + first_lo = 0; } + } else { + // succ_bb hasn't been seen before. Add it to + // `stack` for processing. + self.visit_stack.push(StackEntry { + bb: succ_bb, + lo: 0, + hi: self.body[succ_bb].statements.len(), + }); } } } @@ -231,27 +228,32 @@ impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> { } } +pub fn calculate_borrows_out_of_scope_at_location<'tcx>( + body: &Body<'tcx>, + regioncx: &RegionInferenceContext<'tcx>, + borrow_set: &BorrowSet<'tcx>, +) -> FxIndexMap<Location, Vec<BorrowIndex>> { + let mut prec = OutOfScopePrecomputer::new(body, regioncx); + for (borrow_index, borrow_data) in borrow_set.iter_enumerated() { + let borrow_region = borrow_data.region; + let location = borrow_data.reserve_location; + + prec.precompute_borrows_out_of_scope(borrow_index, borrow_region, location); + } + + prec.borrows_out_of_scope_at_location +} + impl<'a, 'tcx> Borrows<'a, 'tcx> { - pub(crate) fn new( + pub fn new( tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, nonlexical_regioncx: &'a RegionInferenceContext<'tcx>, borrow_set: &'a BorrowSet<'tcx>, ) -> Self { - let mut prec = OutOfScopePrecomputer::new(body, nonlexical_regioncx); - for (borrow_index, borrow_data) in borrow_set.iter_enumerated() { - let borrow_region = borrow_data.region; - let location = borrow_data.reserve_location; - - prec.precompute_borrows_out_of_scope(borrow_index, borrow_region, location); - } - - Borrows { - tcx, - body, - borrow_set, - borrows_out_of_scope_at_location: prec.borrows_out_of_scope_at_location, - } + let borrows_out_of_scope_at_location = + calculate_borrows_out_of_scope_at_location(body, nonlexical_regioncx, borrow_set); + Borrows { tcx, body, borrow_set, borrows_out_of_scope_at_location } } pub fn location(&self, idx: BorrowIndex) -> &Location { diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index 84f75caa692..f41795d60a0 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -128,7 +128,7 @@ impl<'tcx> ToUniverseInfo<'tcx> } } -impl<'tcx, F, G> ToUniverseInfo<'tcx> for Canonical<'tcx, type_op::custom::CustomTypeOp<F, G>> { +impl<'tcx, F> ToUniverseInfo<'tcx> for Canonical<'tcx, type_op::custom::CustomTypeOp<F>> { fn to_universe_info(self, _base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> { // We can't rerun custom type ops. UniverseInfo::other() diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index d0cb1126f38..1d430a93a87 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -118,7 +118,7 @@ impl<'tcx> BorrowExplanation<'tcx> { let path_span = path_span.unwrap(); // path_span is only present in the case of closure capture assert!(matches!(later_use_kind, LaterUseKind::ClosureCapture)); - if !borrow_span.map_or(false, |sp| sp.overlaps(var_or_use_span)) { + if !borrow_span.is_some_and(|sp| sp.overlaps(var_or_use_span)) { let path_label = "used here by closure"; let capture_kind_label = message; err.span_label( @@ -224,12 +224,9 @@ impl<'tcx> BorrowExplanation<'tcx> { if info.tail_result_is_ignored { // #85581: If the first mutable borrow's scope contains // the second borrow, this suggestion isn't helpful. - if !multiple_borrow_span - .map(|(old, new)| { - old.to(info.span.shrink_to_hi()).contains(new) - }) - .unwrap_or(false) - { + if !multiple_borrow_span.is_some_and(|(old, new)| { + old.to(info.span.shrink_to_hi()).contains(new) + }) { err.span_suggestion_verbose( info.span.shrink_to_hi(), "consider adding semicolon after the expression so its \ diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 7fc8eb161d2..20370e4c6ac 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -1156,7 +1156,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ty::Adt(def, ..) => Some(def.did()), _ => None, }); - let is_option_or_result = parent_self_ty.map_or(false, |def_id| { + let is_option_or_result = parent_self_ty.is_some_and(|def_id| { matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result)) }); if is_option_or_result && maybe_reinitialized_locations_is_empty { diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 6286033e067..4bde372c847 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -289,8 +289,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { .body .local_decls .get(local) - .map(|l| mut_borrow_of_mutable_ref(l, self.local_names[local])) - .unwrap_or(false) => + .is_some_and(|l| mut_borrow_of_mutable_ref(l, self.local_names[local])) => { let decl = &self.body.local_decls[local]; err.span_label(span, format!("cannot {act}")); @@ -443,7 +442,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { .sess .source_map() .span_to_snippet(span) - .map_or(false, |snippet| snippet.starts_with("&mut ")) => + .is_ok_and(|snippet| snippet.starts_with("&mut ")) => { err.span_label(span, format!("cannot {act}")); err.span_suggestion( diff --git a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs index ffba6058186..b6eb9ae980e 100644 --- a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs +++ b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs @@ -125,8 +125,7 @@ impl OutlivesSuggestionBuilder { |(r, _)| { self.constraints_to_add .get(r) - .map(|r_outlived| r_outlived.as_slice().contains(fr)) - .unwrap_or(false) + .is_some_and(|r_outlived| r_outlived.as_slice().contains(fr)) }, ); diff --git a/compiler/rustc_borrowck/src/invalidation.rs b/compiler/rustc_borrowck/src/invalidation.rs index 863c92acdf4..036391d074d 100644 --- a/compiler/rustc_borrowck/src/invalidation.rs +++ b/compiler/rustc_borrowck/src/invalidation.rs @@ -46,7 +46,7 @@ struct InvalidationGenerator<'cx, 'tcx> { all_facts: &'cx mut AllFacts, location_table: &'cx LocationTable, body: &'cx Body<'tcx>, - dominators: Dominators<BasicBlock>, + dominators: &'cx Dominators<BasicBlock>, borrow_set: &'cx BorrowSet<'tcx>, } diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index eb25d454339..9277a262f97 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -26,7 +26,7 @@ use rustc_hir::def_id::LocalDefId; use rustc_index::bit_set::ChunkedBitSet; use rustc_index::{IndexSlice, IndexVec}; use rustc_infer::infer::{ - DefiningAnchor, InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, TyCtxtInferExt, + InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, TyCtxtInferExt, }; use rustc_middle::mir::{ traversal, Body, ClearCrossCrate, Local, Location, Mutability, NonDivergingIntrinsic, Operand, @@ -36,6 +36,7 @@ use rustc_middle::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind}; use rustc_middle::mir::{ProjectionElem, Promoted, Rvalue, Statement, StatementKind}; use rustc_middle::query::Providers; +use rustc_middle::traits::DefiningAnchor; use rustc_middle::ty::{self, CapturedPlace, ParamEnv, RegionVid, TyCtxt}; use rustc_session::lint::builtin::UNUSED_MUT; use rustc_span::{Span, Symbol}; @@ -43,7 +44,6 @@ use rustc_target::abi::FieldIdx; use either::Either; use smallvec::SmallVec; -use std::cell::OnceCell; use std::cell::RefCell; use std::collections::BTreeMap; use std::ops::Deref; @@ -62,7 +62,7 @@ use crate::session_diagnostics::VarNeedNotMut; use self::diagnostics::{AccessKind, RegionName}; use self::location::LocationTable; use self::prefixes::PrefixSet; -use facts::AllFacts; +use consumers::{BodyWithBorrowckFacts, ConsumerOptions}; use self::path_utils::*; @@ -144,7 +144,7 @@ fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> &BorrowCheckResult<'_> { tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bind(hir_owner.def_id)).build(); let input_body: &Body<'_> = &input_body.borrow(); let promoted: &IndexSlice<_, _> = &promoted.borrow(); - let opt_closure_req = do_mir_borrowck(&infcx, input_body, promoted, false).0; + let opt_closure_req = do_mir_borrowck(&infcx, input_body, promoted, None).0; debug!("mir_borrowck done"); tcx.arena.alloc(opt_closure_req) @@ -152,15 +152,15 @@ fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> &BorrowCheckResult<'_> { /// Perform the actual borrow checking. /// -/// If `return_body_with_facts` is true, then return the body with non-erased -/// region ids on which the borrow checking was performed together with Polonius -/// facts. +/// Use `consumer_options: None` for the default behavior of returning +/// [`BorrowCheckResult`] only. Otherwise, return [`BodyWithBorrowckFacts`] according +/// to the given [`ConsumerOptions`]. #[instrument(skip(infcx, input_body, input_promoted), fields(id=?input_body.source.def_id()), level = "debug")] fn do_mir_borrowck<'tcx>( infcx: &InferCtxt<'tcx>, input_body: &Body<'tcx>, input_promoted: &IndexSlice<Promoted, Body<'tcx>>, - return_body_with_facts: bool, + consumer_options: Option<ConsumerOptions>, ) -> (BorrowCheckResult<'tcx>, Option<Box<BodyWithBorrowckFacts<'tcx>>>) { let def = input_body.source.def_id().expect_local(); debug!(?def); @@ -241,8 +241,6 @@ fn do_mir_borrowck<'tcx>( let borrow_set = Rc::new(BorrowSet::build(tcx, body, locals_are_invalidated_at_exit, &mdpe.move_data)); - let use_polonius = return_body_with_facts || infcx.tcx.sess.opts.unstable_opts.polonius; - // Compute non-lexical lifetimes. let nll::NllOutput { regioncx, @@ -262,7 +260,7 @@ fn do_mir_borrowck<'tcx>( &mdpe.move_data, &borrow_set, &upvars, - use_polonius, + consumer_options, ); // Dump MIR results into a file, if that is enabled. This let us @@ -331,7 +329,6 @@ fn do_mir_borrowck<'tcx>( used_mut: Default::default(), used_mut_upvars: SmallVec::new(), borrow_set: Rc::clone(&borrow_set), - dominators: Default::default(), upvars: Vec::new(), local_names: IndexVec::from_elem(None, &promoted_body.local_decls), region_names: RefCell::default(), @@ -360,7 +357,6 @@ fn do_mir_borrowck<'tcx>( used_mut: Default::default(), used_mut_upvars: SmallVec::new(), borrow_set: Rc::clone(&borrow_set), - dominators: Default::default(), upvars, local_names, region_names: RefCell::default(), @@ -444,13 +440,16 @@ fn do_mir_borrowck<'tcx>( tainted_by_errors, }; - let body_with_facts = if return_body_with_facts { - let output_facts = mbcx.polonius_output.expect("Polonius output was not computed"); + let body_with_facts = if consumer_options.is_some() { + let output_facts = mbcx.polonius_output; Some(Box::new(BodyWithBorrowckFacts { body: body_owned, - input_facts: *polonius_input.expect("Polonius input facts were not generated"), + promoted, + borrow_set, + region_inference_context: regioncx, + location_table: polonius_input.as_ref().map(|_| location_table_owned), + input_facts: polonius_input, output_facts, - location_table: location_table_owned, })) } else { None @@ -461,22 +460,6 @@ fn do_mir_borrowck<'tcx>( (result, body_with_facts) } -/// A `Body` with information computed by the borrow checker. This struct is -/// intended to be consumed by compiler consumers. -/// -/// We need to include the MIR body here because the region identifiers must -/// match the ones in the Polonius facts. -pub struct BodyWithBorrowckFacts<'tcx> { - /// A mir body that contains region identifiers. - pub body: Body<'tcx>, - /// Polonius input facts. - pub input_facts: AllFacts, - /// Polonius output facts. - pub output_facts: Rc<self::nll::PoloniusOutput>, - /// The table that maps Polonius points to locations in the table. - pub location_table: LocationTable, -} - pub struct BorrowckInferCtxt<'cx, 'tcx> { pub(crate) infcx: &'cx InferCtxt<'tcx>, pub(crate) reg_var_to_origin: RefCell<FxIndexMap<ty::RegionVid, RegionCtxt>>, @@ -591,9 +574,6 @@ struct MirBorrowckCtxt<'cx, 'tcx> { /// The set of borrows extracted from the MIR borrow_set: Rc<BorrowSet<'tcx>>, - /// Dominators for MIR - dominators: OnceCell<Dominators<BasicBlock>>, - /// Information about upvars not necessarily preserved in types or MIR upvars: Vec<Upvar<'tcx>>, @@ -2269,7 +2249,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } fn dominators(&self) -> &Dominators<BasicBlock> { - self.dominators.get_or_init(|| self.body.basic_blocks.dominators()) + // `BasicBlocks` computes dominators on-demand and caches them. + self.body.basic_blocks.dominators() } } diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index b6ccf924a5c..889acb3acbe 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -27,6 +27,7 @@ use rustc_mir_dataflow::ResultsCursor; use crate::{ borrow_set::BorrowSet, constraint_generation, + consumers::ConsumerOptions, diagnostics::RegionErrors, facts::{AllFacts, AllFactsExt, RustcFacts}, invalidation, @@ -165,10 +166,14 @@ pub(crate) fn compute_regions<'cx, 'tcx>( move_data: &MoveData<'tcx>, borrow_set: &BorrowSet<'tcx>, upvars: &[Upvar<'tcx>], - use_polonius: bool, + consumer_options: Option<ConsumerOptions>, ) -> NllOutput<'tcx> { + let polonius_input = consumer_options.map(|c| c.polonius_input()).unwrap_or_default() + || infcx.tcx.sess.opts.unstable_opts.polonius; + let polonius_output = consumer_options.map(|c| c.polonius_output()).unwrap_or_default() + || infcx.tcx.sess.opts.unstable_opts.polonius; let mut all_facts = - (use_polonius || AllFacts::enabled(infcx.tcx)).then_some(AllFacts::default()); + (polonius_input || AllFacts::enabled(infcx.tcx)).then_some(AllFacts::default()); let universal_regions = Rc::new(universal_regions); @@ -189,7 +194,7 @@ pub(crate) fn compute_regions<'cx, 'tcx>( move_data, elements, upvars, - use_polonius, + polonius_input, ); if let Some(all_facts) = &mut all_facts { @@ -284,7 +289,7 @@ pub(crate) fn compute_regions<'cx, 'tcx>( all_facts.write_to_dir(dir_path, location_table).unwrap(); } - if use_polonius { + if polonius_output { let algorithm = env::var("POLONIUS_ALGORITHM").unwrap_or_else(|_| String::from("Hybrid")); let algorithm = Algorithm::from_str(&algorithm).unwrap(); diff --git a/compiler/rustc_borrowck/src/place_ext.rs b/compiler/rustc_borrowck/src/place_ext.rs index 85d207b2fc9..d521d0db213 100644 --- a/compiler/rustc_borrowck/src/place_ext.rs +++ b/compiler/rustc_borrowck/src/place_ext.rs @@ -7,7 +7,7 @@ use rustc_middle::mir::{Body, Mutability, Place}; use rustc_middle::ty::{self, TyCtxt}; /// Extension methods for the `Place` type. -pub(crate) trait PlaceExt<'tcx> { +pub trait PlaceExt<'tcx> { /// Returns `true` if we can safely ignore borrows of this place. /// This is true whenever there is no action that the user can do /// to the place `self` that would invalidate the borrow. This is true diff --git a/compiler/rustc_borrowck/src/places_conflict.rs b/compiler/rustc_borrowck/src/places_conflict.rs index 918fb2d6923..25c485b814f 100644 --- a/compiler/rustc_borrowck/src/places_conflict.rs +++ b/compiler/rustc_borrowck/src/places_conflict.rs @@ -16,7 +16,7 @@ use std::iter; /// being run in the calling context, the conservative choice is to assume the compared indices /// are disjoint (and therefore, do not overlap). #[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub(crate) enum PlaceConflictBias { +pub enum PlaceConflictBias { Overlap, NoOverlap, } @@ -24,7 +24,7 @@ pub(crate) enum PlaceConflictBias { /// Helper function for checking if places conflict with a mutable borrow and deep access depth. /// This is used to check for places conflicting outside of the borrow checking code (such as in /// dataflow). -pub(crate) fn places_conflict<'tcx>( +pub fn places_conflict<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, borrow_place: Place<'tcx>, diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 8fbe814c856..50b246b1478 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -12,7 +12,7 @@ use rustc_infer::infer::outlives::test_type_match; use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound, VerifyIfEq}; use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin}; use rustc_middle::mir::{ - Body, ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureOutlivesSubjectTy, + BasicBlock, Body, ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureOutlivesSubjectTy, ClosureRegionRequirements, ConstraintCategory, Local, Location, ReturnConstraint, TerminatorKind, }; @@ -585,6 +585,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.universal_regions.to_region_vid(r) } + /// Returns an iterator over all the outlives constraints. + pub fn outlives_constraints(&self) -> impl Iterator<Item = OutlivesConstraint<'tcx>> + '_ { + self.constraints.outlives().iter().copied() + } + /// Adds annotations for `#[rustc_regions]`; see `UniversalRegions::annotate`. pub(crate) fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut Diagnostic) { self.universal_regions.annotate(tcx, err) @@ -598,6 +603,20 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.scc_values.contains(scc, p) } + /// Returns the lowest statement index in `start..=end` which is not contained by `r`. + /// + /// Panics if called before `solve()` executes. + pub(crate) fn first_non_contained_inclusive( + &self, + r: RegionVid, + block: BasicBlock, + start: usize, + end: usize, + ) -> Option<usize> { + let scc = self.constraint_sccs.scc(r); + self.scc_values.first_non_contained_inclusive(scc, block, start, end) + } + /// Returns access to the value of `r` for debugging purposes. pub(crate) fn region_value_str(&self, r: RegionVid) -> String { let scc = self.constraint_sccs.scc(r); @@ -698,7 +717,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { #[instrument(skip(self, _body), level = "debug")] fn propagate_constraints(&mut self, _body: &Body<'tcx>) { debug!("constraints={:#?}", { - let mut constraints: Vec<_> = self.constraints.outlives().iter().collect(); + let mut constraints: Vec<_> = self.outlives_constraints().collect(); constraints.sort_by_key(|c| (c.sup, c.sub)); constraints .into_iter() diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 309f23d9226..7fc89e89a35 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -2,9 +2,10 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::LocalDefId; use rustc_hir::OpaqueTyOrigin; +use rustc_infer::infer::InferCtxt; use rustc_infer::infer::TyCtxtInferExt as _; -use rustc_infer::infer::{DefiningAnchor, InferCtxt}; use rustc_infer::traits::{Obligation, ObligationCause}; +use rustc_middle::traits::DefiningAnchor; use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts}; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable}; @@ -152,8 +153,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { let guar = ty.error_reported().err().unwrap_or_else(|| { prev.report_mismatch( &OpaqueHiddenType { ty, span: concrete_type.span }, + opaque_type_key.def_id, infcx.tcx, ) + .emit() }); prev.ty = infcx.tcx.ty_error(guar); } diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index 193e2060115..9290e747914 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -159,7 +159,7 @@ impl<N: Idx> LivenessValues<N> { /// Returns `true` if the region `r` contains the given element. pub(crate) fn contains(&self, row: N, location: Location) -> bool { let index = self.elements.point_from_location(location); - self.points.row(row).map_or(false, |r| r.contains(index)) + self.points.row(row).is_some_and(|r| r.contains(index)) } /// Returns an iterator of all the elements contained by the region `r` @@ -283,6 +283,22 @@ impl<N: Idx> RegionValues<N> { elem.contained_in_row(self, r) } + /// Returns the lowest statement index in `start..=end` which is not contained by `r`. + pub(crate) fn first_non_contained_inclusive( + &self, + r: N, + block: BasicBlock, + start: usize, + end: usize, + ) -> Option<usize> { + let row = self.points.row(r)?; + let block = self.elements.entry_point(block); + let start = block.plus(start); + let end = block.plus(end); + let first_unset = row.first_unset_in(start..=end)?; + Some(first_unset.index() - block.index()) + } + /// `self[to] |= values[from]`, essentially: that is, take all the /// elements for the region `from` from `values` and add them to /// the region `to` in `self`. diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index b27d5d20532..95dcc8d4b19 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -1,13 +1,13 @@ use std::fmt; -use rustc_infer::infer::{canonical::Canonical, InferOk}; +use rustc_infer::infer::canonical::Canonical; use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable}; use rustc_span::def_id::DefId; use rustc_span::Span; use rustc_trait_selection::traits::query::type_op::{self, TypeOpOutput}; use rustc_trait_selection::traits::query::{Fallible, NoSolution}; -use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt}; +use rustc_trait_selection::traits::ObligationCause; use crate::diagnostics::{ToUniverseInfo, UniverseInfo}; @@ -219,20 +219,17 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let cause = ObligationCause::dummy_with_span(span); let param_env = self.param_env; - let op = |infcx: &'_ _| { - let ocx = ObligationCtxt::new_in_snapshot(infcx); - let user_ty = ocx.normalize(&cause, param_env, user_ty); - ocx.eq(&cause, param_env, user_ty, mir_ty)?; - if !ocx.select_all_or_error().is_empty() { - return Err(NoSolution); - } - Ok(InferOk { value: (), obligations: vec![] }) - }; - self.fully_perform_op( Locations::All(span), ConstraintCategory::Boring, - type_op::custom::CustomTypeOp::new(op, || "ascribe_user_type_skip_wf".to_string()), + type_op::custom::CustomTypeOp::new( + |ocx| { + let user_ty = ocx.normalize(&cause, param_env, user_ty); + ocx.eq(&cause, param_env, user_ty, mir_ty)?; + Ok(()) + }, + "ascribe_user_type_skip_wf", + ), ) .unwrap_or_else(|err| { span_mirbug!( diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index fa4bc926f27..cf204cff6b3 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -20,7 +20,7 @@ use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::region_constraints::RegionConstraintData; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{ - InferCtxt, InferOk, LateBoundRegion, LateBoundRegionConversionTime, NllRegionVariableOrigin, + InferCtxt, LateBoundRegion, LateBoundRegionConversionTime, NllRegionVariableOrigin, }; use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor}; @@ -48,6 +48,7 @@ use rustc_mir_dataflow::impls::MaybeInitializedPlaces; use rustc_mir_dataflow::move_paths::MoveData; use rustc_mir_dataflow::ResultsCursor; +use crate::renumber::RegionCtxt; use crate::session_diagnostics::MoveUnsized; use crate::{ borrow_set::BorrowSet, @@ -183,17 +184,19 @@ pub(crate) fn type_check<'mir, 'tcx>( &mut borrowck_context, ); - let errors_reported = { - let mut verifier = TypeVerifier::new(&mut checker, promoted); - verifier.visit_body(&body); - verifier.errors_reported - }; - - if !errors_reported { - // if verifier failed, don't do further checks to avoid ICEs - checker.typeck_mir(body); + // FIXME(-Ztrait-solver=next): A bit dubious that we're only registering + // predefined opaques in the typeck root. + // FIXME(-Ztrait-solver=next): This is also totally wrong for TAITs, since + // the HIR typeck map defining usages back to their definition params, + // they won't actually match up with the usages in this body... + if infcx.tcx.trait_solver_next() && !infcx.tcx.is_typeck_child(body.source.def_id()) { + checker.register_predefined_opaques_in_new_solver(); } + let mut verifier = TypeVerifier::new(&mut checker, promoted); + verifier.visit_body(&body); + + checker.typeck_mir(body); checker.equate_inputs_and_outputs(&body, universal_regions, &normalized_inputs_and_output); checker.check_signature_annotation(&body); @@ -218,16 +221,16 @@ pub(crate) fn type_check<'mir, 'tcx>( Locations::All(body.span), ConstraintCategory::OpaqueType, CustomTypeOp::new( - |infcx| { - infcx.register_member_constraints( + |ocx| { + ocx.infcx.register_member_constraints( param_env, opaque_type_key, decl.hidden_type.ty, decl.hidden_type.span, ); - Ok(InferOk { value: (), obligations: vec![] }) + Ok(()) }, - || "opaque_type_map".to_string(), + "opaque_type_map", ), ) .unwrap(); @@ -294,7 +297,6 @@ struct TypeVerifier<'a, 'b, 'tcx> { cx: &'a mut TypeChecker<'b, 'tcx>, promoted: &'b IndexSlice<Promoted, Body<'tcx>>, last_span: Span, - errors_reported: bool, } impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { @@ -383,13 +385,11 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { }; }; - if !self.errors_reported { - let promoted_body = &self.promoted[promoted]; - self.sanitize_promoted(promoted_body, location); + let promoted_body = &self.promoted[promoted]; + self.sanitize_promoted(promoted_body, location); - let promoted_ty = promoted_body.return_ty(); - check_err(self, promoted_body, ty, promoted_ty); - } + let promoted_ty = promoted_body.return_ty(); + check_err(self, promoted_body, ty, promoted_ty); } else { self.cx.ascribe_user_type( constant.literal.ty(), @@ -483,9 +483,6 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { for local_decl in &body.local_decls { self.sanitize_type(local_decl, local_decl.ty); } - if self.errors_reported { - return; - } self.super_body(body); } } @@ -495,7 +492,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { cx: &'a mut TypeChecker<'b, 'tcx>, promoted: &'b IndexSlice<Promoted, Body<'tcx>>, ) -> Self { - TypeVerifier { promoted, last_span: cx.body.span, cx, errors_reported: false } + TypeVerifier { promoted, last_span: cx.body.span, cx } } fn body(&self) -> &Body<'tcx> { @@ -529,7 +526,6 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { for elem in place.projection.iter() { if place_ty.variant_index.is_none() { if let Err(guar) = place_ty.ty.error_reported() { - assert!(self.errors_reported); return PlaceTy::from_ty(self.tcx().ty_error(guar)); } } @@ -593,10 +589,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { self.visit_body(&promoted_body); - if !self.errors_reported { - // if verifier failed, don't do further checks to avoid ICEs - self.cx.typeck_mir(promoted_body); - } + self.cx.typeck_mir(promoted_body); self.cx.body = parent_body; // Merge the outlives constraints back in, at the given location. @@ -762,7 +755,6 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { } fn error(&mut self) -> Ty<'tcx> { - self.errors_reported = true; self.tcx().ty_error_misc() } @@ -1041,6 +1033,57 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { checker } + pub(super) fn register_predefined_opaques_in_new_solver(&mut self) { + // OK to use the identity substitutions for each opaque type key, since + // we remap opaques from HIR typeck back to their definition params. + let opaques: Vec<_> = self + .infcx + .tcx + .typeck(self.body.source.def_id().expect_local()) + .concrete_opaque_types + .iter() + .map(|(&def_id, &hidden_ty)| { + let substs = ty::InternalSubsts::identity_for_item(self.infcx.tcx, def_id); + (ty::OpaqueTypeKey { def_id, substs }, hidden_ty) + }) + .collect(); + + let renumbered_opaques = self.infcx.tcx.fold_regions(opaques, |_, _| { + self.infcx.next_nll_region_var( + NllRegionVariableOrigin::Existential { from_forall: false }, + || RegionCtxt::Unknown, + ) + }); + + let param_env = self.param_env; + let result = self.fully_perform_op( + Locations::All(self.body.span), + ConstraintCategory::OpaqueType, + CustomTypeOp::new( + |ocx| { + for (key, hidden_ty) in renumbered_opaques { + ocx.register_infer_ok_obligations( + ocx.infcx.register_hidden_type_in_new_solver( + key, + param_env, + hidden_ty.ty, + )?, + ); + } + Ok(()) + }, + "register pre-defined opaques", + ), + ); + + if result.is_err() { + self.infcx.tcx.sess.delay_span_bug( + self.body.span, + "failed re-defining predefined opaques in mir typeck", + ); + } + } + fn body(&self) -> &Body<'tcx> { self.body } @@ -2713,8 +2756,9 @@ impl<'tcx> TypeOp<'tcx> for InstantiateOpaqueType<'tcx> { type ErrorInfo = InstantiateOpaqueType<'tcx>; fn fully_perform(mut self, infcx: &InferCtxt<'tcx>) -> Fallible<TypeOpOutput<'tcx, Self>> { - let (mut output, region_constraints) = scrape_region_constraints(infcx, || { - Ok(InferOk { value: (), obligations: self.obligations.clone() }) + let (mut output, region_constraints) = scrape_region_constraints(infcx, |ocx| { + ocx.register_obligations(self.obligations.clone()); + Ok(()) })?; self.region_constraints = Some(region_constraints); output.error_info = Some(self); diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs index 7158c62b548..dd1f89e5b91 100644 --- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs +++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs @@ -185,17 +185,25 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx> } fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { - self.type_checker - .fully_perform_op( - self.locations, - self.category, - InstantiateOpaqueType { - obligations, - // These fields are filled in during execution of the operation - base_universe: None, - region_constraints: None, - }, - ) - .unwrap(); + match self.type_checker.fully_perform_op( + self.locations, + self.category, + InstantiateOpaqueType { + obligations, + // These fields are filled in during execution of the operation + base_universe: None, + region_constraints: None, + }, + ) { + Ok(()) => {} + Err(_) => { + // It's a bit redundant to delay a bug here, but I'd rather + // delay more bugs than accidentally not delay a bug at all. + self.type_checker.tcx().sess.delay_span_bug( + self.locations.span(self.type_checker.body), + "errors selecting obligation during MIR typeck", + ); + } + }; } } diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index ed91cea4ae2..49401e9ca94 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -119,7 +119,7 @@ impl<'ast> visit::Visitor<'ast> for CfgFinder { self.has_cfg_or_cfg_attr = self.has_cfg_or_cfg_attr || attr .ident() - .map_or(false, |ident| ident.name == sym::cfg || ident.name == sym::cfg_attr); + .is_some_and(|ident| ident.name == sym::cfg || ident.name == sym::cfg_attr); } } diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index 866cc5adbf3..f0d378d12f7 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -1,7 +1,7 @@ use crate::util::check_builtin_macro_attribute; use rustc_ast::expand::allocator::{ - AllocatorKind, AllocatorMethod, AllocatorTy, ALLOCATOR_METHODS, + global_fn_name, AllocatorMethod, AllocatorTy, ALLOCATOR_METHODS, }; use rustc_ast::ptr::P; use rustc_ast::{self as ast, AttrVec, Expr, FnHeader, FnSig, Generics, Param, StmtKind}; @@ -40,8 +40,7 @@ pub fn expand( // Generate a bunch of new items using the AllocFnFactory let span = ecx.with_def_site_ctxt(item.span); - let f = - AllocFnFactory { span, ty_span, kind: AllocatorKind::Global, global: item.ident, cx: ecx }; + let f = AllocFnFactory { span, ty_span, global: item.ident, cx: ecx }; // Generate item statements for the allocator methods. let stmts = ALLOCATOR_METHODS.iter().map(|method| f.allocator_fn(method)).collect(); @@ -63,7 +62,6 @@ pub fn expand( struct AllocFnFactory<'a, 'b> { span: Span, ty_span: Span, - kind: AllocatorKind, global: Ident, cx: &'b ExtCtxt<'a>, } @@ -92,7 +90,7 @@ impl AllocFnFactory<'_, '_> { })); let item = self.cx.item( self.span, - Ident::from_str_and_span(&self.kind.fn_name(method.name), self.span), + Ident::from_str_and_span(&global_fn_name(method.name), self.span), self.attrs(), kind, ); diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 73a3e3353f3..84e09cf0abe 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -432,11 +432,9 @@ pub(crate) fn codegen_terminator_call<'tcx>( let is_cold = if fn_sig.abi() == Abi::RustCold { true } else { - instance - .map(|inst| { - fx.tcx.codegen_fn_attrs(inst.def_id()).flags.contains(CodegenFnAttrFlags::COLD) - }) - .unwrap_or(false) + instance.is_some_and(|inst| { + fx.tcx.codegen_fn_attrs(inst.def_id()).flags.contains(CodegenFnAttrFlags::COLD) + }) }; if is_cold { fx.bcx.set_cold_block(fx.bcx.current_block().unwrap()); @@ -470,7 +468,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( }; // Pass the caller location for `#[track_caller]`. - if instance.map(|inst| inst.def.requires_caller_location(fx.tcx)).unwrap_or(false) { + if instance.is_some_and(|inst| inst.def.requires_caller_location(fx.tcx)) { let caller_location = fx.get_caller_location(source_info); args.push(CallArgument { value: caller_location, is_owned: false }); } diff --git a/compiler/rustc_codegen_cranelift/src/allocator.rs b/compiler/rustc_codegen_cranelift/src/allocator.rs index 2c246ceb37d..d4b1ae2b613 100644 --- a/compiler/rustc_codegen_cranelift/src/allocator.rs +++ b/compiler/rustc_codegen_cranelift/src/allocator.rs @@ -3,10 +3,12 @@ use crate::prelude::*; -use rustc_ast::expand::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS}; +use rustc_ast::expand::allocator::{ + alloc_error_handler_name, default_fn_name, global_fn_name, AllocatorKind, AllocatorTy, + ALLOCATOR_METHODS, NO_ALLOC_SHIM_IS_UNSTABLE, +}; use rustc_codegen_ssa::base::allocator_kind_for_codegen; use rustc_session::config::OomStrategy; -use rustc_span::symbol::sym; /// Returns whether an allocator shim was created pub(crate) fn codegen( @@ -34,41 +36,43 @@ fn codegen_inner( ) { let usize_ty = module.target_config().pointer_type(); - for method in ALLOCATOR_METHODS { - let mut arg_tys = Vec::with_capacity(method.inputs.len()); - for ty in method.inputs.iter() { - match *ty { - AllocatorTy::Layout => { - arg_tys.push(usize_ty); // size - arg_tys.push(usize_ty); // align - } - AllocatorTy::Ptr => arg_tys.push(usize_ty), - AllocatorTy::Usize => arg_tys.push(usize_ty), + if kind == AllocatorKind::Default { + for method in ALLOCATOR_METHODS { + let mut arg_tys = Vec::with_capacity(method.inputs.len()); + for ty in method.inputs.iter() { + match *ty { + AllocatorTy::Layout => { + arg_tys.push(usize_ty); // size + arg_tys.push(usize_ty); // align + } + AllocatorTy::Ptr => arg_tys.push(usize_ty), + AllocatorTy::Usize => arg_tys.push(usize_ty), - AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"), + AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"), + } } - } - let output = match method.output { - AllocatorTy::ResultPtr => Some(usize_ty), - AllocatorTy::Unit => None, + let output = match method.output { + AllocatorTy::ResultPtr => Some(usize_ty), + AllocatorTy::Unit => None, - AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => { - panic!("invalid allocator output") - } - }; + AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => { + panic!("invalid allocator output") + } + }; - let sig = Signature { - call_conv: module.target_config().default_call_conv, - params: arg_tys.iter().cloned().map(AbiParam::new).collect(), - returns: output.into_iter().map(AbiParam::new).collect(), - }; - crate::common::create_wrapper_function( - module, - unwind_context, - sig, - &format!("__rust_{}", method.name), - &kind.fn_name(method.name), - ); + let sig = Signature { + call_conv: module.target_config().default_call_conv, + params: arg_tys.iter().cloned().map(AbiParam::new).collect(), + returns: output.into_iter().map(AbiParam::new).collect(), + }; + crate::common::create_wrapper_function( + module, + unwind_context, + sig, + &global_fn_name(method.name), + &default_fn_name(method.name), + ); + } } let sig = Signature { @@ -81,7 +85,7 @@ fn codegen_inner( unwind_context, sig, "__rust_alloc_error_handler", - &alloc_error_handler_kind.fn_name(sym::oom), + &alloc_error_handler_name(alloc_error_handler_kind), ); let data_id = module.declare_data(OomStrategy::SYMBOL, Linkage::Export, false, false).unwrap(); @@ -90,4 +94,11 @@ fn codegen_inner( let val = oom_strategy.should_panic(); data_ctx.define(Box::new([val])); module.define_data(data_id, &data_ctx).unwrap(); + + let data_id = + module.declare_data(NO_ALLOC_SHIM_IS_UNSTABLE, Linkage::Export, false, false).unwrap(); + let mut data_ctx = DataContext::new(); + data_ctx.set_align(1); + data_ctx.define(Box::new([0])); + module.define_data(data_id, &data_ctx).unwrap(); } diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 25fd5ca3ae8..9c6a0fae327 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -630,11 +630,11 @@ fn codegen_stmt<'tcx>( let to_ty = fx.monomorphize(to_ty); fn is_fat_ptr<'tcx>(fx: &FunctionCx<'_, '_, 'tcx>, ty: Ty<'tcx>) -> bool { - ty.builtin_deref(true) - .map(|ty::TypeAndMut { ty: pointee_ty, mutbl: _ }| { + ty.builtin_deref(true).is_some_and( + |ty::TypeAndMut { ty: pointee_ty, mutbl: _ }| { has_ptr_meta(fx.tcx, pointee_ty) - }) - .unwrap_or(false) + }, + ) } if is_fat_ptr(fx, from_ty) { diff --git a/compiler/rustc_codegen_gcc/src/allocator.rs b/compiler/rustc_codegen_gcc/src/allocator.rs index 4bad33ee879..13f88192bbc 100644 --- a/compiler/rustc_codegen_gcc/src/allocator.rs +++ b/compiler/rustc_codegen_gcc/src/allocator.rs @@ -1,11 +1,13 @@ #[cfg(feature="master")] use gccjit::FnAttribute; use gccjit::{FunctionType, GlobalKind, ToRValue}; -use rustc_ast::expand::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS}; +use rustc_ast::expand::allocator::{ + alloc_error_handler_name, default_fn_name, global_fn_name, AllocatorKind, AllocatorTy, + ALLOCATOR_METHODS, NO_ALLOC_SHIM_IS_UNSTABLE, +}; use rustc_middle::bug; use rustc_middle::ty::TyCtxt; use rustc_session::config::OomStrategy; -use rustc_span::symbol::sym; use crate::GccContext; @@ -22,69 +24,71 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut GccContext, _module_nam let i8p = i8.make_pointer(); let void = context.new_type::<()>(); - for method in ALLOCATOR_METHODS { - let mut types = Vec::with_capacity(method.inputs.len()); - for ty in method.inputs.iter() { - match *ty { - AllocatorTy::Layout => { - types.push(usize); - types.push(usize); + if kind == AllocatorKind::Default { + for method in ALLOCATOR_METHODS { + let mut types = Vec::with_capacity(method.inputs.len()); + for ty in method.inputs.iter() { + match *ty { + AllocatorTy::Layout => { + types.push(usize); + types.push(usize); + } + AllocatorTy::Ptr => types.push(i8p), + AllocatorTy::Usize => types.push(usize), + + AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"), } - AllocatorTy::Ptr => types.push(i8p), - AllocatorTy::Usize => types.push(usize), - - AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"), } - } - let output = match method.output { - AllocatorTy::ResultPtr => Some(i8p), - AllocatorTy::Unit => None, + let output = match method.output { + AllocatorTy::ResultPtr => Some(i8p), + AllocatorTy::Unit => None, - AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => { - panic!("invalid allocator output") - } - }; - let name = format!("__rust_{}", method.name); + AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => { + panic!("invalid allocator output") + } + }; + let name = global_fn_name(method.name); - let args: Vec<_> = types.iter().enumerate() - .map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index))) - .collect(); - let func = context.new_function(None, FunctionType::Exported, output.unwrap_or(void), &args, name, false); + let args: Vec<_> = types.iter().enumerate() + .map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index))) + .collect(); + let func = context.new_function(None, FunctionType::Exported, output.unwrap_or(void), &args, name, false); - if tcx.sess.target.options.default_hidden_visibility { + if tcx.sess.target.options.default_hidden_visibility { + #[cfg(feature="master")] + func.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden)); + } + if tcx.sess.must_emit_unwind_tables() { + // TODO(antoyo): emit unwind tables. + } + + let callee = default_fn_name(method.name); + let args: Vec<_> = types.iter().enumerate() + .map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index))) + .collect(); + let callee = context.new_function(None, FunctionType::Extern, output.unwrap_or(void), &args, callee, false); #[cfg(feature="master")] - func.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden)); - } - if tcx.sess.must_emit_unwind_tables() { - // TODO(antoyo): emit unwind tables. - } + callee.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden)); + + let block = func.new_block("entry"); + + let args = args + .iter() + .enumerate() + .map(|(i, _)| func.get_param(i as i32).to_rvalue()) + .collect::<Vec<_>>(); + let ret = context.new_call(None, callee, &args); + //llvm::LLVMSetTailCall(ret, True); + if output.is_some() { + block.end_with_return(None, ret); + } + else { + block.end_with_void_return(None); + } - let callee = kind.fn_name(method.name); - let args: Vec<_> = types.iter().enumerate() - .map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index))) - .collect(); - let callee = context.new_function(None, FunctionType::Extern, output.unwrap_or(void), &args, callee, false); - #[cfg(feature="master")] - callee.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden)); - - let block = func.new_block("entry"); - - let args = args - .iter() - .enumerate() - .map(|(i, _)| func.get_param(i as i32).to_rvalue()) - .collect::<Vec<_>>(); - let ret = context.new_call(None, callee, &args); - //llvm::LLVMSetTailCall(ret, True); - if output.is_some() { - block.end_with_return(None, ret); - } - else { - block.end_with_void_return(None); + // TODO(@Commeownist): Check if we need to emit some extra debugging info in certain circumstances + // as described in https://github.com/rust-lang/rust/commit/77a96ed5646f7c3ee8897693decc4626fe380643 } - - // TODO(@Commeownist): Check if we need to emit some extra debugging info in certain circumstances - // as described in https://github.com/rust-lang/rust/commit/77a96ed5646f7c3ee8897693decc4626fe380643 } let types = [usize, usize]; @@ -99,7 +103,7 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut GccContext, _module_nam func.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden)); } - let callee = alloc_error_handler_kind.fn_name(sym::oom); + let callee = alloc_error_handler_name(alloc_error_handler_kind); let args: Vec<_> = types.iter().enumerate() .map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index))) .collect(); @@ -123,4 +127,9 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut GccContext, _module_nam let value = tcx.sess.opts.unstable_opts.oom.should_panic(); let value = context.new_rvalue_from_int(i8, value as i32); global.global_set_initializer_rvalue(value); + + let name = NO_ALLOC_SHIM_IS_UNSTABLE.to_string(); + let global = context.new_global(None, GlobalKind::Exported, i8, name); + let value = context.new_rvalue_from_int(i8, 0); + global.global_set_initializer_rvalue(value); } diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index 5e750d91b82..ad51f2d0958 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -11,7 +11,7 @@ bitflags = "1.0" cstr = "0.2" libc = "0.2" measureme = "10.0.0" -object = { version = "0.30.1", default-features = false, features = [ +object = { version = "0.31.1", default-features = false, features = [ "std", "read", ] } diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index 668d9292705..a57508815d6 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -1,10 +1,12 @@ use crate::attributes; use libc::c_uint; -use rustc_ast::expand::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS}; +use rustc_ast::expand::allocator::{ + alloc_error_handler_name, default_fn_name, global_fn_name, AllocatorKind, AllocatorTy, + ALLOCATOR_METHODS, NO_ALLOC_SHIM_IS_UNSTABLE, +}; use rustc_middle::bug; use rustc_middle::ty::TyCtxt; use rustc_session::config::{DebugInfo, OomStrategy}; -use rustc_span::symbol::sym; use crate::debuginfo; use crate::llvm::{self, False, True}; @@ -29,75 +31,78 @@ pub(crate) unsafe fn codegen( let i8p = llvm::LLVMPointerType(i8, 0); let void = llvm::LLVMVoidTypeInContext(llcx); - for method in ALLOCATOR_METHODS { - let mut args = Vec::with_capacity(method.inputs.len()); - for ty in method.inputs.iter() { - match *ty { - AllocatorTy::Layout => { - args.push(usize); // size - args.push(usize); // align + if kind == AllocatorKind::Default { + for method in ALLOCATOR_METHODS { + let mut args = Vec::with_capacity(method.inputs.len()); + for ty in method.inputs.iter() { + match *ty { + AllocatorTy::Layout => { + args.push(usize); // size + args.push(usize); // align + } + AllocatorTy::Ptr => args.push(i8p), + AllocatorTy::Usize => args.push(usize), + + AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"), } - AllocatorTy::Ptr => args.push(i8p), - AllocatorTy::Usize => args.push(usize), - - AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"), } - } - let output = match method.output { - AllocatorTy::ResultPtr => Some(i8p), - AllocatorTy::Unit => None, + let output = match method.output { + AllocatorTy::ResultPtr => Some(i8p), + AllocatorTy::Unit => None, - AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => { - panic!("invalid allocator output") + AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => { + panic!("invalid allocator output") + } + }; + let ty = llvm::LLVMFunctionType( + output.unwrap_or(void), + args.as_ptr(), + args.len() as c_uint, + False, + ); + let name = global_fn_name(method.name); + let llfn = + llvm::LLVMRustGetOrInsertFunction(llmod, name.as_ptr().cast(), name.len(), ty); + + if tcx.sess.target.default_hidden_visibility { + llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); + } + if tcx.sess.must_emit_unwind_tables() { + let uwtable = attributes::uwtable_attr(llcx); + attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[uwtable]); } - }; - let ty = llvm::LLVMFunctionType( - output.unwrap_or(void), - args.as_ptr(), - args.len() as c_uint, - False, - ); - let name = format!("__rust_{}", method.name); - let llfn = llvm::LLVMRustGetOrInsertFunction(llmod, name.as_ptr().cast(), name.len(), ty); - - if tcx.sess.target.default_hidden_visibility { - llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); - } - if tcx.sess.must_emit_unwind_tables() { - let uwtable = attributes::uwtable_attr(llcx); - attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[uwtable]); - } - let callee = kind.fn_name(method.name); - let callee = - llvm::LLVMRustGetOrInsertFunction(llmod, callee.as_ptr().cast(), callee.len(), ty); - llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Hidden); - - let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, "entry\0".as_ptr().cast()); - - let llbuilder = llvm::LLVMCreateBuilderInContext(llcx); - llvm::LLVMPositionBuilderAtEnd(llbuilder, llbb); - let args = args - .iter() - .enumerate() - .map(|(i, _)| llvm::LLVMGetParam(llfn, i as c_uint)) - .collect::<Vec<_>>(); - let ret = llvm::LLVMRustBuildCall( - llbuilder, - ty, - callee, - args.as_ptr(), - args.len() as c_uint, - [].as_ptr(), - 0 as c_uint, - ); - llvm::LLVMSetTailCall(ret, True); - if output.is_some() { - llvm::LLVMBuildRet(llbuilder, ret); - } else { - llvm::LLVMBuildRetVoid(llbuilder); + let callee = default_fn_name(method.name); + let callee = + llvm::LLVMRustGetOrInsertFunction(llmod, callee.as_ptr().cast(), callee.len(), ty); + llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Hidden); + + let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, "entry\0".as_ptr().cast()); + + let llbuilder = llvm::LLVMCreateBuilderInContext(llcx); + llvm::LLVMPositionBuilderAtEnd(llbuilder, llbb); + let args = args + .iter() + .enumerate() + .map(|(i, _)| llvm::LLVMGetParam(llfn, i as c_uint)) + .collect::<Vec<_>>(); + let ret = llvm::LLVMRustBuildCall( + llbuilder, + ty, + callee, + args.as_ptr(), + args.len() as c_uint, + [].as_ptr(), + 0 as c_uint, + ); + llvm::LLVMSetTailCall(ret, True); + if output.is_some() { + llvm::LLVMBuildRet(llbuilder, ret); + } else { + llvm::LLVMBuildRetVoid(llbuilder); + } + llvm::LLVMDisposeBuilder(llbuilder); } - llvm::LLVMDisposeBuilder(llbuilder); } // rust alloc error handler @@ -118,7 +123,7 @@ pub(crate) unsafe fn codegen( attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[uwtable]); } - let callee = alloc_error_handler_kind.fn_name(sym::oom); + let callee = alloc_error_handler_name(alloc_error_handler_kind); let callee = llvm::LLVMRustGetOrInsertFunction(llmod, callee.as_ptr().cast(), callee.len(), ty); // -> ! DIFlagNoReturn attributes::apply_to_llfn(callee, llvm::AttributePlace::Function, &[no_return]); @@ -156,6 +161,14 @@ pub(crate) unsafe fn codegen( let llval = llvm::LLVMConstInt(i8, val as u64, False); llvm::LLVMSetInitializer(ll_g, llval); + let name = NO_ALLOC_SHIM_IS_UNSTABLE; + let ll_g = llvm::LLVMRustGetOrInsertGlobal(llmod, name.as_ptr().cast(), name.len(), i8); + if tcx.sess.target.default_hidden_visibility { + llvm::LLVMRustSetVisibility(ll_g, llvm::Visibility::Hidden); + } + let llval = llvm::LLVMConstInt(i8, 0, False); + llvm::LLVMSetInitializer(ll_g, llval); + if tcx.sess.opts.debuginfo != DebugInfo::None { let dbg_cx = debuginfo::CodegenUnitDebugContext::new(llmod); debuginfo::metadata::build_compile_unit_di_node(tcx, module_name, &dbg_cx); diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index 240a9d2f371..21a1ac34844 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -163,7 +163,7 @@ impl CoverageMapGenerator { counter_regions.sort_unstable_by_key(|(_counter, region)| *region); for (counter, region) in counter_regions { let CodeRegion { file_name, start_line, start_col, end_line, end_col } = *region; - let same_file = current_file_name.map_or(false, |p| p == file_name); + let same_file = current_file_name.is_some_and(|p| p == file_name); if !same_file { if current_file_name.is_some() { current_file_id += 1; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs index aaf5dbd9930..37f30917609 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs @@ -9,10 +9,9 @@ use rustc_ast::attr; use rustc_codegen_ssa::base::collect_debugger_visualizers_transitive; use rustc_codegen_ssa::traits::*; use rustc_hir::def_id::LOCAL_CRATE; -use rustc_middle::bug; +use rustc_middle::{bug, middle::debugger_visualizer::DebuggerVisualizerType}; use rustc_session::config::{CrateType, DebugInfo}; use rustc_span::symbol::sym; -use rustc_span::DebuggerVisualizerType; /// Inserts a side-effect free instruction sequence that makes sure that the /// .debug_gdb_scripts global is referenced, so it isn't removed by the linker. diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 6a86237d79e..805843e5863 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -10,6 +10,7 @@ #![feature(iter_intersperse)] #![feature(let_chains)] #![feature(never_type)] +#![feature(impl_trait_in_assoc_type)] #![recursion_limit = "256"] #![allow(rustc::potential_query_instability)] #![deny(rustc::untranslatable_diagnostic)] diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 994addf12eb..03be0654b50 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -16,7 +16,6 @@ use rustc_session::config::PrintRequest; use rustc_session::Session; use rustc_span::symbol::Symbol; use rustc_target::spec::{MergeFunctions, PanicStrategy}; -use smallvec::{smallvec, SmallVec}; use std::ffi::{CStr, CString}; use std::path::Path; @@ -132,6 +131,60 @@ pub fn time_trace_profiler_finish(file_name: &Path) { } } +pub enum TargetFeatureFoldStrength<'a> { + // The feature is only tied when enabling the feature, disabling + // this feature shouldn't disable the tied feature. + EnableOnly(&'a str), + // The feature is tied for both enabling and disabling this feature. + Both(&'a str), +} + +impl<'a> TargetFeatureFoldStrength<'a> { + fn as_str(&self) -> &'a str { + match self { + TargetFeatureFoldStrength::EnableOnly(feat) => feat, + TargetFeatureFoldStrength::Both(feat) => feat, + } + } +} + +pub struct LLVMFeature<'a> { + pub llvm_feature_name: &'a str, + pub dependency: Option<TargetFeatureFoldStrength<'a>>, +} + +impl<'a> LLVMFeature<'a> { + pub fn new(llvm_feature_name: &'a str) -> Self { + Self { llvm_feature_name, dependency: None } + } + + pub fn with_dependency( + llvm_feature_name: &'a str, + dependency: TargetFeatureFoldStrength<'a>, + ) -> Self { + Self { llvm_feature_name, dependency: Some(dependency) } + } + + pub fn contains(&self, feat: &str) -> bool { + self.iter().any(|dep| dep == feat) + } + + pub fn iter(&'a self) -> impl Iterator<Item = &'a str> { + let dependencies = self.dependency.iter().map(|feat| feat.as_str()); + std::iter::once(self.llvm_feature_name).chain(dependencies) + } +} + +impl<'a> IntoIterator for LLVMFeature<'a> { + type Item = &'a str; + type IntoIter = impl Iterator<Item = &'a str>; + + fn into_iter(self) -> Self::IntoIter { + let dependencies = self.dependency.into_iter().map(|feat| feat.as_str()); + std::iter::once(self.llvm_feature_name).chain(dependencies) + } +} + // WARNING: the features after applying `to_llvm_features` must be known // to LLVM or the feature detection code will walk past the end of the feature // array, leading to crashes. @@ -147,36 +200,65 @@ pub fn time_trace_profiler_finish(file_name: &Path) { // Though note that Rust can also be build with an external precompiled version of LLVM // which might lead to failures if the oldest tested / supported LLVM version // doesn't yet support the relevant intrinsics -pub fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> SmallVec<[&'a str; 2]> { +pub fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> LLVMFeature<'a> { let arch = if sess.target.arch == "x86_64" { "x86" } else { &*sess.target.arch }; match (arch, s) { - ("x86", "sse4.2") => smallvec!["sse4.2", "crc32"], - ("x86", "pclmulqdq") => smallvec!["pclmul"], - ("x86", "rdrand") => smallvec!["rdrnd"], - ("x86", "bmi1") => smallvec!["bmi"], - ("x86", "cmpxchg16b") => smallvec!["cx16"], - ("aarch64", "rcpc2") => smallvec!["rcpc-immo"], - ("aarch64", "dpb") => smallvec!["ccpp"], - ("aarch64", "dpb2") => smallvec!["ccdp"], - ("aarch64", "frintts") => smallvec!["fptoint"], - ("aarch64", "fcma") => smallvec!["complxnum"], - ("aarch64", "pmuv3") => smallvec!["perfmon"], - ("aarch64", "paca") => smallvec!["pauth"], - ("aarch64", "pacg") => smallvec!["pauth"], - // Rust ties fp and neon together. In LLVM neon implicitly enables fp, - // but we manually enable neon when a feature only implicitly enables fp - ("aarch64", "f32mm") => smallvec!["f32mm", "neon"], - ("aarch64", "f64mm") => smallvec!["f64mm", "neon"], - ("aarch64", "fhm") => smallvec!["fp16fml", "neon"], - ("aarch64", "fp16") => smallvec!["fullfp16", "neon"], - ("aarch64", "jsconv") => smallvec!["jsconv", "neon"], - ("aarch64", "sve") => smallvec!["sve", "neon"], - ("aarch64", "sve2") => smallvec!["sve2", "neon"], - ("aarch64", "sve2-aes") => smallvec!["sve2-aes", "neon"], - ("aarch64", "sve2-sm4") => smallvec!["sve2-sm4", "neon"], - ("aarch64", "sve2-sha3") => smallvec!["sve2-sha3", "neon"], - ("aarch64", "sve2-bitperm") => smallvec!["sve2-bitperm", "neon"], - (_, s) => smallvec![s], + ("x86", "sse4.2") => { + LLVMFeature::with_dependency("sse4.2", TargetFeatureFoldStrength::EnableOnly("crc32")) + } + ("x86", "pclmulqdq") => LLVMFeature::new("pclmul"), + ("x86", "rdrand") => LLVMFeature::new("rdrnd"), + ("x86", "bmi1") => LLVMFeature::new("bmi"), + ("x86", "cmpxchg16b") => LLVMFeature::new("cx16"), + ("aarch64", "rcpc2") => LLVMFeature::new("rcpc-immo"), + ("aarch64", "dpb") => LLVMFeature::new("ccpp"), + ("aarch64", "dpb2") => LLVMFeature::new("ccdp"), + ("aarch64", "frintts") => LLVMFeature::new("fptoint"), + ("aarch64", "fcma") => LLVMFeature::new("complxnum"), + ("aarch64", "pmuv3") => LLVMFeature::new("perfmon"), + ("aarch64", "paca") => LLVMFeature::new("pauth"), + ("aarch64", "pacg") => LLVMFeature::new("pauth"), + // Rust ties fp and neon together. + ("aarch64", "neon") => { + LLVMFeature::with_dependency("neon", TargetFeatureFoldStrength::Both("fp-armv8")) + } + // In LLVM neon implicitly enables fp, but we manually enable + // neon when a feature only implicitly enables fp + ("aarch64", "f32mm") => { + LLVMFeature::with_dependency("f32mm", TargetFeatureFoldStrength::EnableOnly("neon")) + } + ("aarch64", "f64mm") => { + LLVMFeature::with_dependency("f64mm", TargetFeatureFoldStrength::EnableOnly("neon")) + } + ("aarch64", "fhm") => { + LLVMFeature::with_dependency("fp16fml", TargetFeatureFoldStrength::EnableOnly("neon")) + } + ("aarch64", "fp16") => { + LLVMFeature::with_dependency("fullfp16", TargetFeatureFoldStrength::EnableOnly("neon")) + } + ("aarch64", "jsconv") => { + LLVMFeature::with_dependency("jsconv", TargetFeatureFoldStrength::EnableOnly("neon")) + } + ("aarch64", "sve") => { + LLVMFeature::with_dependency("sve", TargetFeatureFoldStrength::EnableOnly("neon")) + } + ("aarch64", "sve2") => { + LLVMFeature::with_dependency("sve2", TargetFeatureFoldStrength::EnableOnly("neon")) + } + ("aarch64", "sve2-aes") => { + LLVMFeature::with_dependency("sve2-aes", TargetFeatureFoldStrength::EnableOnly("neon")) + } + ("aarch64", "sve2-sm4") => { + LLVMFeature::with_dependency("sve2-sm4", TargetFeatureFoldStrength::EnableOnly("neon")) + } + ("aarch64", "sve2-sha3") => { + LLVMFeature::with_dependency("sve2-sha3", TargetFeatureFoldStrength::EnableOnly("neon")) + } + ("aarch64", "sve2-bitperm") => LLVMFeature::with_dependency( + "sve2-bitperm", + TargetFeatureFoldStrength::EnableOnly("neon"), + ), + (_, s) => LLVMFeature::new(s), } } @@ -274,18 +356,17 @@ fn print_target_features(sess: &Session, tm: &llvm::TargetMachine) { let mut rustc_target_features = supported_target_features(sess) .iter() .map(|(feature, _gate)| { - let desc = if let Some(llvm_feature) = to_llvm_features(sess, *feature).first() { - // LLVM asserts that these are sorted. LLVM and Rust both use byte comparison for these strings. + // LLVM asserts that these are sorted. LLVM and Rust both use byte comparison for these strings. + let llvm_feature = to_llvm_features(sess, *feature).llvm_feature_name; + let desc = match llvm_target_features.binary_search_by_key(&llvm_feature, |(f, _d)| f).ok() { Some(index) => { known_llvm_target_features.insert(llvm_feature); llvm_target_features[index].1 } None => "", - } - } else { - "" - }; + }; + (*feature, desc) }) .collect::<Vec<_>>(); @@ -469,10 +550,19 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec<Str // passing requests down to LLVM. This means that all in-language // features also work on the command line instead of having two // different names when the LLVM name and the Rust name differ. + let llvm_feature = to_llvm_features(sess, feature); + Some( - to_llvm_features(sess, feature) - .into_iter() - .map(move |f| format!("{}{}", enable_disable, f)), + std::iter::once(format!("{}{}", enable_disable, llvm_feature.llvm_feature_name)) + .chain(llvm_feature.dependency.into_iter().filter_map(move |feat| { + match (enable_disable, feat) { + ('-' | '+', TargetFeatureFoldStrength::Both(f)) + | ('+', TargetFeatureFoldStrength::EnableOnly(f)) => { + Some(format!("{}{}", enable_disable, f)) + } + _ => None, + } + })), ) }) .flatten(); diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index e8f8c321510..c24854b27a0 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -125,8 +125,7 @@ impl CodegenCx<'_, '_> { // Thread-local variables generally don't support copy relocations. let is_thread_local_var = llvm::LLVMIsAGlobalVariable(llval) - .map(|v| llvm::LLVMIsThreadLocal(v) == llvm::True) - .unwrap_or(false); + .is_some_and(|v| llvm::LLVMIsThreadLocal(v) == llvm::True); if is_thread_local_var { return false; } diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 02be88df103..0ac12d32be5 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -14,7 +14,7 @@ itertools = "0.10.1" tracing = "0.1" jobserver = "0.1.22" tempfile = "3.2" -thorin-dwp = "0.4" +thorin-dwp = "0.6" pathdiff = "0.2.0" serde_json = "1.0.59" snap = "1" @@ -46,7 +46,7 @@ rustc_session = { path = "../rustc_session" } libc = "0.2.50" [dependencies.object] -version = "0.30.1" +version = "0.31.1" default-features = false features = ["read_core", "elf", "macho", "pe", "unaligned", "archive", "write"] diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index ea06cb02d8b..8a00c42a0e8 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -9,6 +9,7 @@ use rustc_fs_util::fix_windows_verbatim_for_gcc; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_metadata::find_native_static_library; use rustc_metadata::fs::{emit_wrapper_file, METADATA_FILENAME}; +use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::SymbolExportKind; use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip}; @@ -21,7 +22,6 @@ use rustc_session::utils::NativeLibKind; /// need out of the shared crate context before we get rid of it. use rustc_session::{filesearch, Session}; use rustc_span::symbol::Symbol; -use rustc_span::DebuggerVisualizerFile; use rustc_target::spec::crt_objects::{CrtObjects, LinkSelfContainedDefault}; use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, LinkerFlavorCli, Lld, PanicStrategy}; use rustc_target::spec::{RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo}; @@ -574,6 +574,8 @@ fn link_staticlib<'a>( } } + all_native_libs.extend_from_slice(&codegen_results.crate_info.used_libraries); + if sess.opts.prints.contains(&PrintRequest::NativeStaticLibs) { print_native_static_libs(sess, &all_native_libs, &all_rust_dylibs); } diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 1e57f4248d2..cd56f85cccd 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -144,7 +144,7 @@ pub fn get_linker<'a>( cmd, sess, target_cpu, - hinted_static: false, + hinted_static: None, is_ld: cc == Cc::No, is_gnu: flavor.is_gnu(), }) as Box<dyn Linker>, @@ -214,7 +214,7 @@ pub struct GccLinker<'a> { cmd: Command, sess: &'a Session, target_cpu: &'a str, - hinted_static: bool, // Keeps track of the current hinting mode. + hinted_static: Option<bool>, // Keeps track of the current hinting mode. // Link as ld is_ld: bool, is_gnu: bool, @@ -275,9 +275,9 @@ impl<'a> GccLinker<'a> { if !self.takes_hints() { return; } - if !self.hinted_static { + if self.hinted_static != Some(true) { self.linker_arg("-Bstatic"); - self.hinted_static = true; + self.hinted_static = Some(true); } } @@ -285,9 +285,9 @@ impl<'a> GccLinker<'a> { if !self.takes_hints() { return; } - if self.hinted_static { + if self.hinted_static != Some(false) { self.linker_arg("-Bdynamic"); - self.hinted_static = false; + self.hinted_static = Some(false); } } @@ -1484,25 +1484,25 @@ impl<'a> L4Bender<'a> { pub struct AixLinker<'a> { cmd: Command, sess: &'a Session, - hinted_static: bool, + hinted_static: Option<bool>, } impl<'a> AixLinker<'a> { pub fn new(cmd: Command, sess: &'a Session) -> AixLinker<'a> { - AixLinker { cmd: cmd, sess: sess, hinted_static: false } + AixLinker { cmd: cmd, sess: sess, hinted_static: None } } fn hint_static(&mut self) { - if !self.hinted_static { + if self.hinted_static != Some(true) { self.cmd.arg("-bstatic"); - self.hinted_static = true; + self.hinted_static = Some(true); } } fn hint_dynamic(&mut self) { - if self.hinted_static { + if self.hinted_static != Some(false) { self.cmd.arg("-bdynamic"); - self.hinted_static = false; + self.hinted_static = Some(false); } } diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 14460efc1b0..a8b6030ac85 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -2,7 +2,7 @@ use crate::base::allocator_kind_for_codegen; use std::collections::hash_map::Entry::*; -use rustc_ast::expand::allocator::ALLOCATOR_METHODS; +use rustc_ast::expand::allocator::{ALLOCATOR_METHODS, NO_ALLOC_SHIM_IS_UNSTABLE}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LOCAL_CRATE}; @@ -241,6 +241,17 @@ fn exported_symbols_provider_local( used: false, }, )); + + let exported_symbol = + ExportedSymbol::NoDefId(SymbolName::new(tcx, NO_ALLOC_SHIM_IS_UNSTABLE)); + symbols.push(( + exported_symbol, + SymbolExportInfo { + level: SymbolExportLevel::Rust, + kind: SymbolExportKind::Data, + used: false, + }, + )) } if tcx.sess.instrument_coverage() || tcx.sess.opts.cg.profile_generate.enabled() { diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index d9b0a152594..15c7847155d 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -23,6 +23,7 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::lang_items::LangItem; use rustc_metadata::EncodedMetadata; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; +use rustc_middle::middle::debugger_visualizer::{DebuggerVisualizerFile, DebuggerVisualizerType}; use rustc_middle::middle::exported_symbols; use rustc_middle::middle::exported_symbols::SymbolExportKind; use rustc_middle::middle::lang_items; @@ -35,7 +36,6 @@ use rustc_session::config::{self, CrateType, EntryFnType, OutputType}; use rustc_session::Session; use rustc_span::symbol::sym; use rustc_span::Symbol; -use rustc_span::{DebuggerVisualizerFile, DebuggerVisualizerType}; use rustc_target::abi::{Align, FIRST_VARIANT}; use std::collections::BTreeSet; diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 5ce24c66c5c..31854c7f4c4 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -28,6 +28,7 @@ use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage}; use rustc_fluent_macro::fluent_messages; use rustc_hir::def_id::CrateNum; use rustc_middle::dep_graph::WorkProduct; +use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::dependency_format::Dependencies; use rustc_middle::middle::exported_symbols::SymbolExportKind; use rustc_middle::query::{ExternProviders, Providers}; @@ -38,7 +39,6 @@ use rustc_session::cstore::{self, CrateSource}; use rustc_session::utils::NativeLibKind; use rustc_session::Session; use rustc_span::symbol::Symbol; -use rustc_span::DebuggerVisualizerFile; use std::collections::BTreeSet; use std::io; use std::path::{Path, PathBuf}; diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index 569599faa36..835074806e9 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -84,7 +84,7 @@ impl DefLocation { struct LocalAnalyzer<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> { fx: &'mir FunctionCx<'a, 'tcx, Bx>, - dominators: Dominators<mir::BasicBlock>, + dominators: &'mir Dominators<mir::BasicBlock>, locals: IndexVec<mir::Local, LocalKind>, } diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index a832999225a..1f5f5b69d4d 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1031,7 +1031,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }); let needs_location = - instance.map_or(false, |i| i.def.requires_caller_location(self.cx.tcx())); + instance.is_some_and(|i| i.def.requires_caller_location(self.cx.tcx())); if needs_location { let mir_args = if let Some(num_untupled) = num_untupled { first_args.len() + num_untupled diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index 696c4517700..138bc3eb74a 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -944,7 +944,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { tcx.features().declared_lib_features.iter().any(|&(sym, _)| sym == gate) }; let feature_gate_declared = gate_declared(gate); - let implied_gate_declared = implied_by.map(gate_declared).unwrap_or(false); + let implied_gate_declared = implied_by.is_some_and(gate_declared); if !feature_gate_declared && !implied_gate_declared { self.check_op(ops::FnCallUnstable(callee, Some(gate))); return; @@ -971,7 +971,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // have no `rustc_const_stable` attributes to be const-unstable as well. This // should be fixed later. let callee_is_unstable_unmarked = tcx.lookup_const_stability(callee).is_none() - && tcx.lookup_stability(callee).map_or(false, |s| s.is_unstable()); + && tcx.lookup_stability(callee).is_some_and(|s| s.is_unstable()); if callee_is_unstable_unmarked { trace!("callee_is_unstable_unmarked"); // We do not use `const` modifiers for intrinsic "functions", as intrinsics are diff --git a/compiler/rustc_const_eval/src/transform/check_consts/mod.rs b/compiler/rustc_const_eval/src/transform/check_consts/mod.rs index 0e4501922f4..8ebfee8878c 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/mod.rs @@ -139,5 +139,5 @@ fn is_parent_const_stable_trait(tcx: TyCtxt<'_>, def_id: DefId) -> bool { return false; } - tcx.lookup_const_stability(parent.owner).map_or(false, |stab| stab.is_const_stable()) + tcx.lookup_const_stability(parent.owner).is_some_and(|stab| stab.is_const_stable()) } diff --git a/compiler/rustc_const_eval/src/util/compare_types.rs b/compiler/rustc_const_eval/src/util/compare_types.rs index f5f3d5de6b5..d6a2ffb7511 100644 --- a/compiler/rustc_const_eval/src/util/compare_types.rs +++ b/compiler/rustc_const_eval/src/util/compare_types.rs @@ -3,8 +3,8 @@ //! FIXME: Move this to a more general place. The utility of this extends to //! other areas of the compiler as well. -use rustc_infer::infer::{DefiningAnchor, TyCtxtInferExt}; -use rustc_infer::traits::ObligationCause; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_middle::traits::{DefiningAnchor, ObligationCause}; use rustc_middle::ty::{ParamEnv, Ty, TyCtxt}; use rustc_trait_selection::traits::ObligationCtxt; diff --git a/compiler/rustc_const_eval/src/util/type_name.rs b/compiler/rustc_const_eval/src/util/type_name.rs index 4e80a285186..11ad5b49df2 100644 --- a/compiler/rustc_const_eval/src/util/type_name.rs +++ b/compiler/rustc_const_eval/src/util/type_name.rs @@ -58,11 +58,12 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { // Types with identity (print the module path). ty::Adt(ty::AdtDef(Interned(&ty::AdtDefData { did: def_id, .. }, _)), substs) | ty::FnDef(def_id, substs) - | ty::Alias(_, ty::AliasTy { def_id, substs, .. }) + | ty::Alias(ty::Projection | ty::Opaque, ty::AliasTy { def_id, substs, .. }) | ty::Closure(def_id, substs) | ty::Generator(def_id, substs, _) => self.print_def_path(def_id, substs), ty::Foreign(def_id) => self.print_def_path(def_id, &[]), + ty::Alias(ty::Inherent, _) => bug!("type_name: unexpected inherent projection"), ty::GeneratorWitness(_) => bug!("type_name: unexpected `GeneratorWitness`"), ty::GeneratorWitnessMIR(..) => bug!("type_name: unexpected `GeneratorWitnessMIR`"), } diff --git a/compiler/rustc_data_structures/src/graph/dominators/mod.rs b/compiler/rustc_data_structures/src/graph/dominators/mod.rs index a7de709ba72..a5db14d9102 100644 --- a/compiler/rustc_data_structures/src/graph/dominators/mod.rs +++ b/compiler/rustc_data_structures/src/graph/dominators/mod.rs @@ -26,7 +26,7 @@ rustc_index::newtype_index! { struct PreorderIndex {} } -pub fn dominators<G: ControlFlowGraph>(graph: G) -> Dominators<G::Node> { +pub fn dominators<G: ControlFlowGraph>(graph: &G) -> Dominators<G::Node> { // compute the post order index (rank) for each node let mut post_order_rank = IndexVec::from_elem_n(0, graph.num_nodes()); @@ -109,28 +109,27 @@ pub fn dominators<G: ControlFlowGraph>(graph: G) -> Dominators<G::Node> { // they have been placed in the bucket. // // We compute a partial set of immediate dominators here. - let z = parent[w]; - for &v in bucket[z].iter() { + for &v in bucket[w].iter() { // This uses the result of Lemma 5 from section 2 from the original // 1979 paper, to compute either the immediate or relative dominator // for a given vertex v. // // eval returns a vertex y, for which semi[y] is minimum among - // vertices semi[v] +> y *> v. Note that semi[v] = z as we're in the - // z bucket. + // vertices semi[v] +> y *> v. Note that semi[v] = w as we're in the + // w bucket. // // Given such a vertex y, semi[y] <= semi[v] and idom[y] = idom[v]. // If semi[y] = semi[v], though, idom[v] = semi[v]. // // Using this, we can either set idom[v] to be: - // * semi[v] (i.e. z), if semi[y] is z + // * semi[v] (i.e. w), if semi[y] is w // * idom[y], otherwise // // We don't directly set to idom[y] though as it's not necessarily // known yet. The second preorder traversal will cleanup by updating // the idom for any that were missed in this pass. let y = eval(&mut parent, lastlinked, &semi, &mut label, v); - idom[v] = if semi[y] < z { y } else { z }; + idom[v] = if semi[y] < w { y } else { w }; } // This loop computes the semi[w] for w. @@ -213,10 +212,11 @@ pub fn dominators<G: ControlFlowGraph>(graph: G) -> Dominators<G::Node> { // If we don't yet know the idom directly, then push this vertex into // our semidominator's bucket, where it will get processed at a later // stage to compute its immediate dominator. - if parent[w] != semi[w] { + let z = parent[w]; + if z != semi[w] { bucket[semi[w]].push(w); } else { - idom[w] = parent[w]; + idom[w] = z; } // Optimization: We share the parent array between processed and not @@ -244,7 +244,10 @@ pub fn dominators<G: ControlFlowGraph>(graph: G) -> Dominators<G::Node> { let start_node = graph.start_node(); immediate_dominators[start_node] = None; - Dominators { start_node, post_order_rank, immediate_dominators } + + let time = compute_access_time(start_node, &immediate_dominators); + + Dominators { start_node, post_order_rank, immediate_dominators, time } } /// Evaluate the link-eval virtual forest, providing the currently minimum semi @@ -316,6 +319,7 @@ pub struct Dominators<N: Idx> { // possible to get its full list of dominators by looking up the dominator // of each dominator. (See the `impl Iterator for Iter` definition). immediate_dominators: IndexVec<N, Option<N>>, + time: IndexVec<N, Time>, } impl<Node: Idx> Dominators<Node> { @@ -333,12 +337,7 @@ impl<Node: Idx> Dominators<Node> { /// See the `impl Iterator for Iter` definition to understand how this works. pub fn dominators(&self, node: Node) -> Iter<'_, Node> { assert!(self.is_reachable(node), "node {node:?} is not reachable"); - Iter { dominators: self, node: Some(node) } - } - - pub fn dominates(&self, dom: Node, node: Node) -> bool { - // FIXME -- could be optimized by using post-order-rank - self.dominators(node).any(|n| n == dom) + Iter { dom_tree: self, node: Some(node) } } /// Provide deterministic ordering of nodes such that, if any two nodes have a dominator @@ -348,10 +347,22 @@ impl<Node: Idx> Dominators<Node> { pub fn rank_partial_cmp(&self, lhs: Node, rhs: Node) -> Option<Ordering> { self.post_order_rank[rhs].partial_cmp(&self.post_order_rank[lhs]) } + + /// Returns true if `a` dominates `b`. + /// + /// # Panics + /// + /// Panics if `b` is unreachable. + pub fn dominates(&self, a: Node, b: Node) -> bool { + let a = self.time[a]; + let b = self.time[b]; + assert!(b.start != 0, "node {b:?} is not reachable"); + a.start <= b.start && b.finish <= a.finish + } } pub struct Iter<'dom, Node: Idx> { - dominators: &'dom Dominators<Node>, + dom_tree: &'dom Dominators<Node>, node: Option<Node>, } @@ -360,10 +371,74 @@ impl<'dom, Node: Idx> Iterator for Iter<'dom, Node> { fn next(&mut self) -> Option<Self::Item> { if let Some(node) = self.node { - self.node = self.dominators.immediate_dominator(node); + self.node = self.dom_tree.immediate_dominator(node); Some(node) } else { None } } } + +/// Describes the number of vertices discovered at the time when processing of a particular vertex +/// started and when it finished. Both values are zero for unreachable vertices. +#[derive(Copy, Clone, Default, Debug)] +struct Time { + start: u32, + finish: u32, +} + +fn compute_access_time<N: Idx>( + start_node: N, + immediate_dominators: &IndexSlice<N, Option<N>>, +) -> IndexVec<N, Time> { + // Transpose the dominator tree edges, so that child nodes of vertex v are stored in + // node[edges[v].start..edges[v].end]. + let mut edges: IndexVec<N, std::ops::Range<u32>> = + IndexVec::from_elem(0..0, immediate_dominators); + for &idom in immediate_dominators.iter() { + if let Some(idom) = idom { + edges[idom].end += 1; + } + } + let mut m = 0; + for e in edges.iter_mut() { + m += e.end; + e.start = m; + e.end = m; + } + let mut node = IndexVec::from_elem_n(Idx::new(0), m.try_into().unwrap()); + for (i, &idom) in immediate_dominators.iter_enumerated() { + if let Some(idom) = idom { + edges[idom].start -= 1; + node[edges[idom].start] = i; + } + } + + // Perform a depth-first search of the dominator tree. Record the number of vertices discovered + // when vertex v is discovered first as time[v].start, and when its processing is finished as + // time[v].finish. + let mut time: IndexVec<N, Time> = IndexVec::from_elem(Time::default(), immediate_dominators); + let mut stack = Vec::new(); + + let mut discovered = 1; + stack.push(start_node); + time[start_node].start = discovered; + + while let Some(&i) = stack.last() { + let e = &mut edges[i]; + if e.start == e.end { + // Finish processing vertex i. + time[i].finish = discovered; + stack.pop(); + } else { + let j = node[e.start]; + e.start += 1; + // Start processing vertex j. + discovered += 1; + time[j].start = discovered; + stack.push(j); + } + } + + time +} diff --git a/compiler/rustc_data_structures/src/graph/dominators/tests.rs b/compiler/rustc_data_structures/src/graph/dominators/tests.rs index 8b124516623..5472bb8087e 100644 --- a/compiler/rustc_data_structures/src/graph/dominators/tests.rs +++ b/compiler/rustc_data_structures/src/graph/dominators/tests.rs @@ -53,3 +53,30 @@ fn immediate_dominator() { assert_eq!(dominators.immediate_dominator(2), Some(1)); assert_eq!(dominators.immediate_dominator(3), Some(2)); } + +#[test] +fn transitive_dominator() { + let graph = TestGraph::new( + 0, + &[ + // First tree branch. + (0, 1), + (1, 2), + (2, 3), + (3, 4), + // Second tree branch. + (1, 5), + (5, 6), + // Third tree branch. + (0, 7), + // These links make 0 the dominator for 2 and 3. + (7, 2), + (5, 3), + ], + ); + + let dom_tree = dominators(&graph); + let immediate_dominators = &dom_tree.immediate_dominators; + assert_eq!(immediate_dominators[2], Some(0)); + assert_eq!(immediate_dominators[3], Some(0)); // This used to return Some(1). +} diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 5b9b0e106d2..859e384d8b5 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -102,21 +102,27 @@ pub mod unord; pub use ena::undo_log; pub use ena::unify; -pub struct OnDrop<F: Fn()>(pub F); +/// Returns a structure that calls `f` when dropped. +pub fn defer<F: FnOnce()>(f: F) -> OnDrop<F> { + OnDrop(Some(f)) +} + +pub struct OnDrop<F: FnOnce()>(Option<F>); -impl<F: Fn()> OnDrop<F> { - /// Forgets the function which prevents it from running. - /// Ensure that the function owns no memory, otherwise it will be leaked. +impl<F: FnOnce()> OnDrop<F> { + /// Disables on-drop call. #[inline] - pub fn disable(self) { - std::mem::forget(self); + pub fn disable(mut self) { + self.0.take(); } } -impl<F: Fn()> Drop for OnDrop<F> { +impl<F: FnOnce()> Drop for OnDrop<F> { #[inline] fn drop(&mut self) { - (self.0)(); + if let Some(f) = self.0.take() { + f(); + } } } diff --git a/compiler/rustc_data_structures/src/obligation_forest/mod.rs b/compiler/rustc_data_structures/src/obligation_forest/mod.rs index 27a869eb7cd..a47908648ba 100644 --- a/compiler/rustc_data_structures/src/obligation_forest/mod.rs +++ b/compiler/rustc_data_structures/src/obligation_forest/mod.rs @@ -366,7 +366,7 @@ impl<O: ForestObligation> ObligationForest<O> { && self .error_cache .get(&obligation_tree_id) - .map_or(false, |errors| errors.contains(v.key())); + .is_some_and(|errors| errors.contains(v.key())); if already_failed { Err(()) diff --git a/compiler/rustc_data_structures/src/owned_slice/tests.rs b/compiler/rustc_data_structures/src/owned_slice/tests.rs index 1eb5378cd1a..520871a12be 100644 --- a/compiler/rustc_data_structures/src/owned_slice/tests.rs +++ b/compiler/rustc_data_structures/src/owned_slice/tests.rs @@ -7,8 +7,8 @@ use std::{ }; use crate::{ + defer, owned_slice::{slice_owned, try_slice_owned, OwnedSlice}, - OnDrop, }; #[test] @@ -66,7 +66,7 @@ fn boxed() { fn drop_drops() { let flag = Arc::new(AtomicBool::new(false)); let flag_prime = Arc::clone(&flag); - let d = OnDrop(move || flag_prime.store(true, atomic::Ordering::Relaxed)); + let d = defer(move || flag_prime.store(true, atomic::Ordering::Relaxed)); let slice = slice_owned(d, |_| &[]); diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 960d53a62f5..40aa69e5a41 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -1250,7 +1250,8 @@ pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&Handler)) #[cfg(windows)] if let Some(msg) = info.payload().downcast_ref::<String>() { if msg.starts_with("failed printing to stdout: ") && msg.ends_with("(os error 232)") { - early_error_no_abort(ErrorOutputType::default(), msg.as_str()); + // the error code is already going to be reported when the panic unwinds up the stack + let _ = early_error_no_abort(ErrorOutputType::default(), msg.as_str()); return; } }; @@ -1314,7 +1315,7 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, extra_info: } // If backtraces are enabled, also print the query stack - let backtrace = env::var_os("RUST_BACKTRACE").map_or(false, |x| &x != "0"); + let backtrace = env::var_os("RUST_BACKTRACE").is_some_and(|x| &x != "0"); let num_frames = if backtrace { None } else { Some(2) }; diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 68e57de5e08..6d944e51314 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -285,15 +285,11 @@ pub trait Emitter: Translate { format!( "help: {}{}: `{}`", &msg, - if self - .source_map() - .map(|sm| is_case_difference( - sm, - substitution, - sugg.substitutions[0].parts[0].span, - )) - .unwrap_or(false) - { + if self.source_map().is_some_and(|sm| is_case_difference( + sm, + substitution, + sugg.substitutions[0].parts[0].span, + )) { " (notice the capitalization)" } else { "" @@ -2303,22 +2299,25 @@ impl EmitterWriter { // Colorize addition/replacements with green. for &SubstitutionHighlight { start, end } in highlight_parts { - // Account for tabs when highlighting (#87972). - let tabs: usize = line_to_add - .chars() - .take(start) - .map(|ch| match ch { - '\t' => 3, - _ => 0, - }) - .sum(); - buffer.set_style_range( - *row_num, - max_line_num_len + 3 + start + tabs, - max_line_num_len + 3 + end + tabs, - Style::Addition, - true, - ); + // This is a no-op for empty ranges + if start != end { + // Account for tabs when highlighting (#87972). + let tabs: usize = line_to_add + .chars() + .take(start) + .map(|ch| match ch { + '\t' => 3, + _ => 0, + }) + .sum(); + buffer.set_style_range( + *row_num, + max_line_num_len + 3 + start + tabs, + max_line_num_len + 3 + end + tabs, + Style::Addition, + true, + ); + } } *row_num += 1; } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 0f360473619..3dec0d9299c 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -330,12 +330,11 @@ impl CodeSuggestion { }); buf.push_str(&part.snippet); let cur_hi = sm.lookup_char_pos(part.span.hi()); - if cur_hi.line == cur_lo.line && !part.snippet.is_empty() { - // Account for the difference between the width of the current code and the - // snippet being suggested, so that the *later* suggestions are correctly - // aligned on the screen. - acc += len - (cur_hi.col.0 - cur_lo.col.0) as isize; - } + // Account for the difference between the width of the current code and the + // snippet being suggested, so that the *later* suggestions are correctly + // aligned on the screen. Note that cur_hi and cur_lo can be on different + // lines, so cur_hi.col can be smaller than cur_lo.col + acc += len - (cur_hi.col.0 as isize - cur_lo.col.0 as isize); prev_hi = cur_hi; prev_line = sf.get_line(prev_hi.line - 1); for line in part.snippet.split('\n').skip(1) { @@ -479,6 +478,7 @@ pub enum StashKey { MaybeFruTypo, CallAssocMethod, TraitMissingMethod, + OpaqueHiddenTypeMismatch, } fn default_track_diagnostic(d: &mut Diagnostic, f: &mut dyn FnMut(&mut Diagnostic)) { @@ -1437,7 +1437,7 @@ impl HandlerInner { } fn treat_err_as_bug(&self) -> bool { - self.flags.treat_err_as_bug.map_or(false, |c| { + self.flags.treat_err_as_bug.is_some_and(|c| { self.err_count() + self.lint_err_count + self.delayed_bug_count() >= c.get() }) } @@ -1603,7 +1603,7 @@ impl HandlerInner { // This is technically `self.treat_err_as_bug()` but `delay_span_bug` is called before // incrementing `err_count` by one, so we need to +1 the comparing. // FIXME: Would be nice to increment err_count in a more coherent way. - if self.flags.treat_err_as_bug.map_or(false, |c| { + if self.flags.treat_err_as_bug.is_some_and(|c| { self.err_count() + self.lint_err_count + self.delayed_bug_count() + 1 >= c.get() }) { // FIXME: don't abort here if report_delayed_bugs is off @@ -1740,7 +1740,7 @@ impl DelayedDiagnostic { } fn decorate(mut self) -> Diagnostic { - self.inner.note(format!("delayed at {}", self.note)); + self.inner.note(format!("delayed at {}\n{}", self.inner.emitted_at, self.note)); self.inner } } diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index fd721749066..4671adccc54 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -780,7 +780,7 @@ impl SyntaxExtension { let allow_internal_unsafe = attr::contains_name(attrs, sym::allow_internal_unsafe); let local_inner_macros = attr::find_by_name(attrs, sym::macro_export) .and_then(|macro_export| macro_export.meta_item_list()) - .map_or(false, |l| attr::list_contains_name(&l, sym::local_inner_macros)); + .is_some_and(|l| attr::list_contains_name(&l, sym::local_inner_macros)); let collapse_debuginfo = attr::contains_name(attrs, sym::collapse_debuginfo); tracing::debug!(?local_inner_macros, ?collapse_debuginfo, ?allow_internal_unsafe); @@ -1449,7 +1449,7 @@ fn pretty_printing_compatibility_hack(item: &Item, sess: &ParseSess) -> bool { && version .next() .and_then(|c| c.parse::<u32>().ok()) - .map_or(false, |v| v < 6) + .is_some_and(|v| v < 6) }; if crate_matches { diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 1f77e687bb1..ce0093c7d4c 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -722,7 +722,11 @@ impl<'a, 'b> MacroExpander<'a, 'b> { }); } }; - if fragment_kind == AstFragmentKind::Expr && items.is_empty() { + if matches!( + fragment_kind, + AstFragmentKind::Expr | AstFragmentKind::MethodReceiverExpr + ) && items.is_empty() + { self.cx.emit_err(RemoveExprNotSupported { span }); fragment_kind.dummy(span) } else { @@ -1595,7 +1599,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { cfg_pos = Some(pos); // a cfg attr found, no need to search anymore break; } else if attr_pos.is_none() - && !name.map_or(false, rustc_feature::is_builtin_attr_name) + && !name.is_some_and(rustc_feature::is_builtin_attr_name) { attr_pos = Some(pos); // a non-cfg attr found, still may find a cfg attr } @@ -1643,7 +1647,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { let current_span = if let Some(sp) = span { sp.to(attr.span) } else { attr.span }; span = Some(current_span); - if attrs.peek().map_or(false, |next_attr| next_attr.doc_str().is_some()) { + if attrs.peek().is_some_and(|next_attr| next_attr.doc_str().is_some()) { continue; } @@ -1946,6 +1950,6 @@ impl<'feat> ExpansionConfig<'feat> { } fn proc_macro_hygiene(&self) -> bool { - self.features.map_or(false, |features| features.proc_macro_hygiene) + self.features.is_some_and(|features| features.proc_macro_hygiene) } } diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 61cfbf5c5e5..06f4a0b5eef 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -861,11 +861,11 @@ pub fn is_builtin_attr_name(name: Symbol) -> bool { /// Whether this builtin attribute is only used in the local crate. /// If so, it is not encoded in the crate metadata. pub fn is_builtin_only_local(name: Symbol) -> bool { - BUILTIN_ATTRIBUTE_MAP.get(&name).map_or(false, |attr| attr.only_local) + BUILTIN_ATTRIBUTE_MAP.get(&name).is_some_and(|attr| attr.only_local) } pub fn is_valid_for_get_attr(name: Symbol) -> bool { - BUILTIN_ATTRIBUTE_MAP.get(&name).map_or(false, |attr| match attr.duplicates { + BUILTIN_ATTRIBUTE_MAP.get(&name).is_some_and(|attr| match attr.duplicates { WarnFollowing | ErrorFollowing | ErrorPreceding | FutureWarnFollowing | FutureWarnPreceding => true, DuplicatesOk | WarnFollowingWordOnly => false, diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index 3ce16e15667..beb6307846d 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -84,14 +84,13 @@ impl UnstableFeatures { pub fn from_environment(krate: Option<&str>) -> Self { // `true` if this is a feature-staged build, i.e., on the beta or stable channel. let disable_unstable_features = - option_env!("CFG_DISABLE_UNSTABLE_FEATURES").map(|s| s != "0").unwrap_or(false); + option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some_and(|s| s != "0"); // Returns whether `krate` should be counted as unstable - let is_unstable_crate = |var: &str| { - krate.map_or(false, |name| var.split(',').any(|new_krate| new_krate == name)) - }; + let is_unstable_crate = + |var: &str| krate.is_some_and(|name| var.split(',').any(|new_krate| new_krate == name)); // `true` if we should enable unstable features for bootstrapping. - let bootstrap = std::env::var("RUSTC_BOOTSTRAP") - .map_or(false, |var| var == "1" || is_unstable_crate(&var)); + let bootstrap = + std::env::var("RUSTC_BOOTSTRAP").is_ok_and(|var| var == "1" || is_unstable_crate(&var)); match (disable_unstable_features, bootstrap) { (_, true) => UnstableFeatures::Cheat, (true, _) => UnstableFeatures::Disallow, diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 932f0396282..e8447310917 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -787,7 +787,7 @@ pub struct WhereBoundPredicate<'hir> { impl<'hir> WhereBoundPredicate<'hir> { /// Returns `true` if `param_def_id` matches the `bounded_ty` of this predicate. pub fn is_param_bound(&self, param_def_id: DefId) -> bool { - self.bounded_ty.as_generic_param().map_or(false, |(def_id, _)| def_id == param_def_id) + self.bounded_ty.as_generic_param().is_some_and(|(def_id, _)| def_id == param_def_id) } } diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index cf082f1ffaa..5fb06cf9465 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -2625,7 +2625,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { && tcx.all_impls(*trait_def_id) .any(|impl_def_id| { let trait_ref = tcx.impl_trait_ref(impl_def_id); - trait_ref.map_or(false, |trait_ref| { + trait_ref.is_some_and(|trait_ref| { let impl_ = trait_ref.subst( tcx, infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id), @@ -3654,7 +3654,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .. }) = tcx.hir().get_by_def_id(parent_id) && self_ty.hir_id == impl_self_ty.hir_id { - if !of_trait_ref.trait_def_id().map_or(false, |def_id| def_id.is_local()) { + if !of_trait_ref.trait_def_id().is_some_and(|def_id| def_id.is_local()) { return; } let of_trait_span = of_trait_ref.path.span; @@ -3693,7 +3693,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .source_map() .span_to_prev_source(self_ty.span) .ok() - .map_or(false, |s| s.trim_end().ends_with('<')); + .is_some_and(|s| s.trim_end().ends_with('<')); let is_global = poly_trait_ref.trait_ref.path.is_global(); diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs index 1cf93c86f4f..d6d1498d708 100644 --- a/compiler/rustc_hir_analysis/src/autoderef.rs +++ b/compiler/rustc_hir_analysis/src/autoderef.rs @@ -1,6 +1,5 @@ use crate::errors::AutoDerefReachedRecursionLimit; use crate::traits::query::evaluate_obligation::InferCtxtExt; -use crate::traits::NormalizeExt; use crate::traits::{self, TraitEngine, TraitEngineExt}; use rustc_infer::infer::InferCtxt; use rustc_middle::ty::TypeVisitableExt; @@ -9,6 +8,7 @@ use rustc_session::Limit; use rustc_span::def_id::LocalDefId; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::Span; +use rustc_trait_selection::traits::StructurallyNormalizeExt; #[derive(Copy, Clone, Debug)] pub enum AutoderefKind { @@ -66,14 +66,27 @@ impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> { } // Otherwise, deref if type is derefable: - let (kind, new_ty) = - if let Some(mt) = self.state.cur_ty.builtin_deref(self.include_raw_pointers) { - (AutoderefKind::Builtin, mt.ty) - } else if let Some(ty) = self.overloaded_deref_ty(self.state.cur_ty) { - (AutoderefKind::Overloaded, ty) + let (kind, new_ty) = if let Some(ty::TypeAndMut { ty, .. }) = + self.state.cur_ty.builtin_deref(self.include_raw_pointers) + { + debug_assert_eq!(ty, self.infcx.resolve_vars_if_possible(ty)); + // NOTE: we may still need to normalize the built-in deref in case + // we have some type like `&<Ty as Trait>::Assoc`, since users of + // autoderef expect this type to have been structurally normalized. + if self.infcx.tcx.trait_solver_next() + && let ty::Alias(ty::Projection, _) = ty.kind() + { + let (normalized_ty, obligations) = self.structurally_normalize(ty)?; + self.state.obligations.extend(obligations); + (AutoderefKind::Builtin, normalized_ty) } else { - return None; - }; + (AutoderefKind::Builtin, ty) + } + } else if let Some(ty) = self.overloaded_deref_ty(self.state.cur_ty) { + (AutoderefKind::Overloaded, ty) + } else { + return None; + }; if new_ty.references_error() { return None; @@ -119,14 +132,11 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { fn overloaded_deref_ty(&mut self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { debug!("overloaded_deref_ty({:?})", ty); - let tcx = self.infcx.tcx; // <ty as Deref> let trait_ref = ty::TraitRef::new(tcx, tcx.lang_items().deref_trait()?, [ty]); - let cause = traits::ObligationCause::misc(self.span, self.body_id); - let obligation = traits::Obligation::new( tcx, cause.clone(), @@ -138,26 +148,48 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { return None; } - let normalized_ty = self + let (normalized_ty, obligations) = + self.structurally_normalize(tcx.mk_projection(tcx.lang_items().deref_target()?, [ty]))?; + debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations); + self.state.obligations.extend(obligations); + + Some(self.infcx.resolve_vars_if_possible(normalized_ty)) + } + + #[instrument(level = "debug", skip(self), ret)] + pub fn structurally_normalize( + &self, + ty: Ty<'tcx>, + ) -> Option<(Ty<'tcx>, Vec<traits::PredicateObligation<'tcx>>)> { + let tcx = self.infcx.tcx; + let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new_in_snapshot(tcx); + + let cause = traits::ObligationCause::misc(self.span, self.body_id); + let normalized_ty = match self .infcx .at(&cause, self.param_env) - .normalize(tcx.mk_projection(tcx.lang_items().deref_target()?, trait_ref.substs)); - let mut fulfillcx = <dyn TraitEngine<'tcx>>::new_in_snapshot(tcx); - let normalized_ty = - normalized_ty.into_value_registering_obligations(self.infcx, &mut *fulfillcx); - let errors = fulfillcx.select_where_possible(&self.infcx); + .structurally_normalize(ty, &mut *fulfill_cx) + { + Ok(normalized_ty) => normalized_ty, + Err(errors) => { + // This shouldn't happen, except for evaluate/fulfill mismatches, + // but that's not a reason for an ICE (`predicate_may_hold` is conservative + // by design). + debug!(?errors, "encountered errors while fulfilling"); + return None; + } + }; + + let errors = fulfill_cx.select_where_possible(&self.infcx); if !errors.is_empty() { // This shouldn't happen, except for evaluate/fulfill mismatches, // but that's not a reason for an ICE (`predicate_may_hold` is conservative // by design). - debug!("overloaded_deref_ty: encountered errors {:?} while fulfilling", errors); + debug!(?errors, "encountered errors while fulfilling"); return None; } - let obligations = fulfillcx.pending_obligations(); - debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations); - self.state.obligations.extend(obligations); - Some(self.infcx.resolve_vars_if_possible(normalized_ty)) + Some((normalized_ty, fulfill_cx.pending_obligations())) } /// Returns the final type we ended up with, which may be an inference diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index d3495d3dbd7..3b2c052e8f4 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -13,11 +13,12 @@ use rustc_hir::intravisit::Visitor; use rustc_hir::{ItemKind, Node, PathSegment}; use rustc_infer::infer::opaque_types::ConstrainOpaqueTypeRegionVisitor; use rustc_infer::infer::outlives::env::OutlivesEnvironment; -use rustc_infer::infer::{DefiningAnchor, RegionVariableOrigin, TyCtxtInferExt}; +use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::traits::{Obligation, TraitEngineExt as _}; use rustc_lint_defs::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS; use rustc_middle::hir::nested_filter; use rustc_middle::middle::stability::EvalResult; +use rustc_middle::traits::DefiningAnchor; use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES}; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::util::{Discr, IntTypeExt}; @@ -800,16 +801,15 @@ fn check_impl_items_against_trait<'tcx>( let is_implemented = leaf_def .as_ref() - .map_or(false, |node_item| node_item.item.defaultness(tcx).has_value()); + .is_some_and(|node_item| node_item.item.defaultness(tcx).has_value()); if !is_implemented && tcx.impl_defaultness(impl_id).is_final() { missing_items.push(tcx.associated_item(trait_item_id)); } // true if this item is specifically implemented in this impl - let is_implemented_here = leaf_def - .as_ref() - .map_or(false, |node_item| !node_item.defining_node.is_from_trait()); + let is_implemented_here = + leaf_def.as_ref().is_some_and(|node_item| !node_item.defining_node.is_from_trait()); if !is_implemented_here { let full_impl_span = @@ -1082,8 +1082,8 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) let layout = tcx.layout_of(param_env.and(ty)); // We are currently checking the type this field came from, so it must be local let span = tcx.hir().span_if_local(field.did).unwrap(); - let zst = layout.map_or(false, |layout| layout.is_zst()); - let align1 = layout.map_or(false, |layout| layout.align.abi.bytes() == 1); + let zst = layout.is_ok_and(|layout| layout.is_zst()); + let align1 = layout.is_ok_and(|layout| layout.align.abi.bytes() == 1); if !zst { return (span, zst, align1, None); } diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 8918553e5f9..b403ee96b42 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -179,7 +179,7 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) { hir::ItemKind::Impl(impl_) => { let is_auto = tcx .impl_trait_ref(def_id) - .map_or(false, |trait_ref| tcx.trait_is_auto(trait_ref.skip_binder().def_id)); + .is_some_and(|trait_ref| tcx.trait_is_auto(trait_ref.skip_binder().def_id)); if let (hir::Defaultness::Default { .. }, true) = (impl_.defaultness, is_auto) { let sp = impl_.of_trait.as_ref().map_or(item.span, |t| t.path.span); let mut err = diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 22502bd4fdb..ca0d5509c57 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -819,7 +819,7 @@ fn convert_variant( recovered, adt_kind == AdtKind::Struct && tcx.has_attr(parent_did, sym::non_exhaustive) || variant_did - .map_or(false, |variant_did| tcx.has_attr(variant_did, sym::non_exhaustive)), + .is_some_and(|variant_did| tcx.has_attr(variant_did, sym::non_exhaustive)), ) } @@ -1025,7 +1025,7 @@ fn is_suggestable_infer_ty(ty: &hir::Ty<'_>) -> bool { is_suggestable_infer_ty(ty) || are_suggestable_generic_args(segment.args().args) } Path(hir::QPath::Resolved(ty_opt, hir::Path { segments, .. })) => { - ty_opt.map_or(false, is_suggestable_infer_ty) + ty_opt.is_some_and(is_suggestable_infer_ty) || segments.iter().any(|segment| are_suggestable_generic_args(segment.args().args)) } _ => false, diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index a33990813b8..e5b5dae551e 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -427,6 +427,8 @@ pub(super) fn explicit_predicates_of<'tcx>( // supertrait). if let ty::Alias(ty::Projection, projection) = ty.kind() { projection.substs == trait_identity_substs + // FIXME(return_type_notation): This check should be more robust + && !tcx.is_impl_trait_in_trait(projection.def_id) && tcx.associated_item(projection.def_id).container_id(tcx) == def_id.to_def_id() } else { diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index ca430a5e863..8e082d3c532 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -1,10 +1,7 @@ use rustc_errors::{Applicability, StashKey}; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; -use rustc_hir::intravisit; -use rustc_hir::intravisit::Visitor; -use rustc_hir::{HirId, Node}; -use rustc_middle::hir::nested_filter; +use rustc_hir::HirId; use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::util::IntTypeExt; @@ -14,7 +11,8 @@ use rustc_span::{Span, DUMMY_SP}; use super::ItemCtxt; use super::{bad_placeholder, is_suggestable_infer_ty}; -use crate::errors::UnconstrainedOpaqueType; + +mod opaque; fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { use hir::*; @@ -429,7 +427,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty ItemKind::OpaqueTy(OpaqueTy { origin: hir::OpaqueTyOrigin::TyAlias { .. }, .. - }) => find_opaque_ty_constraints_for_tait(tcx, def_id), + }) => opaque::find_opaque_ty_constraints_for_tait(tcx, def_id), // Opaque types desugared from `impl Trait`. ItemKind::OpaqueTy(OpaqueTy { origin: @@ -443,7 +441,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty "tried to get type of this RPITIT with no definition" ); } - find_opaque_ty_constraints_for_rpit(tcx, def_id, owner) + opaque::find_opaque_ty_constraints_for_rpit(tcx, def_id, owner) } ItemKind::Trait(..) | ItemKind::TraitAlias(..) @@ -502,303 +500,6 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty ty::EarlyBinder(output) } -#[instrument(skip(tcx), level = "debug")] -/// Checks "defining uses" of opaque `impl Trait` types to ensure that they meet the restrictions -/// laid for "higher-order pattern unification". -/// This ensures that inference is tractable. -/// In particular, definitions of opaque types can only use other generics as arguments, -/// and they cannot repeat an argument. Example: -/// -/// ```ignore (illustrative) -/// type Foo<A, B> = impl Bar<A, B>; -/// -/// // Okay -- `Foo` is applied to two distinct, generic types. -/// fn a<T, U>() -> Foo<T, U> { .. } -/// -/// // Not okay -- `Foo` is applied to `T` twice. -/// fn b<T>() -> Foo<T, T> { .. } -/// -/// // Not okay -- `Foo` is applied to a non-generic type. -/// fn b<T>() -> Foo<T, u32> { .. } -/// ``` -/// -fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> { - use rustc_hir::{Expr, ImplItem, Item, TraitItem}; - - struct ConstraintLocator<'tcx> { - tcx: TyCtxt<'tcx>, - - /// def_id of the opaque type whose defining uses are being checked - def_id: LocalDefId, - - /// as we walk the defining uses, we are checking that all of them - /// define the same hidden type. This variable is set to `Some` - /// with the first type that we find, and then later types are - /// checked against it (we also carry the span of that first - /// type). - found: Option<ty::OpaqueHiddenType<'tcx>>, - - /// In the presence of dead code, typeck may figure out a hidden type - /// while borrowck will not. We collect these cases here and check at - /// the end that we actually found a type that matches (modulo regions). - typeck_types: Vec<ty::OpaqueHiddenType<'tcx>>, - } - - impl ConstraintLocator<'_> { - #[instrument(skip(self), level = "debug")] - fn check(&mut self, item_def_id: LocalDefId) { - // Don't try to check items that cannot possibly constrain the type. - if !self.tcx.has_typeck_results(item_def_id) { - debug!("no constraint: no typeck results"); - return; - } - // Calling `mir_borrowck` can lead to cycle errors through - // const-checking, avoid calling it if we don't have to. - // ```rust - // type Foo = impl Fn() -> usize; // when computing type for this - // const fn bar() -> Foo { - // || 0usize - // } - // const BAZR: Foo = bar(); // we would mir-borrowck this, causing cycles - // // because we again need to reveal `Foo` so we can check whether the - // // constant does not contain interior mutability. - // ``` - let tables = self.tcx.typeck(item_def_id); - if let Some(guar) = tables.tainted_by_errors { - self.found = - Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: self.tcx.ty_error(guar) }); - return; - } - let Some(&typeck_hidden_ty) = tables.concrete_opaque_types.get(&self.def_id) else { - debug!("no constraints in typeck results"); - return; - }; - if self.typeck_types.iter().all(|prev| prev.ty != typeck_hidden_ty.ty) { - self.typeck_types.push(typeck_hidden_ty); - } - - // Use borrowck to get the type with unerased regions. - let concrete_opaque_types = &self.tcx.mir_borrowck(item_def_id).concrete_opaque_types; - debug!(?concrete_opaque_types); - if let Some(&concrete_type) = concrete_opaque_types.get(&self.def_id) { - debug!(?concrete_type, "found constraint"); - if let Some(prev) = &mut self.found { - if concrete_type.ty != prev.ty && !(concrete_type, prev.ty).references_error() { - let guar = prev.report_mismatch(&concrete_type, self.tcx); - prev.ty = self.tcx.ty_error(guar); - } - } else { - self.found = Some(concrete_type); - } - } - } - } - - impl<'tcx> intravisit::Visitor<'tcx> for ConstraintLocator<'tcx> { - type NestedFilter = nested_filter::All; - - fn nested_visit_map(&mut self) -> Self::Map { - self.tcx.hir() - } - fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { - if let hir::ExprKind::Closure(closure) = ex.kind { - self.check(closure.def_id); - } - intravisit::walk_expr(self, ex); - } - fn visit_item(&mut self, it: &'tcx Item<'tcx>) { - trace!(?it.owner_id); - // The opaque type itself or its children are not within its reveal scope. - if it.owner_id.def_id != self.def_id { - self.check(it.owner_id.def_id); - intravisit::walk_item(self, it); - } - } - fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) { - trace!(?it.owner_id); - // The opaque type itself or its children are not within its reveal scope. - if it.owner_id.def_id != self.def_id { - self.check(it.owner_id.def_id); - intravisit::walk_impl_item(self, it); - } - } - fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) { - trace!(?it.owner_id); - self.check(it.owner_id.def_id); - intravisit::walk_trait_item(self, it); - } - } - - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - let scope = tcx.hir().get_defining_scope(hir_id); - let mut locator = ConstraintLocator { def_id, tcx, found: None, typeck_types: vec![] }; - - debug!(?scope); - - if scope == hir::CRATE_HIR_ID { - tcx.hir().walk_toplevel_module(&mut locator); - } else { - trace!("scope={:#?}", tcx.hir().get(scope)); - match tcx.hir().get(scope) { - // We explicitly call `visit_*` methods, instead of using `intravisit::walk_*` methods - // This allows our visitor to process the defining item itself, causing - // it to pick up any 'sibling' defining uses. - // - // For example, this code: - // ``` - // fn foo() { - // type Blah = impl Debug; - // let my_closure = || -> Blah { true }; - // } - // ``` - // - // requires us to explicitly process `foo()` in order - // to notice the defining usage of `Blah`. - Node::Item(it) => locator.visit_item(it), - Node::ImplItem(it) => locator.visit_impl_item(it), - Node::TraitItem(it) => locator.visit_trait_item(it), - other => bug!("{:?} is not a valid scope for an opaque type item", other), - } - } - - let Some(hidden) = locator.found else { - let reported = tcx.sess.emit_err(UnconstrainedOpaqueType { - span: tcx.def_span(def_id), - name: tcx.item_name(tcx.local_parent(def_id).to_def_id()), - what: match tcx.hir().get(scope) { - _ if scope == hir::CRATE_HIR_ID => "module", - Node::Item(hir::Item { kind: hir::ItemKind::Mod(_), .. }) => "module", - Node::Item(hir::Item { kind: hir::ItemKind::Impl(_), .. }) => "impl", - _ => "item", - }, - }); - return tcx.ty_error(reported); - }; - - // Only check against typeck if we didn't already error - if !hidden.ty.references_error() { - for concrete_type in locator.typeck_types { - if tcx.erase_regions(concrete_type.ty) != tcx.erase_regions(hidden.ty) - && !(concrete_type, hidden).references_error() - { - hidden.report_mismatch(&concrete_type, tcx); - } - } - } - - hidden.ty -} - -fn find_opaque_ty_constraints_for_rpit( - tcx: TyCtxt<'_>, - def_id: LocalDefId, - owner_def_id: LocalDefId, -) -> Ty<'_> { - use rustc_hir::{Expr, ImplItem, Item, TraitItem}; - - struct ConstraintChecker<'tcx> { - tcx: TyCtxt<'tcx>, - - /// def_id of the opaque type whose defining uses are being checked - def_id: LocalDefId, - - found: ty::OpaqueHiddenType<'tcx>, - } - - impl ConstraintChecker<'_> { - #[instrument(skip(self), level = "debug")] - fn check(&self, def_id: LocalDefId) { - // Use borrowck to get the type with unerased regions. - let concrete_opaque_types = &self.tcx.mir_borrowck(def_id).concrete_opaque_types; - debug!(?concrete_opaque_types); - for (&def_id, &concrete_type) in concrete_opaque_types { - if def_id != self.def_id { - // Ignore constraints for other opaque types. - continue; - } - - debug!(?concrete_type, "found constraint"); - - if concrete_type.ty != self.found.ty - && !(concrete_type, self.found).references_error() - { - self.found.report_mismatch(&concrete_type, self.tcx); - } - } - } - } - - impl<'tcx> intravisit::Visitor<'tcx> for ConstraintChecker<'tcx> { - type NestedFilter = nested_filter::OnlyBodies; - - fn nested_visit_map(&mut self) -> Self::Map { - self.tcx.hir() - } - fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { - if let hir::ExprKind::Closure(closure) = ex.kind { - self.check(closure.def_id); - } - intravisit::walk_expr(self, ex); - } - fn visit_item(&mut self, it: &'tcx Item<'tcx>) { - trace!(?it.owner_id); - // The opaque type itself or its children are not within its reveal scope. - if it.owner_id.def_id != self.def_id { - self.check(it.owner_id.def_id); - intravisit::walk_item(self, it); - } - } - fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) { - trace!(?it.owner_id); - // The opaque type itself or its children are not within its reveal scope. - if it.owner_id.def_id != self.def_id { - self.check(it.owner_id.def_id); - intravisit::walk_impl_item(self, it); - } - } - fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) { - trace!(?it.owner_id); - self.check(it.owner_id.def_id); - intravisit::walk_trait_item(self, it); - } - } - - let concrete = tcx.mir_borrowck(owner_def_id).concrete_opaque_types.get(&def_id).copied(); - - if let Some(concrete) = concrete { - let scope = tcx.hir().local_def_id_to_hir_id(owner_def_id); - debug!(?scope); - let mut locator = ConstraintChecker { def_id, tcx, found: concrete }; - - match tcx.hir().get(scope) { - Node::Item(it) => intravisit::walk_item(&mut locator, it), - Node::ImplItem(it) => intravisit::walk_impl_item(&mut locator, it), - Node::TraitItem(it) => intravisit::walk_trait_item(&mut locator, it), - other => bug!("{:?} is not a valid scope for an opaque type item", other), - } - } - - concrete.map(|concrete| concrete.ty).unwrap_or_else(|| { - let table = tcx.typeck(owner_def_id); - if let Some(guar) = table.tainted_by_errors { - // Some error in the - // owner fn prevented us from populating - // the `concrete_opaque_types` table. - tcx.ty_error(guar) - } else { - table.concrete_opaque_types.get(&def_id).map(|ty| ty.ty).unwrap_or_else(|| { - // We failed to resolve the opaque type or it - // resolves to itself. We interpret this as the - // no values of the hidden type ever being constructed, - // so we can just make the hidden type be `!`. - // For backwards compatibility reasons, we fall back to - // `()` until we the diverging default is changed. - tcx.mk_diverging_default() - }) - } - }) -} - fn infer_placeholder_type<'a>( tcx: TyCtxt<'a>, def_id: LocalDefId, diff --git a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs new file mode 100644 index 00000000000..f7c5b44678f --- /dev/null +++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs @@ -0,0 +1,298 @@ +use rustc_hir::def_id::LocalDefId; +use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::{self as hir, Expr, ImplItem, Item, Node, TraitItem}; +use rustc_middle::hir::nested_filter; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; +use rustc_span::DUMMY_SP; + +use crate::errors::UnconstrainedOpaqueType; + +/// Checks "defining uses" of opaque `impl Trait` types to ensure that they meet the restrictions +/// laid for "higher-order pattern unification". +/// This ensures that inference is tractable. +/// In particular, definitions of opaque types can only use other generics as arguments, +/// and they cannot repeat an argument. Example: +/// +/// ```ignore (illustrative) +/// type Foo<A, B> = impl Bar<A, B>; +/// +/// // Okay -- `Foo` is applied to two distinct, generic types. +/// fn a<T, U>() -> Foo<T, U> { .. } +/// +/// // Not okay -- `Foo` is applied to `T` twice. +/// fn b<T>() -> Foo<T, T> { .. } +/// +/// // Not okay -- `Foo` is applied to a non-generic type. +/// fn b<T>() -> Foo<T, u32> { .. } +/// ``` +#[instrument(skip(tcx), level = "debug")] +pub(super) fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> { + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + let scope = tcx.hir().get_defining_scope(hir_id); + let mut locator = TaitConstraintLocator { def_id, tcx, found: None, typeck_types: vec![] }; + + debug!(?scope); + + if scope == hir::CRATE_HIR_ID { + tcx.hir().walk_toplevel_module(&mut locator); + } else { + trace!("scope={:#?}", tcx.hir().get(scope)); + match tcx.hir().get(scope) { + // We explicitly call `visit_*` methods, instead of using `intravisit::walk_*` methods + // This allows our visitor to process the defining item itself, causing + // it to pick up any 'sibling' defining uses. + // + // For example, this code: + // ``` + // fn foo() { + // type Blah = impl Debug; + // let my_closure = || -> Blah { true }; + // } + // ``` + // + // requires us to explicitly process `foo()` in order + // to notice the defining usage of `Blah`. + Node::Item(it) => locator.visit_item(it), + Node::ImplItem(it) => locator.visit_impl_item(it), + Node::TraitItem(it) => locator.visit_trait_item(it), + other => bug!("{:?} is not a valid scope for an opaque type item", other), + } + } + + let Some(hidden) = locator.found else { + let reported = tcx.sess.emit_err(UnconstrainedOpaqueType { + span: tcx.def_span(def_id), + name: tcx.item_name(tcx.local_parent(def_id).to_def_id()), + what: match tcx.hir().get(scope) { + _ if scope == hir::CRATE_HIR_ID => "module", + Node::Item(hir::Item { kind: hir::ItemKind::Mod(_), .. }) => "module", + Node::Item(hir::Item { kind: hir::ItemKind::Impl(_), .. }) => "impl", + _ => "item", + }, + }); + return tcx.ty_error(reported); + }; + + // Only check against typeck if we didn't already error + if !hidden.ty.references_error() { + for concrete_type in locator.typeck_types { + if concrete_type.ty != tcx.erase_regions(hidden.ty) + && !(concrete_type, hidden).references_error() + { + hidden.report_mismatch(&concrete_type, def_id, tcx).emit(); + } + } + } + + hidden.ty +} + +struct TaitConstraintLocator<'tcx> { + tcx: TyCtxt<'tcx>, + + /// def_id of the opaque type whose defining uses are being checked + def_id: LocalDefId, + + /// as we walk the defining uses, we are checking that all of them + /// define the same hidden type. This variable is set to `Some` + /// with the first type that we find, and then later types are + /// checked against it (we also carry the span of that first + /// type). + found: Option<ty::OpaqueHiddenType<'tcx>>, + + /// In the presence of dead code, typeck may figure out a hidden type + /// while borrowck will not. We collect these cases here and check at + /// the end that we actually found a type that matches (modulo regions). + typeck_types: Vec<ty::OpaqueHiddenType<'tcx>>, +} + +impl TaitConstraintLocator<'_> { + #[instrument(skip(self), level = "debug")] + fn check(&mut self, item_def_id: LocalDefId) { + // Don't try to check items that cannot possibly constrain the type. + if !self.tcx.has_typeck_results(item_def_id) { + debug!("no constraint: no typeck results"); + return; + } + // Calling `mir_borrowck` can lead to cycle errors through + // const-checking, avoid calling it if we don't have to. + // ```rust + // type Foo = impl Fn() -> usize; // when computing type for this + // const fn bar() -> Foo { + // || 0usize + // } + // const BAZR: Foo = bar(); // we would mir-borrowck this, causing cycles + // // because we again need to reveal `Foo` so we can check whether the + // // constant does not contain interior mutability. + // ``` + let tables = self.tcx.typeck(item_def_id); + if let Some(guar) = tables.tainted_by_errors { + self.found = Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: self.tcx.ty_error(guar) }); + return; + } + let Some(&typeck_hidden_ty) = tables.concrete_opaque_types.get(&self.def_id) else { + debug!("no constraints in typeck results"); + return; + }; + if self.typeck_types.iter().all(|prev| prev.ty != typeck_hidden_ty.ty) { + self.typeck_types.push(typeck_hidden_ty); + } + + // Use borrowck to get the type with unerased regions. + let concrete_opaque_types = &self.tcx.mir_borrowck(item_def_id).concrete_opaque_types; + debug!(?concrete_opaque_types); + if let Some(&concrete_type) = concrete_opaque_types.get(&self.def_id) { + debug!(?concrete_type, "found constraint"); + if let Some(prev) = &mut self.found { + if concrete_type.ty != prev.ty && !(concrete_type, prev.ty).references_error() { + let guar = prev.report_mismatch(&concrete_type, self.def_id, self.tcx).emit(); + prev.ty = self.tcx.ty_error(guar); + } + } else { + self.found = Some(concrete_type); + } + } + } +} + +impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> { + type NestedFilter = nested_filter::All; + + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() + } + fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { + if let hir::ExprKind::Closure(closure) = ex.kind { + self.check(closure.def_id); + } + intravisit::walk_expr(self, ex); + } + fn visit_item(&mut self, it: &'tcx Item<'tcx>) { + trace!(?it.owner_id); + // The opaque type itself or its children are not within its reveal scope. + if it.owner_id.def_id != self.def_id { + self.check(it.owner_id.def_id); + intravisit::walk_item(self, it); + } + } + fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) { + trace!(?it.owner_id); + // The opaque type itself or its children are not within its reveal scope. + if it.owner_id.def_id != self.def_id { + self.check(it.owner_id.def_id); + intravisit::walk_impl_item(self, it); + } + } + fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) { + trace!(?it.owner_id); + self.check(it.owner_id.def_id); + intravisit::walk_trait_item(self, it); + } +} + +pub(super) fn find_opaque_ty_constraints_for_rpit( + tcx: TyCtxt<'_>, + def_id: LocalDefId, + owner_def_id: LocalDefId, +) -> Ty<'_> { + let concrete = tcx.mir_borrowck(owner_def_id).concrete_opaque_types.get(&def_id).copied(); + + if let Some(concrete) = concrete { + let scope = tcx.hir().local_def_id_to_hir_id(owner_def_id); + debug!(?scope); + let mut locator = RpitConstraintChecker { def_id, tcx, found: concrete }; + + match tcx.hir().get(scope) { + Node::Item(it) => intravisit::walk_item(&mut locator, it), + Node::ImplItem(it) => intravisit::walk_impl_item(&mut locator, it), + Node::TraitItem(it) => intravisit::walk_trait_item(&mut locator, it), + other => bug!("{:?} is not a valid scope for an opaque type item", other), + } + } + + concrete.map(|concrete| concrete.ty).unwrap_or_else(|| { + let table = tcx.typeck(owner_def_id); + if let Some(guar) = table.tainted_by_errors { + // Some error in the + // owner fn prevented us from populating + // the `concrete_opaque_types` table. + tcx.ty_error(guar) + } else { + table.concrete_opaque_types.get(&def_id).map(|ty| ty.ty).unwrap_or_else(|| { + // We failed to resolve the opaque type or it + // resolves to itself. We interpret this as the + // no values of the hidden type ever being constructed, + // so we can just make the hidden type be `!`. + // For backwards compatibility reasons, we fall back to + // `()` until we the diverging default is changed. + tcx.mk_diverging_default() + }) + } + }) +} + +struct RpitConstraintChecker<'tcx> { + tcx: TyCtxt<'tcx>, + + /// def_id of the opaque type whose defining uses are being checked + def_id: LocalDefId, + + found: ty::OpaqueHiddenType<'tcx>, +} + +impl RpitConstraintChecker<'_> { + #[instrument(skip(self), level = "debug")] + fn check(&self, def_id: LocalDefId) { + // Use borrowck to get the type with unerased regions. + let concrete_opaque_types = &self.tcx.mir_borrowck(def_id).concrete_opaque_types; + debug!(?concrete_opaque_types); + for (&def_id, &concrete_type) in concrete_opaque_types { + if def_id != self.def_id { + // Ignore constraints for other opaque types. + continue; + } + + debug!(?concrete_type, "found constraint"); + + if concrete_type.ty != self.found.ty && !(concrete_type, self.found).references_error() + { + self.found.report_mismatch(&concrete_type, self.def_id, self.tcx).emit(); + } + } + } +} + +impl<'tcx> intravisit::Visitor<'tcx> for RpitConstraintChecker<'tcx> { + type NestedFilter = nested_filter::OnlyBodies; + + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() + } + fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { + if let hir::ExprKind::Closure(closure) = ex.kind { + self.check(closure.def_id); + } + intravisit::walk_expr(self, ex); + } + fn visit_item(&mut self, it: &'tcx Item<'tcx>) { + trace!(?it.owner_id); + // The opaque type itself or its children are not within its reveal scope. + if it.owner_id.def_id != self.def_id { + self.check(it.owner_id.def_id); + intravisit::walk_item(self, it); + } + } + fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) { + trace!(?it.owner_id); + // The opaque type itself or its children are not within its reveal scope. + if it.owner_id.def_id != self.def_id { + self.check(it.owner_id.def_id); + intravisit::walk_impl_item(self, it); + } + } + fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) { + trace!(?it.owner_id); + self.check(it.owner_id.def_id); + intravisit::walk_trait_item(self, it); + } +} diff --git a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs index 6d1a1634ab4..ee3457282d3 100644 --- a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs @@ -395,7 +395,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { ) -> String { let fn_sig = self.tcx.hir().get_if_local(self.def_id).and_then(hir::Node::fn_sig); let is_used_in_input = |def_id| { - fn_sig.map_or(false, |fn_sig| { + fn_sig.is_some_and(|fn_sig| { fn_sig.decl.inputs.iter().any(|ty| match ty.kind { hir::TyKind::Path(hir::QPath::Resolved( None, diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index a92f368e083..98c683f0200 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -465,7 +465,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { .sess .source_map() .span_to_snippet(self.expr_span) - .map_or(false, |snip| snip.starts_with('(')); + .is_ok_and(|snip| snip.starts_with('(')); // Very crude check to see whether the expression must be wrapped // in parentheses for the suggestion to work (issue #89497). diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index bf8259ff70f..bfabd44bb57 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -32,6 +32,7 @@ pub(super) fn check_fn<'a, 'tcx>( fn_def_id: LocalDefId, body: &'tcx hir::Body<'tcx>, can_be_generator: Option<hir::Movability>, + params_can_be_unsized: bool, ) -> Option<GeneratorTypes<'tcx>> { let fn_id = fcx.tcx.hir().local_def_id_to_hir_id(fn_def_id); @@ -94,7 +95,7 @@ pub(super) fn check_fn<'a, 'tcx>( // The check for a non-trivial pattern is a hack to avoid duplicate warnings // for simple cases like `fn foo(x: Trait)`, // where we would error once on the parameter as a whole, and once on the binding `x`. - if param.pat.simple_ident().is_none() && !tcx.features().unsized_fn_params { + if param.pat.simple_ident().is_none() && !params_can_be_unsized { fcx.require_type_is_sized(param_ty, param.pat.span, traits::SizedArgumentType(ty_span)); } @@ -103,24 +104,8 @@ pub(super) fn check_fn<'a, 'tcx>( fcx.typeck_results.borrow_mut().liberated_fn_sigs_mut().insert(fn_id, fn_sig); - if let ty::Dynamic(_, _, ty::Dyn) = declared_ret_ty.kind() { - // FIXME: We need to verify that the return type is `Sized` after the return expression has - // been evaluated so that we have types available for all the nodes being returned, but that - // requires the coerced evaluated type to be stored. Moving `check_return_expr` before this - // causes unsized errors caused by the `declared_ret_ty` to point at the return expression, - // while keeping the current ordering we will ignore the tail expression's type because we - // don't know it yet. We can't do `check_expr_kind` while keeping `check_return_expr` - // because we will trigger "unreachable expression" lints unconditionally. - // Because of all of this, we perform a crude check to know whether the simplest `!Sized` - // case that a newcomer might make, returning a bare trait, and in that case we populate - // the tail expression's type so that the suggestion will be correct, but ignore all other - // possible cases. - fcx.check_expr(&body.value); - fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType); - } else { - fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType); - fcx.check_return_expr(&body.value, false); - } + fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType); + fcx.check_return_expr(&body.value, false); // We insert the deferred_generator_interiors entry after visiting the body. // This ensures that all nested generators appear before the entry of this generator. diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 7046269c2de..9659a0ec13d 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -89,6 +89,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr_def_id, body, closure.movability, + // Closure "rust-call" ABI doesn't support unsized params + false, ); let parent_substs = InternalSubsts::identity_for_item( diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index cfe8d59f737..08c4082e885 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1814,7 +1814,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { .span_to_snippet(return_sp) .unwrap_or_else(|_| "dyn Trait".to_string()); let mut snippet_iter = snippet.split_whitespace(); - let has_impl = snippet_iter.next().map_or(false, |s| s == "impl"); + let has_impl = snippet_iter.next().is_some_and(|s| s == "impl"); // Only suggest `Box<dyn Trait>` if `Trait` in `impl Trait` is object safe. let mut is_object_safe = false; if let hir::FnRetTy::Return(ty) = fn_output @@ -1834,7 +1834,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { bound .trait_ref() .and_then(|t| t.trait_def_id()) - .map_or(false, |def_id| { + .is_some_and(|def_id| { fcx.tcx.check_is_object_safe(def_id) }) }) diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 2defca54aff..b50630e636b 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -1748,8 +1748,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { |err: &mut Diagnostic, found_to_exp_is_fallible: bool, exp_to_found_is_fallible: bool| { - let exp_is_lhs = - expected_ty_expr.map(|e| self.tcx.hir().is_lhs(e.hir_id)).unwrap_or(false); + let exp_is_lhs = expected_ty_expr.is_some_and(|e| self.tcx.hir().is_lhs(e.hir_id)); if exp_is_lhs { return; diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index adc1b090af6..19ff77d8349 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -497,7 +497,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .borrow() .adjustments() .get(base.hir_id) - .map_or(false, |x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_)))) + .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_)))) }); if !is_named { self.tcx.sess.emit_err(AddressOfTemporaryTaken { span: oprnd.span }); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 039316c74dd..2fdcd09b9a2 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -35,7 +35,9 @@ use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Span; use rustc_target::abi::FieldIdx; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; -use rustc_trait_selection::traits::{self, NormalizeExt, ObligationCauseCode, ObligationCtxt}; +use rustc_trait_selection::traits::{ + self, NormalizeExt, ObligationCauseCode, ObligationCtxt, StructurallyNormalizeExt, +}; use std::collections::hash_map::Entry; use std::slice; @@ -1015,8 +1017,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .typeck_results .borrow() .expr_ty_adjusted_opt(rcvr) - .and_then(|ty| expected.map(|expected_ty| expected_ty.peel_refs() == ty.peel_refs())) - .unwrap_or(false); + .zip(expected) + .is_some_and(|(ty, expected_ty)| expected_ty.peel_refs() == ty.peel_refs()); let prev_call_mutates_and_returns_unit = || { self.typeck_results @@ -1024,14 +1026,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .type_dependent_def_id(expr.hir_id) .map(|def_id| self.tcx.fn_sig(def_id).skip_binder().skip_binder()) .and_then(|sig| sig.inputs_and_output.split_last()) - .map(|(output, inputs)| { + .is_some_and(|(output, inputs)| { output.is_unit() && inputs .get(0) .and_then(|self_ty| self_ty.ref_mutability()) - .map_or(false, rustc_ast::Mutability::is_mut) + .is_some_and(rustc_ast::Mutability::is_mut) }) - .unwrap_or(false) }; if !(rcvr_has_the_expected_type || prev_call_mutates_and_returns_unit()) { @@ -1198,10 +1199,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - let has_self = path_segs - .last() - .map(|PathSeg(def_id, _)| tcx.generics_of(*def_id).has_self) - .unwrap_or(false); + let has_self = + path_segs.last().is_some_and(|PathSeg(def_id, _)| tcx.generics_of(*def_id).has_self); let (res, self_ctor_substs) = if let Res::SelfCtor(impl_def_id) = res { let ty = self.handle_raw_ty(span, tcx.at(span).type_of(impl_def_id).subst_identity()); @@ -1460,10 +1459,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Resolves `typ` by a single level if `typ` is a type variable. + /// + /// When the new solver is enabled, this will also attempt to normalize + /// the type if it's a projection (note that it will not deeply normalize + /// projections within the type, just the outermost layer of the type). + /// /// If no resolution is possible, then an error is reported. /// Numeric inference variables may be left unresolved. pub fn structurally_resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> { - let ty = self.resolve_vars_with_obligations(ty); + let mut ty = self.resolve_vars_with_obligations(ty); + + if self.tcx.trait_solver_next() + && let ty::Alias(ty::Projection, _) = ty.kind() + { + match self + .at(&self.misc(sp), self.param_env) + .structurally_normalize(ty, &mut **self.fulfillment_cx.borrow_mut()) + { + Ok(normalized_ty) => { + ty = normalized_ty; + }, + Err(errors) => { + let guar = self.err_ctxt().report_fulfillment_errors(&errors); + return self.tcx.ty_error(guar); + } + } + } + if !ty.is_ty_var() { ty } else { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index c7011b23a7d..72c42f8e789 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -876,7 +876,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut errors = errors.into_iter().peekable(); let mut only_extras_so_far = errors .peek() - .map_or(false, |first| matches!(first, Error::Extra(arg_idx) if arg_idx.index() == 0)); + .is_some_and(|first| matches!(first, Error::Extra(arg_idx) if arg_idx.index() == 0)); let mut suggestions = vec![]; while let Some(error) = errors.next() { only_extras_so_far &= matches!(error, Error::Extra(_)); diff --git a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs index cd3966a1214..ecafbd668e2 100644 --- a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs @@ -193,7 +193,7 @@ impl DropRanges { .get(&TrackedValue::Temporary(hir_id)) .or(self.tracked_value_map.get(&TrackedValue::Variable(hir_id))) .cloned() - .map_or(false, |tracked_value_id| { + .is_some_and(|tracked_value_id| { self.expect_node(location.into()).drop_state.contains(tracked_value_id) }) } diff --git a/compiler/rustc_hir_typeck/src/inherited.rs b/compiler/rustc_hir_typeck/src/inherited.rs index 4110b176b41..294c3bb78a5 100644 --- a/compiler/rustc_hir_typeck/src/inherited.rs +++ b/compiler/rustc_hir_typeck/src/inherited.rs @@ -4,7 +4,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_hir::HirIdMap; -use rustc_infer::infer::{DefiningAnchor, InferCtxt, InferOk, TyCtxtInferExt}; +use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt}; +use rustc_middle::traits::DefiningAnchor; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::def_id::LocalDefIdMap; @@ -130,7 +131,7 @@ impl<'tcx> Inherited<'tcx> { // (*) binder skipped if let ty::PredicateKind::Clause(ty::Clause::Trait(tpred)) = obligation.predicate.kind().skip_binder() && let Some(ty) = self.shallow_resolve(tpred.self_ty()).ty_vid().map(|t| self.root_var(t)) - && self.tcx.lang_items().sized_trait().map_or(false, |st| st != tpred.trait_ref.def_id) + && self.tcx.lang_items().sized_trait().is_some_and(|st| st != tpred.trait_ref.def_id) { let new_self_ty = self.tcx.types.unit; diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 64426c4cbbb..b97b55d8f7e 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -212,7 +212,7 @@ fn typeck_with_fallback<'tcx>( let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig); let fn_sig = fcx.normalize(body.value.span, fn_sig); - check_fn(&mut fcx, fn_sig, decl, def_id, body, None); + check_fn(&mut fcx, fn_sig, decl, def_id, body, None, tcx.features().unsized_fn_params); } else { let expected_type = if let Some(&hir::Ty { kind: hir::TyKind::Infer, span, .. }) = body_ty { Some(fcx.next_ty_var(TypeVariableOrigin { diff --git a/compiler/rustc_hir_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs index f5fca14eca8..78171e0b20e 100644 --- a/compiler/rustc_hir_typeck/src/mem_categorization.rs +++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs @@ -411,7 +411,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { } Res::Local(var_id) => { - if self.upvars.map_or(false, |upvars| upvars.contains_key(&var_id)) { + if self.upvars.is_some_and(|upvars| upvars.contains_key(&var_id)) { self.cat_upvar(hir_id, var_id) } else { Ok(PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::Local(var_id), Vec::new())) diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 59bf45f0ed2..98529b66602 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -471,7 +471,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { self_ty, method_self_ty, self.span, pick ); let cause = self.cause( - self.span, + self.self_expr.span, ObligationCauseCode::UnifyReceiver(Box::new(UnifyReceiverContext { assoc_item: pick.item, param_env: self.param_env, @@ -482,13 +482,22 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { Ok(InferOk { obligations, value: () }) => { self.register_predicates(obligations); } - Err(_) => { - span_bug!( - self.span, - "{} was a subtype of {} but now is not?", - self_ty, - method_self_ty - ); + Err(terr) => { + // FIXME(arbitrary_self_types): We probably should limit the + // situations where this can occur by adding additional restrictions + // to the feature, like the self type can't reference method substs. + if self.tcx.features().arbitrary_self_types { + self.err_ctxt() + .report_mismatched_types(&cause, method_self_ty, self_ty, terr) + .emit(); + } else { + span_bug!( + self.span, + "{} was a subtype of {} but now is not?", + self_ty, + method_self_ty + ); + } } } } diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index f91f4f887c6..ba21edea30b 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -1194,7 +1194,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { pick.autoderefs += 1; pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::Autoref { mutbl, - unsize: pick.autoref_or_ptr_adjustment.map_or(false, |a| a.get_unsize()), + unsize: pick.autoref_or_ptr_adjustment.is_some_and(|a| a.get_unsize()), }) } diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 12bc17ca97c..8555c20204a 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -288,8 +288,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mode = no_match_data.mode; let tcx = self.tcx; let rcvr_ty = self.resolve_vars_if_possible(rcvr_ty); - let (ty_str, ty_file) = tcx.short_ty_string(rcvr_ty); - let short_ty_str = with_forced_trimmed_paths!(rcvr_ty.to_string()); + let ((mut ty_str, ty_file), short_ty_str) = if trait_missing_method + && let ty::Dynamic(predicates, _, _) = rcvr_ty.kind() { + ((predicates.to_string(), None), with_forced_trimmed_paths!(predicates.to_string())) + } else { + (tcx.short_ty_string(rcvr_ty), with_forced_trimmed_paths!(rcvr_ty.to_string())) + }; let is_method = mode == Mode::MethodCall; let unsatisfied_predicates = &no_match_data.unsatisfied_predicates; let similar_candidate = no_match_data.similar_candidate; @@ -329,12 +333,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span = item_name.span; // Don't show generic arguments when the method can't be found in any implementation (#81576). - let mut ty_str_reported = if trait_missing_method { - ty_str.strip_prefix("dyn ").expect("Failed to remove the prefix dyn").to_owned() - } else { - ty_str.clone() - }; - + let mut ty_str_reported = ty_str.clone(); if let ty::Adt(_, generics) = rcvr_ty.kind() { if generics.len() > 0 { let mut autoderef = self.autoderef(span, rcvr_ty); @@ -357,7 +356,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - let is_write = sugg_span.ctxt().outer_expn_data().macro_def_id.map_or(false, |def_id| { + let is_write = sugg_span.ctxt().outer_expn_data().macro_def_id.is_some_and(|def_id| { tcx.is_diagnostic_item(sym::write_macro, def_id) || tcx.is_diagnostic_item(sym::writeln_macro, def_id) }) && item_name.name == Symbol::intern("write_fmt"); @@ -383,14 +382,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if tcx.sess.source_map().is_multiline(sugg_span) { err.span_label(sugg_span.with_hi(span.lo()), ""); } - let mut ty_str = if short_ty_str.len() < ty_str.len() && ty_str.len() > 10 { - short_ty_str - } else { - ty_str - }; - if trait_missing_method { - ty_str = - ty_str.strip_prefix("dyn ").expect("Failed to remove the prefix dyn").to_owned(); + + if short_ty_str.len() < ty_str.len() && ty_str.len() > 10 { + ty_str = short_ty_str; } if let Some(file) = ty_file { @@ -1528,7 +1522,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let span_included = match parent_expr.kind { hir::ExprKind::Struct(_, eps, _) => { - eps.len() > 0 && eps.last().map_or(false, |ep| ep.span.contains(span)) + eps.len() > 0 && eps.last().is_some_and(|ep| ep.span.contains(span)) } // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. hir::ExprKind::Call(ref func, ..) => func.span.contains(span), @@ -1787,7 +1781,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ProbeScope::TraitsInScope, return_type, ) - .map_or(false, |pick| { + .is_ok_and(|pick| { !never_mention_traits .iter() .flatten() @@ -2474,7 +2468,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // implement the `AsRef` trait. let skip = skippable.contains(&did) || (("Pin::new" == *pre) && (sym::as_ref == item_name.name)) - || inputs_len.map_or(false, |inputs_len| pick.item.kind == ty::AssocKind::Fn && self.tcx.fn_sig(pick.item.def_id).skip_binder().skip_binder().inputs().len() != inputs_len); + || inputs_len.is_some_and(|inputs_len| pick.item.kind == ty::AssocKind::Fn && self.tcx.fn_sig(pick.item.def_id).skip_binder().skip_binder().inputs().len() != inputs_len); // Make sure the method is defined for the *actual* receiver: we don't // want to treat `Box<Self>` as a receiver if it only works because of // an autoderef to `&self` @@ -2761,7 +2755,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let imp = self.tcx.impl_trait_ref(imp_did).unwrap().subst_identity(); let imp_simp = simplify_type(self.tcx, imp.self_ty(), TreatParams::ForLookup); - imp_simp.map_or(false, |s| s == simp_rcvr_ty) + imp_simp.is_some_and(|s| s == simp_rcvr_ty) }) { explicitly_negative.push(candidate); @@ -2899,7 +2893,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match ty.kind() { ty::Adt(def, _) => def.did().is_local(), ty::Foreign(did) => did.is_local(), - ty::Dynamic(tr, ..) => tr.principal().map_or(false, |d| d.def_id().is_local()), + ty::Dynamic(tr, ..) => tr.principal().is_some_and(|d| d.def_id().is_local()), ty::Param(_) => true, // Everything else (primitive types, etc.) is effectively diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index af351a3fa10..b8bf2b69120 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -549,9 +549,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let to_owned_msg = "create an owned `String` from a string reference"; let string_type = self.tcx.lang_items().string(); - let is_std_string = |ty: Ty<'tcx>| { - ty.ty_adt_def().map_or(false, |ty_def| Some(ty_def.did()) == string_type) - }; + let is_std_string = + |ty: Ty<'tcx>| ty.ty_adt_def().is_some_and(|ty_def| Some(ty_def.did()) == string_type); match (lhs_ty.kind(), rhs_ty.kind()) { (&Ref(_, l_ty, _), &Ref(_, r_ty, _)) // &str or &String + &str, &String or &&str @@ -760,8 +759,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, traits::BinOp { rhs_span: opt_rhs_expr.map(|expr| expr.span), - is_lit: opt_rhs_expr - .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))), + is_lit: opt_rhs_expr.is_some_and(|expr| matches!(expr.kind, hir::ExprKind::Lit(_))), output_ty: expected.only_has_type(self), }, ); diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index f217c5c1e1c..e2b1dc007ba 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -329,7 +329,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If this is a union field, also throw an error for `DerefMut` of `ManuallyDrop` (see RFC 2514). // This helps avoid accidental drops. if inside_union - && source.ty_adt_def().map_or(false, |adt| adt.is_manually_drop()) + && source.ty_adt_def().is_some_and(|adt| adt.is_manually_drop()) { let mut err = self.tcx.sess.struct_span_err( expr.span, diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 543194ac9d6..9458099f56f 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -972,15 +972,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut obligations_should_hold = Vec::new(); // Checks if a root variable implements any of the auto traits for check_trait in auto_traits_def_id.iter() { - obligations_should_hold.push( - check_trait - .map(|check_trait| { - self.infcx - .type_implements_trait(check_trait, [ty], self.param_env) - .must_apply_modulo_regions() - }) - .unwrap_or(false), - ); + obligations_should_hold.push(check_trait.is_some_and(|check_trait| { + self.infcx + .type_implements_trait(check_trait, [ty], self.param_env) + .must_apply_modulo_regions() + })); } let mut problematic_captures = FxHashMap::default(); @@ -996,15 +992,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Checks if a capture implements any of the auto traits let mut obligations_holds_for_capture = Vec::new(); for check_trait in auto_traits_def_id.iter() { - obligations_holds_for_capture.push( - check_trait - .map(|check_trait| { - self.infcx - .type_implements_trait(check_trait, [ty], self.param_env) - .must_apply_modulo_regions() - }) - .unwrap_or(false), - ); + obligations_holds_for_capture.push(check_trait.is_some_and(|check_trait| { + self.infcx + .type_implements_trait(check_trait, [ty], self.param_env) + .must_apply_modulo_regions() + })); } let mut capture_problems = FxHashSet::default(); diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index eed3c65eccc..0f21fc1e662 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -5,11 +5,10 @@ use crate::FnCtxt; use hir::def_id::LocalDefId; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::ErrorGuaranteed; +use rustc_errors::{ErrorGuaranteed, StashKey}; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; -use rustc_infer::infer::InferCtxt; use rustc_middle::hir::place::Place as HirPlace; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast}; @@ -83,10 +82,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { wbcx.typeck_results.treat_byte_string_as_slice = mem::take(&mut self.typeck_results.borrow_mut().treat_byte_string_as_slice); - if let Some(e) = self.tainted_by_errors() { - wbcx.typeck_results.tainted_by_errors = Some(e); - } - debug!("writeback: typeck results for {:?} are {:#?}", item_def_id, wbcx.typeck_results); self.tcx.arena.alloc(wbcx.typeck_results) @@ -119,12 +114,21 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { ) -> WritebackCx<'cx, 'tcx> { let owner = body.id().hir_id.owner; - WritebackCx { + let mut wbcx = WritebackCx { fcx, typeck_results: ty::TypeckResults::new(owner), body, rustc_dump_user_substs, + }; + + // HACK: We specifically don't want the (opaque) error from tainting our + // inference context. That'll prevent us from doing opaque type inference + // later on in borrowck, which affects diagnostic spans pretty negatively. + if let Some(e) = fcx.tainted_by_errors() { + wbcx.typeck_results.tainted_by_errors = Some(e); } + + wbcx } fn tcx(&self) -> TyCtxt<'tcx> { @@ -579,13 +583,26 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { continue; } - let hidden_type = hidden_type.remap_generic_params_to_declaration_params( - opaque_type_key, - self.fcx.infcx.tcx, - true, - ); - - self.typeck_results.concrete_opaque_types.insert(opaque_type_key.def_id, hidden_type); + let hidden_type = + self.tcx().erase_regions(hidden_type.remap_generic_params_to_declaration_params( + opaque_type_key, + self.tcx(), + true, + )); + + if let Some(last_opaque_ty) = self + .typeck_results + .concrete_opaque_types + .insert(opaque_type_key.def_id, hidden_type) + && last_opaque_ty.ty != hidden_type.ty + { + hidden_type + .report_mismatch(&last_opaque_ty, opaque_type_key.def_id, self.tcx()) + .stash( + self.tcx().def_span(opaque_type_key.def_id), + StashKey::OpaqueHiddenTypeMismatch, + ); + } } } @@ -692,15 +709,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { fcx_typeck_results.offset_of_data().items_in_stable_order() { let hir_id = hir::HirId { owner: common_hir_owner, local_id }; - - if cfg!(debug_assertions) && container.has_infer() { - span_bug!( - hir_id.to_span(self.fcx.tcx), - "writeback: `{:?}` has inference variables", - container - ); - }; - + let container = self.resolve(container, &hir_id); self.typeck_results.offset_of_data_mut().insert(hir_id, (container, indices.clone())); } } @@ -745,8 +754,7 @@ impl Locatable for hir::HirId { /// The Resolver. This is the type folding engine that detects /// unresolved types and so forth. struct Resolver<'cx, 'tcx> { - tcx: TyCtxt<'tcx>, - infcx: &'cx InferCtxt<'tcx>, + fcx: &'cx FnCtxt<'cx, 'tcx>, span: &'cx dyn Locatable, body: &'tcx hir::Body<'tcx>, @@ -760,18 +768,18 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { span: &'cx dyn Locatable, body: &'tcx hir::Body<'tcx>, ) -> Resolver<'cx, 'tcx> { - Resolver { tcx: fcx.tcx, infcx: fcx, span, body, replaced_with_error: None } + Resolver { fcx, span, body, replaced_with_error: None } } fn report_error(&self, p: impl Into<ty::GenericArg<'tcx>>) -> ErrorGuaranteed { - match self.tcx.sess.has_errors() { + match self.fcx.tcx.sess.has_errors() { Some(e) => e, None => self - .infcx + .fcx .err_ctxt() .emit_inference_failure_err( - self.tcx.hir().body_owner_def_id(self.body.id()), - self.span.to_span(self.tcx), + self.fcx.tcx.hir().body_owner_def_id(self.body.id()), + self.span.to_span(self.fcx.tcx), p.into(), E0282, false, @@ -803,40 +811,46 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for EraseEarlyRegions<'tcx> { impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Resolver<'cx, 'tcx> { fn interner(&self) -> TyCtxt<'tcx> { - self.tcx + self.fcx.tcx } fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - match self.infcx.fully_resolve(t) { + match self.fcx.fully_resolve(t) { + Ok(t) if self.fcx.tcx.trait_solver_next() => { + // We must normalize erasing regions here, since later lints + // expect that types that show up in the typeck are fully + // normalized. + self.fcx.tcx.try_normalize_erasing_regions(self.fcx.param_env, t).unwrap_or(t) + } Ok(t) => { // Do not anonymize late-bound regions // (e.g. keep `for<'a>` named `for<'a>`). // This allows NLL to generate error messages that // refer to the higher-ranked lifetime names written by the user. - EraseEarlyRegions { tcx: self.tcx }.fold_ty(t) + EraseEarlyRegions { tcx: self.fcx.tcx }.fold_ty(t) } Err(_) => { debug!("Resolver::fold_ty: input type `{:?}` not fully resolvable", t); let e = self.report_error(t); self.replaced_with_error = Some(e); - self.interner().ty_error(e) + self.fcx.tcx.ty_error(e) } } } fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { debug_assert!(!r.is_late_bound(), "Should not be resolving bound region."); - self.tcx.lifetimes.re_erased + self.fcx.tcx.lifetimes.re_erased } fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - match self.infcx.fully_resolve(ct) { - Ok(ct) => self.tcx.erase_regions(ct), + match self.fcx.fully_resolve(ct) { + Ok(ct) => self.fcx.tcx.erase_regions(ct), Err(_) => { debug!("Resolver::fold_const: input const `{:?}` not fully resolvable", ct); let e = self.report_error(ct); self.replaced_with_error = Some(e); - self.interner().const_error(ct.ty(), e) + self.fcx.tcx.const_error(ct.ty(), e) } } } diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index a1ab9c8c5ec..15bc3b4e388 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -1544,7 +1544,7 @@ impl<T: Idx> GrowableBitSet<T> { #[inline] pub fn contains(&self, elem: T) -> bool { let (word_index, mask) = word_index_and_mask(elem); - self.bit_set.words.get(word_index).map_or(false, |word| (word & mask) != 0) + self.bit_set.words.get(word_index).is_some_and(|word| (word & mask) != 0) } #[inline] @@ -1818,7 +1818,7 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> { /// if the matrix represents (transitive) reachability, can /// `row` reach `column`? pub fn contains(&self, row: R, column: C) -> bool { - self.row(row).map_or(false, |r| r.contains(column)) + self.row(row).is_some_and(|r| r.contains(column)) } /// Adds the bits from row `read` to the bits from row `write`, and diff --git a/compiler/rustc_index/src/interval.rs b/compiler/rustc_index/src/interval.rs index 7ed4860c274..d3cf267dc9d 100644 --- a/compiler/rustc_index/src/interval.rs +++ b/compiler/rustc_index/src/interval.rs @@ -181,6 +181,30 @@ impl<I: Idx> IntervalSet<I> { self.map.is_empty() } + /// Equivalent to `range.iter().find(|i| !self.contains(i))`. + pub fn first_unset_in(&self, range: impl RangeBounds<I> + Clone) -> Option<I> { + let start = inclusive_start(range.clone()); + let Some(end) = inclusive_end(self.domain, range) else { + // empty range + return None; + }; + if start > end { + return None; + } + let Some(last) = self.map.partition_point(|r| r.0 <= start).checked_sub(1) else { + // All ranges in the map start after the new range's end + return Some(I::new(start as usize)); + }; + let (_, prev_end) = self.map[last]; + if start > prev_end { + Some(I::new(start as usize)) + } else if prev_end < end { + Some(I::new(prev_end as usize + 1)) + } else { + None + } + } + /// Returns the maximum (last) element present in the set from `range`. pub fn last_set_in(&self, range: impl RangeBounds<I> + Clone) -> Option<I> { let start = inclusive_start(range.clone()); @@ -224,7 +248,7 @@ impl<I: Idx> IntervalSet<I> { fn check_invariants(&self) -> bool { let mut current: Option<u32> = None; for (start, end) in &self.map { - if start > end || current.map_or(false, |x| x + 1 >= *start) { + if start > end || current.is_some_and(|x| x + 1 >= *start) { return false; } current = Some(*end); @@ -297,6 +321,6 @@ impl<R: Idx, C: Step + Idx> SparseIntervalMatrix<R, C> { } pub fn contains(&self, row: R, point: C) -> bool { - self.row(row).map_or(false, |r| r.contains(point)) + self.row(row).is_some_and(|r| r.contains(point)) } } diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 3605e10fecd..de9afbbcaab 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -153,20 +153,22 @@ impl<'tcx> InferCtxt<'tcx> { /// Used by the new solver as that one takes the opaque types at the end of a probe /// to deal with multiple candidates without having to recompute them. - pub fn clone_opaque_types_for_query_response(&self) -> Vec<(Ty<'tcx>, Ty<'tcx>)> { + pub fn clone_opaque_types_for_query_response( + &self, + ) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> { self.inner .borrow() .opaque_type_storage .opaque_types .iter() - .map(|(k, v)| (self.tcx.mk_opaque(k.def_id.to_def_id(), k.substs), v.hidden_type.ty)) + .map(|(k, v)| (*k, v.hidden_type.ty)) .collect() } - fn take_opaque_types_for_query_response(&self) -> Vec<(Ty<'tcx>, Ty<'tcx>)> { + fn take_opaque_types_for_query_response(&self) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> { std::mem::take(&mut self.inner.borrow_mut().opaque_type_storage.opaque_types) .into_iter() - .map(|(k, v)| (self.tcx.mk_opaque(k.def_id.to_def_id(), k.substs), v.hidden_type.ty)) + .map(|(k, v)| (k, v.hidden_type.ty)) .collect() } @@ -507,8 +509,22 @@ impl<'tcx> InferCtxt<'tcx> { let a = substitute_value(self.tcx, &result_subst, a); let b = substitute_value(self.tcx, &result_subst, b); debug!(?a, ?b, "constrain opaque type"); - obligations - .extend(self.at(cause, param_env).eq(DefineOpaqueTypes::Yes, a, b)?.obligations); + // We use equate here instead of, for example, just registering the + // opaque type's hidden value directly, because we may be instantiating + // a query response that was canonicalized in an InferCtxt that had + // a different defining anchor. In that case, we may have inferred + // `NonLocalOpaque := LocalOpaque` but can only instantiate it in + // the other direction as `LocalOpaque := NonLocalOpaque`. Using eq + // here allows us to try both directions (in `InferCtxt::handle_opaque_type`). + obligations.extend( + self.at(cause, param_env) + .eq( + DefineOpaqueTypes::Yes, + self.tcx.mk_opaque(a.def_id.to_def_id(), a.substs), + b, + )? + .obligations, + ); } Ok(InferOk { value: result_subst, obligations }) diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index 79fc02c6c79..b6b935de68c 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -113,10 +113,7 @@ impl<'tcx> InferCtxt<'tcx> { bug!() } - (_, ty::Alias(AliasKind::Projection | AliasKind::Inherent, _)) - | (ty::Alias(AliasKind::Projection | AliasKind::Inherent, _), _) - if self.tcx.trait_solver_next() => - { + (_, ty::Alias(..)) | (ty::Alias(..), _) if self.tcx.trait_solver_next() => { relation.register_type_relate_obligation(a, b); Ok(a) } diff --git a/compiler/rustc_infer/src/infer/equate.rs b/compiler/rustc_infer/src/infer/equate.rs index 793505e4ab2..42dfe4f6bb8 100644 --- a/compiler/rustc_infer/src/infer/equate.rs +++ b/compiler/rustc_infer/src/infer/equate.rs @@ -104,7 +104,8 @@ impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> { (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _) | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) if self.fields.define_opaque_types == DefineOpaqueTypes::Yes - && def_id.is_local() => + && def_id.is_local() + && !self.tcx().trait_solver_next() => { self.fields.obligations.extend( infcx diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index ad4f5058b5e..35c05e80bad 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -1825,7 +1825,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { s }; if !(values.expected.is_simple_text() && values.found.is_simple_text()) - || (exp_found.map_or(false, |ef| { + || (exp_found.is_some_and(|ef| { // This happens when the type error is a subset of the expectation, // like when you have two references but one is `usize` and the other // is `f32`. In those cases we still want to show the `note`. If the @@ -1877,7 +1877,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let exp_found = match terr { // `terr` has more accurate type information than `exp_found` in match expressions. ty::error::TypeError::Sorts(terr) - if exp_found.map_or(false, |ef| terr.found == ef.found) => + if exp_found.is_some_and(|ef| terr.found == ef.found) => { Some(terr) } @@ -1961,7 +1961,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) && let Some(code) = code.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) && !code.starts_with("\\u") // forbid all Unicode escapes - && code.chars().next().map_or(false, |c| c.is_ascii()) // forbids literal Unicode characters beyond ASCII + && code.chars().next().is_some_and(|c| c.is_ascii()) // forbids literal Unicode characters beyond ASCII { suggestions.push(TypeErrorAdditionalDiags::MeantByteLiteral { span, code: escape_literal(code) }) } @@ -2329,7 +2329,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { .source_map() .span_to_prev_source(p.span.shrink_to_hi()) .ok() - .map_or(false, |s| *s.as_bytes().last().unwrap() == b'&') + .is_some_and(|s| *s.as_bytes().last().unwrap() == b'&') { add_lt_suggs .push(Some( diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index 5000b0139df..f3b2ec4c5e3 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -671,7 +671,7 @@ impl<'tcx> InferSource<'tcx> { receiver.span.from_expansion() } InferSourceKind::ClosureReturn { data, should_wrap_expr, .. } => { - data.span().from_expansion() || should_wrap_expr.map_or(false, Span::from_expansion) + data.span().from_expansion() || should_wrap_expr.is_some_and(Span::from_expansion) } }; source_from_expansion || self.span.from_expansion() @@ -984,7 +984,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { ) -> impl Iterator<Item = InsertableGenericArgs<'tcx>> + 'a { let tcx = self.infcx.tcx; let have_turbofish = path.segments.iter().any(|segment| { - segment.args.map_or(false, |args| args.args.iter().any(|arg| arg.is_ty_or_const())) + segment.args.is_some_and(|args| args.args.iter().any(|arg| arg.is_ty_or_const())) }); // The last segment of a path often has `Res::Err` and the // correct `Res` is the one of the whole path. diff --git a/compiler/rustc_infer/src/infer/lattice.rs b/compiler/rustc_infer/src/infer/lattice.rs index 7f4c141b97a..7190d33d299 100644 --- a/compiler/rustc_infer/src/infer/lattice.rs +++ b/compiler/rustc_infer/src/infer/lattice.rs @@ -108,9 +108,12 @@ where &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }), &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }), ) if a_def_id == b_def_id => infcx.super_combine_tys(this, a, b), + (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _) | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) - if this.define_opaque_types() == DefineOpaqueTypes::Yes && def_id.is_local() => + if this.define_opaque_types() == DefineOpaqueTypes::Yes + && def_id.is_local() + && !this.tcx().trait_solver_next() => { this.register_obligations( infcx diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index f8329965c43..cd99fc31212 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -24,7 +24,7 @@ use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType}; use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult}; use rustc_middle::mir::ConstraintCategory; -use rustc_middle::traits::select; +use rustc_middle::traits::{select, DefiningAnchor}; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::BoundVarReplacerDelegate; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; @@ -231,17 +231,6 @@ impl<'tcx> InferCtxtInner<'tcx> { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum DefiningAnchor { - /// `DefId` of the item. - Bind(LocalDefId), - /// When opaque types are not resolved, we `Bubble` up, meaning - /// return the opaque/hidden type pair from query, for caller of query to handle it. - Bubble, - /// Used to catch type mismatch errors when handling opaque types. - Error, -} - pub struct InferCtxt<'tcx> { pub tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs index 4ae6af5f5be..d3fd01b9642 100644 --- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs +++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs @@ -491,16 +491,22 @@ where ( &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }), &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }), - ) if a_def_id == b_def_id => infcx.super_combine_tys(self, a, b).or_else(|err| { - self.tcx().sess.delay_span_bug( - self.delegate.span(), - "failure to relate an opaque to itself should result in an error later on", - ); - if a_def_id.is_local() { self.relate_opaques(a, b) } else { Err(err) } - }), + ) if a_def_id == b_def_id || infcx.tcx.trait_solver_next() => { + infcx.super_combine_tys(self, a, b).or_else(|err| { + // This behavior is only there for the old solver, the new solver + // shouldn't ever fail. Instead, it unconditionally emits an + // alias-relate goal. + assert!(!self.tcx().trait_solver_next()); + self.tcx().sess.delay_span_bug( + self.delegate.span(), + "failure to relate an opaque to itself should result in an error later on", + ); + if a_def_id.is_local() { self.relate_opaques(a, b) } else { Err(err) } + }) + } (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _) | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) - if def_id.is_local() => + if def_id.is_local() && !self.tcx().trait_solver_next() => { self.relate_opaques(a, b) } diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index 545310ad351..9d5ec228d82 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -1,14 +1,14 @@ use super::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use super::{DefineOpaqueTypes, InferResult}; use crate::errors::OpaqueHiddenTypeDiag; -use crate::infer::{DefiningAnchor, InferCtxt, InferOk}; -use crate::traits; +use crate::infer::{InferCtxt, InferOk}; +use crate::traits::{self, PredicateObligation}; use hir::def_id::{DefId, LocalDefId}; use hir::OpaqueTyOrigin; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::Lrc; use rustc_hir as hir; -use rustc_middle::traits::ObligationCause; +use rustc_middle::traits::{DefiningAnchor, ObligationCause}; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::BottomUpFolder; use rustc_middle::ty::GenericArgKind; @@ -48,12 +48,18 @@ impl<'tcx> InferCtxt<'tcx> { span: Span, param_env: ty::ParamEnv<'tcx>, ) -> InferOk<'tcx, T> { + // We handle opaque types differently in the new solver. + if self.tcx.trait_solver_next() { + return InferOk { value, obligations: vec![] }; + } + if !value.has_opaque_types() { return InferOk { value, obligations: vec![] }; } + let mut obligations = vec![]; let replace_opaque_type = |def_id: DefId| { - def_id.as_local().map_or(false, |def_id| self.opaque_type_origin(def_id).is_some()) + def_id.as_local().is_some_and(|def_id| self.opaque_type_origin(def_id).is_some()) }; let value = value.fold_with(&mut BottomUpFolder { tcx: self.tcx, @@ -521,29 +527,68 @@ impl<'tcx> InferCtxt<'tcx> { origin: hir::OpaqueTyOrigin, a_is_expected: bool, ) -> InferResult<'tcx, ()> { - let tcx = self.tcx; - let OpaqueTypeKey { def_id, substs } = opaque_type_key; - // Ideally, we'd get the span where *this specific `ty` came // from*, but right now we just use the span from the overall // value being folded. In simple cases like `-> impl Foo`, // these are the same span, but not in cases like `-> (impl // Foo, impl Bar)`. let span = cause.span; - - let mut obligations = vec![]; let prev = self.inner.borrow_mut().opaque_types().register( - OpaqueTypeKey { def_id, substs }, + opaque_type_key, OpaqueHiddenType { ty: hidden_ty, span }, origin, ); - if let Some(prev) = prev { - obligations = self - .at(&cause, param_env) + let mut obligations = if let Some(prev) = prev { + self.at(&cause, param_env) .eq_exp(DefineOpaqueTypes::Yes, a_is_expected, prev, hidden_ty)? - .obligations; - } + .obligations + } else { + Vec::new() + }; + + self.add_item_bounds_for_hidden_type( + opaque_type_key, + cause, + param_env, + hidden_ty, + &mut obligations, + ); + + Ok(InferOk { value: (), obligations }) + } + /// Registers an opaque's hidden type -- only should be used when the opaque + /// can be defined. For something more fallible -- checks the anchors, tries + /// to unify opaques in both dirs, etc. -- use `InferCtxt::handle_opaque_type`. + pub fn register_hidden_type_in_new_solver( + &self, + opaque_type_key: OpaqueTypeKey<'tcx>, + param_env: ty::ParamEnv<'tcx>, + hidden_ty: Ty<'tcx>, + ) -> InferResult<'tcx, ()> { + assert!(self.tcx.trait_solver_next()); + let origin = self + .opaque_type_origin(opaque_type_key.def_id) + .expect("should be called for defining usages only"); + self.register_hidden_type( + opaque_type_key, + ObligationCause::dummy(), + param_env, + hidden_ty, + origin, + true, + ) + } + + pub fn add_item_bounds_for_hidden_type( + &self, + OpaqueTypeKey { def_id, substs }: OpaqueTypeKey<'tcx>, + cause: ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + hidden_ty: Ty<'tcx>, + obligations: &mut Vec<PredicateObligation<'tcx>>, + ) { + let tcx = self.tcx; let item_bounds = tcx.explicit_item_bounds(def_id); for (predicate, _) in item_bounds.subst_iter_copied(tcx, substs) { @@ -556,14 +601,15 @@ impl<'tcx> InferCtxt<'tcx> { // FIXME(inherent_associated_types): Extend this to support `ty::Inherent`, too. ty::Alias(ty::Projection, projection_ty) if !projection_ty.has_escaping_bound_vars() - && !tcx.is_impl_trait_in_trait(projection_ty.def_id) => + && !tcx.is_impl_trait_in_trait(projection_ty.def_id) + && !tcx.trait_solver_next() => { self.infer_projection( param_env, projection_ty, cause.clone(), 0, - &mut obligations, + obligations, ) } // Replace all other mentions of the same opaque type with the hidden type, @@ -589,10 +635,10 @@ impl<'tcx> InferCtxt<'tcx> { predicate.kind().skip_binder() { if projection.term.references_error() { - // No point on adding these obligations since there's a type error involved. - return Ok(InferOk { value: (), obligations: vec![] }); + // No point on adding any obligations since there's a type error involved. + obligations.clear(); + return; } - trace!("{:#?}", projection.term); } // Require that the predicate holds for the concrete type. debug!(?predicate); @@ -603,7 +649,6 @@ impl<'tcx> InferCtxt<'tcx> { predicate, )); } - Ok(InferOk { value: (), obligations }) } } diff --git a/compiler/rustc_infer/src/infer/sub.rs b/compiler/rustc_infer/src/infer/sub.rs index e0f29a8de8f..ceafafb5582 100644 --- a/compiler/rustc_infer/src/infer/sub.rs +++ b/compiler/rustc_infer/src/infer/sub.rs @@ -131,7 +131,8 @@ impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> { (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _) | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) if self.fields.define_opaque_types == DefineOpaqueTypes::Yes - && def_id.is_local() => + && def_id.is_local() + && !self.tcx().trait_solver_next() => { self.fields.obligations.extend( infcx diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 681819703c2..39d56897999 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -3,9 +3,9 @@ use crate::util; use rustc_ast::token; use rustc_ast::{self as ast, LitKind, MetaItemKind}; use rustc_codegen_ssa::traits::CodegenBackend; +use rustc_data_structures::defer; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::Lrc; -use rustc_data_structures::OnDrop; use rustc_errors::registry::Registry; use rustc_errors::{ErrorGuaranteed, Handler}; use rustc_lint::LintStore; @@ -325,7 +325,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se rustc_span::set_source_map(compiler.sess.parse_sess.clone_source_map(), move || { let r = { - let _sess_abort_error = OnDrop(|| { + let _sess_abort_error = defer(|| { compiler.sess.finish_diagnostics(registry); }); diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index ec3a782e7d2..42d8d228091 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -487,6 +487,11 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P files.push(normalize_path(profile_sample.as_path().to_path_buf())); } + // Debugger visualizer files + for debugger_visualizer in tcx.debugger_visualizers(LOCAL_CRATE) { + files.push(normalize_path(debugger_visualizer.path.clone().unwrap())); + } + if sess.binary_dep_depinfo() { if let Some(ref backend) = sess.opts.unstable_opts.codegen_backend { if backend.contains('.') { diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index a5639404faf..e1658d3ff82 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -521,18 +521,18 @@ lint_opaque_hidden_inferred_bound = opaque type `{$ty}` does not satisfy its ass lint_opaque_hidden_inferred_bound_sugg = add this bound -lint_drop_ref = calls to `std::mem::drop` with a reference instead of an owned value does nothing +lint_dropping_references = calls to `std::mem::drop` with a reference instead of an owned value does nothing .label = argument has type `{$arg_ty}` .note = use `let _ = ...` to ignore the expression or result -lint_drop_copy = calls to `std::mem::drop` with a value that implements `Copy` does nothing +lint_dropping_copy_types = calls to `std::mem::drop` with a value that implements `Copy` does nothing .label = argument has type `{$arg_ty}` .note = use `let _ = ...` to ignore the expression or result -lint_forget_ref = calls to `std::mem::forget` with a reference instead of an owned value does nothing +lint_forgetting_references = calls to `std::mem::forget` with a reference instead of an owned value does nothing .label = argument has type `{$arg_ty}` .note = use `let _ = ...` to ignore the expression or result -lint_forget_copy = calls to `std::mem::forget` with a value that implements `Copy` does nothing +lint_forgetting_copy_types = calls to `std::mem::forget` with a value that implements `Copy` does nothing .label = argument has type `{$arg_ty}` .note = use `let _ = ...` to ignore the expression or result diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 6601a80920b..358d412a4d8 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -961,7 +961,7 @@ fn warn_if_doc(cx: &EarlyContext<'_>, node_span: Span, node_kind: &str, attrs: & Some(sugared_span.map_or(attr.span, |span| span.with_hi(attr.span.hi()))); } - if attrs.peek().map_or(false, |next_attr| next_attr.is_doc_comment()) { + if attrs.peek().is_some_and(|next_attr| next_attr.is_doc_comment()) { continue; } diff --git a/compiler/rustc_lint/src/drop_forget_useless.rs b/compiler/rustc_lint/src/drop_forget_useless.rs index 259abc2af11..ed2b384805e 100644 --- a/compiler/rustc_lint/src/drop_forget_useless.rs +++ b/compiler/rustc_lint/src/drop_forget_useless.rs @@ -7,7 +7,7 @@ use crate::{ }; declare_lint! { - /// The `drop_ref` lint checks for calls to `std::mem::drop` with a reference + /// The `dropping_references` lint checks for calls to `std::mem::drop` with a reference /// instead of an owned value. /// /// ### Example @@ -29,13 +29,13 @@ declare_lint! { /// reference itself, which is a no-op. It will not call the `drop` method (from /// the `Drop` trait implementation) on the underlying referenced value, which /// is likely what was intended. - pub DROP_REF, + pub DROPPING_REFERENCES, Warn, "calls to `std::mem::drop` with a reference instead of an owned value" } declare_lint! { - /// The `forget_ref` lint checks for calls to `std::mem::forget` with a reference + /// The `forgetting_references` lint checks for calls to `std::mem::forget` with a reference /// instead of an owned value. /// /// ### Example @@ -52,13 +52,13 @@ declare_lint! { /// Calling `forget` on a reference will only forget the /// reference itself, which is a no-op. It will not forget the underlying /// referenced value, which is likely what was intended. - pub FORGET_REF, + pub FORGETTING_REFERENCES, Warn, "calls to `std::mem::forget` with a reference instead of an owned value" } declare_lint! { - /// The `drop_copy` lint checks for calls to `std::mem::drop` with a value + /// The `dropping_copy_types` lint checks for calls to `std::mem::drop` with a value /// that derives the Copy trait. /// /// ### Example @@ -76,13 +76,13 @@ declare_lint! { /// Calling `std::mem::drop` [does nothing for types that /// implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html), since the /// value will be copied and moved into the function on invocation. - pub DROP_COPY, + pub DROPPING_COPY_TYPES, Warn, "calls to `std::mem::drop` with a value that implements Copy" } declare_lint! { - /// The `forget_copy` lint checks for calls to `std::mem::forget` with a value + /// The `forgetting_copy_types` lint checks for calls to `std::mem::forget` with a value /// that derives the Copy trait. /// /// ### Example @@ -104,12 +104,12 @@ declare_lint! { /// An alternative, but also valid, explanation is that Copy types do not /// implement the Drop trait, which means they have no destructors. Without a /// destructor, there is nothing for `std::mem::forget` to ignore. - pub FORGET_COPY, + pub FORGETTING_COPY_TYPES, Warn, "calls to `std::mem::forget` with a value that implements Copy" } -declare_lint_pass!(DropForgetUseless => [DROP_REF, FORGET_REF, DROP_COPY, FORGET_COPY]); +declare_lint_pass!(DropForgetUseless => [DROPPING_REFERENCES, FORGETTING_REFERENCES, DROPPING_COPY_TYPES, FORGETTING_COPY_TYPES]); impl<'tcx> LateLintPass<'tcx> for DropForgetUseless { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { @@ -123,16 +123,16 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetUseless { let drop_is_single_call_in_arm = is_single_call_in_arm(cx, arg, expr); match fn_name { sym::mem_drop if arg_ty.is_ref() && !drop_is_single_call_in_arm => { - cx.emit_spanned_lint(DROP_REF, expr.span, DropRefDiag { arg_ty, label: arg.span }); + cx.emit_spanned_lint(DROPPING_REFERENCES, expr.span, DropRefDiag { arg_ty, label: arg.span }); }, sym::mem_forget if arg_ty.is_ref() => { - cx.emit_spanned_lint(FORGET_REF, expr.span, ForgetRefDiag { arg_ty, label: arg.span }); + cx.emit_spanned_lint(FORGETTING_REFERENCES, expr.span, ForgetRefDiag { arg_ty, label: arg.span }); }, sym::mem_drop if is_copy && !drop_is_single_call_in_arm => { - cx.emit_spanned_lint(DROP_COPY, expr.span, DropCopyDiag { arg_ty, label: arg.span }); + cx.emit_spanned_lint(DROPPING_COPY_TYPES, expr.span, DropCopyDiag { arg_ty, label: arg.span }); } sym::mem_forget if is_copy => { - cx.emit_spanned_lint(FORGET_COPY, expr.span, ForgetCopyDiag { arg_ty, label: arg.span }); + cx.emit_spanned_lint(FORGETTING_COPY_TYPES, expr.span, ForgetCopyDiag { arg_ty, label: arg.span }); } _ => return, }; diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 0082aaa4a38..6f773e04a97 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -383,9 +383,8 @@ impl LateLintPass<'_> for Diagnostics { debug!(?span, ?def_id, ?substs); let has_attr = ty::Instance::resolve(cx.tcx, cx.param_env, def_id, substs) .ok() - .and_then(|inst| inst) - .map(|inst| cx.tcx.has_attr(inst.def_id(), sym::rustc_lint_diagnostics)) - .unwrap_or(false); + .flatten() + .is_some_and(|inst| cx.tcx.has_attr(inst.def_id(), sym::rustc_lint_diagnostics)); if !has_attr { return; } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 8e48806b504..de1c2be2875 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -662,9 +662,9 @@ pub struct ForLoopsOverFalliblesSuggestion<'a> { pub end_span: Span, } -// drop_ref.rs +// drop_forget_useless.rs #[derive(LintDiagnostic)] -#[diag(lint_drop_ref)] +#[diag(lint_dropping_references)] #[note] pub struct DropRefDiag<'a> { pub arg_ty: Ty<'a>, @@ -673,7 +673,7 @@ pub struct DropRefDiag<'a> { } #[derive(LintDiagnostic)] -#[diag(lint_drop_copy)] +#[diag(lint_dropping_copy_types)] #[note] pub struct DropCopyDiag<'a> { pub arg_ty: Ty<'a>, @@ -682,7 +682,7 @@ pub struct DropCopyDiag<'a> { } #[derive(LintDiagnostic)] -#[diag(lint_forget_ref)] +#[diag(lint_forgetting_references)] #[note] pub struct ForgetRefDiag<'a> { pub arg_ty: Ty<'a>, @@ -691,7 +691,7 @@ pub struct ForgetRefDiag<'a> { } #[derive(LintDiagnostic)] -#[diag(lint_forget_copy)] +#[diag(lint_forgetting_copy_types)] #[note] pub struct ForgetCopyDiag<'a> { pub arg_ty: Ty<'a>, diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs index 5bb1abfd2ec..b218cc5789d 100644 --- a/compiler/rustc_lint/src/non_fmt_panic.rs +++ b/compiler/rustc_lint/src/non_fmt_panic.rs @@ -128,7 +128,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc // No clue where this argument is coming from. return lint; } - if arg_macro.map_or(false, |id| cx.tcx.is_diagnostic_item(sym::format_macro, id)) { + if arg_macro.is_some_and(|id| cx.tcx.is_diagnostic_item(sym::format_macro, id)) { // A case of `panic!(format!(..))`. lint.note(fluent::lint_supports_fmt_note); if let Some((open, close, _)) = find_delimiters(cx, arg_span) { diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 125b4dc5503..4bf4fda8292 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -677,7 +677,7 @@ pub fn transparent_newtype_field<'a, 'tcx>( let param_env = tcx.param_env(variant.def_id); variant.fields.iter().find(|field| { let field_ty = tcx.type_of(field.did).subst_identity(); - let is_zst = tcx.layout_of(param_env.and(field_ty)).map_or(false, |layout| layout.is_zst()); + let is_zst = tcx.layout_of(param_env.and(field_ty)).is_ok_and(|layout| layout.is_zst()); !is_zst }) } diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index eb175e96997..8f75fa11dd9 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -103,8 +103,10 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { && let ty = cx.typeck_results().expr_ty(&await_expr) && let ty::Alias(ty::Opaque, ty::AliasTy { def_id: future_def_id, .. }) = ty.kind() && cx.tcx.ty_is_opaque_future(ty) - // FIXME: This also includes non-async fns that return `impl Future`. && let async_fn_def_id = cx.tcx.parent(*future_def_id) + && matches!(cx.tcx.def_kind(async_fn_def_id), DefKind::Fn | DefKind::AssocFn) + // Check that this `impl Future` actually comes from an `async fn` + && cx.tcx.asyncness(async_fn_def_id).is_async() && check_must_use_def( cx, async_fn_def_id, @@ -662,8 +664,8 @@ trait UnusedDelimLint { _ => return, }; let keep_space = ( - left_pos.map_or(false, |s| s >= value.span.lo()), - right_pos.map_or(false, |s| s <= value.span.hi()), + left_pos.is_some_and(|s| s >= value.span.lo()), + right_pos.is_some_and(|s| s <= value.span.hi()), ); self.emit_unused_delims(cx, value.span, spans, ctx.into(), keep_space); } diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 98ea9dc7501..aaf72ab94e7 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -4,7 +4,7 @@ use crate::errors; use crate::locator::{CrateError, CrateLocator, CratePaths}; use crate::rmeta::{CrateDep, CrateMetadata, CrateNumMap, CrateRoot, MetadataBlob}; -use rustc_ast::expand::allocator::AllocatorKind; +use rustc_ast::expand::allocator::{alloc_error_handler_name, global_fn_name, AllocatorKind}; use rustc_ast::{self as ast, *}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::svh::Svh; @@ -148,11 +148,15 @@ impl CStore { assert_eq!(self.metas.len(), self.stable_crate_ids.len()); let num = CrateNum::new(self.stable_crate_ids.len()); if let Some(&existing) = self.stable_crate_ids.get(&root.stable_crate_id()) { - let crate_name0 = root.name(); - if let Some(crate_name1) = self.metas[existing].as_ref().map(|data| data.name()) { + // Check for (potential) conflicts with the local crate + if existing == LOCAL_CRATE { + Err(CrateError::SymbolConflictsCurrent(root.name())) + } else if let Some(crate_name1) = self.metas[existing].as_ref().map(|data| data.name()) + { + let crate_name0 = root.name(); Err(CrateError::StableCrateIdCollision(crate_name0, crate_name1)) } else { - Err(CrateError::SymbolConflictsCurrent(crate_name0)) + Err(CrateError::NotFound(root.name())) } } else { self.metas.push(None); @@ -369,7 +373,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { let host_hash = host_lib.as_ref().map(|lib| lib.metadata.get_root().hash()); let private_dep = - self.sess.opts.externs.get(name.as_str()).map_or(false, |e| e.is_private_dep); + self.sess.opts.externs.get(name.as_str()).is_some_and(|e| e.is_private_dep); // Claim this crate number and cache it let cnum = self.cstore.intern_stable_crate_id(&crate_root)?; @@ -1044,7 +1048,7 @@ fn global_allocator_spans(krate: &ast::Crate) -> Vec<Span> { } } - let name = Symbol::intern(&AllocatorKind::Global.fn_name(sym::alloc)); + let name = Symbol::intern(&global_fn_name(sym::alloc)); let mut f = Finder { name, spans: Vec::new() }; visit::walk_crate(&mut f, krate); f.spans @@ -1066,7 +1070,7 @@ fn alloc_error_handler_spans(krate: &ast::Crate) -> Vec<Span> { } } - let name = Symbol::intern(&AllocatorKind::Global.fn_name(sym::oom)); + let name = Symbol::intern(alloc_error_handler_name(AllocatorKind::Global)); let mut f = Finder { name, spans: Vec::new() }; visit::walk_crate(&mut f, krate); f.spans diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 6ec691f73b7..ceb348f3469 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -567,7 +567,7 @@ impl<'a> CrateLocator<'a> { let mut err_data: Option<Vec<PathBuf>> = None; for (lib, kind) in m { info!("{} reading metadata from: {}", flavor, lib.display()); - if flavor == CrateFlavor::Rmeta && lib.metadata().map_or(false, |m| m.len() == 0) { + if flavor == CrateFlavor::Rmeta && lib.metadata().is_ok_and(|m| m.len() == 0) { // Empty files will cause get_metadata_section to fail. Rmeta // files can be empty, for example with binaries (which can // often appear with `cargo check` when checking a library as @@ -602,7 +602,7 @@ impl<'a> CrateLocator<'a> { } }; // If we see multiple hashes, emit an error about duplicate candidates. - if slot.as_ref().map_or(false, |s| s.0 != hash) { + if slot.as_ref().is_some_and(|s| s.0 != hash) { if let Some(candidates) = err_data { return Err(CrateError::MultipleCandidates( self.crate_name, @@ -961,6 +961,7 @@ pub(crate) enum CrateError { DlSym(String), LocatorCombined(Box<CombinedLocatorError>), NonDylibPlugin(Symbol), + NotFound(Symbol), } enum MetadataError<'a> { @@ -1131,6 +1132,18 @@ impl CrateError { CrateError::NonDylibPlugin(crate_name) => { sess.emit_err(errors::NoDylibPlugin { span, crate_name }); } + CrateError::NotFound(crate_name) => { + sess.emit_err(errors::CannotFindCrate { + span, + crate_name, + add_info: String::new(), + missing_core, + current_crate: sess.opts.crate_name.clone().unwrap_or("<unknown>".to_string()), + is_nightly_build: sess.is_nightly_build(), + profiler_runtime: Symbol::intern(&sess.opts.unstable_opts.profiler_runtime), + locator_triple: sess.opts.target_triple.clone(), + }); + } } } } diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index deafad394b8..cc4e60cf6ac 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -19,6 +19,7 @@ use rustc_hir::definitions::{DefKey, DefPath, DefPathData, DefPathHash}; use rustc_hir::diagnostic_items::DiagnosticItems; use rustc_index::{Idx, IndexVec}; use rustc_middle::metadata::ModChild; +use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; use rustc_middle::mir::interpret::{AllocDecodingSession, AllocDecodingState}; use rustc_middle::ty::codec::TyDecoder; @@ -958,7 +959,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .decode((self, sess)) } - fn get_debugger_visualizers(self) -> Vec<rustc_span::DebuggerVisualizerFile> { + fn get_debugger_visualizers(self) -> Vec<DebuggerVisualizerFile> { self.root.debugger_visualizers.decode(self).collect::<Vec<_>>() } @@ -1059,7 +1060,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .expect("argument names not encoded for a function") .decode((self, sess)) .nth(0) - .map_or(false, |ident| ident.name == kw::SelfLower) + .is_some_and(|ident| ident.name == kw::SelfLower) } fn get_associated_item_or_field_def_ids( diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 364269095e0..7425963d30f 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -323,7 +323,7 @@ provide! { tcx, def_id, other, cdata, extra_filename => { cdata.root.extra_filename.clone() } - traits_in_crate => { tcx.arena.alloc_from_iter(cdata.get_traits()) } + traits => { tcx.arena.alloc_from_iter(cdata.get_traits()) } trait_impls_in_crate => { tcx.arena.alloc_from_iter(cdata.get_trait_impls()) } implementations_of_trait => { cdata.get_implementations_of_trait(tcx, other) } crate_incoherent_impls => { cdata.get_incoherent_impls(tcx, other) } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index bce70913f33..f067bca4b0b 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -19,6 +19,7 @@ use rustc_hir::definitions::DefPathData; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::lang_items::LangItem; use rustc_middle::hir::nested_filter; +use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::{ metadata_symbol_name, ExportedSymbol, SymbolExportInfo, @@ -36,9 +37,7 @@ use rustc_session::config::{CrateType, OptLevel}; use rustc_session::cstore::{ForeignModule, LinkagePreference, NativeLib}; use rustc_span::hygiene::{ExpnIndex, HygieneEncodeContext, MacroKind}; use rustc_span::symbol::{sym, Symbol}; -use rustc_span::{ - self, DebuggerVisualizerFile, ExternalSource, FileName, SourceFile, Span, SyntaxContext, -}; +use rustc_span::{self, ExternalSource, FileName, SourceFile, Span, SyntaxContext}; use std::borrow::Borrow; use std::collections::hash_map::Entry; use std::hash::Hash; @@ -1855,7 +1854,16 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { fn encode_debugger_visualizers(&mut self) -> LazyArray<DebuggerVisualizerFile> { empty_proc_macro!(self); - self.lazy_array(self.tcx.debugger_visualizers(LOCAL_CRATE).iter()) + self.lazy_array( + self.tcx + .debugger_visualizers(LOCAL_CRATE) + .iter() + // Erase the path since it may contain privacy sensitive data + // that we don't want to end up in crate metadata. + // The path is only needed for the local crate because of + // `--emit dep-info`. + .map(DebuggerVisualizerFile::path_erased), + ) } fn encode_crate_deps(&mut self) -> LazyArray<CrateDep> { @@ -1930,7 +1938,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { fn encode_traits(&mut self) -> LazyArray<DefIndex> { empty_proc_macro!(self); - self.lazy_array(self.tcx.traits_in_crate(LOCAL_CRATE).iter().map(|def_id| def_id.index)) + self.lazy_array(self.tcx.traits(LOCAL_CRATE).iter().map(|def_id| def_id.index)) } /// Encodes an index, mapping each trait to its (local) implementations. @@ -2321,7 +2329,7 @@ pub fn provide(providers: &mut Providers) { .get(&def_id) .expect("no traits in scope for a doc link") }, - traits_in_crate: |tcx, LocalCrate| { + traits: |tcx, LocalCrate| { let mut traits = Vec::new(); for id in tcx.hir().items() { if matches!(tcx.def_kind(id.owner_id), DefKind::Trait | DefKind::TraitAlias) { diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index b83c9c3be20..97e67fcf8fd 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -2,6 +2,7 @@ use crate::creader::CrateMetadataRef; use decoder::Metadata; use def_path_hash_map::DefPathHashMapRef; use rustc_data_structures::fx::FxHashMap; +use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use table::TableBuilder; use rustc_ast as ast; @@ -245,7 +246,7 @@ pub(crate) struct CrateRoot { proc_macro_data: Option<ProcMacroData>, tables: LazyTables, - debugger_visualizers: LazyArray<rustc_span::DebuggerVisualizerFile>, + debugger_visualizers: LazyArray<DebuggerVisualizerFile>, exported_symbols: LazyArray<(ExportedSymbol<'static>, SymbolExportInfo)>, diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index 6a1a2a061dd..a149a61ec13 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -121,6 +121,7 @@ macro_rules! arena_types { >, [] bit_set_u32: rustc_index::bit_set::BitSet<u32>, [] external_constraints: rustc_middle::traits::solve::ExternalConstraintsData<'tcx>, + [] predefined_opaques_in_body: rustc_middle::traits::solve::PredefinedOpaquesData<'tcx>, [decode] doc_link_resolutions: rustc_hir::def::DocLinkResMap, [] closure_kind_origin: (rustc_span::Span, rustc_middle::hir::place::Place<'tcx>), [] mod_child: rustc_middle::metadata::ModChild, diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 5bf0938d518..d1ddc8fc1fd 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -1,4 +1,5 @@ use crate::hir::{ModuleItems, Owner}; +use crate::middle::debugger_visualizer::DebuggerVisualizerFile; use crate::query::LocalCrate; use crate::ty::TyCtxt; use rustc_ast as ast; @@ -409,7 +410,7 @@ impl<'hir> Map<'hir> { /// item (possibly associated), a closure, or a `hir::AnonConst`. pub fn body_owner(self, BodyId { hir_id }: BodyId) -> HirId { let parent = self.parent_id(hir_id); - assert!(self.find(parent).map_or(false, |n| is_body_owner(n, hir_id)), "{hir_id:?}"); + assert!(self.find(parent).is_some_and(|n| is_body_owner(n, hir_id)), "{hir_id:?}"); parent } @@ -1165,11 +1166,26 @@ pub(super) fn crate_hash(tcx: TyCtxt<'_>, _: LocalCrate) -> Svh { source_file_names.sort_unstable(); + // We have to take care of debugger visualizers explicitly. The HIR (and + // thus `hir_body_hash`) contains the #[debugger_visualizer] attributes but + // these attributes only store the file path to the visualizer file, not + // their content. Yet that content is exported into crate metadata, so any + // changes to it need to be reflected in the crate hash. + let debugger_visualizers: Vec<_> = tcx + .debugger_visualizers(LOCAL_CRATE) + .iter() + // We ignore the path to the visualizer file since it's not going to be + // encoded in crate metadata and we already hash the full contents of + // the file. + .map(DebuggerVisualizerFile::path_erased) + .collect(); + let crate_hash: Fingerprint = tcx.with_stable_hashing_context(|mut hcx| { let mut stable_hasher = StableHasher::new(); hir_body_hash.hash_stable(&mut hcx, &mut stable_hasher); upstream_crates.hash_stable(&mut hcx, &mut stable_hasher); source_file_names.hash_stable(&mut hcx, &mut stable_hasher); + debugger_visualizers.hash_stable(&mut hcx, &mut stable_hasher); if tcx.sess.opts.incremental_relative_spans() { let definitions = tcx.definitions_untracked(); let mut owner_spans: Vec<_> = krate diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index ac0b2844177..45a07fdd293 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -115,7 +115,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Returns `true` if this is a foreign item (i.e., linked via `extern { ... }`). pub fn is_foreign_item(self, def_id: impl Into<DefId>) -> bool { self.opt_parent(def_id.into()) - .map_or(false, |parent| matches!(self.def_kind(parent), DefKind::ForeignMod)) + .is_some_and(|parent| matches!(self.def_kind(parent), DefKind::ForeignMod)) } } diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index c4e41e00520..56171314944 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -280,7 +280,7 @@ pub struct QueryResponse<'tcx, R> { /// should get its hidden type inferred. So we bubble the opaque type /// and the type it was compared against upwards and let the query caller /// handle it. - pub opaque_types: Vec<(Ty<'tcx>, Ty<'tcx>)>, + pub opaque_types: Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>, pub value: R, } diff --git a/compiler/rustc_middle/src/middle/debugger_visualizer.rs b/compiler/rustc_middle/src/middle/debugger_visualizer.rs new file mode 100644 index 00000000000..a0497d805da --- /dev/null +++ b/compiler/rustc_middle/src/middle/debugger_visualizer.rs @@ -0,0 +1,38 @@ +use rustc_data_structures::sync::Lrc; +use std::path::PathBuf; + +#[derive(HashStable)] +#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)] +pub enum DebuggerVisualizerType { + Natvis, + GdbPrettyPrinter, +} + +/// A single debugger visualizer file. +#[derive(HashStable)] +#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Encodable, Decodable)] +pub struct DebuggerVisualizerFile { + /// The complete debugger visualizer source. + pub src: Lrc<[u8]>, + /// Indicates which visualizer type this targets. + pub visualizer_type: DebuggerVisualizerType, + /// The file path to the visualizer file. This is used for reporting + /// visualizer files in dep-info. Before it is written to crate metadata, + /// the path is erased to `None`, so as not to emit potentially privacy + /// sensitive data. + pub path: Option<PathBuf>, +} + +impl DebuggerVisualizerFile { + pub fn new(src: Lrc<[u8]>, visualizer_type: DebuggerVisualizerType, path: PathBuf) -> Self { + DebuggerVisualizerFile { src, visualizer_type, path: Some(path) } + } + + pub fn path_erased(&self) -> Self { + DebuggerVisualizerFile { + src: self.src.clone(), + visualizer_type: self.visualizer_type, + path: None, + } + } +} diff --git a/compiler/rustc_middle/src/middle/mod.rs b/compiler/rustc_middle/src/middle/mod.rs index 9bb4570ef14..85c5af9ca13 100644 --- a/compiler/rustc_middle/src/middle/mod.rs +++ b/compiler/rustc_middle/src/middle/mod.rs @@ -1,4 +1,5 @@ pub mod codegen_fn_attrs; +pub mod debugger_visualizer; pub mod dependency_format; pub mod exported_symbols; pub mod lang_items; diff --git a/compiler/rustc_middle/src/middle/privacy.rs b/compiler/rustc_middle/src/middle/privacy.rs index aeb6a1601fc..f45cf788dd9 100644 --- a/compiler/rustc_middle/src/middle/privacy.rs +++ b/compiler/rustc_middle/src/middle/privacy.rs @@ -94,8 +94,7 @@ pub struct EffectiveVisibilities<Id = LocalDefId> { impl EffectiveVisibilities { pub fn is_public_at_level(&self, id: LocalDefId, level: Level) -> bool { - self.effective_vis(id) - .map_or(false, |effective_vis| effective_vis.is_public_at_level(level)) + self.effective_vis(id).is_some_and(|effective_vis| effective_vis.is_public_at_level(level)) } /// See `Level::Reachable`. diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 89fc864319d..6354c0aabde 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -375,7 +375,7 @@ impl<'tcx> TyCtxt<'tcx> { let parent_def_id = self.hir().get_parent_item(id); let skip = self .lookup_deprecation_entry(parent_def_id.to_def_id()) - .map_or(false, |parent_depr| parent_depr.same_origin(&depr_entry)); + .is_some_and(|parent_depr| parent_depr.same_origin(&depr_entry)); // #[deprecated] doesn't emit a notice if we're not on the // topmost deprecation. For example, if a struct is deprecated, diff --git a/compiler/rustc_middle/src/mir/basic_blocks.rs b/compiler/rustc_middle/src/mir/basic_blocks.rs index 1319ddbb877..9d70dbfa072 100644 --- a/compiler/rustc_middle/src/mir/basic_blocks.rs +++ b/compiler/rustc_middle/src/mir/basic_blocks.rs @@ -27,6 +27,7 @@ struct Cache { switch_sources: OnceCell<SwitchSources>, is_cyclic: OnceCell<bool>, postorder: OnceCell<Vec<BasicBlock>>, + dominators: OnceCell<Dominators<BasicBlock>>, } impl<'tcx> BasicBlocks<'tcx> { @@ -41,8 +42,8 @@ impl<'tcx> BasicBlocks<'tcx> { *self.cache.is_cyclic.get_or_init(|| graph::is_cyclic(self)) } - pub fn dominators(&self) -> Dominators<BasicBlock> { - dominators(&self) + pub fn dominators(&self) -> &Dominators<BasicBlock> { + self.cache.dominators.get_or_init(|| dominators(self)) } /// Returns predecessors for each basic block. diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 1da94dd7917..5c71910a955 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -101,7 +101,7 @@ impl<'tcx> HasLocalDecls<'tcx> for Body<'tcx> { /// pass will be named after the type, and it will consist of a main /// loop that goes over each available MIR and applies `run_pass`. pub trait MirPass<'tcx> { - fn name(&self) -> &str { + fn name(&self) -> &'static str { let name = std::any::type_name::<Self>(); if let Some((_, tail)) = name.rsplit_once(':') { tail } else { name } } diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 33b7fe0c2dc..21faf1958e9 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -749,6 +749,29 @@ pub enum TerminatorKind<'tcx> { }, } +impl TerminatorKind<'_> { + /// Returns a simple string representation of a `TerminatorKind` variant, independent of any + /// values it might hold (e.g. `TerminatorKind::Call` always returns `"Call"`). + pub const fn name(&self) -> &'static str { + match self { + TerminatorKind::Goto { .. } => "Goto", + TerminatorKind::SwitchInt { .. } => "SwitchInt", + TerminatorKind::Resume => "Resume", + TerminatorKind::Terminate => "Terminate", + TerminatorKind::Return => "Return", + TerminatorKind::Unreachable => "Unreachable", + TerminatorKind::Drop { .. } => "Drop", + TerminatorKind::Call { .. } => "Call", + TerminatorKind::Assert { .. } => "Assert", + TerminatorKind::Yield { .. } => "Yield", + TerminatorKind::GeneratorDrop => "GeneratorDrop", + TerminatorKind::FalseEdge { .. } => "FalseEdge", + TerminatorKind::FalseUnwind { .. } => "FalseUnwind", + TerminatorKind::InlineAsm { .. } => "InlineAsm", + } + } +} + /// Action to be taken when a stack unwind happens. #[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)] #[derive(TypeFoldable, TypeVisitable)] diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 12c94dc4676..1528be42f6a 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -12,6 +12,7 @@ use crate::infer::canonical::{self, Canonical}; use crate::lint::LintExpectation; use crate::metadata::ModChild; use crate::middle::codegen_fn_attrs::CodegenFnAttrs; +use crate::middle::debugger_visualizer::DebuggerVisualizerFile; use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; use crate::middle::lib_features::LibFeatures; use crate::middle::privacy::EffectiveVisibilities; @@ -37,7 +38,10 @@ use crate::traits::query::{ OutlivesBound, }; use crate::traits::specialization_graph; -use crate::traits::{self, ImplSource}; +use crate::traits::{ + CanonicalChalkEnvironmentAndGoal, CodegenObligationError, EvaluationResult, ImplSource, + ObjectSafetyViolation, ObligationCause, OverflowError, WellFormedLoc, +}; use crate::ty::fast_reject::SimplifiedType; use crate::ty::layout::ValidityRequirement; use crate::ty::subst::{GenericArg, SubstsRef}; @@ -1272,7 +1276,7 @@ rustc_queries! { query codegen_select_candidate( key: (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>) - ) -> Result<&'tcx ImplSource<'tcx, ()>, traits::CodegenObligationError> { + ) -> Result<&'tcx ImplSource<'tcx, ()>, CodegenObligationError> { cache_on_disk_if { true } desc { |tcx| "computing candidate for `{}`", key.1 } } @@ -1293,7 +1297,7 @@ rustc_queries! { desc { |tcx| "building specialization graph of trait `{}`", tcx.def_path_str(trait_id) } cache_on_disk_if { true } } - query object_safety_violations(trait_id: DefId) -> &'tcx [traits::ObjectSafetyViolation] { + query object_safety_violations(trait_id: DefId) -> &'tcx [ObjectSafetyViolation] { desc { |tcx| "determining object safety of trait `{}`", tcx.def_path_str(trait_id) } } query check_is_object_safe(trait_id: DefId) -> bool { @@ -1784,12 +1788,18 @@ rustc_queries! { desc { "looking at the source for a crate" } separate_provide_extern } + /// Returns the debugger visualizers defined for this crate. - query debugger_visualizers(_: CrateNum) -> &'tcx Vec<rustc_span::DebuggerVisualizerFile> { + /// NOTE: This query has to be marked `eval_always` because it reads data + /// directly from disk that is not tracked anywhere else. I.e. it + /// represents a genuine input to the query system. + query debugger_visualizers(_: CrateNum) -> &'tcx Vec<DebuggerVisualizerFile> { arena_cache desc { "looking up the debugger visualizers for this crate" } separate_provide_extern + eval_always } + query postorder_cnums(_: ()) -> &'tcx [CrateNum] { eval_always desc { "generating a postorder list of CrateNums" } @@ -1831,8 +1841,7 @@ rustc_queries! { } /// A list of all traits in a crate, used by rustdoc and error reporting. - /// NOTE: Not named just `traits` due to a naming conflict. - query traits_in_crate(_: CrateNum) -> &'tcx [DefId] { + query traits(_: CrateNum) -> &'tcx [DefId] { desc { "fetching all traits in a crate" } separate_provide_extern } @@ -1946,12 +1955,12 @@ rustc_queries! { /// `infcx.predicate_must_hold()` instead. query evaluate_obligation( goal: CanonicalPredicateGoal<'tcx> - ) -> Result<traits::EvaluationResult, traits::OverflowError> { + ) -> Result<EvaluationResult, OverflowError> { desc { "evaluating trait selection obligation `{}`", goal.value.value } } query evaluate_goal( - goal: traits::CanonicalChalkEnvironmentAndGoal<'tcx> + goal: CanonicalChalkEnvironmentAndGoal<'tcx> ) -> Result< &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, NoSolution @@ -2121,8 +2130,8 @@ rustc_queries! { /// all of the cases that the normal `ty::Ty`-based wfcheck does. This is fine, /// because the `ty::Ty`-based wfcheck is always run. query diagnostic_hir_wf_check( - key: (ty::Predicate<'tcx>, traits::WellFormedLoc) - ) -> &'tcx Option<traits::ObligationCause<'tcx>> { + key: (ty::Predicate<'tcx>, WellFormedLoc) + ) -> &'tcx Option<ObligationCause<'tcx>> { arena_cache eval_always no_hash diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 8d088b68e59..97edfc2fca2 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -32,20 +32,12 @@ impl QueryKeyStringCache { } } -#[derive(Clone, Copy)] -pub struct QueryStruct<'tcx> { - pub try_collect_active_jobs: fn(TyCtxt<'tcx>, &mut QueryMap<DepKind>) -> Option<()>, - pub alloc_self_profile_query_strings: fn(TyCtxt<'tcx>, &mut QueryKeyStringCache), - pub encode_query_results: - Option<fn(TyCtxt<'tcx>, &mut CacheEncoder<'_, 'tcx>, &mut EncodedDepNodeIndex)>, -} - pub struct DynamicQuery<'tcx, C: QueryCache> { pub name: &'static str, pub eval_always: bool, - pub dep_kind: rustc_middle::dep_graph::DepKind, + pub dep_kind: DepKind, pub handle_cycle_error: HandleCycleError, - pub query_state: FieldOffset<QueryStates<'tcx>, QueryState<C::Key, crate::dep_graph::DepKind>>, + pub query_state: FieldOffset<QueryStates<'tcx>, QueryState<C::Key, DepKind>>, pub query_cache: FieldOffset<QueryCaches<'tcx>, C>, pub cache_on_disk: fn(tcx: TyCtxt<'tcx>, key: &C::Key) -> bool, pub execute_query: fn(tcx: TyCtxt<'tcx>, k: C::Key) -> C::Value, @@ -60,8 +52,7 @@ pub struct DynamicQuery<'tcx, C: QueryCache> { pub loadable_from_disk: fn(tcx: TyCtxt<'tcx>, key: &C::Key, index: SerializedDepNodeIndex) -> bool, pub hash_result: HashResult<C::Value>, - pub value_from_cycle_error: - fn(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo<crate::dep_graph::DepKind>]) -> C::Value, + pub value_from_cycle_error: fn(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo<DepKind>]) -> C::Value, pub format_value: fn(&C::Value) -> String, } @@ -69,7 +60,6 @@ pub struct QuerySystemFns<'tcx> { pub engine: QueryEngine, pub local_providers: Providers, pub extern_providers: ExternProviders, - pub query_structs: Vec<QueryStruct<'tcx>>, pub encode_query_results: fn( tcx: TyCtxt<'tcx>, encoder: &mut CacheEncoder<'_, 'tcx>, diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 2f0b07d4c71..0a903a76974 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -701,9 +701,9 @@ impl<'tcx, N> ImplSource<'tcx, N> { } pub fn borrow_nested_obligations(&self) -> &[N] { - match &self { - ImplSource::UserDefined(i) => &i.nested[..], - ImplSource::Param(n, _) => &n, + match self { + ImplSource::UserDefined(i) => &i.nested, + ImplSource::Param(n, _) => n, ImplSource::Builtin(i) => &i.nested, ImplSource::AutoImpl(d) => &d.nested, ImplSource::Closure(c) => &c.nested, @@ -717,6 +717,23 @@ impl<'tcx, N> ImplSource<'tcx, N> { } } + pub fn borrow_nested_obligations_mut(&mut self) -> &mut [N] { + match self { + ImplSource::UserDefined(i) => &mut i.nested, + ImplSource::Param(n, _) => n, + ImplSource::Builtin(i) => &mut i.nested, + ImplSource::AutoImpl(d) => &mut d.nested, + ImplSource::Closure(c) => &mut c.nested, + ImplSource::Generator(c) => &mut c.nested, + ImplSource::Future(c) => &mut c.nested, + ImplSource::Object(d) => &mut d.nested, + ImplSource::FnPointer(d) => &mut d.nested, + ImplSource::TraitAlias(d) => &mut d.nested, + ImplSource::TraitUpcasting(d) => &mut d.nested, + ImplSource::ConstDestruct(i) => &mut i.nested, + } + } + pub fn map<M, F>(self, f: F) -> ImplSource<'tcx, M> where F: FnMut(N) -> M, @@ -1091,3 +1108,14 @@ pub enum CodegenObligationError { Unimplemented, FulfillmentError, } + +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)] +pub enum DefiningAnchor { + /// `DefId` of the item. + Bind(LocalDefId), + /// When opaque types are not resolved, we `Bubble` up, meaning + /// return the opaque/hidden type pair from query, for caller of query to handle it. + Bubble, + /// Used to catch type mismatch errors when handling opaque types. + Error, +} diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs index 6b7b910a59b..2c5b64a59cd 100644 --- a/compiler/rustc_middle/src/traits/solve.rs +++ b/compiler/rustc_middle/src/traits/solve.rs @@ -5,13 +5,13 @@ use rustc_query_system::cache::Cache; use crate::infer::canonical::{CanonicalVarValues, QueryRegionConstraints}; use crate::traits::query::NoSolution; -use crate::traits::Canonical; +use crate::traits::{Canonical, DefiningAnchor}; use crate::ty::{ self, FallibleTypeFolder, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor, }; -pub type EvaluationCache<'tcx> = Cache<CanonicalGoal<'tcx>, QueryResult<'tcx>>; +pub type EvaluationCache<'tcx> = Cache<CanonicalInput<'tcx>, QueryResult<'tcx>>; /// A goal is a statement, i.e. `predicate`, we want to prove /// given some assumptions, i.e. `param_env`. @@ -96,7 +96,31 @@ pub enum MaybeCause { Overflow, } -pub type CanonicalGoal<'tcx, T = ty::Predicate<'tcx>> = Canonical<'tcx, Goal<'tcx, T>>; +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)] +pub struct QueryInput<'tcx, T> { + pub goal: Goal<'tcx, T>, + pub anchor: DefiningAnchor, + pub predefined_opaques_in_body: PredefinedOpaques<'tcx>, +} + +/// Additional constraints returned on success. +#[derive(Debug, PartialEq, Eq, Clone, Hash, Default)] +pub struct PredefinedOpaquesData<'tcx> { + pub opaque_types: Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>, +} + +#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] +pub struct PredefinedOpaques<'tcx>(pub(crate) Interned<'tcx, PredefinedOpaquesData<'tcx>>); + +impl<'tcx> std::ops::Deref for PredefinedOpaques<'tcx> { + type Target = PredefinedOpaquesData<'tcx>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +pub type CanonicalInput<'tcx, T = ty::Predicate<'tcx>> = Canonical<'tcx, QueryInput<'tcx, T>>; pub type CanonicalResponse<'tcx> = Canonical<'tcx, Response<'tcx>>; @@ -124,7 +148,7 @@ impl<'tcx> std::ops::Deref for ExternalConstraints<'tcx> { pub struct ExternalConstraintsData<'tcx> { // FIXME: implement this. pub region_constraints: QueryRegionConstraints<'tcx>, - pub opaque_types: Vec<(Ty<'tcx>, Ty<'tcx>)>, + pub opaque_types: Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>, } // FIXME: Having to clone `region_constraints` for folding feels bad and @@ -165,3 +189,40 @@ impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for ExternalConstraints<'tcx> { ControlFlow::Continue(()) } } + +// FIXME: Having to clone `region_constraints` for folding feels bad and +// probably isn't great wrt performance. +// +// Not sure how to fix this, maybe we should also intern `opaque_types` and +// `region_constraints` here or something. +impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for PredefinedOpaques<'tcx> { + fn try_fold_with<F: FallibleTypeFolder<TyCtxt<'tcx>>>( + self, + folder: &mut F, + ) -> Result<Self, F::Error> { + Ok(FallibleTypeFolder::interner(folder).mk_predefined_opaques_in_body( + PredefinedOpaquesData { + opaque_types: self + .opaque_types + .iter() + .map(|opaque| opaque.try_fold_with(folder)) + .collect::<Result<_, F::Error>>()?, + }, + )) + } + + fn fold_with<F: TypeFolder<TyCtxt<'tcx>>>(self, folder: &mut F) -> Self { + TypeFolder::interner(folder).mk_predefined_opaques_in_body(PredefinedOpaquesData { + opaque_types: self.opaque_types.iter().map(|opaque| opaque.fold_with(folder)).collect(), + }) + } +} + +impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for PredefinedOpaques<'tcx> { + fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>( + &self, + visitor: &mut V, + ) -> std::ops::ControlFlow<V::BreakTy> { + self.opaque_types.visit_with(visitor) + } +} diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index ad9891a5dca..7c5c030c276 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -111,12 +111,30 @@ impl Ord for AdtDefData { } } -/// There should be only one AdtDef for each `did`, therefore -/// it is fine to implement `PartialEq` only based on `did`. impl PartialEq for AdtDefData { #[inline] fn eq(&self, other: &Self) -> bool { - self.did == other.did + // There should be only one `AdtDefData` for each `def_id`, therefore + // it is fine to implement `PartialEq` only based on `def_id`. + // + // Below, we exhaustively destructure `self` and `other` so that if the + // definition of `AdtDefData` changes, a compile-error will be produced, + // reminding us to revisit this assumption. + + let Self { did: self_def_id, variants: _, flags: _, repr: _ } = self; + let Self { did: other_def_id, variants: _, flags: _, repr: _ } = other; + + let res = self_def_id == other_def_id; + + // Double check that implicit assumption detailed above. + if cfg!(debug_assertions) && res { + let deep = self.flags == other.flags + && self.repr == other.repr + && self.variants == other.variants; + assert!(deep, "AdtDefData for the same def-id has differing data"); + } + + res } } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index bbea3e1412b..2bde55bc4fd 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -21,7 +21,9 @@ use crate::query::{IntoQueryParam, TyCtxtAt}; use crate::thir::Thir; use crate::traits; use crate::traits::solve; -use crate::traits::solve::{ExternalConstraints, ExternalConstraintsData}; +use crate::traits::solve::{ + ExternalConstraints, ExternalConstraintsData, PredefinedOpaques, PredefinedOpaquesData, +}; use crate::ty::{ self, AdtDef, AdtDefData, AdtKind, Binder, Const, ConstData, FloatTy, FloatVar, FloatVid, GenericParamDefKind, ImplPolarity, InferTy, IntTy, IntVar, IntVid, List, ParamConst, ParamTy, @@ -140,6 +142,7 @@ pub struct CtxtInterners<'tcx> { layout: InternedSet<'tcx, LayoutS>, adt_def: InternedSet<'tcx, AdtDefData>, external_constraints: InternedSet<'tcx, ExternalConstraintsData<'tcx>>, + predefined_opaques_in_body: InternedSet<'tcx, PredefinedOpaquesData<'tcx>>, fields: InternedSet<'tcx, List<FieldIdx>>, } @@ -164,6 +167,7 @@ impl<'tcx> CtxtInterners<'tcx> { layout: Default::default(), adt_def: Default::default(), external_constraints: Default::default(), + predefined_opaques_in_body: Default::default(), fields: Default::default(), } } @@ -1199,7 +1203,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn all_traits(self) -> impl Iterator<Item = DefId> + 'tcx { iter::once(LOCAL_CRATE) .chain(self.crates(()).iter().copied()) - .flat_map(move |cnum| self.traits_in_crate(cnum).iter().copied()) + .flat_map(move |cnum| self.traits(cnum).iter().copied()) } #[inline] @@ -1520,6 +1524,8 @@ direct_interners! { adt_def: pub mk_adt_def_from_data(AdtDefData): AdtDef -> AdtDef<'tcx>, external_constraints: pub mk_external_constraints(ExternalConstraintsData<'tcx>): ExternalConstraints -> ExternalConstraints<'tcx>, + predefined_opaques_in_body: pub mk_predefined_opaques_in_body(PredefinedOpaquesData<'tcx>): + PredefinedOpaques -> PredefinedOpaques<'tcx>, } macro_rules! slice_interners { @@ -2341,7 +2347,7 @@ impl<'tcx> TyCtxt<'tcx> { } pub fn is_late_bound(self, id: HirId) -> bool { - self.is_late_bound_map(id.owner).map_or(false, |set| set.contains(&id.local_id)) + self.is_late_bound_map(id.owner).is_some_and(|set| set.contains(&id.local_id)) } pub fn late_bound_vars(self, id: HirId) -> &'tcx List<ty::BoundVariableKind> { @@ -2474,7 +2480,7 @@ pub fn provide(providers: &mut Providers) { |tcx, LocalCrate| attr::contains_name(tcx.hir().krate_attrs(), sym::compiler_builtins); providers.has_panic_handler = |tcx, LocalCrate| { // We want to check if the panic handler was defined in this crate - tcx.lang_items().panic_impl().map_or(false, |did| did.is_local()) + tcx.lang_items().panic_impl().is_some_and(|did| did.is_local()) }; providers.source_span = |tcx, def_id| tcx.untracked.source_span.get(def_id).unwrap_or(DUMMY_SP); } diff --git a/compiler/rustc_middle/src/ty/context/tls.rs b/compiler/rustc_middle/src/ty/context/tls.rs index 5c1c419811e..9de77b9fda1 100644 --- a/compiler/rustc_middle/src/ty/context/tls.rs +++ b/compiler/rustc_middle/src/ty/context/tls.rs @@ -78,7 +78,7 @@ where { TLV.with(|tlv| { let old = tlv.replace(erase(context)); - let _reset = rustc_data_structures::OnDrop(move || tlv.set(old)); + let _reset = rustc_data_structures::defer(move || tlv.set(old)); f() }) } diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index 99174bae3f6..b0ffe78299d 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -298,7 +298,7 @@ impl<'tcx> Generics { .iter() .rev() .take_while(|param| { - param.default_value(tcx).map_or(false, |default| { + param.default_value(tcx).is_some_and(|default| { default.subst(tcx, substs) == substs[param.index as usize] }) }) diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 8c69894f5ba..e641d1ef1be 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -659,7 +659,7 @@ fn polymorphize<'tcx>( } else { None }; - let has_upvars = upvars_ty.map_or(false, |ty| !ty.tuple_fields().is_empty()); + let has_upvars = upvars_ty.is_some_and(|ty| !ty.tuple_fields().is_empty()); debug!("polymorphize: upvars_ty={:?} has_upvars={:?}", upvars_ty, has_upvars); struct PolymorphizationFolder<'tcx> { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index be0d1e61a46..73f435d4840 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -37,7 +37,7 @@ use rustc_data_structures::intern::Interned; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::steal::Steal; use rustc_data_structures::tagged_ptr::CopyTaggedPtr; -use rustc_errors::ErrorGuaranteed; +use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, StashKey}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap}; @@ -996,17 +996,11 @@ impl<'tcx> Term<'tcx> { } } - /// This function returns the inner `AliasTy` if this term is a projection. - /// - /// FIXME: rename `AliasTy` to `AliasTerm` and make sure we correctly - /// deal with constants. - pub fn to_projection_term(&self, tcx: TyCtxt<'tcx>) -> Option<AliasTy<'tcx>> { + /// This function returns the inner `AliasTy` for a `ty::Alias` or `ConstKind::Unevaluated`. + pub fn to_alias_ty(&self, tcx: TyCtxt<'tcx>) -> Option<AliasTy<'tcx>> { match self.unpack() { - TermKind::Ty(ty) => match ty.kind() { - ty::Alias(kind, alias_ty) => match kind { - AliasKind::Projection | AliasKind::Inherent => Some(*alias_ty), - AliasKind::Opaque => None, - }, + TermKind::Ty(ty) => match *ty.kind() { + ty::Alias(_kind, alias_ty) => Some(alias_ty), _ => None, }, TermKind::Const(ct) => match ct.kind() { @@ -1439,14 +1433,26 @@ pub struct OpaqueHiddenType<'tcx> { } impl<'tcx> OpaqueHiddenType<'tcx> { - pub fn report_mismatch(&self, other: &Self, tcx: TyCtxt<'tcx>) -> ErrorGuaranteed { + pub fn report_mismatch( + &self, + other: &Self, + opaque_def_id: LocalDefId, + tcx: TyCtxt<'tcx>, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { + if let Some(diag) = tcx + .sess + .diagnostic() + .steal_diagnostic(tcx.def_span(opaque_def_id), StashKey::OpaqueHiddenTypeMismatch) + { + diag.cancel(); + } // Found different concrete types for the opaque type. let sub_diag = if self.span == other.span { TypeMismatchReason::ConflictType { span: self.span } } else { TypeMismatchReason::PreviousUse { span: self.span } }; - tcx.sess.emit_err(OpaqueHiddenTypeMismatch { + tcx.sess.create_err(OpaqueHiddenTypeMismatch { self_ty: self.ty, other_ty: other.ty, other_span: other.span, @@ -1882,7 +1888,20 @@ impl PartialEq for VariantDef { let Self { def_id: lhs_def_id, ctor: _, name: _, discr: _, fields: _, flags: _ } = &self; let Self { def_id: rhs_def_id, ctor: _, name: _, discr: _, fields: _, flags: _ } = other; - lhs_def_id == rhs_def_id + + let res = lhs_def_id == rhs_def_id; + + // Double check that implicit assumption detailed above. + if cfg!(debug_assertions) && res { + let deep = self.ctor == other.ctor + && self.name == other.name + && self.discr == other.discr + && self.fields == other.fields + && self.flags == other.flags; + assert!(deep, "VariantDef for the same def-id has differing data"); + } + + res } } @@ -1937,7 +1956,15 @@ impl PartialEq for FieldDef { let Self { did: rhs_did, name: _, vis: _ } = other; - lhs_did == rhs_did + let res = lhs_did == rhs_did; + + // Double check that implicit assumption detailed above. + if cfg!(debug_assertions) && res { + let deep = self.name == other.name && self.vis == other.vis; + assert!(deep, "FieldDef for the same def-id has differing data"); + } + + res } } @@ -2187,8 +2214,8 @@ impl<'tcx> TyCtxt<'tcx> { let impl_trait_ref2 = self.impl_trait_ref(def_id2); // If either trait impl references an error, they're allowed to overlap, // as one of them essentially doesn't exist. - if impl_trait_ref1.map_or(false, |tr| tr.subst_identity().references_error()) - || impl_trait_ref2.map_or(false, |tr| tr.subst_identity().references_error()) + if impl_trait_ref1.is_some_and(|tr| tr.subst_identity().references_error()) + || impl_trait_ref2.is_some_and(|tr| tr.subst_identity().references_error()) { return Some(ImplOverlapKind::Permitted { marker: false }); } @@ -2209,7 +2236,7 @@ impl<'tcx> TyCtxt<'tcx> { let is_marker_overlap = { let is_marker_impl = |trait_ref: Option<EarlyBinder<TraitRef<'_>>>| -> bool { - trait_ref.map_or(false, |tr| self.trait_def(tr.skip_binder().def_id).is_marker) + trait_ref.is_some_and(|tr| self.trait_def(tr.skip_binder().def_id).is_marker) }; is_marker_impl(impl_trait_ref1) && is_marker_impl(impl_trait_ref2) }; diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs index 810c388ebbe..a2e77d9cdfe 100644 --- a/compiler/rustc_middle/src/ty/parameterized.rs +++ b/compiler/rustc_middle/src/ty/parameterized.rs @@ -56,6 +56,7 @@ trivially_parameterized_over_tcx! { std::string::String, crate::metadata::ModChild, crate::middle::codegen_fn_attrs::CodegenFnAttrs, + crate::middle::debugger_visualizer::DebuggerVisualizerFile, crate::middle::exported_symbols::SymbolExportInfo, crate::middle::resolve_bound_vars::ObjectLifetimeDefault, crate::mir::ConstQualifs, @@ -91,7 +92,6 @@ trivially_parameterized_over_tcx! { rustc_session::cstore::ForeignModule, rustc_session::cstore::LinkagePreference, rustc_session::cstore::NativeLib, - rustc_span::DebuggerVisualizerFile, rustc_span::ExpnData, rustc_span::ExpnHash, rustc_span::ExpnId, diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 4491d78648f..a064174e261 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1164,6 +1164,22 @@ pub trait PrettyPrinter<'tcx>: traits.entry(trait_ref).or_default().extend(proj_ty); } + fn pretty_print_inherent_projection( + self, + alias_ty: &ty::AliasTy<'tcx>, + ) -> Result<Self::Path, Self::Error> { + let def_key = self.tcx().def_key(alias_ty.def_id); + self.path_generic_args( + |cx| { + cx.path_append( + |cx| cx.path_qualified(alias_ty.self_ty(), None), + &def_key.disambiguated_data, + ) + }, + &alias_ty.substs[1..], + ) + } + fn ty_infer_name(&self, _: ty::TyVid) -> Option<Symbol> { None } @@ -2821,7 +2837,11 @@ define_print_and_forward_display! { } ty::AliasTy<'tcx> { - p!(print_def_path(self.def_id, self.substs)); + if let DefKind::Impl { of_trait: false } = cx.tcx().def_kind(cx.tcx().parent(self.def_id)) { + p!(pretty_print_inherent_projection(self)) + } else { + p!(print_def_path(self.def_id, self.substs)); + } } ty::ClosureKind { diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 5a0571f4bb7..e04dbbff9a7 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -151,10 +151,14 @@ pub struct TypeckResults<'tcx> { /// this field will be set to `Some(ErrorGuaranteed)`. pub tainted_by_errors: Option<ErrorGuaranteed>, - /// All the opaque types that have hidden types set - /// by this function. We also store the - /// type here, so that mir-borrowck can use it as a hint for figuring out hidden types, - /// even if they are only set in dead code (which doesn't show up in MIR). + /// All the opaque types that have hidden types set by this function. + /// We also store the type here, so that the compiler can use it as a hint + /// for figuring out hidden types, even if they are only set in dead code + /// (which doesn't show up in MIR). + /// + /// These types are mapped back to the opaque's identity substitutions + /// (with erased regions), which is why we don't associated substs with any + /// of these usages. pub concrete_opaque_types: FxIndexMap<LocalDefId, ty::OpaqueHiddenType<'tcx>>, /// Tracks the minimum captures required for a closure; diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index eb903ebfd99..ba05135638e 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -518,6 +518,42 @@ impl<'tcx> TyCtxt<'tcx> { Ok(()) } + /// Checks whether each generic argument is simply a unique generic placeholder. + /// + /// This is used in the new solver, which canonicalizes params to placeholders + /// for better caching. + pub fn uses_unique_placeholders_ignoring_regions( + self, + substs: SubstsRef<'tcx>, + ) -> Result<(), NotUniqueParam<'tcx>> { + let mut seen = GrowableBitSet::default(); + for arg in substs { + match arg.unpack() { + // Ignore regions, since we can't resolve those in a canonicalized + // query in the trait solver. + GenericArgKind::Lifetime(_) => {} + GenericArgKind::Type(t) => match t.kind() { + ty::Placeholder(p) => { + if !seen.insert(p.bound.var) { + return Err(NotUniqueParam::DuplicateParam(t.into())); + } + } + _ => return Err(NotUniqueParam::NotParam(t.into())), + }, + GenericArgKind::Const(c) => match c.kind() { + ty::ConstKind::Placeholder(p) => { + if !seen.insert(p.bound) { + return Err(NotUniqueParam::DuplicateParam(c.into())); + } + } + _ => return Err(NotUniqueParam::NotParam(c.into())), + }, + } + } + + Ok(()) + } + /// Returns `true` if `def_id` refers to a closure (e.g., `|x| x * 2`). Note /// that closures have a `DefId`, but the closure *expression* also /// has a `HirId` that is located within the context where the diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index f346cd48347..0282492a261 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -340,6 +340,8 @@ mir_build_uncovered = {$count -> *[other] patterns `{$witness_1}`, `{$witness_2}`, `{$witness_3}` and {$remainder} more } not covered +mir_build_privately_uninhabited = pattern `{$witness_1}` is currently uninhabited, but this variant contains private fields which may become inhabited in the future + mir_build_pattern_not_covered = refutable pattern in {$origin} .pattern_ty = the matched value is of type `{$pattern_ty}` diff --git a/compiler/rustc_mir_build/src/build/block.rs b/compiler/rustc_mir_build/src/build/block.rs index 609ab19289c..ab4cd24881f 100644 --- a/compiler/rustc_mir_build/src/build/block.rs +++ b/compiler/rustc_mir_build/src/build/block.rs @@ -351,7 +351,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } let popped = this.block_context.pop(); - assert!(popped.map_or(false, |bf| bf.is_statement())); + assert!(popped.is_some_and(|bf| bf.is_statement())); } // Then, the block may have an optional trailing expression which is a “return” value @@ -367,7 +367,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { unpack!(block = this.expr_into_dest(destination, block, expr)); let popped = this.block_context.pop(); - assert!(popped.map_or(false, |bf| bf.is_tail_expr())); + assert!(popped.is_some_and(|bf| bf.is_tail_expr())); } else { // If a block has no trailing expression, then it is given an implicit return type. // This return type is usually `()`, unless the block is diverging, in which case the diff --git a/compiler/rustc_mir_build/src/build/expr/as_operand.rs b/compiler/rustc_mir_build/src/build/expr/as_operand.rs index 6941da331fc..744111edb84 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_operand.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_operand.rs @@ -118,7 +118,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let category = Category::of(&expr.kind).unwrap(); debug!(?category, ?expr.kind); match category { - Category::Constant if let NeedsTemporary::No = needs_temporary || !expr.ty.needs_drop(this.tcx, this.param_env) => { + Category::Constant + if matches!(needs_temporary, NeedsTemporary::No) + || !expr.ty.needs_drop(this.tcx, this.param_env) => + { let constant = this.as_constant(expr); block.and(Operand::Constant(Box::new(constant))) } @@ -126,7 +129,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let operand = unpack!(block = this.as_temp(block, scope, expr, Mutability::Mut)); // Overwrite temp local info if we have something more interesting to record. if !matches!(local_info, LocalInfo::Boring) { - let decl_info = this.local_decls[operand].local_info.as_mut().assert_crate_local(); + let decl_info = + this.local_decls[operand].local_info.as_mut().assert_crate_local(); if let LocalInfo::Boring | LocalInfo::BlockTailTemp(_) = **decl_info { **decl_info = local_info; } diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs index 0105a265ffb..bcab4c0d24b 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs @@ -15,6 +15,7 @@ use rustc_middle::mir::Place; use rustc_middle::mir::*; use rustc_middle::thir::*; use rustc_middle::ty::cast::{mir_cast_kind, CastTy}; +use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{self, Ty, UpvarSubsts}; use rustc_span::Span; @@ -225,49 +226,63 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); let (op,ty) = (Operand::Move(discr), discr_ty); - if let Abi::Scalar(scalar) = layout.unwrap().abi{ - if let Primitive::Int(_, signed) = scalar.primitive() { - let range = scalar.valid_range(&this.tcx); - // FIXME: Handle wraparound cases too. - if range.end >= range.start { - let mut assumer = |range: u128, bin_op: BinOp| { - // We will be overwriting this val if our scalar is signed value - // because sign extension on unsigned types might cause unintended things - let mut range_val = - ConstantKind::from_bits(this.tcx, range, ty::ParamEnv::empty().and(discr_ty)); - let bool_ty = this.tcx.types.bool; - if signed { - let scalar_size_extend = scalar.size(&this.tcx).sign_extend(range); - let discr_layout = this.tcx.layout_of(this.param_env.and(discr_ty)); - let truncated_val = discr_layout.unwrap().size.truncate(scalar_size_extend); - range_val = ConstantKind::from_bits( - this.tcx, - truncated_val, - ty::ParamEnv::empty().and(discr_ty), - ); - } - let lit_op = this.literal_operand(expr.span, range_val); - let is_bin_op = this.temp(bool_ty, expr_span); - this.cfg.push_assign( - block, - source_info, - is_bin_op, - Rvalue::BinaryOp(bin_op, Box::new(((lit_op), (Operand::Copy(discr))))), - ); - this.cfg.push( - block, - Statement { - source_info, - kind: StatementKind::Intrinsic(Box::new(NonDivergingIntrinsic::Assume( - Operand::Copy(is_bin_op), - ))), - }, - ) - }; - assumer(range.end, BinOp::Ge); - assumer(range.start, BinOp::Le); - } - } + if let Abi::Scalar(scalar) = layout.unwrap().abi + && !scalar.is_always_valid(&this.tcx) + && let Primitive::Int(int_width, _signed) = scalar.primitive() + { + let unsigned_ty = int_width.to_ty(this.tcx, false); + let unsigned_place = this.temp(unsigned_ty, expr_span); + this.cfg.push_assign( + block, + source_info, + unsigned_place, + Rvalue::Cast(CastKind::IntToInt, Operand::Copy(discr), unsigned_ty)); + + let bool_ty = this.tcx.types.bool; + let range = scalar.valid_range(&this.tcx); + let merge_op = + if range.start <= range.end { + BinOp::BitAnd + } else { + BinOp::BitOr + }; + + let mut comparer = |range: u128, bin_op: BinOp| -> Place<'tcx> { + let range_val = + ConstantKind::from_bits(this.tcx, range, ty::ParamEnv::empty().and(unsigned_ty)); + let lit_op = this.literal_operand(expr.span, range_val); + let is_bin_op = this.temp(bool_ty, expr_span); + this.cfg.push_assign( + block, + source_info, + is_bin_op, + Rvalue::BinaryOp(bin_op, Box::new((Operand::Copy(unsigned_place), lit_op))), + ); + is_bin_op + }; + let assert_place = if range.start == 0 { + comparer(range.end, BinOp::Le) + } else { + let start_place = comparer(range.start, BinOp::Ge); + let end_place = comparer(range.end, BinOp::Le); + let merge_place = this.temp(bool_ty, expr_span); + this.cfg.push_assign( + block, + source_info, + merge_place, + Rvalue::BinaryOp(merge_op, Box::new((Operand::Move(start_place), Operand::Move(end_place)))), + ); + merge_place + }; + this.cfg.push( + block, + Statement { + source_info, + kind: StatementKind::Intrinsic(Box::new(NonDivergingIntrinsic::Assume( + Operand::Move(assert_place), + ))), + }, + ); } (op,ty) @@ -481,10 +496,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { })))) } - ExprKind::OffsetOf { container, fields } => block.and(Rvalue::NullaryOp( - NullOp::OffsetOf(fields), - this.tcx.erase_regions(container), - )), + ExprKind::OffsetOf { container, fields } => { + block.and(Rvalue::NullaryOp(NullOp::OffsetOf(fields), container)) + } ExprKind::Literal { .. } | ExprKind::NamedConst { .. } diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index 56c87c402fe..7331f8ecaa9 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -325,10 +325,10 @@ impl DropTree { entry_points.sort(); for (drop_idx, drop_data) in self.drops.iter_enumerated().rev() { - if entry_points.last().map_or(false, |entry_point| entry_point.0 == drop_idx) { + if entry_points.last().is_some_and(|entry_point| entry_point.0 == drop_idx) { let block = *blocks[drop_idx].get_or_insert_with(|| T::make_block(cfg)); needs_block[drop_idx] = Block::Own; - while entry_points.last().map_or(false, |entry_point| entry_point.0 == drop_idx) { + while entry_points.last().is_some_and(|entry_point| entry_point.0 == drop_idx) { let entry_block = entry_points.pop().unwrap().1; T::add_entry(cfg, entry_block, block); } @@ -644,24 +644,27 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } }; - if let Some(destination) = destination { - if let Some(value) = value { + match (destination, value) { + (Some(destination), Some(value)) => { debug!("stmt_expr Break val block_context.push(SubExpr)"); self.block_context.push(BlockFrame::SubExpr); unpack!(block = self.expr_into_dest(destination, block, value)); self.block_context.pop(); - } else { + } + (Some(destination), None) => { self.cfg.push_assign_unit(block, source_info, destination, self.tcx) } - } else { - assert!(value.is_none(), "`return` and `break` should have a destination"); - if self.tcx.sess.instrument_coverage() { + (None, Some(_)) => { + panic!("`return`, `become` and `break` with value and must have a destination") + } + (None, None) if self.tcx.sess.instrument_coverage() => { // Unlike `break` and `return`, which push an `Assign` statement to MIR, from which // a Coverage code region can be generated, `continue` needs no `Assign`; but // without one, the `InstrumentCoverage` MIR pass cannot generate a code region for // `continue`. Coverage will be missing unless we add a dummy `Assign` to MIR. self.add_dummy_assignment(span, block, source_info); } + (None, None) => {} } let region_scope = self.scopes.breakable_scopes[break_index].region_scope; @@ -671,12 +674,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } else { self.scopes.breakable_scopes[break_index].continue_drops.as_mut().unwrap() }; - let mut drop_idx = ROOT_NODE; - for scope in &self.scopes.scopes[scope_index + 1..] { - for drop in &scope.drops { - drop_idx = drops.add_drop(*drop, drop_idx); - } - } + + let drop_idx = self.scopes.scopes[scope_index + 1..] + .iter() + .flat_map(|scope| &scope.drops) + .fold(ROOT_NODE, |drop_idx, &drop| drops.add_drop(drop, drop_idx)); + drops.add_entry(block, drop_idx); // `build_drop_trees` doesn't have access to our source_info, so we @@ -728,7 +731,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn leave_top_scope(&mut self, block: BasicBlock) -> BasicBlock { // If we are emitting a `drop` statement, we need to have the cached // diverge cleanup pads ready in case that drop panics. - let needs_cleanup = self.scopes.scopes.last().map_or(false, |scope| scope.needs_cleanup()); + let needs_cleanup = self.scopes.scopes.last().is_some_and(|scope| scope.needs_cleanup()); let is_generator = self.generator_kind.is_some(); let unwind_to = if needs_cleanup { self.diverge_cleanup() } else { DropIdx::MAX }; diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index dcdeaf008d6..7c0df201bc2 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -781,6 +781,8 @@ pub(crate) struct PatternNotCovered<'s, 'tcx> { pub interpreted_as_const: Option<InterpretedAsConst>, #[subdiagnostic] pub adt_defined_here: Option<AdtDefinedHere<'tcx>>, + #[note(mir_build_privately_uninhabited)] + pub witness_1_is_privately_uninhabited: Option<()>, #[note(mir_build_pattern_ty)] pub _p: (), pub pattern_ty: Ty<'tcx>, diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index f46eb5a0ce1..b20495d602e 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -130,6 +130,7 @@ impl<'tcx> Cx<'tcx> { ExprKind::Pointer { cast: PointerCast::Unsize, source: self.thir.exprs.push(expr) } } Adjust::Pointer(cast) => ExprKind::Pointer { cast, source: self.thir.exprs.push(expr) }, + Adjust::NeverToAny if adjustment.target.is_never() => return expr, Adjust::NeverToAny => ExprKind::NeverToAny { source: self.thir.exprs.push(expr) }, Adjust::Deref(None) => { adjust_span(&mut expr); @@ -332,7 +333,7 @@ impl<'tcx> Cx<'tcx> { } else if let Some(box_item) = tcx.lang_items().owned_box() { if let hir::ExprKind::Path(hir::QPath::TypeRelative(ty, fn_path)) = fun.kind && let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind - && path.res.opt_def_id().map_or(false, |did| did == box_item) + && path.res.opt_def_id().is_some_and(|did| did == box_item) && fn_path.ident.name == sym::new && let [value] = args { @@ -955,7 +956,7 @@ impl<'tcx> Cx<'tcx> { let is_upvar = self .tcx .upvars_mentioned(self.body_owner) - .map_or(false, |upvars| upvars.contains_key(&var_hir_id)); + .is_some_and(|upvars| upvars.contains_key(&var_hir_id)); debug!( "convert_var({:?}): is_upvar={}, body_owner={:?}", diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 3d4f2cad047..c11a4f7229d 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -491,12 +491,30 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { AdtDefinedHere { adt_def_span, ty, variants } }; + // Emit an extra note if the first uncovered witness is + // visibly uninhabited anywhere in the current crate. + let witness_1_is_privately_uninhabited = + if cx.tcx.features().exhaustive_patterns + && let Some(witness_1) = witnesses.get(0) + && let ty::Adt(adt, substs) = witness_1.ty().kind() + && adt.is_enum() + && let Constructor::Variant(variant_index) = witness_1.ctor() + { + let variant = adt.variant(*variant_index); + let inhabited = variant.inhabited_predicate(cx.tcx, *adt).subst(cx.tcx, substs); + assert!(inhabited.apply(cx.tcx, cx.param_env, cx.module)); + !inhabited.apply_ignore_module(cx.tcx, cx.param_env) + } else { + false + }; + self.error = Err(self.tcx.sess.emit_err(PatternNotCovered { span: pat.span, origin, uncovered: Uncovered::new(pat.span, &cx, witnesses), inform, interpreted_as_const, + witness_1_is_privately_uninhabited: witness_1_is_privately_uninhabited.then_some(()), _p: (), pattern_ty, let_suggestion, diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs index adb09c509d2..0fe49b8a1bb 100644 --- a/compiler/rustc_mir_transform/src/const_prop_lint.rs +++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs @@ -363,7 +363,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { let left_size = self.ecx.layout_of(left_ty).ok()?.size; let right_size = r.layout.size; let r_bits = r.to_scalar().to_bits(right_size).ok(); - if r_bits.map_or(false, |b| b >= left_size.bits() as u128) { + if r_bits.is_some_and(|b| b >= left_size.bits() as u128) { debug!("check_binary_op: reporting assert for {:?}", location); let source_info = self.body().source_info(location); let panic = AssertKind::Overflow( diff --git a/compiler/rustc_mir_transform/src/coverage/debug.rs b/compiler/rustc_mir_transform/src/coverage/debug.rs index e554c470646..6a3d42511ac 100644 --- a/compiler/rustc_mir_transform/src/coverage/debug.rs +++ b/compiler/rustc_mir_transform/src/coverage/debug.rs @@ -118,7 +118,7 @@ use rustc_middle::mir::spanview::{self, SpanViewable}; use rustc_data_structures::fx::FxHashMap; use rustc_middle::mir::coverage::*; -use rustc_middle::mir::{self, BasicBlock, TerminatorKind}; +use rustc_middle::mir::{self, BasicBlock}; use rustc_middle::ty::TyCtxt; use rustc_span::Span; @@ -639,7 +639,7 @@ pub(super) fn dump_coverage_spanview<'tcx>( let def_id = mir_source.def_id(); let span_viewables = span_viewables(tcx, mir_body, basic_coverage_blocks, &coverage_spans); - let mut file = create_dump_file(tcx, "html", false, pass_name, &0, mir_body) + let mut file = create_dump_file(tcx, "html", false, pass_name, &0i32, mir_body) .expect("Unexpected error creating MIR spanview HTML file"); let crate_name = tcx.crate_name(def_id.krate); let item_name = tcx.def_path(def_id).to_filename_friendly_no_crate(); @@ -740,7 +740,7 @@ pub(super) fn dump_coverage_graphviz<'tcx>( .join("\n ") )); } - let mut file = create_dump_file(tcx, "dot", false, pass_name, &0, mir_body) + let mut file = create_dump_file(tcx, "dot", false, pass_name, &0i32, mir_body) .expect("Unexpected error creating BasicCoverageBlock graphviz DOT file"); graphviz_writer .write_graphviz(tcx, &mut file) @@ -796,7 +796,7 @@ fn bcb_to_string_sections<'tcx>( } let non_term_blocks = bcb_data.basic_blocks[0..len - 1] .iter() - .map(|&bb| format!("{:?}: {}", bb, term_type(&mir_body[bb].terminator().kind))) + .map(|&bb| format!("{:?}: {}", bb, mir_body[bb].terminator().kind.name())) .collect::<Vec<_>>(); if non_term_blocks.len() > 0 { sections.push(non_term_blocks.join("\n")); @@ -804,28 +804,7 @@ fn bcb_to_string_sections<'tcx>( sections.push(format!( "{:?}: {}", bcb_data.basic_blocks.last().unwrap(), - term_type(&bcb_data.terminator(mir_body).kind) + bcb_data.terminator(mir_body).kind.name(), )); sections } - -/// Returns a simple string representation of a `TerminatorKind` variant, independent of any -/// values it might hold. -pub(super) fn term_type(kind: &TerminatorKind<'_>) -> &'static str { - match kind { - TerminatorKind::Goto { .. } => "Goto", - TerminatorKind::SwitchInt { .. } => "SwitchInt", - TerminatorKind::Resume => "Resume", - TerminatorKind::Terminate => "Terminate", - TerminatorKind::Return => "Return", - TerminatorKind::Unreachable => "Unreachable", - TerminatorKind::Drop { .. } => "Drop", - TerminatorKind::Call { .. } => "Call", - TerminatorKind::Assert { .. } => "Assert", - TerminatorKind::Yield { .. } => "Yield", - TerminatorKind::GeneratorDrop => "GeneratorDrop", - TerminatorKind::FalseEdge { .. } => "FalseEdge", - TerminatorKind::FalseUnwind { .. } => "FalseUnwind", - TerminatorKind::InlineAsm { .. } => "InlineAsm", - } -} diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs index 986d2fd190d..ea1223fbca6 100644 --- a/compiler/rustc_mir_transform/src/coverage/graph.rs +++ b/compiler/rustc_mir_transform/src/coverage/graph.rs @@ -9,6 +9,7 @@ use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::coverage::*; use rustc_middle::mir::{self, BasicBlock, BasicBlockData, Terminator, TerminatorKind}; +use std::cmp::Ordering; use std::ops::{Index, IndexMut}; const ID_SEPARATOR: &str = ","; @@ -212,8 +213,12 @@ impl CoverageGraph { } #[inline(always)] - pub fn dominators(&self) -> &Dominators<BasicCoverageBlock> { - self.dominators.as_ref().unwrap() + pub fn rank_partial_cmp( + &self, + a: BasicCoverageBlock, + b: BasicCoverageBlock, + ) -> Option<Ordering> { + self.dominators.as_ref().unwrap().rank_partial_cmp(a, b) } } @@ -650,26 +655,6 @@ pub(super) fn find_loop_backedges( let mut backedges = IndexVec::from_elem_n(Vec::<BasicCoverageBlock>::new(), num_bcbs); // Identify loops by their backedges. - // - // The computational complexity is bounded by: n(s) x d where `n` is the number of - // `BasicCoverageBlock` nodes (the simplified/reduced representation of the CFG derived from the - // MIR); `s` is the average number of successors per node (which is most likely less than 2, and - // independent of the size of the function, so it can be treated as a constant); - // and `d` is the average number of dominators per node. - // - // The average number of dominators depends on the size and complexity of the function, and - // nodes near the start of the function's control flow graph typically have less dominators - // than nodes near the end of the CFG. Without doing a detailed mathematical analysis, I - // think the resulting complexity has the characteristics of O(n log n). - // - // The overall complexity appears to be comparable to many other MIR transform algorithms, and I - // don't expect that this function is creating a performance hot spot, but if this becomes an - // issue, there may be ways to optimize the `dominates` algorithm (as indicated by an - // existing `FIXME` comment in that code), or possibly ways to optimize it's usage here, perhaps - // by keeping track of results for visited `BasicCoverageBlock`s if they can be used to short - // circuit downstream `dominates` checks. - // - // For now, that kind of optimization seems unnecessarily complicated. for (bcb, _) in basic_coverage_blocks.iter_enumerated() { for &successor in &basic_coverage_blocks.successors[bcb] { if basic_coverage_blocks.dominates(successor, bcb) { diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index 287ae217087..d27200419e2 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -1,4 +1,3 @@ -use super::debug::term_type; use super::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB}; use itertools::Itertools; @@ -40,7 +39,7 @@ impl CoverageStatement { "{}: @{}.{}: {:?}", source_range_no_file(tcx, span), bb.index(), - term_type(&term.kind), + term.kind.name(), term.kind ) } @@ -345,7 +344,7 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> { // before the dominated equal spans). When later comparing two spans in // order, the first will either dominate the second, or they will have no // dominator relationship. - self.basic_coverage_blocks.dominators().rank_partial_cmp(a.bcb, b.bcb) + self.basic_coverage_blocks.rank_partial_cmp(a.bcb, b.bcb) } } else { // Sort hi() in reverse order so shorter spans are attempted after longer spans. diff --git a/compiler/rustc_mir_transform/src/coverage/tests.rs b/compiler/rustc_mir_transform/src/coverage/tests.rs index 83a335197b3..90b58933df7 100644 --- a/compiler/rustc_mir_transform/src/coverage/tests.rs +++ b/compiler/rustc_mir_transform/src/coverage/tests.rs @@ -25,7 +25,6 @@ //! to: `rustc_span::create_default_session_globals_then(|| { test_here(); })`. use super::counters; -use super::debug; use super::graph; use super::spans; @@ -188,12 +187,12 @@ fn debug_basic_blocks(mir_body: &Body<'_>) -> String { | TerminatorKind::Goto { target } | TerminatorKind::InlineAsm { destination: Some(target), .. } | TerminatorKind::Yield { resume: target, .. } => { - format!("{}{:?}:{} -> {:?}", sp, bb, debug::term_type(kind), target) + format!("{}{:?}:{} -> {:?}", sp, bb, kind.name(), target) } TerminatorKind::SwitchInt { targets, .. } => { - format!("{}{:?}:{} -> {:?}", sp, bb, debug::term_type(kind), targets) + format!("{}{:?}:{} -> {:?}", sp, bb, kind.name(), targets) } - _ => format!("{}{:?}:{}", sp, bb, debug::term_type(kind)), + _ => format!("{}{:?}:{}", sp, bb, kind.name()), } }) .collect::<Vec<_>>() @@ -215,7 +214,7 @@ fn print_mir_graphviz(name: &str, mir_body: &Body<'_>) { " {:?} [label=\"{:?}: {}\"];\n{}", bb, bb, - debug::term_type(&data.terminator().kind), + data.terminator().kind.name(), mir_body .basic_blocks .successors(bb) @@ -244,7 +243,7 @@ fn print_coverage_graphviz( " {:?} [label=\"{:?}: {}\"];\n{}", bcb, bcb, - debug::term_type(&bcb_data.terminator(mir_body).kind), + bcb_data.terminator(mir_body).kind.name(), basic_coverage_blocks .successors(bcb) .map(|successor| { format!(" {:?} -> {:?};", bcb, successor) }) diff --git a/compiler/rustc_mir_transform/src/ctfe_limit.rs b/compiler/rustc_mir_transform/src/ctfe_limit.rs index 1b3ac78fbc6..bf5722b3d00 100644 --- a/compiler/rustc_mir_transform/src/ctfe_limit.rs +++ b/compiler/rustc_mir_transform/src/ctfe_limit.rs @@ -47,7 +47,7 @@ fn has_back_edge( return false; } // Check if any of the dominators of the node are also the node's successor. - doms.dominators(node).any(|dom| node_data.terminator().successors().any(|succ| succ == dom)) + node_data.terminator().successors().any(|succ| doms.dominates(succ, node)) } fn insert_counter(basic_block_data: &mut BasicBlockData<'_>) { diff --git a/compiler/rustc_mir_transform/src/dump_mir.rs b/compiler/rustc_mir_transform/src/dump_mir.rs index 594cbd8977e..746e3d9652d 100644 --- a/compiler/rustc_mir_transform/src/dump_mir.rs +++ b/compiler/rustc_mir_transform/src/dump_mir.rs @@ -12,7 +12,7 @@ use rustc_session::config::OutputType; pub struct Marker(pub &'static str); impl<'tcx> MirPass<'tcx> for Marker { - fn name(&self) -> &str { + fn name(&self) -> &'static str { self.0 } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 12f955d46bd..6c2e22a70b9 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -168,6 +168,18 @@ impl<'tcx> Inliner<'tcx> { ) -> Result<std::ops::Range<BasicBlock>, &'static str> { let callee_attrs = self.tcx.codegen_fn_attrs(callsite.callee.def_id()); self.check_codegen_attributes(callsite, callee_attrs)?; + + let terminator = caller_body[callsite.block].terminator.as_ref().unwrap(); + let TerminatorKind::Call { args, destination, .. } = &terminator.kind else { bug!() }; + let destination_ty = destination.ty(&caller_body.local_decls, self.tcx).ty; + for arg in args { + if !arg.ty(&caller_body.local_decls, self.tcx).is_sized(self.tcx, self.param_env) { + // We do not allow inlining functions with unsized params. Inlining these functions + // could create unsized locals, which are unsound and being phased out. + return Err("Call has unsized argument"); + } + } + self.check_mir_is_available(caller_body, &callsite.callee)?; let callee_body = try_instance_mir(self.tcx, callsite.callee.def)?; self.check_mir_body(callsite, callee_body, callee_attrs)?; @@ -189,9 +201,6 @@ impl<'tcx> Inliner<'tcx> { // Check call signature compatibility. // Normally, this shouldn't be required, but trait normalization failure can create a // validation ICE. - let terminator = caller_body[callsite.block].terminator.as_ref().unwrap(); - let TerminatorKind::Call { args, destination, .. } = &terminator.kind else { bug!() }; - let destination_ty = destination.ty(&caller_body.local_decls, self.tcx).ty; let output_type = callee_body.return_ty(); if !util::is_subtype(self.tcx, self.param_env, output_type, destination_ty) { trace!(?output_type, ?destination_ty); diff --git a/compiler/rustc_mir_transform/src/nrvo.rs b/compiler/rustc_mir_transform/src/nrvo.rs index 85b26220b1e..5ce96012b90 100644 --- a/compiler/rustc_mir_transform/src/nrvo.rs +++ b/compiler/rustc_mir_transform/src/nrvo.rs @@ -108,7 +108,7 @@ fn local_eligible_for_nrvo(body: &mut mir::Body<'_>) -> Option<Local> { // If multiple different locals are copied to the return place. We can't pick a // single one to rename. - if copied_to_return_place.map_or(false, |old| old != returned_local) { + if copied_to_return_place.is_some_and(|old| old != returned_local) { return None; } diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs index e1b65823a5a..710eed3ed38 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -6,7 +6,7 @@ use crate::{validate, MirPass}; /// Just like `MirPass`, except it cannot mutate `Body`. pub trait MirLint<'tcx> { - fn name(&self) -> &str { + fn name(&self) -> &'static str { let name = std::any::type_name::<Self>(); if let Some((_, tail)) = name.rsplit_once(':') { tail } else { name } } @@ -26,7 +26,7 @@ impl<'tcx, T> MirPass<'tcx> for Lint<T> where T: MirLint<'tcx>, { - fn name(&self) -> &str { + fn name(&self) -> &'static str { self.0.name() } @@ -49,7 +49,7 @@ impl<'tcx, T> MirPass<'tcx> for WithMinOptLevel<T> where T: MirPass<'tcx>, { - fn name(&self) -> &str { + fn name(&self) -> &'static str { self.1.name() } @@ -121,7 +121,7 @@ fn run_passes_inner<'tcx>( validate_body(tcx, body, format!("before pass {}", name)); } - pass.run_pass(tcx, body); + tcx.sess.time(name, || pass.run_pass(tcx, body)); if dump_enabled { dump_mir_for_pass(tcx, body, &name, true); diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 1b96df3aed5..e59219321b7 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -74,7 +74,7 @@ pub fn simplify_cfg<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { } impl<'tcx> MirPass<'tcx> for SimplifyCfg { - fn name(&self) -> &str { + fn name(&self) -> &'static str { &self.name() } diff --git a/compiler/rustc_mir_transform/src/ssa.rs b/compiler/rustc_mir_transform/src/ssa.rs index 2b404efccc7..e8e4246b797 100644 --- a/compiler/rustc_mir_transform/src/ssa.rs +++ b/compiler/rustc_mir_transform/src/ssa.rs @@ -31,11 +31,11 @@ pub struct SsaLocals { /// We often encounter MIR bodies with 1 or 2 basic blocks. In those cases, it's unnecessary to /// actually compute dominators, we can just compare block indices because bb0 is always the first /// block, and in any body all other blocks are always dominated by bb0. -struct SmallDominators { - inner: Option<Dominators<BasicBlock>>, +struct SmallDominators<'a> { + inner: Option<&'a Dominators<BasicBlock>>, } -impl SmallDominators { +impl SmallDominators<'_> { fn dominates(&self, first: Location, second: Location) -> bool { if first.block == second.block { first.statement_index <= second.statement_index @@ -198,14 +198,14 @@ enum LocationExtended { Arg, } -struct SsaVisitor { - dominators: SmallDominators, +struct SsaVisitor<'a> { + dominators: SmallDominators<'a>, assignments: IndexVec<Local, Set1<LocationExtended>>, assignment_order: Vec<Local>, direct_uses: IndexVec<Local, u32>, } -impl<'tcx> Visitor<'tcx> for SsaVisitor { +impl<'tcx> Visitor<'tcx> for SsaVisitor<'_> { fn visit_local(&mut self, local: Local, ctxt: PlaceContext, loc: Location) { match ctxt { PlaceContext::MutatingUse(MutatingUseContext::Projection) diff --git a/compiler/rustc_monomorphize/src/partitioning/mod.rs b/compiler/rustc_monomorphize/src/partitioning/mod.rs index c10180ee3f4..eafe57a0c02 100644 --- a/compiler/rustc_monomorphize/src/partitioning/mod.rs +++ b/compiler/rustc_monomorphize/src/partitioning/mod.rs @@ -250,13 +250,13 @@ where cgu.create_size_estimate(tcx); } - debug_dump(tcx, "INITIAL PARTITIONING:", initial_partitioning.codegen_units.iter()); + debug_dump(tcx, "INITIAL PARTITIONING", &initial_partitioning.codegen_units); // Merge until we have at most `max_cgu_count` codegen units. { let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_merge_cgus"); partitioner.merge_codegen_units(cx, &mut initial_partitioning); - debug_dump(tcx, "POST MERGING:", initial_partitioning.codegen_units.iter()); + debug_dump(tcx, "POST MERGING", &initial_partitioning.codegen_units); } // In the next step, we use the inlining map to determine which additional @@ -272,7 +272,7 @@ where cgu.create_size_estimate(tcx); } - debug_dump(tcx, "POST INLINING:", post_inlining.codegen_units.iter()); + debug_dump(tcx, "POST INLINING", &post_inlining.codegen_units); // Next we try to make as many symbols "internal" as possible, so LLVM has // more freedom to optimize. @@ -322,6 +322,8 @@ where result.sort_by(|a, b| a.name().as_str().cmp(b.name().as_str())); + debug_dump(tcx, "FINAL", &result); + result } @@ -346,33 +348,37 @@ struct PostInliningPartitioning<'tcx> { internalization_candidates: FxHashSet<MonoItem<'tcx>>, } -fn debug_dump<'a, 'tcx, I>(tcx: TyCtxt<'tcx>, label: &str, cgus: I) -where - I: Iterator<Item = &'a CodegenUnit<'tcx>>, - 'tcx: 'a, -{ +fn debug_dump<'a, 'tcx: 'a>(tcx: TyCtxt<'tcx>, label: &str, cgus: &[CodegenUnit<'tcx>]) { let dump = move || { use std::fmt::Write; + let num_cgus = cgus.len(); + let max = cgus.iter().map(|cgu| cgu.size_estimate()).max().unwrap(); + let min = cgus.iter().map(|cgu| cgu.size_estimate()).min().unwrap(); + let ratio = max as f64 / min as f64; + let s = &mut String::new(); - let _ = writeln!(s, "{label}"); + let _ = writeln!( + s, + "{label} ({num_cgus} CodegenUnits, max={max}, min={min}, max/min={ratio:.1}):" + ); for cgu in cgus { let _ = - writeln!(s, "CodegenUnit {} estimated size {} :", cgu.name(), cgu.size_estimate()); + writeln!(s, "CodegenUnit {} estimated size {}:", cgu.name(), cgu.size_estimate()); for (mono_item, linkage) in cgu.items() { let symbol_name = mono_item.symbol_name(tcx).name; let symbol_hash_start = symbol_name.rfind('h'); let symbol_hash = symbol_hash_start.map_or("<no hash>", |i| &symbol_name[i..]); - let _ = writeln!( + let _ = with_no_trimmed_paths!(writeln!( s, " - {} [{:?}] [{}] estimated size {}", mono_item, linkage, symbol_hash, mono_item.size_estimate(tcx) - ); + )); } let _ = writeln!(s); diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index b0ab0f10624..1e6ac54964f 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -72,7 +72,7 @@ fn has_cfg_or_cfg_attr(attrs: &[Attribute]) -> bool { // Therefore, the absence of a literal `cfg` or `cfg_attr` guarantees that // we don't need to do any eager expansion. attrs.iter().any(|attr| { - attr.ident().map_or(false, |ident| ident.name == sym::cfg || ident.name == sym::cfg_attr) + attr.ident().is_some_and(|ident| ident.name == sym::cfg || ident.name == sym::cfg_attr) }) } diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 3002f23da75..c1454039685 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -238,6 +238,7 @@ impl<'a> DerefMut for SnapshotParser<'a> { impl<'a> Parser<'a> { #[rustc_lint_diagnostics] + #[track_caller] pub fn struct_span_err<S: Into<MultiSpan>>( &self, sp: S, @@ -844,7 +845,7 @@ impl<'a> Parser<'a> { // // `x.foo::<u32>>>(3)` let parsed_angle_bracket_args = - segment.args.as_ref().map_or(false, |args| args.is_angle_bracketed()); + segment.args.as_ref().is_some_and(|args| args.is_angle_bracketed()); debug!( "check_trailing_angle_brackets: parsed_angle_bracket_args={:?}", @@ -2609,7 +2610,7 @@ impl<'a> Parser<'a> { let TyKind::Path(qself, path) = &ty.kind else { return Ok(()) }; let qself_position = qself.as_ref().map(|qself| qself.position); for (i, segments) in path.segments.windows(2).enumerate() { - if qself_position.map(|pos| i < pos).unwrap_or(false) { + if qself_position.is_some_and(|pos| i < pos) { continue; } if let [a, b] = segments { diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index ee712a8e1b5..1b28f3c97e8 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1188,7 +1188,7 @@ impl<'a> Parser<'a> { // `token.kind` should not be compared here. // This is because the `snapshot.token.kind` is treated as the same as // that of the open delim in `TokenTreesReader::parse_token_tree`, even if they are different. - self.span_to_snippet(close_paren).map_or(false, |snippet| snippet == ")") + self.span_to_snippet(close_paren).is_ok_and(|snippet| snippet == ")") { let mut replacement_err = errors::ParenthesesWithStructFields { span, @@ -2078,7 +2078,7 @@ impl<'a> Parser<'a> { // Therefore, `token.kind` should not be compared here. if snapshot .span_to_snippet(snapshot.token.span) - .map_or(false, |snippet| snippet == "]") => + .is_ok_and(|snippet| snippet == "]") => { return Err(errors::MissingSemicolonBeforeArray { open_delim: open_delim_span, @@ -2773,7 +2773,7 @@ impl<'a> Parser<'a> { // We might have a `=>` -> `=` or `->` typo (issue #89396). if TokenKind::FatArrow .similar_tokens() - .map_or(false, |similar_tokens| similar_tokens.contains(&this.token.kind)) + .is_some_and(|similar_tokens| similar_tokens.contains(&this.token.kind)) { err.span_suggestion( this.token.span, @@ -3059,7 +3059,7 @@ impl<'a> Parser<'a> { } }; - let is_shorthand = parsed_field.as_ref().map_or(false, |f| f.is_shorthand); + let is_shorthand = parsed_field.as_ref().is_some_and(|f| f.is_shorthand); // A shorthand field can be turned into a full field with `:`. // We should point this out. self.check_or_expected(!is_shorthand, TokenType::Token(token::Colon)); diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index dc18d400f1e..3783ec41b7e 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -699,7 +699,7 @@ impl<'a> Parser<'a> { // ``` && self .span_to_snippet(self.prev_token.span) - .map_or(false, |snippet| snippet == "}") + .is_ok_and(|snippet| snippet == "}") && self.token.kind == token::Semi; let mut semicolon_span = self.token.span; if !is_unnecessary_semicolon { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 455d7b89f9c..c3189d1fefe 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -8,7 +8,6 @@ use crate::{errors, fluent_generated as fluent}; use rustc_ast::{ast, AttrStyle, Attribute, LitKind, MetaItemKind, MetaItemLit, NestedMetaItem}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, IntoDiagnosticArg, MultiSpan}; -use rustc_expand::base::resolve_path; use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; @@ -1817,7 +1816,7 @@ impl CheckAttrVisitor<'_> { || (is_simd && is_c) || (int_reprs == 1 && is_c - && item.map_or(false, |item| { + && item.is_some_and(|item| { if let ItemLike::Item(item) = item { return is_c_like_enum(item); } @@ -1916,6 +1915,10 @@ impl CheckAttrVisitor<'_> { /// Checks if the items on the `#[debugger_visualizer]` attribute are valid. fn check_debugger_visualizer(&self, attr: &Attribute, target: Target) -> bool { + // Here we only check that the #[debugger_visualizer] attribute is attached + // to nothing other than a module. All other checks are done in the + // `debugger_visualizer` query where they need to be done for decoding + // anyway. match target { Target::Mod => {} _ => { @@ -1924,53 +1927,7 @@ impl CheckAttrVisitor<'_> { } } - let Some(hints) = attr.meta_item_list() else { - self.tcx.sess.emit_err(errors::DebugVisualizerInvalid { span: attr.span }); - return false; - }; - - let hint = match hints.len() { - 1 => &hints[0], - _ => { - self.tcx.sess.emit_err(errors::DebugVisualizerInvalid { span: attr.span }); - return false; - } - }; - - let Some(meta_item) = hint.meta_item() else { - self.tcx.sess.emit_err(errors::DebugVisualizerInvalid { span: attr.span }); - return false; - }; - - let visualizer_path = match (meta_item.name_or_empty(), meta_item.value_str()) { - (sym::natvis_file, Some(value)) => value, - (sym::gdb_script_file, Some(value)) => value, - (_, _) => { - self.tcx.sess.emit_err(errors::DebugVisualizerInvalid { span: meta_item.span }); - return false; - } - }; - - let file = - match resolve_path(&self.tcx.sess.parse_sess, visualizer_path.as_str(), attr.span) { - Ok(file) => file, - Err(mut err) => { - err.emit(); - return false; - } - }; - - match std::fs::File::open(&file) { - Ok(_) => true, - Err(error) => { - self.tcx.sess.emit_err(errors::DebugVisualizerUnreadable { - span: meta_item.span, - file: &file, - error, - }); - false - } - } + true } /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros. @@ -2138,7 +2095,7 @@ impl CheckAttrVisitor<'_> { | sym::feature | sym::repr | sym::target_feature - ) && attr.meta_item_list().map_or(false, |list| list.is_empty()) + ) && attr.meta_item_list().is_some_and(|list| list.is_empty()) { errors::UnusedNote::EmptyList { name: attr.name_or_empty() } } else if matches!( diff --git a/compiler/rustc_passes/src/debugger_visualizer.rs b/compiler/rustc_passes/src/debugger_visualizer.rs index 8ea95b3f383..3483f7da528 100644 --- a/compiler/rustc_passes/src/debugger_visualizer.rs +++ b/compiler/rustc_passes/src/debugger_visualizer.rs @@ -1,60 +1,69 @@ //! Detecting usage of the `#[debugger_visualizer]` attribute. -use hir::CRATE_HIR_ID; -use rustc_data_structures::fx::FxHashSet; +use rustc_ast::Attribute; use rustc_data_structures::sync::Lrc; use rustc_expand::base::resolve_path; -use rustc_hir as hir; -use rustc_hir::HirId; -use rustc_middle::query::{LocalCrate, Providers}; -use rustc_middle::ty::TyCtxt; -use rustc_span::{sym, DebuggerVisualizerFile, DebuggerVisualizerType}; +use rustc_middle::{ + middle::debugger_visualizer::{DebuggerVisualizerFile, DebuggerVisualizerType}, + query::{LocalCrate, Providers}, + ty::TyCtxt, +}; +use rustc_session::Session; +use rustc_span::sym; -use crate::errors::DebugVisualizerUnreadable; +use crate::errors::{DebugVisualizerInvalid, DebugVisualizerUnreadable}; -fn check_for_debugger_visualizer( - tcx: TyCtxt<'_>, - hir_id: HirId, - debugger_visualizers: &mut FxHashSet<DebuggerVisualizerFile>, -) { - let attrs = tcx.hir().attrs(hir_id); - for attr in attrs { +impl DebuggerVisualizerCollector<'_> { + fn check_for_debugger_visualizer(&mut self, attr: &Attribute) { if attr.has_name(sym::debugger_visualizer) { - let Some(list) = attr.meta_item_list() else { - continue + let Some(hints) = attr.meta_item_list() else { + self.sess.emit_err(DebugVisualizerInvalid { span: attr.span }); + return; }; - let meta_item = match list.len() { - 1 => match list[0].meta_item() { - Some(meta_item) => meta_item, - _ => continue, - }, - _ => continue, + let hint = if hints.len() == 1 { + &hints[0] + } else { + self.sess.emit_err(DebugVisualizerInvalid { span: attr.span }); + return; }; - let visualizer_type = match meta_item.name_or_empty() { - sym::natvis_file => DebuggerVisualizerType::Natvis, - sym::gdb_script_file => DebuggerVisualizerType::GdbPrettyPrinter, - _ => continue, + let Some(meta_item) = hint.meta_item() else { + self.sess.emit_err(DebugVisualizerInvalid { span: attr.span }); + return; }; - let file = match meta_item.value_str() { - Some(value) => { - match resolve_path(&tcx.sess.parse_sess, value.as_str(), attr.span) { - Ok(file) => file, - _ => continue, + let (visualizer_type, visualizer_path) = + match (meta_item.name_or_empty(), meta_item.value_str()) { + (sym::natvis_file, Some(value)) => (DebuggerVisualizerType::Natvis, value), + (sym::gdb_script_file, Some(value)) => { + (DebuggerVisualizerType::GdbPrettyPrinter, value) } - } - None => continue, - }; + (_, _) => { + self.sess.emit_err(DebugVisualizerInvalid { span: meta_item.span }); + return; + } + }; + + let file = + match resolve_path(&self.sess.parse_sess, visualizer_path.as_str(), attr.span) { + Ok(file) => file, + Err(mut err) => { + err.emit(); + return; + } + }; match std::fs::read(&file) { Ok(contents) => { - debugger_visualizers - .insert(DebuggerVisualizerFile::new(Lrc::from(contents), visualizer_type)); + self.visualizers.push(DebuggerVisualizerFile::new( + Lrc::from(contents), + visualizer_type, + file, + )); } Err(error) => { - tcx.sess.emit_err(DebugVisualizerUnreadable { + self.sess.emit_err(DebugVisualizerUnreadable { span: meta_item.span, file: &file, error, @@ -65,29 +74,30 @@ fn check_for_debugger_visualizer( } } -/// Traverses and collects the debugger visualizers for a specific crate. -fn debugger_visualizers(tcx: TyCtxt<'_>, _: LocalCrate) -> Vec<DebuggerVisualizerFile> { - // Initialize the collector. - let mut debugger_visualizers = FxHashSet::default(); +struct DebuggerVisualizerCollector<'a> { + sess: &'a Session, + visualizers: Vec<DebuggerVisualizerFile>, +} - // Collect debugger visualizers in this crate. - tcx.hir().for_each_module(|id| { - check_for_debugger_visualizer( - tcx, - tcx.hir().local_def_id_to_hir_id(id), - &mut debugger_visualizers, - ) - }); +impl<'ast> rustc_ast::visit::Visitor<'ast> for DebuggerVisualizerCollector<'_> { + fn visit_attribute(&mut self, attr: &'ast Attribute) { + self.check_for_debugger_visualizer(attr); + rustc_ast::visit::walk_attribute(self, attr); + } +} - // Collect debugger visualizers on the crate attributes. - check_for_debugger_visualizer(tcx, CRATE_HIR_ID, &mut debugger_visualizers); +/// Traverses and collects the debugger visualizers for a specific crate. +fn debugger_visualizers(tcx: TyCtxt<'_>, _: LocalCrate) -> Vec<DebuggerVisualizerFile> { + let resolver_and_krate = tcx.resolver_for_lowering(()).borrow(); + let krate = &*resolver_and_krate.1; - // Extract out the found debugger_visualizer items. - let mut visualizers = debugger_visualizers.into_iter().collect::<Vec<_>>(); + let mut visitor = DebuggerVisualizerCollector { sess: tcx.sess, visualizers: Vec::new() }; + rustc_ast::visit::Visitor::visit_crate(&mut visitor, krate); - // Sort the visualizers so we always get a deterministic query result. - visualizers.sort(); - visualizers + // We are collecting visualizers in AST-order, which is deterministic, + // so we don't need to do any explicit sorting in order to get a + // deterministic query result + visitor.visualizers } pub fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index f9060328f48..b81b7ad6013 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -554,10 +554,8 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> { let is_const = self.tcx.is_const_fn(def_id.to_def_id()) || self.tcx.is_const_trait_impl_raw(def_id.to_def_id()); - let is_stable = self - .tcx - .lookup_stability(def_id) - .map_or(false, |stability| stability.level.is_stable()); + let is_stable = + self.tcx.lookup_stability(def_id).is_some_and(|stability| stability.level.is_stable()); let missing_const_stability_attribute = self.tcx.lookup_const_stability(def_id).is_none(); let is_reachable = self.effective_visibilities.is_reachable(def_id); @@ -772,7 +770,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { // needs to have an error emitted. if features.const_trait_impl && *constness == hir::Constness::Const - && const_stab.map_or(false, |(stab, _)| stab.is_const_stable()) + && const_stab.is_some_and(|(stab, _)| stab.is_const_stable()) { self.tcx.sess.emit_err(errors::TraitImplConstStable { span: item.span }); } @@ -809,15 +807,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { ); let is_allowed_through_unstable_modules = |def_id| { - self.tcx - .lookup_stability(def_id) - .map(|stab| match stab.level { - StabilityLevel::Stable { allowed_through_unstable_modules, .. } => { - allowed_through_unstable_modules - } - _ => false, - }) - .unwrap_or(false) + self.tcx.lookup_stability(def_id).is_some_and(|stab| match stab.level { + StabilityLevel::Stable { allowed_through_unstable_modules, .. } => { + allowed_through_unstable_modules + } + _ => false, + }) }; if item_is_allowed && !is_allowed_through_unstable_modules(def_id) { diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 7b39cb0a068..65dfdf31e54 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -454,12 +454,14 @@ struct EmbargoVisitor<'tcx> { /// n::p::f() /// } macro_reachable: FxHashSet<(LocalDefId, LocalDefId)>, + /// Preliminary pass for marking all underlying types of `impl Trait`s as reachable. + impl_trait_pass: bool, /// Has something changed in the level map? changed: bool, } struct ReachEverythingInTheInterfaceVisitor<'a, 'tcx> { - effective_vis: Option<EffectiveVisibility>, + effective_vis: EffectiveVisibility, item_def_id: LocalDefId, ev: &'a mut EmbargoVisitor<'tcx>, level: Level, @@ -474,7 +476,7 @@ impl<'tcx> EmbargoVisitor<'tcx> { fn update( &mut self, def_id: LocalDefId, - inherited_effective_vis: Option<EffectiveVisibility>, + inherited_effective_vis: EffectiveVisibility, level: Level, ) { let nominal_vis = self.tcx.local_visibility(def_id); @@ -484,30 +486,27 @@ impl<'tcx> EmbargoVisitor<'tcx> { fn update_eff_vis( &mut self, def_id: LocalDefId, - inherited_effective_vis: Option<EffectiveVisibility>, + inherited_effective_vis: EffectiveVisibility, nominal_vis: Option<ty::Visibility>, level: Level, ) { - if let Some(inherited_effective_vis) = inherited_effective_vis { - let private_vis = - ty::Visibility::Restricted(self.tcx.parent_module_from_def_id(def_id)); - if Some(private_vis) != nominal_vis { - self.changed |= self.effective_visibilities.update( - def_id, - nominal_vis, - || private_vis, - inherited_effective_vis, - level, - self.tcx, - ); - } + let private_vis = ty::Visibility::Restricted(self.tcx.parent_module_from_def_id(def_id)); + if Some(private_vis) != nominal_vis { + self.changed |= self.effective_visibilities.update( + def_id, + nominal_vis, + || private_vis, + inherited_effective_vis, + level, + self.tcx, + ); } } fn reach( &mut self, def_id: LocalDefId, - effective_vis: Option<EffectiveVisibility>, + effective_vis: EffectiveVisibility, ) -> ReachEverythingInTheInterfaceVisitor<'_, 'tcx> { ReachEverythingInTheInterfaceVisitor { effective_vis, @@ -520,7 +519,7 @@ impl<'tcx> EmbargoVisitor<'tcx> { fn reach_through_impl_trait( &mut self, def_id: LocalDefId, - effective_vis: Option<EffectiveVisibility>, + effective_vis: EffectiveVisibility, ) -> ReachEverythingInTheInterfaceVisitor<'_, 'tcx> { ReachEverythingInTheInterfaceVisitor { effective_vis, @@ -532,9 +531,13 @@ impl<'tcx> EmbargoVisitor<'tcx> { // We have to make sure that the items that macros might reference // are reachable, since they might be exported transitively. - fn update_reachability_from_macro(&mut self, local_def_id: LocalDefId, md: &MacroDef) { + fn update_reachability_from_macro( + &mut self, + local_def_id: LocalDefId, + md: &MacroDef, + macro_ev: EffectiveVisibility, + ) { // Non-opaque macros cannot make other items more accessible than they already are. - let hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id); let attrs = self.tcx.hir().attrs(hir_id); if attr::find_transparency(attrs, md.macro_rules).0 != Transparency::Opaque { @@ -554,8 +557,6 @@ impl<'tcx> EmbargoVisitor<'tcx> { // Since we are starting from an externally visible module, // all the parents in the loop below are also guaranteed to be modules. let mut module_def_id = macro_module_def_id; - let macro_ev = self.get(local_def_id); - assert!(macro_ev.is_some()); loop { let changed_reachability = self.update_macro_reachable(module_def_id, macro_module_def_id, macro_ev); @@ -572,7 +573,7 @@ impl<'tcx> EmbargoVisitor<'tcx> { &mut self, module_def_id: LocalDefId, defining_mod: LocalDefId, - macro_ev: Option<EffectiveVisibility>, + macro_ev: EffectiveVisibility, ) -> bool { if self.macro_reachable.insert((module_def_id, defining_mod)) { self.update_macro_reachable_mod(module_def_id, defining_mod, macro_ev); @@ -586,7 +587,7 @@ impl<'tcx> EmbargoVisitor<'tcx> { &mut self, module_def_id: LocalDefId, defining_mod: LocalDefId, - macro_ev: Option<EffectiveVisibility>, + macro_ev: EffectiveVisibility, ) { let module = self.tcx.hir().get_module(module_def_id).0; for item_id in module.item_ids { @@ -618,7 +619,7 @@ impl<'tcx> EmbargoVisitor<'tcx> { def_kind: DefKind, vis: ty::Visibility, module: LocalDefId, - macro_ev: Option<EffectiveVisibility>, + macro_ev: EffectiveVisibility, ) { self.update(def_id, macro_ev, Level::Reachable); match def_kind { @@ -700,128 +701,53 @@ impl<'tcx> EmbargoVisitor<'tcx> { } impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { - type NestedFilter = nested_filter::All; - - /// We want to visit items in the context of their containing - /// module and so forth, so supply a crate for doing a deep walk. - fn nested_visit_map(&mut self) -> Self::Map { - self.tcx.hir() - } - fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { - let item_ev = match item.kind { - hir::ItemKind::Impl { .. } => { - let impl_ev = Option::<EffectiveVisibility>::of_impl( - item.owner_id.def_id, - self.tcx, - &self.effective_visibilities, - ); - - self.update_eff_vis(item.owner_id.def_id, impl_ev, None, Level::Direct); - impl_ev - } - _ => self.get(item.owner_id.def_id), - }; - - // Update levels of nested things. - match item.kind { - hir::ItemKind::Enum(ref def, _) => { - for variant in def.variants { - self.update(variant.def_id, item_ev, Level::Reachable); - let variant_ev = self.get(variant.def_id); - if let Some(ctor_def_id) = variant.data.ctor_def_id() { - self.update(ctor_def_id, variant_ev, Level::Reachable); - } - for field in variant.data.fields() { - self.update(field.def_id, variant_ev, Level::Reachable); - } - } - } - hir::ItemKind::Impl(ref impl_) => { - for impl_item_ref in impl_.items { - let def_id = impl_item_ref.id.owner_id.def_id; - let nominal_vis = - impl_.of_trait.is_none().then(|| self.tcx.local_visibility(def_id)); - self.update_eff_vis(def_id, item_ev, nominal_vis, Level::Direct); - } - } - hir::ItemKind::Trait(.., trait_item_refs) => { - for trait_item_ref in trait_item_refs { - self.update(trait_item_ref.id.owner_id.def_id, item_ev, Level::Reachable); - } - } - hir::ItemKind::Struct(ref def, _) | hir::ItemKind::Union(ref def, _) => { - if let Some(ctor_def_id) = def.ctor_def_id() { - self.update(ctor_def_id, item_ev, Level::Reachable); - } - for field in def.fields() { - self.update(field.def_id, item_ev, Level::Reachable); - } - } - hir::ItemKind::Macro(ref macro_def, _) => { - self.update_reachability_from_macro(item.owner_id.def_id, macro_def); - } - hir::ItemKind::ForeignMod { items, .. } => { - for foreign_item in items { - self.update(foreign_item.id.owner_id.def_id, item_ev, Level::Reachable); - } - } - - hir::ItemKind::OpaqueTy(..) - | hir::ItemKind::Use(..) - | hir::ItemKind::Static(..) - | hir::ItemKind::Const(..) - | hir::ItemKind::GlobalAsm(..) - | hir::ItemKind::TyAlias(..) - | hir::ItemKind::Mod(..) - | hir::ItemKind::TraitAlias(..) - | hir::ItemKind::Fn(..) - | hir::ItemKind::ExternCrate(..) => {} + if self.impl_trait_pass + && let hir::ItemKind::OpaqueTy(ref opaque) = item.kind + && !opaque.in_trait { + // FIXME: This is some serious pessimization intended to workaround deficiencies + // in the reachability pass (`middle/reachable.rs`). Types are marked as link-time + // reachable if they are returned via `impl Trait`, even from private functions. + let pub_ev = EffectiveVisibility::from_vis(ty::Visibility::Public); + self.reach_through_impl_trait(item.owner_id.def_id, pub_ev) + .generics() + .predicates() + .ty(); + return; } - // Mark all items in interfaces of reachable items as reachable. + // Update levels of nested things and mark all items + // in interfaces of reachable items as reachable. + let item_ev = self.get(item.owner_id.def_id); match item.kind { - // The interface is empty. - hir::ItemKind::Macro(..) | hir::ItemKind::ExternCrate(..) => {} - // All nested items are checked by `visit_item`. - hir::ItemKind::Mod(..) => {} - // Handled in `rustc_resolve`. - hir::ItemKind::Use(..) => {} - // The interface is empty. - hir::ItemKind::GlobalAsm(..) => {} - hir::ItemKind::OpaqueTy(ref opaque) => { - // HACK(jynelson): trying to infer the type of `impl trait` breaks `async-std` (and `pub async fn` in general) - // Since rustdoc never needs to do codegen and doesn't care about link-time reachability, - // mark this as unreachable. - // See https://github.com/rust-lang/rust/issues/75100 - if !opaque.in_trait && !self.tcx.sess.opts.actually_rustdoc { - // FIXME: This is some serious pessimization intended to workaround deficiencies - // in the reachability pass (`middle/reachable.rs`). Types are marked as link-time - // reachable if they are returned via `impl Trait`, even from private functions. - let exist_ev = Some(EffectiveVisibility::from_vis(ty::Visibility::Public)); - self.reach_through_impl_trait(item.owner_id.def_id, exist_ev) - .generics() - .predicates() - .ty(); + // The interface is empty, and no nested items. + hir::ItemKind::Use(..) + | hir::ItemKind::ExternCrate(..) + | hir::ItemKind::GlobalAsm(..) => {} + // The interface is empty, and all nested items are processed by `visit_item`. + hir::ItemKind::Mod(..) | hir::ItemKind::OpaqueTy(..) => {} + hir::ItemKind::Macro(ref macro_def, _) => { + if let Some(item_ev) = item_ev { + self.update_reachability_from_macro(item.owner_id.def_id, macro_def, item_ev); } } - // Visit everything. hir::ItemKind::Const(..) | hir::ItemKind::Static(..) | hir::ItemKind::Fn(..) | hir::ItemKind::TyAlias(..) => { - if item_ev.is_some() { + if let Some(item_ev) = item_ev { self.reach(item.owner_id.def_id, item_ev).generics().predicates().ty(); } } hir::ItemKind::Trait(.., trait_item_refs) => { - if item_ev.is_some() { + if let Some(item_ev) = item_ev { self.reach(item.owner_id.def_id, item_ev).generics().predicates(); for trait_item_ref in trait_item_refs { + self.update(trait_item_ref.id.owner_id.def_id, item_ev, Level::Reachable); + let tcx = self.tcx; let mut reach = self.reach(trait_item_ref.id.owner_id.def_id, item_ev); - reach.generics().predicates(); if trait_item_ref.kind == AssocItemKind::Type @@ -835,13 +761,18 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { } } hir::ItemKind::TraitAlias(..) => { - if item_ev.is_some() { + if let Some(item_ev) = item_ev { self.reach(item.owner_id.def_id, item_ev).generics().predicates(); } } - // Visit everything except for private impl items. hir::ItemKind::Impl(ref impl_) => { - if item_ev.is_some() { + if let Some(item_ev) = Option::<EffectiveVisibility>::of_impl( + item.owner_id.def_id, + self.tcx, + &self.effective_visibilities, + ) { + self.update_eff_vis(item.owner_id.def_id, item_ev, None, Level::Direct); + self.reach(item.owner_id.def_id, item_ev) .generics() .predicates() @@ -849,27 +780,32 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { .trait_ref(); for impl_item_ref in impl_.items { - let impl_item_ev = self.get(impl_item_ref.id.owner_id.def_id); + let def_id = impl_item_ref.id.owner_id.def_id; + let nominal_vis = + impl_.of_trait.is_none().then(|| self.tcx.local_visibility(def_id)); + self.update_eff_vis(def_id, item_ev, nominal_vis, Level::Direct); - if impl_item_ev.is_some() { - self.reach(impl_item_ref.id.owner_id.def_id, impl_item_ev) - .generics() - .predicates() - .ty(); + if let Some(impl_item_ev) = self.get(def_id) { + self.reach(def_id, impl_item_ev).generics().predicates().ty(); } } } } - - // Visit everything, but enum variants have their own levels. hir::ItemKind::Enum(ref def, _) => { - if item_ev.is_some() { + if let Some(item_ev) = item_ev { self.reach(item.owner_id.def_id, item_ev).generics().predicates(); } for variant in def.variants { - let variant_ev = self.get(variant.def_id); - if variant_ev.is_some() { + if let Some(item_ev) = item_ev { + self.update(variant.def_id, item_ev, Level::Reachable); + } + + if let Some(variant_ev) = self.get(variant.def_id) { + if let Some(ctor_def_id) = variant.data.ctor_def_id() { + self.update(ctor_def_id, variant_ev, Level::Reachable); + } for field in variant.data.fields() { + self.update(field.def_id, variant_ev, Level::Reachable); self.reach(field.def_id, variant_ev).ty(); } // Corner case: if the variant is reachable, but its @@ -877,18 +813,15 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { self.reach(item.owner_id.def_id, variant_ev).ty(); } if let Some(ctor_def_id) = variant.data.ctor_def_id() { - let ctor_ev = self.get(ctor_def_id); - if ctor_ev.is_some() { + if let Some(ctor_ev) = self.get(ctor_def_id) { self.reach(item.owner_id.def_id, ctor_ev).ty(); } } } } - // Visit everything, but foreign items have their own levels. hir::ItemKind::ForeignMod { items, .. } => { for foreign_item in items { - let foreign_item_ev = self.get(foreign_item.id.owner_id.def_id); - if foreign_item_ev.is_some() { + if let Some(foreign_item_ev) = self.get(foreign_item.id.owner_id.def_id) { self.reach(foreign_item.id.owner_id.def_id, foreign_item_ev) .generics() .predicates() @@ -896,34 +829,26 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { } } } - // Visit everything except for private fields. hir::ItemKind::Struct(ref struct_def, _) | hir::ItemKind::Union(ref struct_def, _) => { - if item_ev.is_some() { + if let Some(item_ev) = item_ev { self.reach(item.owner_id.def_id, item_ev).generics().predicates(); for field in struct_def.fields() { - let field_ev = self.get(field.def_id); - if field_ev.is_some() { + self.update(field.def_id, item_ev, Level::Reachable); + if let Some(field_ev) = self.get(field.def_id) { self.reach(field.def_id, field_ev).ty(); } } } if let Some(ctor_def_id) = struct_def.ctor_def_id() { - let ctor_ev = self.get(ctor_def_id); - if ctor_ev.is_some() { + if let Some(item_ev) = item_ev { + self.update(ctor_def_id, item_ev, Level::Reachable); + } + if let Some(ctor_ev) = self.get(ctor_def_id) { self.reach(item.owner_id.def_id, ctor_ev).ty(); } } } } - - intravisit::walk_item(self, item); - } - - fn visit_block(&mut self, b: &'tcx hir::Block<'tcx>) { - // Blocks can have public items, for example impls, but they always - // start as completely private regardless of publicity of a function, - // constant, type, field, etc., in which this block resides. - intravisit::walk_block(self, b); } } @@ -2205,12 +2130,24 @@ fn effective_visibilities(tcx: TyCtxt<'_>, (): ()) -> &EffectiveVisibilities { tcx, effective_visibilities: tcx.resolutions(()).effective_visibilities.clone(), macro_reachable: Default::default(), + // HACK(jynelson): trying to infer the type of `impl Trait` breaks `async-std` (and + // `pub async fn` in general). Since rustdoc never needs to do codegen and doesn't + // care about link-time reachability, keep them unreachable (issue #75100). + impl_trait_pass: !tcx.sess.opts.actually_rustdoc, changed: false, }; visitor.effective_visibilities.check_invariants(tcx, true); + if visitor.impl_trait_pass { + // Underlying types of `impl Trait`s are marked as reachable unconditionally, + // so this pass doesn't need to be a part of the fixed point iteration below. + tcx.hir().visit_all_item_likes_in_crate(&mut visitor); + visitor.impl_trait_pass = false; + visitor.changed = false; + } + loop { - tcx.hir().walk_toplevel_module(&mut visitor); + tcx.hir().visit_all_item_likes_in_crate(&mut visitor); if visitor.changed { visitor.changed = false; } else { diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index 7afcbebe274..4cf0f1305a7 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -23,8 +23,10 @@ use rustc_middle::arena::Arena; use rustc_middle::dep_graph::DepNodeIndex; use rustc_middle::dep_graph::{self, DepKind, DepKindStruct}; use rustc_middle::query::erase::{erase, restore, Erase}; -use rustc_middle::query::on_disk_cache::OnDiskCache; -use rustc_middle::query::plumbing::{DynamicQuery, QuerySystem, QuerySystemFns}; +use rustc_middle::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache}; +use rustc_middle::query::plumbing::{ + DynamicQuery, QueryKeyStringCache, QuerySystem, QuerySystemFns, +}; use rustc_middle::query::AsLocalKey; use rustc_middle::query::{ queries, DynamicQueries, ExternProviders, Providers, QueryCaches, QueryEngine, QueryStates, @@ -215,7 +217,6 @@ pub fn query_system<'tcx>( engine: engine(incremental), local_providers, extern_providers, - query_structs: make_dep_kind_array!(query_structs).to_vec(), encode_query_results: encode_all_query_results, try_mark_green: try_mark_green, }, diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index ebbf69a1007..244f0e84b43 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -81,8 +81,8 @@ impl QueryContext for QueryCtxt<'_> { fn try_collect_active_jobs(self) -> Option<QueryMap<DepKind>> { let mut jobs = QueryMap::default(); - for query in &self.query_system.fns.query_structs { - (query.try_collect_active_jobs)(self.tcx, &mut jobs); + for collect in super::TRY_COLLECT_ACTIVE_JOBS.iter() { + collect(self.tcx, &mut jobs); } Some(jobs) @@ -183,10 +183,8 @@ pub(super) fn encode_all_query_results<'tcx>( encoder: &mut CacheEncoder<'_, 'tcx>, query_result_index: &mut EncodedDepNodeIndex, ) { - for query in &tcx.query_system.fns.query_structs { - if let Some(encode) = query.encode_query_results { - encode(tcx, encoder, query_result_index); - } + for encode in super::ENCODE_QUERY_RESULTS.iter().copied().filter_map(|e| e) { + encode(tcx, encoder, query_result_index); } } @@ -476,6 +474,16 @@ where } } +macro_rules! item_if_cached { + ([] $tokens:tt) => {}; + ([(cache) $($rest:tt)*] { $($tokens:tt)* }) => { + $($tokens)* + }; + ([$other:tt $($modifiers:tt)*] $tokens:tt) => { + item_if_cached! { [$($modifiers)*] $tokens } + }; +} + macro_rules! expand_if_cached { ([], $tokens:expr) => {{ None @@ -506,173 +514,209 @@ macro_rules! define_queries { ( $($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty,)*) => { - mod get_query_incr { - use super::*; - $( + pub(crate) mod query_impl { $(pub mod $name { + use super::super::*; + use std::marker::PhantomData; + + pub mod get_query_incr { + use super::*; + // Adding `__rust_end_short_backtrace` marker to backtraces so that we emit the frames // when `RUST_BACKTRACE=1`, add a new mod with `$name` here is to allow duplicate naming - pub mod $name { - use super::*; - #[inline(never)] - pub fn __rust_end_short_backtrace<'tcx>( - tcx: TyCtxt<'tcx>, - span: Span, - key: queries::$name::Key<'tcx>, - mode: QueryMode, - ) -> Option<Erase<queries::$name::Value<'tcx>>> { - get_query_incr( - query_config::$name::config(tcx), - QueryCtxt::new(tcx), - span, - key, - mode + #[inline(never)] + pub fn __rust_end_short_backtrace<'tcx>( + tcx: TyCtxt<'tcx>, + span: Span, + key: queries::$name::Key<'tcx>, + mode: QueryMode, + ) -> Option<Erase<queries::$name::Value<'tcx>>> { + get_query_incr( + QueryType::config(tcx), + QueryCtxt::new(tcx), + span, + key, + mode + ) + } + } + + pub mod get_query_non_incr { + use super::*; + + #[inline(never)] + pub fn __rust_end_short_backtrace<'tcx>( + tcx: TyCtxt<'tcx>, + span: Span, + key: queries::$name::Key<'tcx>, + __mode: QueryMode, + ) -> Option<Erase<queries::$name::Value<'tcx>>> { + Some(get_query_non_incr( + QueryType::config(tcx), + QueryCtxt::new(tcx), + span, + key, + )) + } + } + + pub fn dynamic_query<'tcx>() -> DynamicQuery<'tcx, queries::$name::Storage<'tcx>> { + DynamicQuery { + name: stringify!($name), + eval_always: is_eval_always!([$($modifiers)*]), + dep_kind: dep_graph::DepKind::$name, + handle_cycle_error: handle_cycle_error!([$($modifiers)*]), + query_state: offset_of!(QueryStates<'tcx> => $name), + query_cache: offset_of!(QueryCaches<'tcx> => $name), + cache_on_disk: |tcx, key| ::rustc_middle::query::cached::$name(tcx, key), + execute_query: |tcx, key| erase(tcx.$name(key)), + compute: |tcx, key| { + __rust_begin_short_backtrace(|| + queries::$name::provided_to_erased( + tcx, + call_provider!([$($modifiers)*][tcx, $name, key]) + ) ) - } + }, + can_load_from_disk: should_ever_cache_on_disk!([$($modifiers)*] true false), + try_load_from_disk: should_ever_cache_on_disk!([$($modifiers)*] { + |tcx, key, prev_index, index| { + if ::rustc_middle::query::cached::$name(tcx, key) { + let value = $crate::plumbing::try_load_from_disk::< + queries::$name::ProvidedValue<'tcx> + >( + tcx, + prev_index, + index, + ); + value.map(|value| queries::$name::provided_to_erased(tcx, value)) + } else { + None + } + } + } { + |_tcx, _key, _prev_index, _index| None + }), + value_from_cycle_error: |tcx, cycle| { + let result: queries::$name::Value<'tcx> = Value::from_cycle_error(tcx, cycle); + erase(result) + }, + loadable_from_disk: |_tcx, _key, _index| { + should_ever_cache_on_disk!([$($modifiers)*] { + ::rustc_middle::query::cached::$name(_tcx, _key) && + $crate::plumbing::loadable_from_disk(_tcx, _index) + } { + false + }) + }, + hash_result: hash_result!([$($modifiers)*][queries::$name::Value<'tcx>]), + format_value: |value| format!("{:?}", restore::<queries::$name::Value<'tcx>>(*value)), } - )* - } + } - mod get_query_non_incr { - use super::*; + #[derive(Copy, Clone, Default)] + pub struct QueryType<'tcx> { + data: PhantomData<&'tcx ()> + } - $( - pub mod $name { - use super::*; - #[inline(never)] - pub fn __rust_end_short_backtrace<'tcx>( - tcx: TyCtxt<'tcx>, - span: Span, - key: queries::$name::Key<'tcx>, - __mode: QueryMode, - ) -> Option<Erase<queries::$name::Value<'tcx>>> { - Some(get_query_non_incr( - query_config::$name::config(tcx), - QueryCtxt::new(tcx), - span, - key, - )) + impl<'tcx> QueryConfigRestored<'tcx> for QueryType<'tcx> { + type RestoredValue = queries::$name::Value<'tcx>; + type Config = DynamicConfig< + 'tcx, + queries::$name::Storage<'tcx>, + { is_anon!([$($modifiers)*]) }, + { depth_limit!([$($modifiers)*]) }, + { feedable!([$($modifiers)*]) }, + >; + + #[inline(always)] + fn config(tcx: TyCtxt<'tcx>) -> Self::Config { + DynamicConfig { + dynamic: &tcx.query_system.dynamic_queries.$name, } } - )* - } - pub(crate) fn engine(incremental: bool) -> QueryEngine { - if incremental { - QueryEngine { - $($name: get_query_incr::$name::__rust_end_short_backtrace,)* - } - } else { - QueryEngine { - $($name: get_query_non_incr::$name::__rust_end_short_backtrace,)* + #[inline(always)] + fn restore(value: <Self::Config as QueryConfig<QueryCtxt<'tcx>>>::Value) -> Self::RestoredValue { + restore::<queries::$name::Value<'tcx>>(value) } } - } - - #[allow(nonstandard_style)] - mod query_config { - use std::marker::PhantomData; - $( - #[derive(Copy, Clone, Default)] - pub struct $name<'tcx> { - data: PhantomData<&'tcx ()> - } - )* - } + pub fn try_collect_active_jobs<'tcx>(tcx: TyCtxt<'tcx>, qmap: &mut QueryMap<DepKind>) { + let make_query = |tcx, key| { + let kind = rustc_middle::dep_graph::DepKind::$name; + let name = stringify!($name); + $crate::plumbing::create_query_frame(tcx, rustc_middle::query::descs::$name, key, kind, name) + }; + tcx.query_system.states.$name.try_collect_active_jobs( + tcx, + make_query, + qmap, + ).unwrap(); + } - #[allow(nonstandard_style)] - mod dynamic_query { - use super::*; + pub fn alloc_self_profile_query_strings<'tcx>(tcx: TyCtxt<'tcx>, string_cache: &mut QueryKeyStringCache) { + $crate::profiling_support::alloc_self_profile_query_strings_for_query_cache( + tcx, + stringify!($name), + &tcx.query_system.caches.$name, + string_cache, + ) + } - $( - pub(super) fn $name<'tcx>() -> DynamicQuery<'tcx, queries::$name::Storage<'tcx>> { - DynamicQuery { - name: stringify!($name), - eval_always: is_eval_always!([$($modifiers)*]), - dep_kind: dep_graph::DepKind::$name, - handle_cycle_error: handle_cycle_error!([$($modifiers)*]), - query_state: offset_of!(QueryStates<'tcx> => $name), - query_cache: offset_of!(QueryCaches<'tcx> => $name), - cache_on_disk: |tcx, key| ::rustc_middle::query::cached::$name(tcx, key), - execute_query: |tcx, key| erase(tcx.$name(key)), - compute: |tcx, key| { - __rust_begin_short_backtrace(|| - queries::$name::provided_to_erased( - tcx, - call_provider!([$($modifiers)*][tcx, $name, key]) - ) - ) - }, - can_load_from_disk: should_ever_cache_on_disk!([$($modifiers)*] true false), - try_load_from_disk: should_ever_cache_on_disk!([$($modifiers)*] { - |tcx, key, prev_index, index| { - if ::rustc_middle::query::cached::$name(tcx, key) { - let value = $crate::plumbing::try_load_from_disk::< - queries::$name::ProvidedValue<'tcx> - >( - tcx, - prev_index, - index, - ); - value.map(|value| queries::$name::provided_to_erased(tcx, value)) - } else { - None - } - } - } { - |_tcx, _key, _prev_index, _index| None - }), - value_from_cycle_error: |tcx, cycle| { - let result: queries::$name::Value<'tcx> = Value::from_cycle_error(tcx, cycle); - erase(result) - }, - loadable_from_disk: |_tcx, _key, _index| { - should_ever_cache_on_disk!([$($modifiers)*] { - ::rustc_middle::query::cached::$name(_tcx, _key) && - $crate::plumbing::loadable_from_disk(_tcx, _index) - } { - false - }) - }, - hash_result: hash_result!([$($modifiers)*][queries::$name::Value<'tcx>]), - format_value: |value| format!("{:?}", restore::<queries::$name::Value<'tcx>>(*value)), - } + item_if_cached! { [$($modifiers)*] { + pub fn encode_query_results<'tcx>( + tcx: TyCtxt<'tcx>, + encoder: &mut CacheEncoder<'_, 'tcx>, + query_result_index: &mut EncodedDepNodeIndex + ) { + $crate::plumbing::encode_query_results::<query_impl::$name::QueryType<'tcx>>( + query_impl::$name::QueryType::config(tcx), + QueryCtxt::new(tcx), + encoder, + query_result_index, + ) } - )* - } + }} + })*} - $(impl<'tcx> QueryConfigRestored<'tcx> for query_config::$name<'tcx> { - type RestoredValue = queries::$name::Value<'tcx>; - type Config = DynamicConfig< - 'tcx, - queries::$name::Storage<'tcx>, - { is_anon!([$($modifiers)*]) }, - { depth_limit!([$($modifiers)*]) }, - { feedable!([$($modifiers)*]) }, - >; - - #[inline(always)] - fn config(tcx: TyCtxt<'tcx>) -> Self::Config { - DynamicConfig { - dynamic: &tcx.query_system.dynamic_queries.$name, + pub(crate) fn engine(incremental: bool) -> QueryEngine { + if incremental { + QueryEngine { + $($name: query_impl::$name::get_query_incr::__rust_end_short_backtrace,)* + } + } else { + QueryEngine { + $($name: query_impl::$name::get_query_non_incr::__rust_end_short_backtrace,)* } } - - #[inline(always)] - fn restore(value: <Self::Config as QueryConfig<QueryCtxt<'tcx>>>::Value) -> Self::RestoredValue { - restore::<queries::$name::Value<'tcx>>(value) - } - })* + } pub fn dynamic_queries<'tcx>() -> DynamicQueries<'tcx> { DynamicQueries { $( - $name: dynamic_query::$name(), + $name: query_impl::$name::dynamic_query(), )* } } + // These arrays are used for iteration and can't be indexed by `DepKind`. + + const TRY_COLLECT_ACTIVE_JOBS: &[for<'tcx> fn(TyCtxt<'tcx>, &mut QueryMap<DepKind>)] = + &[$(query_impl::$name::try_collect_active_jobs),*]; + + const ALLOC_SELF_PROFILE_QUERY_STRINGS: &[ + for<'tcx> fn(TyCtxt<'tcx>, &mut QueryKeyStringCache) + ] = &[$(query_impl::$name::alloc_self_profile_query_strings),*]; + + const ENCODE_QUERY_RESULTS: &[ + Option<for<'tcx> fn( + TyCtxt<'tcx>, + &mut CacheEncoder<'_, 'tcx>, + &mut EncodedDepNodeIndex) + > + ] = &[$(expand_if_cached!([$($modifiers)*], query_impl::$name::encode_query_results)),*]; + #[allow(nonstandard_style)] mod query_callbacks { use super::*; @@ -731,71 +775,13 @@ macro_rules! define_queries { } $(pub(crate) fn $name<'tcx>()-> DepKindStruct<'tcx> { - $crate::plumbing::query_callback::<query_config::$name<'tcx>>( + $crate::plumbing::query_callback::<query_impl::$name::QueryType<'tcx>>( is_anon!([$($modifiers)*]), is_eval_always!([$($modifiers)*]), ) })* } - mod query_structs { - use super::*; - use rustc_middle::query::plumbing::{QueryKeyStringCache, QueryStruct}; - use rustc_middle::dep_graph::DepKind; - use crate::QueryConfigRestored; - - pub(super) const fn dummy_query_struct<'tcx>() -> QueryStruct<'tcx> { - fn noop_try_collect_active_jobs(_: TyCtxt<'_>, _: &mut QueryMap<DepKind>) -> Option<()> { - None - } - fn noop_alloc_self_profile_query_strings(_: TyCtxt<'_>, _: &mut QueryKeyStringCache) {} - - QueryStruct { - try_collect_active_jobs: noop_try_collect_active_jobs, - alloc_self_profile_query_strings: noop_alloc_self_profile_query_strings, - encode_query_results: None, - } - } - - pub(super) use dummy_query_struct as Null; - pub(super) use dummy_query_struct as Red; - pub(super) use dummy_query_struct as TraitSelect; - pub(super) use dummy_query_struct as CompileCodegenUnit; - pub(super) use dummy_query_struct as CompileMonoItem; - - $( - pub(super) const fn $name<'tcx>() -> QueryStruct<'tcx> { QueryStruct { - try_collect_active_jobs: |tcx, qmap| { - let make_query = |tcx, key| { - let kind = rustc_middle::dep_graph::DepKind::$name; - let name = stringify!($name); - $crate::plumbing::create_query_frame(tcx, rustc_middle::query::descs::$name, key, kind, name) - }; - tcx.query_system.states.$name.try_collect_active_jobs( - tcx, - make_query, - qmap, - ) - }, - alloc_self_profile_query_strings: |tcx, string_cache| { - $crate::profiling_support::alloc_self_profile_query_strings_for_query_cache( - tcx, - stringify!($name), - &tcx.query_system.caches.$name, - string_cache, - ) - }, - encode_query_results: expand_if_cached!([$($modifiers)*], |tcx, encoder, query_result_index| - $crate::plumbing::encode_query_results::<super::query_config::$name<'tcx>>( - super::query_config::$name::config(tcx), - QueryCtxt::new(tcx), - encoder, - query_result_index, - ) - ), - }})* - } - pub fn query_callbacks<'tcx>(arena: &'tcx Arena<'tcx>) -> &'tcx [DepKindStruct<'tcx>] { arena.alloc_from_iter(make_dep_kind_array!(query_callbacks)) } diff --git a/compiler/rustc_query_impl/src/profiling_support.rs b/compiler/rustc_query_impl/src/profiling_support.rs index e042ee62dfe..fbc6db93e01 100644 --- a/compiler/rustc_query_impl/src/profiling_support.rs +++ b/compiler/rustc_query_impl/src/profiling_support.rs @@ -243,7 +243,7 @@ pub fn alloc_self_profile_query_strings(tcx: TyCtxt<'_>) { let mut string_cache = QueryKeyStringCache::new(); - for query in &tcx.query_system.fns.query_structs { - (query.alloc_self_profile_query_strings)(tcx, &mut string_cache); + for alloc in super::ALLOC_SELF_PROFILE_QUERY_STRINGS.iter() { + alloc(tcx, &mut string_cache) } } diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 8de4d06fe78..c0d7386dd6a 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -656,7 +656,7 @@ impl<K: DepKind> DepGraphData<K> { /// current compilation session. Used in various assertions #[inline] pub fn is_index_green(&self, prev_index: SerializedDepNodeIndex) -> bool { - self.colors.get(prev_index).map_or(false, |c| c.is_green()) + self.colors.get(prev_index).is_some_and(|c| c.is_green()) } #[inline] @@ -677,7 +677,7 @@ impl<K: DepKind> DepGraphData<K> { impl<K: DepKind> DepGraph<K> { #[inline] pub fn dep_node_exists(&self, dep_node: &DepNode<K>) -> bool { - self.data.as_ref().map_or(false, |data| data.dep_node_exists(dep_node)) + self.data.as_ref().is_some_and(|data| data.dep_node_exists(dep_node)) } /// Checks whether a previous work product exists for `v` and, if @@ -955,7 +955,7 @@ impl<K: DepKind> DepGraph<K> { /// Returns true if the given node has been marked as green during the /// current compilation session. Used in various assertions pub fn is_green(&self, dep_node: &DepNode<K>) -> bool { - self.node_color(dep_node).map_or(false, |c| c.is_green()) + self.node_color(dep_node).is_some_and(|c| c.is_green()) } /// This method loads all on-disk cacheable query results into memory, so diff --git a/compiler/rustc_query_system/src/ich/impls_syntax.rs b/compiler/rustc_query_system/src/ich/impls_syntax.rs index 8865ecf3e05..e673d5b8c6e 100644 --- a/compiler/rustc_query_system/src/ich/impls_syntax.rs +++ b/compiler/rustc_query_system/src/ich/impls_syntax.rs @@ -24,7 +24,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for [ast::Attribute] { .iter() .filter(|attr| { !attr.is_doc_comment() - && !attr.ident().map_or(false, |ident| hcx.is_ignored_attr(ident.name)) + && !attr.ident().is_some_and(|ident| hcx.is_ignored_attr(ident.name)) }) .collect(); @@ -38,7 +38,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for [ast::Attribute] { impl<'ctx> rustc_ast::HashStableContext for StableHashingContext<'ctx> { fn hash_attr(&mut self, attr: &ast::Attribute, hasher: &mut StableHasher) { // Make sure that these have been filtered out. - debug_assert!(!attr.ident().map_or(false, |ident| self.is_ignored_attr(ident.name))); + debug_assert!(!attr.ident().is_some_and(|ident| self.is_ignored_attr(ident.name))); debug_assert!(!attr.is_doc_comment()); let ast::Attribute { kind, id: _, style, span } = attr; diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 5f2ec656d1d..f45f7ca5da6 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -22,7 +22,7 @@ use { rustc_data_structures::fx::FxHashSet, rustc_data_structures::sync::Lock, rustc_data_structures::sync::Lrc, - rustc_data_structures::{jobserver, OnDrop}, + rustc_data_structures::{defer, jobserver}, rustc_span::DUMMY_SP, std::iter, std::process, @@ -530,7 +530,7 @@ fn remove_cycle<D: DepKind>( /// all active queries for cycles before finally resuming all the waiters at once. #[cfg(parallel_compiler)] pub fn deadlock<D: DepKind>(query_map: QueryMap<D>, registry: &rayon_core::Registry) { - let on_panic = OnDrop(|| { + let on_panic = defer(|| { eprintln!("deadlock handler panicked, aborting process"); process::abort(); }); diff --git a/compiler/rustc_resolve/Cargo.toml b/compiler/rustc_resolve/Cargo.toml index 1c16d85f1b9..46da0aa2853 100644 --- a/compiler/rustc_resolve/Cargo.toml +++ b/compiler/rustc_resolve/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] bitflags = "1.2.1" -pulldown-cmark = { version = "0.9.2", default-features = false } +pulldown-cmark = { version = "0.9.3", default-features = false } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 08b73ebb694..72777733345 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -129,7 +129,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { expn_id, self.def_span(def_id), // FIXME: Account for `#[no_implicit_prelude]` attributes. - parent.map_or(false, |module| module.no_implicit_prelude), + parent.is_some_and(|module| module.no_implicit_prelude), )); } } @@ -873,6 +873,11 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { let msg = "macro-expanded `extern crate` items cannot \ shadow names passed with `--extern`"; self.r.tcx.sess.span_err(item.span, msg); + // `return` is intended to discard this binding because it's an + // unregistered ambiguity error which would result in a panic + // caused by inconsistency `path_res` + // more details: https://github.com/rust-lang/rust/pull/111761 + return; } } let entry = self.r.extern_prelude.entry(ident.normalize_to_macros_2_0()).or_insert( diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 17c4a6be049..dc35c8b176f 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -117,16 +117,11 @@ impl<'a, 'b, 'tcx> UnusedImportCheckVisitor<'a, 'b, 'tcx> { match item.kind { ast::UseTreeKind::Simple(Some(ident)) => { if ident.name == kw::Underscore - && !self - .r - .import_res_map - .get(&id) - .map(|per_ns| { - per_ns.iter().filter_map(|res| res.as_ref()).any(|res| { - matches!(res, Res::Def(DefKind::Trait | DefKind::TraitAlias, _)) - }) + && !self.r.import_res_map.get(&id).is_some_and(|per_ns| { + per_ns.iter().filter_map(|res| res.as_ref()).any(|res| { + matches!(res, Res::Def(DefKind::Trait | DefKind::TraitAlias, _)) }) - .unwrap_or(false) + }) { self.unused_import(self.base_id).add(id); } @@ -469,7 +464,7 @@ impl Resolver<'_, '_> { .r .extern_prelude .get(&extern_crate.ident) - .map_or(false, |entry| !entry.introduced_by_item) + .is_some_and(|entry| !entry.introduced_by_item) { continue; } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 3ed7580af05..8c6ac822a77 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1832,8 +1832,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } (msg, None) } else if ident.name == kw::SelfUpper { - ("`Self` is only available in impls, traits, and type definitions".to_string(), None) - } else if ident.name.as_str().chars().next().map_or(false, |c| c.is_ascii_uppercase()) { + // As mentioned above, `opt_ns` being `None` indicates a module path in import. + // We can use this to improve a confusing error for, e.g. `use Self::Variant` in an + // impl + if opt_ns.is_none() { + ("`Self` cannot be used in imports".to_string(), None) + } else { + ( + "`Self` is only available in impls, traits, and type definitions".to_string(), + None, + ) + } + } else if ident.name.as_str().chars().next().is_some_and(|c| c.is_ascii_uppercase()) { // Check whether the name refers to an item in the value namespace. let binding = if let Some(ribs) = ribs { self.resolve_ident_in_lexical_scope( @@ -2155,7 +2165,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let is_definitely_crate = import .module_path .first() - .map_or(false, |f| f.ident.name != kw::SelfLower && f.ident.name != kw::Super); + .is_some_and(|f| f.ident.name != kw::SelfLower && f.ident.name != kw::Super); // Add the import to the start, with a `{` if required. let start_point = source_map.start_point(after_crate_name); diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index a1077615d95..e0611907613 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -17,7 +17,7 @@ use rustc_ast::*; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_errors::{Applicability, DiagnosticArgValue, DiagnosticId, IntoDiagnosticArg}; use rustc_hir::def::Namespace::{self, *}; -use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, PartialRes, PerNS}; +use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, NonMacroAttrKind, PartialRes, PerNS}; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; use rustc_hir::{BindingAnnotation, PrimTy, TraitCandidate}; use rustc_middle::middle::resolve_bound_vars::Set1; @@ -4287,12 +4287,12 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { } } - fn resolve_and_cache_rustdoc_path(&mut self, path_str: &str, ns: Namespace) -> bool { + fn resolve_and_cache_rustdoc_path(&mut self, path_str: &str, ns: Namespace) -> Option<Res> { // FIXME: This caching may be incorrect in case of multiple `macro_rules` // items with the same name in the same module. // Also hygiene is not considered. let mut doc_link_resolutions = std::mem::take(&mut self.r.doc_link_resolutions); - let res = doc_link_resolutions + let res = *doc_link_resolutions .entry(self.parent_scope.module.nearest_parent_mod().expect_local()) .or_default() .entry((Symbol::intern(path_str), ns)) @@ -4307,8 +4307,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { return None; } res - }) - .is_some(); + }); self.r.doc_link_resolutions = doc_link_resolutions; res } @@ -4343,8 +4342,10 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { let mut any_resolved = false; let mut need_assoc = false; for ns in [TypeNS, ValueNS, MacroNS] { - if self.resolve_and_cache_rustdoc_path(&path_str, ns) { - any_resolved = true; + if let Some(res) = self.resolve_and_cache_rustdoc_path(&path_str, ns) { + // Rustdoc ignores tool attribute resolutions and attempts + // to resolve their prefixes for diagnostics. + any_resolved = !matches!(res, Res::NonMacroAttr(NonMacroAttrKind::Tool)); } else if ns != MacroNS { need_assoc = true; } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index c9131d8c8a9..df65825802e 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -197,8 +197,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { .sess .source_map() .span_to_snippet(span) - .map(|snippet| snippet.ends_with(')')) - .unwrap_or(false) + .is_ok_and(|snippet| snippet.ends_with(')')) } Res::Def( DefKind::Ctor(..) | DefKind::AssocFn | DefKind::Const | DefKind::AssocConst, @@ -722,7 +721,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { if let TypoCandidate::Shadowed(res, Some(sugg_span)) = typo_sugg && res .opt_def_id() - .map_or(false, |id| id.is_local() || is_in_same_file(span, sugg_span)) + .is_some_and(|id| id.is_local() || is_in_same_file(span, sugg_span)) { err.span_label( sugg_span, @@ -856,7 +855,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { // The current function has a `self` parameter, but we were unable to resolve // a reference to `self`. This can only happen if the `self` identifier we // are resolving came from a different hygiene context. - if fn_kind.decl().inputs.get(0).map_or(false, |p| p.is_self()) { + if fn_kind.decl().inputs.get(0).is_some_and(|p| p.is_self()) { err.span_label(*span, "this function has a `self` parameter, but a macro invocation can only access identifiers it receives from parameters"); } else { let doesnt = if is_assoc_fn { @@ -1632,7 +1631,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { .tcx .fn_arg_names(def_id) .first() - .map_or(false, |ident| ident.name == kw::SelfLower), + .is_some_and(|ident| ident.name == kw::SelfLower), }; if has_self { return Some(AssocSuggestion::MethodWithSelf { called }); @@ -1931,10 +1930,9 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { let def_id = self.r.tcx.parent(ctor_def_id); match kind { CtorKind::Const => false, - CtorKind::Fn => !self - .r - .field_def_ids(def_id) - .map_or(false, |field_ids| field_ids.is_empty()), + CtorKind::Fn => { + !self.r.field_def_ids(def_id).is_some_and(|field_ids| field_ids.is_empty()) + } } }; diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 3cdc3f0ecf8..3d2bd842906 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -106,7 +106,7 @@ impl Determinacy { /// A specific scope in which a name can be looked up. /// This enum is currently used only for early resolution (imports and macros), /// but not for late resolution yet. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] enum Scope<'a> { DeriveHelpers(LocalExpnId), DeriveHelpersCompat, @@ -1477,7 +1477,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } fn is_builtin_macro(&mut self, res: Res) -> bool { - self.get_macro(res).map_or(false, |macro_data| macro_data.ext.builtin_name.is_some()) + self.get_macro(res).is_some_and(|macro_data| macro_data.ext.builtin_name.is_some()) } fn macro_def(&self, mut ctxt: SyntaxContext) -> DefId { diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 4da43c6a9a2..df5c16a9375 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -823,8 +823,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let is_allowed = |feature| { self.active_features.contains(&feature) || span.allows_unstable(feature) }; - let allowed_by_implication = - implied_by.map(|feature| is_allowed(feature)).unwrap_or(false); + let allowed_by_implication = implied_by.is_some_and(|feature| is_allowed(feature)); if !is_allowed(feature) && !allowed_by_implication { let lint_buffer = &mut self.lint_buffer; let soft_handler = diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index a328447aca9..6c8c8e484f9 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1907,7 +1907,7 @@ fn collect_print_requests( error_format: ErrorOutputType, ) -> Vec<PrintRequest> { let mut prints = Vec::<PrintRequest>::new(); - if cg.target_cpu.as_ref().map_or(false, |s| s == "help") { + if cg.target_cpu.as_ref().is_some_and(|s| s == "help") { prints.push(PrintRequest::TargetCPUs); cg.target_cpu = None; }; diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index d91d920140d..bbe52dbced0 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1378,8 +1378,8 @@ pub fn build_session( .lint_opts .iter() .rfind(|&(key, _)| *key == "warnings") - .map_or(false, |&(_, level)| level == lint::Allow); - let cap_lints_allow = sopts.lint_cap.map_or(false, |cap| cap == lint::Allow); + .is_some_and(|&(_, level)| level == lint::Allow); + let cap_lints_allow = sopts.lint_cap.is_some_and(|cap| cap == lint::Allow); let can_emit_warnings = !(warnings_allow || cap_lints_allow); let sysroot = match &sopts.maybe_sysroot { @@ -1732,6 +1732,7 @@ fn early_error_handler(output: config::ErrorOutputType) -> rustc_errors::Handler #[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::diagnostic_outside_of_impl)] +#[must_use = "ErrorGuaranteed must be returned from `run_compiler` in order to exit with a non-zero status code"] pub fn early_error_no_abort( output: config::ErrorOutputType, msg: impl Into<DiagnosticMessage>, diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 97cb734619e..3d3833dfdab 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -754,7 +754,7 @@ impl Span { self.ctxt() .outer_expn_data() .allow_internal_unstable - .map_or(false, |features| features.iter().any(|&f| f == feature)) + .is_some_and(|features| features.iter().any(|&f| f == feature)) } /// Checks if this span arises from a compiler desugaring of kind `kind`. @@ -1257,29 +1257,6 @@ impl SourceFileHash { } } -#[derive(HashStable_Generic)] -#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)] -pub enum DebuggerVisualizerType { - Natvis, - GdbPrettyPrinter, -} - -/// A single debugger visualizer file. -#[derive(HashStable_Generic)] -#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Encodable, Decodable)] -pub struct DebuggerVisualizerFile { - /// The complete debugger visualizer source. - pub src: Lrc<[u8]>, - /// Indicates which visualizer type this targets. - pub visualizer_type: DebuggerVisualizerType, -} - -impl DebuggerVisualizerFile { - pub fn new(src: Lrc<[u8]>, visualizer_type: DebuggerVisualizerType) -> Self { - DebuggerVisualizerFile { src, visualizer_type } - } -} - #[derive(Clone)] pub enum SourceFileLines { /// The source file lines, in decoded (random-access) form. diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index 11ea5fe4ddf..1824510a974 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -639,7 +639,7 @@ impl SourceMap { self.span_to_source(sp, |src, start_index, end_index| { Ok(src.get(start_index..end_index).is_some()) }) - .map_or(false, |is_accessible| is_accessible) + .is_ok_and(|is_accessible| is_accessible) } /// Returns the source snippet as `String` corresponding to the given `Span`. @@ -835,7 +835,7 @@ impl SourceMap { } return Ok(true); }) - .map_or(false, |is_accessible| is_accessible) + .is_ok_and(|is_accessible| is_accessible) } /// Given a `Span`, tries to get a shorter span ending just after the first occurrence of `char` @@ -967,7 +967,7 @@ impl SourceMap { for _ in 0..limit.unwrap_or(100_usize) { sp = self.next_point(sp); if let Ok(ref snippet) = self.span_to_snippet(sp) { - if expect.map_or(false, |es| snippet == es) { + if expect.is_some_and(|es| snippet == es) { break; } if expect.is_none() && snippet.chars().any(|c| !c.is_whitespace()) { diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index a3f262905c7..254ede4e6a0 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -220,7 +220,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolPrinter<'tcx> { match *ty.kind() { // Print all nominal types as paths (unlike `pretty_print_type`). ty::FnDef(def_id, substs) - | ty::Alias(_, ty::AliasTy { def_id, substs, .. }) + | ty::Alias(ty::Projection | ty::Opaque, ty::AliasTy { def_id, substs, .. }) | ty::Closure(def_id, substs) | ty::Generator(def_id, substs, _) => self.print_def_path(def_id, substs), @@ -241,6 +241,8 @@ impl<'tcx> Printer<'tcx> for &mut SymbolPrinter<'tcx> { Ok(self) } + ty::Alias(ty::Inherent, _) => panic!("unexpected inherent projection"), + _ => self.pretty_print_type(ty), } } diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs index c281aa7e83a..9fa49123a86 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs @@ -272,12 +272,11 @@ fn encode_region<'tcx>( s.push('E'); compress(dict, DictKey::Region(region), &mut s); } - RegionKind::ReErased => { + RegionKind::ReEarlyBound(..) | RegionKind::ReErased => { s.push_str("u6region"); compress(dict, DictKey::Region(region), &mut s); } - RegionKind::ReEarlyBound(..) - | RegionKind::ReFree(..) + RegionKind::ReFree(..) | RegionKind::ReStatic | RegionKind::ReError(_) | RegionKind::ReVar(..) @@ -673,6 +672,14 @@ fn encode_ty<'tcx>( typeid.push_str(&s); } + // Type parameters + ty::Param(..) => { + // u5param as vendor extended type + let mut s = String::from("u5param"); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + // Unexpected types ty::Bound(..) | ty::Error(..) @@ -680,7 +687,6 @@ fn encode_ty<'tcx>( | ty::GeneratorWitnessMIR(..) | ty::Infer(..) | ty::Alias(..) - | ty::Param(..) | ty::Placeholder(..) => { bug!("encode_ty: unexpected `{:?}`", ty.kind()); } @@ -689,6 +695,42 @@ fn encode_ty<'tcx>( typeid } +/// Transforms predicates for being encoded and used in the substitution dictionary. +fn transform_predicates<'tcx>( + tcx: TyCtxt<'tcx>, + predicates: &List<ty::PolyExistentialPredicate<'tcx>>, + _options: EncodeTyOptions, +) -> &'tcx List<ty::PolyExistentialPredicate<'tcx>> { + let predicates: Vec<ty::PolyExistentialPredicate<'tcx>> = predicates + .iter() + .filter_map(|predicate| match predicate.skip_binder() { + ty::ExistentialPredicate::Trait(trait_ref) => { + let trait_ref = ty::TraitRef::identity(tcx, trait_ref.def_id); + Some(ty::Binder::dummy(ty::ExistentialPredicate::Trait( + ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref), + ))) + } + ty::ExistentialPredicate::Projection(..) => None, + ty::ExistentialPredicate::AutoTrait(..) => Some(predicate), + }) + .collect(); + tcx.mk_poly_existential_predicates(&predicates) +} + +/// Transforms substs for being encoded and used in the substitution dictionary. +fn transform_substs<'tcx>( + tcx: TyCtxt<'tcx>, + substs: SubstsRef<'tcx>, + options: TransformTyOptions, +) -> SubstsRef<'tcx> { + let substs = substs.iter().map(|subst| match subst.unpack() { + GenericArgKind::Type(ty) if ty.is_c_void(tcx) => tcx.mk_unit().into(), + GenericArgKind::Type(ty) => transform_ty(tcx, ty, options).into(), + _ => subst, + }); + tcx.mk_substs_from_iter(substs) +} + // Transforms a ty:Ty for being encoded and used in the substitution dictionary. It transforms all // c_void types into unit types unconditionally, generalizes pointers if // TransformTyOptions::GENERALIZE_POINTERS option is set, and normalizes integers if @@ -697,7 +739,7 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio let mut ty = ty; match ty.kind() { - ty::Float(..) | ty::Char | ty::Str | ty::Never | ty::Foreign(..) | ty::Dynamic(..) => {} + ty::Float(..) | ty::Char | ty::Str | ty::Never | ty::Foreign(..) => {} ty::Bool => { if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { @@ -776,7 +818,7 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio let field = variant.fields.iter().find(|field| { let ty = tcx.type_of(field.did).subst_identity(); let is_zst = - tcx.layout_of(param_env.and(ty)).map_or(false, |layout| layout.is_zst()); + tcx.layout_of(param_env.and(ty)).is_ok_and(|layout| layout.is_zst()); !is_zst }); if let Some(field) = field { @@ -870,6 +912,14 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio } } + ty::Dynamic(predicates, _region, kind) => { + ty = tcx.mk_dynamic( + transform_predicates(tcx, predicates, options), + tcx.lifetimes.re_erased, + *kind, + ); + } + ty::Bound(..) | ty::Error(..) | ty::GeneratorWitness(..) @@ -885,20 +935,6 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio ty } -/// Transforms substs for being encoded and used in the substitution dictionary. -fn transform_substs<'tcx>( - tcx: TyCtxt<'tcx>, - substs: SubstsRef<'tcx>, - options: TransformTyOptions, -) -> SubstsRef<'tcx> { - let substs = substs.iter().map(|subst| match subst.unpack() { - GenericArgKind::Type(ty) if ty.is_c_void(tcx) => tcx.mk_unit().into(), - GenericArgKind::Type(ty) => transform_ty(tcx, ty, options).into(), - _ => subst, - }); - tcx.mk_substs_from_iter(substs) -} - /// Returns a type metadata identifier for the specified FnAbi using the Itanium C++ ABI with vendor /// extended type qualifiers and types for Rust types that are not used at the FFI boundary. #[instrument(level = "trace", skip(tcx))] diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 2235524129e..4cccc639892 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -433,7 +433,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { // Mangle all nominal types as paths. ty::Adt(ty::AdtDef(Interned(&ty::AdtDefData { did: def_id, .. }, _)), substs) | ty::FnDef(def_id, substs) - | ty::Alias(_, ty::AliasTy { def_id, substs, .. }) + | ty::Alias(ty::Projection | ty::Opaque, ty::AliasTy { def_id, substs, .. }) | ty::Closure(def_id, substs) | ty::Generator(def_id, substs, _) => { self = self.print_def_path(def_id, substs)?; @@ -482,6 +482,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { self = r.print(self)?; } + ty::Alias(ty::Inherent, _) => bug!("symbol_names: unexpected inherent projection"), ty::GeneratorWitness(_) => bug!("symbol_names: unexpected `GeneratorWitness`"), ty::GeneratorWitnessMIR(..) => bug!("symbol_names: unexpected `GeneratorWitnessMIR`"), } diff --git a/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs b/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs index b69ade7e4aa..9ac73235141 100644 --- a/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs +++ b/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs @@ -4,7 +4,7 @@ use crate::spec::{FramePointer, SanitizerSet, Target, TargetOptions}; pub fn target() -> Target { let arch = Arch::Arm64; let mut base = opts("macos", arch); - base.cpu = "apple-a14".into(); + base.cpu = "apple-m1".into(); base.max_atomic_width = Some(128); // FIXME: The leak sanitizer currently fails the tests, see #88132. diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs index d0c950c2e32..523eb6bd2fe 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs @@ -1,10 +1,15 @@ -use crate::spec::{Target, TargetOptions}; +use crate::spec::{SanitizerSet, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::linux_musl_base::opts(); base.max_atomic_width = Some(128); base.supports_xray = true; base.features = "+v8a".into(); + base.supported_sanitizers = SanitizerSet::ADDRESS + | SanitizerSet::CFI + | SanitizerSet::LEAK + | SanitizerSet::MEMORY + | SanitizerSet::THREAD; Target { llvm_target: "aarch64-unknown-linux-musl".into(), diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index d14e6244f7d..f32ff0442a4 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -2,7 +2,6 @@ use super::search_graph::OverflowHandler; use super::{EvalCtxt, SolverMode}; -use crate::solve::CanonicalResponseExt; use crate::traits::coherence; use rustc_data_structures::fx::FxIndexSet; use rustc_hir::def_id::DefId; @@ -333,8 +332,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { candidates: &mut Vec<Candidate<'tcx>>, ) { let tcx = self.tcx(); - // FIXME: We also have to normalize opaque types, not sure where to best fit that in. - let &ty::Alias(ty::Projection, projection_ty) = goal.predicate.self_ty().kind() else { + let &ty::Alias(_, projection_ty) = goal.predicate.self_ty().kind() else { return }; @@ -356,8 +354,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { }), ); ecx.add_goal(normalizes_to_goal); - let _ = ecx.try_evaluate_added_goals()?; + let _ = ecx.try_evaluate_added_goals().inspect_err(|_| { + debug!("self type normalization failed"); + })?; let normalized_ty = ecx.resolve_vars_if_possible(normalized_ty); + debug!(?normalized_ty, "self type normalized"); // NOTE: Alternatively we could call `evaluate_goal` here and only // have a `Normalized` candidate. This doesn't work as long as we // use `CandidateSource` in winnowing. @@ -742,13 +743,18 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { SolverMode::Normal => { let param_env_responses = candidates .iter() - .filter(|c| matches!(c.source, CandidateSource::ParamEnv(_))) + .filter(|c| { + matches!( + c.source, + CandidateSource::ParamEnv(_) | CandidateSource::AliasBound + ) + }) .map(|c| c.result) .collect::<Vec<_>>(); if let Some(result) = self.try_merge_responses(¶m_env_responses) { - if result.has_only_region_constraints() { - return Ok(result); - } + // We strongly prefer alias and param-env bounds here, even if they affect inference. + // See https://github.com/rust-lang/trait-system-refactor-initiative/issues/11. + return Ok(result); } } } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index 63a73f8d50d..f91c6727753 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -1,4 +1,4 @@ -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::infer::at::ToTrace; use rustc_infer::infer::canonical::CanonicalVarValues; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; @@ -9,7 +9,11 @@ use rustc_infer::infer::{ use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::ObligationCause; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; -use rustc_middle::traits::solve::{CanonicalGoal, Certainty, MaybeCause, QueryResult}; +use rustc_middle::traits::solve::{ + CanonicalInput, CanonicalResponse, Certainty, MaybeCause, PredefinedOpaques, + PredefinedOpaquesData, QueryResult, +}; +use rustc_middle::traits::DefiningAnchor; use rustc_middle::ty::{ self, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, @@ -44,6 +48,9 @@ pub struct EvalCtxt<'a, 'tcx> { infcx: &'a InferCtxt<'tcx>, pub(super) var_values: CanonicalVarValues<'tcx>, + + predefined_opaques_in_body: PredefinedOpaques<'tcx>, + /// The highest universe index nameable by the caller. /// /// When we enter a new binder inside of the query we create new universes @@ -126,6 +133,11 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> { let mut ecx = EvalCtxt { search_graph: &mut search_graph, infcx: self, + // Only relevant when canonicalizing the response, + // which we don't do within this evaluation context. + predefined_opaques_in_body: self + .tcx + .mk_predefined_opaques_in_body(PredefinedOpaquesData::default()), // Only relevant when canonicalizing the response. max_input_universe: ty::UniverseIndex::ROOT, var_values: CanonicalVarValues::dummy(), @@ -162,29 +174,53 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { fn evaluate_canonical_goal( tcx: TyCtxt<'tcx>, search_graph: &'a mut search_graph::SearchGraph<'tcx>, - canonical_goal: CanonicalGoal<'tcx>, + canonical_input: CanonicalInput<'tcx>, ) -> QueryResult<'tcx> { // Deal with overflow, caching, and coinduction. // // The actual solver logic happens in `ecx.compute_goal`. - search_graph.with_new_goal(tcx, canonical_goal, |search_graph| { + search_graph.with_new_goal(tcx, canonical_input, |search_graph| { let intercrate = match search_graph.solver_mode() { SolverMode::Normal => false, SolverMode::Coherence => true, }; - let (ref infcx, goal, var_values) = tcx + let (ref infcx, input, var_values) = tcx .infer_ctxt() .intercrate(intercrate) - .build_with_canonical(DUMMY_SP, &canonical_goal); + .with_opaque_type_inference(canonical_input.value.anchor) + .build_with_canonical(DUMMY_SP, &canonical_input); + + for &(a, b) in &input.predefined_opaques_in_body.opaque_types { + let InferOk { value: (), obligations } = infcx + .register_hidden_type_in_new_solver(a, input.goal.param_env, b) + .expect("expected opaque type instantiation to succeed"); + // We're only registering opaques already defined by the caller, + // so we're not responsible for proving that they satisfy their + // item bounds, unless we use them in a normalizes-to goal, + // which is handled in `EvalCtxt::unify_existing_opaque_tys`. + let _ = obligations; + } let mut ecx = EvalCtxt { infcx, var_values, - max_input_universe: canonical_goal.max_universe, + predefined_opaques_in_body: input.predefined_opaques_in_body, + max_input_universe: canonical_input.max_universe, search_graph, nested_goals: NestedGoals::new(), tainted: Ok(()), }; - ecx.compute_goal(goal) + + let result = ecx.compute_goal(input.goal); + + // When creating a query response we clone the opaque type constraints + // instead of taking them. This would cause an ICE here, since we have + // assertions against dropping an `InferCtxt` without taking opaques. + // FIXME: Once we remove support for the old impl we can remove this. + if input.anchor != DefiningAnchor::Error { + let _ = infcx.take_opaque_types(); + } + + result }) } @@ -199,7 +235,8 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { let canonical_response = EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?; - let has_changed = !canonical_response.value.var_values.is_identity(); + let has_changed = !canonical_response.value.var_values.is_identity() + || !canonical_response.value.external_constraints.opaque_types.is_empty(); let (certainty, nested_goals) = self.instantiate_and_apply_query_response( goal.param_env, orig_values, @@ -418,6 +455,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let mut ecx = EvalCtxt { infcx: self.infcx, var_values: self.var_values, + predefined_opaques_in_body: self.predefined_opaques_in_body, max_input_universe: self.max_input_universe, search_graph: self.search_graph, nested_goals: self.nested_goals.clone(), @@ -682,4 +720,56 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { | rustc_transmute::Answer::IfAny(_) => Err(NoSolution), } } + + pub(super) fn can_define_opaque_ty(&mut self, def_id: LocalDefId) -> bool { + self.infcx.opaque_type_origin(def_id).is_some() + } + + pub(super) fn register_opaque_ty( + &mut self, + a: ty::OpaqueTypeKey<'tcx>, + b: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> Result<(), NoSolution> { + let InferOk { value: (), obligations } = + self.infcx.register_hidden_type_in_new_solver(a, param_env, b)?; + self.add_goals(obligations.into_iter().map(|obligation| obligation.into())); + Ok(()) + } + + // Do something for each opaque/hidden pair defined with `def_id` in the + // current inference context. + pub(super) fn unify_existing_opaque_tys( + &mut self, + param_env: ty::ParamEnv<'tcx>, + key: ty::OpaqueTypeKey<'tcx>, + ty: Ty<'tcx>, + ) -> Vec<CanonicalResponse<'tcx>> { + // FIXME: Super inefficient to be cloning this... + let opaques = self.infcx.clone_opaque_types_for_query_response(); + + let mut values = vec![]; + for (candidate_key, candidate_ty) in opaques { + if candidate_key.def_id != key.def_id { + continue; + } + values.extend(self.probe(|ecx| { + for (a, b) in std::iter::zip(candidate_key.substs, key.substs) { + ecx.eq(param_env, a, b)?; + } + ecx.eq(param_env, candidate_ty, ty)?; + let mut obl = vec![]; + ecx.infcx.add_item_bounds_for_hidden_type( + candidate_key, + ObligationCause::dummy(), + param_env, + candidate_ty, + &mut obl, + ); + ecx.add_goals(obl.into_iter().map(Into::into)); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + })); + } + values + } } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs index 67ad7fb4bd2..fdb209fbff8 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs @@ -8,16 +8,19 @@ /// section of the [rustc-dev-guide][c]. /// /// [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html -use super::{CanonicalGoal, Certainty, EvalCtxt, Goal}; +use super::{CanonicalInput, Certainty, EvalCtxt, Goal}; use crate::solve::canonicalize::{CanonicalizeMode, Canonicalizer}; use crate::solve::{CanonicalResponse, QueryResult, Response}; use rustc_index::IndexVec; use rustc_infer::infer::canonical::query_response::make_query_region_constraints; use rustc_infer::infer::canonical::CanonicalVarValues; use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints}; +use rustc_infer::infer::InferOk; use rustc_middle::traits::query::NoSolution; -use rustc_middle::traits::solve::{ExternalConstraints, ExternalConstraintsData, MaybeCause}; -use rustc_middle::ty::{self, BoundVar, GenericArgKind}; +use rustc_middle::traits::solve::{ + ExternalConstraints, ExternalConstraintsData, MaybeCause, PredefinedOpaquesData, QueryInput, +}; +use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty}; use rustc_span::DUMMY_SP; use std::iter; use std::ops::Deref; @@ -28,13 +31,21 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { pub(super) fn canonicalize_goal( &self, goal: Goal<'tcx, ty::Predicate<'tcx>>, - ) -> (Vec<ty::GenericArg<'tcx>>, CanonicalGoal<'tcx>) { + ) -> (Vec<ty::GenericArg<'tcx>>, CanonicalInput<'tcx>) { let mut orig_values = Default::default(); let canonical_goal = Canonicalizer::canonicalize( self.infcx, CanonicalizeMode::Input, &mut orig_values, - goal, + QueryInput { + goal, + anchor: self.infcx.defining_use_anchor, + predefined_opaques_in_body: self.tcx().mk_predefined_opaques_in_body( + PredefinedOpaquesData { + opaque_types: self.infcx.clone_opaque_types_for_query_response(), + }, + ), + }, ); (orig_values, canonical_goal) } @@ -138,7 +149,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { region_constraints, ) }); - let opaque_types = self.infcx.clone_opaque_types_for_query_response(); + + let mut opaque_types = self.infcx.clone_opaque_types_for_query_response(); + // Only return opaque type keys for newly-defined opaques + opaque_types.retain(|(a, _)| { + self.predefined_opaques_in_body.opaque_types.iter().all(|(pa, _)| pa != a) + }); + Ok(self .tcx() .mk_external_constraints(ExternalConstraintsData { region_constraints, opaque_types })) @@ -164,10 +181,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let nested_goals = self.unify_query_var_values(param_env, &original_values, var_values)?; - // FIXME: implement external constraints. - let ExternalConstraintsData { region_constraints, opaque_types: _ } = + let ExternalConstraintsData { region_constraints, opaque_types } = external_constraints.deref(); self.register_region_constraints(region_constraints); + self.register_opaque_types(param_env, opaque_types)?; Ok((certainty, nested_goals)) } @@ -287,4 +304,19 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let _ = member_constraint; } } + + fn register_opaque_types( + &mut self, + param_env: ty::ParamEnv<'tcx>, + opaque_types: &[(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)], + ) -> Result<(), NoSolution> { + for &(a, b) in opaque_types { + let InferOk { value: (), obligations } = + self.infcx.register_hidden_type_in_new_solver(a, param_env, b)?; + // It's sound to drop these obligations, since the normalizes-to goal + // is responsible for proving these obligations. + let _ = obligations; + } + Ok(()) + } } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 32bd10f0beb..4a403196c7e 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -133,12 +133,14 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { | ty::PredicateKind::ObjectSafe(_) | ty::PredicateKind::ClosureKind(_, _, _) | ty::PredicateKind::ConstEvaluatable(_) - | ty::PredicateKind::TypeWellFormedFromEnv(_) | ty::PredicateKind::Ambiguous => { FulfillmentErrorCode::CodeSelectionError( SelectionError::Unimplemented, ) } + ty::PredicateKind::TypeWellFormedFromEnv(_) => { + bug!("unexpected goal: {goal:?}") + } }, root_obligation: obligation, }); diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index d94679fef28..26ace28f5fd 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -24,6 +24,7 @@ mod assembly; mod canonicalize; mod eval_ctxt; mod fulfill; +mod opaques; mod project_goals; mod search_graph; mod trait_goals; @@ -212,7 +213,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { ); } - match (lhs.to_projection_term(tcx), rhs.to_projection_term(tcx)) { + match (lhs.to_alias_ty(tcx), rhs.to_alias_ty(tcx)) { (None, None) => bug!("`AliasRelate` goal without an alias on either lhs or rhs"), // RHS is not a projection, only way this is true is if LHS normalizes-to RHS @@ -238,34 +239,34 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { evaluate_normalizes_to(self, alias_rhs, lhs, direction, Invert::Yes).ok(), ); // Relate via substs - candidates.extend( - self.probe(|ecx| { - let span = tracing::span!( - tracing::Level::DEBUG, - "compute_alias_relate_goal(relate_via_substs)", - ?alias_lhs, - ?alias_rhs, - ?direction - ); - let _enter = span.enter(); - - match direction { - ty::AliasRelationDirection::Equate => { - ecx.eq(goal.param_env, alias_lhs, alias_rhs)?; - } - ty::AliasRelationDirection::Subtype => { - ecx.sub(goal.param_env, alias_lhs, alias_rhs)?; - } + let subst_relate_response = self.probe(|ecx| { + let span = tracing::span!( + tracing::Level::DEBUG, + "compute_alias_relate_goal(relate_via_substs)", + ?alias_lhs, + ?alias_rhs, + ?direction + ); + let _enter = span.enter(); + + match direction { + ty::AliasRelationDirection::Equate => { + ecx.eq(goal.param_env, alias_lhs, alias_rhs)?; + } + ty::AliasRelationDirection::Subtype => { + ecx.sub(goal.param_env, alias_lhs, alias_rhs)?; } + } - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) - .ok(), - ); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }); + candidates.extend(subst_relate_response); debug!(?candidates); if let Some(merged) = self.try_merge_responses(&candidates) { Ok(merged) + } else if let Ok(subst_relate_response) = subst_relate_response { + Ok(subst_relate_response) } else { self.flounder(&candidates) } diff --git a/compiler/rustc_trait_selection/src/solve/opaques.rs b/compiler/rustc_trait_selection/src/solve/opaques.rs new file mode 100644 index 00000000000..a5de4ddee82 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/opaques.rs @@ -0,0 +1,67 @@ +use rustc_middle::traits::query::NoSolution; +use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; +use rustc_middle::traits::Reveal; +use rustc_middle::ty; +use rustc_middle::ty::util::NotUniqueParam; + +use super::{EvalCtxt, SolverMode}; + +impl<'tcx> EvalCtxt<'_, 'tcx> { + pub(super) fn normalize_opaque_type( + &mut self, + goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>, + ) -> QueryResult<'tcx> { + let tcx = self.tcx(); + let opaque_ty = goal.predicate.projection_ty; + let expected = goal.predicate.term.ty().expect("no such thing as an opaque const"); + + match (goal.param_env.reveal(), self.solver_mode()) { + (Reveal::UserFacing, SolverMode::Normal) => { + let Some(opaque_ty_def_id) = opaque_ty.def_id.as_local() else { + return Err(NoSolution); + }; + let opaque_ty = + ty::OpaqueTypeKey { def_id: opaque_ty_def_id, substs: opaque_ty.substs }; + // FIXME: at some point we should call queries without defining + // new opaque types but having the existing opaque type definitions. + // This will require moving this below "Prefer opaques registered already". + if !self.can_define_opaque_ty(opaque_ty_def_id) { + return Err(NoSolution); + } + // FIXME: This may have issues when the substs contain aliases... + match self.tcx().uses_unique_placeholders_ignoring_regions(opaque_ty.substs) { + Err(NotUniqueParam::NotParam(param)) if param.is_non_region_infer() => { + return self.evaluate_added_goals_and_make_canonical_response( + Certainty::AMBIGUOUS, + ); + } + Err(_) => { + return Err(NoSolution); + } + Ok(()) => {} + } + // Prefer opaques registered already. + let matches = self.unify_existing_opaque_tys(goal.param_env, opaque_ty, expected); + if !matches.is_empty() { + if let Some(response) = self.try_merge_responses(&matches) { + return Ok(response); + } else { + return self.flounder(&matches); + } + } + // Otherwise, define a new opaque type + self.register_opaque_ty(opaque_ty, expected, goal.param_env)?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + (Reveal::UserFacing, SolverMode::Coherence) => { + self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + } + (Reveal::All, _) => { + // FIXME: Add an assertion that opaque type storage is empty. + let actual = tcx.type_of(opaque_ty.def_id).subst(tcx, opaque_ty.substs); + self.eq(goal.param_env, expected, actual)?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + } + } +} diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index d3228074421..7d7dfa2c837 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -22,19 +22,25 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { &mut self, goal: Goal<'tcx, ProjectionPredicate<'tcx>>, ) -> QueryResult<'tcx> { - // To only compute normalization once for each projection we only - // normalize if the expected term is an unconstrained inference variable. - // - // E.g. for `<T as Trait>::Assoc == u32` we recursively compute the goal - // `exists<U> <T as Trait>::Assoc == U` and then take the resulting type for - // `U` and equate it with `u32`. This means that we don't need a separate - // projection cache in the solver. - if self.term_is_fully_unconstrained(goal) { - let candidates = self.assemble_and_evaluate_candidates(goal); - self.merge_candidates(candidates) - } else { - self.set_normalizes_to_hack_goal(goal); - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + match goal.predicate.projection_ty.kind(self.tcx()) { + ty::AliasKind::Projection => { + // To only compute normalization once for each projection we only + // normalize if the expected term is an unconstrained inference variable. + // + // E.g. for `<T as Trait>::Assoc == u32` we recursively compute the goal + // `exists<U> <T as Trait>::Assoc == U` and then take the resulting type for + // `U` and equate it with `u32`. This means that we don't need a separate + // projection cache in the solver. + if self.term_is_fully_unconstrained(goal) { + let candidates = self.assemble_and_evaluate_candidates(goal); + self.merge_candidates(candidates) + } else { + self.set_normalizes_to_hack_goal(goal); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + } + ty::AliasKind::Opaque => self.normalize_opaque_type(goal), + ty::AliasKind::Inherent => bug!("IATs not supported here yet"), } } } diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs b/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs index e6941af7b57..56f126e9157 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs @@ -11,7 +11,7 @@ use super::StackDepth; use rustc_data_structures::fx::FxHashMap; use rustc_index::IndexVec; -use rustc_middle::traits::solve::{CanonicalGoal, QueryResult}; +use rustc_middle::traits::solve::{CanonicalInput, QueryResult}; rustc_index::newtype_index! { pub struct EntryIndex {} @@ -34,7 +34,7 @@ pub(super) struct ProvisionalEntry<'tcx> { // The goal for this entry. Should always be equal to the corresponding goal // in the lookup table. - pub(super) goal: CanonicalGoal<'tcx>, + pub(super) input: CanonicalInput<'tcx>, } pub(super) struct ProvisionalCache<'tcx> { @@ -42,7 +42,7 @@ pub(super) struct ProvisionalCache<'tcx> { // FIXME: This is only used to quickly check whether a given goal // is in the cache. We should experiment with using something like // `SsoHashSet` here because in most cases there are only a few entries. - pub(super) lookup_table: FxHashMap<CanonicalGoal<'tcx>, EntryIndex>, + pub(super) lookup_table: FxHashMap<CanonicalInput<'tcx>, EntryIndex>, } impl<'tcx> ProvisionalCache<'tcx> { diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs index c1904352574..19e4b23009a 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs @@ -8,7 +8,7 @@ use cache::ProvisionalCache; use overflow::OverflowData; use rustc_index::IndexVec; use rustc_middle::dep_graph::DepKind; -use rustc_middle::traits::solve::{CanonicalGoal, Certainty, MaybeCause, QueryResult}; +use rustc_middle::traits::solve::{CanonicalInput, Certainty, MaybeCause, QueryResult}; use rustc_middle::ty::TyCtxt; use std::{collections::hash_map::Entry, mem}; @@ -19,7 +19,7 @@ rustc_index::newtype_index! { } struct StackElem<'tcx> { - goal: CanonicalGoal<'tcx>, + input: CanonicalInput<'tcx>, has_been_used: bool, } @@ -77,7 +77,7 @@ impl<'tcx> SearchGraph<'tcx> { } // ...or it depends on a goal with a lower depth. - let current_goal = self.stack[stack_depth].goal; + let current_goal = self.stack[stack_depth].input; let entry_index = self.provisional_cache.lookup_table[¤t_goal]; self.provisional_cache.entries[entry_index].depth != stack_depth } else { @@ -92,20 +92,20 @@ impl<'tcx> SearchGraph<'tcx> { fn try_push_stack( &mut self, tcx: TyCtxt<'tcx>, - goal: CanonicalGoal<'tcx>, + input: CanonicalInput<'tcx>, ) -> Result<(), QueryResult<'tcx>> { // Look at the provisional cache to check for cycles. let cache = &mut self.provisional_cache; - match cache.lookup_table.entry(goal) { + match cache.lookup_table.entry(input) { // No entry, simply push this goal on the stack after dealing with overflow. Entry::Vacant(v) => { if self.overflow_data.has_overflow(self.stack.len()) { - return Err(self.deal_with_overflow(tcx, goal)); + return Err(self.deal_with_overflow(tcx, input)); } - let depth = self.stack.push(StackElem { goal, has_been_used: false }); - let response = super::response_no_constraints(tcx, goal, Certainty::Yes); - let entry_index = cache.entries.push(ProvisionalEntry { response, depth, goal }); + let depth = self.stack.push(StackElem { input, has_been_used: false }); + let response = super::response_no_constraints(tcx, input, Certainty::Yes); + let entry_index = cache.entries.push(ProvisionalEntry { response, depth, input }); v.insert(entry_index); Ok(()) } @@ -135,13 +135,13 @@ impl<'tcx> SearchGraph<'tcx> { // the stack is enough. if self.stack.raw[stack_depth.index()..] .iter() - .all(|g| g.goal.value.predicate.is_coinductive(tcx)) + .all(|g| g.input.value.goal.predicate.is_coinductive(tcx)) { Err(cache.provisional_result(entry_index)) } else { Err(super::response_no_constraints( tcx, - goal, + input, Certainty::Maybe(MaybeCause::Overflow), )) } @@ -161,18 +161,18 @@ impl<'tcx> SearchGraph<'tcx> { /// updated the provisional cache and we have to recompute the current goal. /// /// FIXME: Refer to the rustc-dev-guide entry once it exists. - #[instrument(level = "debug", skip(self, actual_goal), ret)] + #[instrument(level = "debug", skip(self, actual_input), ret)] fn try_finalize_goal( &mut self, - actual_goal: CanonicalGoal<'tcx>, + actual_input: CanonicalInput<'tcx>, response: QueryResult<'tcx>, ) -> bool { let stack_elem = self.stack.pop().unwrap(); - let StackElem { goal, has_been_used } = stack_elem; - assert_eq!(goal, actual_goal); + let StackElem { input, has_been_used } = stack_elem; + assert_eq!(input, actual_input); let cache = &mut self.provisional_cache; - let provisional_entry_index = *cache.lookup_table.get(&goal).unwrap(); + let provisional_entry_index = *cache.lookup_table.get(&input).unwrap(); let provisional_entry = &mut cache.entries[provisional_entry_index]; // We eagerly update the response in the cache here. If we have to reevaluate // this goal we use the new response when hitting a cycle, and we definitely @@ -194,7 +194,7 @@ impl<'tcx> SearchGraph<'tcx> { cache.entries.truncate(provisional_entry_index.index() + 1); // ...and finally push our goal back on the stack and reevaluate it. - self.stack.push(StackElem { goal, has_been_used: false }); + self.stack.push(StackElem { input, has_been_used: false }); false } else { true @@ -204,17 +204,17 @@ impl<'tcx> SearchGraph<'tcx> { pub(super) fn with_new_goal( &mut self, tcx: TyCtxt<'tcx>, - canonical_goal: CanonicalGoal<'tcx>, + canonical_input: CanonicalInput<'tcx>, mut loop_body: impl FnMut(&mut Self) -> QueryResult<'tcx>, ) -> QueryResult<'tcx> { if self.should_use_global_cache() { - if let Some(result) = tcx.new_solver_evaluation_cache.get(&canonical_goal, tcx) { - debug!(?canonical_goal, ?result, "cache hit"); + if let Some(result) = tcx.new_solver_evaluation_cache.get(&canonical_input, tcx) { + debug!(?canonical_input, ?result, "cache hit"); return result; } } - match self.try_push_stack(tcx, canonical_goal) { + match self.try_push_stack(tcx, canonical_input) { Ok(()) => {} // Our goal is already on the stack, eager return. Err(response) => return response, @@ -226,19 +226,19 @@ impl<'tcx> SearchGraph<'tcx> { let (result, dep_node) = tcx.dep_graph.with_anon_task(tcx, DepKind::TraitSelect, || { self.repeat_while_none( |this| { - let result = this.deal_with_overflow(tcx, canonical_goal); + let result = this.deal_with_overflow(tcx, canonical_input); let _ = this.stack.pop().unwrap(); result }, |this| { let result = loop_body(this); - this.try_finalize_goal(canonical_goal, result).then(|| result) + this.try_finalize_goal(canonical_input, result).then(|| result) }, ) }); let cache = &mut self.provisional_cache; - let provisional_entry_index = *cache.lookup_table.get(&canonical_goal).unwrap(); + let provisional_entry_index = *cache.lookup_table.get(&canonical_input).unwrap(); let provisional_entry = &mut cache.entries[provisional_entry_index]; let depth = provisional_entry.depth; @@ -254,13 +254,13 @@ impl<'tcx> SearchGraph<'tcx> { // cycle participants without moving them to the global cache. let other_cycle_participants = provisional_entry_index.index() + 1; for (i, entry) in cache.entries.drain_enumerated(other_cycle_participants..) { - let actual_index = cache.lookup_table.remove(&entry.goal); + let actual_index = cache.lookup_table.remove(&entry.input); debug_assert_eq!(Some(i), actual_index); debug_assert!(entry.depth == depth); } let current_goal = cache.entries.pop().unwrap(); - let actual_index = cache.lookup_table.remove(¤t_goal.goal); + let actual_index = cache.lookup_table.remove(¤t_goal.input); debug_assert_eq!(Some(provisional_entry_index), actual_index); debug_assert!(current_goal.depth == depth); @@ -274,7 +274,7 @@ impl<'tcx> SearchGraph<'tcx> { let can_cache = !self.overflow_data.did_overflow() || self.stack.is_empty(); if self.should_use_global_cache() && can_cache { tcx.new_solver_evaluation_cache.insert( - current_goal.goal, + current_goal.input, dep_node, current_goal.response, ); diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index dcfa33ae842..f722f281314 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -177,14 +177,18 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { return Err(NoSolution); } - if goal.predicate.self_ty().has_non_region_infer() { + // The regions of a type don't affect the size of the type + let tcx = ecx.tcx(); + // We should erase regions from both the param-env and type, since both + // may have infer regions. Specifically, after canonicalizing and instantiating, + // early bound regions turn into region vars in both the new and old solver. + let key = tcx.erase_regions(goal.param_env.and(goal.predicate.self_ty())); + // But if there are inference variables, we have to wait until it's resolved. + if key.has_non_region_infer() { return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); } - let tcx = ecx.tcx(); - let self_ty = tcx.erase_regions(goal.predicate.self_ty()); - - if let Ok(layout) = tcx.layout_of(goal.param_env.and(self_ty)) + if let Ok(layout) = tcx.layout_of(key) && layout.layout.is_pointer_like(&tcx.data_layout) { // FIXME: We could make this faster by making a no-constraints response @@ -354,7 +358,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { // Can only unsize to an object-safe type if data .principal_def_id() - .map_or(false, |def_id| !tcx.check_is_object_safe(def_id)) + .is_some_and(|def_id| !tcx.check_is_object_safe(def_id)) { return Err(NoSolution); } diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 183c2401fc3..62d2aad5277 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -834,8 +834,10 @@ impl<'tcx> AutoTraitFinder<'tcx> { | ty::PredicateKind::Subtype(..) // FIXME(generic_const_exprs): you can absolutely add this as a where clauses | ty::PredicateKind::ConstEvaluatable(..) - | ty::PredicateKind::Coerce(..) - | ty::PredicateKind::TypeWellFormedFromEnv(..) => {} + | ty::PredicateKind::Coerce(..) => {} + ty::PredicateKind::TypeWellFormedFromEnv(..) => { + bug!("predicate should only exist in the environment: {bound_predicate:?}") + } ty::PredicateKind::Ambiguous => return false, }; } diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 969e5fa64b0..e8c5a8fab2a 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -17,9 +17,10 @@ use crate::traits::{ use rustc_data_structures::fx::FxIndexSet; use rustc_errors::Diagnostic; use rustc_hir::def_id::{DefId, CRATE_DEF_ID, LOCAL_CRATE}; -use rustc_infer::infer::{DefineOpaqueTypes, DefiningAnchor, InferCtxt, TyCtxtInferExt}; +use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::util; use rustc_middle::traits::specialization_graph::OverlapMode; +use rustc_middle::traits::DefiningAnchor; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt}; use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt, TypeVisitor}; @@ -706,7 +707,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OrphanChecker<'tcx> { } ty::Dynamic(tt, ..) => { let principal = tt.principal().map(|p| p.def_id()); - if principal.map_or(false, |p| self.def_id_is_local(p)) { + if principal.is_some_and(|p| self.def_id_is_local(p)) { ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) } else { self.found_non_local_ty(ty) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index f5f2fe54217..a10ececbb1e 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -437,7 +437,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // 1) strictly implied by another error. // 2) implied by an error with a smaller index. for error2 in error_set { - if error2.index.map_or(false, |index2| is_suppressed[index2]) { + if error2.index.is_some_and(|index2| is_suppressed[index2]) { // Avoid errors being suppressed by already-suppressed // errors, to prevent all errors from being suppressed // at once. @@ -885,7 +885,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { return; } - if self.suggest_impl_trait(&mut err, span, &obligation, trait_predicate) { + if self.suggest_impl_trait(&mut err, &obligation, trait_predicate) { err.emit(); return; } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs index 88525e1b720..10bd027b684 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -306,6 +306,14 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } } + + // `&[{integral}]` - `FromIterator` needs that. + if let ty::Ref(_, ref_ty, rustc_ast::Mutability::Not) = self_ty.kind() + && let ty::Slice(sty) = ref_ty.kind() + && sty.is_integral() + { + flags.push((sym::_Self, Some("&[{integral}]".to_owned()))); + } }); if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, def_id) { diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index ea17f23434b..82bad96ea42 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -30,9 +30,9 @@ use rustc_middle::hir::map; use rustc_middle::ty::error::TypeError::{self, Sorts}; use rustc_middle::ty::{ self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, - GeneratorDiagnosticData, GeneratorInteriorTypeCause, Infer, InferTy, InternalSubsts, - IsSuggestable, ToPredicate, Ty, TyCtxt, TypeAndMut, TypeFoldable, TypeFolder, - TypeSuperFoldable, TypeVisitableExt, TypeckResults, + GeneratorDiagnosticData, GeneratorInteriorTypeCause, InferTy, InternalSubsts, IsSuggestable, + ToPredicate, Ty, TyCtxt, TypeAndMut, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitableExt, TypeckResults, }; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::{sym, Ident, Symbol}; @@ -261,7 +261,6 @@ pub trait TypeErrCtxtExt<'tcx> { fn suggest_impl_trait( &self, err: &mut Diagnostic, - span: Span, obligation: &PredicateObligation<'tcx>, trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool; @@ -421,7 +420,7 @@ fn suggest_restriction<'tcx>( ) { if hir_generics.where_clause_span.from_expansion() || hir_generics.where_clause_span.desugaring_kind().is_some() - || projection.map_or(false, |projection| tcx.opt_rpitit_info(projection.def_id).is_some()) + || projection.is_some_and(|projection| tcx.opt_rpitit_info(projection.def_id).is_some()) { return; } @@ -1792,215 +1791,66 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { fn suggest_impl_trait( &self, err: &mut Diagnostic, - span: Span, obligation: &PredicateObligation<'tcx>, trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { - match obligation.cause.code().peel_derives() { - // Only suggest `impl Trait` if the return type is unsized because it is `dyn Trait`. - ObligationCauseCode::SizedReturnType => {} - _ => return false, - } - - let hir = self.tcx.hir(); - let fn_hir_id = hir.local_def_id_to_hir_id(obligation.cause.body_id); - let node = hir.find_by_def_id(obligation.cause.body_id); - let Some(hir::Node::Item(hir::Item { - kind: hir::ItemKind::Fn(sig, _, body_id), - .. - })) = node - else { + let ObligationCauseCode::SizedReturnType = obligation.cause.code() else { return false; }; - let body = hir.body(*body_id); - let trait_pred = self.resolve_vars_if_possible(trait_pred); - let ty = trait_pred.skip_binder().self_ty(); - let is_object_safe = match ty.kind() { - ty::Dynamic(predicates, _, ty::Dyn) => { - // If the `dyn Trait` is not object safe, do not suggest `Box<dyn Trait>`. - predicates - .principal_def_id() - .map_or(true, |def_id| self.tcx.check_is_object_safe(def_id)) - } - // We only want to suggest `impl Trait` to `dyn Trait`s. - // For example, `fn foo() -> str` needs to be filtered out. - _ => return false, - }; - - let hir::FnRetTy::Return(ret_ty) = sig.decl.output else { + let ty::Dynamic(_, _, ty::Dyn) = trait_pred.self_ty().skip_binder().kind() else { return false; }; - // Use `TypeVisitor` instead of the output type directly to find the span of `ty` for - // cases like `fn foo() -> (dyn Trait, i32) {}`. - // Recursively look for `TraitObject` types and if there's only one, use that span to - // suggest `impl Trait`. - - // Visit to make sure there's a single `return` type to suggest `impl Trait`, - // otherwise suggest using `Box<dyn Trait>` or an enum. - let mut visitor = ReturnsVisitor::default(); - visitor.visit_body(&body); - - let typeck_results = self.typeck_results.as_ref().unwrap(); - let Some(liberated_sig) = typeck_results.liberated_fn_sigs().get(fn_hir_id).copied() else { return false; }; - - let ret_types = visitor - .returns - .iter() - .filter_map(|expr| Some((expr.span, typeck_results.node_type_opt(expr.hir_id)?))) - .map(|(expr_span, ty)| (expr_span, self.resolve_vars_if_possible(ty))); - let (last_ty, all_returns_have_same_type, only_never_return) = ret_types.clone().fold( - (None, true, true), - |(last_ty, mut same, only_never_return): (std::option::Option<Ty<'_>>, bool, bool), - (_, ty)| { - let ty = self.resolve_vars_if_possible(ty); - same &= - !matches!(ty.kind(), ty::Error(_)) - && last_ty.map_or(true, |last_ty| { - // FIXME: ideally we would use `can_coerce` here instead, but `typeck` comes - // *after* in the dependency graph. - match (ty.kind(), last_ty.kind()) { - (Infer(InferTy::IntVar(_)), Infer(InferTy::IntVar(_))) - | (Infer(InferTy::FloatVar(_)), Infer(InferTy::FloatVar(_))) - | (Infer(InferTy::FreshIntTy(_)), Infer(InferTy::FreshIntTy(_))) - | ( - Infer(InferTy::FreshFloatTy(_)), - Infer(InferTy::FreshFloatTy(_)), - ) => true, - _ => ty == last_ty, - } - }); - (Some(ty), same, only_never_return && matches!(ty.kind(), ty::Never)) - }, - ); - let mut spans_and_needs_box = vec![]; - - match liberated_sig.output().kind() { - ty::Dynamic(predicates, _, ty::Dyn) => { - let cause = ObligationCause::misc(ret_ty.span, obligation.cause.body_id); - let param_env = ty::ParamEnv::empty(); - - if !only_never_return { - for (expr_span, return_ty) in ret_types { - let self_ty_satisfies_dyn_predicates = |self_ty| { - predicates.iter().all(|predicate| { - let pred = predicate.with_self_ty(self.tcx, self_ty); - let obl = Obligation::new(self.tcx, cause.clone(), param_env, pred); - self.predicate_may_hold(&obl) - }) - }; - - if let ty::Adt(def, substs) = return_ty.kind() - && def.is_box() - && self_ty_satisfies_dyn_predicates(substs.type_at(0)) - { - spans_and_needs_box.push((expr_span, false)); - } else if self_ty_satisfies_dyn_predicates(return_ty) { - spans_and_needs_box.push((expr_span, true)); - } else { - return false; - } - } - } - } - _ => return false, - }; - - let sm = self.tcx.sess.source_map(); - if !ret_ty.span.overlaps(span) { - return false; - } - let snippet = if let hir::TyKind::TraitObject(..) = ret_ty.kind { - if let Ok(snippet) = sm.span_to_snippet(ret_ty.span) { - snippet - } else { - return false; - } - } else { - // Substitute the type, so we can print a fixup given `type Alias = dyn Trait` - let name = liberated_sig.output().to_string(); - let name = - name.strip_prefix('(').and_then(|name| name.strip_suffix(')')).unwrap_or(&name); - if !name.starts_with("dyn ") { - return false; - } - name.to_owned() - }; - err.code(error_code!(E0746)); err.set_primary_message("return type cannot have an unboxed trait object"); err.children.clear(); - let impl_trait_msg = "for information on `impl Trait`, see \ - <https://doc.rust-lang.org/book/ch10-02-traits.html\ - #returning-types-that-implement-traits>"; - let trait_obj_msg = "for information on trait objects, see \ - <https://doc.rust-lang.org/book/ch17-02-trait-objects.html\ - #using-trait-objects-that-allow-for-values-of-different-types>"; - - let has_dyn = snippet.split_whitespace().next().map_or(false, |s| s == "dyn"); - let trait_obj = if has_dyn { &snippet[4..] } else { &snippet }; - if only_never_return { - // No return paths, probably using `panic!()` or similar. - // Suggest `-> impl Trait`, and if `Trait` is object safe, `-> Box<dyn Trait>`. - suggest_trait_object_return_type_alternatives( - err, - ret_ty.span, - trait_obj, - is_object_safe, - ); - } else if let (Some(last_ty), true) = (last_ty, all_returns_have_same_type) { - // Suggest `-> impl Trait`. + + let span = obligation.cause.span; + if let Ok(snip) = self.tcx.sess.source_map().span_to_snippet(span) + && snip.starts_with("dyn ") + { err.span_suggestion( - ret_ty.span, - format!( - "use `impl {1}` as the return type, as all return paths are of type `{}`, \ - which implements `{1}`", - last_ty, trait_obj, - ), - format!("impl {}", trait_obj), - Applicability::MachineApplicable, + span.with_hi(span.lo() + BytePos(4)), + "return an `impl Trait` instead of a `dyn Trait`, \ + if all returned values are the same type", + "impl ", + Applicability::MaybeIncorrect, ); - err.note(impl_trait_msg); - } else { - if is_object_safe { - // Suggest `-> Box<dyn Trait>` and `Box::new(returned_value)`. - err.multipart_suggestion( - "return a boxed trait object instead", - vec![ - (ret_ty.span.shrink_to_lo(), "Box<".to_string()), - (span.shrink_to_hi(), ">".to_string()), - ], - Applicability::MaybeIncorrect, - ); - for (span, needs_box) in spans_and_needs_box { - if needs_box { - err.multipart_suggestion( - "... and box this value", - vec![ - (span.shrink_to_lo(), "Box::new(".to_string()), - (span.shrink_to_hi(), ")".to_string()), - ], - Applicability::MaybeIncorrect, - ); - } - } + } + + let body = self.tcx.hir().body(self.tcx.hir().body_owned_by(obligation.cause.body_id)); + + let mut visitor = ReturnsVisitor::default(); + visitor.visit_body(&body); + + let mut sugg = + vec![(span.shrink_to_lo(), "Box<".to_string()), (span.shrink_to_hi(), ">".to_string())]; + sugg.extend(visitor.returns.into_iter().flat_map(|expr| { + let span = expr.span.find_ancestor_in_same_ctxt(obligation.cause.span).unwrap_or(expr.span); + if !span.can_be_used_for_suggestions() { + vec![] + } else if let hir::ExprKind::Call(path, ..) = expr.kind + && let hir::ExprKind::Path(hir::QPath::TypeRelative(ty, method)) = path.kind + && method.ident.name == sym::new + && let hir::TyKind::Path(hir::QPath::Resolved(.., box_path)) = ty.kind + && box_path.res.opt_def_id().is_some_and(|def_id| Some(def_id) == self.tcx.lang_items().owned_box()) + { + // Don't box `Box::new` + vec![] } else { - // This is currently not possible to trigger because E0038 takes precedence, but - // leave it in for completeness in case anything changes in an earlier stage. - err.note(format!( - "if trait `{}` were object-safe, you could return a trait object", - trait_obj, - )); + vec![ + (span.shrink_to_lo(), "Box::new(".to_string()), + (span.shrink_to_hi(), ")".to_string()), + ] } - err.note(trait_obj_msg); - err.note(format!( - "if all the returned values were of the same type you could use `impl {}` as the \ - return type", - trait_obj, - )); - err.note(impl_trait_msg); - err.note("you can create a new `enum` with a variant for each returned type"); - } + })); + + err.multipart_suggestion( + "box the return type, and wrap all of the returned values in `Box::new`", + sugg, + Applicability::MaybeIncorrect, + ); + true } @@ -3086,7 +2936,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { "note_obligation_cause_code: check for async fn" ); if is_future - && obligated_types.last().map_or(false, |ty| match ty.kind() { + && obligated_types.last().is_some_and(|ty| match ty.kind() { ty::Generator(last_def_id, ..) => { tcx.generator_is_async(*last_def_id) } @@ -4139,37 +3989,6 @@ impl NextTypeParamName for &[hir::GenericParam<'_>] { } } -fn suggest_trait_object_return_type_alternatives( - err: &mut Diagnostic, - ret_ty: Span, - trait_obj: &str, - is_object_safe: bool, -) { - err.span_suggestion( - ret_ty, - format!( - "use `impl {}` as the return type if all return paths have the same type but you \ - want to expose only the trait in the signature", - trait_obj, - ), - format!("impl {}", trait_obj), - Applicability::MaybeIncorrect, - ); - if is_object_safe { - err.multipart_suggestion( - format!( - "use a boxed trait object if all return paths implement trait `{}`", - trait_obj, - ), - vec![ - (ret_ty.shrink_to_lo(), "Box<".to_string()), - (ret_ty.shrink_to_hi(), ">".to_string()), - ], - Applicability::MaybeIncorrect, - ); - } -} - /// Collect the spans that we see the generic param `param_did` struct ReplaceImplTraitVisitor<'a> { ty_spans: &'a mut Vec<Span>, diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 28dad8592a8..f265230ff77 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -17,6 +17,7 @@ pub mod query; mod select; mod specialize; mod structural_match; +mod structural_normalize; mod util; mod vtable; pub mod wf; @@ -62,6 +63,7 @@ pub use self::specialize::{ pub use self::structural_match::{ search_for_adt_const_param_violation, search_for_structural_match_violation, }; +pub use self::structural_normalize::StructurallyNormalizeExt; pub use self::util::elaborate; pub use self::util::{expand_trait_aliases, TraitAliasExpander}; pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices}; diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs index 1f8e756043d..3d6c1d9e2b0 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs @@ -1,32 +1,31 @@ use crate::infer::canonical::query_response; -use crate::infer::{InferCtxt, InferOk}; +use crate::infer::InferCtxt; use crate::traits::query::type_op::TypeOpOutput; use crate::traits::query::Fallible; use crate::traits::ObligationCtxt; use rustc_infer::infer::region_constraints::RegionConstraintData; +use rustc_middle::traits::query::NoSolution; use rustc_span::source_map::DUMMY_SP; use std::fmt; -pub struct CustomTypeOp<F, G> { +pub struct CustomTypeOp<F> { closure: F, - description: G, + description: &'static str, } -impl<F, G> CustomTypeOp<F, G> { - pub fn new<'tcx, R>(closure: F, description: G) -> Self +impl<F> CustomTypeOp<F> { + pub fn new<'tcx, R>(closure: F, description: &'static str) -> Self where - F: FnOnce(&InferCtxt<'tcx>) -> Fallible<InferOk<'tcx, R>>, - G: Fn() -> String, + F: FnOnce(&ObligationCtxt<'_, 'tcx>) -> Fallible<R>, { CustomTypeOp { closure, description } } } -impl<'tcx, F, R: fmt::Debug, G> super::TypeOp<'tcx> for CustomTypeOp<F, G> +impl<'tcx, F, R: fmt::Debug> super::TypeOp<'tcx> for CustomTypeOp<F> where - F: for<'a, 'cx> FnOnce(&'a InferCtxt<'tcx>) -> Fallible<InferOk<'tcx, R>>, - G: Fn() -> String, + F: FnOnce(&ObligationCtxt<'_, 'tcx>) -> Fallible<R>, { type Output = R; /// We can't do any custom error reporting for `CustomTypeOp`, so @@ -41,16 +40,13 @@ where info!("fully_perform({:?})", self); } - Ok(scrape_region_constraints(infcx, || (self.closure)(infcx))?.0) + Ok(scrape_region_constraints(infcx, self.closure)?.0) } } -impl<F, G> fmt::Debug for CustomTypeOp<F, G> -where - G: Fn() -> String, -{ +impl<F> fmt::Debug for CustomTypeOp<F> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", (self.description)()) + self.description.fmt(f) } } @@ -58,7 +54,7 @@ where /// constraints that result, creating query-region-constraints. pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>( infcx: &InferCtxt<'tcx>, - op: impl FnOnce() -> Fallible<InferOk<'tcx, R>>, + op: impl FnOnce(&ObligationCtxt<'_, 'tcx>) -> Fallible<R>, ) -> Fallible<(TypeOpOutput<'tcx, Op>, RegionConstraintData<'tcx>)> { // During NLL, we expect that nobody will register region // obligations **except** as part of a custom type op (and, at the @@ -72,16 +68,20 @@ pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>( pre_obligations, ); - let InferOk { value, obligations } = infcx.commit_if_ok(|_| op())?; - let ocx = ObligationCtxt::new(infcx); - ocx.register_obligations(obligations); - let errors = ocx.select_all_or_error(); - if !errors.is_empty() { - infcx.tcx.sess.diagnostic().delay_span_bug( - DUMMY_SP, - format!("errors selecting obligation during MIR typeck: {:?}", errors), - ); - } + let value = infcx.commit_if_ok(|_| { + let ocx = ObligationCtxt::new_in_snapshot(infcx); + let value = op(&ocx)?; + let errors = ocx.select_all_or_error(); + if errors.is_empty() { + Ok(value) + } else { + infcx.tcx.sess.delay_span_bug( + DUMMY_SP, + format!("errors selecting obligation during MIR typeck: {:?}", errors), + ); + Err(NoSolution) + } + })?; let region_obligations = infcx.take_registered_region_obligations(); let region_constraint_data = infcx.take_and_reset_region_constraints(); diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index aa230936903..8bc82b9f549 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -967,16 +967,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) { // The regions of a type don't affect the size of the type let tcx = self.tcx(); - let self_ty = - tcx.erase_regions(tcx.erase_late_bound_regions(obligation.predicate.self_ty())); - + let self_ty = tcx.erase_late_bound_regions(obligation.predicate.self_ty()); + // We should erase regions from both the param-env and type, since both + // may have infer regions. Specifically, after canonicalizing and instantiating, + // early bound regions turn into region vars in both the new and old solver. + let key = tcx.erase_regions(obligation.param_env.and(self_ty)); // But if there are inference variables, we have to wait until it's resolved. - if self_ty.has_non_region_infer() { + if key.has_non_region_infer() { candidates.ambiguous = true; return; } - if let Ok(layout) = tcx.layout_of(obligation.param_env.and(self_ty)) + if let Ok(layout) = tcx.layout_of(key) && layout.layout.is_pointer_like(&tcx.data_layout) { candidates.vec.push(BuiltinCandidate { has_nested: false }); diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index de023501f9e..0d9f55d4c2e 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -132,6 +132,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } }; + // The obligations returned by confirmation are recursively evaluated + // so we need to make sure they have the correct depth. + for subobligation in impl_src.borrow_nested_obligations_mut() { + subobligation.set_depth_from_parent(obligation.recursion_depth); + } + if !obligation.predicate.is_const_if_const() { // normalize nested predicates according to parent predicate's constness. impl_src = impl_src.map(|mut o| { diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index b366bbd531b..3baf1c97c9f 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1793,12 +1793,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .infcx .at(&obligation.cause, obligation.param_env) .sup(DefineOpaqueTypes::No, obligation.predicate, infer_projection) - .map_or(false, |InferOk { obligations, value: () }| { + .is_ok_and(|InferOk { obligations, value: () }| { self.evaluate_predicates_recursively( TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()), nested_obligations.into_iter().chain(obligations), ) - .map_or(false, |res| res.may_apply()) + .is_ok_and(|res| res.may_apply()) }); if is_match { diff --git a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs new file mode 100644 index 00000000000..af8dd0da579 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs @@ -0,0 +1,55 @@ +use rustc_infer::infer::at::At; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::traits::{FulfillmentError, TraitEngine}; +use rustc_middle::ty::{self, Ty}; + +use crate::traits::{query::evaluate_obligation::InferCtxtExt, NormalizeExt, Obligation}; + +pub trait StructurallyNormalizeExt<'tcx> { + fn structurally_normalize( + &self, + ty: Ty<'tcx>, + fulfill_cx: &mut dyn TraitEngine<'tcx>, + ) -> Result<Ty<'tcx>, Vec<FulfillmentError<'tcx>>>; +} + +impl<'tcx> StructurallyNormalizeExt<'tcx> for At<'_, 'tcx> { + fn structurally_normalize( + &self, + mut ty: Ty<'tcx>, + fulfill_cx: &mut dyn TraitEngine<'tcx>, + ) -> Result<Ty<'tcx>, Vec<FulfillmentError<'tcx>>> { + assert!(!ty.is_ty_var(), "should have resolved vars before calling"); + + if self.infcx.tcx.trait_solver_next() { + while let ty::Alias(ty::Projection, projection_ty) = *ty.kind() { + let new_infer_ty = self.infcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::NormalizeProjectionType, + span: self.cause.span, + }); + let obligation = Obligation::new( + self.infcx.tcx, + self.cause.clone(), + self.param_env, + ty::Binder::dummy(ty::ProjectionPredicate { + projection_ty, + term: new_infer_ty.into(), + }), + ); + if self.infcx.predicate_may_hold(&obligation) { + fulfill_cx.register_predicate_obligation(self.infcx, obligation); + let errors = fulfill_cx.select_where_possible(self.infcx); + if !errors.is_empty() { + return Err(errors); + } + ty = self.infcx.resolve_vars_if_possible(new_infer_ty); + } else { + break; + } + } + Ok(ty) + } else { + Ok(self.normalize(ty).into_value_registering_obligations(self.infcx, fulfill_cx)) + } + } +} diff --git a/compiler/rustc_traits/src/chalk/lowering.rs b/compiler/rustc_traits/src/chalk/lowering.rs index 2f9e480d8bd..e447ab94f64 100644 --- a/compiler/rustc_traits/src/chalk/lowering.rs +++ b/compiler/rustc_traits/src/chalk/lowering.rs @@ -679,7 +679,7 @@ impl<'tcx> LowerInto<'tcx, Option<chalk_ir::QuantifiedWhereClause<RustInterner<' | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Ambiguous | ty::PredicateKind::TypeWellFormedFromEnv(..) => { - bug!("unexpected predicate {}", &self) + bug!("unexpected predicate {self}") } }; value.map(|value| chalk_ir::Binders::new(binders, value)) diff --git a/compiler/rustc_traits/src/codegen.rs b/compiler/rustc_traits/src/codegen.rs index 6f81d343e0f..ddba03b0b12 100644 --- a/compiler/rustc_traits/src/codegen.rs +++ b/compiler/rustc_traits/src/codegen.rs @@ -3,9 +3,9 @@ // seems likely that they should eventually be merged into more // general routines. -use rustc_infer::infer::{DefiningAnchor, TyCtxtInferExt}; +use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::{FulfillmentErrorCode, TraitEngineExt as _}; -use rustc_middle::traits::CodegenObligationError; +use rustc_middle::traits::{CodegenObligationError, DefiningAnchor}; use rustc_middle::ty::{self, TyCtxt}; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::{ diff --git a/compiler/rustc_traits/src/evaluate_obligation.rs b/compiler/rustc_traits/src/evaluate_obligation.rs index 149dffc7e31..f5b2753b797 100644 --- a/compiler/rustc_traits/src/evaluate_obligation.rs +++ b/compiler/rustc_traits/src/evaluate_obligation.rs @@ -1,5 +1,6 @@ -use rustc_infer::infer::{DefiningAnchor, TyCtxtInferExt}; +use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::query::Providers; +use rustc_middle::traits::DefiningAnchor; use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; use rustc_span::source_map::DUMMY_SP; use rustc_trait_selection::traits::query::CanonicalPredicateGoal; @@ -15,6 +16,7 @@ fn evaluate_obligation<'tcx>( tcx: TyCtxt<'tcx>, canonical_goal: CanonicalPredicateGoal<'tcx>, ) -> Result<EvaluationResult, OverflowError> { + assert!(!tcx.trait_solver_next()); debug!("evaluate_obligation(canonical_goal={:#?})", canonical_goal); // HACK This bubble is required for this tests to pass: // impl-trait/issue99642.rs diff --git a/compiler/rustc_traits/src/type_op.rs b/compiler/rustc_traits/src/type_op.rs index 70dc7ccec63..b1cbd88ce27 100644 --- a/compiler/rustc_traits/src/type_op.rs +++ b/compiler/rustc_traits/src/type_op.rs @@ -1,8 +1,8 @@ use rustc_hir as hir; use rustc_infer::infer::canonical::{Canonical, QueryResponse}; -use rustc_infer::infer::{DefiningAnchor, TyCtxtInferExt}; -use rustc_infer::traits::ObligationCauseCode; +use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::query::Providers; +use rustc_middle::traits::{DefiningAnchor, ObligationCauseCode}; use rustc_middle::ty::{self, FnSig, Lift, PolyFnSig, Ty, TyCtxt, TypeFoldable}; use rustc_middle::ty::{ParamEnvAnd, Predicate}; use rustc_middle::ty::{UserSelfTy, UserSubsts, UserType}; diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 442d041a8a7..15c19104616 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -238,6 +238,7 @@ fn adjust_for_rust_scalar<'tcx>( layout: TyAndLayout<'tcx>, offset: Size, is_return: bool, + drop_target_pointee: Option<Ty<'tcx>>, ) { // Booleans are always a noundef i1 that needs to be zero-extended. if scalar.is_bool() { @@ -251,14 +252,24 @@ fn adjust_for_rust_scalar<'tcx>( } // Only pointer types handled below. - let Scalar::Initialized { value: Pointer(_), valid_range} = scalar else { return }; + let Scalar::Initialized { value: Pointer(_), valid_range } = scalar else { return }; - if !valid_range.contains(0) { + // Set `nonnull` if the validity range excludes zero, or for the argument to `drop_in_place`, + // which must be nonnull per its documented safety requirements. + if !valid_range.contains(0) || drop_target_pointee.is_some() { attrs.set(ArgAttribute::NonNull); } if let Some(pointee) = layout.pointee_info_at(&cx, offset) { - if let Some(kind) = pointee.safe { + let kind = if let Some(kind) = pointee.safe { + Some(kind) + } else if let Some(pointee) = drop_target_pointee { + // The argument to `drop_in_place` is semantically equivalent to a mutable reference. + Some(PointerKind::MutableRef { unpin: pointee.is_unpin(cx.tcx, cx.param_env()) }) + } else { + None + }; + if let Some(kind) = kind { attrs.pointee_align = Some(pointee.align); // `Box` are not necessarily dereferenceable for the entire duration of the function as @@ -362,10 +373,18 @@ fn fn_abi_new_uncached<'tcx>( use SpecAbi::*; let rust_abi = matches!(sig.abi, RustIntrinsic | PlatformIntrinsic | Rust | RustCall); + let is_drop_in_place = + fn_def_id.is_some() && fn_def_id == cx.tcx.lang_items().drop_in_place_fn(); + let arg_of = |ty: Ty<'tcx>, arg_idx: Option<usize>| -> Result<_, FnAbiError<'tcx>> { let span = tracing::debug_span!("arg_of"); let _entered = span.enter(); let is_return = arg_idx.is_none(); + let is_drop_target = is_drop_in_place && arg_idx == Some(0); + let drop_target_pointee = is_drop_target.then(|| match ty.kind() { + ty::RawPtr(ty::TypeAndMut { ty, .. }) => *ty, + _ => bug!("argument to drop_in_place is not a raw ptr: {:?}", ty), + }); let layout = cx.layout_of(ty)?; let layout = if force_thin_self_ptr && arg_idx == Some(0) { @@ -379,7 +398,15 @@ fn fn_abi_new_uncached<'tcx>( let mut arg = ArgAbi::new(cx, layout, |layout, scalar, offset| { let mut attrs = ArgAttributes::new(); - adjust_for_rust_scalar(*cx, &mut attrs, scalar, *layout, offset, is_return); + adjust_for_rust_scalar( + *cx, + &mut attrs, + scalar, + *layout, + offset, + is_return, + drop_target_pointee, + ); attrs }); diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index 6f2ba957bcd..01d1fdc9b2a 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -37,6 +37,9 @@ extern "Rust" { #[rustc_allocator_zeroed] #[rustc_nounwind] fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8; + + #[cfg(not(bootstrap))] + static __rust_no_alloc_shim_is_unstable: u8; } /// The global memory allocator. @@ -90,7 +93,14 @@ pub use std::alloc::Global; #[must_use = "losing the pointer will leak memory"] #[inline] pub unsafe fn alloc(layout: Layout) -> *mut u8 { - unsafe { __rust_alloc(layout.size(), layout.align()) } + unsafe { + // Make sure we don't accidentally allow omitting the allocator shim in + // stable code until it is actually stabilized. + #[cfg(not(bootstrap))] + core::ptr::read_volatile(&__rust_no_alloc_shim_is_unstable); + + __rust_alloc(layout.size(), layout.align()) + } } /// Deallocate memory with the global allocator. diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 7347980abbc..bfdb7a92bef 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -502,6 +502,7 @@ impl<T> Arc<T> { /// assert_eq!(*five, 5) /// ``` #[cfg(not(no_global_oom_handling))] + #[inline] #[unstable(feature = "new_uninit", issue = "63291")] #[must_use] pub fn new_uninit() -> Arc<mem::MaybeUninit<T>> { @@ -535,6 +536,7 @@ impl<T> Arc<T> { /// /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] + #[inline] #[unstable(feature = "new_uninit", issue = "63291")] #[must_use] pub fn new_zeroed() -> Arc<mem::MaybeUninit<T>> { @@ -844,6 +846,7 @@ impl<T> Arc<[T]> { /// assert_eq!(*values, [1, 2, 3]) /// ``` #[cfg(not(no_global_oom_handling))] + #[inline] #[unstable(feature = "new_uninit", issue = "63291")] #[must_use] pub fn new_uninit_slice(len: usize) -> Arc<[mem::MaybeUninit<T>]> { @@ -871,6 +874,7 @@ impl<T> Arc<[T]> { /// /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] + #[inline] #[unstable(feature = "new_uninit", issue = "63291")] #[must_use] pub fn new_zeroed_slice(len: usize) -> Arc<[mem::MaybeUninit<T>]> { @@ -1300,10 +1304,10 @@ impl<T: ?Sized> Arc<T> { mem_to_arcinner: impl FnOnce(*mut u8) -> *mut ArcInner<T>, ) -> *mut ArcInner<T> { let layout = arcinner_layout_for_value_layout(value_layout); - unsafe { - Arc::try_allocate_for_layout(value_layout, allocate, mem_to_arcinner) - .unwrap_or_else(|_| handle_alloc_error(layout)) - } + + let ptr = allocate(layout).unwrap_or_else(|_| handle_alloc_error(layout)); + + unsafe { Self::initialize_arcinner(ptr, layout, mem_to_arcinner) } } /// Allocates an `ArcInner<T>` with sufficient space for @@ -1321,7 +1325,16 @@ impl<T: ?Sized> Arc<T> { let ptr = allocate(layout)?; - // Initialize the ArcInner + let inner = unsafe { Self::initialize_arcinner(ptr, layout, mem_to_arcinner) }; + + Ok(inner) + } + + unsafe fn initialize_arcinner( + ptr: NonNull<[u8]>, + layout: Layout, + mem_to_arcinner: impl FnOnce(*mut u8) -> *mut ArcInner<T>, + ) -> *mut ArcInner<T> { let inner = mem_to_arcinner(ptr.as_non_null_ptr().as_ptr()); debug_assert_eq!(unsafe { Layout::for_value(&*inner) }, layout); @@ -1330,7 +1343,7 @@ impl<T: ?Sized> Arc<T> { ptr::write(&mut (*inner).weak, atomic::AtomicUsize::new(1)); } - Ok(inner) + inner } /// Allocates an `ArcInner<T>` with sufficient space for an unsized inner value. diff --git a/library/alloc/src/vec/in_place_collect.rs b/library/alloc/src/vec/in_place_collect.rs index 2f1ee8b0353..5ecd0479971 100644 --- a/library/alloc/src/vec/in_place_collect.rs +++ b/library/alloc/src/vec/in_place_collect.rs @@ -178,7 +178,8 @@ where ) }; - let len = SpecInPlaceCollect::collect_in_place(&mut iterator, dst_buf, dst_end); + // SAFETY: `dst_buf` and `dst_end` are the start and end of the buffer. + let len = unsafe { SpecInPlaceCollect::collect_in_place(&mut iterator, dst_buf, dst_end) }; let src = unsafe { iterator.as_inner().as_into_iter() }; // check if SourceIter contract was upheld @@ -239,7 +240,7 @@ trait SpecInPlaceCollect<T, I>: Iterator<Item = T> { /// `Iterator::__iterator_get_unchecked` calls with a `TrustedRandomAccessNoCoerce` bound /// on `I` which means the caller of this method must take the safety conditions /// of that trait into consideration. - fn collect_in_place(&mut self, dst: *mut T, end: *const T) -> usize; + unsafe fn collect_in_place(&mut self, dst: *mut T, end: *const T) -> usize; } impl<T, I> SpecInPlaceCollect<T, I> for I @@ -247,7 +248,7 @@ where I: Iterator<Item = T>, { #[inline] - default fn collect_in_place(&mut self, dst_buf: *mut T, end: *const T) -> usize { + default unsafe fn collect_in_place(&mut self, dst_buf: *mut T, end: *const T) -> usize { // use try-fold since // - it vectorizes better for some iterator adapters // - unlike most internal iteration methods, it only takes a &mut self @@ -265,7 +266,7 @@ where I: Iterator<Item = T> + TrustedRandomAccessNoCoerce, { #[inline] - fn collect_in_place(&mut self, dst_buf: *mut T, end: *const T) -> usize { + unsafe fn collect_in_place(&mut self, dst_buf: *mut T, end: *const T) -> usize { let len = self.size(); let mut drop_guard = InPlaceDrop { inner: dst_buf, dst: dst_buf }; for i in 0..len { diff --git a/library/core/benches/iter.rs b/library/core/benches/iter.rs index 9193c79bee8..60ef83223d1 100644 --- a/library/core/benches/iter.rs +++ b/library/core/benches/iter.rs @@ -404,7 +404,7 @@ fn bench_trusted_random_access_adapters(b: &mut Bencher) { /// Exercises the iter::Copied specialization for slice::Iter #[bench] -fn bench_copied_chunks(b: &mut Bencher) { +fn bench_next_chunk_copied(b: &mut Bencher) { let v = vec![1u8; 1024]; b.iter(|| { @@ -421,7 +421,7 @@ fn bench_copied_chunks(b: &mut Bencher) { /// Exercises the TrustedRandomAccess specialization in ArrayChunks #[bench] -fn bench_trusted_random_access_chunks(b: &mut Bencher) { +fn bench_next_chunk_trusted_random_access(b: &mut Bencher) { let v = vec![1u8; 1024]; b.iter(|| { @@ -437,3 +437,45 @@ fn bench_trusted_random_access_chunks(b: &mut Bencher) { .sum::<Wrapping<u64>>() }) } + +#[bench] +fn bench_next_chunk_filter_even(b: &mut Bencher) { + let a = (0..1024).next_chunk::<1024>().unwrap(); + + b.iter(|| black_box(&a).iter().filter(|&&i| i % 2 == 0).next_chunk::<32>()) +} + +#[bench] +fn bench_next_chunk_filter_predictably_true(b: &mut Bencher) { + let a = (0..1024).next_chunk::<1024>().unwrap(); + + b.iter(|| black_box(&a).iter().filter(|&&i| i < 100).next_chunk::<32>()) +} + +#[bench] +fn bench_next_chunk_filter_mostly_false(b: &mut Bencher) { + let a = (0..1024).next_chunk::<1024>().unwrap(); + + b.iter(|| black_box(&a).iter().filter(|&&i| i > 900).next_chunk::<32>()) +} + +#[bench] +fn bench_next_chunk_filter_map_even(b: &mut Bencher) { + let a = (0..1024).next_chunk::<1024>().unwrap(); + + b.iter(|| black_box(&a).iter().filter_map(|&i| (i % 2 == 0).then(|| i)).next_chunk::<32>()) +} + +#[bench] +fn bench_next_chunk_filter_map_predictably_true(b: &mut Bencher) { + let a = (0..1024).next_chunk::<1024>().unwrap(); + + b.iter(|| black_box(&a).iter().filter_map(|&i| (i < 100).then(|| i)).next_chunk::<32>()) +} + +#[bench] +fn bench_next_chunk_filter_map_mostly_false(b: &mut Bencher) { + let a = (0..1024).next_chunk::<1024>().unwrap(); + + b.iter(|| black_box(&a).iter().filter_map(|&i| (i > 900).then(|| i)).next_chunk::<32>()) +} diff --git a/library/core/src/ascii.rs b/library/core/src/ascii.rs index 7fd14a7e1ea..ef8e4d098ed 100644 --- a/library/core/src/ascii.rs +++ b/library/core/src/ascii.rs @@ -91,7 +91,7 @@ pub struct EscapeDefault(escape::EscapeIterInner<4>); /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn escape_default(c: u8) -> EscapeDefault { - let mut data = [0; 4]; + let mut data = [Char::Null; 4]; let range = escape::escape_ascii_into(&mut data, c); EscapeDefault(escape::EscapeIterInner::new(data, range)) } diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 1dfa9c34db1..515b8d20ead 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -392,13 +392,13 @@ impl char { #[inline] pub(crate) fn escape_debug_ext(self, args: EscapeDebugExtArgs) -> EscapeDebug { match self { - '\0' => EscapeDebug::backslash(b'0'), - '\t' => EscapeDebug::backslash(b't'), - '\r' => EscapeDebug::backslash(b'r'), - '\n' => EscapeDebug::backslash(b'n'), - '\\' => EscapeDebug::backslash(b'\\'), - '"' if args.escape_double_quote => EscapeDebug::backslash(b'"'), - '\'' if args.escape_single_quote => EscapeDebug::backslash(b'\''), + '\0' => EscapeDebug::backslash(ascii::Char::Digit0), + '\t' => EscapeDebug::backslash(ascii::Char::SmallT), + '\r' => EscapeDebug::backslash(ascii::Char::SmallR), + '\n' => EscapeDebug::backslash(ascii::Char::SmallN), + '\\' => EscapeDebug::backslash(ascii::Char::ReverseSolidus), + '\"' if args.escape_double_quote => EscapeDebug::backslash(ascii::Char::QuotationMark), + '\'' if args.escape_single_quote => EscapeDebug::backslash(ascii::Char::Apostrophe), _ if args.escape_grapheme_extended && self.is_grapheme_extended() => { EscapeDebug::from_unicode(self.escape_unicode()) } @@ -503,11 +503,11 @@ impl char { #[inline] pub fn escape_default(self) -> EscapeDefault { match self { - '\t' => EscapeDefault::backslash(b't'), - '\r' => EscapeDefault::backslash(b'r'), - '\n' => EscapeDefault::backslash(b'n'), - '\\' | '\'' | '"' => EscapeDefault::backslash(self as u8), - '\x20'..='\x7e' => EscapeDefault::printable(self as u8), + '\t' => EscapeDefault::backslash(ascii::Char::SmallT), + '\r' => EscapeDefault::backslash(ascii::Char::SmallR), + '\n' => EscapeDefault::backslash(ascii::Char::SmallN), + '\\' | '\'' | '"' => EscapeDefault::backslash(self.as_ascii().unwrap()), + '\x20'..='\x7e' => EscapeDefault::printable(self.as_ascii().unwrap()), _ => EscapeDefault::from_unicode(self.escape_unicode()), } } diff --git a/library/core/src/char/mod.rs b/library/core/src/char/mod.rs index e186db7052c..5c42912874c 100644 --- a/library/core/src/char/mod.rs +++ b/library/core/src/char/mod.rs @@ -38,6 +38,7 @@ pub use self::methods::encode_utf16_raw; #[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] pub use self::methods::encode_utf8_raw; +use crate::ascii; use crate::error::Error; use crate::escape; use crate::fmt::{self, Write}; @@ -152,7 +153,7 @@ pub struct EscapeUnicode(escape::EscapeIterInner<10>); impl EscapeUnicode { fn new(chr: char) -> Self { - let mut data = [0; 10]; + let mut data = [ascii::Char::Null; 10]; let range = escape::escape_unicode_into(&mut data, chr); Self(escape::EscapeIterInner::new(data, range)) } @@ -218,14 +219,14 @@ impl fmt::Display for EscapeUnicode { pub struct EscapeDefault(escape::EscapeIterInner<10>); impl EscapeDefault { - fn printable(chr: u8) -> Self { - let data = [chr, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Self(escape::EscapeIterInner::new(data, 0..1)) + fn printable(chr: ascii::Char) -> Self { + let data = [chr]; + Self(escape::EscapeIterInner::from_array(data)) } - fn backslash(chr: u8) -> Self { - let data = [b'\\', chr, 0, 0, 0, 0, 0, 0, 0, 0]; - Self(escape::EscapeIterInner::new(data, 0..2)) + fn backslash(chr: ascii::Char) -> Self { + let data = [ascii::Char::ReverseSolidus, chr]; + Self(escape::EscapeIterInner::from_array(data)) } fn from_unicode(esc: EscapeUnicode) -> Self { @@ -307,9 +308,9 @@ impl EscapeDebug { Self(EscapeDebugInner::Char(chr)) } - fn backslash(chr: u8) -> Self { - let data = [b'\\', chr, 0, 0, 0, 0, 0, 0, 0, 0]; - let iter = escape::EscapeIterInner::new(data, 0..2); + fn backslash(chr: ascii::Char) -> Self { + let data = [ascii::Char::ReverseSolidus, chr]; + let iter = escape::EscapeIterInner::from_array(data); Self(EscapeDebugInner::Bytes(iter)) } @@ -318,7 +319,7 @@ impl EscapeDebug { } fn clear(&mut self) { - let bytes = escape::EscapeIterInner::new([0; 10], 0..0); + let bytes = escape::EscapeIterInner::from_array([]); self.0 = EscapeDebugInner::Bytes(bytes); } } diff --git a/library/core/src/escape.rs b/library/core/src/escape.rs index 20ac3cf027f..3d471419bb8 100644 --- a/library/core/src/escape.rs +++ b/library/core/src/escape.rs @@ -1,34 +1,41 @@ //! Helper code for character escaping. +use crate::ascii; use crate::num::NonZeroUsize; use crate::ops::Range; -const HEX_DIGITS: [u8; 16] = *b"0123456789abcdef"; +const HEX_DIGITS: [ascii::Char; 16] = *b"0123456789abcdef".as_ascii().unwrap(); /// Escapes a byte into provided buffer; returns length of escaped /// representation. -pub(crate) fn escape_ascii_into(output: &mut [u8; 4], byte: u8) -> Range<u8> { +pub(crate) fn escape_ascii_into(output: &mut [ascii::Char; 4], byte: u8) -> Range<u8> { + #[inline] + fn backslash(a: ascii::Char) -> ([ascii::Char; 4], u8) { + ([ascii::Char::ReverseSolidus, a, ascii::Char::Null, ascii::Char::Null], 2) + } + let (data, len) = match byte { - b'\t' => ([b'\\', b't', 0, 0], 2), - b'\r' => ([b'\\', b'r', 0, 0], 2), - b'\n' => ([b'\\', b'n', 0, 0], 2), - b'\\' => ([b'\\', b'\\', 0, 0], 2), - b'\'' => ([b'\\', b'\'', 0, 0], 2), - b'"' => ([b'\\', b'"', 0, 0], 2), - b'\x20'..=b'\x7e' => ([byte, 0, 0, 0], 1), - _ => { + b'\t' => backslash(ascii::Char::SmallT), + b'\r' => backslash(ascii::Char::SmallR), + b'\n' => backslash(ascii::Char::SmallN), + b'\\' => backslash(ascii::Char::ReverseSolidus), + b'\'' => backslash(ascii::Char::Apostrophe), + b'\"' => backslash(ascii::Char::QuotationMark), + _ => if let Some(a) = byte.as_ascii() && !byte.is_ascii_control() { + ([a, ascii::Char::Null, ascii::Char::Null, ascii::Char::Null], 1) + } else { let hi = HEX_DIGITS[usize::from(byte >> 4)]; let lo = HEX_DIGITS[usize::from(byte & 0xf)]; - ([b'\\', b'x', hi, lo], 4) + ([ascii::Char::ReverseSolidus, ascii::Char::SmallX, hi, lo], 4) } }; *output = data; - 0..(len as u8) + 0..len } /// Escapes a character into provided buffer using `\u{NNNN}` representation. -pub(crate) fn escape_unicode_into(output: &mut [u8; 10], ch: char) -> Range<u8> { - output[9] = b'}'; +pub(crate) fn escape_unicode_into(output: &mut [ascii::Char; 10], ch: char) -> Range<u8> { + output[9] = ascii::Char::RightCurlyBracket; let ch = ch as u32; output[3] = HEX_DIGITS[((ch >> 20) & 15) as usize]; @@ -41,7 +48,8 @@ pub(crate) fn escape_unicode_into(output: &mut [u8; 10], ch: char) -> Range<u8> // or-ing 1 ensures that for ch==0 the code computes that one digit should // be printed. let start = (ch | 1).leading_zeros() as usize / 4 - 2; - output[start..start + 3].copy_from_slice(b"\\u{"); + const UNICODE_ESCAPE_PREFIX: &[ascii::Char; 3] = b"\\u{".as_ascii().unwrap(); + output[start..][..3].copy_from_slice(UNICODE_ESCAPE_PREFIX); (start as u8)..10 } @@ -52,29 +60,34 @@ pub(crate) fn escape_unicode_into(output: &mut [u8; 10], ch: char) -> Range<u8> /// limited to u8 to reduce size of the structure. #[derive(Clone, Debug)] pub(crate) struct EscapeIterInner<const N: usize> { - // Invariant: data[alive] is all ASCII. - pub(crate) data: [u8; N], + // The element type ensures this is always ASCII, and thus also valid UTF-8. + pub(crate) data: [ascii::Char; N], // Invariant: alive.start <= alive.end <= N. pub(crate) alive: Range<u8>, } impl<const N: usize> EscapeIterInner<N> { - pub fn new(data: [u8; N], alive: Range<u8>) -> Self { + pub fn new(data: [ascii::Char; N], alive: Range<u8>) -> Self { const { assert!(N < 256) }; debug_assert!(alive.start <= alive.end && usize::from(alive.end) <= N, "{alive:?}"); - let this = Self { data, alive }; - debug_assert!(this.as_bytes().is_ascii(), "Expected ASCII, got {:?}", this.as_bytes()); - this + Self { data, alive } + } + + pub fn from_array<const M: usize>(array: [ascii::Char; M]) -> Self { + const { assert!(M <= N) }; + + let mut data = [ascii::Char::Null; N]; + data[..M].copy_from_slice(&array); + Self::new(data, 0..M as u8) } - fn as_bytes(&self) -> &[u8] { + pub fn as_ascii(&self) -> &[ascii::Char] { &self.data[usize::from(self.alive.start)..usize::from(self.alive.end)] } pub fn as_str(&self) -> &str { - // SAFETY: self.data[self.alive] is all ASCII characters. - unsafe { crate::str::from_utf8_unchecked(self.as_bytes()) } + self.as_ascii().as_str() } pub fn len(&self) -> usize { @@ -82,11 +95,11 @@ impl<const N: usize> EscapeIterInner<N> { } pub fn next(&mut self) -> Option<u8> { - self.alive.next().map(|i| self.data[usize::from(i)]) + self.alive.next().map(|i| self.data[usize::from(i)].as_u8()) } pub fn next_back(&mut self) -> Option<u8> { - self.alive.next_back().map(|i| self.data[usize::from(i)]) + self.alive.next_back().map(|i| self.data[usize::from(i)].as_u8()) } pub fn advance_by(&mut self, n: usize) -> Result<(), NonZeroUsize> { diff --git a/library/core/src/fmt/float.rs b/library/core/src/fmt/float.rs index 89d5fac30d3..3bbf5d8770b 100644 --- a/library/core/src/fmt/float.rs +++ b/library/core/src/fmt/float.rs @@ -45,7 +45,8 @@ where &mut buf, &mut parts, ); - fmt.pad_formatted_parts(&formatted) + // SAFETY: `to_exact_fixed_str` and `format_exact` produce only ASCII characters. + unsafe { fmt.pad_formatted_parts(&formatted) } } // Don't inline this so callers that call both this and the above won't wind @@ -71,7 +72,8 @@ where &mut buf, &mut parts, ); - fmt.pad_formatted_parts(&formatted) + // SAFETY: `to_shortest_str` and `format_shortest` produce only ASCII characters. + unsafe { fmt.pad_formatted_parts(&formatted) } } fn float_to_decimal_display<T>(fmt: &mut Formatter<'_>, num: &T) -> Result @@ -116,7 +118,8 @@ where &mut buf, &mut parts, ); - fmt.pad_formatted_parts(&formatted) + // SAFETY: `to_exact_exp_str` and `format_exact` produce only ASCII characters. + unsafe { fmt.pad_formatted_parts(&formatted) } } // Don't inline this so callers that call both this and the above won't wind @@ -143,7 +146,8 @@ where &mut buf, &mut parts, ); - fmt.pad_formatted_parts(&formatted) + // SAFETY: `to_shortest_exp_str` and `format_shortest` produce only ASCII characters. + unsafe { fmt.pad_formatted_parts(&formatted) } } // Common code of floating point LowerExp and UpperExp. diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index bac2f31878b..1786b309c5b 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -1415,7 +1415,11 @@ impl<'a> Formatter<'a> { /// Takes the formatted parts and applies the padding. /// Assumes that the caller already has rendered the parts with required precision, /// so that `self.precision` can be ignored. - fn pad_formatted_parts(&mut self, formatted: &numfmt::Formatted<'_>) -> Result { + /// + /// # Safety + /// + /// Any `numfmt::Part::Copy` parts in `formatted` must contain valid UTF-8. + unsafe fn pad_formatted_parts(&mut self, formatted: &numfmt::Formatted<'_>) -> Result { if let Some(mut width) = self.width { // for the sign-aware zero padding, we render the sign first and // behave as if we had no sign from the beginning. @@ -1438,10 +1442,14 @@ impl<'a> Formatter<'a> { let len = formatted.len(); let ret = if width <= len { // no padding - self.write_formatted_parts(&formatted) + // SAFETY: Per the precondition. + unsafe { self.write_formatted_parts(&formatted) } } else { let post_padding = self.padding(width - len, Alignment::Right)?; - self.write_formatted_parts(&formatted)?; + // SAFETY: Per the precondition. + unsafe { + self.write_formatted_parts(&formatted)?; + } post_padding.write(self) }; self.fill = old_fill; @@ -1449,20 +1457,20 @@ impl<'a> Formatter<'a> { ret } else { // this is the common case and we take a shortcut - self.write_formatted_parts(formatted) + // SAFETY: Per the precondition. + unsafe { self.write_formatted_parts(formatted) } } } - fn write_formatted_parts(&mut self, formatted: &numfmt::Formatted<'_>) -> Result { - fn write_bytes(buf: &mut dyn Write, s: &[u8]) -> Result { + /// # Safety + /// + /// Any `numfmt::Part::Copy` parts in `formatted` must contain valid UTF-8. + unsafe fn write_formatted_parts(&mut self, formatted: &numfmt::Formatted<'_>) -> Result { + unsafe fn write_bytes(buf: &mut dyn Write, s: &[u8]) -> Result { // SAFETY: This is used for `numfmt::Part::Num` and `numfmt::Part::Copy`. // It's safe to use for `numfmt::Part::Num` since every char `c` is between - // `b'0'` and `b'9'`, which means `s` is valid UTF-8. - // It's also probably safe in practice to use for `numfmt::Part::Copy(buf)` - // since `buf` should be plain ASCII, but it's possible for someone to pass - // in a bad value for `buf` into `numfmt::to_shortest_str` since it is a - // public function. - // FIXME: Determine whether this could result in UB. + // `b'0'` and `b'9'`, which means `s` is valid UTF-8. It's safe to use for + // `numfmt::Part::Copy` due to this function's precondition. buf.write_str(unsafe { str::from_utf8_unchecked(s) }) } @@ -1489,11 +1497,15 @@ impl<'a> Formatter<'a> { *c = b'0' + (v % 10) as u8; v /= 10; } - write_bytes(self.buf, &s[..len])?; + // SAFETY: Per the precondition. + unsafe { + write_bytes(self.buf, &s[..len])?; + } } - numfmt::Part::Copy(buf) => { + // SAFETY: Per the precondition. + numfmt::Part::Copy(buf) => unsafe { write_bytes(self.buf, buf)?; - } + }, } } Ok(()) diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index d8365ae9bf9..4f42f73ebba 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -52,8 +52,12 @@ impl_int! { i8 i16 i32 i64 i128 isize } impl_uint! { u8 u16 u32 u64 u128 usize } /// A type that represents a specific radix +/// +/// # Safety +/// +/// `digit` must return an ASCII character. #[doc(hidden)] -trait GenericRadix: Sized { +unsafe trait GenericRadix: Sized { /// The number of digits. const BASE: u8; @@ -129,7 +133,7 @@ struct UpperHex; macro_rules! radix { ($T:ident, $base:expr, $prefix:expr, $($x:pat => $conv:expr),+) => { - impl GenericRadix for $T { + unsafe impl GenericRadix for $T { const BASE: u8 = $base; const PREFIX: &'static str = $prefix; fn digit(x: u8) -> u8 { @@ -407,7 +411,7 @@ macro_rules! impl_Exp { let parts = &[ numfmt::Part::Copy(buf_slice), numfmt::Part::Zero(added_precision), - numfmt::Part::Copy(exp_slice) + numfmt::Part::Copy(exp_slice), ]; let sign = if !is_nonnegative { "-" @@ -416,8 +420,9 @@ macro_rules! impl_Exp { } else { "" }; - let formatted = numfmt::Formatted{sign, parts}; - f.pad_formatted_parts(&formatted) + let formatted = numfmt::Formatted { sign, parts }; + // SAFETY: `buf_slice` and `exp_slice` contain only ASCII characters. + unsafe { f.pad_formatted_parts(&formatted) } } $( diff --git a/library/core/src/future/join.rs b/library/core/src/future/join.rs index 35f0dea062e..3f35179ddc2 100644 --- a/library/core/src/future/join.rs +++ b/library/core/src/future/join.rs @@ -4,7 +4,7 @@ use crate::cell::UnsafeCell; use crate::future::{poll_fn, Future}; use crate::mem; use crate::pin::Pin; -use crate::task::{Context, Poll}; +use crate::task::{ready, Context, Poll}; /// Polls multiple futures simultaneously, returning a tuple /// of all results once complete. @@ -118,7 +118,7 @@ macro join_internal { fut }) }; - // Despite how tempting it may be to `let () = fut.poll(cx).ready()?;` + // Despite how tempting it may be to `let () = ready!(fut.poll(cx));` // doing so would defeat the point of `join!`: to start polling eagerly all // of the futures, to allow parallelizing the waits. done &= fut.poll(cx).is_ready(); @@ -180,7 +180,7 @@ impl<F: Future> Future for MaybeDone<F> { // Do not mix match ergonomics with unsafe. match *self.as_mut().get_unchecked_mut() { MaybeDone::Future(ref mut f) => { - let val = Pin::new_unchecked(f).poll(cx).ready()?; + let val = ready!(Pin::new_unchecked(f).poll(cx)); self.set(Self::Done(val)); } MaybeDone::Done(_) => {} diff --git a/library/core/src/iter/adapters/filter.rs b/library/core/src/iter/adapters/filter.rs index a0afaa326ad..723657b9e43 100644 --- a/library/core/src/iter/adapters/filter.rs +++ b/library/core/src/iter/adapters/filter.rs @@ -1,6 +1,9 @@ use crate::fmt; use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable}; use crate::ops::Try; +use core::array; +use core::mem::{ManuallyDrop, MaybeUninit}; +use core::ops::ControlFlow; /// An iterator that filters the elements of `iter` with `predicate`. /// @@ -57,6 +60,58 @@ where } #[inline] + fn next_chunk<const N: usize>( + &mut self, + ) -> Result<[Self::Item; N], array::IntoIter<Self::Item, N>> { + let mut array: [MaybeUninit<Self::Item>; N] = MaybeUninit::uninit_array(); + + struct Guard<'a, T> { + array: &'a mut [MaybeUninit<T>], + initialized: usize, + } + + impl<T> Drop for Guard<'_, T> { + #[inline] + fn drop(&mut self) { + if const { crate::mem::needs_drop::<T>() } { + // SAFETY: self.initialized is always <= N, which also is the length of the array. + unsafe { + core::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut( + self.array.get_unchecked_mut(..self.initialized), + )); + } + } + } + } + + let mut guard = Guard { array: &mut array, initialized: 0 }; + + let result = self.iter.try_for_each(|element| { + let idx = guard.initialized; + guard.initialized = idx + (self.predicate)(&element) as usize; + + // SAFETY: Loop conditions ensure the index is in bounds. + unsafe { guard.array.get_unchecked_mut(idx) }.write(element); + + if guard.initialized < N { ControlFlow::Continue(()) } else { ControlFlow::Break(()) } + }); + + let guard = ManuallyDrop::new(guard); + + match result { + ControlFlow::Break(()) => { + // SAFETY: The loop above is only explicitly broken when the array has been fully initialized + Ok(unsafe { MaybeUninit::array_assume_init(array) }) + } + ControlFlow::Continue(()) => { + let initialized = guard.initialized; + // SAFETY: The range is in bounds since the loop breaks when reaching N elements. + Err(unsafe { array::IntoIter::new_unchecked(array, 0..initialized) }) + } + } + } + + #[inline] fn size_hint(&self) -> (usize, Option<usize>) { let (_, upper) = self.iter.size_hint(); (0, upper) // can't know a lower bound, due to the predicate diff --git a/library/core/src/iter/adapters/filter_map.rs b/library/core/src/iter/adapters/filter_map.rs index 6bdf53f7fc9..693479977db 100644 --- a/library/core/src/iter/adapters/filter_map.rs +++ b/library/core/src/iter/adapters/filter_map.rs @@ -1,6 +1,7 @@ -use crate::fmt; use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable}; +use crate::mem::{ManuallyDrop, MaybeUninit}; use crate::ops::{ControlFlow, Try}; +use crate::{array, fmt}; /// An iterator that uses `f` to both filter and map elements from `iter`. /// @@ -62,6 +63,65 @@ where } #[inline] + fn next_chunk<const N: usize>( + &mut self, + ) -> Result<[Self::Item; N], array::IntoIter<Self::Item, N>> { + let mut array: [MaybeUninit<Self::Item>; N] = MaybeUninit::uninit_array(); + + struct Guard<'a, T> { + array: &'a mut [MaybeUninit<T>], + initialized: usize, + } + + impl<T> Drop for Guard<'_, T> { + #[inline] + fn drop(&mut self) { + if const { crate::mem::needs_drop::<T>() } { + // SAFETY: self.initialized is always <= N, which also is the length of the array. + unsafe { + core::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut( + self.array.get_unchecked_mut(..self.initialized), + )); + } + } + } + } + + let mut guard = Guard { array: &mut array, initialized: 0 }; + + let result = self.iter.try_for_each(|element| { + let idx = guard.initialized; + let val = (self.f)(element); + guard.initialized = idx + val.is_some() as usize; + + // SAFETY: Loop conditions ensure the index is in bounds. + + unsafe { + let opt_payload_at = core::intrinsics::option_payload_ptr(&val); + let dst = guard.array.as_mut_ptr().add(idx); + crate::ptr::copy_nonoverlapping(opt_payload_at.cast(), dst, 1); + crate::mem::forget(val); + }; + + if guard.initialized < N { ControlFlow::Continue(()) } else { ControlFlow::Break(()) } + }); + + let guard = ManuallyDrop::new(guard); + + match result { + ControlFlow::Break(()) => { + // SAFETY: The loop above is only explicitly broken when the array has been fully initialized + Ok(unsafe { MaybeUninit::array_assume_init(array) }) + } + ControlFlow::Continue(()) => { + let initialized = guard.initialized; + // SAFETY: The range is in bounds since the loop breaks when reaching N elements. + Err(unsafe { array::IntoIter::new_unchecked(array, 0..initialized) }) + } + } + } + + #[inline] fn size_hint(&self) -> (usize, Option<usize>) { let (_, upper) = self.iter.size_hint(); (0, upper) // can't know a lower bound, due to the predicate diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs index 76b3a32880d..0675e56358f 100644 --- a/library/core/src/iter/traits/collect.rs +++ b/library/core/src/iter/traits/collect.rs @@ -95,6 +95,16 @@ #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented( on( + _Self = "&[{A}]", + message = "a slice of type `{Self}` cannot be built since we need to store the elements somewhere", + label = "try explicitly collecting into a `Vec<{A}>`", + ), + on( + all(A = "{integer}", any(_Self = "&[{integral}]",)), + message = "a slice of type `{Self}` cannot be built since we need to store the elements somewhere", + label = "try explicitly collecting into a `Vec<{A}>`", + ), + on( _Self = "[{A}]", message = "a slice of type `{Self}` cannot be built since `{Self}` has no definite size", label = "try explicitly collecting into a `Vec<{A}>`", diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 0af04fac909..6c419eb16f3 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -216,6 +216,7 @@ #![feature(intra_doc_pointers)] #![feature(intrinsics)] #![feature(lang_items)] +#![feature(let_chains)] #![feature(link_llvm_intrinsics)] #![feature(macro_metavar_expr)] #![feature(min_specialization)] diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index ca45683d3d6..8dab8d1a692 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -43,27 +43,17 @@ use crate::hash::Hasher; /// ``` #[unstable(feature = "internal_impls_macro", issue = "none")] macro marker_impls { - ( $(#[$($meta:tt)*])* $Trait:ident for $( $({$($bounds:tt)*})? $T:ty ),+ $(,)?) => { - // This inner macro is needed because... idk macros are weird. - // It allows repeating `meta` on all impls. - #[unstable(feature = "internal_impls_macro", issue = "none")] - macro _impl { - ( $$({$$($$bounds_:tt)*})? $$T_:ty ) => { - $(#[$($meta)*])* impl<$$($$($$bounds_)*)?> $Trait for $$T_ {} - } - } - $( _impl! { $({$($bounds)*})? $T } )+ + ( $(#[$($meta:tt)*])* $Trait:ident for $({$($bounds:tt)*})? $T:ty $(, $($rest:tt)*)? ) => { + $(#[$($meta)*])* impl< $($($bounds)*)? > $Trait for $T {} + marker_impls! { $(#[$($meta)*])* $Trait for $($($rest)*)? } }, - ( $(#[$($meta:tt)*])* unsafe $Trait:ident for $( $({$($bounds:tt)*})? $T:ty ),+ $(,)?) => { - #[unstable(feature = "internal_impls_macro", issue = "none")] - macro _impl { - ( $$({$$($$bounds_:tt)*})? $$T_:ty ) => { - $(#[$($meta)*])* unsafe impl<$$($$($$bounds_)*)?> $Trait for $$T_ {} - } - } + ( $(#[$($meta:tt)*])* $Trait:ident for ) => {}, - $( _impl! { $({$($bounds)*})? $T } )+ + ( $(#[$($meta:tt)*])* unsafe $Trait:ident for $({$($bounds:tt)*})? $T:ty $(, $($rest:tt)*)? ) => { + $(#[$($meta)*])* unsafe impl< $($($bounds)*)? > $Trait for $T {} + marker_impls! { $(#[$($meta)*])* unsafe $Trait for $($($rest)*)? } }, + ( $(#[$($meta:tt)*])* unsafe $Trait:ident for ) => {}, } /// Types that can be transferred across thread boundaries. @@ -986,7 +976,7 @@ pub trait PointerLike {} #[rustc_on_unimplemented(message = "`{Self}` can't be used as a const parameter type")] pub trait ConstParamTy: StructuralEq {} -/// Derive macro generating an impl of the trait `Copy`. +/// Derive macro generating an impl of the trait `ConstParamTy`. #[rustc_builtin_macro] #[unstable(feature = "adt_const_params", issue = "95174")] #[cfg(not(bootstrap))] diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 289305253ec..afbfd6d362d 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -968,7 +968,7 @@ pub const fn replace<T>(dest: &mut T, src: T) -> T { /// Integers and other types implementing [`Copy`] are unaffected by `drop`. /// /// ``` -/// # #![cfg_attr(not(bootstrap), allow(drop_copy))] +/// # #![cfg_attr(not(bootstrap), allow(dropping_copy_types))] /// #[derive(Copy, Clone)] /// struct Foo(u8); /// diff --git a/library/core/src/panic.rs b/library/core/src/panic.rs index ebcce79b0f8..20be60d3535 100644 --- a/library/core/src/panic.rs +++ b/library/core/src/panic.rs @@ -28,13 +28,13 @@ pub macro panic_2015 { $crate::panicking::panic($msg) ), // Use `panic_str` instead of `panic_display::<&str>` for non_fmt_panic lint. - ($msg:expr $(,)?) => ( - $crate::panicking::panic_str($msg) - ), + ($msg:expr $(,)?) => ({ + $crate::panicking::panic_str($msg); + }), // Special-case the single-argument case for const_panic. - ("{}", $arg:expr $(,)?) => ( - $crate::panicking::panic_display(&$arg) - ), + ("{}", $arg:expr $(,)?) => ({ + $crate::panicking::panic_display(&$arg); + }), ($fmt:expr, $($arg:tt)+) => ({ // Semicolon to prevent temporaries inside the formatting machinery from // being considered alive in the caller after the panic_fmt call. @@ -52,9 +52,9 @@ pub macro panic_2021 { $crate::panicking::panic("explicit panic") ), // Special-case the single-argument case for const_panic. - ("{}", $arg:expr $(,)?) => ( - $crate::panicking::panic_display(&$arg) - ), + ("{}", $arg:expr $(,)?) => ({ + $crate::panicking::panic_display(&$arg); + }), ($($t:tt)+) => ({ // Semicolon to prevent temporaries inside the formatting machinery from // being considered alive in the caller after the panic_fmt call. @@ -73,9 +73,9 @@ pub macro unreachable_2015 { ), // Use of `unreachable_display` for non_fmt_panic lint. // NOTE: the message ("internal error ...") is embedded directly in unreachable_display - ($msg:expr $(,)?) => ( - $crate::panicking::unreachable_display(&$msg) - ), + ($msg:expr $(,)?) => ({ + $crate::panicking::unreachable_display(&$msg); + }), ($fmt:expr, $($arg:tt)*) => ( $crate::panic!($crate::concat!("internal error: entered unreachable code: ", $fmt), $($arg)*) ), diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index c4b89a63019..6b319b4355c 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -393,6 +393,8 @@ use crate::ops::{CoerceUnsized, Deref, DerefMut, DispatchFromDyn, Receiver}; /// value in place, preventing the value referenced by that pointer from being moved /// unless it implements [`Unpin`]. /// +/// `Pin<P>` is guaranteed to have the same memory layout and ABI as `P`. +/// /// *See the [`pin` module] documentation for an explanation of pinning.* /// /// [`pin` module]: self diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index ecbf4e66fa4..ff9fa48f311 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -441,10 +441,18 @@ mod mut_ptr; /// /// * `to_drop` must be [valid] for both reads and writes. /// -/// * `to_drop` must be properly aligned. +/// * `to_drop` must be properly aligned, even if `T` has size 0. /// -/// * The value `to_drop` points to must be valid for dropping, which may mean it must uphold -/// additional invariants - this is type-dependent. +/// * `to_drop` must be nonnull, even if `T` has size 0. +/// +/// * The value `to_drop` points to must be valid for dropping, which may mean +/// it must uphold additional invariants. These invariants depend on the type +/// of the value being dropped. For instance, when dropping a Box, the box's +/// pointer to the heap must be valid. +/// +/// * While `drop_in_place` is executing, the only way to access parts of +/// `to_drop` is through the `&mut self` references supplied to the +/// `Drop::drop` methods that `drop_in_place` invokes. /// /// Additionally, if `T` is not [`Copy`], using the pointed-to value after /// calling `drop_in_place` can cause undefined behavior. Note that `*to_drop = @@ -452,8 +460,6 @@ mod mut_ptr; /// again. [`write()`] can be used to overwrite data without causing it to be /// dropped. /// -/// Note that even if `T` has size `0`, the pointer must be non-null and properly aligned. -/// /// [valid]: self#safety /// /// # Examples diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 6fd2b87d0e3..bd1b16e8d73 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -42,6 +42,7 @@ mod index; mod iter; mod raw; mod rotate; +mod select; mod specialize; #[unstable(feature = "str_internals", issue = "none")] @@ -319,6 +320,264 @@ impl<T> [T] { if let [.., last] = self { Some(last) } else { None } } + /// Returns the first `N` elements of the slice, or `None` if it has fewer than `N` elements. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_first_last_chunk)] + /// + /// let u = [10, 40, 30]; + /// assert_eq!(Some(&[10, 40]), u.first_chunk::<2>()); + /// + /// let v: &[i32] = &[10]; + /// assert_eq!(None, v.first_chunk::<2>()); + /// + /// let w: &[i32] = &[]; + /// assert_eq!(Some(&[]), w.first_chunk::<0>()); + /// ``` + #[unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[inline] + pub const fn first_chunk<const N: usize>(&self) -> Option<&[T; N]> { + if self.len() < N { + None + } else { + // SAFETY: We explicitly check for the correct number of elements, + // and do not let the reference outlive the slice. + Some(unsafe { &*(self.as_ptr() as *const [T; N]) }) + } + } + + /// Returns a mutable reference to the first `N` elements of the slice, + /// or `None` if it has fewer than `N` elements. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_first_last_chunk)] + /// + /// let x = &mut [0, 1, 2]; + /// + /// if let Some(first) = x.first_chunk_mut::<2>() { + /// first[0] = 5; + /// first[1] = 4; + /// } + /// assert_eq!(x, &[5, 4, 2]); + /// ``` + #[unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[inline] + pub const fn first_chunk_mut<const N: usize>(&mut self) -> Option<&mut [T; N]> { + if self.len() < N { + None + } else { + // SAFETY: We explicitly check for the correct number of elements, + // do not let the reference outlive the slice, + // and require exclusive access to the entire slice to mutate the chunk. + Some(unsafe { &mut *(self.as_mut_ptr() as *mut [T; N]) }) + } + } + + /// Returns the first `N` elements of the slice and the remainder, + /// or `None` if it has fewer than `N` elements. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_first_last_chunk)] + /// + /// let x = &[0, 1, 2]; + /// + /// if let Some((first, elements)) = x.split_first_chunk::<2>() { + /// assert_eq!(first, &[0, 1]); + /// assert_eq!(elements, &[2]); + /// } + /// ``` + #[unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[inline] + pub const fn split_first_chunk<const N: usize>(&self) -> Option<(&[T; N], &[T])> { + if self.len() < N { + None + } else { + // SAFETY: We manually verified the bounds of the split. + let (first, tail) = unsafe { self.split_at_unchecked(N) }; + + // SAFETY: We explicitly check for the correct number of elements, + // and do not let the references outlive the slice. + Some((unsafe { &*(first.as_ptr() as *const [T; N]) }, tail)) + } + } + + /// Returns a mutable reference to the first `N` elements of the slice and the remainder, + /// or `None` if it has fewer than `N` elements. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_first_last_chunk)] + /// + /// let x = &mut [0, 1, 2]; + /// + /// if let Some((first, elements)) = x.split_first_chunk_mut::<2>() { + /// first[0] = 3; + /// first[1] = 4; + /// elements[0] = 5; + /// } + /// assert_eq!(x, &[3, 4, 5]); + /// ``` + #[unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[inline] + pub const fn split_first_chunk_mut<const N: usize>( + &mut self, + ) -> Option<(&mut [T; N], &mut [T])> { + if self.len() < N { + None + } else { + // SAFETY: We manually verified the bounds of the split. + let (first, tail) = unsafe { self.split_at_mut_unchecked(N) }; + + // SAFETY: We explicitly check for the correct number of elements, + // do not let the reference outlive the slice, + // and enforce exclusive mutability of the chunk by the split. + Some((unsafe { &mut *(first.as_mut_ptr() as *mut [T; N]) }, tail)) + } + } + + /// Returns the last `N` elements of the slice and the remainder, + /// or `None` if it has fewer than `N` elements. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_first_last_chunk)] + /// + /// let x = &[0, 1, 2]; + /// + /// if let Some((last, elements)) = x.split_last_chunk::<2>() { + /// assert_eq!(last, &[1, 2]); + /// assert_eq!(elements, &[0]); + /// } + /// ``` + #[unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[inline] + pub const fn split_last_chunk<const N: usize>(&self) -> Option<(&[T; N], &[T])> { + if self.len() < N { + None + } else { + // SAFETY: We manually verified the bounds of the split. + let (init, last) = unsafe { self.split_at_unchecked(self.len() - N) }; + + // SAFETY: We explicitly check for the correct number of elements, + // and do not let the references outlive the slice. + Some((unsafe { &*(last.as_ptr() as *const [T; N]) }, init)) + } + } + + /// Returns the last and all the rest of the elements of the slice, or `None` if it is empty. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_first_last_chunk)] + /// + /// let x = &mut [0, 1, 2]; + /// + /// if let Some((last, elements)) = x.split_last_chunk_mut::<2>() { + /// last[0] = 3; + /// last[1] = 4; + /// elements[0] = 5; + /// } + /// assert_eq!(x, &[5, 3, 4]); + /// ``` + #[unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[inline] + pub const fn split_last_chunk_mut<const N: usize>( + &mut self, + ) -> Option<(&mut [T; N], &mut [T])> { + if self.len() < N { + None + } else { + // SAFETY: We manually verified the bounds of the split. + let (init, last) = unsafe { self.split_at_mut_unchecked(self.len() - N) }; + + // SAFETY: We explicitly check for the correct number of elements, + // do not let the reference outlive the slice, + // and enforce exclusive mutability of the chunk by the split. + Some((unsafe { &mut *(last.as_mut_ptr() as *mut [T; N]) }, init)) + } + } + + /// Returns the last element of the slice, or `None` if it is empty. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_first_last_chunk)] + /// + /// let u = [10, 40, 30]; + /// assert_eq!(Some(&[40, 30]), u.last_chunk::<2>()); + /// + /// let v: &[i32] = &[10]; + /// assert_eq!(None, v.last_chunk::<2>()); + /// + /// let w: &[i32] = &[]; + /// assert_eq!(Some(&[]), w.last_chunk::<0>()); + /// ``` + #[unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[inline] + pub const fn last_chunk<const N: usize>(&self) -> Option<&[T; N]> { + if self.len() < N { + None + } else { + // SAFETY: We manually verified the bounds of the slice. + // FIXME: Without const traits, we need this instead of `get_unchecked`. + let last = unsafe { self.split_at_unchecked(self.len() - N).1 }; + + // SAFETY: We explicitly check for the correct number of elements, + // and do not let the references outlive the slice. + Some(unsafe { &*(last.as_ptr() as *const [T; N]) }) + } + } + + /// Returns a mutable pointer to the last item in the slice. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_first_last_chunk)] + /// + /// let x = &mut [0, 1, 2]; + /// + /// if let Some(last) = x.last_chunk_mut::<2>() { + /// last[0] = 10; + /// last[1] = 20; + /// } + /// assert_eq!(x, &[0, 10, 20]); + /// ``` + #[unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")] + #[inline] + pub const fn last_chunk_mut<const N: usize>(&mut self) -> Option<&mut [T; N]> { + if self.len() < N { + None + } else { + // SAFETY: We manually verified the bounds of the slice. + // FIXME: Without const traits, we need this instead of `get_unchecked`. + let last = unsafe { self.split_at_mut_unchecked(self.len() - N).1 }; + + // SAFETY: We explicitly check for the correct number of elements, + // do not let the reference outlive the slice, + // and require exclusive access to the entire slice to mutate the chunk. + Some(unsafe { &mut *(last.as_mut_ptr() as *mut [T; N]) }) + } + } + /// Returns a reference to an element or subslice depending on the type of /// index. /// @@ -995,7 +1254,7 @@ impl<T> [T] { #[unstable(feature = "slice_as_chunks", issue = "74985")] #[inline] #[must_use] - pub unsafe fn as_chunks_unchecked<const N: usize>(&self) -> &[[T; N]] { + pub const unsafe fn as_chunks_unchecked<const N: usize>(&self) -> &[[T; N]] { let this = self; // SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length let new_len = unsafe { @@ -1043,7 +1302,7 @@ impl<T> [T] { #[inline] #[track_caller] #[must_use] - pub fn as_chunks<const N: usize>(&self) -> (&[[T; N]], &[T]) { + pub const fn as_chunks<const N: usize>(&self) -> (&[[T; N]], &[T]) { assert!(N != 0, "chunk size must be non-zero"); let len = self.len() / N; let (multiple_of_n, remainder) = self.split_at(len * N); @@ -1075,7 +1334,7 @@ impl<T> [T] { #[inline] #[track_caller] #[must_use] - pub fn as_rchunks<const N: usize>(&self) -> (&[T], &[[T; N]]) { + pub const fn as_rchunks<const N: usize>(&self) -> (&[T], &[[T; N]]) { assert!(N != 0, "chunk size must be non-zero"); let len = self.len() / N; let (remainder, multiple_of_n) = self.split_at(self.len() - len * N); @@ -1152,7 +1411,7 @@ impl<T> [T] { #[unstable(feature = "slice_as_chunks", issue = "74985")] #[inline] #[must_use] - pub unsafe fn as_chunks_unchecked_mut<const N: usize>(&mut self) -> &mut [[T; N]] { + pub const unsafe fn as_chunks_unchecked_mut<const N: usize>(&mut self) -> &mut [[T; N]] { let this = &*self; // SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length let new_len = unsafe { @@ -1195,7 +1454,7 @@ impl<T> [T] { #[inline] #[track_caller] #[must_use] - pub fn as_chunks_mut<const N: usize>(&mut self) -> (&mut [[T; N]], &mut [T]) { + pub const fn as_chunks_mut<const N: usize>(&mut self) -> (&mut [[T; N]], &mut [T]) { assert!(N != 0, "chunk size must be non-zero"); let len = self.len() / N; let (multiple_of_n, remainder) = self.split_at_mut(len * N); @@ -1233,7 +1492,7 @@ impl<T> [T] { #[inline] #[track_caller] #[must_use] - pub fn as_rchunks_mut<const N: usize>(&mut self) -> (&mut [T], &mut [[T; N]]) { + pub const fn as_rchunks_mut<const N: usize>(&mut self) -> (&mut [T], &mut [[T; N]]) { assert!(N != 0, "chunk size must be non-zero"); let len = self.len() / N; let (remainder, multiple_of_n) = self.split_at_mut(self.len() - len * N); @@ -2776,7 +3035,7 @@ impl<T> [T] { where T: Ord, { - sort::partition_at_index(self, index, T::lt) + select::partition_at_index(self, index, T::lt) } /// Reorder the slice with a comparator function such that the element at `index` is at its @@ -2831,7 +3090,7 @@ impl<T> [T] { where F: FnMut(&T, &T) -> Ordering, { - sort::partition_at_index(self, index, |a: &T, b: &T| compare(a, b) == Less) + select::partition_at_index(self, index, |a: &T, b: &T| compare(a, b) == Less) } /// Reorder the slice with a key extraction function such that the element at `index` is at its @@ -2887,7 +3146,7 @@ impl<T> [T] { F: FnMut(&T) -> K, K: Ord, { - sort::partition_at_index(self, index, |a: &T, b: &T| f(a).lt(&f(b))) + select::partition_at_index(self, index, |a: &T, b: &T| f(a).lt(&f(b))) } /// Moves all consecutive repeated elements to the end of the slice according to the diff --git a/library/core/src/slice/select.rs b/library/core/src/slice/select.rs new file mode 100644 index 00000000000..ffc193578e0 --- /dev/null +++ b/library/core/src/slice/select.rs @@ -0,0 +1,302 @@ +//! Slice selection +//! +//! This module contains the implementation for `slice::select_nth_unstable`. +//! It uses an introselect algorithm based on Orson Peters' pattern-defeating quicksort, +//! published at: <https://github.com/orlp/pdqsort> +//! +//! The fallback algorithm used for introselect is Median of Medians using Tukey's Ninther +//! for pivot selection. Using this as a fallback ensures O(n) worst case running time with +//! better performance than one would get using heapsort as fallback. + +use crate::cmp; +use crate::mem::{self, SizedTypeProperties}; +use crate::slice::sort::{ + break_patterns, choose_pivot, insertion_sort_shift_left, partition, partition_equal, +}; + +// For slices of up to this length it's probably faster to simply sort them. +// Defined at the module scope because it's used in multiple functions. +const MAX_INSERTION: usize = 10; + +fn partition_at_index_loop<'a, T, F>( + mut v: &'a mut [T], + mut index: usize, + is_less: &mut F, + mut pred: Option<&'a T>, +) where + F: FnMut(&T, &T) -> bool, +{ + // Limit the amount of iterations and fall back to fast deterministic selection + // to ensure O(n) worst case running time. This limit needs to be constant, because + // using `ilog2(len)` like in `sort` would result in O(n log n) time complexity. + // The exact value of the limit is chosen somewhat arbitrarily, but for most inputs bad pivot + // selections should be relatively rare, so the limit usually shouldn't be reached + // anyways. + let mut limit = 16; + + // True if the last partitioning was reasonably balanced. + let mut was_balanced = true; + + loop { + if v.len() <= MAX_INSERTION { + if v.len() > 1 { + insertion_sort_shift_left(v, 1, is_less); + } + return; + } + + if limit == 0 { + median_of_medians(v, is_less, index); + return; + } + + // If the last partitioning was imbalanced, try breaking patterns in the slice by shuffling + // some elements around. Hopefully we'll choose a better pivot this time. + if !was_balanced { + break_patterns(v); + limit -= 1; + } + + // Choose a pivot + let (pivot, _) = choose_pivot(v, is_less); + + // If the chosen pivot is equal to the predecessor, then it's the smallest element in the + // slice. Partition the slice into elements equal to and elements greater than the pivot. + // This case is usually hit when the slice contains many duplicate elements. + if let Some(p) = pred { + if !is_less(p, &v[pivot]) { + let mid = partition_equal(v, pivot, is_less); + + // If we've passed our index, then we're good. + if mid > index { + return; + } + + // Otherwise, continue sorting elements greater than the pivot. + v = &mut v[mid..]; + index = index - mid; + pred = None; + continue; + } + } + + let (mid, _) = partition(v, pivot, is_less); + was_balanced = cmp::min(mid, v.len() - mid) >= v.len() / 8; + + // Split the slice into `left`, `pivot`, and `right`. + let (left, right) = v.split_at_mut(mid); + let (pivot, right) = right.split_at_mut(1); + let pivot = &pivot[0]; + + if mid < index { + v = right; + index = index - mid - 1; + pred = Some(pivot); + } else if mid > index { + v = left; + } else { + // If mid == index, then we're done, since partition() guaranteed that all elements + // after mid are greater than or equal to mid. + return; + } + } +} + +/// Helper function that returns the index of the minimum element in the slice using the given +/// comparator function +fn min_index<T, F: FnMut(&T, &T) -> bool>(slice: &[T], is_less: &mut F) -> Option<usize> { + slice + .iter() + .enumerate() + .reduce(|acc, t| if is_less(t.1, acc.1) { t } else { acc }) + .map(|(i, _)| i) +} + +/// Helper function that returns the index of the maximum element in the slice using the given +/// comparator function +fn max_index<T, F: FnMut(&T, &T) -> bool>(slice: &[T], is_less: &mut F) -> Option<usize> { + slice + .iter() + .enumerate() + .reduce(|acc, t| if is_less(acc.1, t.1) { t } else { acc }) + .map(|(i, _)| i) +} + +/// Reorder the slice such that the element at `index` is at its final sorted position. +pub fn partition_at_index<T, F>( + v: &mut [T], + index: usize, + mut is_less: F, +) -> (&mut [T], &mut T, &mut [T]) +where + F: FnMut(&T, &T) -> bool, +{ + if index >= v.len() { + panic!("partition_at_index index {} greater than length of slice {}", index, v.len()); + } + + if T::IS_ZST { + // Sorting has no meaningful behavior on zero-sized types. Do nothing. + } else if index == v.len() - 1 { + // Find max element and place it in the last position of the array. We're free to use + // `unwrap()` here because we know v must not be empty. + let max_idx = max_index(v, &mut is_less).unwrap(); + v.swap(max_idx, index); + } else if index == 0 { + // Find min element and place it in the first position of the array. We're free to use + // `unwrap()` here because we know v must not be empty. + let min_idx = min_index(v, &mut is_less).unwrap(); + v.swap(min_idx, index); + } else { + partition_at_index_loop(v, index, &mut is_less, None); + } + + let (left, right) = v.split_at_mut(index); + let (pivot, right) = right.split_at_mut(1); + let pivot = &mut pivot[0]; + (left, pivot, right) +} + +/// Selection algorithm to select the k-th element from the slice in guaranteed O(n) time. +/// This is essentially a quickselect that uses Tukey's Ninther for pivot selection +fn median_of_medians<T, F: FnMut(&T, &T) -> bool>(mut v: &mut [T], is_less: &mut F, mut k: usize) { + // Since this function isn't public, it should never be called with an out-of-bounds index. + debug_assert!(k < v.len()); + + // If T is as ZST, `partition_at_index` will already return early. + debug_assert!(!T::IS_ZST); + + // We now know that `k < v.len() <= isize::MAX` + loop { + if v.len() <= MAX_INSERTION { + if v.len() > 1 { + insertion_sort_shift_left(v, 1, is_less); + } + return; + } + + // `median_of_{minima,maxima}` can't handle the extreme cases of the first/last element, + // so we catch them here and just do a linear search. + if k == v.len() - 1 { + // Find max element and place it in the last position of the array. We're free to use + // `unwrap()` here because we know v must not be empty. + let max_idx = max_index(v, is_less).unwrap(); + v.swap(max_idx, k); + return; + } else if k == 0 { + // Find min element and place it in the first position of the array. We're free to use + // `unwrap()` here because we know v must not be empty. + let min_idx = min_index(v, is_less).unwrap(); + v.swap(min_idx, k); + return; + } + + let p = median_of_ninthers(v, is_less); + + if p == k { + return; + } else if p > k { + v = &mut v[..p]; + } else { + // Since `p < k < v.len()`, `p + 1` doesn't overflow and is + // a valid index into the slice. + v = &mut v[p + 1..]; + k -= p + 1; + } + } +} + +// Optimized for when `k` lies somewhere in the middle of the slice. Selects a pivot +// as close as possible to the median of the slice. For more details on how the algorithm +// operates, refer to the paper <https://drops.dagstuhl.de/opus/volltexte/2017/7612/pdf/LIPIcs-SEA-2017-24.pdf>. +fn median_of_ninthers<T, F: FnMut(&T, &T) -> bool>(v: &mut [T], is_less: &mut F) -> usize { + // use `saturating_mul` so the multiplication doesn't overflow on 16-bit platforms. + let frac = if v.len() <= 1024 { + v.len() / 12 + } else if v.len() <= 128_usize.saturating_mul(1024) { + v.len() / 64 + } else { + v.len() / 1024 + }; + + let pivot = frac / 2; + let lo = v.len() / 2 - pivot; + let hi = frac + lo; + let gap = (v.len() - 9 * frac) / 4; + let mut a = lo - 4 * frac - gap; + let mut b = hi + gap; + for i in lo..hi { + ninther(v, is_less, a, i - frac, b, a + 1, i, b + 1, a + 2, i + frac, b + 2); + a += 3; + b += 3; + } + + median_of_medians(&mut v[lo..lo + frac], is_less, pivot); + partition(v, lo + pivot, is_less).0 +} + +/// Moves around the 9 elements at the indices a..i, such that +/// `v[d]` contains the median of the 9 elements and the other +/// elements are partitioned around it. +fn ninther<T, F: FnMut(&T, &T) -> bool>( + v: &mut [T], + is_less: &mut F, + a: usize, + mut b: usize, + c: usize, + mut d: usize, + e: usize, + mut f: usize, + g: usize, + mut h: usize, + i: usize, +) { + b = median_idx(v, is_less, a, b, c); + h = median_idx(v, is_less, g, h, i); + if is_less(&v[h], &v[b]) { + mem::swap(&mut b, &mut h); + } + if is_less(&v[f], &v[d]) { + mem::swap(&mut d, &mut f); + } + if is_less(&v[e], &v[d]) { + // do nothing + } else if is_less(&v[f], &v[e]) { + d = f; + } else { + if is_less(&v[e], &v[b]) { + v.swap(e, b); + } else if is_less(&v[h], &v[e]) { + v.swap(e, h); + } + return; + } + if is_less(&v[d], &v[b]) { + d = b; + } else if is_less(&v[h], &v[d]) { + d = h; + } + + v.swap(d, e); +} + +/// returns the index pointing to the median of the 3 +/// elements `v[a]`, `v[b]` and `v[c]` +fn median_idx<T, F: FnMut(&T, &T) -> bool>( + v: &[T], + is_less: &mut F, + mut a: usize, + b: usize, + mut c: usize, +) -> usize { + if is_less(&v[c], &v[a]) { + mem::swap(&mut a, &mut c); + } + if is_less(&v[c], &v[b]) { + return c; + } + if is_less(&v[b], &v[a]) { + return a; + } + b +} diff --git a/library/core/src/slice/sort.rs b/library/core/src/slice/sort.rs index e6e3b55efa9..db76d26257a 100644 --- a/library/core/src/slice/sort.rs +++ b/library/core/src/slice/sort.rs @@ -145,7 +145,7 @@ where /// Never inline this function to avoid code bloat. It still optimizes nicely and has practically no /// performance impact. Even improving performance in some cases. #[inline(never)] -fn insertion_sort_shift_left<T, F>(v: &mut [T], offset: usize, is_less: &mut F) +pub(super) fn insertion_sort_shift_left<T, F>(v: &mut [T], offset: usize, is_less: &mut F) where F: FnMut(&T, &T) -> bool, { @@ -557,7 +557,7 @@ where /// /// 1. Number of elements smaller than `v[pivot]`. /// 2. True if `v` was already partitioned. -fn partition<T, F>(v: &mut [T], pivot: usize, is_less: &mut F) -> (usize, bool) +pub(super) fn partition<T, F>(v: &mut [T], pivot: usize, is_less: &mut F) -> (usize, bool) where F: FnMut(&T, &T) -> bool, { @@ -612,7 +612,7 @@ where /// /// Returns the number of elements equal to the pivot. It is assumed that `v` does not contain /// elements smaller than the pivot. -fn partition_equal<T, F>(v: &mut [T], pivot: usize, is_less: &mut F) -> usize +pub(super) fn partition_equal<T, F>(v: &mut [T], pivot: usize, is_less: &mut F) -> usize where F: FnMut(&T, &T) -> bool, { @@ -670,7 +670,7 @@ where /// Scatters some elements around in an attempt to break patterns that might cause imbalanced /// partitions in quicksort. #[cold] -fn break_patterns<T>(v: &mut [T]) { +pub(super) fn break_patterns<T>(v: &mut [T]) { let len = v.len(); if len >= 8 { let mut seed = len; @@ -719,7 +719,7 @@ fn break_patterns<T>(v: &mut [T]) { /// Chooses a pivot in `v` and returns the index and `true` if the slice is likely already sorted. /// /// Elements in `v` might be reordered in the process. -fn choose_pivot<T, F>(v: &mut [T], is_less: &mut F) -> (usize, bool) +pub(super) fn choose_pivot<T, F>(v: &mut [T], is_less: &mut F) -> (usize, bool) where F: FnMut(&T, &T) -> bool, { @@ -897,138 +897,6 @@ where recurse(v, &mut is_less, None, limit); } -fn partition_at_index_loop<'a, T, F>( - mut v: &'a mut [T], - mut index: usize, - is_less: &mut F, - mut pred: Option<&'a T>, -) where - F: FnMut(&T, &T) -> bool, -{ - // Limit the amount of iterations and fall back to heapsort, similarly to `slice::sort_unstable`. - // This lowers the worst case running time from O(n^2) to O(n log n). - // FIXME: Investigate whether it would be better to use something like Median of Medians - // or Fast Deterministic Selection to guarantee O(n) worst case. - let mut limit = usize::BITS - v.len().leading_zeros(); - - // True if the last partitioning was reasonably balanced. - let mut was_balanced = true; - - loop { - let len = v.len(); - - // For slices of up to this length it's probably faster to simply sort them. - const MAX_INSERTION: usize = 10; - if len <= MAX_INSERTION { - if len >= 2 { - insertion_sort_shift_left(v, 1, is_less); - } - return; - } - - if limit == 0 { - heapsort(v, is_less); - return; - } - - // If the last partitioning was imbalanced, try breaking patterns in the slice by shuffling - // some elements around. Hopefully we'll choose a better pivot this time. - if !was_balanced { - break_patterns(v); - limit -= 1; - } - - // Choose a pivot - let (pivot, _) = choose_pivot(v, is_less); - - // If the chosen pivot is equal to the predecessor, then it's the smallest element in the - // slice. Partition the slice into elements equal to and elements greater than the pivot. - // This case is usually hit when the slice contains many duplicate elements. - if let Some(p) = pred { - if !is_less(p, &v[pivot]) { - let mid = partition_equal(v, pivot, is_less); - - // If we've passed our index, then we're good. - if mid > index { - return; - } - - // Otherwise, continue sorting elements greater than the pivot. - v = &mut v[mid..]; - index = index - mid; - pred = None; - continue; - } - } - - let (mid, _) = partition(v, pivot, is_less); - was_balanced = cmp::min(mid, len - mid) >= len / 8; - - // Split the slice into `left`, `pivot`, and `right`. - let (left, right) = v.split_at_mut(mid); - let (pivot, right) = right.split_at_mut(1); - let pivot = &pivot[0]; - - if mid < index { - v = right; - index = index - mid - 1; - pred = Some(pivot); - } else if mid > index { - v = left; - } else { - // If mid == index, then we're done, since partition() guaranteed that all elements - // after mid are greater than or equal to mid. - return; - } - } -} - -/// Reorder the slice such that the element at `index` is at its final sorted position. -pub fn partition_at_index<T, F>( - v: &mut [T], - index: usize, - mut is_less: F, -) -> (&mut [T], &mut T, &mut [T]) -where - F: FnMut(&T, &T) -> bool, -{ - use cmp::Ordering::Greater; - use cmp::Ordering::Less; - - if index >= v.len() { - panic!("partition_at_index index {} greater than length of slice {}", index, v.len()); - } - - if T::IS_ZST { - // Sorting has no meaningful behavior on zero-sized types. Do nothing. - } else if index == v.len() - 1 { - // Find max element and place it in the last position of the array. We're free to use - // `unwrap()` here because we know v must not be empty. - let (max_index, _) = v - .iter() - .enumerate() - .max_by(|&(_, x), &(_, y)| if is_less(x, y) { Less } else { Greater }) - .unwrap(); - v.swap(max_index, index); - } else if index == 0 { - // Find min element and place it in the first position of the array. We're free to use - // `unwrap()` here because we know v must not be empty. - let (min_index, _) = v - .iter() - .enumerate() - .min_by(|&(_, x), &(_, y)| if is_less(x, y) { Less } else { Greater }) - .unwrap(); - v.swap(min_index, index); - } else { - partition_at_index_loop(v, index, &mut is_less, None); - } - - let (left, right) = v.split_at_mut(index); - let (pivot, right) = right.split_at_mut(1); - let pivot = &mut pivot[0]; - (left, pivot, right) -} - /// Merges non-decreasing runs `v[..mid]` and `v[mid..]` using `buf` as temporary storage, and /// stores the result into `v[..]`. /// @@ -1085,12 +953,12 @@ where // SAFETY: left and right must be valid and part of v same for out. unsafe { - let to_copy = if is_less(&*right, &**left) { - get_and_increment(&mut right) - } else { - get_and_increment(left) - }; - ptr::copy_nonoverlapping(to_copy, get_and_increment(out), 1); + let is_l = is_less(&*right, &**left); + let to_copy = if is_l { right } else { *left }; + ptr::copy_nonoverlapping(to_copy, *out, 1); + *out = out.add(1); + right = right.add(is_l as usize); + *left = left.add(!is_l as usize); } } } else { @@ -1113,32 +981,18 @@ where // SAFETY: left and right must be valid and part of v same for out. unsafe { - let to_copy = if is_less(&*right.sub(1), &*left.sub(1)) { - decrement_and_get(left) - } else { - decrement_and_get(right) - }; - ptr::copy_nonoverlapping(to_copy, decrement_and_get(&mut out), 1); + let is_l = is_less(&*right.sub(1), &*left.sub(1)); + *left = left.sub(is_l as usize); + *right = right.sub(!is_l as usize); + let to_copy = if is_l { *left } else { *right }; + out = out.sub(1); + ptr::copy_nonoverlapping(to_copy, out, 1); } } } // Finally, `hole` gets dropped. If the shorter run was not fully consumed, whatever remains of // it will now be copied into the hole in `v`. - unsafe fn get_and_increment<T>(ptr: &mut *mut T) -> *mut T { - let old = *ptr; - - // SAFETY: ptr.add(1) must still be a valid pointer and part of `v`. - *ptr = unsafe { ptr.add(1) }; - old - } - - unsafe fn decrement_and_get<T>(ptr: &mut *mut T) -> *mut T { - // SAFETY: ptr.sub(1) must still be a valid pointer and part of `v`. - *ptr = unsafe { ptr.sub(1) }; - *ptr - } - // When dropped, copies the range `start..end` into `dest..`. struct MergeHole<T> { start: *mut T, diff --git a/library/core/src/task/mod.rs b/library/core/src/task/mod.rs index c5f89b9a2c6..3f0080e3832 100644 --- a/library/core/src/task/mod.rs +++ b/library/core/src/task/mod.rs @@ -13,5 +13,3 @@ pub use self::wake::{Context, RawWaker, RawWakerVTable, Waker}; mod ready; #[stable(feature = "ready_macro", since = "1.64.0")] pub use ready::ready; -#[unstable(feature = "poll_ready", issue = "89780")] -pub use ready::Ready; diff --git a/library/core/src/task/poll.rs b/library/core/src/task/poll.rs index 5283a576d1b..0a0f702f6fb 100644 --- a/library/core/src/task/poll.rs +++ b/library/core/src/task/poll.rs @@ -3,7 +3,6 @@ use crate::convert; use crate::ops::{self, ControlFlow}; use crate::result::Result; -use crate::task::Ready; /// Indicates whether a value is available or if the current task has been /// scheduled to receive a wakeup instead. @@ -95,38 +94,6 @@ impl<T> Poll<T> { pub const fn is_pending(&self) -> bool { !self.is_ready() } - - /// Extracts the successful type of a [`Poll<T>`]. - /// - /// When combined with the `?` operator, this function will - /// propagate any [`Poll::Pending`] values to the caller, and - /// extract the `T` from [`Poll::Ready`]. - /// - /// # Examples - /// - /// ```rust - /// #![feature(poll_ready)] - /// - /// use std::task::{Context, Poll}; - /// use std::future::{self, Future}; - /// use std::pin::Pin; - /// - /// pub fn do_poll(cx: &mut Context<'_>) -> Poll<()> { - /// let mut fut = future::ready(42); - /// let fut = Pin::new(&mut fut); - /// - /// let num = fut.poll(cx).ready()?; - /// # let _ = num; // to silence unused warning - /// // ... use num - /// - /// Poll::Ready(()) - /// } - /// ``` - #[inline] - #[unstable(feature = "poll_ready", issue = "89780")] - pub fn ready(self) -> Ready<T> { - Ready(self) - } } impl<T, E> Poll<Result<T, E>> { diff --git a/library/core/src/task/ready.rs b/library/core/src/task/ready.rs index 8d12625e88d..495d72fd14b 100644 --- a/library/core/src/task/ready.rs +++ b/library/core/src/task/ready.rs @@ -1,8 +1,3 @@ -use core::convert; -use core::fmt; -use core::ops::{ControlFlow, FromResidual, Try}; -use core::task::Poll; - /// Extracts the successful type of a [`Poll<T>`]. /// /// This macro bakes in propagation of [`Pending`] signals by returning early. @@ -60,55 +55,3 @@ pub macro ready($e:expr) { } } } - -/// Extracts the successful type of a [`Poll<T>`]. -/// -/// See [`Poll::ready`] for details. -#[unstable(feature = "poll_ready", issue = "89780")] -pub struct Ready<T>(pub(crate) Poll<T>); - -#[unstable(feature = "poll_ready", issue = "89780")] -impl<T> Try for Ready<T> { - type Output = T; - type Residual = Ready<convert::Infallible>; - - #[inline] - fn from_output(output: Self::Output) -> Self { - Ready(Poll::Ready(output)) - } - - #[inline] - fn branch(self) -> ControlFlow<Self::Residual, Self::Output> { - match self.0 { - Poll::Ready(v) => ControlFlow::Continue(v), - Poll::Pending => ControlFlow::Break(Ready(Poll::Pending)), - } - } -} - -#[unstable(feature = "poll_ready", issue = "89780")] -impl<T> FromResidual for Ready<T> { - #[inline] - fn from_residual(residual: Ready<convert::Infallible>) -> Self { - match residual.0 { - Poll::Pending => Ready(Poll::Pending), - } - } -} - -#[unstable(feature = "poll_ready", issue = "89780")] -impl<T> FromResidual<Ready<convert::Infallible>> for Poll<T> { - #[inline] - fn from_residual(residual: Ready<convert::Infallible>) -> Self { - match residual.0 { - Poll::Pending => Poll::Pending, - } - } -} - -#[unstable(feature = "poll_ready", issue = "89780")] -impl<T> fmt::Debug for Ready<T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("Ready").finish() - } -} diff --git a/library/core/tests/mem.rs b/library/core/tests/mem.rs index 7f033816901..aee9c89b595 100644 --- a/library/core/tests/mem.rs +++ b/library/core/tests/mem.rs @@ -386,6 +386,26 @@ fn offset_of() { // Layout of tuples is unstable assert!(offset_of!((u8, u16), 0) <= size_of::<(u8, u16)>() - 1); assert!(offset_of!((u8, u16), 1) <= size_of::<(u8, u16)>() - 2); + + #[repr(C)] + struct Generic<T> { + x: u8, + y: u32, + z: T + } + + trait Trait {} + + // Ensure that this type of generics works + fn offs_of_z<T>() -> usize { + offset_of!(Generic<T>, z) + } + + assert_eq!(offset_of!(Generic<u8>, z), 8); + assert_eq!(offs_of_z::<u8>(), 8); + + // Ensure that it works with the implicit lifetime in `Box<dyn Trait + '_>`. + assert_eq!(offset_of!(Generic<Box<dyn Trait>>, z), 8); } #[test] diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 1454b002556..824abc72a22 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -16,7 +16,7 @@ panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core" } libc = { version = "0.2.143", default-features = false, features = ['rustc-dep-of-std'] } -compiler_builtins = { version = "0.1.91" } +compiler_builtins = { version = "0.1.92" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } hashbrown = { version = "0.13", default-features = false, features = ['rustc-dep-of-std'] } diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 2a6b1a5ec73..89dfdfafdb1 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -15,6 +15,7 @@ use crate::ffi::OsString; use crate::fmt; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write}; use crate::path::{Path, PathBuf}; +use crate::sealed::Sealed; use crate::sys::fs as fs_imp; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; use crate::time::SystemTime; @@ -1391,6 +1392,16 @@ impl FileTimes { } } +impl AsInnerMut<fs_imp::FileTimes> for FileTimes { + fn as_inner_mut(&mut self) -> &mut fs_imp::FileTimes { + &mut self.0 + } +} + +// For implementing OS extension traits in `std::os` +#[unstable(feature = "file_set_times", issue = "98245")] +impl Sealed for FileTimes {} + impl Permissions { /// Returns `true` if these permissions describe a readonly (unwritable) file. /// diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index a8a0b9f122d..e2480bcbbc7 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -1,7 +1,7 @@ use crate::io::prelude::*; use crate::env; -use crate::fs::{self, File, OpenOptions}; +use crate::fs::{self, File, FileTimes, OpenOptions}; use crate::io::{BorrowedBuf, ErrorKind, SeekFrom}; use crate::mem::MaybeUninit; use crate::path::Path; @@ -9,7 +9,7 @@ use crate::str; use crate::sync::Arc; use crate::sys_common::io::test::{tmpdir, TempDir}; use crate::thread; -use crate::time::{Duration, Instant}; +use crate::time::{Duration, Instant, SystemTime}; use rand::RngCore; @@ -1633,3 +1633,53 @@ fn rename_directory() { assert!(new_path.join("newdir").is_dir()); assert!(new_path.join("newdir/temp.txt").exists()); } + +#[test] +fn test_file_times() { + #[cfg(target_os = "ios")] + use crate::os::ios::fs::FileTimesExt; + #[cfg(target_os = "macos")] + use crate::os::macos::fs::FileTimesExt; + #[cfg(target_os = "watchos")] + use crate::os::watchos::fs::FileTimesExt; + #[cfg(windows)] + use crate::os::windows::fs::FileTimesExt; + + let tmp = tmpdir(); + let file = File::create(tmp.join("foo")).unwrap(); + let mut times = FileTimes::new(); + let accessed = SystemTime::UNIX_EPOCH + Duration::from_secs(12345); + let modified = SystemTime::UNIX_EPOCH + Duration::from_secs(54321); + times = times.set_accessed(accessed).set_modified(modified); + #[cfg(any(windows, target_os = "macos", target_os = "ios", target_os = "watchos"))] + let created = SystemTime::UNIX_EPOCH + Duration::from_secs(32123); + #[cfg(any(windows, target_os = "macos", target_os = "ios", target_os = "watchos"))] + { + times = times.set_created(created); + } + match file.set_times(times) { + // Allow unsupported errors on platforms which don't support setting times. + #[cfg(not(any( + windows, + all( + unix, + not(any( + target_os = "android", + target_os = "redox", + target_os = "espidf", + target_os = "horizon" + )) + ) + )))] + Err(e) if e.kind() == ErrorKind::Unsupported => return, + Err(e) => panic!("error setting file times: {e:?}"), + Ok(_) => {} + } + let metadata = file.metadata().unwrap(); + assert_eq!(metadata.accessed().unwrap(), accessed); + assert_eq!(metadata.modified().unwrap(), modified); + #[cfg(any(windows, target_os = "macos", target_os = "ios", target_os = "watchos"))] + { + assert_eq!(metadata.created().unwrap(), created); + } +} diff --git a/library/std/src/os/ios/fs.rs b/library/std/src/os/ios/fs.rs index 4a4637ce072..6d4d54b7c78 100644 --- a/library/std/src/os/ios/fs.rs +++ b/library/std/src/os/ios/fs.rs @@ -1,7 +1,9 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] -use crate::fs::Metadata; -use crate::sys_common::AsInner; +use crate::fs::{self, Metadata}; +use crate::sealed::Sealed; +use crate::sys_common::{AsInner, AsInnerMut, IntoInner}; +use crate::time::SystemTime; #[allow(deprecated)] use crate::os::ios::raw; @@ -140,3 +142,19 @@ impl MetadataExt for Metadata { self.as_inner().as_inner().st_lspare as u32 } } + +/// OS-specific extensions to [`fs::FileTimes`]. +#[unstable(feature = "file_set_times", issue = "98245")] +pub trait FileTimesExt: Sealed { + /// Set the creation time of a file. + #[unstable(feature = "file_set_times", issue = "98245")] + fn set_created(self, t: SystemTime) -> Self; +} + +#[unstable(feature = "file_set_times", issue = "98245")] +impl FileTimesExt for fs::FileTimes { + fn set_created(mut self, t: SystemTime) -> Self { + self.as_inner_mut().set_created(t.into_inner()); + self + } +} diff --git a/library/std/src/os/macos/fs.rs b/library/std/src/os/macos/fs.rs index 91915da6a43..fe82d03d869 100644 --- a/library/std/src/os/macos/fs.rs +++ b/library/std/src/os/macos/fs.rs @@ -1,7 +1,9 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] -use crate::fs::Metadata; -use crate::sys_common::AsInner; +use crate::fs::{self, Metadata}; +use crate::sealed::Sealed; +use crate::sys_common::{AsInner, AsInnerMut, IntoInner}; +use crate::time::SystemTime; #[allow(deprecated)] use crate::os::macos::raw; @@ -146,3 +148,19 @@ impl MetadataExt for Metadata { [qspare[0] as u64, qspare[1] as u64] } } + +/// OS-specific extensions to [`fs::FileTimes`]. +#[unstable(feature = "file_set_times", issue = "98245")] +pub trait FileTimesExt: Sealed { + /// Set the creation time of a file. + #[unstable(feature = "file_set_times", issue = "98245")] + fn set_created(self, t: SystemTime) -> Self; +} + +#[unstable(feature = "file_set_times", issue = "98245")] +impl FileTimesExt for fs::FileTimes { + fn set_created(mut self, t: SystemTime) -> Self { + self.as_inner_mut().set_created(t.into_inner()); + self + } +} diff --git a/library/std/src/os/watchos/fs.rs b/library/std/src/os/watchos/fs.rs index a14fe35a77c..2ecc4c68a96 100644 --- a/library/std/src/os/watchos/fs.rs +++ b/library/std/src/os/watchos/fs.rs @@ -1,7 +1,9 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] -use crate::fs::Metadata; -use crate::sys_common::AsInner; +use crate::fs::{self, Metadata}; +use crate::sealed::Sealed; +use crate::sys_common::{AsInner, AsInnerMut, IntoInner}; +use crate::time::SystemTime; #[allow(deprecated)] use crate::os::watchos::raw; @@ -140,3 +142,19 @@ impl MetadataExt for Metadata { self.as_inner().as_inner().st_lspare as u32 } } + +/// OS-specific extensions to [`fs::FileTimes`]. +#[unstable(feature = "file_set_times", issue = "98245")] +pub trait FileTimesExt: Sealed { + /// Set the creation time of a file. + #[unstable(feature = "file_set_times", issue = "98245")] + fn set_created(self, t: SystemTime) -> Self; +} + +#[unstable(feature = "file_set_times", issue = "98245")] +impl FileTimesExt for fs::FileTimes { + fn set_created(mut self, t: SystemTime) -> Self { + self.as_inner_mut().set_created(t.into_inner()); + self + } +} diff --git a/library/std/src/os/windows/fs.rs b/library/std/src/os/windows/fs.rs index a091f06dd53..94509e54796 100644 --- a/library/std/src/os/windows/fs.rs +++ b/library/std/src/os/windows/fs.rs @@ -9,7 +9,8 @@ use crate::io; use crate::path::Path; use crate::sealed::Sealed; use crate::sys; -use crate::sys_common::{AsInner, AsInnerMut}; +use crate::sys_common::{AsInner, AsInnerMut, IntoInner}; +use crate::time::SystemTime; /// Windows-specific extensions to [`fs::File`]. #[stable(feature = "file_offset", since = "1.15.0")] @@ -526,6 +527,22 @@ impl FileTypeExt for fs::FileType { } } +/// Windows-specific extensions to [`fs::FileTimes`]. +#[unstable(feature = "file_set_times", issue = "98245")] +pub trait FileTimesExt: Sealed { + /// Set the creation time of a file. + #[unstable(feature = "file_set_times", issue = "98245")] + fn set_created(self, t: SystemTime) -> Self; +} + +#[unstable(feature = "file_set_times", issue = "98245")] +impl FileTimesExt for fs::FileTimes { + fn set_created(mut self, t: SystemTime) -> Self { + self.as_inner_mut().set_created(t.into_inner()); + self + } +} + /// Creates a new symlink to a non-directory file on the filesystem. /// /// The `link` path will be a file symbolic link pointing to the `original` diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index a2ffd8b1e7e..69a6f3e6d5a 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -19,11 +19,11 @@ pub macro panic_2015 { $crate::rt::begin_panic("explicit panic") }), ($msg:expr $(,)?) => ({ - $crate::rt::begin_panic($msg) + $crate::rt::begin_panic($msg); }), // Special-case the single-argument case for const_panic. ("{}", $arg:expr $(,)?) => ({ - $crate::rt::panic_display(&$arg) + $crate::rt::panic_display(&$arg); }), ($fmt:expr, $($arg:tt)+) => ({ // Semicolon to prevent temporaries inside the formatting machinery from diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 43203c5824d..febdeb51463 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -733,8 +733,9 @@ impl<'a> Components<'a> { } } - // parse a given byte sequence into the corresponding path component - fn parse_single_component<'b>(&self, comp: &'b [u8]) -> Option<Component<'b>> { + // parse a given byte sequence following the OsStr encoding into the + // corresponding path component + unsafe fn parse_single_component<'b>(&self, comp: &'b [u8]) -> Option<Component<'b>> { match comp { b"." if self.prefix_verbatim() => Some(Component::CurDir), b"." => None, // . components are normalized away, except at @@ -754,7 +755,8 @@ impl<'a> Components<'a> { None => (0, self.path), Some(i) => (1, &self.path[..i]), }; - (comp.len() + extra, self.parse_single_component(comp)) + // SAFETY: `comp` is a valid substring, since it is split on a separator. + (comp.len() + extra, unsafe { self.parse_single_component(comp) }) } // parse a component from the right, saying how many bytes to consume to @@ -766,7 +768,8 @@ impl<'a> Components<'a> { None => (0, &self.path[start..]), Some(i) => (1, &self.path[start + i + 1..]), }; - (comp.len() + extra, self.parse_single_component(comp)) + // SAFETY: `comp` is a valid substring, since it is split on a separator. + (comp.len() + extra, unsafe { self.parse_single_component(comp) }) } // trim away repeated separators (i.e., empty components) on the left diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index 09db5b11dbf..09e9ae2720f 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -349,6 +349,8 @@ pub struct FilePermissions { pub struct FileTimes { accessed: Option<SystemTime>, modified: Option<SystemTime>, + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] + created: Option<SystemTime>, } #[derive(Copy, Clone, Eq, Debug)] @@ -591,6 +593,11 @@ impl FileTimes { pub fn set_modified(&mut self, t: SystemTime) { self.modified = Some(t); } + + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] + pub fn set_created(&mut self, t: SystemTime) { + self.created = Some(t); + } } impl FileType { @@ -1215,26 +1222,41 @@ impl File { io::ErrorKind::Unsupported, "setting file times not supported", )) - } else if #[cfg(any(target_os = "android", target_os = "macos"))] { + } else if #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] { + let mut buf = [mem::MaybeUninit::<libc::timespec>::uninit(); 3]; + let mut num_times = 0; + let mut attrlist: libc::attrlist = unsafe { mem::zeroed() }; + attrlist.bitmapcount = libc::ATTR_BIT_MAP_COUNT; + if times.created.is_some() { + buf[num_times].write(to_timespec(times.created)?); + num_times += 1; + attrlist.commonattr |= libc::ATTR_CMN_CRTIME; + } + if times.modified.is_some() { + buf[num_times].write(to_timespec(times.modified)?); + num_times += 1; + attrlist.commonattr |= libc::ATTR_CMN_MODTIME; + } + if times.accessed.is_some() { + buf[num_times].write(to_timespec(times.accessed)?); + num_times += 1; + attrlist.commonattr |= libc::ATTR_CMN_ACCTIME; + } + cvt(unsafe { libc::fsetattrlist( + self.as_raw_fd(), + (&attrlist as *const libc::attrlist).cast::<libc::c_void>().cast_mut(), + buf.as_ptr().cast::<libc::c_void>().cast_mut(), + num_times * mem::size_of::<libc::timespec>(), + 0 + ) })?; + Ok(()) + } else if #[cfg(target_os = "android")] { let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?]; - // futimens requires macOS 10.13, and Android API level 19 + // futimens requires Android API level 19 cvt(unsafe { weak!(fn futimens(c_int, *const libc::timespec) -> c_int); match futimens.get() { Some(futimens) => futimens(self.as_raw_fd(), times.as_ptr()), - #[cfg(target_os = "macos")] - None => { - fn ts_to_tv(ts: &libc::timespec) -> libc::timeval { - libc::timeval { - tv_sec: ts.tv_sec, - tv_usec: (ts.tv_nsec / 1000) as _ - } - } - let timevals = [ts_to_tv(×[0]), ts_to_tv(×[1])]; - libc::futimes(self.as_raw_fd(), timevals.as_ptr()) - } - // futimes requires even newer Android. - #[cfg(target_os = "android")] None => return Err(io::const_io_error!( io::ErrorKind::Unsupported, "setting file times requires Android API level >= 19", diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index ce427766d17..21a65bc25f3 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -88,8 +88,10 @@ pub struct FilePermissions { pub struct FileTimes { accessed: Option<c::FILETIME>, modified: Option<c::FILETIME>, + created: Option<c::FILETIME>, } -impl core::fmt::Debug for c::FILETIME { + +impl fmt::Debug for c::FILETIME { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let time = ((self.dwHighDateTime as u64) << 32) | self.dwLowDateTime as u64; f.debug_tuple("FILETIME").field(&time).finish() @@ -582,7 +584,10 @@ impl File { pub fn set_times(&self, times: FileTimes) -> io::Result<()> { let is_zero = |t: c::FILETIME| t.dwLowDateTime == 0 && t.dwHighDateTime == 0; - if times.accessed.map_or(false, is_zero) || times.modified.map_or(false, is_zero) { + if times.accessed.map_or(false, is_zero) + || times.modified.map_or(false, is_zero) + || times.created.map_or(false, is_zero) + { return Err(io::const_io_error!( io::ErrorKind::InvalidInput, "Cannot set file timestamp to 0", @@ -590,18 +595,23 @@ impl File { } let is_max = |t: c::FILETIME| t.dwLowDateTime == c::DWORD::MAX && t.dwHighDateTime == c::DWORD::MAX; - if times.accessed.map_or(false, is_max) || times.modified.map_or(false, is_max) { + if times.accessed.map_or(false, is_max) + || times.modified.map_or(false, is_max) + || times.created.map_or(false, is_max) + { return Err(io::const_io_error!( io::ErrorKind::InvalidInput, "Cannot set file timestamp to 0xFFFF_FFFF_FFFF_FFFF", )); } cvt(unsafe { + let created = + times.created.as_ref().map(|a| a as *const c::FILETIME).unwrap_or(ptr::null()); let accessed = times.accessed.as_ref().map(|a| a as *const c::FILETIME).unwrap_or(ptr::null()); let modified = times.modified.as_ref().map(|a| a as *const c::FILETIME).unwrap_or(ptr::null()); - c::SetFileTime(self.as_raw_handle(), ptr::null_mut(), accessed, modified) + c::SetFileTime(self.as_raw_handle(), created, accessed, modified) })?; Ok(()) } @@ -1005,6 +1015,10 @@ impl FileTimes { pub fn set_modified(&mut self, t: SystemTime) { self.modified = Some(t.into_inner()); } + + pub fn set_created(&mut self, t: SystemTime) { + self.created = Some(t.into_inner()); + } } impl FileType { diff --git a/library/test/src/cli.rs b/library/test/src/cli.rs index 9d22ebbee87..6ac3b3eaa79 100644 --- a/library/test/src/cli.rs +++ b/library/test/src/cli.rs @@ -404,13 +404,13 @@ fn get_format( Some("terse") => OutputFormat::Terse, Some("json") => { if !allow_unstable { - return Err("The \"json\" format is only accepted on the nightly compiler".into()); + return Err("The \"json\" format is only accepted on the nightly compiler with -Z unstable-options".into()); } OutputFormat::Json } Some("junit") => { if !allow_unstable { - return Err("The \"junit\" format is only accepted on the nightly compiler".into()); + return Err("The \"junit\" format is only accepted on the nightly compiler with -Z unstable-options".into()); } OutputFormat::Junit } diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index 1ab5565fdb8..8f8778efee7 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -488,9 +488,9 @@ dependencies = [ [[package]] name = "object" -version = "0.29.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" dependencies = [ "memchr", ] diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index 746c8dcfce0..367c6190967 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -37,7 +37,7 @@ filetime = "0.2" cc = "1.0.69" libc = "0.2" hex = "0.4" -object = { version = "0.29.0", default-features = false, features = ["archive", "coff", "read_core", "unaligned"] } +object = { version = "0.31.1", default-features = false, features = ["archive", "coff", "read_core", "unaligned"] } serde = "1.0.137" # Directly use serde_derive rather than through the derive feature of serde to allow building both # in parallel and to allow serde_json and toml to start building as soon as serde has been built. diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index f22cdad7df4..50ace987193 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -109,7 +109,7 @@ def _download(path, url, probably_big, verbose, exception): "-L", # Follow redirect. "-y", "30", "-Y", "10", # timeout if speed is < 10 bytes/sec for > 30 seconds "--connect-timeout", "30", # timeout if cannot connect within 30 seconds - "--retry", "3", "-Sf", url], + "--retry", "3", "-SRf", url], stdout=outfile, #Implements cli redirect operator '>' verbose=verbose, exception=True, # Will raise RuntimeError on failure diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 5c37fab5470..cf7c6596c02 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -942,7 +942,6 @@ impl<'a> Builder<'a> { self.run_step_descriptions(&Builder::get_step_descriptions(Kind::Doc), paths); } - /// NOTE: keep this in sync with `rustdoc::clean::utils::doc_rust_lang_org_channel`, or tests will fail on beta/stable. pub fn doc_rust_lang_org_channel(&self) -> String { let channel = match &*self.config.channel { "stable" => &self.version, diff --git a/src/bootstrap/builder/tests.rs b/src/bootstrap/builder/tests.rs index c32fe59bbf0..edca8fe9b13 100644 --- a/src/bootstrap/builder/tests.rs +++ b/src/bootstrap/builder/tests.rs @@ -146,6 +146,22 @@ fn alias_and_path_for_library() { ); } +#[test] +fn test_beta_rev_parsing() { + use crate::extract_beta_rev; + + // single digit revision + assert_eq!(extract_beta_rev("1.99.9-beta.7 (xxxxxx)"), Some("7".to_string())); + // multiple digits + assert_eq!(extract_beta_rev("1.99.9-beta.777 (xxxxxx)"), Some("777".to_string())); + // nightly channel (no beta revision) + assert_eq!(extract_beta_rev("1.99.9-nightly (xxxxxx)"), None); + // stable channel (no beta revision) + assert_eq!(extract_beta_rev("1.99.9 (xxxxxxx)"), None); + // invalid string + assert_eq!(extract_beta_rev("invalid"), None); +} + mod defaults { use super::{configure, first, run_build}; use crate::builder::*; diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 710c8b52194..e192cda9a9a 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -350,7 +350,7 @@ impl SplitDebuginfo { } /// LTO mode used for compiling rustc itself. -#[derive(Default, Clone, PartialEq)] +#[derive(Default, Clone, PartialEq, Debug)] pub enum RustcLto { Off, #[default] @@ -508,29 +508,42 @@ struct TomlConfig { profile: Option<String>, } +/// Describes how to handle conflicts in merging two [`TomlConfig`] +#[derive(Copy, Clone, Debug)] +enum ReplaceOpt { + /// Silently ignore a duplicated value + IgnoreDuplicate, + /// Override the current value, even if it's `Some` + Override, + /// Exit with an error on duplicate values + ErrorOnDuplicate, +} + trait Merge { - fn merge(&mut self, other: Self); + fn merge(&mut self, other: Self, replace: ReplaceOpt); } impl Merge for TomlConfig { fn merge( &mut self, - TomlConfig { build, install, llvm, rust, dist, target, profile: _, changelog_seen: _ }: Self, + TomlConfig { build, install, llvm, rust, dist, target, profile: _, changelog_seen }: Self, + replace: ReplaceOpt, ) { - fn do_merge<T: Merge>(x: &mut Option<T>, y: Option<T>) { + fn do_merge<T: Merge>(x: &mut Option<T>, y: Option<T>, replace: ReplaceOpt) { if let Some(new) = y { if let Some(original) = x { - original.merge(new); + original.merge(new, replace); } else { *x = Some(new); } } } - do_merge(&mut self.build, build); - do_merge(&mut self.install, install); - do_merge(&mut self.llvm, llvm); - do_merge(&mut self.rust, rust); - do_merge(&mut self.dist, dist); + self.changelog_seen.merge(changelog_seen, replace); + do_merge(&mut self.build, build, replace); + do_merge(&mut self.install, install, replace); + do_merge(&mut self.llvm, llvm, replace); + do_merge(&mut self.rust, rust, replace); + do_merge(&mut self.dist, dist, replace); assert!(target.is_none(), "merging target-specific config is not currently supported"); } } @@ -547,10 +560,33 @@ macro_rules! define_config { } impl Merge for $name { - fn merge(&mut self, other: Self) { + fn merge(&mut self, other: Self, replace: ReplaceOpt) { $( - if !self.$field.is_some() { - self.$field = other.$field; + match replace { + ReplaceOpt::IgnoreDuplicate => { + if self.$field.is_none() { + self.$field = other.$field; + } + }, + ReplaceOpt::Override => { + if other.$field.is_some() { + self.$field = other.$field; + } + } + ReplaceOpt::ErrorOnDuplicate => { + if other.$field.is_some() { + if self.$field.is_some() { + if cfg!(test) { + panic!("overriding existing option") + } else { + eprintln!("overriding existing option: `{}`", stringify!($field)); + crate::detail_exit(2); + } + } else { + self.$field = other.$field; + } + } + } } )* } @@ -623,6 +659,37 @@ macro_rules! define_config { } } +impl<T> Merge for Option<T> { + fn merge(&mut self, other: Self, replace: ReplaceOpt) { + match replace { + ReplaceOpt::IgnoreDuplicate => { + if self.is_none() { + *self = other; + } + } + ReplaceOpt::Override => { + if other.is_some() { + *self = other; + } + } + ReplaceOpt::ErrorOnDuplicate => { + if other.is_some() { + if self.is_some() { + if cfg!(test) { + panic!("overriding existing option") + } else { + eprintln!("overriding existing option"); + crate::detail_exit(2); + } + } else { + *self = other; + } + } + } + } + } +} + define_config! { /// TOML representation of various global build decisions. #[derive(Default)] @@ -864,28 +931,27 @@ impl Config { pub fn parse(args: &[String]) -> Config { #[cfg(test)] - let get_toml = |_: &_| TomlConfig::default(); + fn get_toml(_: &Path) -> TomlConfig { + TomlConfig::default() + } + #[cfg(not(test))] - let get_toml = |file: &Path| { + fn get_toml(file: &Path) -> TomlConfig { let contents = t!(fs::read_to_string(file), format!("config file {} not found", file.display())); // Deserialize to Value and then TomlConfig to prevent the Deserialize impl of // TomlConfig and sub types to be monomorphized 5x by toml. - match toml::from_str(&contents) + toml::from_str(&contents) .and_then(|table: toml::Value| TomlConfig::deserialize(table)) - { - Ok(table) => table, - Err(err) => { - eprintln!("failed to parse TOML configuration '{}': {}", file.display(), err); + .unwrap_or_else(|err| { + eprintln!("failed to parse TOML configuration '{}': {err}", file.display()); crate::detail_exit(2); - } - } - }; - + }) + } Self::parse_inner(args, get_toml) } - fn parse_inner<'a>(args: &[String], get_toml: impl 'a + Fn(&Path) -> TomlConfig) -> Config { + fn parse_inner(args: &[String], get_toml: impl Fn(&Path) -> TomlConfig) -> Config { let mut flags = Flags::parse(&args); let mut config = Config::default_opts(); @@ -998,8 +1064,40 @@ impl Config { include_path.push("defaults"); include_path.push(format!("config.{}.toml", include)); let included_toml = get_toml(&include_path); - toml.merge(included_toml); + toml.merge(included_toml, ReplaceOpt::IgnoreDuplicate); + } + + let mut override_toml = TomlConfig::default(); + for option in flags.set.iter() { + fn get_table(option: &str) -> Result<TomlConfig, toml::de::Error> { + toml::from_str(&option) + .and_then(|table: toml::Value| TomlConfig::deserialize(table)) + } + + let mut err = match get_table(option) { + Ok(v) => { + override_toml.merge(v, ReplaceOpt::ErrorOnDuplicate); + continue; + } + Err(e) => e, + }; + // We want to be able to set string values without quotes, + // like in `configure.py`. Try adding quotes around the right hand side + if let Some((key, value)) = option.split_once("=") { + if !value.contains('"') { + match get_table(&format!(r#"{key}="{value}""#)) { + Ok(v) => { + override_toml.merge(v, ReplaceOpt::ErrorOnDuplicate); + continue; + } + Err(e) => err = e, + } + } + } + eprintln!("failed to parse override `{option}`: `{err}"); + crate::detail_exit(2) } + toml.merge(override_toml, ReplaceOpt::Override); config.changelog_seen = toml.changelog_seen; diff --git a/src/bootstrap/config/tests.rs b/src/bootstrap/config/tests.rs index d913ca295e2..4de84b543ed 100644 --- a/src/bootstrap/config/tests.rs +++ b/src/bootstrap/config/tests.rs @@ -1,13 +1,11 @@ -use super::{Config, Flags, TomlConfig}; +use super::{Config, Flags}; use clap::CommandFactory; use std::{env, path::Path}; -fn toml(config: &str) -> impl '_ + Fn(&Path) -> TomlConfig { - |&_| toml::from_str(config).unwrap() -} - fn parse(config: &str) -> Config { - Config::parse_inner(&["check".to_owned(), "--config=/does/not/exist".to_owned()], toml(config)) + Config::parse_inner(&["check".to_owned(), "--config=/does/not/exist".to_owned()], |&_| { + toml::from_str(config).unwrap() + }) } #[test] @@ -94,3 +92,70 @@ fn detect_src_and_out() { fn clap_verify() { Flags::command().debug_assert(); } + +#[test] +fn override_toml() { + let config = Config::parse_inner( + &[ + "check".to_owned(), + "--config=/does/not/exist".to_owned(), + "--set=changelog-seen=1".to_owned(), + "--set=rust.lto=fat".to_owned(), + "--set=rust.deny-warnings=false".to_owned(), + "--set=build.gdb=\"bar\"".to_owned(), + "--set=build.tools=[\"cargo\"]".to_owned(), + "--set=llvm.build-config={\"foo\" = \"bar\"}".to_owned(), + ], + |&_| { + toml::from_str( + r#" +changelog-seen = 0 +[rust] +lto = "off" +deny-warnings = true + +[build] +gdb = "foo" +tools = [] + +[llvm] +download-ci-llvm = false +build-config = {} + "#, + ) + .unwrap() + }, + ); + assert_eq!(config.changelog_seen, Some(1), "setting top-level value"); + assert_eq!( + config.rust_lto, + crate::config::RustcLto::Fat, + "setting string value without quotes" + ); + assert_eq!(config.gdb, Some("bar".into()), "setting string value with quotes"); + assert_eq!(config.deny_warnings, false, "setting boolean value"); + assert_eq!( + config.tools, + Some(["cargo".to_string()].into_iter().collect()), + "setting list value" + ); + assert_eq!( + config.llvm_build_config, + [("foo".to_string(), "bar".to_string())].into_iter().collect(), + "setting dictionary value" + ); +} + +#[test] +#[should_panic] +fn override_toml_duplicate() { + Config::parse_inner( + &[ + "check".to_owned(), + "--config=/does/not/exist".to_owned(), + "--set=changelog-seen=1".to_owned(), + "--set=changelog-seen=2".to_owned(), + ], + |&_| toml::from_str("changelog-seen = 0").unwrap(), + ); +} diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 9cead7adc8c..b49845386da 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -1071,7 +1071,11 @@ impl Step for Cargo { tarball.add_file(&cargo, "bin", 0o755); tarball.add_file(etc.join("_cargo"), "share/zsh/site-functions", 0o644); - tarball.add_renamed_file(etc.join("cargo.bashcomp.sh"), "etc/bash_completion.d", "cargo"); + tarball.add_renamed_file( + etc.join("cargo.bashcomp.sh"), + "src/etc/bash_completion.d", + "cargo", + ); tarball.add_dir(etc.join("man"), "share/man/man1"); tarball.add_legal_and_readme_to("share/doc/cargo"); diff --git a/src/bootstrap/download.rs b/src/bootstrap/download.rs index 3e82a381a1b..25df5b2573b 100644 --- a/src/bootstrap/download.rs +++ b/src/bootstrap/download.rs @@ -219,7 +219,7 @@ impl Config { "30", // timeout if cannot connect within 30 seconds "--retry", "3", - "-Sf", + "-SRf", ]); curl.arg(url); let f = File::create(tempfile).unwrap(); diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index d8b298b59a3..80e71577798 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -158,6 +158,9 @@ pub struct Flags { #[arg(global(true))] /// paths for the subcommand pub paths: Vec<PathBuf>, + /// override options in config.toml + #[arg(global(true), value_hint = clap::ValueHint::Other, long, value_name = "section.option=value")] + pub set: Vec<String>, /// arguments passed to subcommands #[arg(global(true), last(true), value_name = "ARGS")] pub free_args: Vec<String>, diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 3756976dee0..6ee50ee6573 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -1324,7 +1324,7 @@ impl Build { match &self.config.channel[..] { "stable" => num.to_string(), "beta" => { - if self.rust_info().is_managed_git_subrepository() && !self.config.omit_git_hash { + if !self.config.omit_git_hash { format!("{}-beta.{}", num, self.beta_prerelease_version()) } else { format!("{}-beta", num) @@ -1336,18 +1336,28 @@ impl Build { } fn beta_prerelease_version(&self) -> u32 { + fn extract_beta_rev_from_file<P: AsRef<Path>>(version_file: P) -> Option<String> { + let version = fs::read_to_string(version_file).ok()?; + + extract_beta_rev(&version) + } + if let Some(s) = self.prerelease_version.get() { return s; } - // Figure out how many merge commits happened since we branched off master. - // That's our beta number! - // (Note that we use a `..` range, not the `...` symmetric difference.) - let count = + // First check if there is a version file available. + // If available, we read the beta revision from that file. + // This only happens when building from a source tarball when Git should not be used. + let count = extract_beta_rev_from_file(self.src.join("version")).unwrap_or_else(|| { + // Figure out how many merge commits happened since we branched off master. + // That's our beta number! + // (Note that we use a `..` range, not the `...` symmetric difference.) output(self.config.git().arg("rev-list").arg("--count").arg("--merges").arg(format!( "refs/remotes/origin/{}..HEAD", self.config.stage0_metadata.config.nightly_branch - ))); + ))) + }); let n = count.trim().parse().unwrap(); self.prerelease_version.set(Some(n)); n @@ -1707,6 +1717,17 @@ to download LLVM rather than building it. } } +/// Extract the beta revision from the full version string. +/// +/// The full version string looks like "a.b.c-beta.y". And we need to extract +/// the "y" part from the string. +pub fn extract_beta_rev(version: &str) -> Option<String> { + let parts = version.splitn(2, "-beta.").collect::<Vec<_>>(); + let count = parts.get(1).and_then(|s| s.find(' ').map(|p| (&s[..p]).to_string())); + + count +} + #[cfg(unix)] fn chmod(path: &Path, perms: u32) { use std::os::unix::fs::*; diff --git a/src/bootstrap/llvm.rs b/src/bootstrap/llvm.rs index 67cb8837391..040a12f5d10 100644 --- a/src/bootstrap/llvm.rs +++ b/src/bootstrap/llvm.rs @@ -185,6 +185,7 @@ pub(crate) fn is_ci_llvm_available(config: &Config, asserts: bool) -> bool { ("arm-unknown-linux-gnueabi", false), ("arm-unknown-linux-gnueabihf", false), ("armv7-unknown-linux-gnueabihf", false), + ("loongarch64-unknown-linux-gnu", false), ("mips-unknown-linux-gnu", false), ("mips64-unknown-linux-gnuabi64", false), ("mips64el-unknown-linux-gnuabi64", false), diff --git a/src/bootstrap/render_tests.rs b/src/bootstrap/render_tests.rs index bedf34d89e8..fa0a4806618 100644 --- a/src/bootstrap/render_tests.rs +++ b/src/bootstrap/render_tests.rs @@ -168,9 +168,14 @@ impl<'a> Renderer<'a> { if !self.failures.is_empty() { println!("\nfailures:\n"); for failure in &self.failures { - if let Some(stdout) = &failure.stdout { + if failure.stdout.is_some() || failure.message.is_some() { println!("---- {} stdout ----", failure.name); - println!("{stdout}"); + if let Some(stdout) = &failure.stdout { + println!("{stdout}"); + } + if let Some(message) = &failure.message { + println!("note: {message}"); + } } } diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 2d600704e02..2b72d6c48eb 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -620,6 +620,8 @@ impl Step for Miri { cargo.env("MIRIFLAGS", "-O -Zmir-opt-level=4 -Cdebug-assertions=yes"); // Optimizations can change backtraces cargo.env("MIRI_SKIP_UI_CHECKS", "1"); + // `MIRI_SKIP_UI_CHECKS` and `MIRI_BLESS` are incompatible + cargo.env_remove("MIRI_BLESS"); // Optimizations can change error locations and remove UB so don't run `fail` tests. cargo.args(&["tests/pass", "tests/panic"]); diff --git a/src/ci/docker/README.md b/src/ci/docker/README.md index 5898a21d613..e799d7c9688 100644 --- a/src/ci/docker/README.md +++ b/src/ci/docker/README.md @@ -257,6 +257,22 @@ For targets: `i586-unknown-linux-gnu` (\*) Compressed debug is enabled by default for gas (assembly) on Linux/x86 targets, but that makes our `compiler_builtins` incompatible with binutils < 2.32. +### `loongarch64-linux-gnu.defconfig` + +For targets: `loongarch64-unknown-linux-gnu` + +- Path and misc options > Prefix directory = /x-tools/${CT\_TARGET} +- Path and misc options > Use a mirror = ENABLE +- Path and misc options > Base URL = https://ci-mirrors.rust-lang.org/rustc +- Target options > Target Architecture = loongarch +- Target options > Bitness = 64-bit +- Operating System > Target OS = linux +- Operating System > Linux kernel version = 5.19.16 +- Binary utilities > Version of binutils = 2.40 +- C-library > glibc version = 2.36 +- C compiler > gcc version = 12.2.0 +- C compiler > C++ = ENABLE -- to cross compile LLVM + ### `mips-linux-gnu.defconfig` For targets: `mips-unknown-linux-gnu` diff --git a/src/ci/docker/host-x86_64/dist-arm-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-arm-linux/Dockerfile index 3ac5343aa57..420c42bc9d8 100644 --- a/src/ci/docker/host-x86_64/dist-arm-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-arm-linux/Dockerfile @@ -33,6 +33,11 @@ ENV CC_arm_unknown_linux_gnueabi=arm-unknown-linux-gnueabi-gcc \ ENV HOSTS=arm-unknown-linux-gnueabi,aarch64-unknown-linux-musl -ENV RUST_CONFIGURE_ARGS --enable-full-tools --disable-docs --musl-root-aarch64=/usr/local/aarch64-linux-musl \ - --set target.aarch64-unknown-linux-musl.crt-static=false +ENV RUST_CONFIGURE_ARGS \ + --enable-full-tools \ + --disable-docs \ + --musl-root-aarch64=/usr/local/aarch64-linux-musl \ + --enable-sanitizers \ + --enable-profiler \ + --set target.aarch64-unknown-linux-musl.crt-static=false ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile new file mode 100644 index 00000000000..78689c429c2 --- /dev/null +++ b/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile @@ -0,0 +1,30 @@ +FROM ubuntu:22.04 + +COPY scripts/cross-apt-packages.sh /scripts/ +RUN sh /scripts/cross-apt-packages.sh + +# The latest released version does not support LoongArch. +COPY scripts/crosstool-ng-git.sh /scripts/ +RUN sh /scripts/crosstool-ng-git.sh + +COPY scripts/rustbuild-setup.sh /scripts/ +RUN sh /scripts/rustbuild-setup.sh +WORKDIR /tmp + +COPY scripts/crosstool-ng-build.sh /scripts/ +COPY host-x86_64/dist-loongarch64-linux/loongarch64-unknown-linux-gnu.defconfig /tmp/crosstool.defconfig +RUN /scripts/crosstool-ng-build.sh + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +ENV PATH=$PATH:/x-tools/loongarch64-unknown-linux-gnu/bin + +ENV CC_loongarch64_unknown_linux_gnu=loongarch64-unknown-linux-gnu-gcc \ + AR_loongarch64_unknown_linux_gnu=loongarch64-unknown-linux-gnu-ar \ + CXX_loongarch64_unknown_linux_gnu=loongarch64-unknown-linux-gnu-g++ + +ENV HOSTS=loongarch64-unknown-linux-gnu + +ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs +ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/host-x86_64/dist-loongarch64-linux/loongarch64-unknown-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-loongarch64-linux/loongarch64-unknown-linux-gnu.defconfig new file mode 100644 index 00000000000..576f3631cd5 --- /dev/null +++ b/src/ci/docker/host-x86_64/dist-loongarch64-linux/loongarch64-unknown-linux-gnu.defconfig @@ -0,0 +1,14 @@ +CT_CONFIG_VERSION="4" +CT_EXPERIMENTAL=y +CT_PREFIX_DIR="/x-tools/${CT_TARGET}" +CT_USE_MIRROR=y +CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" +CT_ARCH_LOONGARCH=y +# CT_DEMULTILIB is not set +CT_ARCH_USE_MMU=y +CT_ARCH_ARCH="loongarch64" +CT_KERNEL_LINUX=y +CT_LINUX_V_5_19=y +CT_GLIBC_V_2_36=y +CT_CC_GCC_ENABLE_DEFAULT_PIE=y +CT_CC_LANG_CXX=y diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version index 6b4966ddeb4..806935b827f 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version @@ -1 +1 @@ -0.16.3 \ No newline at end of file +0.16.4 \ No newline at end of file diff --git a/src/ci/docker/scripts/cross-apt-packages.sh b/src/ci/docker/scripts/cross-apt-packages.sh index 2f8bf119424..398362ca52f 100644 --- a/src/ci/docker/scripts/cross-apt-packages.sh +++ b/src/ci/docker/scripts/cross-apt-packages.sh @@ -22,6 +22,7 @@ apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install patch \ pkg-config \ python3 \ + rsync \ sudo \ texinfo \ unzip \ diff --git a/src/ci/docker/scripts/crosstool-ng-git.sh b/src/ci/docker/scripts/crosstool-ng-git.sh new file mode 100644 index 00000000000..449cc476f9a --- /dev/null +++ b/src/ci/docker/scripts/crosstool-ng-git.sh @@ -0,0 +1,17 @@ +#!/bin/sh +set -ex + +URL=https://github.com/crosstool-ng/crosstool-ng +REV=943364711a650d9b9e84c1b42c91cc0265b6ab5c + +mkdir crosstool-ng +cd crosstool-ng +git init +git fetch --depth=1 ${URL} ${REV} +git reset --hard FETCH_HEAD +./bootstrap +./configure --prefix=/usr/local +make -j$(nproc) +make install +cd .. +rm -rf crosstool-ng diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml index f81e740936b..fd619467fc1 100644 --- a/src/ci/github-actions/ci.yml +++ b/src/ci/github-actions/ci.yml @@ -359,6 +359,9 @@ jobs: - name: dist-i686-linux <<: *job-linux-8c + - name: dist-loongarch64-linux + <<: *job-linux-8c + - name: dist-mips-linux <<: *job-linux-8c diff --git a/src/doc/embedded-book b/src/doc/embedded-book -Subproject d9eb4c3f75435b008881062ffa77bf0d1527b37 +Subproject f2aed2fe8e9f55508c86ba3aa4b6789b18a08a2 diff --git a/src/doc/nomicon b/src/doc/nomicon -Subproject b5f7500fc40775096c2bbd204eae096612cf904 +Subproject b5f018fb5930cb733b0a8aaf2eed975d4771e74 diff --git a/src/doc/reference b/src/doc/reference -Subproject 28dc0f3576b55f5e57c5d6e65cd68ba3161e9fd +Subproject 553d99b02a53b4133a40d5bd2e19958c67487c0 diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide -Subproject 28dbeaf5c44bc7f5111ad412e99f2d7c5cec6c9 +Subproject f1e637883fafeb83bdd5906ee7f467e4d35b733 diff --git a/src/doc/rustc/src/exploit-mitigations.md b/src/doc/rustc/src/exploit-mitigations.md index 98b49e07171..a82a53248d4 100644 --- a/src/doc/rustc/src/exploit-mitigations.md +++ b/src/doc/rustc/src/exploit-mitigations.md @@ -115,9 +115,9 @@ equivalent. <tr> <td>Stack smashing protection </td> - <td>No + <td>Yes </td> - <td> + <td>Nightly </td> </tr> <tr> @@ -432,18 +432,16 @@ saved return instruction pointer, and checking if this value has changed when returning from a function. This is also known as “Stack Protector” or “Stack Smashing Protector (SSP)”. -The Rust compiler does not support stack smashing protection. However, more -comprehensive alternatives to stack smashing protection exist, such as -shadow and safe stack (see backward-edge control flow protection). +The Rust compiler supports stack smashing protection on nightly builds[42].  Fig. 14. IDA Pro listing cross references to `__stack_chk_fail` in hello-rust. To check if stack smashing protection is enabled for a given binary, search -for cross references to `__stack_chk_fail`. The only cross references to -`__stack_chk_fail` in hello-rust are from the statically-linked libbacktrace -library (see Fig. 14). +for cross references to `__stack_chk_fail`. The presence of these +cross-references in Rust-compiled code (e.g., `hello_rust::main`) indicates +that the stack smashing protection is enabled (see Fig. 14). ### Forward-edge control flow protection @@ -697,3 +695,6 @@ defaults (unrelated to `READ_IMPLIES_EXEC`). 41. “ControlFlowIntegrity.” The Rust Unstable Book. [https://doc.rust-lang.org/unstable-book/compiler-flags/sanitizer.html#controlflowintegrity](../unstable-book/compiler-flags/sanitizer.html#controlflowintegrity). + +42. bbjornse. “add codegen option for using LLVM stack smash protection #84197.” + GitHub. <https://github.com/rust-lang/rust/pull/84197> diff --git a/src/doc/rustc/src/images/image3.png b/src/doc/rustc/src/images/image3.png index a49e14b5ed2..ef02c605ead 100644 --- a/src/doc/rustc/src/images/image3.png +++ b/src/doc/rustc/src/images/image3.png Binary files differdiff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index d22e1cf7f68..3b2463aa5b2 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -88,6 +88,7 @@ target | notes `arm-unknown-linux-gnueabi` | ARMv6 Linux (kernel 3.2, glibc 2.17) `arm-unknown-linux-gnueabihf` | ARMv6 Linux, hardfloat (kernel 3.2, glibc 2.17) `armv7-unknown-linux-gnueabihf` | ARMv7 Linux, hardfloat (kernel 3.2, glibc 2.17) +[`loongarch64-unknown-linux-gnu`](platform-support/loongarch-linux.md) | LoongArch64 Linux, LP64D ABI (kernel 5.19, glibc 2.36) `mips-unknown-linux-gnu` | MIPS Linux (kernel 4.4, glibc 2.23) `mips64-unknown-linux-gnuabi64` | MIPS64 Linux, n64 ABI (kernel 4.4, glibc 2.23) `mips64el-unknown-linux-gnuabi64` | MIPS64 (LE) Linux, n64 ABI (kernel 4.4, glibc 2.23) @@ -266,7 +267,6 @@ target | std | host | notes `i686-uwp-windows-gnu` | ? | | `i686-uwp-windows-msvc` | ? | | `i686-wrs-vxworks` | ? | | -[`loongarch64-unknown-linux-gnu`](platform-support/loongarch-linux.md) | ? | | LoongArch64 Linux (LP64D ABI) [`m68k-unknown-linux-gnu`](platform-support/m68k-unknown-linux-gnu.md) | ? | | Motorola 680x0 Linux `mips-unknown-linux-uclibc` | ✓ | | MIPS Linux with uClibc [`mips64-openwrt-linux-musl`](platform-support/mips64-openwrt-linux-musl.md) | ? | | MIPS64 for OpenWrt Linux MUSL diff --git a/src/doc/rustc/src/platform-support/loongarch-linux.md b/src/doc/rustc/src/platform-support/loongarch-linux.md index 16ccaf46b2f..999e71f8028 100644 --- a/src/doc/rustc/src/platform-support/loongarch-linux.md +++ b/src/doc/rustc/src/platform-support/loongarch-linux.md @@ -1,6 +1,6 @@ # loongarch\*-unknown-linux-\* -**Tier: 3** +**Tier: 2** [LoongArch] is a new RISC ISA developed by Loongson Technology Corporation Limited. diff --git a/src/doc/rustdoc/src/how-to-write-documentation.md b/src/doc/rustdoc/src/how-to-write-documentation.md index 38fd1db5c21..1fa9f814476 100644 --- a/src/doc/rustdoc/src/how-to-write-documentation.md +++ b/src/doc/rustdoc/src/how-to-write-documentation.md @@ -165,15 +165,15 @@ extensions: ### Strikethrough Text may be rendered with a horizontal line through the center by wrapping the -text with two tilde characters on each side: +text with one or two tilde characters on each side: ```text -An example of ~~strikethrough text~~. +An example of ~~strikethrough text~~. You can also use ~single tildes~. ``` This example will render as: -> An example of ~~strikethrough text~~. +> An example of ~~strikethrough text~~. You can also use ~single tildes~. This follows the [GitHub Strikethrough extension][strikethrough]. diff --git a/src/etc/completions/x.py.fish b/src/etc/completions/x.py.fish index 4eddd5cedf1..9f65f1eeeb7 100644 --- a/src/etc/completions/x.py.fish +++ b/src/etc/completions/x.py.fish @@ -19,6 +19,7 @@ complete -c x.py -n "__fish_use_subcommand" -l rust-profile-generate -d 'generat complete -c x.py -n "__fish_use_subcommand" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F complete -c x.py -n "__fish_use_subcommand" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F complete -c x.py -n "__fish_use_subcommand" -l llvm-bolt-profile-use -d 'use BOLT profile for LLVM build' -r -F +complete -c x.py -n "__fish_use_subcommand" -l set -d 'override options in config.toml' -r -f complete -c x.py -n "__fish_use_subcommand" -s v -l verbose -d 'use verbose output (-vv for very verbose)' complete -c x.py -n "__fish_use_subcommand" -s i -l incremental -d 'use incremental compilation' complete -c x.py -n "__fish_use_subcommand" -l include-default-paths -d 'include default paths in addition to the provided ones' @@ -62,6 +63,7 @@ complete -c x.py -n "__fish_seen_subcommand_from build" -l rust-profile-generate complete -c x.py -n "__fish_seen_subcommand_from build" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F complete -c x.py -n "__fish_seen_subcommand_from build" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F complete -c x.py -n "__fish_seen_subcommand_from build" -l llvm-bolt-profile-use -d 'use BOLT profile for LLVM build' -r -F +complete -c x.py -n "__fish_seen_subcommand_from build" -l set -d 'override options in config.toml' -r -f complete -c x.py -n "__fish_seen_subcommand_from build" -s v -l verbose -d 'use verbose output (-vv for very verbose)' complete -c x.py -n "__fish_seen_subcommand_from build" -s i -l incremental -d 'use incremental compilation' complete -c x.py -n "__fish_seen_subcommand_from build" -l include-default-paths -d 'include default paths in addition to the provided ones' @@ -91,6 +93,7 @@ complete -c x.py -n "__fish_seen_subcommand_from check" -l rust-profile-generate complete -c x.py -n "__fish_seen_subcommand_from check" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F complete -c x.py -n "__fish_seen_subcommand_from check" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F complete -c x.py -n "__fish_seen_subcommand_from check" -l llvm-bolt-profile-use -d 'use BOLT profile for LLVM build' -r -F +complete -c x.py -n "__fish_seen_subcommand_from check" -l set -d 'override options in config.toml' -r -f complete -c x.py -n "__fish_seen_subcommand_from check" -l all-targets -d 'Check all targets' complete -c x.py -n "__fish_seen_subcommand_from check" -s v -l verbose -d 'use verbose output (-vv for very verbose)' complete -c x.py -n "__fish_seen_subcommand_from check" -s i -l incremental -d 'use incremental compilation' @@ -125,6 +128,7 @@ complete -c x.py -n "__fish_seen_subcommand_from clippy" -l rust-profile-generat complete -c x.py -n "__fish_seen_subcommand_from clippy" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F complete -c x.py -n "__fish_seen_subcommand_from clippy" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F complete -c x.py -n "__fish_seen_subcommand_from clippy" -l llvm-bolt-profile-use -d 'use BOLT profile for LLVM build' -r -F +complete -c x.py -n "__fish_seen_subcommand_from clippy" -l set -d 'override options in config.toml' -r -f complete -c x.py -n "__fish_seen_subcommand_from clippy" -l fix complete -c x.py -n "__fish_seen_subcommand_from clippy" -s v -l verbose -d 'use verbose output (-vv for very verbose)' complete -c x.py -n "__fish_seen_subcommand_from clippy" -s i -l incremental -d 'use incremental compilation' @@ -155,6 +159,7 @@ complete -c x.py -n "__fish_seen_subcommand_from fix" -l rust-profile-generate - complete -c x.py -n "__fish_seen_subcommand_from fix" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F complete -c x.py -n "__fish_seen_subcommand_from fix" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F complete -c x.py -n "__fish_seen_subcommand_from fix" -l llvm-bolt-profile-use -d 'use BOLT profile for LLVM build' -r -F +complete -c x.py -n "__fish_seen_subcommand_from fix" -l set -d 'override options in config.toml' -r -f complete -c x.py -n "__fish_seen_subcommand_from fix" -s v -l verbose -d 'use verbose output (-vv for very verbose)' complete -c x.py -n "__fish_seen_subcommand_from fix" -s i -l incremental -d 'use incremental compilation' complete -c x.py -n "__fish_seen_subcommand_from fix" -l include-default-paths -d 'include default paths in addition to the provided ones' @@ -184,6 +189,7 @@ complete -c x.py -n "__fish_seen_subcommand_from fmt" -l rust-profile-generate - complete -c x.py -n "__fish_seen_subcommand_from fmt" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F complete -c x.py -n "__fish_seen_subcommand_from fmt" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F complete -c x.py -n "__fish_seen_subcommand_from fmt" -l llvm-bolt-profile-use -d 'use BOLT profile for LLVM build' -r -F +complete -c x.py -n "__fish_seen_subcommand_from fmt" -l set -d 'override options in config.toml' -r -f complete -c x.py -n "__fish_seen_subcommand_from fmt" -l check -d 'check formatting instead of applying' complete -c x.py -n "__fish_seen_subcommand_from fmt" -s v -l verbose -d 'use verbose output (-vv for very verbose)' complete -c x.py -n "__fish_seen_subcommand_from fmt" -s i -l incremental -d 'use incremental compilation' @@ -214,6 +220,7 @@ complete -c x.py -n "__fish_seen_subcommand_from doc" -l rust-profile-generate - complete -c x.py -n "__fish_seen_subcommand_from doc" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F complete -c x.py -n "__fish_seen_subcommand_from doc" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F complete -c x.py -n "__fish_seen_subcommand_from doc" -l llvm-bolt-profile-use -d 'use BOLT profile for LLVM build' -r -F +complete -c x.py -n "__fish_seen_subcommand_from doc" -l set -d 'override options in config.toml' -r -f complete -c x.py -n "__fish_seen_subcommand_from doc" -l open -d 'open the docs in a browser' complete -c x.py -n "__fish_seen_subcommand_from doc" -l json -d 'render the documentation in JSON format in addition to the usual HTML format' complete -c x.py -n "__fish_seen_subcommand_from doc" -s v -l verbose -d 'use verbose output (-vv for very verbose)' @@ -251,6 +258,7 @@ complete -c x.py -n "__fish_seen_subcommand_from test" -l rust-profile-generate complete -c x.py -n "__fish_seen_subcommand_from test" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F complete -c x.py -n "__fish_seen_subcommand_from test" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F complete -c x.py -n "__fish_seen_subcommand_from test" -l llvm-bolt-profile-use -d 'use BOLT profile for LLVM build' -r -F +complete -c x.py -n "__fish_seen_subcommand_from test" -l set -d 'override options in config.toml' -r -f complete -c x.py -n "__fish_seen_subcommand_from test" -l no-fail-fast -d 'run all tests regardless of failure' complete -c x.py -n "__fish_seen_subcommand_from test" -l no-doc -d 'do not run doc tests' complete -c x.py -n "__fish_seen_subcommand_from test" -l doc -d 'only run doc tests' @@ -288,6 +296,7 @@ complete -c x.py -n "__fish_seen_subcommand_from bench" -l rust-profile-generate complete -c x.py -n "__fish_seen_subcommand_from bench" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F complete -c x.py -n "__fish_seen_subcommand_from bench" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F complete -c x.py -n "__fish_seen_subcommand_from bench" -l llvm-bolt-profile-use -d 'use BOLT profile for LLVM build' -r -F +complete -c x.py -n "__fish_seen_subcommand_from bench" -l set -d 'override options in config.toml' -r -f complete -c x.py -n "__fish_seen_subcommand_from bench" -s v -l verbose -d 'use verbose output (-vv for very verbose)' complete -c x.py -n "__fish_seen_subcommand_from bench" -s i -l incremental -d 'use incremental compilation' complete -c x.py -n "__fish_seen_subcommand_from bench" -l include-default-paths -d 'include default paths in addition to the provided ones' @@ -317,6 +326,7 @@ complete -c x.py -n "__fish_seen_subcommand_from clean" -l rust-profile-generate complete -c x.py -n "__fish_seen_subcommand_from clean" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F complete -c x.py -n "__fish_seen_subcommand_from clean" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F complete -c x.py -n "__fish_seen_subcommand_from clean" -l llvm-bolt-profile-use -d 'use BOLT profile for LLVM build' -r -F +complete -c x.py -n "__fish_seen_subcommand_from clean" -l set -d 'override options in config.toml' -r -f complete -c x.py -n "__fish_seen_subcommand_from clean" -l all complete -c x.py -n "__fish_seen_subcommand_from clean" -s v -l verbose -d 'use verbose output (-vv for very verbose)' complete -c x.py -n "__fish_seen_subcommand_from clean" -s i -l incremental -d 'use incremental compilation' @@ -347,6 +357,7 @@ complete -c x.py -n "__fish_seen_subcommand_from dist" -l rust-profile-generate complete -c x.py -n "__fish_seen_subcommand_from dist" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F complete -c x.py -n "__fish_seen_subcommand_from dist" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F complete -c x.py -n "__fish_seen_subcommand_from dist" -l llvm-bolt-profile-use -d 'use BOLT profile for LLVM build' -r -F +complete -c x.py -n "__fish_seen_subcommand_from dist" -l set -d 'override options in config.toml' -r -f complete -c x.py -n "__fish_seen_subcommand_from dist" -s v -l verbose -d 'use verbose output (-vv for very verbose)' complete -c x.py -n "__fish_seen_subcommand_from dist" -s i -l incremental -d 'use incremental compilation' complete -c x.py -n "__fish_seen_subcommand_from dist" -l include-default-paths -d 'include default paths in addition to the provided ones' @@ -376,6 +387,7 @@ complete -c x.py -n "__fish_seen_subcommand_from install" -l rust-profile-genera complete -c x.py -n "__fish_seen_subcommand_from install" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F complete -c x.py -n "__fish_seen_subcommand_from install" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F complete -c x.py -n "__fish_seen_subcommand_from install" -l llvm-bolt-profile-use -d 'use BOLT profile for LLVM build' -r -F +complete -c x.py -n "__fish_seen_subcommand_from install" -l set -d 'override options in config.toml' -r -f complete -c x.py -n "__fish_seen_subcommand_from install" -s v -l verbose -d 'use verbose output (-vv for very verbose)' complete -c x.py -n "__fish_seen_subcommand_from install" -s i -l incremental -d 'use incremental compilation' complete -c x.py -n "__fish_seen_subcommand_from install" -l include-default-paths -d 'include default paths in addition to the provided ones' @@ -406,6 +418,7 @@ complete -c x.py -n "__fish_seen_subcommand_from run" -l rust-profile-generate - complete -c x.py -n "__fish_seen_subcommand_from run" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F complete -c x.py -n "__fish_seen_subcommand_from run" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F complete -c x.py -n "__fish_seen_subcommand_from run" -l llvm-bolt-profile-use -d 'use BOLT profile for LLVM build' -r -F +complete -c x.py -n "__fish_seen_subcommand_from run" -l set -d 'override options in config.toml' -r -f complete -c x.py -n "__fish_seen_subcommand_from run" -s v -l verbose -d 'use verbose output (-vv for very verbose)' complete -c x.py -n "__fish_seen_subcommand_from run" -s i -l incremental -d 'use incremental compilation' complete -c x.py -n "__fish_seen_subcommand_from run" -l include-default-paths -d 'include default paths in addition to the provided ones' @@ -435,6 +448,7 @@ complete -c x.py -n "__fish_seen_subcommand_from setup" -l rust-profile-generate complete -c x.py -n "__fish_seen_subcommand_from setup" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F complete -c x.py -n "__fish_seen_subcommand_from setup" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F complete -c x.py -n "__fish_seen_subcommand_from setup" -l llvm-bolt-profile-use -d 'use BOLT profile for LLVM build' -r -F +complete -c x.py -n "__fish_seen_subcommand_from setup" -l set -d 'override options in config.toml' -r -f complete -c x.py -n "__fish_seen_subcommand_from setup" -s v -l verbose -d 'use verbose output (-vv for very verbose)' complete -c x.py -n "__fish_seen_subcommand_from setup" -s i -l incremental -d 'use incremental compilation' complete -c x.py -n "__fish_seen_subcommand_from setup" -l include-default-paths -d 'include default paths in addition to the provided ones' @@ -464,6 +478,7 @@ complete -c x.py -n "__fish_seen_subcommand_from suggest" -l rust-profile-genera complete -c x.py -n "__fish_seen_subcommand_from suggest" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F complete -c x.py -n "__fish_seen_subcommand_from suggest" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F complete -c x.py -n "__fish_seen_subcommand_from suggest" -l llvm-bolt-profile-use -d 'use BOLT profile for LLVM build' -r -F +complete -c x.py -n "__fish_seen_subcommand_from suggest" -l set -d 'override options in config.toml' -r -f complete -c x.py -n "__fish_seen_subcommand_from suggest" -l run -d 'run suggested tests' complete -c x.py -n "__fish_seen_subcommand_from suggest" -s v -l verbose -d 'use verbose output (-vv for very verbose)' complete -c x.py -n "__fish_seen_subcommand_from suggest" -s i -l incremental -d 'use incremental compilation' diff --git a/src/etc/completions/x.py.ps1 b/src/etc/completions/x.py.ps1 index 59fabf53f98..569c186555c 100644 --- a/src/etc/completions/x.py.ps1 +++ b/src/etc/completions/x.py.ps1 @@ -43,6 +43,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--rust-profile-use', 'rust-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for rustc build') [CompletionResult]::new('--llvm-profile-use', 'llvm-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for LLVM build') [CompletionResult]::new('--llvm-bolt-profile-use', 'llvm-bolt-profile-use', [CompletionResultType]::ParameterName, 'use BOLT profile for LLVM build') + [CompletionResult]::new('--set', 'set', [CompletionResultType]::ParameterName, 'override options in config.toml') [CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'use incremental compilation') @@ -93,6 +94,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--rust-profile-use', 'rust-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for rustc build') [CompletionResult]::new('--llvm-profile-use', 'llvm-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for LLVM build') [CompletionResult]::new('--llvm-bolt-profile-use', 'llvm-bolt-profile-use', [CompletionResultType]::ParameterName, 'use BOLT profile for LLVM build') + [CompletionResult]::new('--set', 'set', [CompletionResultType]::ParameterName, 'override options in config.toml') [CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'use incremental compilation') @@ -129,6 +131,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--rust-profile-use', 'rust-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for rustc build') [CompletionResult]::new('--llvm-profile-use', 'llvm-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for LLVM build') [CompletionResult]::new('--llvm-bolt-profile-use', 'llvm-bolt-profile-use', [CompletionResultType]::ParameterName, 'use BOLT profile for LLVM build') + [CompletionResult]::new('--set', 'set', [CompletionResultType]::ParameterName, 'override options in config.toml') [CompletionResult]::new('--all-targets', 'all-targets', [CompletionResultType]::ParameterName, 'Check all targets') [CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') @@ -170,6 +173,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--rust-profile-use', 'rust-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for rustc build') [CompletionResult]::new('--llvm-profile-use', 'llvm-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for LLVM build') [CompletionResult]::new('--llvm-bolt-profile-use', 'llvm-bolt-profile-use', [CompletionResultType]::ParameterName, 'use BOLT profile for LLVM build') + [CompletionResult]::new('--set', 'set', [CompletionResultType]::ParameterName, 'override options in config.toml') [CompletionResult]::new('--fix', 'fix', [CompletionResultType]::ParameterName, 'fix') [CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') @@ -207,6 +211,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--rust-profile-use', 'rust-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for rustc build') [CompletionResult]::new('--llvm-profile-use', 'llvm-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for LLVM build') [CompletionResult]::new('--llvm-bolt-profile-use', 'llvm-bolt-profile-use', [CompletionResultType]::ParameterName, 'use BOLT profile for LLVM build') + [CompletionResult]::new('--set', 'set', [CompletionResultType]::ParameterName, 'override options in config.toml') [CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'use incremental compilation') @@ -243,6 +248,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--rust-profile-use', 'rust-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for rustc build') [CompletionResult]::new('--llvm-profile-use', 'llvm-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for LLVM build') [CompletionResult]::new('--llvm-bolt-profile-use', 'llvm-bolt-profile-use', [CompletionResultType]::ParameterName, 'use BOLT profile for LLVM build') + [CompletionResult]::new('--set', 'set', [CompletionResultType]::ParameterName, 'override options in config.toml') [CompletionResult]::new('--check', 'check', [CompletionResultType]::ParameterName, 'check formatting instead of applying') [CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') @@ -280,6 +286,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--rust-profile-use', 'rust-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for rustc build') [CompletionResult]::new('--llvm-profile-use', 'llvm-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for LLVM build') [CompletionResult]::new('--llvm-bolt-profile-use', 'llvm-bolt-profile-use', [CompletionResultType]::ParameterName, 'use BOLT profile for LLVM build') + [CompletionResult]::new('--set', 'set', [CompletionResultType]::ParameterName, 'override options in config.toml') [CompletionResult]::new('--open', 'open', [CompletionResultType]::ParameterName, 'open the docs in a browser') [CompletionResult]::new('--json', 'json', [CompletionResultType]::ParameterName, 'render the documentation in JSON format in addition to the usual HTML format') [CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') @@ -324,6 +331,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--rust-profile-use', 'rust-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for rustc build') [CompletionResult]::new('--llvm-profile-use', 'llvm-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for LLVM build') [CompletionResult]::new('--llvm-bolt-profile-use', 'llvm-bolt-profile-use', [CompletionResultType]::ParameterName, 'use BOLT profile for LLVM build') + [CompletionResult]::new('--set', 'set', [CompletionResultType]::ParameterName, 'override options in config.toml') [CompletionResult]::new('--no-fail-fast', 'no-fail-fast', [CompletionResultType]::ParameterName, 'run all tests regardless of failure') [CompletionResult]::new('--no-doc', 'no-doc', [CompletionResultType]::ParameterName, 'do not run doc tests') [CompletionResult]::new('--doc', 'doc', [CompletionResultType]::ParameterName, 'only run doc tests') @@ -368,6 +376,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--rust-profile-use', 'rust-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for rustc build') [CompletionResult]::new('--llvm-profile-use', 'llvm-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for LLVM build') [CompletionResult]::new('--llvm-bolt-profile-use', 'llvm-bolt-profile-use', [CompletionResultType]::ParameterName, 'use BOLT profile for LLVM build') + [CompletionResult]::new('--set', 'set', [CompletionResultType]::ParameterName, 'override options in config.toml') [CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'use incremental compilation') @@ -404,6 +413,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--rust-profile-use', 'rust-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for rustc build') [CompletionResult]::new('--llvm-profile-use', 'llvm-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for LLVM build') [CompletionResult]::new('--llvm-bolt-profile-use', 'llvm-bolt-profile-use', [CompletionResultType]::ParameterName, 'use BOLT profile for LLVM build') + [CompletionResult]::new('--set', 'set', [CompletionResultType]::ParameterName, 'override options in config.toml') [CompletionResult]::new('--all', 'all', [CompletionResultType]::ParameterName, 'all') [CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') @@ -441,6 +451,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--rust-profile-use', 'rust-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for rustc build') [CompletionResult]::new('--llvm-profile-use', 'llvm-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for LLVM build') [CompletionResult]::new('--llvm-bolt-profile-use', 'llvm-bolt-profile-use', [CompletionResultType]::ParameterName, 'use BOLT profile for LLVM build') + [CompletionResult]::new('--set', 'set', [CompletionResultType]::ParameterName, 'override options in config.toml') [CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'use incremental compilation') @@ -477,6 +488,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--rust-profile-use', 'rust-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for rustc build') [CompletionResult]::new('--llvm-profile-use', 'llvm-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for LLVM build') [CompletionResult]::new('--llvm-bolt-profile-use', 'llvm-bolt-profile-use', [CompletionResultType]::ParameterName, 'use BOLT profile for LLVM build') + [CompletionResult]::new('--set', 'set', [CompletionResultType]::ParameterName, 'override options in config.toml') [CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'use incremental compilation') @@ -514,6 +526,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--rust-profile-use', 'rust-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for rustc build') [CompletionResult]::new('--llvm-profile-use', 'llvm-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for LLVM build') [CompletionResult]::new('--llvm-bolt-profile-use', 'llvm-bolt-profile-use', [CompletionResultType]::ParameterName, 'use BOLT profile for LLVM build') + [CompletionResult]::new('--set', 'set', [CompletionResultType]::ParameterName, 'override options in config.toml') [CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'use incremental compilation') @@ -550,6 +563,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--rust-profile-use', 'rust-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for rustc build') [CompletionResult]::new('--llvm-profile-use', 'llvm-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for LLVM build') [CompletionResult]::new('--llvm-bolt-profile-use', 'llvm-bolt-profile-use', [CompletionResultType]::ParameterName, 'use BOLT profile for LLVM build') + [CompletionResult]::new('--set', 'set', [CompletionResultType]::ParameterName, 'override options in config.toml') [CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'use incremental compilation') @@ -586,6 +600,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--rust-profile-use', 'rust-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for rustc build') [CompletionResult]::new('--llvm-profile-use', 'llvm-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for LLVM build') [CompletionResult]::new('--llvm-bolt-profile-use', 'llvm-bolt-profile-use', [CompletionResultType]::ParameterName, 'use BOLT profile for LLVM build') + [CompletionResult]::new('--set', 'set', [CompletionResultType]::ParameterName, 'override options in config.toml') [CompletionResult]::new('--run', 'run', [CompletionResultType]::ParameterName, 'run suggested tests') [CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') diff --git a/src/etc/completions/x.py.sh b/src/etc/completions/x.py.sh index 931cc4353b2..322afdb2883 100644 --- a/src/etc/completions/x.py.sh +++ b/src/etc/completions/x.py.sh @@ -61,7 +61,7 @@ _x.py() { case "${cmd}" in x.py) - opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --llvm-bolt-profile-generate --llvm-bolt-profile-use --help [PATHS]... [ARGS]... build check clippy fix fmt doc test bench clean dist install run setup suggest" + opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --llvm-bolt-profile-generate --llvm-bolt-profile-use --set --help [PATHS]... [ARGS]... build check clippy fix fmt doc test bench clean dist install run setup suggest" if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -155,6 +155,10 @@ _x.py() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --set) + COMPREPLY=("${cur}") + return 0 + ;; *) COMPREPLY=() ;; @@ -163,7 +167,7 @@ _x.py() { return 0 ;; x.py__bench) - opts="-v -i -j -h --test-args --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --llvm-bolt-profile-generate --llvm-bolt-profile-use --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --test-args --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --llvm-bolt-profile-generate --llvm-bolt-profile-use --set --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -261,6 +265,10 @@ _x.py() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --set) + COMPREPLY=("${cur}") + return 0 + ;; *) COMPREPLY=() ;; @@ -269,7 +277,7 @@ _x.py() { return 0 ;; x.py__build) - opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --llvm-bolt-profile-generate --llvm-bolt-profile-use --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --llvm-bolt-profile-generate --llvm-bolt-profile-use --set --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -363,6 +371,10 @@ _x.py() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --set) + COMPREPLY=("${cur}") + return 0 + ;; *) COMPREPLY=() ;; @@ -371,7 +383,7 @@ _x.py() { return 0 ;; x.py__check) - opts="-v -i -j -h --all-targets --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --llvm-bolt-profile-generate --llvm-bolt-profile-use --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --all-targets --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --llvm-bolt-profile-generate --llvm-bolt-profile-use --set --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -465,6 +477,10 @@ _x.py() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --set) + COMPREPLY=("${cur}") + return 0 + ;; *) COMPREPLY=() ;; @@ -473,7 +489,7 @@ _x.py() { return 0 ;; x.py__clean) - opts="-v -i -j -h --all --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --llvm-bolt-profile-generate --llvm-bolt-profile-use --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --all --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --llvm-bolt-profile-generate --llvm-bolt-profile-use --set --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -567,6 +583,10 @@ _x.py() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --set) + COMPREPLY=("${cur}") + return 0 + ;; *) COMPREPLY=() ;; @@ -575,7 +595,7 @@ _x.py() { return 0 ;; x.py__clippy) - opts="-A -D -W -F -v -i -j -h --fix --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --llvm-bolt-profile-generate --llvm-bolt-profile-use --help [PATHS]... [ARGS]..." + opts="-A -D -W -F -v -i -j -h --fix --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --llvm-bolt-profile-generate --llvm-bolt-profile-use --set --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -685,6 +705,10 @@ _x.py() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --set) + COMPREPLY=("${cur}") + return 0 + ;; *) COMPREPLY=() ;; @@ -693,7 +717,7 @@ _x.py() { return 0 ;; x.py__dist) - opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --llvm-bolt-profile-generate --llvm-bolt-profile-use --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --llvm-bolt-profile-generate --llvm-bolt-profile-use --set --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -787,6 +811,10 @@ _x.py() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --set) + COMPREPLY=("${cur}") + return 0 + ;; *) COMPREPLY=() ;; @@ -795,7 +823,7 @@ _x.py() { return 0 ;; x.py__doc) - opts="-v -i -j -h --open --json --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --llvm-bolt-profile-generate --llvm-bolt-profile-use --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --open --json --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --llvm-bolt-profile-generate --llvm-bolt-profile-use --set --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -889,6 +917,10 @@ _x.py() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --set) + COMPREPLY=("${cur}") + return 0 + ;; *) COMPREPLY=() ;; @@ -897,7 +929,7 @@ _x.py() { return 0 ;; x.py__fix) - opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --llvm-bolt-profile-generate --llvm-bolt-profile-use --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --llvm-bolt-profile-generate --llvm-bolt-profile-use --set --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -991,6 +1023,10 @@ _x.py() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --set) + COMPREPLY=("${cur}") + return 0 + ;; *) COMPREPLY=() ;; @@ -999,7 +1035,7 @@ _x.py() { return 0 ;; x.py__fmt) - opts="-v -i -j -h --check --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --llvm-bolt-profile-generate --llvm-bolt-profile-use --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --check --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --llvm-bolt-profile-generate --llvm-bolt-profile-use --set --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -1093,6 +1129,10 @@ _x.py() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --set) + COMPREPLY=("${cur}") + return 0 + ;; *) COMPREPLY=() ;; @@ -1101,7 +1141,7 @@ _x.py() { return 0 ;; x.py__install) - opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --llvm-bolt-profile-generate --llvm-bolt-profile-use --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --llvm-bolt-profile-generate --llvm-bolt-profile-use --set --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -1195,6 +1235,10 @@ _x.py() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --set) + COMPREPLY=("${cur}") + return 0 + ;; *) COMPREPLY=() ;; @@ -1203,7 +1247,7 @@ _x.py() { return 0 ;; x.py__run) - opts="-v -i -j -h --args --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --llvm-bolt-profile-generate --llvm-bolt-profile-use --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --args --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --llvm-bolt-profile-generate --llvm-bolt-profile-use --set --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -1301,6 +1345,10 @@ _x.py() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --set) + COMPREPLY=("${cur}") + return 0 + ;; *) COMPREPLY=() ;; @@ -1309,7 +1357,7 @@ _x.py() { return 0 ;; x.py__setup) - opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --llvm-bolt-profile-generate --llvm-bolt-profile-use --help [<PROFILE>|hook|vscode|link] [PATHS]... [ARGS]..." + opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --llvm-bolt-profile-generate --llvm-bolt-profile-use --set --help [<PROFILE>|hook|vscode|link] [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -1403,6 +1451,10 @@ _x.py() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --set) + COMPREPLY=("${cur}") + return 0 + ;; *) COMPREPLY=() ;; @@ -1411,7 +1463,7 @@ _x.py() { return 0 ;; x.py__suggest) - opts="-v -i -j -h --run --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --llvm-bolt-profile-generate --llvm-bolt-profile-use --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --run --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --llvm-bolt-profile-generate --llvm-bolt-profile-use --set --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -1505,6 +1557,10 @@ _x.py() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --set) + COMPREPLY=("${cur}") + return 0 + ;; *) COMPREPLY=() ;; @@ -1513,7 +1569,7 @@ _x.py() { return 0 ;; x.py__test) - opts="-v -i -j -h --no-fail-fast --skip --test-args --rustc-args --no-doc --doc --bless --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --llvm-bolt-profile-generate --llvm-bolt-profile-use --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --no-fail-fast --skip --test-args --rustc-args --no-doc --doc --bless --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --llvm-bolt-profile-generate --llvm-bolt-profile-use --set --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -1631,6 +1687,10 @@ _x.py() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --set) + COMPREPLY=("${cur}") + return 0 + ;; *) COMPREPLY=() ;; diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 38664c3e359..e9ccea2cf27 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -401,12 +401,18 @@ impl Item { .unwrap_or_else(|| self.span(tcx).map_or(rustc_span::DUMMY_SP, |span| span.inner())) } - /// Finds the `doc` attribute as a NameValue and returns the corresponding - /// value found. - pub(crate) fn doc_value(&self) -> Option<String> { + /// Combine all doc strings into a single value handling indentation and newlines as needed. + pub(crate) fn doc_value(&self) -> String { self.attrs.doc_value() } + /// Combine all doc strings into a single value handling indentation and newlines as needed. + /// Returns `None` is there's no documentation at all, and `Some("")` if there is some + /// documentation but it is empty (e.g. `#[doc = ""]`). + pub(crate) fn opt_doc_value(&self) -> Option<String> { + self.attrs.opt_doc_value() + } + pub(crate) fn from_def_id_and_parts( def_id: DefId, name: Option<Symbol>, @@ -443,12 +449,6 @@ impl Item { } } - /// Finds all `doc` attributes as NameValues and returns their corresponding values, joined - /// with newlines. - pub(crate) fn collapsed_doc_value(&self) -> Option<String> { - self.attrs.collapsed_doc_value() - } - pub(crate) fn links(&self, cx: &Context<'_>) -> Vec<RenderedLink> { use crate::html::format::{href, link_tooltip}; @@ -1068,17 +1068,6 @@ impl<I: Iterator<Item = ast::NestedMetaItem>> NestedAttributesExt for I { } } -/// Collapse a collection of [`DocFragment`]s into one string, -/// handling indentation and newlines as needed. -pub(crate) fn collapse_doc_fragments(doc_strings: &[DocFragment]) -> String { - let mut acc = String::new(); - for frag in doc_strings { - add_doc_fragment(&mut acc, frag); - } - acc.pop(); - acc -} - /// A link that has not yet been rendered. /// /// This link will be turned into a rendered link by [`Item::links`]. @@ -1163,29 +1152,23 @@ impl Attributes { Attributes { doc_strings, other_attrs } } - /// Finds the `doc` attribute as a NameValue and returns the corresponding - /// value found. - pub(crate) fn doc_value(&self) -> Option<String> { - let mut iter = self.doc_strings.iter(); - - let ori = iter.next()?; - let mut out = String::new(); - add_doc_fragment(&mut out, ori); - for new_frag in iter { - add_doc_fragment(&mut out, new_frag); - } - out.pop(); - if out.is_empty() { None } else { Some(out) } + /// Combine all doc strings into a single value handling indentation and newlines as needed. + pub(crate) fn doc_value(&self) -> String { + self.opt_doc_value().unwrap_or_default() } - /// Finds all `doc` attributes as NameValues and returns their corresponding values, joined - /// with newlines. - pub(crate) fn collapsed_doc_value(&self) -> Option<String> { - if self.doc_strings.is_empty() { - None - } else { - Some(collapse_doc_fragments(&self.doc_strings)) - } + /// Combine all doc strings into a single value handling indentation and newlines as needed. + /// Returns `None` is there's no documentation at all, and `Some("")` if there is some + /// documentation but it is empty (e.g. `#[doc = ""]`). + pub(crate) fn opt_doc_value(&self) -> Option<String> { + (!self.doc_strings.is_empty()).then(|| { + let mut res = String::new(); + for frag in &self.doc_strings { + add_doc_fragment(&mut res, frag); + } + res.pop(); + res + }) } pub(crate) fn get_doc_aliases(&self) -> Box<[Symbol]> { diff --git a/src/librustdoc/clean/types/tests.rs b/src/librustdoc/clean/types/tests.rs index d8c91a96804..394954208a4 100644 --- a/src/librustdoc/clean/types/tests.rs +++ b/src/librustdoc/clean/types/tests.rs @@ -1,7 +1,5 @@ use super::*; -use crate::clean::collapse_doc_fragments; - use rustc_resolve::rustdoc::{unindent_doc_fragments, DocFragment, DocFragmentKind}; use rustc_span::create_default_session_globals_then; use rustc_span::source_map::DUMMY_SP; @@ -22,7 +20,8 @@ fn run_test(input: &str, expected: &str) { create_default_session_globals_then(|| { let mut s = create_doc_fragment(input); unindent_doc_fragments(&mut s); - assert_eq!(collapse_doc_fragments(&s), expected); + let attrs = Attributes { doc_strings: s, other_attrs: Default::default() }; + assert_eq!(attrs.doc_value(), expected); }); } diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index a6be132337e..e10a6297775 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -367,7 +367,7 @@ pub(crate) fn run_global_ctxt( let mut krate = tcx.sess.time("clean_crate", || clean::krate(&mut ctxt)); - if krate.module.doc_value().map(|d| d.is_empty()).unwrap_or(true) { + if krate.module.doc_value().is_empty() { let help = format!( "The following guide may be of use:\n\ {}/rustdoc/how-to-write-documentation.html", diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 575d8ee65b7..f6631b66f5b 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -1237,7 +1237,7 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> { // The collapse-docs pass won't combine sugared/raw doc attributes, or included files with // anything else, this will combine them for us. let attrs = Attributes::from_ast(ast_attrs); - if let Some(doc) = attrs.collapsed_doc_value() { + if let Some(doc) = attrs.opt_doc_value() { // Use the outermost invocation, so that doctest names come from where the docs were written. let span = ast_attrs .iter() diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index c0730e90740..c4758fd9866 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -327,9 +327,8 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { // which should not be indexed. The crate-item itself is // inserted later on when serializing the search-index. if item.item_id.as_def_id().map_or(false, |idx| !idx.is_crate_root()) { - let desc = item.doc_value().map_or_else(String::new, |x| { - short_markdown_summary(x.as_str(), &item.link_names(self.cache)) - }); + let desc = + short_markdown_summary(&item.doc_value(), &item.link_names(self.cache)); let ty = item.type_(); if ty != ItemType::StructField || u16::from_str_radix(s.as_str(), 10).is_err() diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index c9957e8e39c..09e7ed293d4 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -551,7 +551,15 @@ impl<'a, I: Iterator<Item = Event<'a>>> SummaryLine<'a, I> { } fn check_if_allowed_tag(t: &Tag<'_>) -> bool { - matches!(t, Tag::Paragraph | Tag::Emphasis | Tag::Strong | Tag::Link(..) | Tag::BlockQuote) + matches!( + t, + Tag::Paragraph + | Tag::Emphasis + | Tag::Strong + | Tag::Strikethrough + | Tag::Link(..) + | Tag::BlockQuote + ) } fn is_forbidden_tag(t: &Tag<'_>) -> bool { diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 01a92f6df6a..56af257fd5e 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -184,11 +184,8 @@ impl<'tcx> Context<'tcx> { }; title.push_str(" - Rust"); let tyname = it.type_(); - let desc = it - .doc_value() - .as_ref() - .map(|doc| plain_text_summary(doc, &it.link_names(&self.cache()))); - let desc = if let Some(desc) = desc { + let desc = plain_text_summary(&it.doc_value(), &it.link_names(&self.cache())); + let desc = if !desc.is_empty() { desc } else if it.is_crate() { format!("API documentation for the Rust `{}` crate.", self.shared.layout.krate) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 0a2f5f6653c..42e27d35a94 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -468,7 +468,8 @@ fn document_short<'a, 'cx: 'a>( if !show_def_docs { return Ok(()); } - if let Some(s) = item.doc_value() { + let s = item.doc_value(); + if !s.is_empty() { let (mut summary_html, has_more_content) = MarkdownSummaryLine(&s, &item.links(cx)).into_string_with_has_more_content(); @@ -511,7 +512,7 @@ fn document_full_inner<'a, 'cx: 'a>( heading_offset: HeadingOffset, ) -> impl fmt::Display + 'a + Captures<'cx> { display_fn(move |f| { - if let Some(s) = item.collapsed_doc_value() { + if let Some(s) = item.opt_doc_value() { debug!("Doc block: =====\n{}\n=====", s); if is_collapsible { write!( @@ -1476,7 +1477,7 @@ fn render_impl( if let Some(it) = t.items.iter().find(|i| i.name == item.name) { // We need the stability of the item from the trait // because impls can't have a stability. - if item.doc_value().is_some() { + if !item.doc_value().is_empty() { document_item_info(cx, it, Some(parent)) .render_into(&mut info_buffer) .unwrap(); @@ -1747,7 +1748,7 @@ fn render_impl( write!(w, "</summary>") } - if let Some(ref dox) = i.impl_item.collapsed_doc_value() { + if let Some(ref dox) = i.impl_item.opt_doc_value() { if trait_.is_none() && i.inner_impl().items.is_empty() { w.write_str( "<div class=\"item-info\">\ diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 4cc81e860f0..76c8e0885a0 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -420,9 +420,9 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: _ => "", }; - let doc_value = myitem.doc_value().unwrap_or_default(); w.write_str(ITEM_TABLE_ROW_OPEN); - let docs = MarkdownSummaryLine(&doc_value, &myitem.links(cx)).into_string(); + let docs = + MarkdownSummaryLine(&myitem.doc_value(), &myitem.links(cx)).into_string(); let (docs_before, docs_after) = if docs.is_empty() { ("", "") } else { @@ -1338,7 +1338,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: clean::VariantKind::Tuple(fields) => { // Documentation on tuple variant fields is rare, so to reduce noise we only emit // the section if at least one field is documented. - if fields.iter().any(|f| f.doc_value().is_some()) { + if fields.iter().any(|f| !f.doc_value().is_empty()) { Some(("Tuple Fields", fields)) } else { None diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index a3be6dd5269..846299f02e3 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -28,9 +28,7 @@ pub(crate) fn build_index<'tcx>( // has since been learned. for &OrphanImplItem { parent, ref item, ref impl_generics } in &cache.orphan_impl_items { if let Some((fqp, _)) = cache.paths.get(&parent) { - let desc = item - .doc_value() - .map_or_else(String::new, |s| short_markdown_summary(&s, &item.link_names(cache))); + let desc = short_markdown_summary(&item.doc_value(), &item.link_names(cache)); cache.search_index.push(IndexItem { ty: item.type_(), name: item.name.unwrap(), @@ -45,10 +43,8 @@ pub(crate) fn build_index<'tcx>( } } - let crate_doc = krate - .module - .doc_value() - .map_or_else(String::new, |s| short_markdown_summary(&s, &krate.module.link_names(cache))); + let crate_doc = + short_markdown_summary(&krate.module.doc_value(), &krate.module.link_names(cache)); // Aliases added through `#[doc(alias = "...")]`. Since a few items can have the same alias, // we need the alias element to have an array of items. diff --git a/src/librustdoc/html/static/css/settings.css b/src/librustdoc/html/static/css/settings.css index 99cf8e443f0..c1324c0760e 100644 --- a/src/librustdoc/html/static/css/settings.css +++ b/src/librustdoc/html/static/css/settings.css @@ -1,6 +1,5 @@ .setting-line { margin: 1.2em 0.6em; - position: relative; } .setting-radio input, .setting-check input { @@ -15,11 +14,6 @@ .setting-radio input { border-radius: 50%; } -.setting-check input:checked { - content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40">\ - <path d="M7,25L17,32L33,12" fill="none" stroke="black" stroke-width="5"/>\ - <path d="M7,23L17,30L33,10" fill="none" stroke="white" stroke-width="5"/></svg>'); -} .setting-radio span, .setting-check span { padding-bottom: 1px; @@ -52,6 +46,9 @@ .setting-check input:checked { background-color: var(--settings-input-color); border-width: 1px; + content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40">\ + <path d="M7,25L17,32L33,12" fill="none" stroke="black" stroke-width="5"/>\ + <path d="M7,23L17,30L33,10" fill="none" stroke="white" stroke-width="5"/></svg>'); } .setting-radio input:focus, .setting-check input:focus { box-shadow: 0 0 1px 1px var(--settings-input-color); diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index b1cef20b434..935bb721f18 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -40,7 +40,7 @@ impl JsonRenderer<'_> { (String::from(&**link), id_from_item_default(id.into(), self.tcx)) }) .collect(); - let docs = item.attrs.collapsed_doc_value(); + let docs = item.opt_doc_value(); let attrs = item.attributes(self.tcx, true); let span = item.span(self.tcx); let visibility = item.visibility(self.tcx); diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs index be5286b24d7..6ead0cd961a 100644 --- a/src/librustdoc/passes/calculate_doc_coverage.rs +++ b/src/librustdoc/passes/calculate_doc_coverage.rs @@ -206,13 +206,7 @@ impl<'a, 'b> DocVisitor for CoverageCalculator<'a, 'b> { let has_docs = !i.attrs.doc_strings.is_empty(); let mut tests = Tests { found_tests: 0 }; - find_testable_code( - &i.attrs.collapsed_doc_value().unwrap_or_default(), - &mut tests, - ErrorCodes::No, - false, - None, - ); + find_testable_code(&i.doc_value(), &mut tests, ErrorCodes::No, false, None); let has_doc_example = tests.found_tests != 0; let hir_id = DocContext::as_local_hir_id(self.ctx.tcx, i.item_id).unwrap(); diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs index 10295cbd189..b6cd897d317 100644 --- a/src/librustdoc/passes/check_doc_test_visibility.rs +++ b/src/librustdoc/passes/check_doc_test_visibility.rs @@ -34,9 +34,7 @@ pub(crate) fn check_doc_test_visibility(krate: Crate, cx: &mut DocContext<'_>) - impl<'a, 'tcx> DocVisitor for DocTestVisibilityLinter<'a, 'tcx> { fn visit_item(&mut self, item: &Item) { - let dox = item.attrs.collapsed_doc_value().unwrap_or_default(); - - look_for_tests(self.cx, &dox, item); + look_for_tests(self.cx, &item.doc_value(), item); self.visit_item_recur(item) } diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 9381b3e0567..9e6894a77df 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -149,7 +149,7 @@ impl TryFrom<ResolveRes> for Res { Def(kind, id) => Ok(Res::Def(kind, id)), PrimTy(prim) => Ok(Res::Primitive(PrimitiveType::from_hir(prim))), // e.g. `#[derive]` - NonMacroAttr(..) | Err => Result::Err(()), + ToolMod | NonMacroAttr(..) | Err => Result::Err(()), other => bug!("unrecognized res {:?}", other), } } diff --git a/src/librustdoc/passes/lint/bare_urls.rs b/src/librustdoc/passes/lint/bare_urls.rs index 423230cfe38..a10d5fdb410 100644 --- a/src/librustdoc/passes/lint/bare_urls.rs +++ b/src/librustdoc/passes/lint/bare_urls.rs @@ -18,7 +18,7 @@ pub(super) fn visit_item(cx: &DocContext<'_>, item: &Item) { // If non-local, no need to check anything. return; }; - let dox = item.attrs.collapsed_doc_value().unwrap_or_default(); + let dox = item.doc_value(); if !dox.is_empty() { let report_diag = |cx: &DocContext<'_>, msg: &str, url: &str, range: Range<usize>| { let sp = source_span_for_markdown_range(cx.tcx, &dox, &range, &item.attrs) diff --git a/src/librustdoc/passes/lint/check_code_block_syntax.rs b/src/librustdoc/passes/lint/check_code_block_syntax.rs index 8f873dbe501..f489f5081da 100644 --- a/src/librustdoc/passes/lint/check_code_block_syntax.rs +++ b/src/librustdoc/passes/lint/check_code_block_syntax.rs @@ -17,7 +17,7 @@ use crate::html::markdown::{self, RustCodeBlock}; use crate::passes::source_span_for_markdown_range; pub(crate) fn visit_item(cx: &DocContext<'_>, item: &clean::Item) { - if let Some(dox) = &item.attrs.collapsed_doc_value() { + if let Some(dox) = &item.opt_doc_value() { let sp = item.attr_span(cx.tcx); let extra = crate::html::markdown::ExtraInfo::new(cx.tcx, item.item_id.expect_def_id(), sp); for code_block in markdown::rust_code_blocks(dox, &extra) { diff --git a/src/librustdoc/passes/lint/html_tags.rs b/src/librustdoc/passes/lint/html_tags.rs index 4f72df5a5cd..f0403647af0 100644 --- a/src/librustdoc/passes/lint/html_tags.rs +++ b/src/librustdoc/passes/lint/html_tags.rs @@ -15,7 +15,7 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) { let Some(hir_id) = DocContext::as_local_hir_id(tcx, item.item_id) // If non-local, no need to check anything. else { return }; - let dox = item.attrs.collapsed_doc_value().unwrap_or_default(); + let dox = item.doc_value(); if !dox.is_empty() { let report_diag = |msg: &str, range: &Range<usize>, is_open_tag: bool| { let sp = match source_span_for_markdown_range(tcx, &dox, range, &item.attrs) { diff --git a/src/librustdoc/passes/lint/unescaped_backticks.rs b/src/librustdoc/passes/lint/unescaped_backticks.rs index 33cef82a60c..683c224c4be 100644 --- a/src/librustdoc/passes/lint/unescaped_backticks.rs +++ b/src/librustdoc/passes/lint/unescaped_backticks.rs @@ -16,7 +16,7 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) { return; }; - let dox = item.attrs.collapsed_doc_value().unwrap_or_default(); + let dox = item.doc_value(); if dox.is_empty() { return; } diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs index cba55e5fe65..73fc26a6b04 100644 --- a/src/librustdoc/passes/stripper.rs +++ b/src/librustdoc/passes/stripper.rs @@ -194,7 +194,7 @@ impl<'a> DocFolder for ImplStripper<'a, '_> { }) { return None; - } else if imp.items.is_empty() && i.doc_value().is_none() { + } else if imp.items.is_empty() && i.doc_value().is_empty() { return None; } } diff --git a/src/rustdoc-json-types/Cargo.toml b/src/rustdoc-json-types/Cargo.toml index d63caa7ad70..d3548036d4c 100644 --- a/src/rustdoc-json-types/Cargo.toml +++ b/src/rustdoc-json-types/Cargo.toml @@ -12,3 +12,4 @@ rustc-hash = "1.1.0" [dev-dependencies] serde_json = "1.0" +bincode = "1" diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index 3556834071f..ba8eeaa6682 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize}; use std::path::PathBuf; /// rustdoc format-version. -pub const FORMAT_VERSION: u32 = 25; +pub const FORMAT_VERSION: u32 = 26; /// A `Crate` is the root of the emitted JSON blob. It contains all type/documentation information /// about the language items in the local crate, as well as info about external items to allow @@ -83,7 +83,6 @@ pub struct Item { /// Stringified versions of the attributes on this item (e.g. `"#[inline]"`) pub attrs: Vec<String>, pub deprecation: Option<Deprecation>, - #[serde(flatten)] pub inner: ItemEnum, } @@ -222,7 +221,7 @@ pub enum ItemKind { } #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[serde(tag = "kind", content = "inner", rename_all = "snake_case")] +#[serde(rename_all = "snake_case")] pub enum ItemEnum { Module(Module), ExternCrate { @@ -543,7 +542,6 @@ pub enum Term { #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] -#[serde(tag = "kind", content = "inner")] pub enum Type { /// Structs, enums, and unions ResolvedPath(Path), diff --git a/src/rustdoc-json-types/tests.rs b/src/rustdoc-json-types/tests.rs index 399ff54b294..1126d5f786f 100644 --- a/src/rustdoc-json-types/tests.rs +++ b/src/rustdoc-json-types/tests.rs @@ -8,11 +8,15 @@ fn test_struct_info_roundtrip() { impls: vec![], }); + // JSON let struct_json = serde_json::to_string(&s).unwrap(); - let de_s = serde_json::from_str(&struct_json).unwrap(); - assert_eq!(s, de_s); + + // Bincode + let encoded: Vec<u8> = bincode::serialize(&s).unwrap(); + let decoded: ItemEnum = bincode::deserialize(&encoded).unwrap(); + assert_eq!(s, decoded); } #[test] @@ -24,9 +28,13 @@ fn test_union_info_roundtrip() { impls: vec![], }); + // JSON let union_json = serde_json::to_string(&u).unwrap(); - let de_u = serde_json::from_str(&union_json).unwrap(); - assert_eq!(u, de_u); + + // Bincode + let encoded: Vec<u8> = bincode::serialize(&u).unwrap(); + let decoded: ItemEnum = bincode::deserialize(&encoded).unwrap(); + assert_eq!(u, decoded); } diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index 21dad9eb74a..8b28c68e04f 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -24,6 +24,7 @@ static HOSTS: &[&str] = &[ "i686-pc-windows-gnu", "i686-pc-windows-msvc", "i686-unknown-linux-gnu", + "loongarch64-unknown-linux-gnu", "mips-unknown-linux-gnu", "mips64-unknown-linux-gnuabi64", "mips64el-unknown-linux-gnuabi64", @@ -97,6 +98,7 @@ static TARGETS: &[&str] = &[ "i686-unknown-linux-gnu", "i686-unknown-linux-musl", "i686-unknown-uefi", + "loongarch64-unknown-linux-gnu", "m68k-unknown-linux-gnu", "mips-unknown-linux-gnu", "mips-unknown-linux-musl", diff --git a/src/tools/cargo b/src/tools/cargo -Subproject 09276c703a473ab33daaeb94917232e80eefd62 +Subproject 64fb38c97ac4d3a327fc9032c862dd28c8833b1 diff --git a/src/tools/clippy/.github/workflows/clippy.yml b/src/tools/clippy/.github/workflows/clippy.yml index b9921301197..a9d42159c4b 100644 --- a/src/tools/clippy/.github/workflows/clippy.yml +++ b/src/tools/clippy/.github/workflows/clippy.yml @@ -39,7 +39,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v3.0.2 + uses: actions/checkout@v3 - name: Install toolchain run: rustup show active-toolchain diff --git a/src/tools/clippy/.github/workflows/clippy_bors.yml b/src/tools/clippy/.github/workflows/clippy_bors.yml index 93198aabdb5..30a156c925b 100644 --- a/src/tools/clippy/.github/workflows/clippy_bors.yml +++ b/src/tools/clippy/.github/workflows/clippy_bors.yml @@ -27,7 +27,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v3.0.2 + uses: actions/checkout@v3 with: ref: ${{ github.ref }} @@ -83,7 +83,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v3.0.2 + uses: actions/checkout@v3 - name: Install toolchain run: rustup show active-toolchain @@ -149,7 +149,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v3.0.2 + uses: actions/checkout@v3 - name: Install toolchain run: rustup show active-toolchain @@ -173,7 +173,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v3.0.2 + uses: actions/checkout@v3 - name: Install toolchain run: rustup show active-toolchain @@ -233,7 +233,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v3.0.2 + uses: actions/checkout@v3 - name: Install toolchain run: rustup show active-toolchain diff --git a/src/tools/clippy/.github/workflows/clippy_dev.yml b/src/tools/clippy/.github/workflows/clippy_dev.yml index 14f20212add..514706d64c8 100644 --- a/src/tools/clippy/.github/workflows/clippy_dev.yml +++ b/src/tools/clippy/.github/workflows/clippy_dev.yml @@ -25,7 +25,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v3.0.2 + uses: actions/checkout@v3 # Run - name: Build diff --git a/src/tools/clippy/.github/workflows/deploy.yml b/src/tools/clippy/.github/workflows/deploy.yml index 71d71d10359..f42928c2cd1 100644 --- a/src/tools/clippy/.github/workflows/deploy.yml +++ b/src/tools/clippy/.github/workflows/deploy.yml @@ -21,10 +21,10 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v3.0.2 + uses: actions/checkout@v3 - name: Checkout - uses: actions/checkout@v3.0.2 + uses: actions/checkout@v3 with: ref: ${{ env.TARGET_BRANCH }} path: 'out' diff --git a/src/tools/clippy/.github/workflows/remark.yml b/src/tools/clippy/.github/workflows/remark.yml index 0bc2f49f5e9..7d25b6a2b79 100644 --- a/src/tools/clippy/.github/workflows/remark.yml +++ b/src/tools/clippy/.github/workflows/remark.yml @@ -16,10 +16,10 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v3.0.2 + uses: actions/checkout@v3 - name: Setup Node.js - uses: actions/setup-node@v1.4.4 + uses: actions/setup-node@v3 with: node-version: '14.x' diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index ebf5b58a586..79f2a47110b 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -4620,6 +4620,7 @@ Released 2018-09-13 [`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else [`empty_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_drop [`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum +[`empty_line_after_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_doc_comments [`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr [`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop [`empty_structs_with_brackets`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_structs_with_brackets @@ -4785,6 +4786,7 @@ Released 2018-09-13 [`manual_main_separator_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_main_separator_str [`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy +[`manual_next_back`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_next_back [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive [`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or [`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains @@ -4897,6 +4899,7 @@ Released 2018-09-13 [`no_effect_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_underscore_binding [`no_mangle_with_rust_abi`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_mangle_with_rust_abi [`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal +[`non_minimal_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_minimal_cfg [`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions [`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty [`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool @@ -4978,6 +4981,7 @@ Released 2018-09-13 [`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference [`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref [`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref +[`ref_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_patterns [`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro [`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once [`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts diff --git a/src/tools/clippy/README.md b/src/tools/clippy/README.md index 6745e15c006..d712d3e6750 100644 --- a/src/tools/clippy/README.md +++ b/src/tools/clippy/README.md @@ -278,7 +278,7 @@ If you want to contribute to Clippy, you can find more information in [CONTRIBUT <!-- REUSE-IgnoreStart --> -Copyright 2014-2022 The Rust Project Developers +Copyright 2014-2023 The Rust Project Developers Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)> or the MIT license diff --git a/src/tools/clippy/book/src/development/type_checking.md b/src/tools/clippy/book/src/development/type_checking.md index 225de849566..d7c2775b896 100644 --- a/src/tools/clippy/book/src/development/type_checking.md +++ b/src/tools/clippy/book/src/development/type_checking.md @@ -133,7 +133,7 @@ in this chapter: - [Type checking](https://rustc-dev-guide.rust-lang.org/type-checking.html) - [Ty module](https://rustc-dev-guide.rust-lang.org/ty.html) -[Adt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html#variant.Adt +[Adt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/sty/enum.TyKind.html#variant.Adt [AdtDef]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/adt/struct.AdtDef.html [expr_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.expr_ty [node_type]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.node_type @@ -142,9 +142,9 @@ in this chapter: [kind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html#method.kind [LateContext]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LateContext.html [LateLintPass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html -[pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TypeckResults.html#method.pat_ty +[pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/typeck_results/struct.TypeckResults.html#method.pat_ty [Ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html -[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html +[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/sty/enum.TyKind.html [TypeckResults]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html [middle_ty]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_middle/ty/struct.Ty.html [hir_ty]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_hir/struct.Ty.html diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index 37e1e6a742f..98e69c7fd26 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -17,7 +17,7 @@ if_chain = "1.0" itertools = "0.10.1" pulldown-cmark = { version = "0.9", default-features = false } quine-mc_cluskey = "0.2" -regex-syntax = "0.6" +regex-syntax = "0.7" serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", optional = true } tempfile = { version = "3.2", optional = true } diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs index a36df55d0bd..a8dc0cb3b58 100644 --- a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs +++ b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs @@ -38,7 +38,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { _ => return, }; let Some((condition, panic_expn)) = find_assert_args(cx, e, macro_call.expn) else { return }; - let Some((Constant::Bool(val), _)) = constant(cx, cx.typeck_results(), condition) else { return }; + let Some(Constant::Bool(val)) = constant(cx, cx.typeck_results(), condition) else { return }; if val { span_lint_and_help( cx, diff --git a/src/tools/clippy/clippy_lints/src/attrs.rs b/src/tools/clippy/clippy_lints/src/attrs.rs index 751c262673b..897495ba108 100644 --- a/src/tools/clippy/clippy_lints/src/attrs.rs +++ b/src/tools/clippy/clippy_lints/src/attrs.rs @@ -178,6 +178,52 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does + /// Checks for empty lines after documenation comments. + /// + /// ### Why is this bad? + /// The documentation comment was most likely meant to be an inner attribute or regular comment. + /// If it was intended to be a documentation comment, then the empty line should be removed to + /// be more idiomatic. + /// + /// ### Known problems + /// Only detects empty lines immediately following the documentation. If the doc comment is followed + /// by an attribute and then an empty line, this lint will not trigger. Use `empty_line_after_outer_attr` + /// in combination with this lint to detect both cases. + /// + /// Does not detect empty lines after doc attributes (e.g. `#[doc = ""]`). + /// + /// ### Example + /// ```rust + /// /// Some doc comment with a blank line after it. + /// + /// fn not_quite_good_code() { } + /// ``` + /// + /// Use instead: + /// ```rust + /// /// Good (no blank line) + /// fn this_is_fine() { } + /// ``` + /// + /// ```rust + /// // Good (convert to a regular comment) + /// + /// fn this_is_fine_too() { } + /// ``` + /// + /// ```rust + /// //! Good (convert to a comment on an inner attribute) + /// + /// fn this_is_fine_as_well() { } + /// ``` + #[clippy::version = "1.70.0"] + pub EMPTY_LINE_AFTER_DOC_COMMENTS, + nursery, + "empty line after documentation comments" +} + +declare_clippy_lint! { + /// ### What it does /// Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category. /// /// ### Why is this bad? @@ -292,6 +338,30 @@ declare_clippy_lint! { "ensures that all `allow` and `expect` attributes have a reason" } +declare_clippy_lint! { + /// ### What it does + /// Checks for `any` and `all` combinators in `cfg` with only one condition. + /// + /// ### Why is this bad? + /// If there is only one condition, no need to wrap it into `any` or `all` combinators. + /// + /// ### Example + /// ```rust + /// #[cfg(any(unix))] + /// pub struct Bar; + /// ``` + /// + /// Use instead: + /// ```rust + /// #[cfg(unix)] + /// pub struct Bar; + /// ``` + #[clippy::version = "1.71.0"] + pub NON_MINIMAL_CFG, + style, + "ensure that all `cfg(any())` and `cfg(all())` have more than one condition" +} + declare_lint_pass!(Attributes => [ ALLOW_ATTRIBUTES_WITHOUT_REASON, INLINE_ALWAYS, @@ -604,6 +674,8 @@ impl_lint_pass!(EarlyAttributes => [ DEPRECATED_CFG_ATTR, MISMATCHED_TARGET_OS, EMPTY_LINE_AFTER_OUTER_ATTR, + EMPTY_LINE_AFTER_DOC_COMMENTS, + NON_MINIMAL_CFG, ]); impl EarlyLintPass for EarlyAttributes { @@ -614,15 +686,22 @@ impl EarlyLintPass for EarlyAttributes { fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { check_deprecated_cfg_attr(cx, attr, &self.msrv); check_mismatched_target_os(cx, attr); + check_minimal_cfg_condition(cx, attr); } extract_msrv_attr!(EarlyContext); } +/// Check for empty lines after outer attributes. +/// +/// Attributes and documenation comments are both considered outer attributes +/// by the AST. However, the average user likely considers them to be different. +/// Checking for empty lines after each of these attributes is split into two different +/// lints but can share the same logic. fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::Item) { let mut iter = item.attrs.iter().peekable(); while let Some(attr) = iter.next() { - if matches!(attr.kind, AttrKind::Normal(..)) + if (matches!(attr.kind, AttrKind::Normal(..)) || matches!(attr.kind, AttrKind::DocComment(..))) && attr.style == AttrStyle::Outer && is_present_in_source(cx, attr.span) { @@ -639,13 +718,20 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::It let lines = without_block_comments(lines); if lines.iter().filter(|l| l.trim().is_empty()).count() > 2 { - span_lint( - cx, - EMPTY_LINE_AFTER_OUTER_ATTR, - begin_of_attr_to_item, - "found an empty line after an outer attribute. \ - Perhaps you forgot to add a `!` to make it an inner attribute?", - ); + let (lint_msg, lint_type) = match attr.kind { + AttrKind::DocComment(..) => ( + "found an empty line after a doc comment. \ + Perhaps you need to use `//!` to make a comment on a module, remove the empty line, or make a regular comment with `//`?", + EMPTY_LINE_AFTER_DOC_COMMENTS, + ), + AttrKind::Normal(..) => ( + "found an empty line after an outer attribute. \ + Perhaps you forgot to add a `!` to make it an inner attribute?", + EMPTY_LINE_AFTER_OUTER_ATTR, + ), + }; + + span_lint(cx, lint_type, begin_of_attr_to_item, lint_msg); } } } @@ -690,6 +776,48 @@ fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msr } } +fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) { + for item in items.iter() { + if let NestedMetaItem::MetaItem(meta) = item { + if !meta.has_name(sym::any) && !meta.has_name(sym::all) { + continue; + } + if let MetaItemKind::List(list) = &meta.kind { + check_nested_cfg(cx, list); + if list.len() == 1 { + span_lint_and_then( + cx, + NON_MINIMAL_CFG, + meta.span, + "unneeded sub `cfg` when there is only one condition", + |diag| { + if let Some(snippet) = snippet_opt(cx, list[0].span()) { + diag.span_suggestion(meta.span, "try", snippet, Applicability::MaybeIncorrect); + } + }, + ); + } else if list.is_empty() && meta.has_name(sym::all) { + span_lint_and_then( + cx, + NON_MINIMAL_CFG, + meta.span, + "unneeded sub `cfg` when there is no condition", + |_| {}, + ); + } + } + } + } +} + +fn check_minimal_cfg_condition(cx: &EarlyContext<'_>, attr: &Attribute) { + if attr.has_name(sym::cfg) && + let Some(items) = attr.meta_item_list() + { + check_nested_cfg(cx, &items); + } +} + fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) { fn find_os(name: &str) -> Option<&'static str> { UNIX_SYSTEMS diff --git a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs index c4520d00392..814108ed8a7 100644 --- a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs +++ b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs @@ -1,5 +1,6 @@ use crate::reference::DEREF_ADDROF; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_from_proc_macro; use clippy_utils::source::snippet_opt; use clippy_utils::ty::implements_trait; use clippy_utils::{get_parent_expr, is_lint_allowed}; @@ -47,8 +48,8 @@ declare_clippy_lint! { declare_lint_pass!(BorrowDerefRef => [BORROW_DEREF_REF]); -impl LateLintPass<'_> for BorrowDerefRef { - fn check_expr(&mut self, cx: &LateContext<'_>, e: &rustc_hir::Expr<'_>) { +impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef { + fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &rustc_hir::Expr<'tcx>) { if_chain! { if !e.span.from_expansion(); if let ExprKind::AddrOf(_, Mutability::Not, addrof_target) = e.kind; @@ -58,6 +59,7 @@ impl LateLintPass<'_> for BorrowDerefRef { if !matches!(deref_target.kind, ExprKind::Unary(UnOp::Deref, ..) ); let ref_ty = cx.typeck_results().expr_ty(deref_target); if let ty::Ref(_, inner_ty, Mutability::Not) = ref_ty.kind(); + if !is_from_proc_macro(cx, e); then{ if let Some(parent_expr) = get_parent_expr(cx, e){ diff --git a/src/tools/clippy/clippy_lints/src/box_default.rs b/src/tools/clippy/clippy_lints/src/box_default.rs index dfa949d1af2..e42c3fe2432 100644 --- a/src/tools/clippy/clippy_lints/src/box_default.rs +++ b/src/tools/clippy/clippy_lints/src/box_default.rs @@ -8,7 +8,9 @@ use rustc_hir::{ Block, Expr, ExprKind, Local, Node, QPath, TyKind, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::{lint::in_external_macro, ty::print::with_forced_trimmed_paths}; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::print::with_forced_trimmed_paths; +use rustc_middle::ty::IsSuggestable; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -49,7 +51,6 @@ impl LateLintPass<'_> for BoxDefault { && path_def_id(cx, ty).map_or(false, |id| Some(id) == cx.tcx.lang_items().owned_box()) && is_default_equivalent(cx, arg) { - let arg_ty = cx.typeck_results().expr_ty(arg); span_lint_and_sugg( cx, BOX_DEFAULT, @@ -58,8 +59,10 @@ impl LateLintPass<'_> for BoxDefault { "try", if is_plain_default(arg_path) || given_type(cx, expr) { "Box::default()".into() - } else { + } else if let Some(arg_ty) = cx.typeck_results().expr_ty(arg).make_suggestable(cx.tcx, true) { with_forced_trimmed_paths!(format!("Box::<{arg_ty}>::default()")) + } else { + return }, Applicability::MachineApplicable ); diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_nan_to_int.rs b/src/tools/clippy/clippy_lints/src/casts/cast_nan_to_int.rs index 322dc41b3a1..da756129db3 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_nan_to_int.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_nan_to_int.rs @@ -21,8 +21,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, fn is_known_nan(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { match constant(cx, cx.typeck_results(), e) { - Some((Constant::F64(n), _)) => n.is_nan(), - Some((Constant::F32(n), _)) => n.is_nan(), + Some(Constant::F64(n)) => n.is_nan(), + Some(Constant::F32(n)) => n.is_nan(), _ => false, } } diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs index 95c2ecbf791..84b99ad5c24 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs @@ -15,7 +15,7 @@ use rustc_target::abi::IntegerType; use super::{utils, CAST_ENUM_TRUNCATION, CAST_POSSIBLE_TRUNCATION}; fn constant_int(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> { - if let Some((Constant::Int(c), _)) = constant(cx, cx.typeck_results(), expr) { + if let Some(Constant::Int(c)) = constant(cx, cx.typeck_results(), expr) { Some(c) } else { None diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs index a20a97d4e56..a83dfd94dc2 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs @@ -29,7 +29,7 @@ fn should_lint(cx: &LateContext<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast // Don't lint for positive constants. let const_val = constant(cx, cx.typeck_results(), cast_op); if_chain! { - if let Some((Constant::Int(n), _)) = const_val; + if let Some(Constant::Int(n)) = const_val; if let ty::Int(ity) = *cast_from.kind(); if sext(cx.tcx, n, ity) >= 0; then { diff --git a/src/tools/clippy/clippy_lints/src/dbg_macro.rs b/src/tools/clippy/clippy_lints/src/dbg_macro.rs index 799e71e847a..ea17e7a6071 100644 --- a/src/tools/clippy/clippy_lints/src/dbg_macro.rs +++ b/src/tools/clippy/clippy_lints/src/dbg_macro.rs @@ -3,10 +3,10 @@ use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_in_cfg_test, is_in_test_function}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_hir::{Expr, ExprKind, Node}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::sym; +use rustc_span::{sym, BytePos, Pos, Span}; declare_clippy_lint! { /// ### What it does @@ -31,6 +31,31 @@ declare_clippy_lint! { "`dbg!` macro is intended as a debugging tool" } +/// Gets the span of the statement up to the next semicolon, if and only if the next +/// non-whitespace character actually is a semicolon. +/// E.g. +/// ```rust,ignore +/// +/// dbg!(); +/// ^^^^^^^ this span is returned +/// +/// foo!(dbg!()); +/// no span is returned +/// ``` +fn span_including_semi(cx: &LateContext<'_>, span: Span) -> Option<Span> { + let sm = cx.sess().source_map(); + let sf = sm.lookup_source_file(span.hi()); + let src = sf.src.as_ref()?.get(span.hi().to_usize()..)?; + let first_non_whitespace = src.find(|c: char| !c.is_whitespace())?; + + if src.as_bytes()[first_non_whitespace] == b';' { + let hi = span.hi() + BytePos::from_usize(first_non_whitespace + 1); + Some(span.with_hi(hi)) + } else { + None + } +} + #[derive(Copy, Clone)] pub struct DbgMacro { allow_dbg_in_tests: bool, @@ -55,13 +80,25 @@ impl LateLintPass<'_> for DbgMacro { return; } let mut applicability = Applicability::MachineApplicable; - let suggestion = match expr.peel_drop_temps().kind { + + let (sugg_span, suggestion) = match expr.peel_drop_temps().kind { // dbg!() - ExprKind::Block(_, _) => String::new(), - // dbg!(1) - ExprKind::Match(val, ..) => { - snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability).to_string() + ExprKind::Block(..) => { + // If the `dbg!` macro is a "free" statement and not contained within other expressions, + // remove the whole statement. + if let Some(Node::Stmt(stmt)) = cx.tcx.hir().find_parent(expr.hir_id) + && let Some(span) = span_including_semi(cx, stmt.span.source_callsite()) + { + (span, String::new()) + } else { + (macro_call.span, String::from("()")) + } }, + // dbg!(1) + ExprKind::Match(val, ..) => ( + macro_call.span, + snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability).to_string(), + ), // dbg!(2, 3) ExprKind::Tup( [ @@ -82,7 +119,7 @@ impl LateLintPass<'_> for DbgMacro { "..", &mut applicability, ); - format!("({snippet})") + (macro_call.span, format!("({snippet})")) }, _ => return, }; @@ -90,7 +127,7 @@ impl LateLintPass<'_> for DbgMacro { span_lint_and_sugg( cx, DBG_MACRO, - macro_call.span, + sugg_span, "the `dbg!` macro is intended as a debugging tool", "remove the invocation before committing it to a version control system", suggestion, diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 04993e49287..423eee47742 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -48,9 +48,11 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::attrs::BLANKET_CLIPPY_RESTRICTION_LINTS_INFO, crate::attrs::DEPRECATED_CFG_ATTR_INFO, crate::attrs::DEPRECATED_SEMVER_INFO, + crate::attrs::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO, crate::attrs::EMPTY_LINE_AFTER_OUTER_ATTR_INFO, crate::attrs::INLINE_ALWAYS_INFO, crate::attrs::MISMATCHED_TARGET_OS_INFO, + crate::attrs::NON_MINIMAL_CFG_INFO, crate::attrs::USELESS_ATTRIBUTE_INFO, crate::await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE_INFO, crate::await_holding_invalid::AWAIT_HOLDING_LOCK_INFO, @@ -347,6 +349,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::ITER_WITH_DRAIN_INFO, crate::methods::MANUAL_FILTER_MAP_INFO, crate::methods::MANUAL_FIND_MAP_INFO, + crate::methods::MANUAL_NEXT_BACK_INFO, crate::methods::MANUAL_OK_OR_INFO, crate::methods::MANUAL_SATURATING_ARITHMETIC_INFO, crate::methods::MANUAL_SPLIT_ONCE_INFO, @@ -485,7 +488,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::operators::FLOAT_EQUALITY_WITHOUT_ABS_INFO, crate::operators::IDENTITY_OP_INFO, crate::operators::INEFFECTIVE_BIT_MASK_INFO, - crate::operators::INTEGER_ARITHMETIC_INFO, crate::operators::INTEGER_DIVISION_INFO, crate::operators::MISREFACTORED_ASSIGN_OP_INFO, crate::operators::MODULO_ARITHMETIC_INFO, @@ -535,6 +537,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::redundant_slicing::REDUNDANT_SLICING_INFO, crate::redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES_INFO, crate::ref_option_ref::REF_OPTION_REF_INFO, + crate::ref_patterns::REF_PATTERNS_INFO, crate::reference::DEREF_ADDROF_INFO, crate::regex::INVALID_REGEX_INFO, crate::regex::TRIVIAL_REGEX_INFO, diff --git a/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs b/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs index e529d81a7e9..9bd7a0dc0f3 100644 --- a/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs +++ b/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs @@ -1,4 +1,4 @@ -use clippy_utils::{diagnostics::span_lint_and_sugg, is_from_proc_macro, match_def_path, paths}; +use clippy_utils::{diagnostics::span_lint_and_sugg, match_def_path, paths}; use hir::{def::Res, ExprKind}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -55,7 +55,8 @@ impl LateLintPass<'_> for DefaultConstructedUnitStructs { if let ty::Adt(def, ..) = cx.typeck_results().expr_ty(expr).kind(); if def.is_struct(); if let var @ ty::VariantDef { ctor: Some((hir::def::CtorKind::Const, _)), .. } = def.non_enum_variant(); - if !var.is_field_list_non_exhaustive() && !is_from_proc_macro(cx, expr); + if !var.is_field_list_non_exhaustive(); + if !expr.span.from_expansion() && !qpath.span().from_expansion(); then { span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs index b2f7d026cc8..9c60edb1794 100644 --- a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs +++ b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs @@ -98,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { let is_copy = is_copy(cx, arg_ty); let drop_is_single_call_in_arm = is_single_call_in_arm(cx, arg, expr); let (lint, msg) = match fn_name { - // early return for uplifted lints: drop_ref, drop_copy, forget_ref, forget_copy + // early return for uplifted lints: dropping_references, dropping_copy_types, forgetting_references, forgetting_copy_types sym::mem_drop if arg_ty.is_ref() && !drop_is_single_call_in_arm => return, sym::mem_forget if arg_ty.is_ref() => return, sym::mem_drop if is_copy && !drop_is_single_call_in_arm => return, diff --git a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs index a1a2c398a8a..3c55a563af4 100644 --- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs @@ -114,7 +114,7 @@ declare_lint_pass!(FloatingPointArithmetic => [ // Returns the specialized log method for a given base if base is constant // and is one of 2, 10 and e fn get_specialized_log_method(cx: &LateContext<'_>, base: &Expr<'_>) -> Option<&'static str> { - if let Some((value, _)) = constant(cx, cx.typeck_results(), base) { + if let Some(value) = constant(cx, cx.typeck_results(), base) { if F32(2.0) == value || F64(2.0) == value { return Some("log2"); } else if F32(10.0) == value || F64(10.0) == value { @@ -193,8 +193,8 @@ fn check_ln1p(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) { constant(cx, cx.typeck_results(), lhs), constant(cx, cx.typeck_results(), rhs), ) { - (Some((value, _)), _) if F32(1.0) == value || F64(1.0) == value => rhs, - (_, Some((value, _))) if F32(1.0) == value || F64(1.0) == value => lhs, + (Some(value), _) if F32(1.0) == value || F64(1.0) == value => rhs, + (_, Some(value)) if F32(1.0) == value || F64(1.0) == value => lhs, _ => return, }; @@ -237,7 +237,7 @@ fn get_integer_from_float_constant(value: &Constant) -> Option<i32> { fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) { // Check receiver - if let Some((value, _)) = constant(cx, cx.typeck_results(), receiver) { + if let Some(value) = constant(cx, cx.typeck_results(), receiver) { if let Some(method) = if F32(f32_consts::E) == value || F64(f64_consts::E) == value { Some("exp") } else if F32(2.0) == value || F64(2.0) == value { @@ -258,7 +258,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: } // Check argument - if let Some((value, _)) = constant(cx, cx.typeck_results(), &args[0]) { + if let Some(value) = constant(cx, cx.typeck_results(), &args[0]) { let (lint, help, suggestion) = if F32(1.0 / 2.0) == value || F64(1.0 / 2.0) == value { ( SUBOPTIMAL_FLOPS, @@ -298,7 +298,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: } fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) { - if let Some((value, _)) = constant(cx, cx.typeck_results(), &args[0]) { + if let Some(value) = constant(cx, cx.typeck_results(), &args[0]) { if value == Int(2) { if let Some(parent) = get_parent_expr(cx, expr) { if let Some(grandparent) = get_parent_expr(cx, parent) { @@ -384,8 +384,8 @@ fn detect_hypot(cx: &LateContext<'_>, receiver: &Expr<'_>) -> Option<String> { _ ) = &add_rhs.kind; if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi"; - if let Some((lvalue, _)) = constant(cx, cx.typeck_results(), largs_1); - if let Some((rvalue, _)) = constant(cx, cx.typeck_results(), rargs_1); + if let Some(lvalue) = constant(cx, cx.typeck_results(), largs_1); + if let Some(rvalue) = constant(cx, cx.typeck_results(), rargs_1); if Int(2) == lvalue && Int(2) == rvalue; then { return Some(format!("{}.hypot({})", Sugg::hir(cx, largs_0, "..").maybe_par(), Sugg::hir(cx, rargs_0, ".."))); @@ -416,7 +416,7 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, lhs, rhs) = expr.kind; if cx.typeck_results().expr_ty(lhs).is_floating_point(); - if let Some((value, _)) = constant(cx, cx.typeck_results(), rhs); + if let Some(value) = constant(cx, cx.typeck_results(), rhs); if F32(1.0) == value || F64(1.0) == value; if let ExprKind::MethodCall(path, self_arg, ..) = &lhs.kind; if cx.typeck_results().expr_ty(self_arg).is_floating_point(); @@ -669,8 +669,8 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { mul_lhs, mul_rhs, ) = &div_lhs.kind; - if let Some((rvalue, _)) = constant(cx, cx.typeck_results(), div_rhs); - if let Some((lvalue, _)) = constant(cx, cx.typeck_results(), mul_rhs); + if let Some(rvalue) = constant(cx, cx.typeck_results(), div_rhs); + if let Some(lvalue) = constant(cx, cx.typeck_results(), mul_rhs); then { // TODO: also check for constant values near PI/180 or 180/PI if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) && diff --git a/src/tools/clippy/clippy_lints/src/fn_null_check.rs b/src/tools/clippy/clippy_lints/src/fn_null_check.rs index d8f4a5fe221..521045a9fed 100644 --- a/src/tools/clippy/clippy_lints/src/fn_null_check.rs +++ b/src/tools/clippy/clippy_lints/src/fn_null_check.rs @@ -89,11 +89,7 @@ impl<'tcx> LateLintPass<'tcx> for FnNullCheck { // Catching: // (fn_ptr as *<const/mut> <ty>) == <const that evaluates to null_ptr> - _ if matches!( - constant(cx, cx.typeck_results(), to_check), - Some((Constant::RawPtr(0), _)) - ) => - { + _ if matches!(constant(cx, cx.typeck_results(), to_check), Some(Constant::RawPtr(0))) => { lint_expr(cx, expr); }, diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs index 012aa5a1d1d..ee7973b82ab 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs @@ -101,10 +101,10 @@ fn get_int_max(ty: Ty<'_>) -> Option<u128> { fn get_const<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<(u128, BinOpKind, &'tcx Expr<'tcx>)> { if let ExprKind::Binary(op, l, r) = expr.kind { let tr = cx.typeck_results(); - if let Some((Constant::Int(c), _)) = constant(cx, tr, r) { + if let Some(Constant::Int(c)) = constant(cx, tr, r) { return Some((c, op.node, l)); }; - if let Some((Constant::Int(c), _)) = constant(cx, tr, l) { + if let Some(Constant::Int(c)) = constant(cx, tr, l) { return Some((c, invert_op(op.node)?, r)); } } diff --git a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs index bdeddf44df7..7a269e98ff1 100644 --- a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs +++ b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs @@ -254,7 +254,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'a, 'tcx> { let parent_id = map.parent_id(expr.hir_id); if let Some(hir::Node::Expr(parent_expr)) = map.find(parent_id); if let hir::ExprKind::Index(_, index_expr) = parent_expr.kind; - if let Some((Constant::Int(index_value), _)) = constant(cx, cx.typeck_results(), index_expr); + if let Some(Constant::Int(index_value)) = constant(cx, cx.typeck_results(), index_expr); if let Ok(index_value) = index_value.try_into(); if index_value < max_suggested_slice; diff --git a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs index 924a361c0f6..22c14d9b04d 100644 --- a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs @@ -191,18 +191,14 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { /// Returns a tuple of options with the start and end (exclusive) values of /// the range. If the start or end is not constant, None is returned. fn to_const_range(cx: &LateContext<'_>, range: higher::Range<'_>, array_size: u128) -> (Option<u128>, Option<u128>) { - let s = range - .start - .map(|expr| constant(cx, cx.typeck_results(), expr).map(|(c, _)| c)); + let s = range.start.map(|expr| constant(cx, cx.typeck_results(), expr)); let start = match s { Some(Some(Constant::Int(x))) => Some(x), Some(_) => None, None => Some(0), }; - let e = range - .end - .map(|expr| constant(cx, cx.typeck_results(), expr).map(|(c, _)| c)); + let e = range.end.map(|expr| constant(cx, cx.typeck_results(), expr)); let end = match e { Some(Some(Constant::Int(x))) => { if range.limits == RangeLimits::Closed { diff --git a/src/tools/clippy/clippy_lints/src/let_underscore.rs b/src/tools/clippy/clippy_lints/src/let_underscore.rs index 16772a9d598..e6614180920 100644 --- a/src/tools/clippy/clippy_lints/src/let_underscore.rs +++ b/src/tools/clippy/clippy_lints/src/let_underscore.rs @@ -1,10 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_from_proc_macro; use clippy_utils::ty::{implements_trait, is_must_use_ty, match_type}; use clippy_utils::{is_must_use_func_call, paths}; -use rustc_hir::{ExprKind, Local, PatKind}; +use rustc_hir::{Local, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::IsSuggestable; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{BytePos, Span}; @@ -138,7 +140,7 @@ const SYNC_GUARD_PATHS: [&[&str]; 3] = [ ]; impl<'tcx> LateLintPass<'tcx> for LetUnderscore { - fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &Local<'tcx>) { if !in_external_macro(cx.tcx.sess, local.span) && let PatKind::Wild = local.pat.kind && let Some(init) = local.init @@ -191,15 +193,17 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { if local.pat.default_binding_modes && local.ty.is_none() { // When `default_binding_modes` is true, the `let` keyword is present. - // Ignore function calls that return impl traits... - if let Some(init) = local.init && - matches!(init.kind, ExprKind::Call(_, _) | ExprKind::MethodCall(_, _, _, _)) { - let expr_ty = cx.typeck_results().expr_ty(init); - if expr_ty.is_impl_trait() { - return; - } - } + // Ignore unnameable types + if let Some(init) = local.init + && !cx.typeck_results().expr_ty(init).is_suggestable(cx.tcx, true) + { + return; + } + // Ignore if it is from a procedural macro... + if is_from_proc_macro(cx, init) { + return; + } span_lint_and_help( cx, diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 3517842a01e..b442a4ac5f6 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -266,6 +266,7 @@ mod redundant_pub_crate; mod redundant_slicing; mod redundant_static_lifetimes; mod ref_option_ref; +mod ref_patterns; mod reference; mod regex; mod return_self_not_must_use; @@ -331,8 +332,11 @@ mod zero_div_zero; mod zero_sized_map_values; // end lints modules, do not remove this comment, it’s used in `update_lints` -use crate::utils::conf::{format_error, TryConf}; pub use crate::utils::conf::{lookup_conf_file, Conf}; +use crate::utils::{ + conf::{format_error, metadata::get_configuration_metadata, TryConf}, + FindAll, +}; /// Register all pre expansion lints /// @@ -471,7 +475,22 @@ pub(crate) struct LintInfo { pub fn explain(name: &str) { let target = format!("clippy::{}", name.to_ascii_uppercase()); match declared_lints::LINTS.iter().find(|info| info.lint.name == target) { - Some(info) => print!("{}", info.explanation), + Some(info) => { + println!("{}", info.explanation); + // Check if the lint has configuration + let mdconf = get_configuration_metadata(); + if let Some(config_vec_positions) = mdconf + .iter() + .find_all(|cconf| cconf.lints.contains(&info.lint.name_lower()[8..].to_owned())) + { + // If it has, print it + println!("### Configuration for {}:\n", info.lint.name_lower()); + for position in config_vec_positions { + let conf = &mdconf[position]; + println!(" - {}: {} (default: {})", conf.name, conf.doc, conf.default); + } + } + }, None => println!("unknown lint: {name}"), } } @@ -971,6 +990,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation)); store.register_early_pass(|| Box::new(suspicious_doc_comments::SuspiciousDocComments)); store.register_late_pass(|_| Box::new(items_after_test_module::ItemsAfterTestModule)); + store.register_early_pass(|| Box::new(ref_patterns::RefPatterns)); store.register_late_pass(|_| Box::new(default_constructed_unit_structs::DefaultConstructedUnitStructs)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs b/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs index bba9bb445a7..09b2032e20f 100644 --- a/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs +++ b/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs @@ -25,7 +25,7 @@ declare_clippy_lint! { /// /// ### Known problems /// This lint suggests replacing `filter_map()` or `flat_map()` applied to a `Lines` - /// instance in all cases. There two cases where the suggestion might not be + /// instance in all cases. There are two cases where the suggestion might not be /// appropriate or necessary: /// /// - If the `Lines` instance can never produce any error, or if an error is produced diff --git a/src/tools/clippy/clippy_lints/src/manual_let_else.rs b/src/tools/clippy/clippy_lints/src/manual_let_else.rs index 1247370b74a..3f8b42ffe80 100644 --- a/src/tools/clippy/clippy_lints/src/manual_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/manual_let_else.rs @@ -38,7 +38,6 @@ declare_clippy_lint! { /// Could be written: /// /// ```rust - /// # #![feature(let_else)] /// # fn main () { /// # let w = Some(0); /// let Some(v) = w else { return }; @@ -69,29 +68,23 @@ impl_lint_pass!(ManualLetElse => [MANUAL_LET_ELSE]); impl<'tcx> LateLintPass<'tcx> for ManualLetElse { fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &'tcx Stmt<'tcx>) { - let if_let_or_match = if_chain! { - if self.msrv.meets(msrvs::LET_ELSE); - if !in_external_macro(cx.sess(), stmt.span); - if let StmtKind::Local(local) = stmt.kind; - if let Some(init) = local.init; - if local.els.is_none(); - if local.ty.is_none(); - if init.span.ctxt() == stmt.span.ctxt(); - if let Some(if_let_or_match) = IfLetOrMatch::parse(cx, init); - then { - if_let_or_match - } else { - return; - } - }; + if !self.msrv.meets(msrvs::LET_ELSE) || in_external_macro(cx.sess(), stmt.span) { + return; + } + if let StmtKind::Local(local) = stmt.kind && + let Some(init) = local.init && + local.els.is_none() && + local.ty.is_none() && + init.span.ctxt() == stmt.span.ctxt() && + let Some(if_let_or_match) = IfLetOrMatch::parse(cx, init) { match if_let_or_match { IfLetOrMatch::IfLet(if_let_expr, let_pat, if_then, if_else) => if_chain! { if expr_is_simple_identity(let_pat, if_then); if let Some(if_else) = if_else; if expr_diverges(cx, if_else); then { - emit_manual_let_else(cx, stmt.span, if_let_expr, let_pat, if_else); + emit_manual_let_else(cx, stmt.span, if_let_expr, local.pat, let_pat, if_else); } }, IfLetOrMatch::Match(match_expr, arms, source) => { @@ -128,15 +121,23 @@ impl<'tcx> LateLintPass<'tcx> for ManualLetElse { return; } - emit_manual_let_else(cx, stmt.span, match_expr, pat_arm.pat, diverging_arm.body); + emit_manual_let_else(cx, stmt.span, match_expr, local.pat, pat_arm.pat, diverging_arm.body); }, } + }; } extract_msrv_attr!(LateContext); } -fn emit_manual_let_else(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, pat: &Pat<'_>, else_body: &Expr<'_>) { +fn emit_manual_let_else( + cx: &LateContext<'_>, + span: Span, + expr: &Expr<'_>, + local: &Pat<'_>, + pat: &Pat<'_>, + else_body: &Expr<'_>, +) { span_lint_and_then( cx, MANUAL_LET_ELSE, @@ -145,12 +146,11 @@ fn emit_manual_let_else(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, pat: |diag| { // This is far from perfect, for example there needs to be: // * mut additions for the bindings - // * renamings of the bindings + // * renamings of the bindings for `PatKind::Or` // * unused binding collision detection with existing ones // * putting patterns with at the top level | inside () // for this to be machine applicable. let mut app = Applicability::HasPlaceholders; - let (sn_pat, _) = snippet_with_context(cx, pat.span, span.ctxt(), "", &mut app); let (sn_expr, _) = snippet_with_context(cx, expr.span, span.ctxt(), "", &mut app); let (sn_else, _) = snippet_with_context(cx, else_body.span, span.ctxt(), "", &mut app); @@ -159,10 +159,21 @@ fn emit_manual_let_else(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, pat: } else { format!("{{ {sn_else} }}") }; - let sn_bl = if matches!(pat.kind, PatKind::Or(..)) { - format!("({sn_pat})") - } else { - sn_pat.into_owned() + let sn_bl = match pat.kind { + PatKind::Or(..) => { + let (sn_pat, _) = snippet_with_context(cx, pat.span, span.ctxt(), "", &mut app); + format!("({sn_pat})") + }, + // Replace the variable name iff `TupleStruct` has one argument like `Variant(v)`. + PatKind::TupleStruct(ref w, args, ..) if args.len() == 1 => { + let sn_wrapper = cx.sess().source_map().span_to_snippet(w.span()).unwrap_or_default(); + let (sn_inner, _) = snippet_with_context(cx, local.span, span.ctxt(), "", &mut app); + format!("{sn_wrapper}({sn_inner})") + }, + _ => { + let (sn_pat, _) = snippet_with_context(cx, pat.span, span.ctxt(), "", &mut app); + sn_pat.into_owned() + }, }; let sugg = format!("let {sn_bl} = {sn_expr} else {else_bl};"); diag.span_suggestion(span, "consider writing", sugg, app); diff --git a/src/tools/clippy/clippy_lints/src/manual_strip.rs b/src/tools/clippy/clippy_lints/src/manual_strip.rs index 7d28c111624..93d977a5c96 100644 --- a/src/tools/clippy/clippy_lints/src/manual_strip.rs +++ b/src/tools/clippy/clippy_lints/src/manual_strip.rs @@ -144,7 +144,7 @@ fn len_arg<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx E // Returns the length of the `expr` if it's a constant string or char. fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> { - let (value, _) = constant(cx, cx.typeck_results(), expr)?; + let value = constant(cx, cx.typeck_results(), expr)?; match value { Constant::Str(value) => Some(value.len() as u128), Constant::Char(value) => Some(value.len_utf8() as u128), diff --git a/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs b/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs index 33bc20dad6b..0064619ef89 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs @@ -1,10 +1,12 @@ +use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_lint_allowed; use clippy_utils::is_wild; use clippy_utils::source::snippet_with_applicability; use clippy_utils::span_contains_comment; use rustc_ast::{Attribute, LitKind}; use rustc_errors::Applicability; -use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Guard, Pat}; +use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Guard, Pat, PatKind, QPath}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty; use rustc_span::source_map::Spanned; @@ -99,6 +101,14 @@ where } } + for arm in iter_without_last.clone() { + if let Some(pat) = arm.1 { + if !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, pat.hir_id) && is_some(pat.kind) { + return false; + } + } + } + // The suggestion may be incorrect, because some arms can have `cfg` attributes // evaluated into `false` and so such arms will be stripped before. let mut applicability = Applicability::MaybeIncorrect; @@ -170,3 +180,13 @@ fn find_bool_lit(ex: &ExprKind<'_>) -> Option<bool> { _ => None, } } + +fn is_some(path_kind: PatKind<'_>) -> bool { + match path_kind { + PatKind::TupleStruct(QPath::Resolved(_, path), [first, ..], _) if is_wild(first) => { + let name = path.segments[0].ident; + name.name == rustc_span::sym::Some + }, + _ => false, + } +} diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs index a48f4c77f85..ae8262ace96 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs @@ -282,9 +282,8 @@ impl<'a> NormalizedPat<'a> { // TODO: Handle negative integers. They're currently treated as a wild match. ExprKind::Lit(lit) => match lit.node { LitKind::Str(sym, _) => Self::LitStr(sym), - LitKind::ByteStr(ref bytes, _) => Self::LitBytes(bytes), + LitKind::ByteStr(ref bytes, _) | LitKind::CStr(ref bytes, _) => Self::LitBytes(bytes), LitKind::Byte(val) => Self::LitInt(val.into()), - LitKind::CStr(ref bytes, _) => Self::LitBytes(bytes), LitKind::Char(val) => Self::LitInt(val.into()), LitKind::Int(val, _) => Self::LitInt(val), LitKind::Bool(val) => Self::LitBool(val), diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs index 87b63eead25..55ec9d4474f 100644 --- a/src/tools/clippy/clippy_lints/src/matches/mod.rs +++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs @@ -25,9 +25,9 @@ mod wild_in_or_pats; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{snippet_opt, walk_span_to_context}; -use clippy_utils::{higher, in_constant, is_span_match}; +use clippy_utils::{higher, in_constant, is_span_match, tokenize_with_text}; use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat}; -use rustc_lexer::{tokenize, TokenKind}; +use rustc_lexer::TokenKind; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -1147,12 +1147,7 @@ fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool { // Assume true. This would require either an invalid span, or one which crosses file boundaries. return true; }; - let mut pos = 0usize; - let mut iter = tokenize(&snip).map(|t| { - let start = pos; - pos += t.len as usize; - (t.kind, start..pos) - }); + let mut iter = tokenize_with_text(&snip); // Search for the token sequence [`#`, `[`, `cfg`] while iter.any(|(t, _)| matches!(t, TokenKind::Pound)) { @@ -1163,7 +1158,7 @@ fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool { ) }); if matches!(iter.next(), Some((TokenKind::OpenBracket, _))) - && matches!(iter.next(), Some((TokenKind::Ident, range)) if &snip[range.clone()] == "cfg") + && matches!(iter.next(), Some((TokenKind::Ident, "cfg"))) { return true; } diff --git a/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs b/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs index ae69ca8a339..abf2525a61c 100644 --- a/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs @@ -34,7 +34,7 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) if let Arm { pat, guard: None, .. } = *arm { if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind { let lhs_const = match lhs { - Some(lhs) => constant(cx, cx.typeck_results(), lhs)?.0, + Some(lhs) => constant(cx, cx.typeck_results(), lhs)?, None => { let min_val_const = ty.numeric_min_val(cx.tcx)?; let min_constant = mir::ConstantKind::from_value( @@ -45,7 +45,7 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) }, }; let rhs_const = match rhs { - Some(rhs) => constant(cx, cx.typeck_results(), rhs)?.0, + Some(rhs) => constant(cx, cx.typeck_results(), rhs)?, None => { let max_val_const = ty.numeric_max_val(cx.tcx)?; let max_constant = mir::ConstantKind::from_value( diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs index 0809837d1fd..e81e09da425 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs @@ -189,73 +189,7 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op if arms.len() == 2 { let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind); - let found_good_method = match node_pair { - ( - PatKind::TupleStruct(ref path_left, patterns_left, _), - PatKind::TupleStruct(ref path_right, patterns_right, _), - ) if patterns_left.len() == 1 && patterns_right.len() == 1 => { - if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) { - find_good_method_for_match( - cx, - arms, - path_left, - path_right, - Item::Lang(ResultOk), - Item::Lang(ResultErr), - "is_ok()", - "is_err()", - ) - .or_else(|| { - find_good_method_for_match( - cx, - arms, - path_left, - path_right, - Item::Diag(sym::IpAddr, sym!(V4)), - Item::Diag(sym::IpAddr, sym!(V6)), - "is_ipv4()", - "is_ipv6()", - ) - }) - } else { - None - } - }, - (PatKind::TupleStruct(ref path_left, patterns, _), PatKind::Path(ref path_right)) - | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, patterns, _)) - if patterns.len() == 1 => - { - if let PatKind::Wild = patterns[0].kind { - find_good_method_for_match( - cx, - arms, - path_left, - path_right, - Item::Lang(OptionSome), - Item::Lang(OptionNone), - "is_some()", - "is_none()", - ) - .or_else(|| { - find_good_method_for_match( - cx, - arms, - path_left, - path_right, - Item::Lang(PollReady), - Item::Lang(PollPending), - "is_ready()", - "is_pending()", - ) - }) - } else { - None - } - }, - _ => None, - }; - - if let Some(good_method) = found_good_method { + if let Some(good_method) = found_good_method(cx, arms, node_pair) { let span = expr.span.to(op.span); let result_expr = match &op.kind { ExprKind::AddrOf(_, _, borrowed) => borrowed, @@ -279,6 +213,127 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op } } +fn found_good_method<'a>( + cx: &LateContext<'_>, + arms: &[Arm<'_>], + node: (&PatKind<'_>, &PatKind<'_>), +) -> Option<&'a str> { + match node { + ( + PatKind::TupleStruct(ref path_left, patterns_left, _), + PatKind::TupleStruct(ref path_right, patterns_right, _), + ) if patterns_left.len() == 1 && patterns_right.len() == 1 => { + if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) { + find_good_method_for_match( + cx, + arms, + path_left, + path_right, + Item::Lang(ResultOk), + Item::Lang(ResultErr), + "is_ok()", + "is_err()", + ) + .or_else(|| { + find_good_method_for_match( + cx, + arms, + path_left, + path_right, + Item::Diag(sym::IpAddr, sym!(V4)), + Item::Diag(sym::IpAddr, sym!(V6)), + "is_ipv4()", + "is_ipv6()", + ) + }) + } else { + None + } + }, + (PatKind::TupleStruct(ref path_left, patterns, _), PatKind::Path(ref path_right)) + | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, patterns, _)) + if patterns.len() == 1 => + { + if let PatKind::Wild = patterns[0].kind { + find_good_method_for_match( + cx, + arms, + path_left, + path_right, + Item::Lang(OptionSome), + Item::Lang(OptionNone), + "is_some()", + "is_none()", + ) + .or_else(|| { + find_good_method_for_match( + cx, + arms, + path_left, + path_right, + Item::Lang(PollReady), + Item::Lang(PollPending), + "is_ready()", + "is_pending()", + ) + }) + } else { + None + } + }, + (PatKind::TupleStruct(ref path_left, patterns, _), PatKind::Wild) if patterns.len() == 1 => { + if let PatKind::Wild = patterns[0].kind { + get_good_method(cx, arms, path_left) + } else { + None + } + }, + (PatKind::Path(ref path_left), PatKind::Wild) => get_good_method(cx, arms, path_left), + _ => None, + } +} + +fn get_ident(path: &QPath<'_>) -> Option<rustc_span::symbol::Ident> { + match path { + QPath::Resolved(_, path) => { + let name = path.segments[0].ident; + Some(name) + }, + _ => None, + } +} + +fn get_good_method<'a>(cx: &LateContext<'_>, arms: &[Arm<'_>], path_left: &QPath<'_>) -> Option<&'a str> { + if let Some(name) = get_ident(path_left) { + return match name.as_str() { + "Ok" => { + find_good_method_for_matches_macro(cx, arms, path_left, Item::Lang(ResultOk), "is_ok()", "is_err()") + }, + "Err" => { + find_good_method_for_matches_macro(cx, arms, path_left, Item::Lang(ResultErr), "is_err()", "is_ok()") + }, + "Some" => find_good_method_for_matches_macro( + cx, + arms, + path_left, + Item::Lang(OptionSome), + "is_some()", + "is_none()", + ), + "None" => find_good_method_for_matches_macro( + cx, + arms, + path_left, + Item::Lang(OptionNone), + "is_none()", + "is_some()", + ), + _ => None, + }; + } + None +} + #[derive(Clone, Copy)] enum Item { Lang(LangItem), @@ -346,3 +401,29 @@ fn find_good_method_for_match<'a>( _ => None, } } + +fn find_good_method_for_matches_macro<'a>( + cx: &LateContext<'_>, + arms: &[Arm<'_>], + path_left: &QPath<'_>, + expected_item_left: Item, + should_be_left: &'a str, + should_be_right: &'a str, +) -> Option<&'a str> { + let first_pat = arms[0].pat; + + let body_node_pair = if is_pat_variant(cx, first_pat, path_left, expected_item_left) { + (&arms[0].body.kind, &arms[1].body.kind) + } else { + return None; + }; + + match body_node_pair { + (ExprKind::Lit(lit_left), ExprKind::Lit(lit_right)) => match (&lit_left.node, &lit_right.node) { + (LitKind::Bool(true), LitKind::Bool(false)) => Some(should_be_left), + (LitKind::Bool(false), LitKind::Bool(true)) => Some(should_be_right), + _ => None, + }, + _ => None, + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_nth_zero.rs b/src/tools/clippy/clippy_lints/src/methods/iter_nth_zero.rs index c830958d5c8..d1609eebfdc 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_nth_zero.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_nth_zero.rs @@ -13,7 +13,7 @@ use super::ITER_NTH_ZERO; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { if_chain! { if is_trait_method(cx, expr, sym::Iterator); - if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), arg); + if let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), arg); then { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/methods/iterator_step_by_zero.rs b/src/tools/clippy/clippy_lints/src/methods/iterator_step_by_zero.rs index 64c09214a76..b631cd00cda 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iterator_step_by_zero.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iterator_step_by_zero.rs @@ -9,7 +9,7 @@ use super::ITERATOR_STEP_BY_ZERO; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) { if is_trait_method(cx, expr, sym::Iterator) { - if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), arg) { + if let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), arg) { span_lint( cx, ITERATOR_STEP_BY_ZERO, diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_next_back.rs b/src/tools/clippy/clippy_lints/src/methods/manual_next_back.rs new file mode 100644 index 00000000000..5f3fec53827 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/manual_next_back.rs @@ -0,0 +1,38 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_trait_method; +use clippy_utils::ty::implements_trait; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_span::symbol::sym; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + rev_call: &'tcx Expr<'_>, + rev_recv: &'tcx Expr<'_>, +) { + let rev_recv_ty = cx.typeck_results().expr_ty(rev_recv); + + // check that the receiver of `rev` implements `DoubleEndedIterator` and + // that `rev` and `next` come from `Iterator` + if cx + .tcx + .get_diagnostic_item(sym::DoubleEndedIterator) + .map_or(false, |double_ended_iterator| { + implements_trait(cx, rev_recv_ty, double_ended_iterator, &[]) + }) + && is_trait_method(cx, rev_call, sym::Iterator) + && is_trait_method(cx, expr, sym::Iterator) + { + span_lint_and_sugg( + cx, + super::MANUAL_NEXT_BACK, + expr.span.with_lo(rev_recv.span.hi()), + "manual backwards iteration", + "use", + String::from(".next_back()"), + Applicability::MachineApplicable, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 06b88e34d24..9a594d964ab 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -45,6 +45,7 @@ mod iter_overeager_cloned; mod iter_skip_next; mod iter_with_drain; mod iterator_step_by_zero; +mod manual_next_back; mod manual_ok_or; mod manual_saturating_arithmetic; mod manual_str_repeat; @@ -3132,8 +3133,11 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # let iterator = vec![1].into_iter(); - /// let len = iterator.clone().collect::<Vec<_>>().len(); - /// // should be + /// let len = iterator.collect::<Vec<_>>().len(); + /// ``` + /// Use instead: + /// ```rust + /// # let iterator = vec![1].into_iter(); /// let len = iterator.count(); /// ``` #[clippy::version = "1.30.0"] @@ -3193,6 +3197,29 @@ declare_clippy_lint! { "calling `drain` in order to `clear` a container" } +declare_clippy_lint! { + /// ### What it does + /// Checks for `.rev().next()` on a `DoubleEndedIterator` + /// + /// ### Why is this bad? + /// `.next_back()` is cleaner. + /// + /// ### Example + /// ```rust + /// # let foo = [0; 10]; + /// foo.iter().rev().next(); + /// ``` + /// Use instead: + /// ```rust + /// # let foo = [0; 10]; + /// foo.iter().next_back(); + /// ``` + #[clippy::version = "1.71.0"] + pub MANUAL_NEXT_BACK, + style, + "manual reverse iteration of `DoubleEndedIterator`" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -3321,6 +3348,7 @@ impl_lint_pass!(Methods => [ NEEDLESS_COLLECT, SUSPICIOUS_COMMAND_ARG_SPACE, CLEAR_WITH_DRAIN, + MANUAL_NEXT_BACK, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -3677,6 +3705,7 @@ impl Methods { ("iter", []) => iter_next_slice::check(cx, expr, recv2), ("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg), ("skip_while", [_]) => skip_while_next::check(cx, expr), + ("rev", [])=> manual_next_back::check(cx, expr, recv, recv2), _ => {}, } } @@ -3741,13 +3770,13 @@ impl Methods { unnecessary_sort_by::check(cx, expr, recv, arg, true); }, ("splitn" | "rsplitn", [count_arg, pat_arg]) => { - if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) { + if let Some(Constant::Int(count)) = constant(cx, cx.typeck_results(), count_arg) { suspicious_splitn::check(cx, name, expr, recv, count); str_splitn::check(cx, name, expr, recv, pat_arg, count, &self.msrv); } }, ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => { - if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) { + if let Some(Constant::Int(count)) = constant(cx, cx.typeck_results(), count_arg) { suspicious_splitn::check(cx, name, expr, recv, count); } }, diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs index 0b0c6adc504..6841aaf626c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs @@ -1,6 +1,5 @@ use super::NEEDLESS_COLLECT; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; -use clippy_utils::higher; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, make_normalized_projection, make_projection}; @@ -8,6 +7,7 @@ use clippy_utils::{ can_move_expr_to_closure, get_enclosing_block, get_parent_node, is_trait_method, path_to_local, path_to_local_id, CaptureKind, }; +use clippy_utils::{fn_def_id, higher}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::intravisit::{walk_block, walk_expr, Visitor}; @@ -16,7 +16,7 @@ use rustc_hir::{ }; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::{self, AssocKind, EarlyBinder, GenericArg, GenericArgKind, Ty}; +use rustc_middle::ty::{self, AssocKind, Clause, EarlyBinder, GenericArg, GenericArgKind, PredicateKind, Ty}; use rustc_span::symbol::Ident; use rustc_span::{sym, Span, Symbol}; @@ -32,6 +32,8 @@ pub(super) fn check<'tcx>( if let Some(parent) = get_parent_node(cx.tcx, collect_expr.hir_id) { match parent { Node::Expr(parent) => { + check_collect_into_intoiterator(cx, parent, collect_expr, call_span, iter_expr); + if let ExprKind::MethodCall(name, _, args @ ([] | [_]), _) = parent.kind { let mut app = Applicability::MachineApplicable; let name = name.ident.as_str(); @@ -134,6 +136,68 @@ pub(super) fn check<'tcx>( } } +/// checks for for collecting into a (generic) method or function argument +/// taking an `IntoIterator` +fn check_collect_into_intoiterator<'tcx>( + cx: &LateContext<'tcx>, + parent: &'tcx Expr<'tcx>, + collect_expr: &'tcx Expr<'tcx>, + call_span: Span, + iter_expr: &'tcx Expr<'tcx>, +) { + if let Some(id) = fn_def_id(cx, parent) { + let args = match parent.kind { + ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) => args, + _ => &[], + }; + // find the argument index of the `collect_expr` in the + // function / method call + if let Some(arg_idx) = args.iter().position(|e| e.hir_id == collect_expr.hir_id).map(|i| { + if matches!(parent.kind, ExprKind::MethodCall(_, _, _, _)) { + i + 1 + } else { + i + } + }) { + // extract the input types of the function/method call + // that contains `collect_expr` + let inputs = cx + .tcx + .liberate_late_bound_regions(id, cx.tcx.fn_sig(id).subst_identity()) + .inputs(); + + // map IntoIterator generic bounds to their signature + // types and check whether the argument type is an + // `IntoIterator` + if cx + .tcx + .param_env(id) + .caller_bounds() + .into_iter() + .filter_map(|p| { + if let PredicateKind::Clause(Clause::Trait(t)) = p.kind().skip_binder() + && cx.tcx.is_diagnostic_item(sym::IntoIterator,t.trait_ref.def_id) { + Some(t.self_ty()) + } else { + None + } + }) + .any(|ty| ty == inputs[arg_idx]) + { + span_lint_and_sugg( + cx, + NEEDLESS_COLLECT, + call_span.with_lo(iter_expr.span.hi()), + NEEDLESS_COLLECT_MSG, + "remove this call", + String::new(), + Applicability::MachineApplicable, + ); + } + } + } +} + /// Checks if the given method call matches the expected signature of `([&[mut]] self) -> bool` fn is_is_empty_sig(cx: &LateContext<'_>, call_id: HirId) -> bool { cx.typeck_results().type_dependent_def_id(call_id).map_or(false, |id| { diff --git a/src/tools/clippy/clippy_lints/src/methods/repeat_once.rs b/src/tools/clippy/clippy_lints/src/methods/repeat_once.rs index a345ec813ff..bb4cdd2a6fa 100644 --- a/src/tools/clippy/clippy_lints/src/methods/repeat_once.rs +++ b/src/tools/clippy/clippy_lints/src/methods/repeat_once.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant_context, Constant}; +use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_lang_item; @@ -14,7 +14,7 @@ pub(super) fn check<'tcx>( recv: &'tcx Expr<'_>, repeat_arg: &'tcx Expr<'_>, ) { - if constant_context(cx, cx.typeck_results()).expr(repeat_arg) == Some(Constant::Int(1)) { + if constant(cx, cx.typeck_results(), repeat_arg) == Some(Constant::Int(1)) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); if ty.is_str() { span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs index 91f7ce1dbe5..5ea12c44184 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs @@ -316,7 +316,7 @@ fn parse_iter_usage<'tcx>( }; }, ("nth" | "skip", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => { - if let Some((Constant::Int(idx), _)) = constant(cx, cx.typeck_results(), idx_expr) { + if let Some(Constant::Int(idx)) = constant(cx, cx.typeck_results(), idx_expr) { let span = if name.ident.as_str() == "nth" { e.span } else { diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs index 3752b9a946f..303f0125690 100644 --- a/src/tools/clippy/clippy_lints/src/misc.rs +++ b/src/tools/clippy/clippy_lints/src/misc.rs @@ -16,9 +16,12 @@ use rustc_span::source_map::{ExpnKind, Span}; use clippy_utils::sugg::Sugg; use clippy_utils::{ - get_parent_expr, in_constant, is_integer_literal, is_no_std_crate, iter_input_pats, last_path_segment, SpanlessEq, + get_parent_expr, in_constant, is_integer_literal, is_lint_allowed, is_no_std_crate, iter_input_pats, + last_path_segment, SpanlessEq, }; +use crate::ref_patterns::REF_PATTERNS; + declare_clippy_lint! { /// ### What it does /// Checks for function arguments and let bindings denoted as @@ -162,6 +165,10 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { return; } for arg in iter_input_pats(decl, body) { + // Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue. + if !is_lint_allowed(cx, REF_PATTERNS, arg.pat.hir_id) { + return; + } if let PatKind::Binding(BindingAnnotation(ByRef::Yes, _), ..) = arg.pat.kind { span_lint( cx, @@ -180,6 +187,8 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { if let StmtKind::Local(local) = stmt.kind; if let PatKind::Binding(BindingAnnotation(ByRef::Yes, mutabl), .., name, None) = local.pat.kind; if let Some(init) = local.init; + // Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue. + if is_lint_allowed(cx, REF_PATTERNS, local.pat.hir_id); then { let ctxt = local.span.ctxt(); let mut app = Applicability::MachineApplicable; diff --git a/src/tools/clippy/clippy_lints/src/needless_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bool.rs index 71281a0b40b..62af42a3961 100644 --- a/src/tools/clippy/clippy_lints/src/needless_bool.rs +++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs @@ -146,7 +146,7 @@ fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool { impl<'tcx> LateLintPass<'tcx> for NeedlessBool { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { use self::Expression::{Bool, RetBool}; - if e.span.from_expansion() { + if e.span.from_expansion() || !span_extract_comment(cx.tcx.sess.source_map(), e.span).is_empty() { return; } if let Some(higher::If { @@ -209,8 +209,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { } if let Some((lhs_a, a)) = fetch_assign(then) && let Some((lhs_b, b)) = fetch_assign(r#else) && - SpanlessEq::new(cx).eq_expr(lhs_a, lhs_b) && - span_extract_comment(cx.tcx.sess.source_map(), e.span).is_empty() + SpanlessEq::new(cx).eq_expr(lhs_a, lhs_b) { let mut applicability = Applicability::MachineApplicable; let cond = Sugg::hir_with_applicability(cx, cond, "..", &mut applicability); diff --git a/src/tools/clippy/clippy_lints/src/operators/absurd_extreme_comparisons.rs b/src/tools/clippy/clippy_lints/src/operators/absurd_extreme_comparisons.rs index d29ca37eaeb..f4863600ccc 100644 --- a/src/tools/clippy/clippy_lints/src/operators/absurd_extreme_comparisons.rs +++ b/src/tools/clippy/clippy_lints/src/operators/absurd_extreme_comparisons.rs @@ -121,7 +121,7 @@ fn detect_absurd_comparison<'tcx>( fn detect_extreme_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<ExtremeExpr<'tcx>> { let ty = cx.typeck_results().expr_ty(expr); - let cv = constant(cx, cx.typeck_results(), expr)?.0; + let cv = constant(cx, cx.typeck_results(), expr)?; let which = match (ty.kind(), cv) { (&ty::Bool, Constant::Bool(false)) | (&ty::Uint(_), Constant::Int(0)) => ExtremeType::Minimum, diff --git a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs index f72595987ee..5c240276b76 100644 --- a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -21,7 +21,7 @@ const HARD_CODED_ALLOWED_BINARY: &[[&str; 2]] = &[ ["f64", "f64"], ["std::num::Saturating", "std::num::Saturating"], ["std::num::Wrapping", "std::num::Wrapping"], - ["std::string::String", "&str"], + ["std::string::String", "str"], ]; const HARD_CODED_ALLOWED_UNARY: &[&str] = &["f32", "f64", "std::num::Saturating", "std::num::Wrapping"]; const INTEGER_METHODS: &[&str] = &["saturating_div", "wrapping_div", "wrapping_rem", "wrapping_rem_euclid"]; @@ -113,7 +113,7 @@ impl ArithmeticSideEffects { if let hir::ExprKind::Lit(lit) = actual.kind && let ast::LitKind::Int(n, _) = lit.node { return Some(n) } - if let Some((Constant::Int(n), _)) = constant(cx, cx.typeck_results(), expr) { + if let Some(Constant::Int(n)) = constant(cx, cx.typeck_results(), expr) { return Some(n); } None @@ -144,8 +144,10 @@ impl ArithmeticSideEffects { ) { return; }; - let lhs_ty = cx.typeck_results().expr_ty(lhs); - let rhs_ty = cx.typeck_results().expr_ty(rhs); + let (actual_lhs, lhs_ref_counter) = peel_hir_expr_refs(lhs); + let (actual_rhs, rhs_ref_counter) = peel_hir_expr_refs(rhs); + let lhs_ty = cx.typeck_results().expr_ty(actual_lhs).peel_refs(); + let rhs_ty = cx.typeck_results().expr_ty(actual_rhs).peel_refs(); if self.has_allowed_binary(lhs_ty, rhs_ty) { return; } @@ -154,8 +156,6 @@ impl ArithmeticSideEffects { // At least for integers, shifts are already handled by the CTFE return; } - let (actual_lhs, lhs_ref_counter) = peel_hir_expr_refs(lhs); - let (actual_rhs, rhs_ref_counter) = peel_hir_expr_refs(rhs); match ( Self::literal_integer(cx, actual_lhs), Self::literal_integer(cx, actual_rhs), diff --git a/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs b/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs index 1369b3e7462..1fddf0f50e3 100644 --- a/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs +++ b/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs @@ -166,7 +166,7 @@ fn check_ineffective_gt(cx: &LateContext<'_>, span: Span, m: u128, c: u128, op: } fn fetch_int_literal(cx: &LateContext<'_>, lit: &Expr<'_>) -> Option<u128> { - match constant(cx, cx.typeck_results(), lit)?.0 { + match constant(cx, cx.typeck_results(), lit)? { Constant::Int(n) => Some(n), _ => None, } diff --git a/src/tools/clippy/clippy_lints/src/operators/cmp_nan.rs b/src/tools/clippy/clippy_lints/src/operators/cmp_nan.rs index 786ae1552ad..e18064b7061 100644 --- a/src/tools/clippy/clippy_lints/src/operators/cmp_nan.rs +++ b/src/tools/clippy/clippy_lints/src/operators/cmp_nan.rs @@ -18,7 +18,7 @@ pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, op: BinOpKind, lhs: &Exp } fn is_nan(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - if let Some((value, _)) = constant(cx, cx.typeck_results(), e) { + if let Some(value) = constant(cx, cx.typeck_results(), e) { match value { Constant::F32(num) => num.is_nan(), Constant::F64(num) => num.is_nan(), diff --git a/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs b/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs index 49e662cacb0..f120be13836 100644 --- a/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs +++ b/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs @@ -19,7 +19,7 @@ pub(crate) fn check<'tcx>( if op == BinOpKind::Div && let ExprKind::MethodCall(method_path, self_arg, [], _) = left.kind && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_arg).peel_refs(), sym::Duration) - && let Some((Constant::Int(divisor), _)) = constant(cx, cx.typeck_results(), right) + && let Some(Constant::Int(divisor)) = constant(cx, cx.typeck_results(), right) { let suggested_fn = match (method_path.ident.as_str(), divisor) { ("subsec_micros", 1_000) | ("subsec_nanos", 1_000_000) => "subsec_millis", diff --git a/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs index 97ddcdb2479..15dff126be7 100644 --- a/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{constant_with_source, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_item_name; use clippy_utils::sugg::Sugg; @@ -18,9 +18,16 @@ pub(crate) fn check<'tcx>( right: &'tcx Expr<'_>, ) { if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) { - if is_allowed(cx, left) || is_allowed(cx, right) { - return; - } + let left_is_local = match constant_with_source(cx, cx.typeck_results(), left) { + Some((c, s)) if !is_allowed(&c) => s.is_local(), + Some(_) => return, + None => true, + }; + let right_is_local = match constant_with_source(cx, cx.typeck_results(), right) { + Some((c, s)) if !is_allowed(&c) => s.is_local(), + Some(_) => return, + None => true, + }; // Allow comparing the results of signum() if is_signum(cx, left) && is_signum(cx, right) { @@ -34,10 +41,7 @@ pub(crate) fn check<'tcx>( } } let is_comparing_arrays = is_array(cx, left) || is_array(cx, right); - let (lint, msg) = get_lint_and_message( - is_named_constant(cx, left) || is_named_constant(cx, right), - is_comparing_arrays, - ); + let (lint, msg) = get_lint_and_message(left_is_local && right_is_local, is_comparing_arrays); span_lint_and_then(cx, lint, expr.span, msg, |diag| { let lhs = Sugg::hir(cx, left, ".."); let rhs = Sugg::hir(cx, right, ".."); @@ -59,44 +63,33 @@ pub(crate) fn check<'tcx>( } } -fn get_lint_and_message( - is_comparing_constants: bool, - is_comparing_arrays: bool, -) -> (&'static rustc_lint::Lint, &'static str) { - if is_comparing_constants { +fn get_lint_and_message(is_local: bool, is_comparing_arrays: bool) -> (&'static rustc_lint::Lint, &'static str) { + if is_local { ( - FLOAT_CMP_CONST, + FLOAT_CMP, if is_comparing_arrays { - "strict comparison of `f32` or `f64` constant arrays" + "strict comparison of `f32` or `f64` arrays" } else { - "strict comparison of `f32` or `f64` constant" + "strict comparison of `f32` or `f64`" }, ) } else { ( - FLOAT_CMP, + FLOAT_CMP_CONST, if is_comparing_arrays { - "strict comparison of `f32` or `f64` arrays" + "strict comparison of `f32` or `f64` constant arrays" } else { - "strict comparison of `f32` or `f64`" + "strict comparison of `f32` or `f64` constant" }, ) } } -fn is_named_constant<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { - if let Some((_, res)) = constant(cx, cx.typeck_results(), expr) { - res - } else { - false - } -} - -fn is_allowed<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { - match constant(cx, cx.typeck_results(), expr) { - Some((Constant::F32(f), _)) => f == 0.0 || f.is_infinite(), - Some((Constant::F64(f), _)) => f == 0.0 || f.is_infinite(), - Some((Constant::Vec(vec), _)) => vec.iter().all(|f| match f { +fn is_allowed(val: &Constant) -> bool { + match val { + &Constant::F32(f) => f == 0.0 || f.is_infinite(), + &Constant::F64(f) => f == 0.0 || f.is_infinite(), + Constant::Vec(vec) => vec.iter().all(|f| match f { Constant::F32(f) => *f == 0.0 || (*f).is_infinite(), Constant::F64(f) => *f == 0.0 || (*f).is_infinite(), _ => false, diff --git a/src/tools/clippy/clippy_lints/src/operators/mod.rs b/src/tools/clippy/clippy_lints/src/operators/mod.rs index 19599731bd6..d63a836e73d 100644 --- a/src/tools/clippy/clippy_lints/src/operators/mod.rs +++ b/src/tools/clippy/clippy_lints/src/operators/mod.rs @@ -98,32 +98,6 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for integer arithmetic operations which could overflow or panic. - /// - /// Specifically, checks for any operators (`+`, `-`, `*`, `<<`, etc) which are capable - /// of overflowing according to the [Rust - /// Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow), - /// or which can panic (`/`, `%`). No bounds analysis or sophisticated reasoning is - /// attempted. - /// - /// ### Why is this bad? - /// Integer overflow will trigger a panic in debug builds or will wrap in - /// release mode. Division by zero will cause a panic in either mode. In some applications one - /// wants explicitly checked, wrapping or saturating arithmetic. - /// - /// ### Example - /// ```rust - /// # let a = 0; - /// a + 1; - /// ``` - #[clippy::version = "pre 1.29.0"] - pub INTEGER_ARITHMETIC, - restriction, - "any integer arithmetic expression which could overflow or panic" -} - -declare_clippy_lint! { - /// ### What it does /// Checks for float arithmetic. /// /// ### Why is this bad? @@ -787,7 +761,6 @@ pub struct Operators { impl_lint_pass!(Operators => [ ABSURD_EXTREME_COMPARISONS, ARITHMETIC_SIDE_EFFECTS, - INTEGER_ARITHMETIC, FLOAT_ARITHMETIC, ASSIGN_OP_PATTERN, MISREFACTORED_ASSIGN_OP, diff --git a/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs b/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs index af4e74947f4..a2c3a4d8ba7 100644 --- a/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs @@ -40,7 +40,7 @@ struct OperandInfo { fn analyze_operand(operand: &Expr<'_>, cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<OperandInfo> { match constant(cx, cx.typeck_results(), operand) { - Some((Constant::Int(v), _)) => match *cx.typeck_results().expr_ty(expr).kind() { + Some(Constant::Int(v)) => match *cx.typeck_results().expr_ty(expr).kind() { ty::Int(ity) => { let value = sext(cx.tcx, v, ity); return Some(OperandInfo { @@ -58,10 +58,10 @@ fn analyze_operand(operand: &Expr<'_>, cx: &LateContext<'_>, expr: &Expr<'_>) -> }, _ => {}, }, - Some((Constant::F32(f), _)) => { + Some(Constant::F32(f)) => { return Some(floating_point_operand_info(&f)); }, - Some((Constant::F64(f), _)) => { + Some(Constant::F64(f)) => { return Some(floating_point_operand_info(&f)); }, _ => {}, diff --git a/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs b/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs index 77fd45b199a..102845ceed0 100644 --- a/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs @@ -1,8 +1,6 @@ -use super::{FLOAT_ARITHMETIC, INTEGER_ARITHMETIC}; +use super::FLOAT_ARITHMETIC; use clippy_utils::consts::constant_simple; use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_from_proc_macro; -use clippy_utils::is_integer_literal; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::source_map::Span; @@ -45,31 +43,8 @@ impl Context { _ => (), } - let (l_ty, r_ty) = (cx.typeck_results().expr_ty(l), cx.typeck_results().expr_ty(r)); - if l_ty.peel_refs().is_integral() && r_ty.peel_refs().is_integral() { - if is_from_proc_macro(cx, expr) { - return; - } - match op { - hir::BinOpKind::Div | hir::BinOpKind::Rem => match &r.kind { - hir::ExprKind::Lit(_lit) => (), - hir::ExprKind::Unary(hir::UnOp::Neg, expr) => { - if is_integer_literal(expr, 1) { - span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); - self.expr_id = Some(expr.hir_id); - } - }, - _ => { - span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); - self.expr_id = Some(expr.hir_id); - }, - }, - _ => { - span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); - self.expr_id = Some(expr.hir_id); - }, - } - } else if r_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() { + let (_, r_ty) = (cx.typeck_results().expr_ty(l), cx.typeck_results().expr_ty(r)); + if r_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() { span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected"); self.expr_id = Some(expr.hir_id); } @@ -80,17 +55,9 @@ impl Context { return; } let ty = cx.typeck_results().expr_ty(arg); - if constant_simple(cx, cx.typeck_results(), expr).is_none() { - if ty.is_integral() { - if is_from_proc_macro(cx, expr) { - return; - } - span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); - self.expr_id = Some(expr.hir_id); - } else if ty.is_floating_point() { - span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected"); - self.expr_id = Some(expr.hir_id); - } + if constant_simple(cx, cx.typeck_results(), expr).is_none() && ty.is_floating_point() { + span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected"); + self.expr_id = Some(expr.hir_id); } } diff --git a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs index bbbcda069c5..aa6d4004268 100644 --- a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs @@ -122,7 +122,7 @@ fn try_get_option_occurrence<'tcx>( ExprKind::Unary(UnOp::Deref, inner_expr) | ExprKind::AddrOf(_, _, inner_expr) => inner_expr, _ => expr, }; - let inner_pat = try_get_inner_pat(cx, pat)?; + let (inner_pat, is_result) = try_get_inner_pat_and_is_result(cx, pat)?; if_chain! { if let PatKind::Binding(bind_annotation, _, id, None) = inner_pat.kind; if let Some(some_captures) = can_move_expr_to_closure(cx, if_then); @@ -176,7 +176,7 @@ fn try_get_option_occurrence<'tcx>( ), none_expr: format!( "{}{}", - if method_sugg == "map_or" { "" } else { "|| " }, + if method_sugg == "map_or" { "" } else if is_result { "|_| " } else { "|| "}, Sugg::hir_with_context(cx, none_body, ctxt, "..", &mut app), ), }); @@ -186,11 +186,13 @@ fn try_get_option_occurrence<'tcx>( None } -fn try_get_inner_pat<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<&'tcx Pat<'tcx>> { +fn try_get_inner_pat_and_is_result<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<(&'tcx Pat<'tcx>, bool)> { if let PatKind::TupleStruct(ref qpath, [inner_pat], ..) = pat.kind { let res = cx.qpath_res(qpath, pat.hir_id); - if is_res_lang_ctor(cx, res, OptionSome) || is_res_lang_ctor(cx, res, ResultOk) { - return Some(inner_pat); + if is_res_lang_ctor(cx, res, OptionSome) { + return Some((inner_pat, false)); + } else if is_res_lang_ctor(cx, res, ResultOk) { + return Some((inner_pat, true)); } } None diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs index fc655fe2d0b..dd7ded491e7 100644 --- a/src/tools/clippy/clippy_lints/src/ranges.rs +++ b/src/tools/clippy/clippy_lints/src/ranges.rs @@ -319,7 +319,7 @@ fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option<R _ => return None, }; if let Some(id) = path_to_local(l) { - if let Some((c, _)) = constant(cx, cx.typeck_results(), r) { + if let Some(c) = constant(cx, cx.typeck_results(), r) { return Some(RangeBounds { val: c, expr: r, @@ -331,7 +331,7 @@ fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option<R }); } } else if let Some(id) = path_to_local(r) { - if let Some((c, _)) = constant(cx, cx.typeck_results(), l) { + if let Some(c) = constant(cx, cx.typeck_results(), l) { return Some(RangeBounds { val: c, expr: l, @@ -451,8 +451,8 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { if let Some(higher::Range { start: Some(start), end: Some(end), limits }) = higher::Range::hir(expr); let ty = cx.typeck_results().expr_ty(start); if let ty::Int(_) | ty::Uint(_) = ty.kind(); - if let Some((start_idx, _)) = constant(cx, cx.typeck_results(), start); - if let Some((end_idx, _)) = constant(cx, cx.typeck_results(), end); + if let Some(start_idx) = constant(cx, cx.typeck_results(), start); + if let Some(end_idx) = constant(cx, cx.typeck_results(), end); if let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx); if is_empty_range(limits, ordering); then { diff --git a/src/tools/clippy/clippy_lints/src/ref_patterns.rs b/src/tools/clippy/clippy_lints/src/ref_patterns.rs new file mode 100644 index 00000000000..b1530eed1c1 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/ref_patterns.rs @@ -0,0 +1,44 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_ast::ast::{BindingAnnotation, Pat, PatKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for usages of the `ref` keyword. + /// ### Why is this bad? + /// The `ref` keyword can be confusing for people unfamiliar with it, and often + /// it is more concise to use `&` instead. + /// ### Example + /// ```rust + /// let opt = Some(5); + /// if let Some(ref foo) = opt {} + /// ``` + /// Use instead: + /// ```rust + /// let opt = Some(5); + /// if let Some(foo) = &opt {} + /// ``` + #[clippy::version = "1.71.0"] + pub REF_PATTERNS, + restriction, + "use of a ref pattern, e.g. Some(ref value)" +} +declare_lint_pass!(RefPatterns => [REF_PATTERNS]); + +impl EarlyLintPass for RefPatterns { + fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) { + if let PatKind::Ident(BindingAnnotation::REF, _, _) = pat.kind + && !pat.span.from_expansion() + { + span_lint_and_help( + cx, + REF_PATTERNS, + pat.span, + "usage of ref pattern", + None, + "consider using `&` for clarity instead", + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/regex.rs b/src/tools/clippy/clippy_lints/src/regex.rs index b8b32df6cc6..ef19c6f4617 100644 --- a/src/tools/clippy/clippy_lints/src/regex.rs +++ b/src/tools/clippy/clippy_lints/src/regex.rs @@ -122,37 +122,39 @@ fn lint_syntax_error(cx: &LateContext<'_>, error: ®ex_syntax::Error, unescape } fn const_str<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<String> { - constant(cx, cx.typeck_results(), e).and_then(|(c, _)| match c { + constant(cx, cx.typeck_results(), e).and_then(|c| match c { Constant::Str(s) => Some(s), _ => None, }) } fn is_trivial_regex(s: ®ex_syntax::hir::Hir) -> Option<&'static str> { - use regex_syntax::hir::Anchor::{EndText, StartText}; - use regex_syntax::hir::HirKind::{Alternation, Anchor, Concat, Empty, Literal}; + use regex_syntax::hir::HirKind::{Alternation, Concat, Empty, Literal, Look}; + use regex_syntax::hir::Look as HirLook; let is_literal = |e: &[regex_syntax::hir::Hir]| e.iter().all(|e| matches!(*e.kind(), Literal(_))); match *s.kind() { - Empty | Anchor(_) => Some("the regex is unlikely to be useful as it is"), + Empty | Look(_) => Some("the regex is unlikely to be useful as it is"), Literal(_) => Some("consider using `str::contains`"), Alternation(ref exprs) => { - if exprs.iter().all(|e| e.kind().is_empty()) { + if exprs.iter().all(|e| matches!(e.kind(), Empty)) { Some("the regex is unlikely to be useful as it is") } else { None } }, Concat(ref exprs) => match (exprs[0].kind(), exprs[exprs.len() - 1].kind()) { - (&Anchor(StartText), &Anchor(EndText)) if exprs[1..(exprs.len() - 1)].is_empty() => { + (&Look(HirLook::Start), &Look(HirLook::End)) if exprs[1..(exprs.len() - 1)].is_empty() => { Some("consider using `str::is_empty`") }, - (&Anchor(StartText), &Anchor(EndText)) if is_literal(&exprs[1..(exprs.len() - 1)]) => { + (&Look(HirLook::Start), &Look(HirLook::End)) if is_literal(&exprs[1..(exprs.len() - 1)]) => { Some("consider using `==` on `str`s") }, - (&Anchor(StartText), &Literal(_)) if is_literal(&exprs[1..]) => Some("consider using `str::starts_with`"), - (&Literal(_), &Anchor(EndText)) if is_literal(&exprs[1..(exprs.len() - 1)]) => { + (&Look(HirLook::Start), &Literal(_)) if is_literal(&exprs[1..]) => { + Some("consider using `str::starts_with`") + }, + (&Literal(_), &Look(HirLook::End)) if is_literal(&exprs[1..(exprs.len() - 1)]) => { Some("consider using `str::ends_with`") }, _ if is_literal(exprs) => Some("consider using `str::contains`"), @@ -175,10 +177,7 @@ fn check_set<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { } fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { - let mut parser = regex_syntax::ParserBuilder::new() - .unicode(true) - .allow_invalid_utf8(!utf8) - .build(); + let mut parser = regex_syntax::ParserBuilder::new().unicode(true).utf8(!utf8).build(); if let ExprKind::Lit(lit) = expr.kind { if let LitKind::Str(ref r, style) = lit.node { diff --git a/src/tools/clippy/clippy_lints/src/renamed_lints.rs b/src/tools/clippy/clippy_lints/src/renamed_lints.rs index 52e22c0c630..b0db56bb417 100644 --- a/src/tools/clippy/clippy_lints/src/renamed_lints.rs +++ b/src/tools/clippy/clippy_lints/src/renamed_lints.rs @@ -15,6 +15,7 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"), ("clippy::identity_conversion", "clippy::useless_conversion"), ("clippy::if_let_some_result", "clippy::match_result_ok"), + ("clippy::integer_arithmetic", "clippy::arithmetic_side_effects"), ("clippy::logic_bug", "clippy::overly_complex_bool_expr"), ("clippy::new_without_default_derive", "clippy::new_without_default"), ("clippy::option_and_then_some", "clippy::bind_instead_of_map"), @@ -32,13 +33,13 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::zero_width_space", "clippy::invisible_characters"), ("clippy::clone_double_ref", "suspicious_double_ref_op"), ("clippy::drop_bounds", "drop_bounds"), - ("clippy::drop_copy", "drop_copy"), - ("clippy::drop_ref", "drop_ref"), + ("clippy::drop_copy", "dropping_copy_types"), + ("clippy::drop_ref", "dropping_references"), ("clippy::for_loop_over_option", "for_loops_over_fallibles"), ("clippy::for_loop_over_result", "for_loops_over_fallibles"), ("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"), - ("clippy::forget_copy", "forget_copy"), - ("clippy::forget_ref", "forget_ref"), + ("clippy::forget_copy", "forgetting_copy_types"), + ("clippy::forget_ref", "forgetting_references"), ("clippy::into_iter_on_array", "array_into_iter"), ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"), ("clippy::invalid_ref", "invalid_value"), diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs index 5b588e914fd..483f860a8b5 100644 --- a/src/tools/clippy/clippy_lints/src/strings.rs +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -132,7 +132,7 @@ declare_clippy_lint! { /// Probably lots of false positives. If an index comes from a known valid position (e.g. /// obtained via `char_indices` over the same string), it is totally OK. /// - /// # Example + /// ### Example /// ```rust,should_panic /// &"Ölkanne"[1..]; /// ``` diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs index b5f11b4acae..4ccda15068b 100644 --- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs @@ -37,12 +37,12 @@ declare_clippy_lint! { #[clippy::version = "1.38.0"] pub TYPE_REPETITION_IN_BOUNDS, nursery, - "types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`" + "types are repeated unnecessarily in trait bounds, use `+` instead of using `T: _, T: _`" } declare_clippy_lint! { /// ### What it does - /// Checks for cases where generics are being used and multiple + /// Checks for cases where generics or trait objects are being used and multiple /// syntax specifications for trait bounds are used simultaneously. /// /// ### Why is this bad? @@ -167,6 +167,61 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { } } } + + fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) { + if_chain! { + if let TyKind::Ref(.., mut_ty) = &ty.kind; + if let TyKind::TraitObject(bounds, ..) = mut_ty.ty.kind; + if bounds.len() > 2; + then { + + // Build up a hash of every trait we've seen + // When we see a trait for the first time, add it to unique_traits + // so we can later use it to build a string of all traits exactly once, without duplicates + + let mut seen_def_ids = FxHashSet::default(); + let mut unique_traits = Vec::new(); + + // Iterate the bounds and add them to our seen hash + // If we haven't yet seen it, add it to the fixed traits + for bound in bounds.iter() { + let Some(def_id) = bound.trait_ref.trait_def_id() else { continue; }; + + let new_trait = seen_def_ids.insert(def_id); + + if new_trait { + unique_traits.push(bound); + } + } + + // If the number of unique traits isn't the same as the number of traits in the bounds, + // there must be 1 or more duplicates + if bounds.len() != unique_traits.len() { + let mut bounds_span = bounds[0].span; + + for bound in bounds.iter().skip(1) { + bounds_span = bounds_span.to(bound.span); + } + + let fixed_trait_snippet = unique_traits + .iter() + .filter_map(|b| snippet_opt(cx, b.span)) + .collect::<Vec<_>>() + .join(" + "); + + span_lint_and_sugg( + cx, + TRAIT_DUPLICATION_IN_BOUNDS, + bounds_span, + "this trait bound is already specified in trait declaration", + "try", + fixed_trait_snippet, + Applicability::MaybeIncorrect, + ); + } + } + } + } } impl TraitBounds { diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_null_to_fn.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_null_to_fn.rs index e75d7f6bf1d..4944381da24 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_null_to_fn.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_null_to_fn.rs @@ -31,9 +31,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t match arg.kind { // Catching: // transmute over constants that resolve to `null`. - ExprKind::Path(ref _qpath) - if matches!(constant(cx, cx.typeck_results(), arg), Some((Constant::RawPtr(0), _))) => - { + ExprKind::Path(ref _qpath) if matches!(constant(cx, cx.typeck_results(), arg), Some(Constant::RawPtr(0))) => { lint_expr(cx, expr); true }, diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmuting_null.rs b/src/tools/clippy/clippy_lints/src/transmute/transmuting_null.rs index 1e407fc4138..770914e99e1 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmuting_null.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmuting_null.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant_context, Constant}; +use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::{is_integer_literal, is_path_diagnostic_item}; use rustc_hir::{Expr, ExprKind}; @@ -16,9 +16,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t } // Catching transmute over constants that resolve to `null`. - let mut const_eval_context = constant_context(cx, cx.typeck_results()); if let ExprKind::Path(ref _qpath) = arg.kind && - let Some(Constant::RawPtr(0)) = const_eval_context.expr(arg) + let Some(Constant::RawPtr(0)) = constant(cx, cx.typeck_results(), arg) { span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); return true; diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs index ddbe6b2c790..28c3fc859e3 100644 --- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs +++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; +use clippy_utils::is_ty_alias; use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts}; @@ -138,6 +139,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { if_chain! { if let ExprKind::Path(ref qpath) = path.kind; if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); + if !is_ty_alias(qpath); then { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(arg); diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 935ea90d245..3c2bf5abab2 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -308,7 +308,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { bind!(self, vec); kind!("CStr(ref {vec})"); chain!(self, "let [{:?}] = **{vec}", vec.value); - } + }, LitKind::Str(s, _) => { bind!(self, s); kind!("Str({s}, _)"); diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs index 5f05d971fce..f6de66bb514 100644 --- a/src/tools/clippy/clippy_lints/src/utils/conf.rs +++ b/src/tools/clippy/clippy_lints/src/utils/conf.rs @@ -174,16 +174,15 @@ macro_rules! define_Conf { } } - #[cfg(feature = "internal")] pub mod metadata { - use crate::utils::internal_lints::metadata_collector::ClippyConfiguration; + use crate::utils::ClippyConfiguration; macro_rules! wrap_option { () => (None); ($x:literal) => (Some($x)); } - pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> { + pub fn get_configuration_metadata() -> Vec<ClippyConfiguration> { vec![ $( { diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 3d0d4a52511..7a1cd3effae 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -8,7 +8,11 @@ //! a simple mistake) use crate::renamed_lints::RENAMED_LINTS; -use crate::utils::internal_lints::lint_without_lint_pass::{extract_clippy_version_value, is_lint_ref_type}; +use crate::utils::{ + collect_configs, + internal_lints::lint_without_lint_pass::{extract_clippy_version_value, is_lint_ref_type}, + ClippyConfiguration, +}; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::{match_type, walk_ptrs_ty_depth}; @@ -520,111 +524,6 @@ impl Serialize for ApplicabilityInfo { } } -// ================================================================== -// Configuration -// ================================================================== -#[derive(Debug, Clone, Default)] -pub struct ClippyConfiguration { - name: String, - config_type: &'static str, - default: String, - lints: Vec<String>, - doc: String, - #[allow(dead_code)] - deprecation_reason: Option<&'static str>, -} - -impl ClippyConfiguration { - pub fn new( - name: &'static str, - config_type: &'static str, - default: String, - doc_comment: &'static str, - deprecation_reason: Option<&'static str>, - ) -> Self { - let (lints, doc) = parse_config_field_doc(doc_comment) - .unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string())); - - Self { - name: to_kebab(name), - lints, - doc, - config_type, - default, - deprecation_reason, - } - } - - fn to_markdown_paragraph(&self) -> String { - format!( - "### {}\n{}\n\n**Default Value:** `{}` (`{}`)\n\n{}\n\n", - self.name, - self.doc - .lines() - .map(|line| line.strip_prefix(" ").unwrap_or(line)) - .join("\n"), - self.default, - self.config_type, - self.lints - .iter() - .map(|name| name.to_string().split_whitespace().next().unwrap().to_string()) - .map(|name| format!("* [{name}](https://rust-lang.github.io/rust-clippy/master/index.html#{name})")) - .join("\n"), - ) - } - - fn to_markdown_table_entry(&self) -> String { - format!("| [{}](#{}) | `{}` |", self.name, self.name, self.default) - } -} - -fn collect_configs() -> Vec<ClippyConfiguration> { - crate::utils::conf::metadata::get_configuration_metadata() -} - -/// This parses the field documentation of the config struct. -/// -/// ```rust, ignore -/// parse_config_field_doc(cx, "Lint: LINT_NAME_1, LINT_NAME_2. Papa penguin, papa penguin") -/// ``` -/// -/// Would yield: -/// ```rust, ignore -/// Some(["lint_name_1", "lint_name_2"], "Papa penguin, papa penguin") -/// ``` -fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec<String>, String)> { - const DOC_START: &str = " Lint: "; - if_chain! { - if doc_comment.starts_with(DOC_START); - if let Some(split_pos) = doc_comment.find('.'); - then { - let mut doc_comment = doc_comment.to_string(); - let mut documentation = doc_comment.split_off(split_pos); - - // Extract lints - doc_comment.make_ascii_lowercase(); - let lints: Vec<String> = doc_comment - .split_off(DOC_START.len()) - .split(", ") - .map(str::to_string) - .collect(); - - // Format documentation correctly - // split off leading `.` from lint name list and indent for correct formatting - documentation = documentation.trim_start_matches('.').trim().replace("\n ", "\n "); - - Some((lints, documentation)) - } else { - None - } - } -} - -/// Transforms a given `snake_case_string` to a tasty `kebab-case-string` -fn to_kebab(config_name: &str) -> String { - config_name.replace('_', "-") -} - impl fmt::Display for ClippyConfiguration { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result { writeln!( diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs index dc647af264c..d3ea7cafa80 100644 --- a/src/tools/clippy/clippy_lints/src/utils/mod.rs +++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs @@ -4,3 +4,143 @@ pub mod dump_hir; pub mod format_args_collector; #[cfg(feature = "internal")] pub mod internal_lints; +#[cfg(feature = "internal")] +use itertools::Itertools; + +/// Transforms a given `snake_case_string` to a tasty `kebab-case-string` +fn to_kebab(config_name: &str) -> String { + config_name.replace('_', "-") +} + +// ================================================================== +// Configuration +// ================================================================== +#[derive(Debug, Clone, Default)] //~ ERROR no such field +pub struct ClippyConfiguration { + pub name: String, + #[allow(dead_code)] + config_type: &'static str, + pub default: String, + pub lints: Vec<String>, + pub doc: String, + #[allow(dead_code)] + deprecation_reason: Option<&'static str>, +} + +impl ClippyConfiguration { + pub fn new( + name: &'static str, + config_type: &'static str, + default: String, + doc_comment: &'static str, + deprecation_reason: Option<&'static str>, + ) -> Self { + let (lints, doc) = parse_config_field_doc(doc_comment) + .unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string())); + + Self { + name: to_kebab(name), + lints, + doc, + config_type, + default, + deprecation_reason, + } + } + + #[cfg(feature = "internal")] + fn to_markdown_paragraph(&self) -> String { + format!( + "### {}\n{}\n\n**Default Value:** `{}` (`{}`)\n\n{}\n\n", + self.name, + self.doc + .lines() + .map(|line| line.strip_prefix(" ").unwrap_or(line)) + .join("\n"), + self.default, + self.config_type, + self.lints + .iter() + .map(|name| name.to_string().split_whitespace().next().unwrap().to_string()) + .map(|name| format!("* [{name}](https://rust-lang.github.io/rust-clippy/master/index.html#{name})")) + .join("\n"), + ) + } + + #[cfg(feature = "internal")] + fn to_markdown_table_entry(&self) -> String { + format!("| [{}](#{}) | `{}` |", self.name, self.name, self.default) + } +} + +#[cfg(feature = "internal")] +fn collect_configs() -> Vec<ClippyConfiguration> { + crate::utils::conf::metadata::get_configuration_metadata() +} + +/// This parses the field documentation of the config struct. +/// +/// ```rust, ignore +/// parse_config_field_doc(cx, "Lint: LINT_NAME_1, LINT_NAME_2. Papa penguin, papa penguin") +/// ``` +/// +/// Would yield: +/// ```rust, ignore +/// Some(["lint_name_1", "lint_name_2"], "Papa penguin, papa penguin") +/// ``` +fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec<String>, String)> { + const DOC_START: &str = " Lint: "; + if_chain! { + if doc_comment.starts_with(DOC_START); + if let Some(split_pos) = doc_comment.find('.'); + then { + let mut doc_comment = doc_comment.to_string(); + let mut documentation = doc_comment.split_off(split_pos); + + // Extract lints + doc_comment.make_ascii_lowercase(); + let lints: Vec<String> = doc_comment + .split_off(DOC_START.len()) + .split(", ") + .map(str::to_string) + .collect(); + + // Format documentation correctly + // split off leading `.` from lint name list and indent for correct formatting + documentation = documentation.trim_start_matches('.').trim().replace("\n ", "\n "); + + Some((lints, documentation)) + } else { + None + } + } +} + +// Shamelessly stolen from find_all (https://github.com/nectariner/find_all) +pub trait FindAll: Iterator + Sized { + fn find_all<P>(&mut self, predicate: P) -> Option<Vec<usize>> + where + P: FnMut(&Self::Item) -> bool; +} + +impl<I> FindAll for I +where + I: Iterator, +{ + fn find_all<P>(&mut self, mut predicate: P) -> Option<Vec<usize>> + where + P: FnMut(&Self::Item) -> bool, + { + let mut occurences = Vec::<usize>::default(); + for (index, element) in self.enumerate() { + if predicate(&element) { + occurences.push(index); + } + } + + match occurences.len() { + 0 => None, + _ => Some(occurences), + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs index 297a80e5767..7329e508106 100644 --- a/src/tools/clippy/clippy_lints/src/vec.rs +++ b/src/tools/clippy/clippy_lints/src/vec.rs @@ -84,7 +84,7 @@ impl UselessVec { let mut applicability = Applicability::MachineApplicable; let snippet = match *vec_args { higher::VecArgs::Repeat(elem, len) => { - if let Some((Constant::Int(len_constant), _)) = constant(cx, cx.typeck_results(), len) { + if let Some(Constant::Int(len_constant)) = constant(cx, cx.typeck_results(), len) { #[expect(clippy::cast_possible_truncation)] if len_constant as u64 * size_of(cx, elem) > self.too_large_for_stack { return; diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs index 36f910c983f..a9089fba3c5 100644 --- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs +++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs @@ -7,7 +7,7 @@ use rustc_hir::{ def::{DefKind, Res}, Item, ItemKind, PathSegment, UseKind, }; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::kw; @@ -117,6 +117,10 @@ impl_lint_pass!(WildcardImports => [ENUM_GLOB_USE, WILDCARD_IMPORTS]); impl LateLintPass<'_> for WildcardImports { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if cx.sess().is_test_crate() { + return; + } + if is_test_module_or_function(cx.tcx, item) { self.test_modules_deep = self.test_modules_deep.saturating_add(1); } diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index 8075881e3bb..fb772644c0d 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -1,18 +1,21 @@ #![allow(clippy::float_cmp)] +use crate::source::{get_source_text, walk_span_to_context}; use crate::{clip, is_direct_expn_of, sext, unsext}; use if_chain::if_chain; use rustc_ast::ast::{self, LitFloatType, LitKind}; use rustc_data_structures::sync::Lrc; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp}; +use rustc_lexer::tokenize; use rustc_lint::LateContext; use rustc_middle::mir; use rustc_middle::mir::interpret::Scalar; -use rustc_middle::ty::SubstsRef; use rustc_middle::ty::{self, EarlyBinder, FloatTy, ScalarInt, Ty, TyCtxt}; +use rustc_middle::ty::{List, SubstsRef}; use rustc_middle::{bug, span_bug}; use rustc_span::symbol::Symbol; +use rustc_span::SyntaxContext; use std::cmp::Ordering::{self, Equal}; use std::hash::{Hash, Hasher}; use std::iter; @@ -210,8 +213,7 @@ pub fn lit_to_mir_constant(lit: &LitKind, ty: Option<Ty<'_>>) -> Constant { match *lit { LitKind::Str(ref is, _) => Constant::Str(is.to_string()), LitKind::Byte(b) => Constant::Int(u128::from(b)), - LitKind::ByteStr(ref s, _) => Constant::Binary(Lrc::clone(s)), - LitKind::CStr(ref s, _) => Constant::Binary(Lrc::clone(s)), + LitKind::ByteStr(ref s, _) | LitKind::CStr(ref s, _) => Constant::Binary(Lrc::clone(s)), LitKind::Char(c) => Constant::Char(c), LitKind::Int(n, _) => Constant::Int(n), LitKind::Float(ref is, LitFloatType::Suffixed(fty)) => match fty { @@ -228,27 +230,46 @@ pub fn lit_to_mir_constant(lit: &LitKind, ty: Option<Ty<'_>>) -> Constant { } } +/// The source of a constant value. +pub enum ConstantSource { + /// The value is determined solely from the expression. + Local, + /// The value is dependent on a defined constant. + Constant, +} +impl ConstantSource { + pub fn is_local(&self) -> bool { + matches!(self, Self::Local) + } +} + +/// Attempts to evaluate the expression as a constant. pub fn constant<'tcx>( lcx: &LateContext<'tcx>, typeck_results: &ty::TypeckResults<'tcx>, e: &Expr<'_>, -) -> Option<(Constant, bool)> { - let mut cx = ConstEvalLateContext { - lcx, - typeck_results, - param_env: lcx.param_env, - needed_resolution: false, - substs: ty::List::empty(), - }; - cx.expr(e).map(|cst| (cst, cx.needed_resolution)) +) -> Option<Constant> { + ConstEvalLateContext::new(lcx, typeck_results).expr(e) +} + +/// Attempts to evaluate the expression as a constant. +pub fn constant_with_source<'tcx>( + lcx: &LateContext<'tcx>, + typeck_results: &ty::TypeckResults<'tcx>, + e: &Expr<'_>, +) -> Option<(Constant, ConstantSource)> { + let mut ctxt = ConstEvalLateContext::new(lcx, typeck_results); + let res = ctxt.expr(e); + res.map(|x| (x, ctxt.source)) } +/// Attempts to evaluate an expression only if it's value is not dependent on other items. pub fn constant_simple<'tcx>( lcx: &LateContext<'tcx>, typeck_results: &ty::TypeckResults<'tcx>, e: &Expr<'_>, ) -> Option<Constant> { - constant(lcx, typeck_results, e).and_then(|(cst, res)| if res { None } else { Some(cst) }) + constant_with_source(lcx, typeck_results, e).and_then(|(c, s)| s.is_local().then_some(c)) } pub fn constant_full_int<'tcx>( @@ -297,29 +318,25 @@ impl Ord for FullInt { } } -/// Creates a `ConstEvalLateContext` from the given `LateContext` and `TypeckResults`. -pub fn constant_context<'a, 'tcx>( - lcx: &'a LateContext<'tcx>, - typeck_results: &'a ty::TypeckResults<'tcx>, -) -> ConstEvalLateContext<'a, 'tcx> { - ConstEvalLateContext { - lcx, - typeck_results, - param_env: lcx.param_env, - needed_resolution: false, - substs: ty::List::empty(), - } -} - pub struct ConstEvalLateContext<'a, 'tcx> { lcx: &'a LateContext<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>, param_env: ty::ParamEnv<'tcx>, - needed_resolution: bool, + source: ConstantSource, substs: SubstsRef<'tcx>, } impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { + fn new(lcx: &'a LateContext<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>) -> Self { + Self { + lcx, + typeck_results, + param_env: lcx.param_env, + source: ConstantSource::Local, + substs: List::empty(), + } + } + /// Simple constant folding: Insert an expression, get a constant or none. pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant> { match e.kind { @@ -454,11 +471,9 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { .const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, substs), None) .ok() .map(|val| rustc_middle::mir::ConstantKind::from_value(val, ty))?; - let result = miri_to_const(self.lcx.tcx, result); - if result.is_some() { - self.needed_resolution = true; - } - result + let result = miri_to_const(self.lcx.tcx, result)?; + self.source = ConstantSource::Constant; + Some(result) }, // FIXME: cover all usable cases. _ => None, @@ -492,8 +507,33 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { /// A block can only yield a constant if it only has one constant expression. fn block(&mut self, block: &Block<'_>) -> Option<Constant> { - if block.stmts.is_empty() { - block.expr.as_ref().and_then(|b| self.expr(b)) + if block.stmts.is_empty() + && let Some(expr) = block.expr + { + // Try to detect any `cfg`ed statements or empty macro expansions. + let span = block.span.data(); + if span.ctxt == SyntaxContext::root() { + if let Some(expr_span) = walk_span_to_context(expr.span, span.ctxt) + && let expr_lo = expr_span.lo() + && expr_lo >= span.lo + && let Some(src) = get_source_text(self.lcx, span.lo..expr_lo) + && let Some(src) = src.as_str() + { + use rustc_lexer::TokenKind::{Whitespace, LineComment, BlockComment, Semi, OpenBrace}; + if !tokenize(src) + .map(|t| t.kind) + .filter(|t| !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. } | Semi)) + .eq([OpenBrace]) + { + self.source = ConstantSource::Constant; + } + } else { + // Unable to access the source. Assume a non-local dependency. + self.source = ConstantSource::Constant; + } + } + + self.expr(expr) } else { None } diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 9b7408d5133..a49246a7832 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -1,6 +1,7 @@ use crate::consts::constant_simple; use crate::macros::macro_backtrace; -use crate::source::snippet_opt; +use crate::source::{get_source_text, snippet_opt, walk_span_to_context, SpanRange}; +use crate::tokenize_with_text; use rustc_ast::ast::InlineAsmTemplatePiece; use rustc_data_structures::fx::FxHasher; use rustc_hir::def::Res; @@ -13,8 +14,9 @@ use rustc_hir::{ use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::LateContext; use rustc_middle::ty::TypeckResults; -use rustc_span::{sym, Symbol}; +use rustc_span::{sym, BytePos, ExpnKind, MacroKind, Symbol, SyntaxContext}; use std::hash::{Hash, Hasher}; +use std::ops::Range; /// Callback that is called when two expressions are not equal in the sense of `SpanlessEq`, but /// other conditions would make them equal. @@ -65,6 +67,8 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { pub fn inter_expr(&mut self) -> HirEqInterExpr<'_, 'a, 'tcx> { HirEqInterExpr { inner: self, + left_ctxt: SyntaxContext::root(), + right_ctxt: SyntaxContext::root(), locals: HirIdMap::default(), } } @@ -92,6 +96,8 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { pub struct HirEqInterExpr<'a, 'b, 'tcx> { inner: &'a mut SpanlessEq<'b, 'tcx>, + left_ctxt: SyntaxContext, + right_ctxt: SyntaxContext, // When binding are declared, the binding ID in the left expression is mapped to the one on the // right. For example, when comparing `{ let x = 1; x + 2 }` and `{ let y = 1; y + 2 }`, @@ -126,52 +132,88 @@ impl HirEqInterExpr<'_, '_, '_> { } /// Checks whether two blocks are the same. + #[expect(clippy::similar_names)] fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool { - match (left.stmts, left.expr, right.stmts, right.expr) { - ([], None, [], None) => { - // For empty blocks, check to see if the tokens are equal. This will catch the case where a macro - // expanded to nothing, or the cfg attribute was used. - let (Some(left), Some(right)) = ( - snippet_opt(self.inner.cx, left.span), - snippet_opt(self.inner.cx, right.span), - ) else { return true }; - let mut left_pos = 0; - let left = tokenize(&left) - .map(|t| { - let end = left_pos + t.len as usize; - let s = &left[left_pos..end]; - left_pos = end; - (t, s) - }) - .filter(|(t, _)| { - !matches!( - t.kind, - TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace - ) - }) - .map(|(_, s)| s); - let mut right_pos = 0; - let right = tokenize(&right) - .map(|t| { - let end = right_pos + t.len as usize; - let s = &right[right_pos..end]; - right_pos = end; - (t, s) - }) - .filter(|(t, _)| { - !matches!( - t.kind, - TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace - ) - }) - .map(|(_, s)| s); - left.eq(right) - }, - _ => { - over(left.stmts, right.stmts, |l, r| self.eq_stmt(l, r)) - && both(&left.expr, &right.expr, |l, r| self.eq_expr(l, r)) - }, + use TokenKind::{BlockComment, LineComment, Semi, Whitespace}; + if left.stmts.len() != right.stmts.len() { + return false; + } + let lspan = left.span.data(); + let rspan = right.span.data(); + if lspan.ctxt != SyntaxContext::root() && rspan.ctxt != SyntaxContext::root() { + // Don't try to check in between statements inside macros. + return over(left.stmts, right.stmts, |left, right| self.eq_stmt(left, right)) + && both(&left.expr, &right.expr, |left, right| self.eq_expr(left, right)); + } + if lspan.ctxt != rspan.ctxt { + return false; } + + let mut lstart = lspan.lo; + let mut rstart = rspan.lo; + + for (left, right) in left.stmts.iter().zip(right.stmts) { + if !self.eq_stmt(left, right) { + return false; + } + + // Try to detect any `cfg`ed statements or empty macro expansions. + let Some(lstmt_span) = walk_span_to_context(left.span, lspan.ctxt) else { + return false; + }; + let Some(rstmt_span) = walk_span_to_context(right.span, rspan.ctxt) else { + return false; + }; + let lstmt_span = lstmt_span.data(); + let rstmt_span = rstmt_span.data(); + + if lstmt_span.lo < lstart && rstmt_span.lo < rstart { + // Can happen when macros expand to multiple statements, or rearrange statements. + // Nothing in between the statements to check in this case. + continue; + } + if lstmt_span.lo < lstart || rstmt_span.lo < rstart { + // Only one of the blocks had a weird macro. + return false; + } + if !eq_span_tokens(self.inner.cx, lstart..lstmt_span.lo, rstart..rstmt_span.lo, |t| { + !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. } | Semi) + }) { + return false; + } + + lstart = lstmt_span.hi; + rstart = rstmt_span.hi; + } + + let (lend, rend) = match (left.expr, right.expr) { + (Some(left), Some(right)) => { + if !self.eq_expr(left, right) { + return false; + } + let Some(lexpr_span) = walk_span_to_context(left.span, lspan.ctxt) else { + return false; + }; + let Some(rexpr_span) = walk_span_to_context(right.span, rspan.ctxt) else { + return false; + }; + (lexpr_span.lo(), rexpr_span.lo()) + }, + (None, None) => (lspan.hi, rspan.hi), + (Some(_), None) | (None, Some(_)) => return false, + }; + + if lend < lstart && rend < rstart { + // Can happen when macros rearrange the input. + // Nothing in between the statements to check in this case. + return true; + } else if lend < lstart || rend < rstart { + // Only one of the blocks had a weird macro + return false; + } + eq_span_tokens(self.inner.cx, lstart..lend, rstart..rend, |t| { + !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. } | Semi) + }) } fn should_ignore(&mut self, expr: &Expr<'_>) -> bool { @@ -207,7 +249,7 @@ impl HirEqInterExpr<'_, '_, '_> { #[expect(clippy::similar_names)] pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { - if !self.inner.allow_side_effects && left.span.ctxt() != right.span.ctxt() { + if !self.check_ctxt(left.span.ctxt(), right.span.ctxt()) { return false; } @@ -440,6 +482,45 @@ impl HirEqInterExpr<'_, '_, '_> { fn eq_type_binding(&mut self, left: &TypeBinding<'_>, right: &TypeBinding<'_>) -> bool { left.ident.name == right.ident.name && self.eq_ty(left.ty(), right.ty()) } + + fn check_ctxt(&mut self, left: SyntaxContext, right: SyntaxContext) -> bool { + if self.left_ctxt == left && self.right_ctxt == right { + return true; + } else if self.left_ctxt == left || self.right_ctxt == right { + // Only one context has changed. This can only happen if the two nodes are written differently. + return false; + } else if left != SyntaxContext::root() { + let mut left_data = left.outer_expn_data(); + let mut right_data = right.outer_expn_data(); + loop { + use TokenKind::{BlockComment, LineComment, Whitespace}; + if left_data.macro_def_id != right_data.macro_def_id + || (matches!(left_data.kind, ExpnKind::Macro(MacroKind::Bang, name) if name == sym::cfg) + && !eq_span_tokens(self.inner.cx, left_data.call_site, right_data.call_site, |t| { + !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. }) + })) + { + // Either a different chain of macro calls, or different arguments to the `cfg` macro. + return false; + } + let left_ctxt = left_data.call_site.ctxt(); + let right_ctxt = right_data.call_site.ctxt(); + if left_ctxt == SyntaxContext::root() && right_ctxt == SyntaxContext::root() { + break; + } + if left_ctxt == SyntaxContext::root() || right_ctxt == SyntaxContext::root() { + // Different lengths for the expansion stack. This can only happen if nodes are written differently, + // or shouldn't be compared to start with. + return false; + } + left_data = left_ctxt.outer_expn_data(); + right_data = right_ctxt.outer_expn_data(); + } + } + self.left_ctxt = left; + self.right_ctxt = right; + true + } } /// Some simple reductions like `{ return }` => `return` @@ -1038,3 +1119,34 @@ pub fn hash_expr(cx: &LateContext<'_>, e: &Expr<'_>) -> u64 { h.hash_expr(e); h.finish() } + +#[expect(clippy::similar_names)] +fn eq_span_tokens( + cx: &LateContext<'_>, + left: impl SpanRange, + right: impl SpanRange, + pred: impl Fn(TokenKind) -> bool, +) -> bool { + fn f(cx: &LateContext<'_>, left: Range<BytePos>, right: Range<BytePos>, pred: impl Fn(TokenKind) -> bool) -> bool { + if let Some(lsrc) = get_source_text(cx, left) + && let Some(lsrc) = lsrc.as_str() + && let Some(rsrc) = get_source_text(cx, right) + && let Some(rsrc) = rsrc.as_str() + { + let pred = |t: &(_, _)| pred(t.0); + let map = |(_, x)| x; + + let ltok = tokenize_with_text(lsrc) + .filter(pred) + .map(map); + let rtok = tokenize_with_text(rsrc) + .filter(pred) + .map(map); + ltok.eq(rtok) + } else { + // Unable to access the source. Conservatively assume the blocks aren't equal. + false + } + } + f(cx, left.into_range(), right.into_range(), pred) +} diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 964104fc31d..575c29a6b6f 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -1,5 +1,6 @@ #![feature(array_chunks)] #![feature(box_patterns)] +#![feature(if_let_guard)] #![feature(let_chains)] #![feature(lint_reasons)] #![feature(never_type)] @@ -76,6 +77,7 @@ use std::sync::OnceLock; use std::sync::{Mutex, MutexGuard}; use if_chain::if_chain; +use itertools::Itertools; use rustc_ast::ast::{self, LitKind, RangeLimits}; use rustc_ast::Attribute; use rustc_data_structures::fx::FxHashMap; @@ -282,6 +284,15 @@ pub fn is_wild(pat: &Pat<'_>) -> bool { matches!(pat.kind, PatKind::Wild) } +/// Checks if the given `QPath` belongs to a type alias. +pub fn is_ty_alias(qpath: &QPath<'_>) -> bool { + match *qpath { + QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias, ..)), + QPath::TypeRelative(ty, _) if let TyKind::Path(qpath) = ty.kind => { is_ty_alias(&qpath) }, + _ => false, + } +} + /// Checks if the method call given in `expr` belongs to the given trait. /// This is a deprecated function, consider using [`is_trait_method`]. pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool { @@ -1488,7 +1499,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti && let const_val = cx.tcx.valtree_to_const_val((bnd_ty, min_val.to_valtree())) && let min_const_kind = ConstantKind::from_value(const_val, bnd_ty) && let Some(min_const) = miri_to_const(cx.tcx, min_const_kind) - && let Some((start_const, _)) = constant(cx, cx.typeck_results(), start) + && let Some(start_const) = constant(cx, cx.typeck_results(), start) { start_const == min_const } else { @@ -1504,7 +1515,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti && let const_val = cx.tcx.valtree_to_const_val((bnd_ty, max_val.to_valtree())) && let max_const_kind = ConstantKind::from_value(const_val, bnd_ty) && let Some(max_const) = miri_to_const(cx.tcx, max_const_kind) - && let Some((end_const, _)) = constant(cx, cx.typeck_results(), end) + && let Some(end_const) = constant(cx, cx.typeck_results(), end) { end_const == max_const } else { @@ -1536,7 +1547,7 @@ pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool return true; } let enclosing_body = cx.tcx.hir().enclosing_body_owner(e.hir_id); - if let Some((Constant::Int(v), _)) = constant(cx, cx.tcx.typeck(enclosing_body), e) { + if let Some(Constant::Int(v)) = constant(cx, cx.tcx.typeck(enclosing_body), e) { return value == v; } false @@ -2480,6 +2491,17 @@ pub fn walk_to_expr_usage<'tcx, T>( None } +/// Tokenizes the input while keeping the text associated with each token. +pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str)> { + let mut pos = 0; + tokenize(s).map(move |t| { + let end = pos + t.len; + let range = pos as usize..end as usize; + pos = end; + (t.kind, s.get(range).unwrap_or_default()) + }) +} + /// Checks whether a given span has any comment token /// This checks for all types of comment: line "//", block "/**", doc "///" "//!" pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool { @@ -2496,23 +2518,11 @@ pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool { /// Comments are returned wrapped with their relevant delimiters pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String { let snippet = sm.span_to_snippet(span).unwrap_or_default(); - let mut comments_buf: Vec<String> = Vec::new(); - let mut index: usize = 0; - - for token in tokenize(&snippet) { - let token_range = index..(index + token.len as usize); - index += token.len as usize; - match token.kind { - TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => { - if let Some(comment) = snippet.get(token_range) { - comments_buf.push(comment.to_string()); - } - }, - _ => (), - } - } - - comments_buf.join("\n") + let res = tokenize_with_text(&snippet) + .filter(|(t, _)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. })) + .map(|(_, s)| s) + .join("\n"); + res } pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span { diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs index 62fa37660fa..0f60290644a 100644 --- a/src/tools/clippy/clippy_utils/src/source.rs +++ b/src/tools/clippy/clippy_utils/src/source.rs @@ -2,14 +2,64 @@ #![allow(clippy::module_name_repetitions)] +use rustc_data_structures::sync::Lrc; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LintContext}; use rustc_session::Session; -use rustc_span::hygiene; use rustc_span::source_map::{original_sp, SourceMap}; +use rustc_span::{hygiene, SourceFile}; use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext, DUMMY_SP}; use std::borrow::Cow; +use std::ops::Range; + +/// A type which can be converted to the range portion of a `Span`. +pub trait SpanRange { + fn into_range(self) -> Range<BytePos>; +} +impl SpanRange for Span { + fn into_range(self) -> Range<BytePos> { + let data = self.data(); + data.lo..data.hi + } +} +impl SpanRange for SpanData { + fn into_range(self) -> Range<BytePos> { + self.lo..self.hi + } +} +impl SpanRange for Range<BytePos> { + fn into_range(self) -> Range<BytePos> { + self + } +} + +pub struct SourceFileRange { + pub sf: Lrc<SourceFile>, + pub range: Range<usize>, +} +impl SourceFileRange { + /// Attempts to get the text from the source file. This can fail if the source text isn't + /// loaded. + pub fn as_str(&self) -> Option<&str> { + self.sf.src.as_ref().and_then(|x| x.get(self.range.clone())) + } +} + +/// Gets the source file, and range in the file, of the given span. Returns `None` if the span +/// extends through multiple files, or is malformed. +pub fn get_source_text(cx: &impl LintContext, sp: impl SpanRange) -> Option<SourceFileRange> { + fn f(sm: &SourceMap, sp: Range<BytePos>) -> Option<SourceFileRange> { + let start = sm.lookup_byte_offset(sp.start); + let end = sm.lookup_byte_offset(sp.end); + if !Lrc::ptr_eq(&start.sf, &end.sf) || start.pos > end.pos { + return None; + } + let range = start.pos.to_usize()..end.pos.to_usize(); + Some(SourceFileRange { sf: start.sf, range }) + } + f(cx.sess().source_map(), sp.into_range()) +} /// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`. pub fn expr_block<T: LintContext>( diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index 60b8a5ac071..bc7fb711ed8 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-05-05" +channel = "nightly-2023-05-20" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/tools/clippy/tests/ui-internal/custom_ice_message.rs b/src/tools/clippy/tests/ui-internal/custom_ice_message.rs index acb98d7ba98..99ce7028390 100644 --- a/src/tools/clippy/tests/ui-internal/custom_ice_message.rs +++ b/src/tools/clippy/tests/ui-internal/custom_ice_message.rs @@ -3,6 +3,7 @@ //@normalize-stderr-test: "produce_ice.rs:\d*:\d*" -> "produce_ice.rs" //@normalize-stderr-test: "', .*clippy_lints" -> "', clippy_lints" //@normalize-stderr-test: "'rustc'" -> "'<unnamed>'" +//@normalize-stderr-test: "running on .*" -> "running on <target>" //@normalize-stderr-test: "(?ms)query stack during panic:\n.*end of query stack\n" -> "" #![deny(clippy::internal)] diff --git a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr index b4619e980f3..0fc385cd693 100644 --- a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr +++ b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr @@ -1,12 +1,14 @@ thread '<unnamed>' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints/produce_ice.rs note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace -error: internal compiler error: unexpected panic - -note: the compiler unexpectedly panicked. this is a bug. +error: the compiler unexpectedly panicked. this is a bug. note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new +note: rustc 1.71.0-nightly (521f4dae1 2023-05-19) running on <target> + +note: compiler flags: -C prefer-dynamic -Z ui-testing + note: Clippy version: foo thread panicked while panicking. aborting. diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs index ab408bdf261..f95af1017bc 100644 --- a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs +++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs @@ -458,4 +458,12 @@ pub fn issue_10583(a: u16) -> u16 { 10 / a } +pub fn issue_10767() { + let n = &1.0; + n + n; + 3.1_f32 + &1.2_f32; + &3.4_f32 + 1.5_f32; + &3.5_f32 + &1.3_f32; +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/borrow_deref_ref.fixed b/src/tools/clippy/tests/ui/borrow_deref_ref.fixed index 165e4bc8272..75526461792 100644 --- a/src/tools/clippy/tests/ui/borrow_deref_ref.fixed +++ b/src/tools/clippy/tests/ui/borrow_deref_ref.fixed @@ -1,7 +1,11 @@ //@run-rustfix +//@aux-build: proc_macros.rs #![allow(dead_code, unused_variables)] +extern crate proc_macros; +use proc_macros::with_span; + fn main() {} mod should_lint { @@ -47,6 +51,17 @@ mod should_not_lint2 { } } +with_span!( + span + + fn just_returning(x: &u32) -> &u32 { + x + } + + fn dont_lint_proc_macro() { + let a = &mut &*just_returning(&12); + } +); // this mod explains why we should not lint `& &* (&T)` mod false_negative { fn foo() { diff --git a/src/tools/clippy/tests/ui/borrow_deref_ref.rs b/src/tools/clippy/tests/ui/borrow_deref_ref.rs index 66c8d69bef9..e319d365f7e 100644 --- a/src/tools/clippy/tests/ui/borrow_deref_ref.rs +++ b/src/tools/clippy/tests/ui/borrow_deref_ref.rs @@ -1,7 +1,11 @@ //@run-rustfix +//@aux-build: proc_macros.rs #![allow(dead_code, unused_variables)] +extern crate proc_macros; +use proc_macros::with_span; + fn main() {} mod should_lint { @@ -47,6 +51,17 @@ mod should_not_lint2 { } } +with_span!( + span + + fn just_returning(x: &u32) -> &u32 { + x + } + + fn dont_lint_proc_macro() { + let a = &mut &*just_returning(&12); + } +); // this mod explains why we should not lint `& &* (&T)` mod false_negative { fn foo() { diff --git a/src/tools/clippy/tests/ui/borrow_deref_ref.stderr b/src/tools/clippy/tests/ui/borrow_deref_ref.stderr index d72de37c69f..1e47cda6796 100644 --- a/src/tools/clippy/tests/ui/borrow_deref_ref.stderr +++ b/src/tools/clippy/tests/ui/borrow_deref_ref.stderr @@ -1,5 +1,5 @@ error: deref on an immutable reference - --> $DIR/borrow_deref_ref.rs:10:17 + --> $DIR/borrow_deref_ref.rs:14:17 | LL | let b = &*a; | ^^^ help: if you would like to reborrow, try removing `&*`: `a` @@ -7,13 +7,13 @@ LL | let b = &*a; = note: `-D clippy::borrow-deref-ref` implied by `-D warnings` error: deref on an immutable reference - --> $DIR/borrow_deref_ref.rs:12:22 + --> $DIR/borrow_deref_ref.rs:16:22 | LL | let b = &mut &*bar(&12); | ^^^^^^^^^^ help: if you would like to reborrow, try removing `&*`: `bar(&12)` error: deref on an immutable reference - --> $DIR/borrow_deref_ref.rs:55:23 + --> $DIR/borrow_deref_ref.rs:70:23 | LL | let addr_y = &&*x as *const _ as usize; // assert ok | ^^^ help: if you would like to reborrow, try removing `&*`: `x` diff --git a/src/tools/clippy/tests/ui/box_default.fixed b/src/tools/clippy/tests/ui/box_default.fixed index e6331290420..840902b5323 100644 --- a/src/tools/clippy/tests/ui/box_default.fixed +++ b/src/tools/clippy/tests/ui/box_default.fixed @@ -35,6 +35,13 @@ fn main() { let _more = ret_ty_fn(); call_ty_fn(Box::default()); issue_10381(); + + // `Box::<Option<_>>::default()` would be valid here, but not `Box::default()` or + // `Box::<Option<[closure@...]>::default()` + // + // Would have a suggestion after https://github.com/rust-lang/rust/blob/fdd030127cc68afec44a8d3f6341525dd34e50ae/compiler/rustc_middle/src/ty/diagnostics.rs#L554-L563 + let mut unnameable = Box::new(Option::default()); + let _ = unnameable.insert(|| {}); } fn ret_ty_fn() -> Box<bool> { diff --git a/src/tools/clippy/tests/ui/box_default.rs b/src/tools/clippy/tests/ui/box_default.rs index 34a05a29c5a..3618486a473 100644 --- a/src/tools/clippy/tests/ui/box_default.rs +++ b/src/tools/clippy/tests/ui/box_default.rs @@ -35,6 +35,13 @@ fn main() { let _more = ret_ty_fn(); call_ty_fn(Box::new(u8::default())); issue_10381(); + + // `Box::<Option<_>>::default()` would be valid here, but not `Box::default()` or + // `Box::<Option<[closure@...]>::default()` + // + // Would have a suggestion after https://github.com/rust-lang/rust/blob/fdd030127cc68afec44a8d3f6341525dd34e50ae/compiler/rustc_middle/src/ty/diagnostics.rs#L554-L563 + let mut unnameable = Box::new(Option::default()); + let _ = unnameable.insert(|| {}); } fn ret_ty_fn() -> Box<bool> { diff --git a/src/tools/clippy/tests/ui/box_default.stderr b/src/tools/clippy/tests/ui/box_default.stderr index c9834863601..13dfc5ae48a 100644 --- a/src/tools/clippy/tests/ui/box_default.stderr +++ b/src/tools/clippy/tests/ui/box_default.stderr @@ -73,25 +73,25 @@ LL | call_ty_fn(Box::new(u8::default())); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:41:5 + --> $DIR/box_default.rs:48:5 | LL | Box::new(bool::default()) | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<bool>::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:58:28 + --> $DIR/box_default.rs:65:28 | LL | let _: Box<dyn Read> = Box::new(ImplementsDefault::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<ImplementsDefault>::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:67:17 + --> $DIR/box_default.rs:74:17 | LL | let _ = Box::new(WeirdPathed::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<WeirdPathed>::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:79:18 + --> $DIR/box_default.rs:86:18 | LL | Some(Box::new(Foo::default())) | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<Foo>::default()` diff --git a/src/tools/clippy/tests/ui/collapsible_if.fixed b/src/tools/clippy/tests/ui/collapsible_if.fixed index d2aba2ac59b..c6514a55934 100644 --- a/src/tools/clippy/tests/ui/collapsible_if.fixed +++ b/src/tools/clippy/tests/ui/collapsible_if.fixed @@ -1,5 +1,10 @@ //@run-rustfix -#![allow(clippy::assertions_on_constants, clippy::equatable_if_let)] +#![allow( + clippy::assertions_on_constants, + clippy::equatable_if_let, + clippy::nonminimal_bool, + clippy::eq_op +)] #[rustfmt::skip] #[warn(clippy::collapsible_if)] diff --git a/src/tools/clippy/tests/ui/collapsible_if.rs b/src/tools/clippy/tests/ui/collapsible_if.rs index e0bef7f9c97..2c85b68df63 100644 --- a/src/tools/clippy/tests/ui/collapsible_if.rs +++ b/src/tools/clippy/tests/ui/collapsible_if.rs @@ -1,5 +1,10 @@ //@run-rustfix -#![allow(clippy::assertions_on_constants, clippy::equatable_if_let)] +#![allow( + clippy::assertions_on_constants, + clippy::equatable_if_let, + clippy::nonminimal_bool, + clippy::eq_op +)] #[rustfmt::skip] #[warn(clippy::collapsible_if)] diff --git a/src/tools/clippy/tests/ui/collapsible_if.stderr b/src/tools/clippy/tests/ui/collapsible_if.stderr index 6327444df21..c687bae1acc 100644 --- a/src/tools/clippy/tests/ui/collapsible_if.stderr +++ b/src/tools/clippy/tests/ui/collapsible_if.stderr @@ -1,5 +1,5 @@ error: this `if` statement can be collapsed - --> $DIR/collapsible_if.rs:9:5 + --> $DIR/collapsible_if.rs:14:5 | LL | / if x == "hello" { LL | | if y == "world" { @@ -17,7 +17,7 @@ LL + } | error: this `if` statement can be collapsed - --> $DIR/collapsible_if.rs:15:5 + --> $DIR/collapsible_if.rs:20:5 | LL | / if x == "hello" || x == "world" { LL | | if y == "world" || y == "hello" { @@ -34,7 +34,7 @@ LL + } | error: this `if` statement can be collapsed - --> $DIR/collapsible_if.rs:21:5 + --> $DIR/collapsible_if.rs:26:5 | LL | / if x == "hello" && x == "world" { LL | | if y == "world" || y == "hello" { @@ -51,7 +51,7 @@ LL + } | error: this `if` statement can be collapsed - --> $DIR/collapsible_if.rs:27:5 + --> $DIR/collapsible_if.rs:32:5 | LL | / if x == "hello" || x == "world" { LL | | if y == "world" && y == "hello" { @@ -68,7 +68,7 @@ LL + } | error: this `if` statement can be collapsed - --> $DIR/collapsible_if.rs:33:5 + --> $DIR/collapsible_if.rs:38:5 | LL | / if x == "hello" && x == "world" { LL | | if y == "world" && y == "hello" { @@ -85,7 +85,7 @@ LL + } | error: this `if` statement can be collapsed - --> $DIR/collapsible_if.rs:39:5 + --> $DIR/collapsible_if.rs:44:5 | LL | / if 42 == 1337 { LL | | if 'a' != 'A' { @@ -102,7 +102,7 @@ LL + } | error: this `if` statement can be collapsed - --> $DIR/collapsible_if.rs:95:5 + --> $DIR/collapsible_if.rs:100:5 | LL | / if x == "hello" { LL | | if y == "world" { // Collapsible @@ -119,7 +119,7 @@ LL + } | error: this `if` statement can be collapsed - --> $DIR/collapsible_if.rs:154:5 + --> $DIR/collapsible_if.rs:159:5 | LL | / if matches!(true, true) { LL | | if matches!(true, true) {} @@ -127,7 +127,7 @@ LL | | } | |_____^ help: collapse nested if block: `if matches!(true, true) && matches!(true, true) {}` error: this `if` statement can be collapsed - --> $DIR/collapsible_if.rs:159:5 + --> $DIR/collapsible_if.rs:164:5 | LL | / if matches!(true, true) && truth() { LL | | if matches!(true, true) {} diff --git a/src/tools/clippy/tests/ui/dbg_macro.rs b/src/tools/clippy/tests/ui/dbg_macro.rs index 8701e3cd29f..10788d40481 100644 --- a/src/tools/clippy/tests/ui/dbg_macro.rs +++ b/src/tools/clippy/tests/ui/dbg_macro.rs @@ -4,6 +4,7 @@ fn foo(n: u32) -> u32 { if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } } +fn bar(_: ()) {} fn factorial(n: u32) -> u32 { if dbg!(n <= 1) { @@ -21,6 +22,32 @@ fn main() { dbg!(1, 2, 3, 4, 5); } +fn issue9914() { + macro_rules! foo { + ($x:expr) => { + $x; + }; + } + macro_rules! foo2 { + ($x:expr) => { + $x; + }; + } + macro_rules! expand_to_dbg { + () => { + dbg!(); + }; + } + + dbg!(); + #[allow(clippy::let_unit_value)] + let _ = dbg!(); + bar(dbg!()); + foo!(dbg!()); + foo2!(foo!(dbg!())); + expand_to_dbg!(); +} + mod issue7274 { trait Thing<'b> { fn foo(&self); diff --git a/src/tools/clippy/tests/ui/dbg_macro.stderr b/src/tools/clippy/tests/ui/dbg_macro.stderr index ddb5f1342e9..530e7663317 100644 --- a/src/tools/clippy/tests/ui/dbg_macro.stderr +++ b/src/tools/clippy/tests/ui/dbg_macro.stderr @@ -11,7 +11,7 @@ LL | if let Some(n) = n.checked_sub(4) { n } else { n } | ~~~~~~~~~~~~~~~~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:9:8 + --> $DIR/dbg_macro.rs:10:8 | LL | if dbg!(n <= 1) { | ^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | if n <= 1 { | ~~~~~~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:10:9 + --> $DIR/dbg_macro.rs:11:9 | LL | dbg!(1) | ^^^^^^^ @@ -33,7 +33,7 @@ LL | 1 | error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:12:9 + --> $DIR/dbg_macro.rs:13:9 | LL | dbg!(n * factorial(n - 1)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | n * factorial(n - 1) | error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:17:5 + --> $DIR/dbg_macro.rs:18:5 | LL | dbg!(42); | ^^^^^^^^ @@ -55,7 +55,7 @@ LL | 42; | ~~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:18:5 + --> $DIR/dbg_macro.rs:19:5 | LL | dbg!(dbg!(dbg!(42))); | ^^^^^^^^^^^^^^^^^^^^ @@ -66,7 +66,7 @@ LL | dbg!(dbg!(42)); | ~~~~~~~~~~~~~~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:19:14 + --> $DIR/dbg_macro.rs:20:14 | LL | foo(3) + dbg!(factorial(4)); | ^^^^^^^^^^^^^^^^^^ @@ -77,7 +77,7 @@ LL | foo(3) + factorial(4); | ~~~~~~~~~~~~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:20:5 + --> $DIR/dbg_macro.rs:21:5 | LL | dbg!(1, 2, dbg!(3, 4)); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -88,7 +88,7 @@ LL | (1, 2, dbg!(3, 4)); | ~~~~~~~~~~~~~~~~~~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:21:5 + --> $DIR/dbg_macro.rs:22:5 | LL | dbg!(1, 2, 3, 4, 5); | ^^^^^^^^^^^^^^^^^^^ @@ -99,7 +99,63 @@ LL | (1, 2, 3, 4, 5); | ~~~~~~~~~~~~~~~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:41:9 + --> $DIR/dbg_macro.rs:42:5 + | +LL | dbg!(); + | ^^^^^^^ + | +help: remove the invocation before committing it to a version control system + | +LL - dbg!(); +LL + + | + +error: the `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:44:13 + | +LL | let _ = dbg!(); + | ^^^^^^ + | +help: remove the invocation before committing it to a version control system + | +LL | let _ = (); + | ~~ + +error: the `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:45:9 + | +LL | bar(dbg!()); + | ^^^^^^ + | +help: remove the invocation before committing it to a version control system + | +LL | bar(()); + | ~~ + +error: the `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:46:10 + | +LL | foo!(dbg!()); + | ^^^^^^ + | +help: remove the invocation before committing it to a version control system + | +LL | foo!(()); + | ~~ + +error: the `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:47:16 + | +LL | foo2!(foo!(dbg!())); + | ^^^^^^ + | +help: remove the invocation before committing it to a version control system + | +LL | foo2!(foo!(())); + | ~~ + +error: the `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:68:9 | LL | dbg!(2); | ^^^^^^^ @@ -110,7 +166,7 @@ LL | 2; | ~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:47:5 + --> $DIR/dbg_macro.rs:74:5 | LL | dbg!(1); | ^^^^^^^ @@ -121,7 +177,7 @@ LL | 1; | ~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:52:5 + --> $DIR/dbg_macro.rs:79:5 | LL | dbg!(1); | ^^^^^^^ @@ -132,7 +188,7 @@ LL | 1; | ~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:58:9 + --> $DIR/dbg_macro.rs:85:9 | LL | dbg!(1); | ^^^^^^^ @@ -142,5 +198,5 @@ help: remove the invocation before committing it to a version control system LL | 1; | ~ -error: aborting due to 13 previous errors +error: aborting due to 18 previous errors diff --git a/src/tools/clippy/tests/ui/default_constructed_unit_structs.fixed b/src/tools/clippy/tests/ui/default_constructed_unit_structs.fixed index 4c2d1ea48e1..e1012f38bba 100644 --- a/src/tools/clippy/tests/ui/default_constructed_unit_structs.fixed +++ b/src/tools/clippy/tests/ui/default_constructed_unit_structs.fixed @@ -105,6 +105,7 @@ fn main() { // should lint let _ = PhantomData::<usize>; let _: PhantomData<i32> = PhantomData; + let _: PhantomData<i32> = std::marker::PhantomData; let _ = UnitStruct; // should not lint @@ -116,4 +117,21 @@ fn main() { let _ = EmptyStruct::default(); let _ = FakeDefault::default(); let _ = <FakeDefault as Default>::default(); + + macro_rules! in_macro { + ($i:ident) => {{ + let _ = UnitStruct::default(); + let _ = $i::default(); + }}; + } + + in_macro!(UnitStruct); + + macro_rules! struct_from_macro { + () => { + UnitStruct + }; + } + + let _ = <struct_from_macro!()>::default(); } diff --git a/src/tools/clippy/tests/ui/default_constructed_unit_structs.rs b/src/tools/clippy/tests/ui/default_constructed_unit_structs.rs index 850793dd5de..c7b4313dbf0 100644 --- a/src/tools/clippy/tests/ui/default_constructed_unit_structs.rs +++ b/src/tools/clippy/tests/ui/default_constructed_unit_structs.rs @@ -105,6 +105,7 @@ fn main() { // should lint let _ = PhantomData::<usize>::default(); let _: PhantomData<i32> = PhantomData::default(); + let _: PhantomData<i32> = std::marker::PhantomData::default(); let _ = UnitStruct::default(); // should not lint @@ -116,4 +117,21 @@ fn main() { let _ = EmptyStruct::default(); let _ = FakeDefault::default(); let _ = <FakeDefault as Default>::default(); + + macro_rules! in_macro { + ($i:ident) => {{ + let _ = UnitStruct::default(); + let _ = $i::default(); + }}; + } + + in_macro!(UnitStruct); + + macro_rules! struct_from_macro { + () => { + UnitStruct + }; + } + + let _ = <struct_from_macro!()>::default(); } diff --git a/src/tools/clippy/tests/ui/default_constructed_unit_structs.stderr b/src/tools/clippy/tests/ui/default_constructed_unit_structs.stderr index 4058943d087..61a32fb10e5 100644 --- a/src/tools/clippy/tests/ui/default_constructed_unit_structs.stderr +++ b/src/tools/clippy/tests/ui/default_constructed_unit_structs.stderr @@ -25,10 +25,16 @@ LL | let _: PhantomData<i32> = PhantomData::default(); | ^^^^^^^^^^^ help: remove this call to `default` error: use of `default` to create a unit struct - --> $DIR/default_constructed_unit_structs.rs:108:23 + --> $DIR/default_constructed_unit_structs.rs:108:55 + | +LL | let _: PhantomData<i32> = std::marker::PhantomData::default(); + | ^^^^^^^^^^^ help: remove this call to `default` + +error: use of `default` to create a unit struct + --> $DIR/default_constructed_unit_structs.rs:109:23 | LL | let _ = UnitStruct::default(); | ^^^^^^^^^^^ help: remove this call to `default` -error: aborting due to 5 previous errors +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/empty_line_after_doc_comments.rs b/src/tools/clippy/tests/ui/empty_line_after_doc_comments.rs new file mode 100644 index 00000000000..e843770f578 --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_line_after_doc_comments.rs @@ -0,0 +1,132 @@ +//@aux-build:proc_macro_attr.rs +#![warn(clippy::empty_line_after_doc_comments)] +#![allow(clippy::assertions_on_constants)] +#![feature(custom_inner_attributes)] +#![rustfmt::skip] + +#[macro_use] +extern crate proc_macro_attr; + +mod some_mod { + //! This doc comment should *NOT* produce a warning + + mod some_inner_mod { + fn some_noop() {} + } +} + +/// This should produce a warning + +fn with_doc_and_newline() { assert!(true)} + +// This should *NOT* produce a warning +#[crate_type = "lib"] + +/// some comment +fn with_one_newline_and_comment() { assert!(true) } + +// This should *NOT* produce a warning +#[crate_type = "lib"] +/// some comment +fn with_no_newline_and_comment() { assert!(true) } + + +// This should *NOT* produce a warning +#[crate_type = "lib"] + +fn with_one_newline() { assert!(true) } + +// This should *NOT* produce a warning +#[crate_type = "lib"] + + +fn with_two_newlines() { assert!(true) } + + +// This should *NOT* produce a warning +#[crate_type = "lib"] + +enum Baz { + One, + Two +} + +// This should *NOT* produce a warning +#[crate_type = "lib"] + +struct Foo { + one: isize, + two: isize +} + +// This should *NOT* produce a warning +#[crate_type = "lib"] + +mod foo { +} + +/// This doc comment should produce a warning + +/** This is also a doc comment and should produce a warning + */ + +// This should *NOT* produce a warning +#[allow(non_camel_case_types)] +#[allow(missing_docs)] +#[allow(missing_docs)] +fn three_attributes() { assert!(true) } + +// This should *NOT* produce a warning +#[doc = " +Returns the escaped value of the textual representation of + +"] +pub fn function() -> bool { + true +} + +// This should *NOT* produce a warning +#[derive(Clone, Copy)] +pub enum FooFighter { + Bar1, + + Bar2, + + Bar3, + + Bar4 +} + +// This should *NOT* produce a warning because the empty line is inside a block comment +#[crate_type = "lib"] +/* + +*/ +pub struct S; + +// This should *NOT* produce a warning +#[crate_type = "lib"] +/* test */ +pub struct T; + +// This should *NOT* produce a warning +// See https://github.com/rust-lang/rust-clippy/issues/5567 +#[fake_async_trait] +pub trait Bazz { + fn foo() -> Vec<u8> { + let _i = ""; + + + + vec![] + } +} + +#[derive(Clone, Copy)] +#[dummy(string = "first line + +second line +")] +pub struct Args; + +fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_line_after_doc_comments.stderr b/src/tools/clippy/tests/ui/empty_line_after_doc_comments.stderr new file mode 100644 index 00000000000..2ca1b51679e --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_line_after_doc_comments.stderr @@ -0,0 +1,36 @@ +error: found an empty line after a doc comment. Perhaps you need to use `//!` to make a comment on a module, remove the empty line, or make a regular comment with `//`? + --> $DIR/empty_line_after_doc_comments.rs:18:1 + | +LL | / /// This should produce a warning +LL | | +LL | | fn with_doc_and_newline() { assert!(true)} + | |_ + | + = note: `-D clippy::empty-line-after-doc-comments` implied by `-D warnings` + +error: found an empty line after a doc comment. Perhaps you need to use `//!` to make a comment on a module, remove the empty line, or make a regular comment with `//`? + --> $DIR/empty_line_after_doc_comments.rs:68:1 + | +LL | / /// This doc comment should produce a warning +LL | | +LL | | /** This is also a doc comment and should produce a warning +LL | | */ +... | +LL | | #[allow(missing_docs)] +LL | | fn three_attributes() { assert!(true) } + | |_ + +error: found an empty line after a doc comment. Perhaps you need to use `//!` to make a comment on a module, remove the empty line, or make a regular comment with `//`? + --> $DIR/empty_line_after_doc_comments.rs:70:1 + | +LL | / /** This is also a doc comment and should produce a warning +LL | | */ +LL | | +LL | | // This should *NOT* produce a warning +... | +LL | | #[allow(missing_docs)] +LL | | fn three_attributes() { assert!(true) } + | |_ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/float_arithmetic.rs b/src/tools/clippy/tests/ui/float_arithmetic.rs index 60fa7569eb9..a928c35e8bc 100644 --- a/src/tools/clippy/tests/ui/float_arithmetic.rs +++ b/src/tools/clippy/tests/ui/float_arithmetic.rs @@ -1,4 +1,4 @@ -#![warn(clippy::integer_arithmetic, clippy::float_arithmetic)] +#![warn(clippy::arithmetic_side_effects, clippy::float_arithmetic)] #![allow( unused, clippy::shadow_reuse, diff --git a/src/tools/clippy/tests/ui/integer_arithmetic.rs b/src/tools/clippy/tests/ui/integer_arithmetic.rs deleted file mode 100644 index ab9b6094c2c..00000000000 --- a/src/tools/clippy/tests/ui/integer_arithmetic.rs +++ /dev/null @@ -1,109 +0,0 @@ -//@aux-build:proc_macro_derive.rs - -#![warn(clippy::integer_arithmetic, clippy::float_arithmetic)] -#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::op_ref)] - -extern crate proc_macro_derive; - -#[derive(proc_macro_derive::ShadowDerive)] -pub struct Nothing; - -#[rustfmt::skip] -fn main() { - let mut i = 1i32; - let mut var1 = 13i32; - let mut var2 = -1i32; - 1 + i; - i * 2; - 1 % - i / 2; // no error, this is part of the expression in the preceding line - i - 2 + 2 - i; - -i; - i >> 1; - i << 1; - - // no error, overflows are checked by `overflowing_literals` - -1; - -(-1); - - i & 1; // no wrapping - i | 1; - i ^ 1; - - i += 1; - i -= 1; - i *= 2; - i /= 2; - i /= 0; - i /= -1; - i /= var1; - i /= var2; - i %= 2; - i %= 0; - i %= -1; - i %= var1; - i %= var2; - i <<= 3; - i >>= 2; - - // no errors - i |= 1; - i &= 1; - i ^= i; - - // No errors for the following items because they are constant expressions - enum Foo { - Bar = -2, - } - struct Baz([i32; 1 + 1]); - union Qux { - field: [i32; 1 + 1], - } - type Alias = [i32; 1 + 1]; - - const FOO: i32 = -2; - static BAR: i32 = -2; - - let _: [i32; 1 + 1] = [0, 0]; - - let _: [i32; 1 + 1] = { - let a: [i32; 1 + 1] = [0, 0]; - a - }; - - trait Trait { - const ASSOC: i32 = 1 + 1; - } - - impl Trait for Foo { - const ASSOC: i32 = { - let _: [i32; 1 + 1]; - fn foo() {} - 1 + 1 - }; - } -} - -// warn on references as well! (#5328) -pub fn int_arith_ref() { - 3 + &1; - &3 + 1; - &3 + &1; -} - -pub fn foo(x: &i32) -> i32 { - let a = 5; - a + x -} - -pub fn bar(x: &i32, y: &i32) -> i32 { - x + y -} - -pub fn baz(x: i32, y: &i32) -> i32 { - x + y -} - -pub fn qux(x: i32, y: i32) -> i32 { - (&x + &y) -} diff --git a/src/tools/clippy/tests/ui/integer_arithmetic.stderr b/src/tools/clippy/tests/ui/integer_arithmetic.stderr deleted file mode 100644 index add3b6b90fa..00000000000 --- a/src/tools/clippy/tests/ui/integer_arithmetic.stderr +++ /dev/null @@ -1,169 +0,0 @@ -error: this operation will panic at runtime - --> $DIR/integer_arithmetic.rs:37:5 - | -LL | i /= 0; - | ^^^^^^ attempt to divide `_` by zero - | - = note: `#[deny(unconditional_panic)]` on by default - -error: this operation will panic at runtime - --> $DIR/integer_arithmetic.rs:42:5 - | -LL | i %= 0; - | ^^^^^^ attempt to calculate the remainder of `_` with a divisor of zero - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:16:5 - | -LL | 1 + i; - | ^^^^^ - | - = note: `-D clippy::integer-arithmetic` implied by `-D warnings` - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:17:5 - | -LL | i * 2; - | ^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:18:5 - | -LL | / 1 % -LL | | i / 2; // no error, this is part of the expression in the preceding line - | |_____^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:20:5 - | -LL | i - 2 + 2 - i; - | ^^^^^^^^^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:21:5 - | -LL | -i; - | ^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:22:5 - | -LL | i >> 1; - | ^^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:23:5 - | -LL | i << 1; - | ^^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:33:5 - | -LL | i += 1; - | ^^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:34:5 - | -LL | i -= 1; - | ^^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:35:5 - | -LL | i *= 2; - | ^^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:38:11 - | -LL | i /= -1; - | ^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:39:5 - | -LL | i /= var1; - | ^^^^^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:40:5 - | -LL | i /= var2; - | ^^^^^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:43:11 - | -LL | i %= -1; - | ^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:44:5 - | -LL | i %= var1; - | ^^^^^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:45:5 - | -LL | i %= var2; - | ^^^^^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:46:5 - | -LL | i <<= 3; - | ^^^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:47:5 - | -LL | i >>= 2; - | ^^^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:89:5 - | -LL | 3 + &1; - | ^^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:90:5 - | -LL | &3 + 1; - | ^^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:91:5 - | -LL | &3 + &1; - | ^^^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:96:5 - | -LL | a + x - | ^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:100:5 - | -LL | x + y - | ^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:104:5 - | -LL | x + y - | ^^^^^ - -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:108:5 - | -LL | (&x + &y) - | ^^^^^^^^^ - -error: aborting due to 27 previous errors - diff --git a/src/tools/clippy/tests/ui/let_underscore_untyped.rs b/src/tools/clippy/tests/ui/let_underscore_untyped.rs index 8486137d3a6..2c313ff35d5 100644 --- a/src/tools/clippy/tests/ui/let_underscore_untyped.rs +++ b/src/tools/clippy/tests/ui/let_underscore_untyped.rs @@ -1,6 +1,12 @@ +//@aux-build: proc_macros.rs + #![allow(unused)] #![warn(clippy::let_underscore_untyped)] +extern crate proc_macros; +use proc_macros::with_span; + +use clippy_utils::is_from_proc_macro; use std::future::Future; use std::{boxed::Box, fmt::Display}; @@ -32,6 +38,14 @@ fn g() -> impl Fn() { || {} } +with_span!( + span + + fn dont_lint_proc_macro() { + let _ = a(); + } +); + fn main() { let _ = a(); let _ = b(1); @@ -40,6 +54,7 @@ fn main() { let _ = e(); let _ = f(); let _ = g(); + let closure = || {}; _ = a(); _ = b(1); diff --git a/src/tools/clippy/tests/ui/let_underscore_untyped.stderr b/src/tools/clippy/tests/ui/let_underscore_untyped.stderr index 6844cb998f7..bbf2508af10 100644 --- a/src/tools/clippy/tests/ui/let_underscore_untyped.stderr +++ b/src/tools/clippy/tests/ui/let_underscore_untyped.stderr @@ -1,60 +1,60 @@ error: non-binding `let` without a type annotation - --> $DIR/let_underscore_untyped.rs:36:5 + --> $DIR/let_underscore_untyped.rs:50:5 | LL | let _ = a(); | ^^^^^^^^^^^^ | help: consider adding a type annotation - --> $DIR/let_underscore_untyped.rs:36:10 + --> $DIR/let_underscore_untyped.rs:50:10 | LL | let _ = a(); | ^ = note: `-D clippy::let-underscore-untyped` implied by `-D warnings` error: non-binding `let` without a type annotation - --> $DIR/let_underscore_untyped.rs:37:5 + --> $DIR/let_underscore_untyped.rs:51:5 | LL | let _ = b(1); | ^^^^^^^^^^^^^ | help: consider adding a type annotation - --> $DIR/let_underscore_untyped.rs:37:10 + --> $DIR/let_underscore_untyped.rs:51:10 | LL | let _ = b(1); | ^ error: non-binding `let` without a type annotation - --> $DIR/let_underscore_untyped.rs:39:5 + --> $DIR/let_underscore_untyped.rs:53:5 | LL | let _ = d(&1); | ^^^^^^^^^^^^^^ | help: consider adding a type annotation - --> $DIR/let_underscore_untyped.rs:39:10 + --> $DIR/let_underscore_untyped.rs:53:10 | LL | let _ = d(&1); | ^ error: non-binding `let` without a type annotation - --> $DIR/let_underscore_untyped.rs:40:5 + --> $DIR/let_underscore_untyped.rs:54:5 | LL | let _ = e(); | ^^^^^^^^^^^^ | help: consider adding a type annotation - --> $DIR/let_underscore_untyped.rs:40:10 + --> $DIR/let_underscore_untyped.rs:54:10 | LL | let _ = e(); | ^ error: non-binding `let` without a type annotation - --> $DIR/let_underscore_untyped.rs:41:5 + --> $DIR/let_underscore_untyped.rs:55:5 | LL | let _ = f(); | ^^^^^^^^^^^^ | help: consider adding a type annotation - --> $DIR/let_underscore_untyped.rs:41:10 + --> $DIR/let_underscore_untyped.rs:55:10 | LL | let _ = f(); | ^ diff --git a/src/tools/clippy/tests/ui/manual_let_else.rs b/src/tools/clippy/tests/ui/manual_let_else.rs index d175597a44a..3996d775f55 100644 --- a/src/tools/clippy/tests/ui/manual_let_else.rs +++ b/src/tools/clippy/tests/ui/manual_let_else.rs @@ -8,6 +8,12 @@ )] #![warn(clippy::manual_let_else)] +enum Variant { + A(usize, usize), + B(usize), + C, +} + fn g() -> Option<()> { None } @@ -135,6 +141,15 @@ fn fire() { }; } create_binding_if_some!(w, g()); + + fn e() -> Variant { + Variant::A(0, 0) + } + + // Should not be renamed + let v = if let Variant::A(a, 0) = e() { a } else { return }; + // Should be renamed + let v = if let Variant::B(b) = e() { b } else { return }; } fn not_fire() { diff --git a/src/tools/clippy/tests/ui/manual_let_else.stderr b/src/tools/clippy/tests/ui/manual_let_else.stderr index 52aac6bc673..f6f56f7b00e 100644 --- a/src/tools/clippy/tests/ui/manual_let_else.stderr +++ b/src/tools/clippy/tests/ui/manual_let_else.stderr @@ -1,13 +1,13 @@ error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:18:5 + --> $DIR/manual_let_else.rs:24:5 | LL | let v = if let Some(v_some) = g() { v_some } else { return }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v_some) = g() else { return };` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { return };` | = note: `-D clippy::manual-let-else` implied by `-D warnings` error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:19:5 + --> $DIR/manual_let_else.rs:25:5 | LL | / let v = if let Some(v_some) = g() { LL | | v_some @@ -18,13 +18,13 @@ LL | | }; | help: consider writing | -LL ~ let Some(v_some) = g() else { +LL ~ let Some(v) = g() else { LL + return; LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:25:5 + --> $DIR/manual_let_else.rs:31:5 | LL | / let v = if let Some(v) = g() { LL | | // Blocks around the identity should have no impact @@ -45,25 +45,25 @@ LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:38:9 + --> $DIR/manual_let_else.rs:44:9 | LL | let v = if let Some(v_some) = g() { v_some } else { continue }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v_some) = g() else { continue };` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { continue };` error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:39:9 + --> $DIR/manual_let_else.rs:45:9 | LL | let v = if let Some(v_some) = g() { v_some } else { break }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v_some) = g() else { break };` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { break };` error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:43:5 + --> $DIR/manual_let_else.rs:49:5 | LL | let v = if let Some(v_some) = g() { v_some } else { panic!() }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v_some) = g() else { panic!() };` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { panic!() };` error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:46:5 + --> $DIR/manual_let_else.rs:52:5 | LL | / let v = if let Some(v_some) = g() { LL | | v_some @@ -74,13 +74,13 @@ LL | | }; | help: consider writing | -LL ~ let Some(v_some) = g() else { +LL ~ let Some(v) = g() else { LL + std::process::abort() LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:53:5 + --> $DIR/manual_let_else.rs:59:5 | LL | / let v = if let Some(v_some) = g() { LL | | v_some @@ -91,13 +91,13 @@ LL | | }; | help: consider writing | -LL ~ let Some(v_some) = g() else { +LL ~ let Some(v) = g() else { LL + if true { return } else { panic!() } LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:60:5 + --> $DIR/manual_let_else.rs:66:5 | LL | / let v = if let Some(v_some) = g() { LL | | v_some @@ -109,14 +109,14 @@ LL | | }; | help: consider writing | -LL ~ let Some(v_some) = g() else { +LL ~ let Some(v) = g() else { LL + if true {} LL + panic!(); LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:70:5 + --> $DIR/manual_let_else.rs:76:5 | LL | / let v = if let Some(v_some) = g() { LL | | v_some @@ -129,7 +129,7 @@ LL | | }; | help: consider writing | -LL ~ let Some(v_some) = g() else { +LL ~ let Some(v) = g() else { LL + match () { LL + _ if panic!() => {}, LL + _ => panic!(), @@ -138,13 +138,13 @@ LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:80:5 + --> $DIR/manual_let_else.rs:86:5 | LL | let v = if let Some(v_some) = g() { v_some } else { if panic!() {} }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v_some) = g() else { if panic!() {} };` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { if panic!() {} };` error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:83:5 + --> $DIR/manual_let_else.rs:89:5 | LL | / let v = if let Some(v_some) = g() { LL | | v_some @@ -157,7 +157,7 @@ LL | | }; | help: consider writing | -LL ~ let Some(v_some) = g() else { +LL ~ let Some(v) = g() else { LL + match panic!() { LL + _ => {}, LL + } @@ -165,7 +165,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:92:5 + --> $DIR/manual_let_else.rs:98:5 | LL | / let v = if let Some(v_some) = g() { LL | | v_some @@ -178,7 +178,7 @@ LL | | }; | help: consider writing | -LL ~ let Some(v_some) = g() else { if true { +LL ~ let Some(v) = g() else { if true { LL + return; LL + } else { LL + panic!("diverge"); @@ -186,7 +186,7 @@ LL + } }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:101:5 + --> $DIR/manual_let_else.rs:107:5 | LL | / let v = if let Some(v_some) = g() { LL | | v_some @@ -199,7 +199,7 @@ LL | | }; | help: consider writing | -LL ~ let Some(v_some) = g() else { +LL ~ let Some(v) = g() else { LL + match (g(), g()) { LL + (Some(_), None) => return, LL + (None, Some(_)) => { @@ -215,7 +215,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:118:5 + --> $DIR/manual_let_else.rs:124:5 | LL | / let (v, w) = if let Some(v_some) = g().map(|v| (v, 42)) { LL | | v_some @@ -226,13 +226,13 @@ LL | | }; | help: consider writing | -LL ~ let Some(v_some) = g().map(|v| (v, 42)) else { +LL ~ let Some((v, w)) = g().map(|v| (v, 42)) else { LL + return; LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:125:5 + --> $DIR/manual_let_else.rs:131:5 | LL | / let v = if let (Some(v_some), w_some) = (g(), 0) { LL | | (w_some, v_some) @@ -249,10 +249,10 @@ LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:134:13 + --> $DIR/manual_let_else.rs:140:13 | LL | let $n = if let Some(v) = $e { v } else { return }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { return };` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some($n) = g() else { return };` ... LL | create_binding_if_some!(w, g()); | ------------------------------- in this macro invocation @@ -260,13 +260,25 @@ LL | create_binding_if_some!(w, g()); = note: this error originates in the macro `create_binding_if_some` (in Nightly builds, run with -Z macro-backtrace for more info) error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:247:5 + --> $DIR/manual_let_else.rs:150:5 + | +LL | let v = if let Variant::A(a, 0) = e() { a } else { return }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::A(a, 0) = e() else { return };` + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:152:5 + | +LL | let v = if let Variant::B(b) = e() { b } else { return }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::B(v) = e() else { return };` + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:262:5 | LL | / let _ = match ff { LL | | Some(value) => value, LL | | _ => macro_call!(), LL | | }; - | |______^ help: consider writing: `let Some(value) = ff else { macro_call!() };` + | |______^ help: consider writing: `let Some(_) = ff else { macro_call!() };` -error: aborting due to 18 previous errors +error: aborting due to 20 previous errors diff --git a/src/tools/clippy/tests/ui/manual_let_else_match.stderr b/src/tools/clippy/tests/ui/manual_let_else_match.stderr index 7abaa0b85d2..bacc14dc967 100644 --- a/src/tools/clippy/tests/ui/manual_let_else_match.stderr +++ b/src/tools/clippy/tests/ui/manual_let_else_match.stderr @@ -5,7 +5,7 @@ LL | / let v = match g() { LL | | Some(v_some) => v_some, LL | | None => return, LL | | }; - | |______^ help: consider writing: `let Some(v_some) = g() else { return };` + | |______^ help: consider writing: `let Some(v) = g() else { return };` | = note: `-D clippy::manual-let-else` implied by `-D warnings` @@ -16,7 +16,7 @@ LL | / let v = match g() { LL | | Some(v_some) => v_some, LL | | _ => return, LL | | }; - | |______^ help: consider writing: `let Some(v_some) = g() else { return };` + | |______^ help: consider writing: `let Some(v) = g() else { return };` error: this could be rewritten as `let...else` --> $DIR/manual_let_else_match.rs:44:9 diff --git a/src/tools/clippy/tests/ui/manual_next_back.fixed b/src/tools/clippy/tests/ui/manual_next_back.fixed new file mode 100644 index 00000000000..e8a47063ad6 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_next_back.fixed @@ -0,0 +1,36 @@ +//@run-rustfix + +#![allow(unused)] +#![warn(clippy::manual_next_back)] + +struct FakeIter(std::ops::Range<i32>); + +impl FakeIter { + fn rev(self) -> Self { + self + } + + fn next(&self) {} +} + +impl DoubleEndedIterator for FakeIter { + fn next_back(&mut self) -> Option<Self::Item> { + self.0.next_back() + } +} + +impl Iterator for FakeIter { + type Item = i32; + fn next(&mut self) -> Option<Self::Item> { + self.0.next() + } +} + +fn main() { + // should not lint + FakeIter(0..10).rev().next(); + + // should lint + let _ = (0..10).next_back().unwrap(); + let _ = "something".bytes().next_back(); +} diff --git a/src/tools/clippy/tests/ui/manual_next_back.rs b/src/tools/clippy/tests/ui/manual_next_back.rs new file mode 100644 index 00000000000..9ec89242241 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_next_back.rs @@ -0,0 +1,36 @@ +//@run-rustfix + +#![allow(unused)] +#![warn(clippy::manual_next_back)] + +struct FakeIter(std::ops::Range<i32>); + +impl FakeIter { + fn rev(self) -> Self { + self + } + + fn next(&self) {} +} + +impl DoubleEndedIterator for FakeIter { + fn next_back(&mut self) -> Option<Self::Item> { + self.0.next_back() + } +} + +impl Iterator for FakeIter { + type Item = i32; + fn next(&mut self) -> Option<Self::Item> { + self.0.next() + } +} + +fn main() { + // should not lint + FakeIter(0..10).rev().next(); + + // should lint + let _ = (0..10).rev().next().unwrap(); + let _ = "something".bytes().rev().next(); +} diff --git a/src/tools/clippy/tests/ui/manual_next_back.stderr b/src/tools/clippy/tests/ui/manual_next_back.stderr new file mode 100644 index 00000000000..94ccaa9e4cc --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_next_back.stderr @@ -0,0 +1,16 @@ +error: manual backwards iteration + --> $DIR/manual_next_back.rs:34:20 + | +LL | let _ = (0..10).rev().next().unwrap(); + | ^^^^^^^^^^^^^ help: use: `.next_back()` + | + = note: `-D clippy::manual-next-back` implied by `-D warnings` + +error: manual backwards iteration + --> $DIR/manual_next_back.rs:35:32 + | +LL | let _ = "something".bytes().rev().next(); + | ^^^^^^^^^^^^^ help: use: `.next_back()` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed index 7215660da67..60f59066173 100644 --- a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed +++ b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed @@ -15,7 +15,7 @@ fn main() { let _y = matches!(x, Some(0)); // Lint - let _w = matches!(x, Some(_)); + let _w = x.is_some(); // Turn into is_none let _z = x.is_none(); diff --git a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.stderr b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.stderr index 46f67ef4900..b72fe10b748 100644 --- a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.stderr +++ b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.stderr @@ -10,7 +10,7 @@ LL | | }; | = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` -error: match expression looks like `matches!` macro +error: redundant pattern matching, consider using `is_some()` --> $DIR/match_expr_like_matches_macro.rs:21:14 | LL | let _w = match x { @@ -18,7 +18,9 @@ LL | let _w = match x { LL | | Some(_) => true, LL | | _ => false, LL | | }; - | |_____^ help: try this: `matches!(x, Some(_))` + | |_____^ help: try this: `x.is_some()` + | + = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_none()` --> $DIR/match_expr_like_matches_macro.rs:27:14 @@ -29,8 +31,6 @@ LL | | Some(_) => false, LL | | None => true, LL | | }; | |_____^ help: try this: `x.is_none()` - | - = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: match expression looks like `matches!` macro --> $DIR/match_expr_like_matches_macro.rs:33:15 diff --git a/src/tools/clippy/tests/ui/match_same_arms.rs b/src/tools/clippy/tests/ui/match_same_arms.rs index 0b9342c9c42..3914b45464c 100644 --- a/src/tools/clippy/tests/ui/match_same_arms.rs +++ b/src/tools/clippy/tests/ui/match_same_arms.rs @@ -53,4 +53,84 @@ mod issue4244 { } } -fn main() {} +macro_rules! m { + (foo) => {}; + (bar) => {}; +} +macro_rules! foo { + () => { + 1 + }; +} +macro_rules! bar { + () => { + 1 + }; +} + +fn main() { + let x = 0; + let _ = match 0 { + 0 => { + m!(foo); + x + }, + 1 => { + m!(bar); + x + }, + _ => 1, + }; + + let _ = match 0 { + 0 => { + m!(foo); + 0 + }, + 1 => { + m!(bar); + 0 + }, + _ => 1, + }; + + let _ = match 0 { + 0 => { + let mut x = 0; + #[cfg(not_enabled)] + { + x = 5; + } + #[cfg(not(not_enabled))] + { + x = 6; + } + x + }, + 1 => { + let mut x = 0; + #[cfg(also_not_enabled)] + { + x = 5; + } + #[cfg(not(also_not_enabled))] + { + x = 6; + } + x + }, + _ => 0, + }; + + let _ = match 0 { + 0 => foo!(), + 1 => bar!(), + _ => 1, + }; + + let _ = match 0 { + 0 => cfg!(not_enabled), + 1 => cfg!(also_not_enabled), + _ => false, + }; +} diff --git a/src/tools/clippy/tests/ui/match_same_arms2.rs b/src/tools/clippy/tests/ui/match_same_arms2.rs index 82b2c433d99..60b2975be04 100644 --- a/src/tools/clippy/tests/ui/match_same_arms2.rs +++ b/src/tools/clippy/tests/ui/match_same_arms2.rs @@ -239,4 +239,10 @@ fn main() { 3 => core::convert::identity::<u32>(todo!()), _ => 5, }; + + let _ = match 0 { + 0 => cfg!(not_enable), + 1 => cfg!(not_enable), + _ => false, + }; } diff --git a/src/tools/clippy/tests/ui/match_same_arms2.stderr b/src/tools/clippy/tests/ui/match_same_arms2.stderr index 06cd4300054..8fb461bd286 100644 --- a/src/tools/clippy/tests/ui/match_same_arms2.stderr +++ b/src/tools/clippy/tests/ui/match_same_arms2.stderr @@ -192,5 +192,20 @@ note: other arm here LL | Some(Bar { x: 0, y: 5, .. }) => 1, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 12 previous errors +error: this match arm has an identical body to another arm + --> $DIR/match_same_arms2.rs:245:9 + | +LL | 1 => cfg!(not_enable), + | -^^^^^^^^^^^^^^^^^^^^ + | | + | help: try merging the arm patterns: `1 | 0` + | + = help: or try changing either arm body +note: other arm here + --> $DIR/match_same_arms2.rs:244:9 + | +LL | 0 => cfg!(not_enable), + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/mem_forget.rs b/src/tools/clippy/tests/ui/mem_forget.rs index 5137448a6d4..edb9d87d032 100644 --- a/src/tools/clippy/tests/ui/mem_forget.rs +++ b/src/tools/clippy/tests/ui/mem_forget.rs @@ -5,7 +5,7 @@ use std::mem as memstuff; use std::mem::forget as forgetSomething; #[warn(clippy::mem_forget)] -#[allow(forget_copy)] +#[allow(forgetting_copy_types)] fn main() { let five: i32 = 5; forgetSomething(five); diff --git a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs index f28153e56b0..4ef6f0ca92f 100644 --- a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs +++ b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs @@ -2,7 +2,7 @@ #![allow(unused)] #![allow(deref_nullptr)] #![allow(clippy::unnecessary_operation)] -#![allow(drop_copy)] +#![allow(dropping_copy_types)] #![warn(clippy::multiple_unsafe_ops_per_block)] extern crate proc_macros; diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.fixed b/src/tools/clippy/tests/ui/needless_bool/fixable.fixed index f860852e7b7..bf1911881c8 100644 --- a/src/tools/clippy/tests/ui/needless_bool/fixable.fixed +++ b/src/tools/clippy/tests/ui/needless_bool/fixable.fixed @@ -63,6 +63,13 @@ fn main() { needless_bool2(x); needless_bool3(x); needless_bool_condition(); + + if a == b { + true + } else { + // Do not lint as this comment might be important + false + }; } fn bool_ret3(x: bool) -> bool { diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.rs b/src/tools/clippy/tests/ui/needless_bool/fixable.rs index 6680dab5b6d..a6c465d4fbd 100644 --- a/src/tools/clippy/tests/ui/needless_bool/fixable.rs +++ b/src/tools/clippy/tests/ui/needless_bool/fixable.rs @@ -99,6 +99,13 @@ fn main() { needless_bool2(x); needless_bool3(x); needless_bool_condition(); + + if a == b { + true + } else { + // Do not lint as this comment might be important + false + }; } fn bool_ret3(x: bool) -> bool { diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.stderr b/src/tools/clippy/tests/ui/needless_bool/fixable.stderr index d2c48376f76..fa906374fb3 100644 --- a/src/tools/clippy/tests/ui/needless_bool/fixable.stderr +++ b/src/tools/clippy/tests/ui/needless_bool/fixable.stderr @@ -91,7 +91,7 @@ LL | | }; | |_____^ help: you can reduce it to: `a < b` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:105:5 + --> $DIR/fixable.rs:112:5 | LL | / if x { LL | | return true; @@ -101,7 +101,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return x` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:113:5 + --> $DIR/fixable.rs:120:5 | LL | / if x { LL | | return false; @@ -111,7 +111,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return !x` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:121:5 + --> $DIR/fixable.rs:128:5 | LL | / if x && y { LL | | return true; @@ -121,7 +121,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return x && y` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:129:5 + --> $DIR/fixable.rs:136:5 | LL | / if x && y { LL | | return false; @@ -131,7 +131,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return !(x && y)` error: equality checks against true are unnecessary - --> $DIR/fixable.rs:137:8 + --> $DIR/fixable.rs:144:8 | LL | if x == true {}; | ^^^^^^^^^ help: try simplifying it as shown: `x` @@ -139,25 +139,25 @@ LL | if x == true {}; = note: `-D clippy::bool-comparison` implied by `-D warnings` error: equality checks against false can be replaced by a negation - --> $DIR/fixable.rs:141:8 + --> $DIR/fixable.rs:148:8 | LL | if x == false {}; | ^^^^^^^^^^ help: try simplifying it as shown: `!x` error: equality checks against true are unnecessary - --> $DIR/fixable.rs:151:8 + --> $DIR/fixable.rs:158:8 | LL | if x == true {}; | ^^^^^^^^^ help: try simplifying it as shown: `x` error: equality checks against false can be replaced by a negation - --> $DIR/fixable.rs:152:8 + --> $DIR/fixable.rs:159:8 | LL | if x == false {}; | ^^^^^^^^^^ help: try simplifying it as shown: `!x` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:161:12 + --> $DIR/fixable.rs:168:12 | LL | } else if returns_bool() { | ____________^ @@ -168,7 +168,7 @@ LL | | }; | |_____^ help: you can reduce it to: `{ !returns_bool() }` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:174:5 + --> $DIR/fixable.rs:181:5 | LL | / if unsafe { no(4) } & 1 != 0 { LL | | true @@ -178,13 +178,13 @@ LL | | }; | |_____^ help: you can reduce it to: `(unsafe { no(4) } & 1 != 0)` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:179:30 + --> $DIR/fixable.rs:186:30 | LL | let _brackets_unneeded = if unsafe { no(4) } & 1 != 0 { true } else { false }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `unsafe { no(4) } & 1 != 0` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:182:9 + --> $DIR/fixable.rs:189:9 | LL | if unsafe { no(4) } & 1 != 0 { true } else { false } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `(unsafe { no(4) } & 1 != 0)` diff --git a/src/tools/clippy/tests/ui/needless_collect.fixed b/src/tools/clippy/tests/ui/needless_collect.fixed index 024c22de225..b7e80af5015 100644 --- a/src/tools/clippy/tests/ui/needless_collect.fixed +++ b/src/tools/clippy/tests/ui/needless_collect.fixed @@ -62,4 +62,16 @@ fn main() { let _ = sample.iter().next().is_none(); let _ = sample.iter().any(|x| x == &0); + + #[allow(clippy::double_parens)] + { + Vec::<u8>::new().extend((0..10)); + foo((0..10)); + bar((0..10).collect::<Vec<_>>(), (0..10)); + baz((0..10), (), ('a'..='z')) + } } + +fn foo(_: impl IntoIterator<Item = usize>) {} +fn bar<I: IntoIterator<Item = usize>>(_: Vec<usize>, _: I) {} +fn baz<I: IntoIterator<Item = usize>>(_: I, _: (), _: impl IntoIterator<Item = char>) {} diff --git a/src/tools/clippy/tests/ui/needless_collect.rs b/src/tools/clippy/tests/ui/needless_collect.rs index 7ed7babec30..680b6fa5b55 100644 --- a/src/tools/clippy/tests/ui/needless_collect.rs +++ b/src/tools/clippy/tests/ui/needless_collect.rs @@ -62,4 +62,16 @@ fn main() { let _ = sample.iter().collect::<VecWrapper<_>>().is_empty(); let _ = sample.iter().collect::<VecWrapper<_>>().contains(&&0); + + #[allow(clippy::double_parens)] + { + Vec::<u8>::new().extend((0..10).collect::<Vec<_>>()); + foo((0..10).collect::<Vec<_>>()); + bar((0..10).collect::<Vec<_>>(), (0..10).collect::<Vec<_>>()); + baz((0..10), (), ('a'..='z').collect::<Vec<_>>()) + } } + +fn foo(_: impl IntoIterator<Item = usize>) {} +fn bar<I: IntoIterator<Item = usize>>(_: Vec<usize>, _: I) {} +fn baz<I: IntoIterator<Item = usize>>(_: I, _: (), _: impl IntoIterator<Item = char>) {} diff --git a/src/tools/clippy/tests/ui/needless_collect.stderr b/src/tools/clippy/tests/ui/needless_collect.stderr index 584d2a1d835..ad22a7b057e 100644 --- a/src/tools/clippy/tests/ui/needless_collect.stderr +++ b/src/tools/clippy/tests/ui/needless_collect.stderr @@ -90,5 +90,29 @@ error: avoid using `collect()` when not needed LL | let _ = sample.iter().collect::<VecWrapper<_>>().contains(&&0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == &0)` -error: aborting due to 15 previous errors +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:68:40 + | +LL | Vec::<u8>::new().extend((0..10).collect::<Vec<_>>()); + | ^^^^^^^^^^^^^^^^^^^^ help: remove this call + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:69:20 + | +LL | foo((0..10).collect::<Vec<_>>()); + | ^^^^^^^^^^^^^^^^^^^^ help: remove this call + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:70:49 + | +LL | bar((0..10).collect::<Vec<_>>(), (0..10).collect::<Vec<_>>()); + | ^^^^^^^^^^^^^^^^^^^^ help: remove this call + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:71:37 + | +LL | baz((0..10), (), ('a'..='z').collect::<Vec<_>>()) + | ^^^^^^^^^^^^^^^^^^^^ help: remove this call + +error: aborting due to 19 previous errors diff --git a/src/tools/clippy/tests/ui/needless_return.fixed b/src/tools/clippy/tests/ui/needless_return.fixed index ee4e5007dc5..d49ae5d8636 100644 --- a/src/tools/clippy/tests/ui/needless_return.fixed +++ b/src/tools/clippy/tests/ui/needless_return.fixed @@ -231,8 +231,9 @@ fn needless_return_macro() -> String { } fn issue_9361() -> i32 { - #[allow(clippy::integer_arithmetic)] - return 1 + 2; + let n = 1; + #[allow(clippy::arithmetic_side_effects)] + return n + n; } fn issue8336(x: i32) -> bool { diff --git a/src/tools/clippy/tests/ui/needless_return.rs b/src/tools/clippy/tests/ui/needless_return.rs index cd999db4f40..36763826174 100644 --- a/src/tools/clippy/tests/ui/needless_return.rs +++ b/src/tools/clippy/tests/ui/needless_return.rs @@ -239,8 +239,9 @@ fn needless_return_macro() -> String { } fn issue_9361() -> i32 { - #[allow(clippy::integer_arithmetic)] - return 1 + 2; + let n = 1; + #[allow(clippy::arithmetic_side_effects)] + return n + n; } fn issue8336(x: i32) -> bool { diff --git a/src/tools/clippy/tests/ui/needless_return.stderr b/src/tools/clippy/tests/ui/needless_return.stderr index 87d0cd3e14c..05f6038cd25 100644 --- a/src/tools/clippy/tests/ui/needless_return.stderr +++ b/src/tools/clippy/tests/ui/needless_return.stderr @@ -328,7 +328,7 @@ LL | return format!("Hello {}", "world!"); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:249:9 + --> $DIR/needless_return.rs:250:9 | LL | return true; | ^^^^^^^^^^^ @@ -336,7 +336,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:251:9 + --> $DIR/needless_return.rs:252:9 | LL | return false; | ^^^^^^^^^^^^ @@ -344,7 +344,7 @@ LL | return false; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:258:13 + --> $DIR/needless_return.rs:259:13 | LL | return 10; | ^^^^^^^^^ @@ -352,7 +352,7 @@ LL | return 10; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:261:13 + --> $DIR/needless_return.rs:262:13 | LL | return 100; | ^^^^^^^^^^ @@ -360,7 +360,7 @@ LL | return 100; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:269:9 + --> $DIR/needless_return.rs:270:9 | LL | return 0; | ^^^^^^^^ @@ -368,7 +368,7 @@ LL | return 0; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:276:13 + --> $DIR/needless_return.rs:277:13 | LL | return *(x as *const isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -376,7 +376,7 @@ LL | return *(x as *const isize); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:278:13 + --> $DIR/needless_return.rs:279:13 | LL | return !*(x as *const isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -384,7 +384,7 @@ LL | return !*(x as *const isize); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:285:20 + --> $DIR/needless_return.rs:286:20 | LL | let _ = 42; | ____________________^ @@ -395,7 +395,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:292:20 + --> $DIR/needless_return.rs:293:20 | LL | let _ = 42; return; | ^^^^^^^ @@ -403,7 +403,7 @@ LL | let _ = 42; return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:304:9 + --> $DIR/needless_return.rs:305:9 | LL | return Ok(format!("ok!")); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -411,7 +411,7 @@ LL | return Ok(format!("ok!")); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:306:9 + --> $DIR/needless_return.rs:307:9 | LL | return Err(format!("err!")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -419,7 +419,7 @@ LL | return Err(format!("err!")); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:312:9 + --> $DIR/needless_return.rs:313:9 | LL | return if true { 1 } else { 2 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -427,7 +427,7 @@ LL | return if true { 1 } else { 2 }; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:316:9 + --> $DIR/needless_return.rs:317:9 | LL | return if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/non_minimal_cfg.fixed b/src/tools/clippy/tests/ui/non_minimal_cfg.fixed new file mode 100644 index 00000000000..430caafb33e --- /dev/null +++ b/src/tools/clippy/tests/ui/non_minimal_cfg.fixed @@ -0,0 +1,17 @@ +//@run-rustfix + +#![allow(unused)] + +#[cfg(windows)] +fn hermit() {} + +#[cfg(windows)] +fn wasi() {} + +#[cfg(all(unix, not(windows)))] +fn the_end() {} + +#[cfg(any())] +fn any() {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/non_minimal_cfg.rs b/src/tools/clippy/tests/ui/non_minimal_cfg.rs new file mode 100644 index 00000000000..a38ce1c21d6 --- /dev/null +++ b/src/tools/clippy/tests/ui/non_minimal_cfg.rs @@ -0,0 +1,17 @@ +//@run-rustfix + +#![allow(unused)] + +#[cfg(all(windows))] +fn hermit() {} + +#[cfg(any(windows))] +fn wasi() {} + +#[cfg(all(any(unix), all(not(windows))))] +fn the_end() {} + +#[cfg(any())] +fn any() {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/non_minimal_cfg.stderr b/src/tools/clippy/tests/ui/non_minimal_cfg.stderr new file mode 100644 index 00000000000..cdfd728aa61 --- /dev/null +++ b/src/tools/clippy/tests/ui/non_minimal_cfg.stderr @@ -0,0 +1,28 @@ +error: unneeded sub `cfg` when there is only one condition + --> $DIR/non_minimal_cfg.rs:5:7 + | +LL | #[cfg(all(windows))] + | ^^^^^^^^^^^^ help: try: `windows` + | + = note: `-D clippy::non-minimal-cfg` implied by `-D warnings` + +error: unneeded sub `cfg` when there is only one condition + --> $DIR/non_minimal_cfg.rs:8:7 + | +LL | #[cfg(any(windows))] + | ^^^^^^^^^^^^ help: try: `windows` + +error: unneeded sub `cfg` when there is only one condition + --> $DIR/non_minimal_cfg.rs:11:11 + | +LL | #[cfg(all(any(unix), all(not(windows))))] + | ^^^^^^^^^ help: try: `unix` + +error: unneeded sub `cfg` when there is only one condition + --> $DIR/non_minimal_cfg.rs:11:22 + | +LL | #[cfg(all(any(unix), all(not(windows))))] + | ^^^^^^^^^^^^^^^^^ help: try: `not(windows)` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/non_minimal_cfg2.rs b/src/tools/clippy/tests/ui/non_minimal_cfg2.rs new file mode 100644 index 00000000000..a4c6abce387 --- /dev/null +++ b/src/tools/clippy/tests/ui/non_minimal_cfg2.rs @@ -0,0 +1,6 @@ +#![allow(unused)] + +#[cfg(all())] +fn all() {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/non_minimal_cfg2.stderr b/src/tools/clippy/tests/ui/non_minimal_cfg2.stderr new file mode 100644 index 00000000000..2a9a36fbcef --- /dev/null +++ b/src/tools/clippy/tests/ui/non_minimal_cfg2.stderr @@ -0,0 +1,10 @@ +error: unneeded sub `cfg` when there is no condition + --> $DIR/non_minimal_cfg2.rs:3:7 + | +LL | #[cfg(all())] + | ^^^^^ + | + = note: `-D clippy::non-minimal-cfg` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/option_if_let_else.fixed b/src/tools/clippy/tests/ui/option_if_let_else.fixed index 57f341e0276..2b8ce5477cc 100644 --- a/src/tools/clippy/tests/ui/option_if_let_else.fixed +++ b/src/tools/clippy/tests/ui/option_if_let_else.fixed @@ -92,6 +92,15 @@ fn pattern_to_vec(pattern: &str) -> Vec<String> { .collect::<Vec<_>>() } +// #10335 +fn test_result_impure_else(variable: Result<u32, &str>) { + variable.map_or_else(|_| { + println!("Err"); + }, |binding| { + println!("Ok {binding}"); + }) +} + enum DummyEnum { One(u8), Two, @@ -113,6 +122,7 @@ fn main() { unop_bad(&None, None); let _ = longer_body(None); test_map_or_else(None); + test_result_impure_else(Ok(42)); let _ = negative_tests(None); let _ = impure_else(None); diff --git a/src/tools/clippy/tests/ui/option_if_let_else.rs b/src/tools/clippy/tests/ui/option_if_let_else.rs index 19f9f704517..cfbec8cb27d 100644 --- a/src/tools/clippy/tests/ui/option_if_let_else.rs +++ b/src/tools/clippy/tests/ui/option_if_let_else.rs @@ -115,6 +115,15 @@ fn pattern_to_vec(pattern: &str) -> Vec<String> { .collect::<Vec<_>>() } +// #10335 +fn test_result_impure_else(variable: Result<u32, &str>) { + if let Ok(binding) = variable { + println!("Ok {binding}"); + } else { + println!("Err"); + } +} + enum DummyEnum { One(u8), Two, @@ -136,6 +145,7 @@ fn main() { unop_bad(&None, None); let _ = longer_body(None); test_map_or_else(None); + test_result_impure_else(Ok(42)); let _ = negative_tests(None); let _ = impure_else(None); diff --git a/src/tools/clippy/tests/ui/option_if_let_else.stderr b/src/tools/clippy/tests/ui/option_if_let_else.stderr index f5e4affb672..91d52fc79b8 100644 --- a/src/tools/clippy/tests/ui/option_if_let_else.stderr +++ b/src/tools/clippy/tests/ui/option_if_let_else.stderr @@ -152,14 +152,33 @@ LL | | vec![s.to_string()] LL | | } | |_____________^ help: try: `s.find('.').map_or_else(|| vec![s.to_string()], |idx| vec![s[..idx].to_string(), s[idx..].to_string()])` +error: use Option::map_or_else instead of an if let/else + --> $DIR/option_if_let_else.rs:120:5 + | +LL | / if let Ok(binding) = variable { +LL | | println!("Ok {binding}"); +LL | | } else { +LL | | println!("Err"); +LL | | } + | |_____^ + | +help: try + | +LL ~ variable.map_or_else(|_| { +LL + println!("Err"); +LL + }, |binding| { +LL + println!("Ok {binding}"); +LL + }) + | + error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:133:13 + --> $DIR/option_if_let_else.rs:142:13 | LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:142:13 + --> $DIR/option_if_let_else.rs:152:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -181,13 +200,13 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:170:13 + --> $DIR/option_if_let_else.rs:180:13 | LL | let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or(s.len(), |x| s.len() + x)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:174:13 + --> $DIR/option_if_let_else.rs:184:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -207,7 +226,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:213:13 + --> $DIR/option_if_let_else.rs:223:13 | LL | let _ = match s { | _____________^ @@ -217,7 +236,7 @@ LL | | }; | |_____^ help: try: `s.map_or(1, |string| string.len())` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:217:13 + --> $DIR/option_if_let_else.rs:227:13 | LL | let _ = match Some(10) { | _____________^ @@ -227,7 +246,7 @@ LL | | }; | |_____^ help: try: `Some(10).map_or(5, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:223:13 + --> $DIR/option_if_let_else.rs:233:13 | LL | let _ = match res { | _____________^ @@ -237,7 +256,7 @@ LL | | }; | |_____^ help: try: `res.map_or(1, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:227:13 + --> $DIR/option_if_let_else.rs:237:13 | LL | let _ = match res { | _____________^ @@ -247,10 +266,10 @@ LL | | }; | |_____^ help: try: `res.map_or(1, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:231:13 + --> $DIR/option_if_let_else.rs:241:13 | LL | let _ = if let Ok(a) = res { a + 1 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `res.map_or(5, |a| a + 1)` -error: aborting due to 20 previous errors +error: aborting due to 21 previous errors diff --git a/src/tools/clippy/tests/ui/partialeq_to_none.fixed b/src/tools/clippy/tests/ui/partialeq_to_none.fixed index 81a716bd276..2df87a26d6d 100644 --- a/src/tools/clippy/tests/ui/partialeq_to_none.fixed +++ b/src/tools/clippy/tests/ui/partialeq_to_none.fixed @@ -1,5 +1,6 @@ //@run-rustfix #![warn(clippy::partialeq_to_none)] +#![allow(clippy::eq_op)] struct Foobar; diff --git a/src/tools/clippy/tests/ui/partialeq_to_none.rs b/src/tools/clippy/tests/ui/partialeq_to_none.rs index f454715fa30..df6233b9afd 100644 --- a/src/tools/clippy/tests/ui/partialeq_to_none.rs +++ b/src/tools/clippy/tests/ui/partialeq_to_none.rs @@ -1,5 +1,6 @@ //@run-rustfix #![warn(clippy::partialeq_to_none)] +#![allow(clippy::eq_op)] struct Foobar; diff --git a/src/tools/clippy/tests/ui/partialeq_to_none.stderr b/src/tools/clippy/tests/ui/partialeq_to_none.stderr index d06ab7aee55..4f84862a22b 100644 --- a/src/tools/clippy/tests/ui/partialeq_to_none.stderr +++ b/src/tools/clippy/tests/ui/partialeq_to_none.stderr @@ -1,5 +1,5 @@ error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:14:8 + --> $DIR/partialeq_to_none.rs:15:8 | LL | if f != None { "yay" } else { "nay" } | ^^^^^^^^^ help: use `Option::is_some()` instead: `f.is_some()` @@ -7,55 +7,55 @@ LL | if f != None { "yay" } else { "nay" } = note: `-D clippy::partialeq-to-none` implied by `-D warnings` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:44:13 + --> $DIR/partialeq_to_none.rs:45:13 | LL | let _ = x == None; | ^^^^^^^^^ help: use `Option::is_none()` instead: `x.is_none()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:45:13 + --> $DIR/partialeq_to_none.rs:46:13 | LL | let _ = x != None; | ^^^^^^^^^ help: use `Option::is_some()` instead: `x.is_some()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:46:13 + --> $DIR/partialeq_to_none.rs:47:13 | LL | let _ = None == x; | ^^^^^^^^^ help: use `Option::is_none()` instead: `x.is_none()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:47:13 + --> $DIR/partialeq_to_none.rs:48:13 | LL | let _ = None != x; | ^^^^^^^^^ help: use `Option::is_some()` instead: `x.is_some()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:49:8 + --> $DIR/partialeq_to_none.rs:50:8 | LL | if foobar() == None {} | ^^^^^^^^^^^^^^^^ help: use `Option::is_none()` instead: `foobar().is_none()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:51:8 + --> $DIR/partialeq_to_none.rs:52:8 | LL | if bar().ok() != None {} | ^^^^^^^^^^^^^^^^^^ help: use `Option::is_some()` instead: `bar().ok().is_some()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:53:13 + --> $DIR/partialeq_to_none.rs:54:13 | LL | let _ = Some(1 + 2) != None; | ^^^^^^^^^^^^^^^^^^^ help: use `Option::is_some()` instead: `Some(1 + 2).is_some()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:55:13 + --> $DIR/partialeq_to_none.rs:56:13 | LL | let _ = { Some(0) } == None; | ^^^^^^^^^^^^^^^^^^^ help: use `Option::is_none()` instead: `{ Some(0) }.is_none()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:57:13 + --> $DIR/partialeq_to_none.rs:58:13 | LL | let _ = { | _____________^ @@ -77,31 +77,31 @@ LL ~ }.is_some(); | error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:67:13 + --> $DIR/partialeq_to_none.rs:68:13 | LL | let _ = optref() == &&None; | ^^^^^^^^^^^^^^^^^^ help: use `Option::is_none()` instead: `optref().is_none()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:68:13 + --> $DIR/partialeq_to_none.rs:69:13 | LL | let _ = &&None != optref(); | ^^^^^^^^^^^^^^^^^^ help: use `Option::is_some()` instead: `optref().is_some()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:69:13 + --> $DIR/partialeq_to_none.rs:70:13 | LL | let _ = **optref() == None; | ^^^^^^^^^^^^^^^^^^ help: use `Option::is_none()` instead: `optref().is_none()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:70:13 + --> $DIR/partialeq_to_none.rs:71:13 | LL | let _ = &None != *optref(); | ^^^^^^^^^^^^^^^^^^ help: use `Option::is_some()` instead: `optref().is_some()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:73:13 + --> $DIR/partialeq_to_none.rs:74:13 | LL | let _ = None != *x; | ^^^^^^^^^^ help: use `Option::is_some()` instead: `(*x).is_some()` diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed index d62f7d26a35..accdf1da9dd 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed @@ -46,6 +46,7 @@ fn main() { let _ = if opt.is_some() { true } else { false }; issue6067(); + issue10726(); let _ = if gen_opt().is_some() { 1 @@ -88,3 +89,21 @@ fn issue7921() { if (&None::<()>).is_none() {} if (&None::<()>).is_none() {} } + +fn issue10726() { + let x = Some(42); + + x.is_some(); + + x.is_none(); + + x.is_none(); + + x.is_some(); + + // Don't lint + match x { + Some(21) => true, + _ => false, + }; +} diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs index d6429426573..ec684bdf71c 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs @@ -55,6 +55,7 @@ fn main() { let _ = if let Some(_) = opt { true } else { false }; issue6067(); + issue10726(); let _ = if let Some(_) = gen_opt() { 1 @@ -103,3 +104,33 @@ fn issue7921() { if let None = *(&None::<()>) {} if let None = *&None::<()> {} } + +fn issue10726() { + let x = Some(42); + + match x { + Some(_) => true, + _ => false, + }; + + match x { + None => true, + _ => false, + }; + + match x { + Some(_) => false, + _ => true, + }; + + match x { + None => false, + _ => true, + }; + + // Don't lint + match x { + Some(21) => true, + _ => false, + }; +} diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr index 7c5a047e455..a69eb390520 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr @@ -77,49 +77,49 @@ LL | let _ = if let Some(_) = opt { true } else { false }; | -------^^^^^^^------ help: try this: `if opt.is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:59:20 + --> $DIR/redundant_pattern_matching_option.rs:60:20 | LL | let _ = if let Some(_) = gen_opt() { | -------^^^^^^^------------ help: try this: `if gen_opt().is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:61:19 + --> $DIR/redundant_pattern_matching_option.rs:62:19 | LL | } else if let None = gen_opt() { | -------^^^^------------ help: try this: `if gen_opt().is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:67:12 + --> $DIR/redundant_pattern_matching_option.rs:68:12 | LL | if let Some(..) = gen_opt() {} | -------^^^^^^^^------------ help: try this: `if gen_opt().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:82:12 + --> $DIR/redundant_pattern_matching_option.rs:83:12 | LL | if let Some(_) = Some(42) {} | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:84:12 + --> $DIR/redundant_pattern_matching_option.rs:85:12 | LL | if let None = None::<()> {} | -------^^^^------------- help: try this: `if None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:86:15 + --> $DIR/redundant_pattern_matching_option.rs:87:15 | LL | while let Some(_) = Some(42) {} | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:88:15 + --> $DIR/redundant_pattern_matching_option.rs:89:15 | LL | while let None = None::<()> {} | ----------^^^^------------- help: try this: `while None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:90:5 + --> $DIR/redundant_pattern_matching_option.rs:91:5 | LL | / match Some(42) { LL | | Some(_) => true, @@ -128,7 +128,7 @@ LL | | }; | |_____^ help: try this: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:95:5 + --> $DIR/redundant_pattern_matching_option.rs:96:5 | LL | / match None::<()> { LL | | Some(_) => false, @@ -137,16 +137,52 @@ LL | | }; | |_____^ help: try this: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:103:12 + --> $DIR/redundant_pattern_matching_option.rs:104:12 | LL | if let None = *(&None::<()>) {} | -------^^^^----------------- help: try this: `if (&None::<()>).is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:104:12 + --> $DIR/redundant_pattern_matching_option.rs:105:12 | LL | if let None = *&None::<()> {} | -------^^^^--------------- help: try this: `if (&None::<()>).is_none()` -error: aborting due to 22 previous errors +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_option.rs:111:5 + | +LL | / match x { +LL | | Some(_) => true, +LL | | _ => false, +LL | | }; + | |_____^ help: try this: `x.is_some()` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching_option.rs:116:5 + | +LL | / match x { +LL | | None => true, +LL | | _ => false, +LL | | }; + | |_____^ help: try this: `x.is_none()` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching_option.rs:121:5 + | +LL | / match x { +LL | | Some(_) => false, +LL | | _ => true, +LL | | }; + | |_____^ help: try this: `x.is_none()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_option.rs:126:5 + | +LL | / match x { +LL | | None => false, +LL | | _ => true, +LL | | }; + | |_____^ help: try this: `x.is_some()` + +error: aborting due to 26 previous errors diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed index c48d1522935..e4032ae44b7 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed @@ -43,6 +43,7 @@ fn main() { issue5504(); issue6067(); issue6065(); + issue10726(); let _ = if gen_res().is_ok() { 1 @@ -107,3 +108,28 @@ const fn issue6067() { Err::<i32, i32>(42).is_err(); } + +fn issue10726() { + // This is optional, but it makes the examples easier + let x: Result<i32, i32> = Ok(42); + + x.is_ok(); + + x.is_err(); + + x.is_err(); + + x.is_ok(); + + // Don't lint + match x { + Err(16) => false, + _ => true, + }; + + // Don't lint + match x { + Ok(16) => false, + _ => true, + }; +} diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs index 26f37d169fa..39eb10df878 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs @@ -55,6 +55,7 @@ fn main() { issue5504(); issue6067(); issue6065(); + issue10726(); let _ = if let Ok(_) = gen_res() { 1 @@ -125,3 +126,40 @@ const fn issue6067() { Err(_) => true, }; } + +fn issue10726() { + // This is optional, but it makes the examples easier + let x: Result<i32, i32> = Ok(42); + + match x { + Ok(_) => true, + _ => false, + }; + + match x { + Ok(_) => false, + _ => true, + }; + + match x { + Err(_) => true, + _ => false, + }; + + match x { + Err(_) => false, + _ => true, + }; + + // Don't lint + match x { + Err(16) => false, + _ => true, + }; + + // Don't lint + match x { + Ok(16) => false, + _ => true, + }; +} diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr index d6a46babb77..5893ae4dcc4 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr @@ -73,67 +73,67 @@ LL | let _ = if let Ok(_) = Ok::<usize, ()>(4) { true } else { false }; | -------^^^^^--------------------- help: try this: `if Ok::<usize, ()>(4).is_ok()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:59:20 + --> $DIR/redundant_pattern_matching_result.rs:60:20 | LL | let _ = if let Ok(_) = gen_res() { | -------^^^^^------------ help: try this: `if gen_res().is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:61:19 + --> $DIR/redundant_pattern_matching_result.rs:62:19 | LL | } else if let Err(_) = gen_res() { | -------^^^^^^------------ help: try this: `if gen_res().is_err()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:84:19 + --> $DIR/redundant_pattern_matching_result.rs:85:19 | LL | while let Some(_) = r#try!(result_opt()) {} | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:85:16 + --> $DIR/redundant_pattern_matching_result.rs:86:16 | LL | if let Some(_) = r#try!(result_opt()) {} | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:91:12 + --> $DIR/redundant_pattern_matching_result.rs:92:12 | LL | if let Some(_) = m!() {} | -------^^^^^^^------- help: try this: `if m!().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:92:15 + --> $DIR/redundant_pattern_matching_result.rs:93:15 | LL | while let Some(_) = m!() {} | ----------^^^^^^^------- help: try this: `while m!().is_some()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:110:12 + --> $DIR/redundant_pattern_matching_result.rs:111:12 | LL | if let Ok(_) = Ok::<i32, i32>(42) {} | -------^^^^^--------------------- help: try this: `if Ok::<i32, i32>(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:112:12 + --> $DIR/redundant_pattern_matching_result.rs:113:12 | LL | if let Err(_) = Err::<i32, i32>(42) {} | -------^^^^^^---------------------- help: try this: `if Err::<i32, i32>(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:114:15 + --> $DIR/redundant_pattern_matching_result.rs:115:15 | LL | while let Ok(_) = Ok::<i32, i32>(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:116:15 + --> $DIR/redundant_pattern_matching_result.rs:117:15 | LL | while let Err(_) = Ok::<i32, i32>(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:118:5 + --> $DIR/redundant_pattern_matching_result.rs:119:5 | LL | / match Ok::<i32, i32>(42) { LL | | Ok(_) => true, @@ -142,7 +142,7 @@ LL | | }; | |_____^ help: try this: `Ok::<i32, i32>(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:123:5 + --> $DIR/redundant_pattern_matching_result.rs:124:5 | LL | / match Err::<i32, i32>(42) { LL | | Ok(_) => false, @@ -150,5 +150,41 @@ LL | | Err(_) => true, LL | | }; | |_____^ help: try this: `Err::<i32, i32>(42).is_err()` -error: aborting due to 22 previous errors +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_result.rs:134:5 + | +LL | / match x { +LL | | Ok(_) => true, +LL | | _ => false, +LL | | }; + | |_____^ help: try this: `x.is_ok()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching_result.rs:139:5 + | +LL | / match x { +LL | | Ok(_) => false, +LL | | _ => true, +LL | | }; + | |_____^ help: try this: `x.is_err()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching_result.rs:144:5 + | +LL | / match x { +LL | | Err(_) => true, +LL | | _ => false, +LL | | }; + | |_____^ help: try this: `x.is_err()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_result.rs:149:5 + | +LL | / match x { +LL | | Err(_) => false, +LL | | _ => true, +LL | | }; + | |_____^ help: try this: `x.is_ok()` + +error: aborting due to 26 previous errors diff --git a/src/tools/clippy/tests/ui/ref_patterns.rs b/src/tools/clippy/tests/ui/ref_patterns.rs new file mode 100644 index 00000000000..c51e0bc76ef --- /dev/null +++ b/src/tools/clippy/tests/ui/ref_patterns.rs @@ -0,0 +1,19 @@ +#![allow(unused)] +#![warn(clippy::ref_patterns)] + +fn use_in_pattern() { + let opt = Some(5); + match opt { + None => {}, + Some(ref opt) => {}, + } +} + +fn use_in_binding() { + let x = 5; + let ref y = x; +} + +fn use_in_parameter(ref x: i32) {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/ref_patterns.stderr b/src/tools/clippy/tests/ui/ref_patterns.stderr new file mode 100644 index 00000000000..aa007782683 --- /dev/null +++ b/src/tools/clippy/tests/ui/ref_patterns.stderr @@ -0,0 +1,27 @@ +error: usage of ref pattern + --> $DIR/ref_patterns.rs:8:14 + | +LL | Some(ref opt) => {}, + | ^^^^^^^ + | + = help: consider using `&` for clarity instead + = note: `-D clippy::ref-patterns` implied by `-D warnings` + +error: usage of ref pattern + --> $DIR/ref_patterns.rs:14:9 + | +LL | let ref y = x; + | ^^^^^ + | + = help: consider using `&` for clarity instead + +error: usage of ref pattern + --> $DIR/ref_patterns.rs:17:21 + | +LL | fn use_in_parameter(ref x: i32) {} + | ^^^^^ + | + = help: consider using `&` for clarity instead + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/regex.rs b/src/tools/clippy/tests/ui/regex.rs index ab8ac97a0e7..a5f79b139bc 100644 --- a/src/tools/clippy/tests/ui/regex.rs +++ b/src/tools/clippy/tests/ui/regex.rs @@ -34,8 +34,10 @@ fn syntax_error() { let set_error = RegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]); let bset_error = BRegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]); + // These following three cases are considering valid since regex-1.8.0 let raw_string_error = Regex::new(r"[...\/...]"); let raw_string_error = Regex::new(r#"[...\/...]"#); + let _ = Regex::new(r"(?<hi>hi)").unwrap(); let escaped_string_span = Regex::new("\\b\\c"); diff --git a/src/tools/clippy/tests/ui/regex.stderr b/src/tools/clippy/tests/ui/regex.stderr index c2440f39e0a..6b8a772e7f0 100644 --- a/src/tools/clippy/tests/ui/regex.stderr +++ b/src/tools/clippy/tests/ui/regex.stderr @@ -82,23 +82,11 @@ error: regex parse error: LL | let bset_error = BRegexSet::new(&[OPENING_PAREN, r"[a-z]+/.(com|org|net)"]); | ^^^^^^^^^^^^^ -error: regex syntax error: unrecognized escape sequence - --> $DIR/regex.rs:37:45 - | -LL | let raw_string_error = Regex::new(r"[...//...]"); - | ^^ - -error: regex syntax error: unrecognized escape sequence - --> $DIR/regex.rs:38:46 - | -LL | let raw_string_error = Regex::new(r#"[...//...]"#); - | ^^ - error: regex parse error: /b/c ^^ error: unrecognized escape sequence - --> $DIR/regex.rs:40:42 + --> $DIR/regex.rs:42:42 | LL | let escaped_string_span = Regex::new("/b/c"); | ^^^^^^^^ @@ -106,13 +94,13 @@ LL | let escaped_string_span = Regex::new("/b/c"); = help: consider using a raw string literal: `r".."` error: regex syntax error: duplicate flag - --> $DIR/regex.rs:42:34 + --> $DIR/regex.rs:44:34 | LL | let aux_span = Regex::new("(?ixi)"); | ^ ^ error: trivial regex - --> $DIR/regex.rs:46:33 + --> $DIR/regex.rs:48:33 | LL | let trivial_eq = Regex::new("^foobar$"); | ^^^^^^^^^^ @@ -120,7 +108,7 @@ LL | let trivial_eq = Regex::new("^foobar$"); = help: consider using `==` on `str`s error: trivial regex - --> $DIR/regex.rs:48:48 + --> $DIR/regex.rs:50:48 | LL | let trivial_eq_builder = RegexBuilder::new("^foobar$"); | ^^^^^^^^^^ @@ -128,7 +116,7 @@ LL | let trivial_eq_builder = RegexBuilder::new("^foobar$"); = help: consider using `==` on `str`s error: trivial regex - --> $DIR/regex.rs:50:42 + --> $DIR/regex.rs:52:42 | LL | let trivial_starts_with = Regex::new("^foobar"); | ^^^^^^^^^ @@ -136,7 +124,7 @@ LL | let trivial_starts_with = Regex::new("^foobar"); = help: consider using `str::starts_with` error: trivial regex - --> $DIR/regex.rs:52:40 + --> $DIR/regex.rs:54:40 | LL | let trivial_ends_with = Regex::new("foobar$"); | ^^^^^^^^^ @@ -144,7 +132,7 @@ LL | let trivial_ends_with = Regex::new("foobar$"); = help: consider using `str::ends_with` error: trivial regex - --> $DIR/regex.rs:54:39 + --> $DIR/regex.rs:56:39 | LL | let trivial_contains = Regex::new("foobar"); | ^^^^^^^^ @@ -152,7 +140,7 @@ LL | let trivial_contains = Regex::new("foobar"); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:56:39 + --> $DIR/regex.rs:58:39 | LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX); | ^^^^^^^^^^^^^^^^ @@ -160,7 +148,7 @@ LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:58:40 + --> $DIR/regex.rs:60:40 | LL | let trivial_backslash = Regex::new("a/.b"); | ^^^^^^^ @@ -168,7 +156,7 @@ LL | let trivial_backslash = Regex::new("a/.b"); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:61:36 + --> $DIR/regex.rs:63:36 | LL | let trivial_empty = Regex::new(""); | ^^ @@ -176,7 +164,7 @@ LL | let trivial_empty = Regex::new(""); = help: the regex is unlikely to be useful as it is error: trivial regex - --> $DIR/regex.rs:63:36 + --> $DIR/regex.rs:65:36 | LL | let trivial_empty = Regex::new("^"); | ^^^ @@ -184,7 +172,7 @@ LL | let trivial_empty = Regex::new("^"); = help: the regex is unlikely to be useful as it is error: trivial regex - --> $DIR/regex.rs:65:36 + --> $DIR/regex.rs:67:36 | LL | let trivial_empty = Regex::new("^$"); | ^^^^ @@ -192,12 +180,12 @@ LL | let trivial_empty = Regex::new("^$"); = help: consider using `str::is_empty` error: trivial regex - --> $DIR/regex.rs:67:44 + --> $DIR/regex.rs:69:44 | LL | let binary_trivial_empty = BRegex::new("^$"); | ^^^^ | = help: consider using `str::is_empty` -error: aborting due to 25 previous errors +error: aborting due to 23 previous errors diff --git a/src/tools/clippy/tests/ui/rename.fixed b/src/tools/clippy/tests/ui/rename.fixed index 9036f892612..dfe45dec8a7 100644 --- a/src/tools/clippy/tests/ui/rename.fixed +++ b/src/tools/clippy/tests/ui/rename.fixed @@ -16,6 +16,7 @@ #![allow(clippy::mixed_read_write_in_expression)] #![allow(clippy::useless_conversion)] #![allow(clippy::match_result_ok)] +#![allow(clippy::arithmetic_side_effects)] #![allow(clippy::overly_complex_bool_expr)] #![allow(clippy::new_without_default)] #![allow(clippy::bind_instead_of_map)] @@ -29,11 +30,11 @@ #![allow(clippy::invisible_characters)] #![allow(suspicious_double_ref_op)] #![allow(drop_bounds)] -#![allow(drop_copy)] -#![allow(drop_ref)] +#![allow(dropping_copy_types)] +#![allow(dropping_references)] #![allow(for_loops_over_fallibles)] -#![allow(forget_copy)] -#![allow(forget_ref)] +#![allow(forgetting_copy_types)] +#![allow(forgetting_references)] #![allow(array_into_iter)] #![allow(invalid_atomic_ordering)] #![allow(invalid_value)] @@ -58,6 +59,7 @@ #![warn(clippy::mixed_read_write_in_expression)] #![warn(clippy::useless_conversion)] #![warn(clippy::match_result_ok)] +#![warn(clippy::arithmetic_side_effects)] #![warn(clippy::overly_complex_bool_expr)] #![warn(clippy::new_without_default)] #![warn(clippy::bind_instead_of_map)] @@ -75,13 +77,13 @@ #![warn(clippy::invisible_characters)] #![warn(suspicious_double_ref_op)] #![warn(drop_bounds)] -#![warn(drop_copy)] -#![warn(drop_ref)] +#![warn(dropping_copy_types)] +#![warn(dropping_references)] #![warn(for_loops_over_fallibles)] #![warn(for_loops_over_fallibles)] #![warn(for_loops_over_fallibles)] -#![warn(forget_copy)] -#![warn(forget_ref)] +#![warn(forgetting_copy_types)] +#![warn(forgetting_references)] #![warn(array_into_iter)] #![warn(invalid_atomic_ordering)] #![warn(invalid_value)] diff --git a/src/tools/clippy/tests/ui/rename.rs b/src/tools/clippy/tests/ui/rename.rs index 43cabe810f3..ce8eca5a308 100644 --- a/src/tools/clippy/tests/ui/rename.rs +++ b/src/tools/clippy/tests/ui/rename.rs @@ -16,6 +16,7 @@ #![allow(clippy::mixed_read_write_in_expression)] #![allow(clippy::useless_conversion)] #![allow(clippy::match_result_ok)] +#![allow(clippy::arithmetic_side_effects)] #![allow(clippy::overly_complex_bool_expr)] #![allow(clippy::new_without_default)] #![allow(clippy::bind_instead_of_map)] @@ -29,11 +30,11 @@ #![allow(clippy::invisible_characters)] #![allow(suspicious_double_ref_op)] #![allow(drop_bounds)] -#![allow(drop_copy)] -#![allow(drop_ref)] +#![allow(dropping_copy_types)] +#![allow(dropping_references)] #![allow(for_loops_over_fallibles)] -#![allow(forget_copy)] -#![allow(forget_ref)] +#![allow(forgetting_copy_types)] +#![allow(forgetting_references)] #![allow(array_into_iter)] #![allow(invalid_atomic_ordering)] #![allow(invalid_value)] @@ -58,6 +59,7 @@ #![warn(clippy::eval_order_dependence)] #![warn(clippy::identity_conversion)] #![warn(clippy::if_let_some_result)] +#![warn(clippy::integer_arithmetic)] #![warn(clippy::logic_bug)] #![warn(clippy::new_without_default_derive)] #![warn(clippy::option_and_then_some)] diff --git a/src/tools/clippy/tests/ui/rename.stderr b/src/tools/clippy/tests/ui/rename.stderr index 1ad7cf412c8..3fca60aa2eb 100644 --- a/src/tools/clippy/tests/ui/rename.stderr +++ b/src/tools/clippy/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` - --> $DIR/rename.rs:48:9 + --> $DIR/rename.rs:49:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` @@ -7,280 +7,286 @@ LL | #![warn(clippy::almost_complete_letter_range)] = note: `-D renamed-and-removed-lints` implied by `-D warnings` error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> $DIR/rename.rs:49:9 + --> $DIR/rename.rs:50:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:50:9 + --> $DIR/rename.rs:51:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:51:9 + --> $DIR/rename.rs:52:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> $DIR/rename.rs:52:9 + --> $DIR/rename.rs:53:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> $DIR/rename.rs:53:9 + --> $DIR/rename.rs:54:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> $DIR/rename.rs:54:9 + --> $DIR/rename.rs:55:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq` - --> $DIR/rename.rs:55:9 + --> $DIR/rename.rs:56:9 | LL | #![warn(clippy::derive_hash_xor_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> $DIR/rename.rs:56:9 + --> $DIR/rename.rs:57:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> $DIR/rename.rs:57:9 + --> $DIR/rename.rs:58:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> $DIR/rename.rs:58:9 + --> $DIR/rename.rs:59:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> $DIR/rename.rs:59:9 + --> $DIR/rename.rs:60:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> $DIR/rename.rs:60:9 + --> $DIR/rename.rs:61:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` +error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` + --> $DIR/rename.rs:62:9 + | +LL | #![warn(clippy::integer_arithmetic)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` + error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> $DIR/rename.rs:61:9 + --> $DIR/rename.rs:63:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> $DIR/rename.rs:62:9 + --> $DIR/rename.rs:64:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> $DIR/rename.rs:63:9 + --> $DIR/rename.rs:65:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:64:9 + --> $DIR/rename.rs:66:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:65:9 + --> $DIR/rename.rs:67:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:66:9 + --> $DIR/rename.rs:68:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:67:9 + --> $DIR/rename.rs:69:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> $DIR/rename.rs:68:9 + --> $DIR/rename.rs:70:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:69:9 + --> $DIR/rename.rs:71:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:70:9 + --> $DIR/rename.rs:72:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:71:9 + --> $DIR/rename.rs:73:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> $DIR/rename.rs:72:9 + --> $DIR/rename.rs:74:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> $DIR/rename.rs:73:9 + --> $DIR/rename.rs:75:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> $DIR/rename.rs:74:9 + --> $DIR/rename.rs:76:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> $DIR/rename.rs:75:9 + --> $DIR/rename.rs:77:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op` - --> $DIR/rename.rs:76:9 + --> $DIR/rename.rs:78:9 | LL | #![warn(clippy::clone_double_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> $DIR/rename.rs:77:9 + --> $DIR/rename.rs:79:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` -error: lint `clippy::drop_copy` has been renamed to `drop_copy` - --> $DIR/rename.rs:78:9 +error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` + --> $DIR/rename.rs:80:9 | LL | #![warn(clippy::drop_copy)] - | ^^^^^^^^^^^^^^^^^ help: use the new name: `drop_copy` + | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` -error: lint `clippy::drop_ref` has been renamed to `drop_ref` - --> $DIR/rename.rs:79:9 +error: lint `clippy::drop_ref` has been renamed to `dropping_references` + --> $DIR/rename.rs:81:9 | LL | #![warn(clippy::drop_ref)] - | ^^^^^^^^^^^^^^^^ help: use the new name: `drop_ref` + | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:80:9 + --> $DIR/rename.rs:82:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:81:9 + --> $DIR/rename.rs:83:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:82:9 + --> $DIR/rename.rs:84:9 | LL | #![warn(clippy::for_loops_over_fallibles)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` -error: lint `clippy::forget_copy` has been renamed to `forget_copy` - --> $DIR/rename.rs:83:9 +error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` + --> $DIR/rename.rs:85:9 | LL | #![warn(clippy::forget_copy)] - | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forget_copy` + | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` -error: lint `clippy::forget_ref` has been renamed to `forget_ref` - --> $DIR/rename.rs:84:9 +error: lint `clippy::forget_ref` has been renamed to `forgetting_references` + --> $DIR/rename.rs:86:9 | LL | #![warn(clippy::forget_ref)] - | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forget_ref` + | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> $DIR/rename.rs:85:9 + --> $DIR/rename.rs:87:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> $DIR/rename.rs:86:9 + --> $DIR/rename.rs:88:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> $DIR/rename.rs:87:9 + --> $DIR/rename.rs:89:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` - --> $DIR/rename.rs:88:9 + --> $DIR/rename.rs:90:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> $DIR/rename.rs:89:9 + --> $DIR/rename.rs:91:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> $DIR/rename.rs:90:9 + --> $DIR/rename.rs:92:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` - --> $DIR/rename.rs:91:9 + --> $DIR/rename.rs:93:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` - --> $DIR/rename.rs:92:9 + --> $DIR/rename.rs:94:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> $DIR/rename.rs:93:9 + --> $DIR/rename.rs:95:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> $DIR/rename.rs:94:9 + --> $DIR/rename.rs:96:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` -error: aborting due to 47 previous errors +error: aborting due to 48 previous errors diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.fixed b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.fixed index eef8024b131..fdac0e4cb1e 100644 --- a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.fixed +++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.fixed @@ -2,6 +2,8 @@ #![deny(clippy::trait_duplication_in_bounds)] #![allow(unused)] +use std::any::Any; + fn bad_foo<T: Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) { unimplemented!(); } @@ -109,4 +111,12 @@ fn qualified_path<T: std::clone::Clone + foo::Clone>(arg0: T) { unimplemented!(); } +fn good_trait_object(arg0: &(dyn Any + Send)) { + unimplemented!(); +} + +fn bad_trait_object(arg0: &(dyn Any + Send)) { + unimplemented!(); +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs index a7a1caf2880..a0300da5555 100644 --- a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs +++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs @@ -2,6 +2,8 @@ #![deny(clippy::trait_duplication_in_bounds)] #![allow(unused)] +use std::any::Any; + fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) { unimplemented!(); } @@ -109,4 +111,12 @@ fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) { unimplemented!(); } +fn good_trait_object(arg0: &(dyn Any + Send)) { + unimplemented!(); +} + +fn bad_trait_object(arg0: &(dyn Any + Send + Send)) { + unimplemented!(); +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr index af800ba7888..539b6114ca3 100644 --- a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr +++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr @@ -1,5 +1,5 @@ error: these bounds contain repeated elements - --> $DIR/trait_duplication_in_bounds.rs:5:15 + --> $DIR/trait_duplication_in_bounds.rs:7:15 | LL | fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy` @@ -11,46 +11,52 @@ LL | #![deny(clippy::trait_duplication_in_bounds)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: these where clauses contain repeated elements - --> $DIR/trait_duplication_in_bounds.rs:11:8 + --> $DIR/trait_duplication_in_bounds.rs:13:8 | LL | T: Clone + Clone + Clone + Copy, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy` error: these bounds contain repeated elements - --> $DIR/trait_duplication_in_bounds.rs:39:26 + --> $DIR/trait_duplication_in_bounds.rs:41:26 | LL | trait BadSelfTraitBound: Clone + Clone + Clone { | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone` error: these where clauses contain repeated elements - --> $DIR/trait_duplication_in_bounds.rs:46:15 + --> $DIR/trait_duplication_in_bounds.rs:48:15 | LL | Self: Clone + Clone + Clone; | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone` error: these bounds contain repeated elements - --> $DIR/trait_duplication_in_bounds.rs:60:24 + --> $DIR/trait_duplication_in_bounds.rs:62:24 | LL | trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy` error: these where clauses contain repeated elements - --> $DIR/trait_duplication_in_bounds.rs:67:12 + --> $DIR/trait_duplication_in_bounds.rs:69:12 | LL | T: Clone + Clone + Clone + Copy, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy` error: these bounds contain repeated elements - --> $DIR/trait_duplication_in_bounds.rs:100:19 + --> $DIR/trait_duplication_in_bounds.rs:102:19 | LL | fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `GenericTrait<u64> + GenericTrait<u32>` error: these bounds contain repeated elements - --> $DIR/trait_duplication_in_bounds.rs:108:22 + --> $DIR/trait_duplication_in_bounds.rs:110:22 | LL | fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::clone::Clone + foo::Clone` -error: aborting due to 8 previous errors +error: this trait bound is already specified in trait declaration + --> $DIR/trait_duplication_in_bounds.rs:118:33 + | +LL | fn bad_trait_object(arg0: &(dyn Any + Send + Send)) { + | ^^^^^^^^^^^^^^^^^ help: try: `Any + Send` + +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/useless_conversion.fixed b/src/tools/clippy/tests/ui/useless_conversion.fixed index 01eb6c5b080..c16caa38fe9 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.fixed +++ b/src/tools/clippy/tests/ui/useless_conversion.fixed @@ -33,6 +33,11 @@ fn test_issue_3913() -> Result<(), std::io::Error> { Ok(()) } +fn dont_lint_on_type_alias() { + type A = i32; + _ = A::from(0i32); +} + fn dont_lint_into_iter_on_immutable_local_implementing_iterator_in_expr() { let text = "foo\r\nbar\n\nbaz\n"; let lines = text.lines(); @@ -106,6 +111,7 @@ fn main() { test_questionmark().unwrap(); test_issue_3913().unwrap(); + dont_lint_on_type_alias(); dont_lint_into_iter_on_immutable_local_implementing_iterator_in_expr(); lint_into_iter_on_mutable_local_implementing_iterator_in_expr(); lint_into_iter_on_expr_implementing_iterator(); diff --git a/src/tools/clippy/tests/ui/useless_conversion.rs b/src/tools/clippy/tests/ui/useless_conversion.rs index 34b43a6299b..c75a2bce4ca 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.rs +++ b/src/tools/clippy/tests/ui/useless_conversion.rs @@ -33,6 +33,11 @@ fn test_issue_3913() -> Result<(), std::io::Error> { Ok(()) } +fn dont_lint_on_type_alias() { + type A = i32; + _ = A::from(0i32); +} + fn dont_lint_into_iter_on_immutable_local_implementing_iterator_in_expr() { let text = "foo\r\nbar\n\nbaz\n"; let lines = text.lines(); @@ -106,6 +111,7 @@ fn main() { test_questionmark().unwrap(); test_issue_3913().unwrap(); + dont_lint_on_type_alias(); dont_lint_into_iter_on_immutable_local_implementing_iterator_in_expr(); lint_into_iter_on_mutable_local_implementing_iterator_in_expr(); lint_into_iter_on_expr_implementing_iterator(); diff --git a/src/tools/clippy/tests/ui/useless_conversion.stderr b/src/tools/clippy/tests/ui/useless_conversion.stderr index be067c6843a..4dca3aac533 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.stderr +++ b/src/tools/clippy/tests/ui/useless_conversion.stderr @@ -23,97 +23,97 @@ LL | let _: i32 = 0i32.into(); | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` error: useless conversion to the same type: `std::str::Lines<'_>` - --> $DIR/useless_conversion.rs:45:22 + --> $DIR/useless_conversion.rs:50:22 | LL | if Some("ok") == lines.into_iter().next() {} | ^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `lines` error: useless conversion to the same type: `std::str::Lines<'_>` - --> $DIR/useless_conversion.rs:50:21 + --> $DIR/useless_conversion.rs:55:21 | LL | let mut lines = text.lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `text.lines()` error: useless conversion to the same type: `std::str::Lines<'_>` - --> $DIR/useless_conversion.rs:56:22 + --> $DIR/useless_conversion.rs:61:22 | LL | if Some("ok") == text.lines().into_iter().next() {} | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `text.lines()` error: useless conversion to the same type: `std::ops::Range<i32>` - --> $DIR/useless_conversion.rs:62:13 + --> $DIR/useless_conversion.rs:67:13 | LL | let _ = NUMBERS.into_iter().next(); | ^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `NUMBERS` error: useless conversion to the same type: `std::ops::Range<i32>` - --> $DIR/useless_conversion.rs:67:17 + --> $DIR/useless_conversion.rs:72:17 | LL | let mut n = NUMBERS.into_iter(); | ^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `NUMBERS` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:128:21 + --> $DIR/useless_conversion.rs:134:21 | LL | let _: String = "foo".to_string().into(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:129:21 + --> $DIR/useless_conversion.rs:135:21 | LL | let _: String = From::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:130:13 + --> $DIR/useless_conversion.rs:136:13 | LL | let _ = String::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:131:13 + --> $DIR/useless_conversion.rs:137:13 | LL | let _ = String::from(format!("A: {:04}", 123)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)` error: useless conversion to the same type: `std::str::Lines<'_>` - --> $DIR/useless_conversion.rs:132:13 + --> $DIR/useless_conversion.rs:138:13 | LL | let _ = "".lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` error: useless conversion to the same type: `std::vec::IntoIter<i32>` - --> $DIR/useless_conversion.rs:133:13 + --> $DIR/useless_conversion.rs:139:13 | LL | let _ = vec![1, 2, 3].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:134:21 + --> $DIR/useless_conversion.rs:140:21 | LL | let _: String = format!("Hello {}", "world").into(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")` error: useless conversion to the same type: `i32` - --> $DIR/useless_conversion.rs:139:13 + --> $DIR/useless_conversion.rs:145:13 | LL | let _ = i32::from(a + b) * 3; | ^^^^^^^^^^^^^^^^ help: consider removing `i32::from()`: `(a + b)` error: useless conversion to the same type: `Foo<'a'>` - --> $DIR/useless_conversion.rs:145:23 + --> $DIR/useless_conversion.rs:151:23 | LL | let _: Foo<'a'> = s2.into(); | ^^^^^^^^^ help: consider removing `.into()`: `s2` error: useless conversion to the same type: `Foo<'a'>` - --> $DIR/useless_conversion.rs:147:13 + --> $DIR/useless_conversion.rs:153:13 | LL | let _ = Foo::<'a'>::from(s3); | ^^^^^^^^^^^^^^^^^^^^ help: consider removing `Foo::<'a'>::from()`: `s3` error: useless conversion to the same type: `std::vec::IntoIter<Foo<'a'>>` - --> $DIR/useless_conversion.rs:149:13 + --> $DIR/useless_conversion.rs:155:13 | LL | let _ = vec![s4, s4, s4].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![s4, s4, s4].into_iter()` diff --git a/src/tools/clippy/tests/ui/wildcard_imports_cfgtest.rs b/src/tools/clippy/tests/ui/wildcard_imports_cfgtest.rs new file mode 100644 index 00000000000..203c4e15b50 --- /dev/null +++ b/src/tools/clippy/tests/ui/wildcard_imports_cfgtest.rs @@ -0,0 +1,19 @@ +//@compile-flags: --test + +#![warn(clippy::wildcard_imports)] +#![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)] + +// Test for #10580, the lint should ignore it because of the crate's cfg test flag. + +fn foofoo() {} + +mod outer { + mod inner { + use super::super::*; + fn barbar() { + let _ = foofoo(); + } + } +} + +fn main() {} diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml index 3f8f6a7b98c..c40b71f6ca7 100644 --- a/src/tools/clippy/triagebot.toml +++ b/src/tools/clippy/triagebot.toml @@ -17,9 +17,9 @@ contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIB [assign.owners] "/.github" = ["@flip1995"] +"/book" = ["@flip1995"] "/util/gh-pages" = ["@xFrednet"] "*" = [ - "@flip1995", "@Manishearth", "@llogiq", "@giraffate", diff --git a/src/tools/miri/Cargo.lock b/src/tools/miri/Cargo.lock index 737423a2cd1..7bedb5d48f6 100644 --- a/src/tools/miri/Cargo.lock +++ b/src/tools/miri/Cargo.lock @@ -184,6 +184,16 @@ dependencies = [ ] [[package]] +name = "ctrlc" +version = "3.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbcf33c2a618cbe41ee43ae6e9f2e48368cd9f9db2896f10167d8d762679f639" +dependencies = [ + "nix", + "windows-sys 0.45.0", +] + +[[package]] name = "diff" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -421,6 +431,7 @@ name = "miri" version = "0.1.0" dependencies = [ "colored", + "ctrlc", "env_logger", "getrandom", "lazy_static", @@ -438,6 +449,18 @@ dependencies = [ ] [[package]] +name = "nix" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" +dependencies = [ + "bitflags", + "cfg-if", + "libc", + "static_assertions", +] + +[[package]] name = "object" version = "0.30.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -714,6 +737,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] name = "syn" version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml index 5987b0df8d6..683f99ca4de 100644 --- a/src/tools/miri/Cargo.toml +++ b/src/tools/miri/Cargo.toml @@ -29,6 +29,7 @@ smallvec = "1.7" # for more information. rustc-workspace-hack = "1.0.0" measureme = "10.0.0" +ctrlc = "3.2.5" [target.'cfg(unix)'.dependencies] libc = "0.2" diff --git a/src/tools/miri/cargo-miri/src/arg.rs b/src/tools/miri/cargo-miri/src/arg.rs index e8bac4625f7..d7216060358 100644 --- a/src/tools/miri/cargo-miri/src/arg.rs +++ b/src/tools/miri/cargo-miri/src/arg.rs @@ -40,7 +40,8 @@ impl<'s, I: Iterator<Item = Cow<'s, str>>> Iterator for ArgSplitFlagValue<'_, I> if arg == "--" { // Stop searching at `--`. self.args = None; - return None; + // But yield the `--` so that it does not get lost! + return Some(Err(Cow::Borrowed("--"))); } // These branches cannot be merged if we want to avoid the allocation in the `Borrowed` branch. match &arg { @@ -79,9 +80,8 @@ impl<'a, I: Iterator<Item = String> + 'a> ArgSplitFlagValue<'a, I> { ) -> impl Iterator<Item = Result<String, String>> + 'a { ArgSplitFlagValue::new(args.map(Cow::Owned), name).map(|x| { match x { - Ok(Cow::Owned(s)) => Ok(s), - Err(Cow::Owned(s)) => Err(s), - _ => panic!("iterator converted owned to borrowed"), + Ok(s) => Ok(s.into_owned()), + Err(s) => Err(s.into_owned()), } }) } diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs index 37f66d0033f..465e4a1b2d2 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -113,30 +113,17 @@ pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) { }; let metadata = get_cargo_metadata(); let mut cmd = cargo(); - cmd.arg(cargo_cmd); - - // Forward all arguments before `--` other than `--target-dir` and its value to Cargo. - // (We want to *change* the target-dir value, so we must not forward it.) - let mut target_dir = None; - for arg in ArgSplitFlagValue::from_string_iter(&mut args, "--target-dir") { - match arg { - Ok(value) => { - if target_dir.is_some() { - show_error!("`--target-dir` is provided more than once"); - } - target_dir = Some(value.into()); - } - Err(arg) => { - cmd.arg(arg); - } - } + cmd.arg(&cargo_cmd); + // In nextest we have to also forward the main `verb`. + if cargo_cmd == "nextest" { + cmd.arg( + args.next() + .unwrap_or_else(|| show_error!("`cargo miri nextest` expects a verb (e.g. `run`)")), + ); } - // Detect the target directory if it's not specified via `--target-dir`. - // (`cargo metadata` does not support `--target-dir`, that's why we have to handle this ourselves.) - let target_dir = target_dir.get_or_insert_with(|| metadata.target_directory.clone()); - // Set `--target-dir` to `miri` inside the original target directory. - target_dir.push("miri"); - cmd.arg("--target-dir").arg(target_dir); + // We set the following flags *before* forwarding more arguments. + // This is needed to fix <https://github.com/rust-lang/miri/issues/2829>: cargo will stop + // interpreting things as flags when it sees the first positional argument. // Make sure the build target is explicitly set. // This is needed to make the `target.runner` settings do something, @@ -154,8 +141,23 @@ pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) { cmd.arg("--config") .arg(format!("target.'cfg(all())'.runner=[{cargo_miri_path_for_toml}, 'runner']")); - // Forward all further arguments after `--` to cargo. - cmd.arg("--").args(args); + // Set `--target-dir` to `miri` inside the original target directory. + let mut target_dir = match get_arg_flag_value("--target-dir") { + Some(dir) => PathBuf::from(dir), + None => metadata.target_directory.clone().into_std_path_buf(), + }; + target_dir.push("miri"); + cmd.arg("--target-dir").arg(target_dir); + + // *After* we set all the flags that need setting, forward everything else. Make sure to skip + // `--target-dir` (which would otherwise be set twice). + for arg in + ArgSplitFlagValue::from_string_iter(&mut args, "--target-dir").filter_map(Result::err) + { + cmd.arg(arg); + } + // Forward all further arguments (not consumed by `ArgSplitFlagValue`) to cargo. + cmd.args(args); // Set `RUSTC_WRAPPER` to ourselves. Cargo will prepend that binary to its usual invocation, // i.e., the first argument is `rustc` -- which is what we use in `main` to distinguish diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index b450f986149..0bff100dc14 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -69fef92ab2f287f072b66fb7b4f62c8bb4acba43 +8b4b20836b832e91aa605a2faf5e2a55190202c8 diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index e9bbae4d504..d85cac7bcbb 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -3,6 +3,7 @@ use std::cell::RefCell; use std::collections::hash_map::Entry; use std::num::TryFromIntError; +use std::sync::atomic::{AtomicBool, Ordering::Relaxed}; use std::task::Poll; use std::time::{Duration, SystemTime}; @@ -1012,8 +1013,24 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// Run the core interpreter loop. Returns only when an interrupt occurs (an error or program /// termination). fn run_threads(&mut self) -> InterpResult<'tcx, !> { + static SIGNALED: AtomicBool = AtomicBool::new(false); + ctrlc::set_handler(move || { + // Indicate that we have ben signaled to stop. If we were already signaled, exit + // immediately. In our interpreter loop we try to consult this value often, but if for + // whatever reason we don't get to that check or the cleanup we do upon finding that + // this bool has become true takes a long time, the exit here will promptly exit the + // process on the second Ctrl-C. + if SIGNALED.swap(true, Relaxed) { + std::process::exit(1); + } + }) + .unwrap(); let this = self.eval_context_mut(); loop { + if SIGNALED.load(Relaxed) { + this.machine.handle_abnormal_termination(); + std::process::exit(1); + } match this.machine.threads.schedule(&this.machine.clock)? { SchedulingAction::ExecuteStep => { if !this.step()? { diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 32717a0d28b..0ba7dad5649 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -651,6 +651,10 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { /// Sets up the "extern statics" for this machine. fn init_extern_statics(this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> { + // "__rust_no_alloc_shim_is_unstable" + let val = ImmTy::from_int(0, this.machine.layouts.u8); + Self::alloc_extern_static(this, "__rust_no_alloc_shim_is_unstable", val)?; + match this.tcx.sess.target.os.as_ref() { "linux" => { // "environ" @@ -713,6 +717,15 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { let def_id = frame.instance.def_id(); def_id.is_local() || self.local_crates.contains(&def_id.krate) } + + /// Called when the interpreter is going to shut down abnormally, such as due to a Ctrl-C. + pub(crate) fn handle_abnormal_termination(&mut self) { + // All strings in the profile data are stored in a single string table which is not + // written to disk until the profiler is dropped. If the interpreter exits without dropping + // the profiler, it is not possible to interpret the profile data and all measureme tools + // will panic when given the file. + drop(self.profiler.take()); + } } impl VisitTags for MiriMachine<'_, '_> { diff --git a/src/tools/miri/src/shims/backtrace.rs b/src/tools/miri/src/shims/backtrace.rs index 1e4ab2f0f62..323991249d3 100644 --- a/src/tools/miri/src/shims/backtrace.rs +++ b/src/tools/miri/src/shims/backtrace.rs @@ -102,7 +102,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let ptr_layout = this.layout_of(ptr_ty)?; for (i, ptr) in ptrs.into_iter().enumerate() { - let offset = ptr_layout.size * i.try_into().unwrap(); + let offset = ptr_layout.size.checked_mul(i.try_into().unwrap(), this).unwrap(); let op_place = buf_place.offset(offset, ptr_layout, this)?; diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 44bca3796f9..74364994579 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -166,7 +166,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { dependency_format.1.iter().enumerate().filter_map(|(num, &linkage)| { // We add 1 to the number because that's what rustc also does everywhere it // calls `CrateNum::new`... - #[allow(clippy::integer_arithmetic)] + #[allow(clippy::arithmetic_side_effects)] (linkage != Linkage::NotLinked).then_some(CrateNum::new(num + 1)) }), ) { @@ -347,7 +347,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// Emulates calling the internal __rust_* allocator functions fn emulate_allocator( &mut self, - symbol: Symbol, default: impl FnOnce(&mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx>, ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> { let this = self.eval_context_mut(); @@ -359,11 +358,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { match allocator_kind { AllocatorKind::Global => { - let (body, instance) = this - .lookup_exported_symbol(symbol)? - .expect("symbol should be present if there is a global allocator"); - - Ok(EmulateByNameResult::MirBody(body, instance)) + // When `#[global_allocator]` is used, `__rust_*` is defined by the macro expansion + // of this attribute. As such we have to call an exported Rust function, + // and not execute any Miri shim. Somewhat unintuitively doing so is done + // by returning `NotSupported`, which triggers the `lookup_exported_symbol` + // fallback case in `emulate_foreign_item`. + return Ok(EmulateByNameResult::NotSupported); } AllocatorKind::Default => { default(this)?; @@ -558,11 +558,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Rust allocation "__rust_alloc" | "miri_alloc" => { - let [size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?; - let size = this.read_target_usize(size)?; - let align = this.read_target_usize(align)?; - let default = |this: &mut MiriInterpCx<'mir, 'tcx>| { + // Only call `check_shim` when `#[global_allocator]` isn't used. When that + // macro is used, we act like no shim exists, so that the exported function can run. + let [size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?; + let size = this.read_target_usize(size)?; + let align = this.read_target_usize(align)?; + Self::check_alloc_request(size, align)?; let memory_kind = match link_name.as_str() { @@ -581,8 +583,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { }; match link_name.as_str() { - "__rust_alloc" => - return this.emulate_allocator(Symbol::intern("__rg_alloc"), default), + "__rust_alloc" => return this.emulate_allocator(default), "miri_alloc" => { default(this)?; return Ok(EmulateByNameResult::NeedsJumping); @@ -591,11 +592,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } "__rust_alloc_zeroed" => { - let [size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?; - let size = this.read_target_usize(size)?; - let align = this.read_target_usize(align)?; + return this.emulate_allocator(|this| { + // See the comment for `__rust_alloc` why `check_shim` is only called in the + // default case. + let [size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?; + let size = this.read_target_usize(size)?; + let align = this.read_target_usize(align)?; - return this.emulate_allocator(Symbol::intern("__rg_alloc_zeroed"), |this| { Self::check_alloc_request(size, align)?; let ptr = this.allocate_ptr( @@ -614,12 +617,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { }); } "__rust_dealloc" | "miri_dealloc" => { - let [ptr, old_size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?; - let ptr = this.read_pointer(ptr)?; - let old_size = this.read_target_usize(old_size)?; - let align = this.read_target_usize(align)?; - let default = |this: &mut MiriInterpCx<'mir, 'tcx>| { + // See the comment for `__rust_alloc` why `check_shim` is only called in the + // default case. + let [ptr, old_size, align] = + this.check_shim(abi, Abi::Rust, link_name, args)?; + let ptr = this.read_pointer(ptr)?; + let old_size = this.read_target_usize(old_size)?; + let align = this.read_target_usize(align)?; + let memory_kind = match link_name.as_str() { "__rust_dealloc" => MiriMemoryKind::Rust, "miri_dealloc" => MiriMemoryKind::Miri, @@ -635,8 +641,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { }; match link_name.as_str() { - "__rust_dealloc" => - return this.emulate_allocator(Symbol::intern("__rg_dealloc"), default), + "__rust_dealloc" => { + return this.emulate_allocator(default); + } "miri_dealloc" => { default(this)?; return Ok(EmulateByNameResult::NeedsJumping); @@ -645,15 +652,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } "__rust_realloc" => { - let [ptr, old_size, align, new_size] = - this.check_shim(abi, Abi::Rust, link_name, args)?; - let ptr = this.read_pointer(ptr)?; - let old_size = this.read_target_usize(old_size)?; - let align = this.read_target_usize(align)?; - let new_size = this.read_target_usize(new_size)?; - // No need to check old_size; we anyway check that they match the allocation. + return this.emulate_allocator(|this| { + // See the comment for `__rust_alloc` why `check_shim` is only called in the + // default case. + let [ptr, old_size, align, new_size] = + this.check_shim(abi, Abi::Rust, link_name, args)?; + let ptr = this.read_pointer(ptr)?; + let old_size = this.read_target_usize(old_size)?; + let align = this.read_target_usize(align)?; + let new_size = this.read_target_usize(new_size)?; + // No need to check old_size; we anyway check that they match the allocation. - return this.emulate_allocator(Symbol::intern("__rg_realloc"), |this| { Self::check_alloc_request(new_size, align)?; let align = Align::from_bytes(align).unwrap(); @@ -707,7 +716,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { .position(|&c| c == val) { let idx = u64::try_from(idx).unwrap(); - #[allow(clippy::integer_arithmetic)] // idx < num, so this never wraps + #[allow(clippy::arithmetic_side_effects)] // idx < num, so this never wraps let new_ptr = ptr.offset(Size::from_bytes(num - idx - 1), this)?; this.write_pointer(new_ptr, dest)?; } else { @@ -916,10 +925,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let a = this.read_scalar(a)?.to_u64()?; let b = this.read_scalar(b)?.to_u64()?; - #[allow(clippy::integer_arithmetic)] + #[allow(clippy::arithmetic_side_effects)] // adding two u64 and a u8 cannot wrap in a u128 let wide_sum = u128::from(c_in) + u128::from(a) + u128::from(b); - #[allow(clippy::integer_arithmetic)] // it's a u128, we can shift by 64 + #[allow(clippy::arithmetic_side_effects)] // it's a u128, we can shift by 64 let (c_out, sum) = ((wide_sum >> 64).truncate::<u8>(), wide_sum.truncate::<u64>()); let c_out_field = this.place_field(dest, 0)?; diff --git a/src/tools/miri/src/shims/intrinsics/simd.rs b/src/tools/miri/src/shims/intrinsics/simd.rs index 114c66253f7..1995db715e8 100644 --- a/src/tools/miri/src/shims/intrinsics/simd.rs +++ b/src/tools/miri/src/shims/intrinsics/simd.rs @@ -563,7 +563,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let (op, op_len) = this.operand_to_simd(op)?; let bitmask_len = op_len.max(8); - assert!(dest.layout.ty.is_integral()); + // Returns either an unsigned integer or array of `u8`. + assert!( + dest.layout.ty.is_integral() + || matches!(dest.layout.ty.kind(), ty::Array(elemty, _) if elemty == &this.tcx.types.u8) + ); assert!(bitmask_len <= 64); assert_eq!(bitmask_len, dest.layout.size.bits()); let op_len = u32::try_from(op_len).unwrap(); @@ -577,7 +581,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { .unwrap(); } } - this.write_int(res, dest)?; + // We have to force the place type to be an int so that we can write `res` into it. + let mut dest = this.force_allocation(dest)?; + dest.layout = this.machine.layouts.uint(dest.layout.size).unwrap(); + this.write_int(res, &dest.into())?; } name => throw_unsup_format!("unimplemented intrinsic: `simd_{name}`"), @@ -605,7 +612,7 @@ fn simd_bitmask_index(idx: u32, vec_len: u32, endianness: Endian) -> u32 { assert!(idx < vec_len); match endianness { Endian::Little => idx, - #[allow(clippy::integer_arithmetic)] // idx < vec_len + #[allow(clippy::arithmetic_side_effects)] // idx < vec_len Endian::Big => vec_len - 1 - idx, // reverse order of bits } } diff --git a/src/tools/miri/src/shims/mod.rs b/src/tools/miri/src/shims/mod.rs index 918efda3777..a423a0786b7 100644 --- a/src/tools/miri/src/shims/mod.rs +++ b/src/tools/miri/src/shims/mod.rs @@ -1,4 +1,4 @@ -#![warn(clippy::integer_arithmetic)] +#![warn(clippy::arithmetic_side_effects)] mod backtrace; #[cfg(target_os = "linux")] diff --git a/src/tools/miri/src/shims/time.rs b/src/tools/miri/src/shims/time.rs index 2f24c00ce14..756dec1beca 100644 --- a/src/tools/miri/src/shims/time.rs +++ b/src/tools/miri/src/shims/time.rs @@ -108,7 +108,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(0) } - #[allow(non_snake_case, clippy::integer_arithmetic)] + #[allow(non_snake_case, clippy::arithmetic_side_effects)] fn GetSystemTimeAsFileTime( &mut self, LPFILETIME_op: &OpTy<'tcx, Provenance>, diff --git a/src/tools/miri/src/shims/tls.rs b/src/tools/miri/src/shims/tls.rs index 685feeaf892..62bd087e7e8 100644 --- a/src/tools/miri/src/shims/tls.rs +++ b/src/tools/miri/src/shims/tls.rs @@ -56,7 +56,7 @@ impl<'tcx> Default for TlsData<'tcx> { impl<'tcx> TlsData<'tcx> { /// Generate a new TLS key with the given destructor. /// `max_size` determines the integer size the key has to fit in. - #[allow(clippy::integer_arithmetic)] + #[allow(clippy::arithmetic_side_effects)] pub fn create_tls_key( &mut self, dtor: Option<ty::Instance<'tcx>>, diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index abec782b4f0..d1b09cd7b55 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -458,7 +458,7 @@ pub struct DirHandler { } impl DirHandler { - #[allow(clippy::integer_arithmetic)] + #[allow(clippy::arithmetic_side_effects)] fn insert_new(&mut self, read_dir: ReadDir) -> u64 { let id = self.next_id; self.next_id += 1; diff --git a/src/tools/miri/src/shims/unix/linux/sync.rs b/src/tools/miri/src/shims/unix/linux/sync.rs index ffe3ca69c58..6889c430042 100644 --- a/src/tools/miri/src/shims/unix/linux/sync.rs +++ b/src/tools/miri/src/shims/unix/linux/sync.rs @@ -247,7 +247,7 @@ pub fn futex<'tcx>( // before doing the syscall. this.atomic_fence(AtomicFenceOrd::SeqCst)?; let mut n = 0; - #[allow(clippy::integer_arithmetic)] + #[allow(clippy::arithmetic_side_effects)] for _ in 0..val { if let Some(thread) = this.futex_wake(addr_usize, bitset) { this.unblock_thread(thread); diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index f72ba5cca7a..b8bb7f62452 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -219,7 +219,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { .copied() .scan(Size::ZERO, |a, x| { let res = Some(*a); - *a += x; + *a = a.checked_add(x, this).unwrap(); res }) .collect(); diff --git a/src/tools/miri/src/shims/windows/handle.rs b/src/tools/miri/src/shims/windows/handle.rs index 8bffa9991c7..d9ae1b22409 100644 --- a/src/tools/miri/src/shims/windows/handle.rs +++ b/src/tools/miri/src/shims/windows/handle.rs @@ -62,7 +62,7 @@ impl Handle { let floor_log2 = variant_count.ilog2(); // we need to add one for non powers of two to compensate for the difference - #[allow(clippy::integer_arithmetic)] // cannot overflow + #[allow(clippy::arithmetic_side_effects)] // cannot overflow if variant_count.is_power_of_two() { floor_log2 } else { floor_log2 + 1 } } @@ -87,7 +87,7 @@ impl Handle { // packs the data into the lower `data_size` bits // and packs the discriminant right above the data - #[allow(clippy::integer_arithmetic)] // cannot overflow + #[allow(clippy::arithmetic_side_effects)] // cannot overflow return discriminant << data_size | data; } @@ -106,11 +106,11 @@ impl Handle { let data_size = u32::BITS.checked_sub(disc_size).unwrap(); // the lower `data_size` bits of this mask are 1 - #[allow(clippy::integer_arithmetic)] // cannot overflow + #[allow(clippy::arithmetic_side_effects)] // cannot overflow let data_mask = 2u32.pow(data_size) - 1; // the discriminant is stored right above the lower `data_size` bits - #[allow(clippy::integer_arithmetic)] // cannot overflow + #[allow(clippy::arithmetic_side_effects)] // cannot overflow let discriminant = handle >> data_size; // the data is stored in the lower `data_size` bits diff --git a/src/tools/miri/test-cargo-miri/run-test.py b/src/tools/miri/test-cargo-miri/run-test.py index 46b3afa70e5..9df90c725e4 100755 --- a/src/tools/miri/test-cargo-miri/run-test.py +++ b/src/tools/miri/test-cargo-miri/run-test.py @@ -108,8 +108,9 @@ def test_cargo_miri_run(): env={'MIRITESTVAR': "wrongval"}, # changing the env var causes a rebuild (re-runs build.rs), # so keep it set ) + # This also covers passing arguments without `--`: Cargo will forward unused positional arguments to the program. test("`cargo miri run` (with arguments and target)", - cargo_miri("run") + ["--bin", "cargo-miri-test", "--", "hello world", '"hello world"', r'he\\llo\"world'], + cargo_miri("run") + ["--bin", "cargo-miri-test", "hello world", '"hello world"', r'he\\llo\"world'], "run.args.stdout.ref", "run.args.stderr.ref", ) test("`cargo miri r` (subcrate, no isolation)", diff --git a/src/tools/miri/tests/fail/memleak.stderr b/src/tools/miri/tests/fail/memleak.stderr index 6d9b664c8f4..12bb944b076 100644 --- a/src/tools/miri/tests/fail/memleak.stderr +++ b/src/tools/miri/tests/fail/memleak.stderr @@ -1,8 +1,8 @@ error: memory leaked: ALLOC (Rust heap, size: 4, align: 4), allocated here: --> RUSTLIB/alloc/src/alloc.rs:LL:CC | -LL | unsafe { __rust_alloc(layout.size(), layout.align()) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | __rust_alloc(layout.size(), layout.align()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: inside `std::alloc::alloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC = note: inside `std::alloc::Global::alloc_impl` at RUSTLIB/alloc/src/alloc.rs:LL:CC diff --git a/src/tools/miri/tests/fail/memleak_rc.32bit.stderr b/src/tools/miri/tests/fail/memleak_rc.32bit.stderr index 0e1146cf4ad..87c5f466bc4 100644 --- a/src/tools/miri/tests/fail/memleak_rc.32bit.stderr +++ b/src/tools/miri/tests/fail/memleak_rc.32bit.stderr @@ -1,8 +1,8 @@ error: memory leaked: ALLOC (Rust heap, size: 16, align: 4), allocated here: --> RUSTLIB/alloc/src/alloc.rs:LL:CC | -LL | unsafe { __rust_alloc(layout.size(), layout.align()) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | __rust_alloc(layout.size(), layout.align()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: inside `std::alloc::alloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC = note: inside `std::alloc::Global::alloc_impl` at RUSTLIB/alloc/src/alloc.rs:LL:CC diff --git a/src/tools/miri/tests/fail/memleak_rc.64bit.stderr b/src/tools/miri/tests/fail/memleak_rc.64bit.stderr index 4979588f370..ec5f5f5bed3 100644 --- a/src/tools/miri/tests/fail/memleak_rc.64bit.stderr +++ b/src/tools/miri/tests/fail/memleak_rc.64bit.stderr @@ -1,8 +1,8 @@ error: memory leaked: ALLOC (Rust heap, size: 32, align: 8), allocated here: --> RUSTLIB/alloc/src/alloc.rs:LL:CC | -LL | unsafe { __rust_alloc(layout.size(), layout.align()) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | __rust_alloc(layout.size(), layout.align()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: inside `std::alloc::alloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC = note: inside `std::alloc::Global::alloc_impl` at RUSTLIB/alloc/src/alloc.rs:LL:CC diff --git a/src/tools/miri/tests/fail/never_say_never.rs b/src/tools/miri/tests/fail/never_say_never.rs index f6d3dc790bf..fd082e367a8 100644 --- a/src/tools/miri/tests/fail/never_say_never.rs +++ b/src/tools/miri/tests/fail/never_say_never.rs @@ -6,10 +6,8 @@ fn main() { let y = &5; - let x: ! = unsafe { - *(y as *const _ as *const !) //~ ERROR: entering unreachable code - }; - f(x) + let x: ! = unsafe { *(y as *const _ as *const !) }; + f(x) //~ ERROR: entering unreachable code } fn f(x: !) -> ! { diff --git a/src/tools/miri/tests/fail/never_say_never.stderr b/src/tools/miri/tests/fail/never_say_never.stderr index a2a63b8baf5..9d3a8df525a 100644 --- a/src/tools/miri/tests/fail/never_say_never.stderr +++ b/src/tools/miri/tests/fail/never_say_never.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: entering unreachable code --> $DIR/never_say_never.rs:LL:CC | -LL | *(y as *const _ as *const !) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ entering unreachable code +LL | f(x) + | ^^^^ entering unreachable code | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/stacked_borrows/illegal_write2.rs b/src/tools/miri/tests/fail/stacked_borrows/illegal_write2.rs index bf4204c61fd..a5a1930ed65 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/illegal_write2.rs +++ b/src/tools/miri/tests/fail/stacked_borrows/illegal_write2.rs @@ -1,4 +1,4 @@ -#![allow(drop_ref)] +#![allow(dropping_references)] fn main() { let target = &mut 42; diff --git a/src/tools/miri/tests/fail/uninit_buffer.rs b/src/tools/miri/tests/fail/uninit_buffer.rs index 8a330058375..d622b2fa7d8 100644 --- a/src/tools/miri/tests/fail/uninit_buffer.rs +++ b/src/tools/miri/tests/fail/uninit_buffer.rs @@ -1,6 +1,6 @@ //@error-in-other-file: memory is uninitialized at [0x4..0x10] -#![allow(drop_copy)] +#![allow(dropping_copy_types)] use std::alloc::{alloc, dealloc, Layout}; use std::slice::from_raw_parts; diff --git a/src/tools/miri/tests/fail/uninit_buffer_with_provenance.rs b/src/tools/miri/tests/fail/uninit_buffer_with_provenance.rs index 443f481c087..ca825901372 100644 --- a/src/tools/miri/tests/fail/uninit_buffer_with_provenance.rs +++ b/src/tools/miri/tests/fail/uninit_buffer_with_provenance.rs @@ -1,7 +1,7 @@ //@error-in-other-file: memory is uninitialized at [0x4..0x8] //@normalize-stderr-test: "a[0-9]+" -> "ALLOC" #![feature(strict_provenance)] -#![allow(drop_copy)] +#![allow(dropping_copy_types)] // Test printing allocations that contain single-byte provenance. diff --git a/src/tools/miri/tests/pass/portable-simd.rs b/src/tools/miri/tests/pass/portable-simd.rs index 173ac654b03..ee67a65a4f9 100644 --- a/src/tools/miri/tests/pass/portable-simd.rs +++ b/src/tools/miri/tests/pass/portable-simd.rs @@ -2,6 +2,10 @@ #![feature(portable_simd, platform_intrinsics)] use std::simd::*; +extern "platform-intrinsic" { + pub(crate) fn simd_bitmask<T, U>(x: T) -> U; +} + fn simd_ops_f32() { let a = f32x4::splat(10.0); let b = f32x4::from_array([1.0, 2.0, 3.0, -4.0]); @@ -208,11 +212,40 @@ fn simd_mask() { assert_eq!(bitmask, 0b1010001101001001); assert_eq!(Mask::<i64, 16>::from_bitmask(bitmask), mask); + // Also directly call intrinsic, to test both kinds of return types. + unsafe { + let bitmask1: u16 = simd_bitmask(mask.to_int()); + let bitmask2: [u8; 2] = simd_bitmask(mask.to_int()); + if cfg!(target_endian = "little") { + assert_eq!(bitmask1, 0b1010001101001001); + assert_eq!(bitmask2, [0b01001001, 0b10100011]); + } else { + // All the bitstrings are reversed compared to above, but the array elements are in the + // same order. + assert_eq!(bitmask1, 0b1001001011000101); + assert_eq!(bitmask2, [0b10010010, 0b11000101]); + } + } + + // Mask less than 8 bits long, which is a special case (padding with 0s). let values = [false, false, false, true]; let mask = Mask::<i64, 4>::from_array(values); let bitmask = mask.to_bitmask(); assert_eq!(bitmask, 0b1000); assert_eq!(Mask::<i64, 4>::from_bitmask(bitmask), mask); + + // Also directly call intrinsic, to test both kinds of return types. + unsafe { + let bitmask1: u8 = simd_bitmask(mask.to_int()); + let bitmask2: [u8; 1] = simd_bitmask(mask.to_int()); + if cfg!(target_endian = "little") { + assert_eq!(bitmask1, 0b1000); + assert_eq!(bitmask2, [0b1000]); + } else { + assert_eq!(bitmask1, 0b0001); + assert_eq!(bitmask2, [0b0001]); + } + } } fn simd_cast() { diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs index af245aa89aa..3258a2be460 100644 --- a/src/tools/miri/tests/pass/shims/fs.rs +++ b/src/tools/miri/tests/pass/shims/fs.rs @@ -31,7 +31,7 @@ fn main() { } fn host_to_target_path(path: String) -> PathBuf { - use std::ffi::{CStr, CString}; + use std::ffi::{c_char, CStr, CString}; let path = CString::new(path).unwrap(); let mut out = Vec::with_capacity(1024); diff --git a/src/tools/miri/tests/pass/stacked-borrows/zst-field-retagging-terminates.rs b/src/tools/miri/tests/pass/stacked-borrows/zst-field-retagging-terminates.rs index 9f743f0b566..6e13a9ea836 100644 --- a/src/tools/miri/tests/pass/stacked-borrows/zst-field-retagging-terminates.rs +++ b/src/tools/miri/tests/pass/stacked-borrows/zst-field-retagging-terminates.rs @@ -1,7 +1,7 @@ //@compile-flags: -Zmiri-retag-fields // Checks that the test does not run forever (which relies on a fast path). -#![allow(drop_copy)] +#![allow(dropping_copy_types)] fn main() { let array = [(); usize::MAX]; diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index e1004c796c5..2e778110931 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -117,6 +117,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "autocfg", "bitflags", "block-buffer", + "byteorder", // via ruzstd in object in thorin-dwp "cc", "cfg-if", "chalk-derive", @@ -217,6 +218,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "rustc-rayon", "rustc-rayon-core", "rustc_version", + "ruzstd", // via object in thorin-dwp "ryu", "scoped-tls", "scopeguard", diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index ee12f4acb10..be3a5d3aa0f 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -35,6 +35,7 @@ const EXTENSION_EXCEPTION_PATHS: &[&str] = &[ "tests/ui/macros/syntax-extension-source-utils-files/includeme.fragment", // more include "tests/ui/unused-crate-deps/test.mk", // why would you use make "tests/ui/proc-macro/auxiliary/included-file.txt", // more include + "tests/ui/invalid/foo.natvis.xml", // sample debugger visualizer ]; fn check_entries(tests_path: &Path, bad: &mut bool) { diff --git a/tests/assembly/option-nonzero-eq.rs b/tests/assembly/option-nonzero-eq.rs new file mode 100644 index 00000000000..f5d88de76dd --- /dev/null +++ b/tests/assembly/option-nonzero-eq.rs @@ -0,0 +1,28 @@ +// revisions: WIN LIN +// [WIN] only-windows +// [LIN] only-linux +// assembly-output: emit-asm +// compile-flags: --crate-type=lib -O -C llvm-args=-x86-asm-syntax=intel +// only-x86_64 +// ignore-sgx +// ignore-debug + +use std::cmp::Ordering; + +// CHECK-lABEL: ordering_eq: +#[no_mangle] +pub fn ordering_eq(l: Option<Ordering>, r: Option<Ordering>) -> bool { + // Linux (System V): first two arguments are rdi then rsi + // Windows: first two arguments are rcx then rdx + // Both use rax for the return value. + + // CHECK-NOT: mov + // CHECK-NOT: test + // CHECK-NOT: cmp + + // LIN: cmp dil, sil + // WIN: cmp cl, dl + // CHECK-NEXT: sete al + // CHECK-NEXT: ret + l == r +} diff --git a/tests/codegen/alloc-optimisation.rs b/tests/codegen/alloc-optimisation.rs index c3ffaeb9547..f88d695d87e 100644 --- a/tests/codegen/alloc-optimisation.rs +++ b/tests/codegen/alloc-optimisation.rs @@ -1,12 +1,13 @@ // // no-system-llvm // compile-flags: -O -#![crate_type="lib"] +#![crate_type = "lib"] #[no_mangle] pub fn alloc_test(data: u32) { // CHECK-LABEL: @alloc_test // CHECK-NEXT: start: + // CHECK-NEXT: {{.*}} load volatile i8, ptr @__rust_no_alloc_shim_is_unstable, align 1 // CHECK-NEXT: ret void let x = Box::new(data); drop(x); diff --git a/tests/codegen/debug-vtable.rs b/tests/codegen/debug-vtable.rs index d82b737de0b..e52392b260b 100644 --- a/tests/codegen/debug-vtable.rs +++ b/tests/codegen/debug-vtable.rs @@ -11,8 +11,6 @@ // Make sure that vtables don't have the unnamed_addr attribute when debuginfo is enabled. // This helps debuggers more reliably map from dyn pointer to concrete type. -// CHECK: @vtable.0 = private constant <{ -// CHECK: @vtable.1 = private constant <{ // CHECK: @vtable.2 = private constant <{ // CHECK: @vtable.3 = private constant <{ // CHECK: @vtable.4 = private constant <{ diff --git a/tests/codegen/drop-in-place-noalias.rs b/tests/codegen/drop-in-place-noalias.rs new file mode 100644 index 00000000000..725e6fc048d --- /dev/null +++ b/tests/codegen/drop-in-place-noalias.rs @@ -0,0 +1,38 @@ +// compile-flags: -O -C no-prepopulate-passes + +// Tests that the compiler can apply `noalias` and other &mut attributes to `drop_in_place`. +// Note that non-Unpin types should not get `noalias`, matching &mut behavior. + +#![crate_type="lib"] + +use std::marker::PhantomPinned; + +// CHECK: define internal void @{{.*}}core{{.*}}ptr{{.*}}drop_in_place{{.*}}StructUnpin{{.*}}({{.*\*|ptr}} noalias noundef align 4 dereferenceable(12) %{{.+}}) + +// CHECK: define internal void @{{.*}}core{{.*}}ptr{{.*}}drop_in_place{{.*}}StructNotUnpin{{.*}}({{.*\*|ptr}} noundef nonnull align 4 %{{.+}}) + +pub struct StructUnpin { + a: i32, + b: i32, + c: i32, +} + +impl Drop for StructUnpin { + fn drop(&mut self) {} +} + +pub struct StructNotUnpin { + a: i32, + b: i32, + c: i32, + p: PhantomPinned, +} + +impl Drop for StructNotUnpin { + fn drop(&mut self) {} +} + +pub unsafe fn main(x: StructUnpin, y: StructNotUnpin) { + drop(x); + drop(y); +} diff --git a/tests/codegen/issues/issue-111603.rs b/tests/codegen/issues/issue-111603.rs new file mode 100644 index 00000000000..90b3c314d2f --- /dev/null +++ b/tests/codegen/issues/issue-111603.rs @@ -0,0 +1,28 @@ +// compile-flags: -O + +#![crate_type = "lib"] +#![feature(get_mut_unchecked, new_uninit)] + +use std::sync::Arc; + +// CHECK-LABEL: @new_uninit +#[no_mangle] +pub fn new_uninit(x: u64) -> Arc<[u64; 1000]> { + // CHECK: call alloc::sync::arcinner_layout_for_value_layout + // CHECK-NOT: call alloc::sync::arcinner_layout_for_value_layout + let mut arc = Arc::new_uninit(); + unsafe { Arc::get_mut_unchecked(&mut arc) }.write([x; 1000]); + unsafe { arc.assume_init() } +} + +// CHECK-LABEL: @new_uninit_slice +#[no_mangle] +pub fn new_uninit_slice(x: u64) -> Arc<[u64]> { + // CHECK: call alloc::sync::arcinner_layout_for_value_layout + // CHECK-NOT: call alloc::sync::arcinner_layout_for_value_layout + let mut arc = Arc::new_uninit_slice(1000); + for elem in unsafe { Arc::get_mut_unchecked(&mut arc) } { + elem.write(x); + } + unsafe { arc.assume_init() } +} diff --git a/tests/codegen/noalias-box-off.rs b/tests/codegen/noalias-box-off.rs index afd17c7c160..c82c53b2a48 100644 --- a/tests/codegen/noalias-box-off.rs +++ b/tests/codegen/noalias-box-off.rs @@ -4,5 +4,8 @@ // CHECK-LABEL: @box_should_not_have_noalias_if_disabled( // CHECK-NOT: noalias +// CHECK-SAME: %foo) #[no_mangle] -pub fn box_should_not_have_noalias_if_disabled(_b: Box<u8>) {} +pub fn box_should_not_have_noalias_if_disabled(foo: Box<u8>) { + drop(foo); +} diff --git a/tests/codegen/option-nonzero-eq.rs b/tests/codegen/option-nonzero-eq.rs index 835decd3e5f..a394695f3bd 100644 --- a/tests/codegen/option-nonzero-eq.rs +++ b/tests/codegen/option-nonzero-eq.rs @@ -7,6 +7,9 @@ use core::cmp::Ordering; use core::num::{NonZeroU32, NonZeroI64}; use core::ptr::NonNull; +// See also tests/assembly/option-nonzero-eq.rs, for cases with `assume`s in the +// LLVM and thus don't optimize down clearly here, but do in assembly. + // CHECK-lABEL: @non_zero_eq #[no_mangle] pub fn non_zero_eq(l: Option<NonZeroU32>, r: Option<NonZeroU32>) -> bool { @@ -33,12 +36,3 @@ pub fn non_null_eq(l: Option<NonNull<u8>>, r: Option<NonNull<u8>>) -> bool { // CHECK-NEXT: ret i1 l == r } - -// CHECK-lABEL: @ordering_eq -#[no_mangle] -pub fn ordering_eq(l: Option<Ordering>, r: Option<Ordering>) -> bool { - // CHECK: start: - // CHECK-NEXT: icmp eq i8 - // CHECK-NEXT: ret i1 - l == r -} diff --git a/tests/codegen/sanitizer-cfi-emit-type-metadata-id-itanium-cxx-abi.rs b/tests/codegen/sanitizer-cfi-emit-type-metadata-id-itanium-cxx-abi.rs index 71e26e3fe8a..ebb26cd35c0 100644 --- a/tests/codegen/sanitizer-cfi-emit-type-metadata-id-itanium-cxx-abi.rs +++ b/tests/codegen/sanitizer-cfi-emit-type-metadata-id-itanium-cxx-abi.rs @@ -44,7 +44,7 @@ impl<T> Trait1<T> for i32 { } // Trait implementation -impl<T> Trait1<T> for Struct1<T> { +impl<T, U> Trait1<T> for Struct1<U> { fn foo(&self) { } } @@ -536,15 +536,15 @@ pub fn foo149(_: Type14<Bar>, _: Type14<Bar>, _: Type14<Bar>) { } // CHECK: ![[TYPE93]] = !{i64 0, !"_ZTSFvPFu3i32S_EE"} // CHECK: ![[TYPE94]] = !{i64 0, !"_ZTSFvPFu3i32S_ES0_E"} // CHECK: ![[TYPE95]] = !{i64 0, !"_ZTSFvPFu3i32S_ES0_S0_E"} -// CHECK: ![[TYPE96]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function2FnIu5tupleIu3i32EEu{{[0-9]+}}NtNtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnce6OutputIS0_ES_u6regionEEE"} -// CHECK: ![[TYPE97]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function2FnIu5tupleIu3i32EEu{{[0-9]+}}NtNtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnce6OutputIS0_ES_u6regionEES5_E"} -// CHECK: ![[TYPE98]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function2FnIu5tupleIu3i32EEu{{[0-9]+}}NtNtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnce6OutputIS0_ES_u6regionEES5_S5_E"} -// CHECK: ![[TYPE99]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function5FnMutIu5tupleIu3i32EEu{{[0-9]+}}NtNtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnce6OutputIS0_ES_u6regionEEE"} -// CHECK: ![[TYPE100]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function5FnMutIu5tupleIu3i32EEu{{[0-9]+}}NtNtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnce6OutputIS0_ES_u6regionEES5_E"} -// CHECK: ![[TYPE101]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function5FnMutIu5tupleIu3i32EEu{{[0-9]+}}NtNtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnce6OutputIS0_ES_u6regionEES5_S5_E"} -// CHECK: ![[TYPE102]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnceIu5tupleIu3i32EEu{{[0-9]+}}NtNtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnce6OutputIS0_ES_u6regionEEE"} -// CHECK: ![[TYPE103]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnceIu5tupleIu3i32EEu{{[0-9]+}}NtNtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnce6OutputIS0_ES_u6regionEES5_E"} -// CHECK: ![[TYPE104]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnceIu5tupleIu3i32EEu{{[0-9]+}}NtNtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnce6OutputIS0_ES_u6regionEES5_S5_E"} +// CHECK: ![[TYPE96]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function2FnIu5paramEu6regionEEE"} +// CHECK: ![[TYPE97]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function2FnIu5paramEu6regionEES3_E"} +// CHECK: ![[TYPE98]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function2FnIu5paramEu6regionEES3_S3_E"} +// CHECK: ![[TYPE99]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function5FnMutIu5paramEu6regionEEE"} +// CHECK: ![[TYPE100]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function5FnMutIu5paramEu6regionEES3_E"} +// CHECK: ![[TYPE101]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function5FnMutIu5paramEu6regionEES3_S3_E"} +// CHECK: ![[TYPE102]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnceIu5paramEu6regionEEE"} +// CHECK: ![[TYPE103]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnceIu5paramEu6regionEES3_E"} +// CHECK: ![[TYPE104]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnceIu5paramEu6regionEES3_S3_E"} // CHECK: ![[TYPE105]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEEE"} // CHECK: ![[TYPE106]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEES2_E"} // CHECK: ![[TYPE107]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEES2_S2_E"} @@ -566,9 +566,9 @@ pub fn foo149(_: Type14<Bar>, _: Type14<Bar>, _: Type14<Bar>) { } // CHECK: ![[TYPE123]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn18{{[{}][{}]}}impl{{[}][}]}}3fooIu3i32EE"} // CHECK: ![[TYPE124]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn18{{[{}][{}]}}impl{{[}][}]}}3fooIu3i32ES0_E"} // CHECK: ![[TYPE125]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn18{{[{}][{}]}}impl{{[}][}]}}3fooIu3i32ES0_S0_E"} -// CHECK: ![[TYPE126]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait13fooIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait1Iu3i32Eu6regionES_EE"} -// CHECK: ![[TYPE127]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait13fooIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait1Iu3i32Eu6regionES_ES3_E"} -// CHECK: ![[TYPE128]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait13fooIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait1Iu3i32Eu6regionES_ES3_S3_E"} +// CHECK: ![[TYPE126]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait13fooIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait1Iu5paramEu6regionEu3i32EE"} +// CHECK: ![[TYPE127]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait13fooIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait1Iu5paramEu6regionEu3i32ES4_E"} +// CHECK: ![[TYPE128]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait13fooIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait1Iu5paramEu6regionEu3i32ES4_S4_E"} // CHECK: ![[TYPE129]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait13fooIu3i32S_EE"} // CHECK: ![[TYPE130]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait13fooIu3i32S_ES0_E"} // CHECK: ![[TYPE131]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait13fooIu3i32S_ES0_S0_E"} diff --git a/tests/codegen/sanitizer-cfi-emit-type-metadata-trait-objects.rs b/tests/codegen/sanitizer-cfi-emit-type-metadata-trait-objects.rs index ab5dcec7936..0f79adab7bd 100644 --- a/tests/codegen/sanitizer-cfi-emit-type-metadata-trait-objects.rs +++ b/tests/codegen/sanitizer-cfi-emit-type-metadata-trait-objects.rs @@ -1,44 +1,120 @@ // Verifies that type metadata identifiers for trait objects are emitted correctly. // // needs-sanitizer-cfi -// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi +// compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Ctarget-feature=-crt-static -Zsanitizer=cfi #![crate_type="lib"] -trait Trait1 { +pub trait Trait1 { fn foo(&self); } -struct Type1; +#[derive(Clone, Copy)] +pub struct Type1; impl Trait1 for Type1 { fn foo(&self) { } } -pub fn foo() { - let a = Type1; +pub trait Trait2<T> { + fn bar(&self); +} + +pub struct Type2; + +impl Trait2<i32> for Type2 { + fn bar(&self) { + } +} + +pub trait Trait3<T> { + fn baz(&self, _: &T); +} + +pub struct Type3; + +impl<T, U> Trait3<U> for T { + fn baz(&self, _: &U) { + } +} + +pub trait Trait4<'a, T> { + type Output: 'a; + fn qux(&self, _: &T) -> Self::Output; +} + +pub struct Type4; + +impl<'a, T, U> Trait4<'a, U> for T { + type Output = &'a i32; + fn qux(&self, _: &U) -> Self::Output { + &0 + } +} + +pub fn foo1(a: &dyn Trait1) { a.foo(); - // CHECK-LABEL: define{{.*}}foo{{.*}}!type !{{[0-9]+}} - // CHECK: call <sanitizer_cfi_emit_type_metadata_trait_objects::Type1 as sanitizer_cfi_emit_type_metadata_trait_objects::Trait1>::foo + // CHECK-LABEL: define{{.*}}4foo1{{.*}}!type !{{[0-9]+}} + // CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%[0-9]}}, metadata !"[[TYPE1:[[:print:]]+]]") } -pub fn bar() { +pub fn bar1() { let a = Type1; let b = &a as &dyn Trait1; b.foo(); - // CHECK-LABEL: define{{.*}}bar{{.*}}!type !{{[0-9]+}} - // CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%0|%1}}, metadata !"[[TYPE1:[[:print:]]+]]") + // CHECK-LABEL: define{{.*}}4bar1{{.*}}!type !{{[0-9]+}} + // CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%[0-9]}}, metadata !"[[TYPE2:[[:print:]]+]]") } -pub fn baz() { - let a = Type1; - let b = &a as &dyn Trait1; - a.foo(); - b.foo(); - // CHECK-LABEL: define{{.*}}baz{{.*}}!type !{{[0-9]+}} - // CHECK: call <sanitizer_cfi_emit_type_metadata_trait_objects::Type1 as sanitizer_cfi_emit_type_metadata_trait_objects::Trait1>::foo - // CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%0|%1}}, metadata !"[[TYPE1:[[:print:]]+]]") +pub fn foo2<T>(a: &dyn Trait2<T>) { + a.bar(); + // CHECK-LABEL: define{{.*}}4foo2{{.*}}!type !{{[0-9]+}} + // CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%[0-9]}}, metadata !"[[TYPE2:[[:print:]]+]]") +} + +pub fn bar2() { + let a = Type2; + foo2(&a); + let b = &a as &dyn Trait2<i32>; + b.bar(); + // CHECK-LABEL: define{{.*}}4bar2{{.*}}!type !{{[0-9]+}} + // CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%[0-9]}}, metadata !"[[TYPE2:[[:print:]]+]]") +} + +pub fn foo3(a: &dyn Trait3<Type3>) { + let b = Type3; + a.baz(&b); + // CHECK-LABEL: define{{.*}}4foo3{{.*}}!type !{{[0-9]+}} + // CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%[0-9]}}, metadata !"[[TYPE3:[[:print:]]+]]") +} + +pub fn bar3() { + let a = Type3; + foo3(&a); + let b = &a as &dyn Trait3<Type3>; + b.baz(&a); + // CHECK-LABEL: define{{.*}}4bar3{{.*}}!type !{{[0-9]+}} + // CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%[0-9]}}, metadata !"[[TYPE3:[[:print:]]+]]") +} + +pub fn foo4<'a>(a: &dyn Trait4<'a, Type4, Output = &'a i32>) { + let b = Type4; + a.qux(&b); + // CHECK-LABEL: define{{.*}}4foo4{{.*}}!type !{{[0-9]+}} + // CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%[0-9]}}, metadata !"[[TYPE4:[[:print:]]+]]") +} + +pub fn bar4<'a>() { + let a = Type4; + foo4(&a); + let b = &a as &dyn Trait4<'a, Type4, Output = &'a i32>; + b.qux(&a); + // CHECK-LABEL: define{{.*}}4bar4{{.*}}!type !{{[0-9]+}} + // CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%[0-9]}}, metadata !"[[TYPE4:[[:print:]]+]]") } // CHECK: !{{[0-9]+}} = !{i64 0, !"[[TYPE1]]"} +// CHECK: !{{[0-9]+}} = !{i64 0, !"[[TYPE2]]"} +// CHECK: !{{[0-9]+}} = !{i64 0, !"[[TYPE3]]"} +// CHECK: !{{[0-9]+}} = !{i64 0, !"[[TYPE4]]"} diff --git a/tests/codegen/sanitizer-kcfi-emit-type-metadata-trait-objects.rs b/tests/codegen/sanitizer-kcfi-emit-type-metadata-trait-objects.rs index 81e0d9344f7..004a67e7df2 100644 --- a/tests/codegen/sanitizer-kcfi-emit-type-metadata-trait-objects.rs +++ b/tests/codegen/sanitizer-kcfi-emit-type-metadata-trait-objects.rs @@ -30,40 +30,115 @@ trait Freeze { } #[lang="drop_in_place"] fn drop_in_place_fn<T>() { } -trait Trait1 { +pub trait Trait1 { fn foo(&self); } -struct Type1; +pub struct Type1; impl Trait1 for Type1 { fn foo(&self) { } } -pub fn foo() { - let a = Type1; +pub trait Trait2<T> { + fn bar(&self); +} + +pub struct Type2; + +impl Trait2<i32> for Type2 { + fn bar(&self) { + } +} + +pub trait Trait3<T> { + fn baz(&self, _: &T); +} + +pub struct Type3; + +impl<T, U> Trait3<U> for T { + fn baz(&self, _: &U) { + } +} + +pub trait Trait4<'a, T> { + type Output: 'a; + fn qux(&self, _: &T) -> Self::Output; +} + +pub struct Type4; + +impl<'a, T, U> Trait4<'a, U> for T { + type Output = &'a i32; + fn qux(&self, _: &U) -> Self::Output { + &0 + } +} + +pub fn foo1(a: &dyn Trait1) { a.foo(); - // CHECK-LABEL: define{{.*}}foo{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}} - // CHECK: call <sanitizer_kcfi_emit_type_metadata_trait_objects::Type1 as sanitizer_kcfi_emit_type_metadata_trait_objects::Trait1>::foo + // CHECK-LABEL: define{{.*}}4foo1{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}} + // CHECK: call void %{{[0-9]}}({{\{\}\*|ptr}} align 1 {{%[a-z]\.0|%_[0-9]}}){{.*}}[ "kcfi"(i32 [[TYPE1:[[:print:]]+]]) ] } -pub fn bar() { +pub fn bar1() { let a = Type1; let b = &a as &dyn Trait1; b.foo(); - // CHECK-LABEL: define{{.*}}bar{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}} - // CHECK: call void %0({{\{\}\*|ptr}} align 1 {{%b\.0|%_1}}){{.*}}[ "kcfi"(i32 [[TYPE1:[[:print:]]+]]) ] + // CHECK-LABEL: define{{.*}}4bar1{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}} + // CHECK: call void %{{[0-9]}}({{\{\}\*|ptr}} align 1 {{%[a-z]\.0|%_[0-9]}}){{.*}}[ "kcfi"(i32 [[TYPE1:[[:print:]]+]]) ] } -pub fn baz() { - let a = Type1; - let b = &a as &dyn Trait1; - a.foo(); - b.foo(); - // CHECK-LABEL: define{{.*}}baz{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}} - // CHECK: call <sanitizer_kcfi_emit_type_metadata_trait_objects::Type1 as sanitizer_kcfi_emit_type_metadata_trait_objects::Trait1>::foo - // CHECK: call void %0({{\{\}\*|ptr}} align 1 {{%b\.0|%_1}}){{.*}}[ "kcfi"(i32 [[TYPE1:[[:print:]]+]]) ] +pub fn foo2<T>(a: &dyn Trait2<T>) { + a.bar(); + // CHECK-LABEL: define{{.*}}4foo2{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}} + // CHECK: call void %{{[0-9]}}({{\{\}\*|ptr}} align 1 {{%[a-z]\.0|%_[0-9]}}){{.*}}[ "kcfi"(i32 [[TYPE2:[[:print:]]+]]) ] +} + +pub fn bar2() { + let a = Type2; + foo2(&a); + let b = &a as &dyn Trait2<i32>; + b.bar(); + // CHECK-LABEL: define{{.*}}4bar2{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}} + // CHECK: call void %{{[0-9]}}({{\{\}\*|ptr}} align 1 {{%[a-z]\.0|%_[0-9]}}){{.*}}[ "kcfi"(i32 [[TYPE2:[[:print:]]+]]) ] +} + +pub fn foo3(a: &dyn Trait3<Type3>) { + let b = Type3; + a.baz(&b); + // CHECK-LABEL: define{{.*}}4foo3{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}} + // CHECK: call void %{{[0-9]}}({{\{\}\*|ptr}} align 1 {{%[a-z]\.0|%_[0-9]}}, {{\{\}\*|ptr|%Type3\*}} align 1 {{%[a-z]\.0|%_[0-9]}}){{.*}}[ "kcfi"(i32 [[TYPE3:[[:print:]]+]]) ] +} + +pub fn bar3() { + let a = Type3; + foo3(&a); + let b = &a as &dyn Trait3<Type3>; + b.baz(&a); + // CHECK-LABEL: define{{.*}}4bar3{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}} + // CHECK: call void %{{[0-9]}}({{\{\}\*|ptr}} align 1 {{%[a-z]\.0|%_[0-9]}}, {{\{\}\*|ptr|%Type3\*}} align 1 {{%[a-z]\.0|%_[0-9]}}){{.*}}[ "kcfi"(i32 [[TYPE3:[[:print:]]+]]) ] +} + +pub fn foo4<'a>(a: &dyn Trait4<'a, Type4, Output = &'a i32>) { + let b = Type4; + a.qux(&b); + // CHECK-LABEL: define{{.*}}4foo4{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}} + // CHECK: call align 4 {{ptr|i32\*}} %{{[0-9]}}({{\{\}\*|ptr}} align 1 {{%[a-z]\.0|%_[0-9]}}, {{\{\}\*|ptr|%Type4\*}} align 1 {{%[a-z]\.0|%_[0-9]}}){{.*}}[ "kcfi"(i32 [[TYPE4:[[:print:]]+]]) ] +} + +pub fn bar4<'a>() { + let a = Type4; + foo4(&a); + let b = &a as &dyn Trait4<'a, Type4, Output = &'a i32>; + b.qux(&a); + // CHECK-LABEL: define{{.*}}4bar4{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}} + // CHECK: call align 4 {{ptr|i32\*}} %{{[0-9]}}({{\{\}\*|ptr}} align 1 {{%[a-z]\.0|%_[0-9]}}, {{\{\}\*|ptr|%Type4\*}} align 1 {{%[a-z]\.0|%_[0-9]}}){{.*}}[ "kcfi"(i32 [[TYPE4:[[:print:]]+]]) ] } // CHECK: !{{[0-9]+}} = !{i32 [[TYPE1]]} +// CHECK: !{{[0-9]+}} = !{i32 [[TYPE2]]} +// CHECK: !{{[0-9]+}} = !{i32 [[TYPE3]]} +// CHECK: !{{[0-9]+}} = !{i32 [[TYPE4]]} diff --git a/tests/codegen/tied-features-strength.rs b/tests/codegen/tied-features-strength.rs new file mode 100644 index 00000000000..51334c12158 --- /dev/null +++ b/tests/codegen/tied-features-strength.rs @@ -0,0 +1,29 @@ +// ignore-tidy-linelength +// revisions: ENABLE_SVE DISABLE_SVE DISABLE_NEON ENABLE_NEON +// compile-flags: --crate-type=rlib --target=aarch64-unknown-linux-gnu +// needs-llvm-components: aarch64 + +// The "+v8a" feature is matched as optional as it isn't added when we +// are targeting older LLVM versions. Once the min supported version +// is LLVM-14 we can remove the optional regex matching for this feature. + +// [ENABLE_SVE] compile-flags: -C target-feature=+sve +// ENABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)?|(\+sve,?)|(\+neon,?))*}}" } + +// [DISABLE_SVE] compile-flags: -C target-feature=-sve +// DISABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)?|(-sve,?)|(\+neon,?))*}}" } + +// [DISABLE_NEON] compile-flags: -C target-feature=-neon +// DISABLE_NEON: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)?|(-fp-armv8,?)|(-neon,?))*}}" } + +// [ENABLE_NEON] compile-flags: -C target-feature=+neon +// ENABLE_NEON: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)?|(\+fp-armv8,?)|(\+neon,?))*}}" } + + +#![feature(no_core, lang_items)] +#![no_core] + +#[lang = "sized"] +trait Sized {} + +pub fn test() {} diff --git a/tests/codegen/vec-optimizes-away.rs b/tests/codegen/vec-optimizes-away.rs index 9143fad2340..6f477a796b6 100644 --- a/tests/codegen/vec-optimizes-away.rs +++ b/tests/codegen/vec-optimizes-away.rs @@ -1,12 +1,13 @@ // ignore-debug: the debug assertions get in the way // no-system-llvm // compile-flags: -O -#![crate_type="lib"] +#![crate_type = "lib"] #[no_mangle] pub fn sum_me() -> i32 { // CHECK-LABEL: @sum_me // CHECK-NEXT: {{^.*:$}} + // CHECK-NEXT: {{.*}} load volatile i8, ptr @__rust_no_alloc_shim_is_unstable, align 1 // CHECK-NEXT: ret i32 6 vec![1, 2, 3].iter().sum::<i32>() } diff --git a/tests/mir-opt/building/enum_cast.bar.built.after.mir b/tests/mir-opt/building/enum_cast.bar.built.after.mir index 0746e0b498e..9f14c028465 100644 --- a/tests/mir-opt/building/enum_cast.bar.built.after.mir +++ b/tests/mir-opt/building/enum_cast.bar.built.after.mir @@ -5,17 +5,16 @@ fn bar(_1: Bar) -> usize { let mut _0: usize; // return place in scope 0 at $DIR/enum_cast.rs:+0:21: +0:26 let _2: Bar; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:8 let mut _3: isize; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:8 - let mut _4: bool; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:17 + let mut _4: u8; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:17 let mut _5: bool; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:17 bb0: { StorageLive(_2); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:8 _2 = move _1; // scope 0 at $DIR/enum_cast.rs:+1:5: +1:8 _3 = discriminant(_2); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17 - _4 = Ge(const 1_isize, _3); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17 - assume(_4); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17 - _5 = Le(const 0_isize, _3); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17 - assume(_5); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17 + _4 = _3 as u8 (IntToInt); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17 + _5 = Le(_4, const 1_u8); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17 + assume(move _5); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17 _0 = move _3 as usize (IntToInt); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17 StorageDead(_2); // scope 0 at $DIR/enum_cast.rs:+1:16: +1:17 return; // scope 0 at $DIR/enum_cast.rs:+2:2: +2:2 diff --git a/tests/mir-opt/building/enum_cast.boo.built.after.mir b/tests/mir-opt/building/enum_cast.boo.built.after.mir index 699c876b01a..715dedcf24d 100644 --- a/tests/mir-opt/building/enum_cast.boo.built.after.mir +++ b/tests/mir-opt/building/enum_cast.boo.built.after.mir @@ -5,17 +5,16 @@ fn boo(_1: Boo) -> usize { let mut _0: usize; // return place in scope 0 at $DIR/enum_cast.rs:+0:21: +0:26 let _2: Boo; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:8 let mut _3: u8; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:8 - let mut _4: bool; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:17 + let mut _4: u8; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:17 let mut _5: bool; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:17 bb0: { StorageLive(_2); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:8 _2 = move _1; // scope 0 at $DIR/enum_cast.rs:+1:5: +1:8 _3 = discriminant(_2); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17 - _4 = Ge(const 1_u8, _3); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17 - assume(_4); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17 - _5 = Le(const 0_u8, _3); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17 - assume(_5); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17 + _4 = _3 as u8 (IntToInt); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17 + _5 = Le(_4, const 1_u8); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17 + assume(move _5); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17 _0 = move _3 as usize (IntToInt); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17 StorageDead(_2); // scope 0 at $DIR/enum_cast.rs:+1:16: +1:17 return; // scope 0 at $DIR/enum_cast.rs:+2:2: +2:2 diff --git a/tests/mir-opt/building/enum_cast.droppy.built.after.mir b/tests/mir-opt/building/enum_cast.droppy.built.after.mir index 1112177fbbf..6c177c61eca 100644 --- a/tests/mir-opt/building/enum_cast.droppy.built.after.mir +++ b/tests/mir-opt/building/enum_cast.droppy.built.after.mir @@ -6,7 +6,7 @@ fn droppy() -> () { let _2: Droppy; // in scope 0 at $DIR/enum_cast.rs:+2:13: +2:14 let _4: Droppy; // in scope 0 at $DIR/enum_cast.rs:+5:17: +5:18 let mut _5: isize; // in scope 0 at $DIR/enum_cast.rs:+5:17: +5:18 - let mut _6: bool; // in scope 0 at $DIR/enum_cast.rs:+5:17: +5:27 + let mut _6: u8; // in scope 0 at $DIR/enum_cast.rs:+5:17: +5:27 let mut _7: bool; // in scope 0 at $DIR/enum_cast.rs:+5:17: +5:27 let _8: Droppy; // in scope 0 at $DIR/enum_cast.rs:+7:9: +7:10 scope 1 { @@ -31,10 +31,9 @@ fn droppy() -> () { StorageLive(_4); // scope 3 at $DIR/enum_cast.rs:+5:17: +5:18 _4 = move _2; // scope 3 at $DIR/enum_cast.rs:+5:17: +5:18 _5 = discriminant(_4); // scope 3 at $DIR/enum_cast.rs:+5:17: +5:27 - _6 = Ge(const 2_isize, _5); // scope 3 at $DIR/enum_cast.rs:+5:17: +5:27 - assume(_6); // scope 3 at $DIR/enum_cast.rs:+5:17: +5:27 - _7 = Le(const 0_isize, _5); // scope 3 at $DIR/enum_cast.rs:+5:17: +5:27 - assume(_7); // scope 3 at $DIR/enum_cast.rs:+5:17: +5:27 + _6 = _5 as u8 (IntToInt); // scope 3 at $DIR/enum_cast.rs:+5:17: +5:27 + _7 = Le(_6, const 2_u8); // scope 3 at $DIR/enum_cast.rs:+5:17: +5:27 + assume(move _7); // scope 3 at $DIR/enum_cast.rs:+5:17: +5:27 _3 = move _5 as usize (IntToInt); // scope 3 at $DIR/enum_cast.rs:+5:17: +5:27 drop(_4) -> [return: bb1, unwind: bb4]; // scope 3 at $DIR/enum_cast.rs:+5:26: +5:27 } diff --git a/tests/mir-opt/building/enum_cast.far.built.after.mir b/tests/mir-opt/building/enum_cast.far.built.after.mir new file mode 100644 index 00000000000..ab8129ca01c --- /dev/null +++ b/tests/mir-opt/building/enum_cast.far.built.after.mir @@ -0,0 +1,22 @@ +// MIR for `far` after built + +fn far(_1: Far) -> isize { + debug far => _1; // in scope 0 at $DIR/enum_cast.rs:+0:8: +0:11 + let mut _0: isize; // return place in scope 0 at $DIR/enum_cast.rs:+0:21: +0:26 + let _2: Far; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:8 + let mut _3: i16; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:8 + let mut _4: u16; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:17 + let mut _5: bool; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:17 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:8 + _2 = move _1; // scope 0 at $DIR/enum_cast.rs:+1:5: +1:8 + _3 = discriminant(_2); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17 + _4 = _3 as u16 (IntToInt); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17 + _5 = Le(_4, const 1_u16); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17 + assume(move _5); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17 + _0 = move _3 as isize (IntToInt); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17 + StorageDead(_2); // scope 0 at $DIR/enum_cast.rs:+1:16: +1:17 + return; // scope 0 at $DIR/enum_cast.rs:+2:2: +2:2 + } +} diff --git a/tests/mir-opt/building/enum_cast.offsetty.built.after.mir b/tests/mir-opt/building/enum_cast.offsetty.built.after.mir new file mode 100644 index 00000000000..7b2b583f20f --- /dev/null +++ b/tests/mir-opt/building/enum_cast.offsetty.built.after.mir @@ -0,0 +1,26 @@ +// MIR for `offsetty` after built + +fn offsetty(_1: NotStartingAtZero) -> u32 { + debug x => _1; // in scope 0 at $DIR/enum_cast.rs:+0:13: +0:14 + let mut _0: u32; // return place in scope 0 at $DIR/enum_cast.rs:+0:38: +0:41 + let _2: NotStartingAtZero; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:6 + let mut _3: isize; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:6 + let mut _4: u8; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:13 + let mut _5: bool; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:13 + let mut _6: bool; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:13 + let mut _7: bool; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:13 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:6 + _2 = move _1; // scope 0 at $DIR/enum_cast.rs:+1:5: +1:6 + _3 = discriminant(_2); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:13 + _4 = _3 as u8 (IntToInt); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:13 + _5 = Ge(_4, const 4_u8); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:13 + _6 = Le(_4, const 8_u8); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:13 + _7 = BitAnd(move _5, move _6); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:13 + assume(move _7); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:13 + _0 = move _3 as u32 (IntToInt); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:13 + StorageDead(_2); // scope 0 at $DIR/enum_cast.rs:+1:12: +1:13 + return; // scope 0 at $DIR/enum_cast.rs:+2:2: +2:2 + } +} diff --git a/tests/mir-opt/building/enum_cast.rs b/tests/mir-opt/building/enum_cast.rs index 98fd5acfb14..431b5c708b9 100644 --- a/tests/mir-opt/building/enum_cast.rs +++ b/tests/mir-opt/building/enum_cast.rs @@ -1,6 +1,7 @@ // EMIT_MIR enum_cast.foo.built.after.mir // EMIT_MIR enum_cast.bar.built.after.mir // EMIT_MIR enum_cast.boo.built.after.mir +// EMIT_MIR enum_cast.far.built.after.mir enum Foo { A @@ -15,6 +16,11 @@ enum Boo { A, B } +#[repr(i16)] +enum Far { + A, B +} + fn foo(foo: Foo) -> usize { foo as usize } @@ -27,6 +33,10 @@ fn boo(boo: Boo) -> usize { boo as usize } +fn far(far: Far) -> isize { + far as isize +} + // EMIT_MIR enum_cast.droppy.built.after.mir enum Droppy { A, B, C @@ -46,5 +56,37 @@ fn droppy() { let z = Droppy::B; } +#[repr(i16)] +enum SignedAroundZero { + A = -2, + B = 0, + C = 2, +} + +#[repr(u16)] +enum UnsignedAroundZero { + A = 65535, + B = 0, + C = 1, +} + +// EMIT_MIR enum_cast.signy.built.after.mir +fn signy(x: SignedAroundZero) -> i16 { + x as i16 +} + +// EMIT_MIR enum_cast.unsigny.built.after.mir +fn unsigny(x: UnsignedAroundZero) -> u16 { + // FIXME: This doesn't get an around-the-end range today, sadly. + x as u16 +} + +enum NotStartingAtZero { A = 4, B = 6, C = 8 } + +// EMIT_MIR enum_cast.offsetty.built.after.mir +fn offsetty(x: NotStartingAtZero) -> u32 { + x as u32 +} + fn main() { } diff --git a/tests/mir-opt/building/enum_cast.signy.built.after.mir b/tests/mir-opt/building/enum_cast.signy.built.after.mir new file mode 100644 index 00000000000..ef4fea604ec --- /dev/null +++ b/tests/mir-opt/building/enum_cast.signy.built.after.mir @@ -0,0 +1,26 @@ +// MIR for `signy` after built + +fn signy(_1: SignedAroundZero) -> i16 { + debug x => _1; // in scope 0 at $DIR/enum_cast.rs:+0:10: +0:11 + let mut _0: i16; // return place in scope 0 at $DIR/enum_cast.rs:+0:34: +0:37 + let _2: SignedAroundZero; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:6 + let mut _3: i16; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:6 + let mut _4: u16; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:13 + let mut _5: bool; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:13 + let mut _6: bool; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:13 + let mut _7: bool; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:13 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:6 + _2 = move _1; // scope 0 at $DIR/enum_cast.rs:+1:5: +1:6 + _3 = discriminant(_2); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:13 + _4 = _3 as u16 (IntToInt); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:13 + _5 = Ge(_4, const 65534_u16); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:13 + _6 = Le(_4, const 2_u16); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:13 + _7 = BitOr(move _5, move _6); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:13 + assume(move _7); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:13 + _0 = move _3 as i16 (IntToInt); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:13 + StorageDead(_2); // scope 0 at $DIR/enum_cast.rs:+1:12: +1:13 + return; // scope 0 at $DIR/enum_cast.rs:+2:2: +2:2 + } +} diff --git a/tests/mir-opt/building/enum_cast.unsigny.built.after.mir b/tests/mir-opt/building/enum_cast.unsigny.built.after.mir new file mode 100644 index 00000000000..7ca147b1596 --- /dev/null +++ b/tests/mir-opt/building/enum_cast.unsigny.built.after.mir @@ -0,0 +1,17 @@ +// MIR for `unsigny` after built + +fn unsigny(_1: UnsignedAroundZero) -> u16 { + debug x => _1; // in scope 0 at $DIR/enum_cast.rs:+0:12: +0:13 + let mut _0: u16; // return place in scope 0 at $DIR/enum_cast.rs:+0:38: +0:41 + let _2: UnsignedAroundZero; // in scope 0 at $DIR/enum_cast.rs:+2:5: +2:6 + let mut _3: u16; // in scope 0 at $DIR/enum_cast.rs:+2:5: +2:6 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/enum_cast.rs:+2:5: +2:6 + _2 = move _1; // scope 0 at $DIR/enum_cast.rs:+2:5: +2:6 + _3 = discriminant(_2); // scope 0 at $DIR/enum_cast.rs:+2:5: +2:13 + _0 = move _3 as u16 (IntToInt); // scope 0 at $DIR/enum_cast.rs:+2:5: +2:13 + StorageDead(_2); // scope 0 at $DIR/enum_cast.rs:+2:12: +2:13 + return; // scope 0 at $DIR/enum_cast.rs:+3:2: +3:2 + } +} diff --git a/tests/mir-opt/const_prop/transmute.unreachable_box.ConstProp.32bit.diff b/tests/mir-opt/const_prop/transmute.unreachable_box.ConstProp.32bit.diff index bc41b5d0813..5258d75bdf7 100644 --- a/tests/mir-opt/const_prop/transmute.unreachable_box.ConstProp.32bit.diff +++ b/tests/mir-opt/const_prop/transmute.unreachable_box.ConstProp.32bit.diff @@ -3,24 +3,20 @@ fn unreachable_box() -> ! { let mut _0: !; // return place in scope 0 at $DIR/transmute.rs:+0:36: +0:37 - let mut _1: !; // in scope 0 at $DIR/transmute.rs:+0:38: +3:2 - let _2: std::boxed::Box<Never>; // in scope 0 at $DIR/transmute.rs:+1:9: +1:10 - let mut _3: !; // in scope 0 at $DIR/transmute.rs:+2:5: +2:16 + let _1: std::boxed::Box<Never>; // in scope 0 at $DIR/transmute.rs:+1:9: +1:10 scope 1 { - debug x => _2; // in scope 1 at $DIR/transmute.rs:+1:9: +1:10 + debug x => _1; // in scope 1 at $DIR/transmute.rs:+1:9: +1:10 } scope 2 { } bb0: { - StorageLive(_1); // scope 0 at $DIR/transmute.rs:+0:38: +3:2 - StorageLive(_2); // scope 0 at $DIR/transmute.rs:+1:9: +1:10 -- _2 = const 1_usize as std::boxed::Box<Never> (Transmute); // scope 2 at $DIR/transmute.rs:+1:34: +1:52 -+ _2 = const Box::<Never>(Unique::<Never> {{ pointer: NonNull::<Never> {{ pointer: {0x1 as *const Never} }}, _marker: PhantomData::<Never> }}, std::alloc::Global); // scope 2 at $DIR/transmute.rs:+1:34: +1:52 + StorageLive(_1); // scope 0 at $DIR/transmute.rs:+1:9: +1:10 +- _1 = const 1_usize as std::boxed::Box<Never> (Transmute); // scope 2 at $DIR/transmute.rs:+1:34: +1:52 ++ _1 = const Box::<Never>(Unique::<Never> {{ pointer: NonNull::<Never> {{ pointer: {0x1 as *const Never} }}, _marker: PhantomData::<Never> }}, std::alloc::Global); // scope 2 at $DIR/transmute.rs:+1:34: +1:52 + // mir::Constant + // + span: no-location + // + literal: Const { ty: Box<Never>, val: Value(Scalar(0x00000001)) } - StorageLive(_3); // scope 1 at $DIR/transmute.rs:+2:5: +2:16 unreachable; // scope 1 at $DIR/transmute.rs:+2:11: +2:13 } } diff --git a/tests/mir-opt/const_prop/transmute.unreachable_box.ConstProp.64bit.diff b/tests/mir-opt/const_prop/transmute.unreachable_box.ConstProp.64bit.diff index c4376e6e17a..7e57e06a5cf 100644 --- a/tests/mir-opt/const_prop/transmute.unreachable_box.ConstProp.64bit.diff +++ b/tests/mir-opt/const_prop/transmute.unreachable_box.ConstProp.64bit.diff @@ -3,24 +3,20 @@ fn unreachable_box() -> ! { let mut _0: !; // return place in scope 0 at $DIR/transmute.rs:+0:36: +0:37 - let mut _1: !; // in scope 0 at $DIR/transmute.rs:+0:38: +3:2 - let _2: std::boxed::Box<Never>; // in scope 0 at $DIR/transmute.rs:+1:9: +1:10 - let mut _3: !; // in scope 0 at $DIR/transmute.rs:+2:5: +2:16 + let _1: std::boxed::Box<Never>; // in scope 0 at $DIR/transmute.rs:+1:9: +1:10 scope 1 { - debug x => _2; // in scope 1 at $DIR/transmute.rs:+1:9: +1:10 + debug x => _1; // in scope 1 at $DIR/transmute.rs:+1:9: +1:10 } scope 2 { } bb0: { - StorageLive(_1); // scope 0 at $DIR/transmute.rs:+0:38: +3:2 - StorageLive(_2); // scope 0 at $DIR/transmute.rs:+1:9: +1:10 -- _2 = const 1_usize as std::boxed::Box<Never> (Transmute); // scope 2 at $DIR/transmute.rs:+1:34: +1:52 -+ _2 = const Box::<Never>(Unique::<Never> {{ pointer: NonNull::<Never> {{ pointer: {0x1 as *const Never} }}, _marker: PhantomData::<Never> }}, std::alloc::Global); // scope 2 at $DIR/transmute.rs:+1:34: +1:52 + StorageLive(_1); // scope 0 at $DIR/transmute.rs:+1:9: +1:10 +- _1 = const 1_usize as std::boxed::Box<Never> (Transmute); // scope 2 at $DIR/transmute.rs:+1:34: +1:52 ++ _1 = const Box::<Never>(Unique::<Never> {{ pointer: NonNull::<Never> {{ pointer: {0x1 as *const Never} }}, _marker: PhantomData::<Never> }}, std::alloc::Global); // scope 2 at $DIR/transmute.rs:+1:34: +1:52 + // mir::Constant + // + span: no-location + // + literal: Const { ty: Box<Never>, val: Value(Scalar(0x0000000000000001)) } - StorageLive(_3); // scope 1 at $DIR/transmute.rs:+2:5: +2:16 unreachable; // scope 1 at $DIR/transmute.rs:+2:11: +2:13 } } diff --git a/tests/mir-opt/const_prop/transmute.unreachable_direct.ConstProp.32bit.diff b/tests/mir-opt/const_prop/transmute.unreachable_direct.ConstProp.32bit.diff index 81b7b368993..032681f230b 100644 --- a/tests/mir-opt/const_prop/transmute.unreachable_direct.ConstProp.32bit.diff +++ b/tests/mir-opt/const_prop/transmute.unreachable_direct.ConstProp.32bit.diff @@ -3,22 +3,19 @@ fn unreachable_direct() -> ! { let mut _0: !; // return place in scope 0 at $DIR/transmute.rs:+0:39: +0:40 - let mut _1: !; // in scope 0 at $DIR/transmute.rs:+0:41: +3:2 - let _2: Never; // in scope 0 at $DIR/transmute.rs:+1:9: +1:10 - let mut _3: (); // in scope 0 at $DIR/transmute.rs:+1:39: +1:41 - let mut _4: !; // in scope 0 at $DIR/transmute.rs:+2:5: +2:15 + let _1: Never; // in scope 0 at $DIR/transmute.rs:+1:9: +1:10 + let mut _2: (); // in scope 0 at $DIR/transmute.rs:+1:39: +1:41 scope 1 { - debug x => _2; // in scope 1 at $DIR/transmute.rs:+1:9: +1:10 + debug x => _1; // in scope 1 at $DIR/transmute.rs:+1:9: +1:10 } scope 2 { } bb0: { - StorageLive(_1); // scope 0 at $DIR/transmute.rs:+0:41: +3:2 - StorageLive(_2); // scope 0 at $DIR/transmute.rs:+1:9: +1:10 - StorageLive(_3); // scope 2 at $DIR/transmute.rs:+1:39: +1:41 - _3 = (); // scope 2 at $DIR/transmute.rs:+1:39: +1:41 - _2 = move _3 as Never (Transmute); // scope 2 at $DIR/transmute.rs:+1:29: +1:42 + StorageLive(_1); // scope 0 at $DIR/transmute.rs:+1:9: +1:10 + StorageLive(_2); // scope 2 at $DIR/transmute.rs:+1:39: +1:41 + _2 = (); // scope 2 at $DIR/transmute.rs:+1:39: +1:41 + _1 = move _2 as Never (Transmute); // scope 2 at $DIR/transmute.rs:+1:29: +1:42 unreachable; // scope 2 at $DIR/transmute.rs:+1:29: +1:42 } } diff --git a/tests/mir-opt/const_prop/transmute.unreachable_direct.ConstProp.64bit.diff b/tests/mir-opt/const_prop/transmute.unreachable_direct.ConstProp.64bit.diff index 81b7b368993..032681f230b 100644 --- a/tests/mir-opt/const_prop/transmute.unreachable_direct.ConstProp.64bit.diff +++ b/tests/mir-opt/const_prop/transmute.unreachable_direct.ConstProp.64bit.diff @@ -3,22 +3,19 @@ fn unreachable_direct() -> ! { let mut _0: !; // return place in scope 0 at $DIR/transmute.rs:+0:39: +0:40 - let mut _1: !; // in scope 0 at $DIR/transmute.rs:+0:41: +3:2 - let _2: Never; // in scope 0 at $DIR/transmute.rs:+1:9: +1:10 - let mut _3: (); // in scope 0 at $DIR/transmute.rs:+1:39: +1:41 - let mut _4: !; // in scope 0 at $DIR/transmute.rs:+2:5: +2:15 + let _1: Never; // in scope 0 at $DIR/transmute.rs:+1:9: +1:10 + let mut _2: (); // in scope 0 at $DIR/transmute.rs:+1:39: +1:41 scope 1 { - debug x => _2; // in scope 1 at $DIR/transmute.rs:+1:9: +1:10 + debug x => _1; // in scope 1 at $DIR/transmute.rs:+1:9: +1:10 } scope 2 { } bb0: { - StorageLive(_1); // scope 0 at $DIR/transmute.rs:+0:41: +3:2 - StorageLive(_2); // scope 0 at $DIR/transmute.rs:+1:9: +1:10 - StorageLive(_3); // scope 2 at $DIR/transmute.rs:+1:39: +1:41 - _3 = (); // scope 2 at $DIR/transmute.rs:+1:39: +1:41 - _2 = move _3 as Never (Transmute); // scope 2 at $DIR/transmute.rs:+1:29: +1:42 + StorageLive(_1); // scope 0 at $DIR/transmute.rs:+1:9: +1:10 + StorageLive(_2); // scope 2 at $DIR/transmute.rs:+1:39: +1:41 + _2 = (); // scope 2 at $DIR/transmute.rs:+1:39: +1:41 + _1 = move _2 as Never (Transmute); // scope 2 at $DIR/transmute.rs:+1:29: +1:42 unreachable; // scope 2 at $DIR/transmute.rs:+1:29: +1:42 } } diff --git a/tests/mir-opt/const_prop/transmute.unreachable_mut.ConstProp.32bit.diff b/tests/mir-opt/const_prop/transmute.unreachable_mut.ConstProp.32bit.diff index 47f023cd93d..ec8a62bd62c 100644 --- a/tests/mir-opt/const_prop/transmute.unreachable_mut.ConstProp.32bit.diff +++ b/tests/mir-opt/const_prop/transmute.unreachable_mut.ConstProp.32bit.diff @@ -3,28 +3,24 @@ fn unreachable_mut() -> ! { let mut _0: !; // return place in scope 0 at $DIR/transmute.rs:+0:36: +0:37 - let mut _1: !; // in scope 0 at $DIR/transmute.rs:+0:38: +3:2 - let _2: &mut Never; // in scope 0 at $DIR/transmute.rs:+1:9: +1:10 - let mut _3: &mut Never; // in scope 0 at $DIR/transmute.rs:+1:34: +1:52 - let mut _4: !; // in scope 0 at $DIR/transmute.rs:+2:5: +2:16 + let _1: &mut Never; // in scope 0 at $DIR/transmute.rs:+1:9: +1:10 + let mut _2: &mut Never; // in scope 0 at $DIR/transmute.rs:+1:34: +1:52 scope 1 { - debug x => _2; // in scope 1 at $DIR/transmute.rs:+1:9: +1:10 + debug x => _1; // in scope 1 at $DIR/transmute.rs:+1:9: +1:10 } scope 2 { } bb0: { - StorageLive(_1); // scope 0 at $DIR/transmute.rs:+0:38: +3:2 - StorageLive(_2); // scope 0 at $DIR/transmute.rs:+1:9: +1:10 - StorageLive(_3); // scope 0 at $DIR/transmute.rs:+1:34: +1:52 -- _3 = const 1_usize as &mut Never (Transmute); // scope 2 at $DIR/transmute.rs:+1:34: +1:52 -+ _3 = const {0x1 as &mut Never}; // scope 2 at $DIR/transmute.rs:+1:34: +1:52 + StorageLive(_1); // scope 0 at $DIR/transmute.rs:+1:9: +1:10 + StorageLive(_2); // scope 0 at $DIR/transmute.rs:+1:34: +1:52 +- _2 = const 1_usize as &mut Never (Transmute); // scope 2 at $DIR/transmute.rs:+1:34: +1:52 ++ _2 = const {0x1 as &mut Never}; // scope 2 at $DIR/transmute.rs:+1:34: +1:52 + // mir::Constant + // + span: no-location + // + literal: Const { ty: &mut Never, val: Value(Scalar(0x00000001)) } - _2 = &mut (*_3); // scope 0 at $DIR/transmute.rs:+1:34: +1:52 - StorageDead(_3); // scope 0 at $DIR/transmute.rs:+1:54: +1:55 - StorageLive(_4); // scope 1 at $DIR/transmute.rs:+2:5: +2:16 + _1 = &mut (*_2); // scope 0 at $DIR/transmute.rs:+1:34: +1:52 + StorageDead(_2); // scope 0 at $DIR/transmute.rs:+1:54: +1:55 unreachable; // scope 1 at $DIR/transmute.rs:+2:11: +2:13 } } diff --git a/tests/mir-opt/const_prop/transmute.unreachable_mut.ConstProp.64bit.diff b/tests/mir-opt/const_prop/transmute.unreachable_mut.ConstProp.64bit.diff index 62300d2e313..288da6e56c5 100644 --- a/tests/mir-opt/const_prop/transmute.unreachable_mut.ConstProp.64bit.diff +++ b/tests/mir-opt/const_prop/transmute.unreachable_mut.ConstProp.64bit.diff @@ -3,28 +3,24 @@ fn unreachable_mut() -> ! { let mut _0: !; // return place in scope 0 at $DIR/transmute.rs:+0:36: +0:37 - let mut _1: !; // in scope 0 at $DIR/transmute.rs:+0:38: +3:2 - let _2: &mut Never; // in scope 0 at $DIR/transmute.rs:+1:9: +1:10 - let mut _3: &mut Never; // in scope 0 at $DIR/transmute.rs:+1:34: +1:52 - let mut _4: !; // in scope 0 at $DIR/transmute.rs:+2:5: +2:16 + let _1: &mut Never; // in scope 0 at $DIR/transmute.rs:+1:9: +1:10 + let mut _2: &mut Never; // in scope 0 at $DIR/transmute.rs:+1:34: +1:52 scope 1 { - debug x => _2; // in scope 1 at $DIR/transmute.rs:+1:9: +1:10 + debug x => _1; // in scope 1 at $DIR/transmute.rs:+1:9: +1:10 } scope 2 { } bb0: { - StorageLive(_1); // scope 0 at $DIR/transmute.rs:+0:38: +3:2 - StorageLive(_2); // scope 0 at $DIR/transmute.rs:+1:9: +1:10 - StorageLive(_3); // scope 0 at $DIR/transmute.rs:+1:34: +1:52 -- _3 = const 1_usize as &mut Never (Transmute); // scope 2 at $DIR/transmute.rs:+1:34: +1:52 -+ _3 = const {0x1 as &mut Never}; // scope 2 at $DIR/transmute.rs:+1:34: +1:52 + StorageLive(_1); // scope 0 at $DIR/transmute.rs:+1:9: +1:10 + StorageLive(_2); // scope 0 at $DIR/transmute.rs:+1:34: +1:52 +- _2 = const 1_usize as &mut Never (Transmute); // scope 2 at $DIR/transmute.rs:+1:34: +1:52 ++ _2 = const {0x1 as &mut Never}; // scope 2 at $DIR/transmute.rs:+1:34: +1:52 + // mir::Constant + // + span: no-location + // + literal: Const { ty: &mut Never, val: Value(Scalar(0x0000000000000001)) } - _2 = &mut (*_3); // scope 0 at $DIR/transmute.rs:+1:34: +1:52 - StorageDead(_3); // scope 0 at $DIR/transmute.rs:+1:54: +1:55 - StorageLive(_4); // scope 1 at $DIR/transmute.rs:+2:5: +2:16 + _1 = &mut (*_2); // scope 0 at $DIR/transmute.rs:+1:34: +1:52 + StorageDead(_2); // scope 0 at $DIR/transmute.rs:+1:54: +1:55 unreachable; // scope 1 at $DIR/transmute.rs:+2:11: +2:13 } } diff --git a/tests/mir-opt/const_prop/transmute.unreachable_ref.ConstProp.32bit.diff b/tests/mir-opt/const_prop/transmute.unreachable_ref.ConstProp.32bit.diff index 8578f898a7e..dcca0fca619 100644 --- a/tests/mir-opt/const_prop/transmute.unreachable_ref.ConstProp.32bit.diff +++ b/tests/mir-opt/const_prop/transmute.unreachable_ref.ConstProp.32bit.diff @@ -3,24 +3,20 @@ fn unreachable_ref() -> ! { let mut _0: !; // return place in scope 0 at $DIR/transmute.rs:+0:36: +0:37 - let mut _1: !; // in scope 0 at $DIR/transmute.rs:+0:38: +3:2 - let _2: &Never; // in scope 0 at $DIR/transmute.rs:+1:9: +1:10 - let mut _3: !; // in scope 0 at $DIR/transmute.rs:+2:5: +2:16 + let _1: &Never; // in scope 0 at $DIR/transmute.rs:+1:9: +1:10 scope 1 { - debug x => _2; // in scope 1 at $DIR/transmute.rs:+1:9: +1:10 + debug x => _1; // in scope 1 at $DIR/transmute.rs:+1:9: +1:10 } scope 2 { } bb0: { - StorageLive(_1); // scope 0 at $DIR/transmute.rs:+0:38: +3:2 - StorageLive(_2); // scope 0 at $DIR/transmute.rs:+1:9: +1:10 -- _2 = const 1_usize as &Never (Transmute); // scope 2 at $DIR/transmute.rs:+1:30: +1:48 -+ _2 = const {0x1 as &Never}; // scope 2 at $DIR/transmute.rs:+1:30: +1:48 + StorageLive(_1); // scope 0 at $DIR/transmute.rs:+1:9: +1:10 +- _1 = const 1_usize as &Never (Transmute); // scope 2 at $DIR/transmute.rs:+1:30: +1:48 ++ _1 = const {0x1 as &Never}; // scope 2 at $DIR/transmute.rs:+1:30: +1:48 + // mir::Constant + // + span: no-location + // + literal: Const { ty: &Never, val: Value(Scalar(0x00000001)) } - StorageLive(_3); // scope 1 at $DIR/transmute.rs:+2:5: +2:16 unreachable; // scope 1 at $DIR/transmute.rs:+2:11: +2:13 } } diff --git a/tests/mir-opt/const_prop/transmute.unreachable_ref.ConstProp.64bit.diff b/tests/mir-opt/const_prop/transmute.unreachable_ref.ConstProp.64bit.diff index 8b11cea9365..3a0b967e66f 100644 --- a/tests/mir-opt/const_prop/transmute.unreachable_ref.ConstProp.64bit.diff +++ b/tests/mir-opt/const_prop/transmute.unreachable_ref.ConstProp.64bit.diff @@ -3,24 +3,20 @@ fn unreachable_ref() -> ! { let mut _0: !; // return place in scope 0 at $DIR/transmute.rs:+0:36: +0:37 - let mut _1: !; // in scope 0 at $DIR/transmute.rs:+0:38: +3:2 - let _2: &Never; // in scope 0 at $DIR/transmute.rs:+1:9: +1:10 - let mut _3: !; // in scope 0 at $DIR/transmute.rs:+2:5: +2:16 + let _1: &Never; // in scope 0 at $DIR/transmute.rs:+1:9: +1:10 scope 1 { - debug x => _2; // in scope 1 at $DIR/transmute.rs:+1:9: +1:10 + debug x => _1; // in scope 1 at $DIR/transmute.rs:+1:9: +1:10 } scope 2 { } bb0: { - StorageLive(_1); // scope 0 at $DIR/transmute.rs:+0:38: +3:2 - StorageLive(_2); // scope 0 at $DIR/transmute.rs:+1:9: +1:10 -- _2 = const 1_usize as &Never (Transmute); // scope 2 at $DIR/transmute.rs:+1:30: +1:48 -+ _2 = const {0x1 as &Never}; // scope 2 at $DIR/transmute.rs:+1:30: +1:48 + StorageLive(_1); // scope 0 at $DIR/transmute.rs:+1:9: +1:10 +- _1 = const 1_usize as &Never (Transmute); // scope 2 at $DIR/transmute.rs:+1:30: +1:48 ++ _1 = const {0x1 as &Never}; // scope 2 at $DIR/transmute.rs:+1:30: +1:48 + // mir::Constant + // + span: no-location + // + literal: Const { ty: &Never, val: Value(Scalar(0x0000000000000001)) } - StorageLive(_3); // scope 1 at $DIR/transmute.rs:+2:5: +2:16 unreachable; // scope 1 at $DIR/transmute.rs:+2:11: +2:13 } } diff --git a/tests/mir-opt/inline/unsized_argument.caller.Inline.diff b/tests/mir-opt/inline/unsized_argument.caller.Inline.diff new file mode 100644 index 00000000000..745f2bb193a --- /dev/null +++ b/tests/mir-opt/inline/unsized_argument.caller.Inline.diff @@ -0,0 +1,50 @@ +- // MIR for `caller` before Inline ++ // MIR for `caller` after Inline + + fn caller(_1: Box<[i32]>) -> () { + debug x => _1; // in scope 0 at $DIR/unsized_argument.rs:+0:11: +0:12 + let mut _0: (); // return place in scope 0 at $DIR/unsized_argument.rs:+0:26: +0:26 + let _2: (); // in scope 0 at $DIR/unsized_argument.rs:+1:5: +1:15 + let mut _3: std::boxed::Box<[i32]>; // in scope 0 at $DIR/unsized_argument.rs:+1:13: +1:14 + let mut _4: (); // in scope 0 at $DIR/unsized_argument.rs:+1:14: +1:15 + let mut _5: (); // in scope 0 at $DIR/unsized_argument.rs:+1:14: +1:15 + let mut _6: (); // in scope 0 at $DIR/unsized_argument.rs:+1:14: +1:15 + let mut _7: *const [i32]; // in scope 0 at $DIR/unsized_argument.rs:+1:13: +1:14 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/unsized_argument.rs:+1:5: +1:15 + StorageLive(_3); // scope 0 at $DIR/unsized_argument.rs:+1:13: +1:14 + _3 = move _1; // scope 0 at $DIR/unsized_argument.rs:+1:13: +1:14 + _7 = (((_3.0: std::ptr::Unique<[i32]>).0: std::ptr::NonNull<[i32]>).0: *const [i32]); // scope 0 at $DIR/unsized_argument.rs:+1:5: +1:15 + _2 = callee(move (*_7)) -> [return: bb3, unwind: bb4]; // scope 0 at $DIR/unsized_argument.rs:+1:5: +1:15 + // mir::Constant + // + span: $DIR/unsized_argument.rs:9:5: 9:11 + // + literal: Const { ty: fn([i32]) {callee}, val: Value(<ZST>) } + } + + bb1: { + StorageDead(_3); // scope 0 at $DIR/unsized_argument.rs:+1:14: +1:15 + StorageDead(_2); // scope 0 at $DIR/unsized_argument.rs:+1:15: +1:16 + _0 = const (); // scope 0 at $DIR/unsized_argument.rs:+0:26: +2:2 + return; // scope 0 at $DIR/unsized_argument.rs:+2:2: +2:2 + } + + bb2 (cleanup): { + resume; // scope 0 at $DIR/unsized_argument.rs:+0:1: +2:2 + } + + bb3: { + _4 = alloc::alloc::box_free::<[i32], std::alloc::Global>(move (_3.0: std::ptr::Unique<[i32]>), move (_3.1: std::alloc::Global)) -> bb1; // scope 0 at $DIR/unsized_argument.rs:+1:14: +1:15 + // mir::Constant + // + span: $DIR/unsized_argument.rs:9:14: 9:15 + // + literal: Const { ty: unsafe fn(Unique<[i32]>, std::alloc::Global) {alloc::alloc::box_free::<[i32], std::alloc::Global>}, val: Value(<ZST>) } + } + + bb4 (cleanup): { + _6 = alloc::alloc::box_free::<[i32], std::alloc::Global>(move (_3.0: std::ptr::Unique<[i32]>), move (_3.1: std::alloc::Global)) -> [return: bb2, unwind terminate]; // scope 0 at $DIR/unsized_argument.rs:+1:14: +1:15 + // mir::Constant + // + span: $DIR/unsized_argument.rs:9:14: 9:15 + // + literal: Const { ty: unsafe fn(Unique<[i32]>, std::alloc::Global) {alloc::alloc::box_free::<[i32], std::alloc::Global>}, val: Value(<ZST>) } + } + } + diff --git a/tests/mir-opt/inline/unsized_argument.rs b/tests/mir-opt/inline/unsized_argument.rs new file mode 100644 index 00000000000..b2c51407fd5 --- /dev/null +++ b/tests/mir-opt/inline/unsized_argument.rs @@ -0,0 +1,15 @@ +// needs-unwind +#![feature(unsized_fn_params)] + +#[inline(always)] +fn callee(y: [i32]) {} + +// EMIT_MIR unsized_argument.caller.Inline.diff +fn caller(x: Box<[i32]>) { + callee(*x); +} + +fn main() { + let b = Box::new([1]); + caller(b); +} diff --git a/tests/mir-opt/issue_72181_1.f.built.after.mir b/tests/mir-opt/issue_72181_1.f.built.after.mir index 4086da52011..25f47225113 100644 --- a/tests/mir-opt/issue_72181_1.f.built.after.mir +++ b/tests/mir-opt/issue_72181_1.f.built.after.mir @@ -3,27 +3,13 @@ fn f(_1: Void) -> ! { debug v => _1; // in scope 0 at $DIR/issue_72181_1.rs:+0:6: +0:7 let mut _0: !; // return place in scope 0 at $DIR/issue_72181_1.rs:+0:18: +0:19 - let mut _2: !; // in scope 0 at $DIR/issue_72181_1.rs:+0:20: +2:2 - let mut _3: !; // in scope 0 at $DIR/issue_72181_1.rs:+1:5: +1:15 bb0: { - StorageLive(_2); // scope 0 at $DIR/issue_72181_1.rs:+0:20: +2:2 - StorageLive(_3); // scope 0 at $DIR/issue_72181_1.rs:+1:5: +1:15 FakeRead(ForMatchedPlace(None), _1); // scope 0 at $DIR/issue_72181_1.rs:+1:11: +1:12 unreachable; // scope 0 at $DIR/issue_72181_1.rs:+1:11: +1:12 } bb1: { - unreachable; // scope 0 at $DIR/issue_72181_1.rs:+1:5: +1:15 - } - - bb2: { - StorageDead(_3); // scope 0 at $DIR/issue_72181_1.rs:+1:14: +1:15 - unreachable; // scope 0 at $DIR/issue_72181_1.rs:+0:20: +2:2 - } - - bb3: { - StorageDead(_2); // scope 0 at $DIR/issue_72181_1.rs:+2:1: +2:2 return; // scope 0 at $DIR/issue_72181_1.rs:+2:2: +2:2 } } diff --git a/tests/mir-opt/lower_intrinsics.transmute_to_box_uninhabited.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.transmute_to_box_uninhabited.LowerIntrinsics.diff index 8735a750060..aa5d9619d10 100644 --- a/tests/mir-opt/lower_intrinsics.transmute_to_box_uninhabited.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.transmute_to_box_uninhabited.LowerIntrinsics.diff @@ -3,26 +3,22 @@ fn transmute_to_box_uninhabited() -> ! { let mut _0: !; // return place in scope 0 at $DIR/lower_intrinsics.rs:+0:49: +0:50 - let mut _1: !; // in scope 0 at $DIR/lower_intrinsics.rs:+0:51: +3:2 - let _2: std::boxed::Box<Never>; // in scope 0 at $DIR/lower_intrinsics.rs:+1:9: +1:10 - let mut _3: !; // in scope 0 at $DIR/lower_intrinsics.rs:+2:5: +2:16 + let _1: std::boxed::Box<Never>; // in scope 0 at $DIR/lower_intrinsics.rs:+1:9: +1:10 scope 1 { - debug x => _2; // in scope 1 at $DIR/lower_intrinsics.rs:+1:9: +1:10 + debug x => _1; // in scope 1 at $DIR/lower_intrinsics.rs:+1:9: +1:10 } bb0: { - StorageLive(_1); // scope 0 at $DIR/lower_intrinsics.rs:+0:51: +3:2 - StorageLive(_2); // scope 0 at $DIR/lower_intrinsics.rs:+1:9: +1:10 -- _2 = transmute::<usize, Box<Never>>(const 1_usize) -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+1:25: +1:52 + StorageLive(_1); // scope 0 at $DIR/lower_intrinsics.rs:+1:9: +1:10 +- _1 = transmute::<usize, Box<Never>>(const 1_usize) -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+1:25: +1:52 - // mir::Constant - // + span: $DIR/lower_intrinsics.rs:70:25: 70:44 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(usize) -> Box<Never> {transmute::<usize, Box<Never>>}, val: Value(<ZST>) } -+ _2 = const 1_usize as std::boxed::Box<Never> (Transmute); // scope 0 at $DIR/lower_intrinsics.rs:+1:25: +1:52 ++ _1 = const 1_usize as std::boxed::Box<Never> (Transmute); // scope 0 at $DIR/lower_intrinsics.rs:+1:25: +1:52 + goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:25: +1:52 } bb1: { - StorageLive(_3); // scope 1 at $DIR/lower_intrinsics.rs:+2:5: +2:16 unreachable; // scope 1 at $DIR/lower_intrinsics.rs:+2:11: +2:13 } } diff --git a/tests/mir-opt/lower_intrinsics.transmute_to_mut_uninhabited.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.transmute_to_mut_uninhabited.LowerIntrinsics.diff index a772132770c..5fafd45fe85 100644 --- a/tests/mir-opt/lower_intrinsics.transmute_to_mut_uninhabited.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.transmute_to_mut_uninhabited.LowerIntrinsics.diff @@ -3,26 +3,22 @@ fn transmute_to_mut_uninhabited() -> ! { let mut _0: !; // return place in scope 0 at $DIR/lower_intrinsics.rs:+0:49: +0:50 - let mut _1: !; // in scope 0 at $DIR/lower_intrinsics.rs:+0:51: +3:2 - let _2: &mut Never; // in scope 0 at $DIR/lower_intrinsics.rs:+1:9: +1:10 - let mut _3: !; // in scope 0 at $DIR/lower_intrinsics.rs:+2:5: +2:16 + let _1: &mut Never; // in scope 0 at $DIR/lower_intrinsics.rs:+1:9: +1:10 scope 1 { - debug x => _2; // in scope 1 at $DIR/lower_intrinsics.rs:+1:9: +1:10 + debug x => _1; // in scope 1 at $DIR/lower_intrinsics.rs:+1:9: +1:10 } bb0: { - StorageLive(_1); // scope 0 at $DIR/lower_intrinsics.rs:+0:51: +3:2 - StorageLive(_2); // scope 0 at $DIR/lower_intrinsics.rs:+1:9: +1:10 -- _2 = transmute::<usize, &mut Never>(const 1_usize) -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+1:25: +1:52 + StorageLive(_1); // scope 0 at $DIR/lower_intrinsics.rs:+1:9: +1:10 +- _1 = transmute::<usize, &mut Never>(const 1_usize) -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+1:25: +1:52 - // mir::Constant - // + span: $DIR/lower_intrinsics.rs:64:25: 64:44 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(usize) -> &mut Never {transmute::<usize, &mut Never>}, val: Value(<ZST>) } -+ _2 = const 1_usize as &mut Never (Transmute); // scope 0 at $DIR/lower_intrinsics.rs:+1:25: +1:52 ++ _1 = const 1_usize as &mut Never (Transmute); // scope 0 at $DIR/lower_intrinsics.rs:+1:25: +1:52 + goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:25: +1:52 } bb1: { - StorageLive(_3); // scope 1 at $DIR/lower_intrinsics.rs:+2:5: +2:16 unreachable; // scope 1 at $DIR/lower_intrinsics.rs:+2:11: +2:13 } } diff --git a/tests/mir-opt/lower_intrinsics.transmute_to_ref_uninhabited.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.transmute_to_ref_uninhabited.LowerIntrinsics.diff index c4d53d4e8c7..08dead13211 100644 --- a/tests/mir-opt/lower_intrinsics.transmute_to_ref_uninhabited.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.transmute_to_ref_uninhabited.LowerIntrinsics.diff @@ -3,26 +3,22 @@ fn transmute_to_ref_uninhabited() -> ! { let mut _0: !; // return place in scope 0 at $DIR/lower_intrinsics.rs:+0:49: +0:50 - let mut _1: !; // in scope 0 at $DIR/lower_intrinsics.rs:+0:51: +3:2 - let _2: &Never; // in scope 0 at $DIR/lower_intrinsics.rs:+1:9: +1:10 - let mut _3: !; // in scope 0 at $DIR/lower_intrinsics.rs:+2:5: +2:16 + let _1: &Never; // in scope 0 at $DIR/lower_intrinsics.rs:+1:9: +1:10 scope 1 { - debug x => _2; // in scope 1 at $DIR/lower_intrinsics.rs:+1:9: +1:10 + debug x => _1; // in scope 1 at $DIR/lower_intrinsics.rs:+1:9: +1:10 } bb0: { - StorageLive(_1); // scope 0 at $DIR/lower_intrinsics.rs:+0:51: +3:2 - StorageLive(_2); // scope 0 at $DIR/lower_intrinsics.rs:+1:9: +1:10 -- _2 = transmute::<usize, &Never>(const 1_usize) -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+1:21: +1:48 + StorageLive(_1); // scope 0 at $DIR/lower_intrinsics.rs:+1:9: +1:10 +- _1 = transmute::<usize, &Never>(const 1_usize) -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+1:21: +1:48 - // mir::Constant - // + span: $DIR/lower_intrinsics.rs:58:21: 58:40 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(usize) -> &Never {transmute::<usize, &Never>}, val: Value(<ZST>) } -+ _2 = const 1_usize as &Never (Transmute); // scope 0 at $DIR/lower_intrinsics.rs:+1:21: +1:48 ++ _1 = const 1_usize as &Never (Transmute); // scope 0 at $DIR/lower_intrinsics.rs:+1:21: +1:48 + goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:21: +1:48 } bb1: { - StorageLive(_3); // scope 1 at $DIR/lower_intrinsics.rs:+2:5: +2:16 unreachable; // scope 1 at $DIR/lower_intrinsics.rs:+2:11: +2:13 } } diff --git a/tests/mir-opt/lower_intrinsics.unreachable.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.unreachable.LowerIntrinsics.diff index c0cc698c481..28e45909c33 100644 --- a/tests/mir-opt/lower_intrinsics.unreachable.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.unreachable.LowerIntrinsics.diff @@ -3,16 +3,15 @@ fn unreachable() -> ! { let mut _0: !; // return place in scope 0 at $DIR/lower_intrinsics.rs:+0:25: +0:26 - let mut _1: !; // in scope 0 at $DIR/lower_intrinsics.rs:+0:27: +2:2 - let _2: (); // in scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:45 - let mut _3: !; // in scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:45 + let _1: (); // in scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:45 + let mut _2: !; // in scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:45 scope 1 { } bb0: { - StorageLive(_2); // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:47 - StorageLive(_3); // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:45 -- _3 = std::intrinsics::unreachable() -> unwind unreachable; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:45 + StorageLive(_1); // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:47 + StorageLive(_2); // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:45 +- _2 = std::intrinsics::unreachable() -> unwind unreachable; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:45 - // mir::Constant - // + span: $DIR/lower_intrinsics.rs:31:14: 31:43 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn() -> ! {std::intrinsics::unreachable}, val: Value(<ZST>) } diff --git a/tests/run-make-fulldeps/obtain-borrowck/driver.rs b/tests/run-make-fulldeps/obtain-borrowck/driver.rs index d342b2ff6d9..b59a65a713f 100644 --- a/tests/run-make-fulldeps/obtain-borrowck/driver.rs +++ b/tests/run-make-fulldeps/obtain-borrowck/driver.rs @@ -18,7 +18,7 @@ extern crate rustc_interface; extern crate rustc_middle; extern crate rustc_session; -use rustc_borrowck::consumers::BodyWithBorrowckFacts; +use rustc_borrowck::consumers::{self, BodyWithBorrowckFacts, ConsumerOptions}; use rustc_driver::Compilation; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; @@ -102,7 +102,7 @@ impl rustc_driver::Callbacks for CompilerCalls { println!("Bodies retrieved for:"); for (def_id, body) in bodies { println!("{}", def_id); - assert!(body.input_facts.cfg_edge.len() > 0); + assert!(body.input_facts.unwrap().cfg_edge.len() > 0); } }); @@ -127,7 +127,8 @@ thread_local! { } fn mir_borrowck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> ProvidedValue<'tcx> { - let body_with_facts = rustc_borrowck::consumers::get_body_with_borrowck_facts(tcx, def_id); + let opts = ConsumerOptions::PoloniusInputFacts; + let body_with_facts = consumers::get_body_with_borrowck_facts(tcx, def_id, opts); // 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) }; diff --git a/tests/run-make/debugger-visualizer-dep-info/Makefile b/tests/run-make/debugger-visualizer-dep-info/Makefile new file mode 100644 index 00000000000..0877998a74f --- /dev/null +++ b/tests/run-make/debugger-visualizer-dep-info/Makefile @@ -0,0 +1,9 @@ +include ../tools.mk + +# This test makes sure that files referenced via #[debugger_visualizer] are +# included in `--emit dep-info` output. + +all: + $(RUSTC) --emit dep-info main.rs + $(CGREP) "foo.py" < $(TMPDIR)/main.d + $(CGREP) "my_visualizers/bar.natvis" < $(TMPDIR)/main.d diff --git a/tests/run-make/debugger-visualizer-dep-info/foo.py b/tests/run-make/debugger-visualizer-dep-info/foo.py new file mode 100644 index 00000000000..1bb8bf6d7fd --- /dev/null +++ b/tests/run-make/debugger-visualizer-dep-info/foo.py @@ -0,0 +1 @@ +# empty diff --git a/tests/run-make/debugger-visualizer-dep-info/main.rs b/tests/run-make/debugger-visualizer-dep-info/main.rs new file mode 100644 index 00000000000..3aede2215ea --- /dev/null +++ b/tests/run-make/debugger-visualizer-dep-info/main.rs @@ -0,0 +1,12 @@ +#![debugger_visualizer(gdb_script_file = "foo.py")] + +fn main() { + const _UNUSED: u32 = { + mod inner { + #![debugger_visualizer(natvis_file = "my_visualizers/bar.natvis")] + pub const XYZ: u32 = 123; + } + + inner::XYZ + 1 + }; +} diff --git a/tests/run-make/debugger-visualizer-dep-info/my_visualizers/bar.natvis b/tests/run-make/debugger-visualizer-dep-info/my_visualizers/bar.natvis new file mode 100644 index 00000000000..c341a403902 --- /dev/null +++ b/tests/run-make/debugger-visualizer-dep-info/my_visualizers/bar.natvis @@ -0,0 +1 @@ +<!-- empty --> diff --git a/tests/run-make/incremental-debugger-visualizer/Makefile b/tests/run-make/incremental-debugger-visualizer/Makefile new file mode 100644 index 00000000000..8cfe41597ad --- /dev/null +++ b/tests/run-make/incremental-debugger-visualizer/Makefile @@ -0,0 +1,49 @@ +include ../tools.mk + +# This test makes sure that changes to files referenced via #[debugger_visualizer] +# are picked up when compiling incrementally. + +# We have to copy the source to $(TMPDIR) because Github CI mounts the source +# directory as readonly. We need to apply modifications to some of the source +# file. +SRC_DIR := $(TMPDIR)/src +INCR_CACHE_DIR := $(TMPDIR)/incremental + +all: + rm -rf $(TMPDIR)/* + mkdir $(SRC_DIR) + cp ./foo.rs $(SRC_DIR) + echo "GDB script v1" > $(SRC_DIR)/foo.py + echo "Natvis v1" > $(SRC_DIR)/foo.natvis + $(RUSTC) $(SRC_DIR)/foo.rs \ + --crate-type=rlib \ + --emit metadata \ + -C incremental=$(INCR_CACHE_DIR) \ + -Z incremental-verify-ich + $(CGREP) "GDB script v1" < $(TMPDIR)/libfoo.rmeta + $(CGREP) "Natvis v1" < $(TMPDIR)/libfoo.rmeta + + # Change only the GDB script and check that the change has been picked up + echo "GDB script v2" > $(SRC_DIR)/foo.py + $(RUSTC) $(SRC_DIR)/foo.rs \ + --crate-type=rlib \ + --emit metadata \ + -C incremental=$(INCR_CACHE_DIR) \ + -Z incremental-verify-ich + + $(CGREP) "GDB script v2" < $(TMPDIR)/libfoo.rmeta + $(CGREP) -v "GDB script v1" < $(TMPDIR)/libfoo.rmeta + $(CGREP) "Natvis v1" < $(TMPDIR)/libfoo.rmeta + + # Now change the Natvis version and check that the change has been picked up + echo "Natvis v2" > $(SRC_DIR)/foo.natvis + $(RUSTC) $(SRC_DIR)/foo.rs \ + --crate-type=rlib \ + --emit metadata \ + -C incremental=$(INCR_CACHE_DIR) \ + -Z incremental-verify-ich + + $(CGREP) "GDB script v2" < $(TMPDIR)/libfoo.rmeta + $(CGREP) -v "GDB script v1" < $(TMPDIR)/libfoo.rmeta + $(CGREP) "Natvis v2" < $(TMPDIR)/libfoo.rmeta + $(CGREP) -v "Natvis v1" < $(TMPDIR)/libfoo.rmeta diff --git a/tests/run-make/incremental-debugger-visualizer/foo.rs b/tests/run-make/incremental-debugger-visualizer/foo.rs new file mode 100644 index 00000000000..8daa36a12d3 --- /dev/null +++ b/tests/run-make/incremental-debugger-visualizer/foo.rs @@ -0,0 +1,6 @@ +#![debugger_visualizer(natvis_file = "./foo.natvis")] +#![debugger_visualizer(gdb_script_file = "./foo.py")] + +pub struct Foo { + pub x: u32, +} diff --git a/tests/run-make/issue-83045/Makefile b/tests/run-make/issue-83045/Makefile index 7053da00f6b..b76e184b610 100644 --- a/tests/run-make/issue-83045/Makefile +++ b/tests/run-make/issue-83045/Makefile @@ -29,5 +29,5 @@ all: --crate-type=rlib \ --edition=2018 \ c.rs 2>&1 | tee $(TMPDIR)/output.txt || exit 0 - $(CGREP) E0519 < $(TMPDIR)/output.txt + $(CGREP) E0463 < $(TMPDIR)/output.txt $(CGREP) -v "internal compiler error" < $(TMPDIR)/output.txt diff --git a/tests/run-make/no-alloc-shim/Makefile b/tests/run-make/no-alloc-shim/Makefile new file mode 100644 index 00000000000..568e3f9ba1d --- /dev/null +++ b/tests/run-make/no-alloc-shim/Makefile @@ -0,0 +1,24 @@ +include ../tools.mk + +# ignore-cross-compile +# ignore-msvc FIXME(bjorn3) can't figure out how to link with the MSVC toolchain + +TARGET_LIBDIR = $$($(RUSTC) --print target-libdir) + +all: + $(RUSTC) foo.rs --crate-type bin --emit obj -Cpanic=abort +ifdef IS_MSVC + $(CC) $(CFLAGS) $(TMPDIR)/foo.o $(call OUT_EXE,foo) /link $(TARGET_LIBDIR)/liballoc-*.rlib $(TARGET_LIBDIR)/libcore-*.rlib $(TARGET_LIBDIR)/libcompiler_builtins-*.rlib + $(call OUT_EXE,foo) +else + $(CC) $(CFLAGS) $(TMPDIR)/foo.o $(TARGET_LIBDIR)/liballoc-*.rlib $(TARGET_LIBDIR)/libcore-*.rlib $(TARGET_LIBDIR)/libcompiler_builtins-*.rlib -o $(call RUN_BINFILE,foo) + $(call RUN_BINFILE,foo) +endif + + # Check that linking without __rust_no_alloc_shim_is_unstable defined fails + $(RUSTC) foo.rs --crate-type bin --emit obj -Cpanic=abort --cfg check_feature_gate +ifdef IS_MSVC + $(CC) $(CFLAGS) $(TMPDIR)/foo.o $(call OUT_EXE,foo) /link $(TARGET_LIBDIR)/liballoc-*.rlib $(TARGET_LIBDIR)/libcore-*.rlib $(TARGET_LIBDIR)/libcompiler_builtins-*.rlib || exit 0 && exit 1 +else + $(CC) $(CFLAGS) $(TMPDIR)/foo.o $(TARGET_LIBDIR)/liballoc-*.rlib $(TARGET_LIBDIR)/libcore-*.rlib $(TARGET_LIBDIR)/libcompiler_builtins-*.rlib -o $(call RUN_BINFILE,foo) || exit 0 && exit 1 +endif diff --git a/tests/run-make/no-alloc-shim/foo.rs b/tests/run-make/no-alloc-shim/foo.rs new file mode 100644 index 00000000000..a3daec3db39 --- /dev/null +++ b/tests/run-make/no-alloc-shim/foo.rs @@ -0,0 +1,44 @@ +#![feature(default_alloc_error_handler)] +#![no_std] +#![no_main] + +extern crate alloc; + +use alloc::alloc::{GlobalAlloc, Layout}; + +#[panic_handler] +fn panic_handler(_: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[no_mangle] +extern "C" fn rust_eh_personality() { + loop {} +} + +#[global_allocator] +static ALLOC: Alloc = Alloc; + +struct Alloc; + +unsafe impl GlobalAlloc for Alloc { + unsafe fn alloc(&self, _: Layout) -> *mut u8 { + core::ptr::null_mut() + } + unsafe fn dealloc(&self, _: *mut u8, _: Layout) { + todo!() + } +} + +#[cfg(not(check_feature_gate))] +#[no_mangle] +static __rust_no_alloc_shim_is_unstable: u8 = 0; + +#[no_mangle] +extern "C" fn main(_argc: usize, _argv: *const *const i8) -> i32 { + unsafe { + assert_eq!(alloc::alloc::alloc(Layout::new::<()>()), core::ptr::null_mut()); + } + + 0 +} diff --git a/tests/run-make/print-native-static-libs/Makefile b/tests/run-make/print-native-static-libs/Makefile new file mode 100644 index 00000000000..98e72d7696f --- /dev/null +++ b/tests/run-make/print-native-static-libs/Makefile @@ -0,0 +1,15 @@ +include ../tools.mk + +# ignore-cross-compile +# ignore-wasm + +all: + $(RUSTC) --crate-type rlib -lbar_cli bar.rs + $(RUSTC) foo.rs -lfoo_cli --crate-type staticlib --print native-static-libs 2>&1 \ + | grep 'note: native-static-libs: ' \ + | sed 's/note: native-static-libs: \(.*\)/\1/' > $(TMPDIR)/libs.txt + + cat $(TMPDIR)/libs.txt | grep -F "glib-2.0" # in bar.rs + cat $(TMPDIR)/libs.txt | grep -F "systemd" # in foo.rs + cat $(TMPDIR)/libs.txt | grep -F "bar_cli" + cat $(TMPDIR)/libs.txt | grep -F "foo_cli" diff --git a/tests/run-make/print-native-static-libs/bar.rs b/tests/run-make/print-native-static-libs/bar.rs new file mode 100644 index 00000000000..a563bbc2a22 --- /dev/null +++ b/tests/run-make/print-native-static-libs/bar.rs @@ -0,0 +1,13 @@ +#[no_mangle] +pub extern "C" fn my_bar_add(left: i32, right: i32) -> i32 { + // Obviously makes no sense but... + unsafe { + g_free(std::ptr::null_mut()); + } + left + right +} + +#[link(name = "glib-2.0")] +extern "C" { + fn g_free(p: *mut ()); +} diff --git a/tests/run-make/print-native-static-libs/foo.rs b/tests/run-make/print-native-static-libs/foo.rs new file mode 100644 index 00000000000..6acaee20ed4 --- /dev/null +++ b/tests/run-make/print-native-static-libs/foo.rs @@ -0,0 +1,15 @@ +extern crate bar; + +#[no_mangle] +pub extern "C" fn my_foo_add(left: i32, right: i32) -> i32 { + // Obviously makes no sense but... + unsafe { + init(std::ptr::null_mut()); + } + bar::my_bar_add(left, right) +} + +#[link(name = "systemd")] +extern "C" { + fn init(p: *mut ()); +} diff --git a/tests/rustdoc-gui/anchors.goml b/tests/rustdoc-gui/anchors.goml index 0904aa90e1b..e9b77296917 100644 --- a/tests/rustdoc-gui/anchors.goml +++ b/tests/rustdoc-gui/anchors.goml @@ -75,35 +75,35 @@ call-function: ( "check-colors", { "theme": "ayu", - "main_color": "rgb(197, 197, 197)", - "title_color": "rgb(255, 255, 255)", - "main_heading_color": "rgb(255, 255, 255)", - "main_heading_type_color": "rgb(255, 160, 165)", - "src_link_color": "rgb(57, 175, 215)", - "sidebar_link_color": "rgb(83, 177, 219)", + "main_color": "#c5c5c5", + "title_color": "#fff", + "main_heading_color": "#fff", + "main_heading_type_color": "#ffa0a5", + "src_link_color": "#39afd7", + "sidebar_link_color": "#53b1db", }, ) call-function: ( "check-colors", { "theme": "dark", - "main_color": "rgb(221, 221, 221)", - "title_color": "rgb(221, 221, 221)", - "main_heading_color": "rgb(221, 221, 221)", - "main_heading_type_color": "rgb(45, 191, 184)", - "src_link_color": "rgb(210, 153, 29)", - "sidebar_link_color": "rgb(253, 191, 53)", + "main_color": "#ddd", + "title_color": "#ddd", + "main_heading_color": "#ddd", + "main_heading_type_color": "#2dbfb8", + "src_link_color": "#d2991d", + "sidebar_link_color": "#fdbf35", }, ) call-function: ( "check-colors", { "theme": "light", - "main_color": "rgb(0, 0, 0)", - "title_color": "rgb(0, 0, 0)", - "main_heading_color": "rgb(0, 0, 0)", - "main_heading_type_color": "rgb(173, 55, 138)", - "src_link_color": "rgb(56, 115, 173)", - "sidebar_link_color": "rgb(53, 109, 164)", + "main_color": "black", + "title_color": "black", + "main_heading_color": "black", + "main_heading_type_color": "#ad378a", + "src_link_color": "#3873ad", + "sidebar_link_color": "#356da4", }, ) diff --git a/tests/rustdoc-gui/codeblock-tooltip.goml b/tests/rustdoc-gui/codeblock-tooltip.goml index 2ed0579d314..e1c81ed79e4 100644 --- a/tests/rustdoc-gui/codeblock-tooltip.goml +++ b/tests/rustdoc-gui/codeblock-tooltip.goml @@ -109,19 +109,19 @@ define-function: ( call-function: ("check-colors", { "theme": "ayu", - "background": "rgb(15, 20, 25)", - "color": "rgb(197, 197, 197)", - "border": "rgb(92, 103, 115)", + "background": "#0f1419", + "color": "#c5c5c5", + "border": "#5c6773", }) call-function: ("check-colors", { "theme": "dark", - "background": "rgb(53, 53, 53)", - "color": "rgb(221, 221, 221)", - "border": "rgb(224, 224, 224)", + "background": "#353535", + "color": "#ddd", + "border": "#e0e0e0", }) call-function: ("check-colors", { "theme": "light", - "background": "rgb(255, 255, 255)", - "color": "rgb(0, 0, 0)", - "border": "rgb(224, 224, 224)", + "background": "white", + "color": "black", + "border": "#e0e0e0", }) diff --git a/tests/rustdoc-gui/jump-to-def-background.goml b/tests/rustdoc-gui/jump-to-def-background.goml index 3a7d48284d7..6adc36b0edb 100644 --- a/tests/rustdoc-gui/jump-to-def-background.goml +++ b/tests/rustdoc-gui/jump-to-def-background.goml @@ -17,6 +17,6 @@ define-function: ( }, ) -call-function: ("check-background-color", ("ayu", "rgb(51, 51, 51)")) -call-function: ("check-background-color", ("dark", "rgb(51, 51, 51)")) -call-function: ("check-background-color", ("light", "rgb(238, 238, 238)")) +call-function: ("check-background-color", ("ayu", "#333")) +call-function: ("check-background-color", ("dark", "#333")) +call-function: ("check-background-color", ("light", "#eee")) diff --git a/tests/rustdoc-gui/scrape-examples-color.goml b/tests/rustdoc-gui/scrape-examples-color.goml index 8ddb06fccfc..0052d18dc56 100644 --- a/tests/rustdoc-gui/scrape-examples-color.goml +++ b/tests/rustdoc-gui/scrape-examples-color.goml @@ -33,30 +33,30 @@ define-function: ( call-function: ("check-colors", { "theme": "ayu", - "highlight": "rgb(91, 59, 1)", - "highlight_focus": "rgb(124, 75, 15)", - "help_border": "rgb(170, 170, 170)", - "help_color": "rgb(238, 238, 238)", - "help_hover_border": "rgb(255, 255, 255)", - "help_hover_color": "rgb(255, 255, 255)", + "highlight": "#5b3b01", + "highlight_focus": "#7c4b0f", + "help_border": "#aaa", + "help_color": "#eee", + "help_hover_border": "#fff", + "help_hover_color": "#fff", }) call-function: ("check-colors", { "theme": "dark", - "highlight": "rgb(91, 59, 1)", - "highlight_focus": "rgb(124, 75, 15)", - "help_border": "rgb(170, 170, 170)", - "help_color": "rgb(238, 238, 238)", - "help_hover_border": "rgb(255, 255, 255)", - "help_hover_color": "rgb(255, 255, 255)", + "highlight": "#5b3b01", + "highlight_focus": "#7c4b0f", + "help_border": "#aaa", + "help_color": "#eee", + "help_hover_border": "#fff", + "help_hover_color": "#fff", }) call-function: ("check-colors", { "theme": "light", - "highlight": "rgb(252, 255, 214)", - "highlight_focus": "rgb(246, 253, 176)", - "help_border": "rgb(85, 85, 85)", - "help_color": "rgb(51, 51, 51)", - "help_hover_border": "rgb(0, 0, 0)", - "help_hover_color": "rgb(0, 0, 0)", + "highlight": "#fcffd6", + "highlight_focus": "#f6fdb0", + "help_border": "#555", + "help_color": "#333", + "help_hover_border": "#000", + "help_hover_color": "#000", }) // Now testing the top and bottom background in case there is only one scraped examples. @@ -81,16 +81,16 @@ define-function: ( call-function: ("check-background", { "theme": "ayu", - "background_color_start": "rgb(15, 20, 25)", + "background_color_start": "rgba(15, 20, 25, 1)", "background_color_end": "rgba(15, 20, 25, 0)", }) call-function: ("check-background", { "theme": "dark", - "background_color_start": "rgb(53, 53, 53)", + "background_color_start": "rgba(53, 53, 53, 1)", "background_color_end": "rgba(53, 53, 53, 0)", }) call-function: ("check-background", { "theme": "light", - "background_color_start": "rgb(255, 255, 255)", + "background_color_start": "rgba(255, 255, 255, 1)", "background_color_end": "rgba(255, 255, 255, 0)", }) diff --git a/tests/rustdoc-gui/settings.goml b/tests/rustdoc-gui/settings.goml index bf1fe7be910..c37d969324c 100644 --- a/tests/rustdoc-gui/settings.goml +++ b/tests/rustdoc-gui/settings.goml @@ -301,7 +301,7 @@ wait-for-css: ("#help-button .popover", {"display": "block"}) // Now we go to the settings page to check that the CSS is loaded as expected. go-to: "file://" + |DOC_PATH| + "/settings.html" wait-for: "#settings" -assert-css: (".setting-line", {"position": "relative"}) +assert-css: (".setting-radio", {"cursor": "pointer"}) assert-attribute-false: ("#settings", {"class": "popover"}, CONTAINS) compare-elements-position: (".sub form", "#settings", ("x")) @@ -322,4 +322,4 @@ reload: set-window-size: (300, 1000) click: "#settings-menu" wait-for: "#settings" -assert-css: (".setting-line", {"position": "relative"}) +assert-css: (".setting-radio", {"cursor": "pointer"}) diff --git a/tests/rustdoc-gui/source-code-page.goml b/tests/rustdoc-gui/source-code-page.goml index 5c795928bdc..d5dd511b1d3 100644 --- a/tests/rustdoc-gui/source-code-page.goml +++ b/tests/rustdoc-gui/source-code-page.goml @@ -40,24 +40,24 @@ define-function: ( call-function: ("check-colors", { "theme": "ayu", - "color": "rgb(92, 103, 115)", - "background_color": "rgba(0, 0, 0, 0)", - "highlight_color": "rgb(112, 128, 144)", + "color": "#5c6773", + "background_color": "transparent", + "highlight_color": "#708090", "highlight_background_color": "rgba(255, 236, 164, 0.06)", }) call-function: ("check-colors", { "theme": "dark", - "color": "rgb(59, 145, 226)", - "background_color": "rgba(0, 0, 0, 0)", - "highlight_color": "rgb(59, 145, 226)", - "highlight_background_color": "rgb(10, 4, 47)", + "color": "#3b91e2", + "background_color": "transparent", + "highlight_color": "#3b91e2", + "highlight_background_color": "#0a042f", }) call-function: ("check-colors", { "theme": "light", - "color": "rgb(198, 126, 45)", - "background_color": "rgba(0, 0, 0, 0)", - "highlight_color": "rgb(198, 126, 45)", - "highlight_background_color": "rgb(253, 255, 211)", + "color": "#c67e2d", + "background_color": "transparent", + "highlight_color": "#c67e2d", + "highlight_background_color": "#fdffd3", }) // This is to ensure that the content is correctly align with the line numbers. diff --git a/tests/rustdoc-gui/stab-badge.goml b/tests/rustdoc-gui/stab-badge.goml index e2340418871..bb3d2aaa3dc 100644 --- a/tests/rustdoc-gui/stab-badge.goml +++ b/tests/rustdoc-gui/stab-badge.goml @@ -26,16 +26,16 @@ define-function: ( call-function: ("check-badge", { "theme": "ayu", - "color": "rgb(197, 197, 197)", - "background": "rgb(49, 69, 89)", + "color": "#c5c5c5", + "background": "#314559", }) call-function: ("check-badge", { "theme": "dark", - "color": "rgb(221, 221, 221)", - "background": "rgb(49, 69, 89)", + "color": "#ddd", + "background": "#314559", }) call-function: ("check-badge", { "theme": "light", - "color": "rgb(0, 0, 0)", - "background": "rgb(255, 245, 214)", + "color": "black", + "background": "#fff5d6", }) diff --git a/tests/rustdoc-json/assoc_items.rs b/tests/rustdoc-json/assoc_items.rs index 6d7f6bb969f..05c2d428393 100644 --- a/tests/rustdoc-json/assoc_items.rs +++ b/tests/rustdoc-json/assoc_items.rs @@ -3,35 +3,32 @@ pub struct Simple; impl Simple { - // @is "$.index[*][?(@.name=='CONSTANT')].kind" \"assoc_const\" + // @has "$.index[*][?(@.name=='CONSTANT')].inner.assoc_const" pub const CONSTANT: usize = 0; } pub trait EasyToImpl { - // @is "$.index[*][?(@.docs=='ToDeclare trait')].kind" \"assoc_type\" - // @is "$.index[*][?(@.docs=='ToDeclare trait')].inner.default" null - // @is "$.index[*][?(@.docs=='ToDeclare trait')].inner.bounds" [] + // @has "$.index[*][?(@.docs=='ToDeclare trait')].inner.assoc_type" + // @is "$.index[*][?(@.docs=='ToDeclare trait')].inner.assoc_type.default" null + // @is "$.index[*][?(@.docs=='ToDeclare trait')].inner.assoc_type.bounds" [] /// ToDeclare trait type ToDeclare; - // @is "$.index[*][?(@.docs=='AN_ATTRIBUTE trait')].kind" \"assoc_const\" - // @is "$.index[*][?(@.docs=='AN_ATTRIBUTE trait')].inner.default" null - // @is "$.index[*][?(@.docs=='AN_ATTRIBUTE trait')].inner.type.kind" '"primitive"' - // @is "$.index[*][?(@.docs=='AN_ATTRIBUTE trait')].inner.type.inner" '"usize"' + // @has "$.index[*][?(@.docs=='AN_ATTRIBUTE trait')].inner.assoc_const" + // @is "$.index[*][?(@.docs=='AN_ATTRIBUTE trait')].inner.assoc_const.default" null + // @is "$.index[*][?(@.docs=='AN_ATTRIBUTE trait')].inner.assoc_const.type.primitive" '"usize"' /// AN_ATTRIBUTE trait const AN_ATTRIBUTE: usize; } impl EasyToImpl for Simple { - // @is "$.index[*][?(@.docs=='ToDeclare impl')].kind" '"assoc_type"' - // @is "$.index[*][?(@.docs=='ToDeclare impl')].inner.default.kind" \"primitive\" - // @is "$.index[*][?(@.docs=='ToDeclare impl')].inner.default.inner" \"usize\" + // @has "$.index[*][?(@.docs=='ToDeclare impl')].inner.assoc_type" + // @is "$.index[*][?(@.docs=='ToDeclare impl')].inner.assoc_type.default.primitive" \"usize\" /// ToDeclare impl type ToDeclare = usize; - // @is "$.index[*][?(@.docs=='AN_ATTRIBUTE impl')].kind" '"assoc_const"' - // @is "$.index[*][?(@.docs=='AN_ATTRIBUTE impl')].inner.type.kind" \"primitive\" - // @is "$.index[*][?(@.docs=='AN_ATTRIBUTE impl')].inner.type.inner" \"usize\" - // @is "$.index[*][?(@.docs=='AN_ATTRIBUTE impl')].inner.default" \"12\" + // @has "$.index[*][?(@.docs=='AN_ATTRIBUTE impl')].inner.assoc_const" + // @is "$.index[*][?(@.docs=='AN_ATTRIBUTE impl')].inner.assoc_const.type.primitive" \"usize\" + // @is "$.index[*][?(@.docs=='AN_ATTRIBUTE impl')].inner.assoc_const.default" \"12\" /// AN_ATTRIBUTE impl const AN_ATTRIBUTE: usize = 12; } diff --git a/tests/rustdoc-json/blanket_impls.rs b/tests/rustdoc-json/blanket_impls.rs index c5cc87ca1eb..a2a5c4a7146 100644 --- a/tests/rustdoc-json/blanket_impls.rs +++ b/tests/rustdoc-json/blanket_impls.rs @@ -2,7 +2,7 @@ #![no_std] -// @has "$.index[*][?(@.name=='Error')].kind" \"assoc_type\" -// @has "$.index[*][?(@.name=='Error')].inner.default.kind" \"resolved_path\" -// @has "$.index[*][?(@.name=='Error')].inner.default.inner.name" \"Infallible\" +// @has "$.index[*][?(@.name=='Error')].inner.assoc_type" +// @has "$.index[*][?(@.name=='Error')].inner.assoc_type.default.resolved_path" +// @has "$.index[*][?(@.name=='Error')].inner.assoc_type.default.resolved_path.name" \"Infallible\" pub struct ForBlanketTryFromImpl; diff --git a/tests/rustdoc-json/enums/discriminant/basic.rs b/tests/rustdoc-json/enums/discriminant/basic.rs index 06906df3b2c..dbfc5c2cf6b 100644 --- a/tests/rustdoc-json/enums/discriminant/basic.rs +++ b/tests/rustdoc-json/enums/discriminant/basic.rs @@ -1,12 +1,12 @@ #[repr(i8)] pub enum Ordering { - // @is "$.index[*][?(@.name=='Less')].inner.discriminant.expr" '"-1"' - // @is "$.index[*][?(@.name=='Less')].inner.discriminant.value" '"-1"' + // @is "$.index[*][?(@.name=='Less')].inner.variant.discriminant.expr" '"-1"' + // @is "$.index[*][?(@.name=='Less')].inner.variant.discriminant.value" '"-1"' Less = -1, - // @is "$.index[*][?(@.name=='Equal')].inner.discriminant.expr" '"0"' - // @is "$.index[*][?(@.name=='Equal')].inner.discriminant.value" '"0"' + // @is "$.index[*][?(@.name=='Equal')].inner.variant.discriminant.expr" '"0"' + // @is "$.index[*][?(@.name=='Equal')].inner.variant.discriminant.value" '"0"' Equal = 0, - // @is "$.index[*][?(@.name=='Greater')].inner.discriminant.expr" '"1"' - // @is "$.index[*][?(@.name=='Greater')].inner.discriminant.value" '"1"' + // @is "$.index[*][?(@.name=='Greater')].inner.variant.discriminant.expr" '"1"' + // @is "$.index[*][?(@.name=='Greater')].inner.variant.discriminant.value" '"1"' Greater = 1, } diff --git a/tests/rustdoc-json/enums/discriminant/expr.rs b/tests/rustdoc-json/enums/discriminant/expr.rs index e639965e79b..ddcad58a550 100644 --- a/tests/rustdoc-json/enums/discriminant/expr.rs +++ b/tests/rustdoc-json/enums/discriminant/expr.rs @@ -1,30 +1,30 @@ pub enum Foo { - // @is "$.index[*][?(@.name=='Addition')].inner.discriminant.value" '"0"' - // @is "$.index[*][?(@.name=='Addition')].inner.discriminant.expr" '"{ _ }"' + // @is "$.index[*][?(@.name=='Addition')].inner.variant.discriminant.value" '"0"' + // @is "$.index[*][?(@.name=='Addition')].inner.variant.discriminant.expr" '"{ _ }"' Addition = 0 + 0, - // @is "$.index[*][?(@.name=='Bin')].inner.discriminant.value" '"1"' - // @is "$.index[*][?(@.name=='Bin')].inner.discriminant.expr" '"0b1"' + // @is "$.index[*][?(@.name=='Bin')].inner.variant.discriminant.value" '"1"' + // @is "$.index[*][?(@.name=='Bin')].inner.variant.discriminant.expr" '"0b1"' Bin = 0b1, - // @is "$.index[*][?(@.name=='Oct')].inner.discriminant.value" '"2"' - // @is "$.index[*][?(@.name=='Oct')].inner.discriminant.expr" '"0o2"' + // @is "$.index[*][?(@.name=='Oct')].inner.variant.discriminant.value" '"2"' + // @is "$.index[*][?(@.name=='Oct')].inner.variant.discriminant.expr" '"0o2"' Oct = 0o2, - // @is "$.index[*][?(@.name=='PubConst')].inner.discriminant.value" '"3"' - // @is "$.index[*][?(@.name=='PubConst')].inner.discriminant.expr" '"THREE"' + // @is "$.index[*][?(@.name=='PubConst')].inner.variant.discriminant.value" '"3"' + // @is "$.index[*][?(@.name=='PubConst')].inner.variant.discriminant.expr" '"THREE"' PubConst = THREE, - // @is "$.index[*][?(@.name=='Hex')].inner.discriminant.value" '"4"' - // @is "$.index[*][?(@.name=='Hex')].inner.discriminant.expr" '"0x4"' + // @is "$.index[*][?(@.name=='Hex')].inner.variant.discriminant.value" '"4"' + // @is "$.index[*][?(@.name=='Hex')].inner.variant.discriminant.expr" '"0x4"' Hex = 0x4, - // @is "$.index[*][?(@.name=='Cast')].inner.discriminant.value" '"5"' - // @is "$.index[*][?(@.name=='Cast')].inner.discriminant.expr" '"{ _ }"' + // @is "$.index[*][?(@.name=='Cast')].inner.variant.discriminant.value" '"5"' + // @is "$.index[*][?(@.name=='Cast')].inner.variant.discriminant.expr" '"{ _ }"' Cast = 5 as isize, - // @is "$.index[*][?(@.name=='PubCall')].inner.discriminant.value" '"6"' - // @is "$.index[*][?(@.name=='PubCall')].inner.discriminant.expr" '"{ _ }"' + // @is "$.index[*][?(@.name=='PubCall')].inner.variant.discriminant.value" '"6"' + // @is "$.index[*][?(@.name=='PubCall')].inner.variant.discriminant.expr" '"{ _ }"' PubCall = six(), - // @is "$.index[*][?(@.name=='PrivCall')].inner.discriminant.value" '"7"' - // @is "$.index[*][?(@.name=='PrivCall')].inner.discriminant.expr" '"{ _ }"' + // @is "$.index[*][?(@.name=='PrivCall')].inner.variant.discriminant.value" '"7"' + // @is "$.index[*][?(@.name=='PrivCall')].inner.variant.discriminant.expr" '"{ _ }"' PrivCall = seven(), - // @is "$.index[*][?(@.name=='PrivConst')].inner.discriminant.value" '"8"' - // @is "$.index[*][?(@.name=='PrivConst')].inner.discriminant.expr" '"EIGHT"' + // @is "$.index[*][?(@.name=='PrivConst')].inner.variant.discriminant.value" '"8"' + // @is "$.index[*][?(@.name=='PrivConst')].inner.variant.discriminant.expr" '"EIGHT"' PrivConst = EIGHT, } diff --git a/tests/rustdoc-json/enums/discriminant/limits.rs b/tests/rustdoc-json/enums/discriminant/limits.rs index e56d5594f2f..47fb7040896 100644 --- a/tests/rustdoc-json/enums/discriminant/limits.rs +++ b/tests/rustdoc-json/enums/discriminant/limits.rs @@ -4,40 +4,40 @@ #[repr(u64)] pub enum U64 { - // @is "$.index[*][?(@.name=='U64Min')].inner.discriminant.value" '"0"' - // @is "$.index[*][?(@.name=='U64Min')].inner.discriminant.expr" '"u64::MIN"' + // @is "$.index[*][?(@.name=='U64Min')].inner.variant.discriminant.value" '"0"' + // @is "$.index[*][?(@.name=='U64Min')].inner.variant.discriminant.expr" '"u64::MIN"' U64Min = u64::MIN, - // @is "$.index[*][?(@.name=='U64Max')].inner.discriminant.value" '"18446744073709551615"' - // @is "$.index[*][?(@.name=='U64Max')].inner.discriminant.expr" '"u64::MAX"' + // @is "$.index[*][?(@.name=='U64Max')].inner.variant.discriminant.value" '"18446744073709551615"' + // @is "$.index[*][?(@.name=='U64Max')].inner.variant.discriminant.expr" '"u64::MAX"' U64Max = u64::MAX, } #[repr(i64)] pub enum I64 { - // @is "$.index[*][?(@.name=='I64Min')].inner.discriminant.value" '"-9223372036854775808"' - // @is "$.index[*][?(@.name=='I64Min')].inner.discriminant.expr" '"i64::MIN"' + // @is "$.index[*][?(@.name=='I64Min')].inner.variant.discriminant.value" '"-9223372036854775808"' + // @is "$.index[*][?(@.name=='I64Min')].inner.variant.discriminant.expr" '"i64::MIN"' I64Min = i64::MIN, - // @is "$.index[*][?(@.name=='I64Max')].inner.discriminant.value" '"9223372036854775807"' - // @is "$.index[*][?(@.name=='I64Max')].inner.discriminant.expr" '"i64::MAX"' + // @is "$.index[*][?(@.name=='I64Max')].inner.variant.discriminant.value" '"9223372036854775807"' + // @is "$.index[*][?(@.name=='I64Max')].inner.variant.discriminant.expr" '"i64::MAX"' I64Max = i64::MAX, } #[repr(u128)] pub enum U128 { - // @is "$.index[*][?(@.name=='U128Min')].inner.discriminant.value" '"0"' - // @is "$.index[*][?(@.name=='U128Min')].inner.discriminant.expr" '"u128::MIN"' + // @is "$.index[*][?(@.name=='U128Min')].inner.variant.discriminant.value" '"0"' + // @is "$.index[*][?(@.name=='U128Min')].inner.variant.discriminant.expr" '"u128::MIN"' U128Min = u128::MIN, - // @is "$.index[*][?(@.name=='U128Max')].inner.discriminant.value" '"340282366920938463463374607431768211455"' - // @is "$.index[*][?(@.name=='U128Max')].inner.discriminant.expr" '"u128::MAX"' + // @is "$.index[*][?(@.name=='U128Max')].inner.variant.discriminant.value" '"340282366920938463463374607431768211455"' + // @is "$.index[*][?(@.name=='U128Max')].inner.variant.discriminant.expr" '"u128::MAX"' U128Max = u128::MAX, } #[repr(i128)] pub enum I128 { - // @is "$.index[*][?(@.name=='I128Min')].inner.discriminant.value" '"-170141183460469231731687303715884105728"' - // @is "$.index[*][?(@.name=='I128Min')].inner.discriminant.expr" '"i128::MIN"' + // @is "$.index[*][?(@.name=='I128Min')].inner.variant.discriminant.value" '"-170141183460469231731687303715884105728"' + // @is "$.index[*][?(@.name=='I128Min')].inner.variant.discriminant.expr" '"i128::MIN"' I128Min = i128::MIN, - // @is "$.index[*][?(@.name=='I128Max')].inner.discriminant.value" '"170141183460469231731687303715884105727"' - // @is "$.index[*][?(@.name=='I128Max')].inner.discriminant.expr" '"i128::MAX"' + // @is "$.index[*][?(@.name=='I128Max')].inner.variant.discriminant.value" '"170141183460469231731687303715884105727"' + // @is "$.index[*][?(@.name=='I128Max')].inner.variant.discriminant.expr" '"i128::MAX"' I128Max = i128::MAX, } diff --git a/tests/rustdoc-json/enums/discriminant/num_underscore_and_suffix.rs b/tests/rustdoc-json/enums/discriminant/num_underscore_and_suffix.rs index 6889b305ffb..9c3db81c663 100644 --- a/tests/rustdoc-json/enums/discriminant/num_underscore_and_suffix.rs +++ b/tests/rustdoc-json/enums/discriminant/num_underscore_and_suffix.rs @@ -1,15 +1,17 @@ +// ignore-tidy-linelength + #[repr(u32)] pub enum Foo { - // @is "$.index[*][?(@.name=='Basic')].inner.discriminant.value" '"0"' - // @is "$.index[*][?(@.name=='Basic')].inner.discriminant.expr" '"0"' + // @is "$.index[*][?(@.name=='Basic')].inner.variant.discriminant.value" '"0"' + // @is "$.index[*][?(@.name=='Basic')].inner.variant.discriminant.expr" '"0"' Basic = 0, - // @is "$.index[*][?(@.name=='Suffix')].inner.discriminant.value" '"10"' - // @is "$.index[*][?(@.name=='Suffix')].inner.discriminant.expr" '"10u32"' + // @is "$.index[*][?(@.name=='Suffix')].inner.variant.discriminant.value" '"10"' + // @is "$.index[*][?(@.name=='Suffix')].inner.variant.discriminant.expr" '"10u32"' Suffix = 10u32, - // @is "$.index[*][?(@.name=='Underscore')].inner.discriminant.value" '"100"' - // @is "$.index[*][?(@.name=='Underscore')].inner.discriminant.expr" '"1_0_0"' + // @is "$.index[*][?(@.name=='Underscore')].inner.variant.discriminant.value" '"100"' + // @is "$.index[*][?(@.name=='Underscore')].inner.variant.discriminant.expr" '"1_0_0"' Underscore = 1_0_0, - // @is "$.index[*][?(@.name=='SuffixUnderscore')].inner.discriminant.value" '"1000"' - // @is "$.index[*][?(@.name=='SuffixUnderscore')].inner.discriminant.expr" '"1_0_0_0u32"' + // @is "$.index[*][?(@.name=='SuffixUnderscore')].inner.variant.discriminant.value" '"1000"' + // @is "$.index[*][?(@.name=='SuffixUnderscore')].inner.variant.discriminant.expr" '"1_0_0_0u32"' SuffixUnderscore = 1_0_0_0u32, } diff --git a/tests/rustdoc-json/enums/discriminant/only_some_have_discriminant.rs b/tests/rustdoc-json/enums/discriminant/only_some_have_discriminant.rs index 6a4f54de617..38ba1caf140 100644 --- a/tests/rustdoc-json/enums/discriminant/only_some_have_discriminant.rs +++ b/tests/rustdoc-json/enums/discriminant/only_some_have_discriminant.rs @@ -1,10 +1,12 @@ +// ignore-tidy-linelength + pub enum Foo { - // @is "$.index[*][?(@.name=='Has')].inner.discriminant" '{"expr":"0", "value":"0"}' + // @is "$.index[*][?(@.name=='Has')].inner.variant.discriminant" '{"expr":"0", "value":"0"}' Has = 0, - // @is "$.index[*][?(@.name=='Doesnt')].inner.discriminant" null + // @is "$.index[*][?(@.name=='Doesnt')].inner.variant.discriminant" null Doesnt, - // @is "$.index[*][?(@.name=='AlsoDoesnt')].inner.discriminant" null + // @is "$.index[*][?(@.name=='AlsoDoesnt')].inner.variant.discriminant" null AlsoDoesnt, - // @is "$.index[*][?(@.name=='AlsoHas')].inner.discriminant" '{"expr":"44", "value":"44"}' + // @is "$.index[*][?(@.name=='AlsoHas')].inner.variant.discriminant" '{"expr":"44", "value":"44"}' AlsoHas = 44, } diff --git a/tests/rustdoc-json/enums/discriminant/struct.rs b/tests/rustdoc-json/enums/discriminant/struct.rs index e91a632a3b3..c4b087c3635 100644 --- a/tests/rustdoc-json/enums/discriminant/struct.rs +++ b/tests/rustdoc-json/enums/discriminant/struct.rs @@ -3,13 +3,13 @@ #[repr(i32)] // @is "$.index[*][?(@.name=='Foo')].attrs" '["#[repr(i32)]"]' pub enum Foo { - // @is "$.index[*][?(@.name=='Struct')].inner.discriminant" null - // @count "$.index[*][?(@.name=='Struct')].inner.kind.struct.fields[*]" 0 + // @is "$.index[*][?(@.name=='Struct')].inner.variant.discriminant" null + // @count "$.index[*][?(@.name=='Struct')].inner.variant.kind.struct.fields[*]" 0 Struct {}, - // @is "$.index[*][?(@.name=='StructWithDiscr')].inner.discriminant" '{"expr": "42", "value": "42"}' - // @count "$.index[*][?(@.name=='StructWithDiscr')].inner.kind.struct.fields[*]" 1 + // @is "$.index[*][?(@.name=='StructWithDiscr')].inner.variant.discriminant" '{"expr": "42", "value": "42"}' + // @count "$.index[*][?(@.name=='StructWithDiscr')].inner.variant.kind.struct.fields[*]" 1 StructWithDiscr { x: i32 } = 42, - // @is "$.index[*][?(@.name=='StructWithHexDiscr')].inner.discriminant" '{"expr": "0x42", "value": "66"}' - // @count "$.index[*][?(@.name=='StructWithHexDiscr')].inner.kind.struct.fields[*]" 2 + // @is "$.index[*][?(@.name=='StructWithHexDiscr')].inner.variant.discriminant" '{"expr": "0x42", "value": "66"}' + // @count "$.index[*][?(@.name=='StructWithHexDiscr')].inner.variant.kind.struct.fields[*]" 2 StructWithHexDiscr { x: i32, y: bool } = 0x42, } diff --git a/tests/rustdoc-json/enums/discriminant/tuple.rs b/tests/rustdoc-json/enums/discriminant/tuple.rs index b94d5739eab..7da82ec8ea4 100644 --- a/tests/rustdoc-json/enums/discriminant/tuple.rs +++ b/tests/rustdoc-json/enums/discriminant/tuple.rs @@ -3,13 +3,13 @@ #[repr(u32)] // @is "$.index[*][?(@.name=='Foo')].attrs" '["#[repr(u32)]"]' pub enum Foo { - // @is "$.index[*][?(@.name=='Tuple')].inner.discriminant" null - // @count "$.index[*][?(@.name=='Tuple')].inner.kind.tuple[*]" 0 + // @is "$.index[*][?(@.name=='Tuple')].inner.variant.discriminant" null + // @count "$.index[*][?(@.name=='Tuple')].inner.variant.kind.tuple[*]" 0 Tuple(), - // @is "$.index[*][?(@.name=='TupleWithDiscr')].inner.discriminant" '{"expr": "1", "value": "1"}' - // @count "$.index[*][?(@.name=='TupleWithDiscr')].inner.kind.tuple[*]" 1 + // @is "$.index[*][?(@.name=='TupleWithDiscr')].inner.variant.discriminant" '{"expr": "1", "value": "1"}' + // @count "$.index[*][?(@.name=='TupleWithDiscr')].inner.variant.kind.tuple[*]" 1 TupleWithDiscr(i32) = 1, - // @is "$.index[*][?(@.name=='TupleWithBinDiscr')].inner.discriminant" '{"expr": "0b10", "value": "2"}' - // @count "$.index[*][?(@.name=='TupleWithBinDiscr')].inner.kind.tuple[*]" 2 + // @is "$.index[*][?(@.name=='TupleWithBinDiscr')].inner.variant.discriminant" '{"expr": "0b10", "value": "2"}' + // @count "$.index[*][?(@.name=='TupleWithBinDiscr')].inner.variant.kind.tuple[*]" 2 TupleWithBinDiscr(i32, i32) = 0b10, } diff --git a/tests/rustdoc-json/enums/doc_link_to_foreign_variant.rs b/tests/rustdoc-json/enums/doc_link_to_foreign_variant.rs index 470b195a292..b00156124fc 100644 --- a/tests/rustdoc-json/enums/doc_link_to_foreign_variant.rs +++ b/tests/rustdoc-json/enums/doc_link_to_foreign_variant.rs @@ -5,7 +5,7 @@ extern crate color; use color::Color::Red; -// @set red = "$.index[*][?(@.inner.is_crate == true)].links.Red" +// @set red = "$.index[*][?(@.inner.module.is_crate)].links.Red" // @!has "$.index[*][?(@.name == 'Red')]" // @!has "$.index[*][?(@.name == 'Color')]" diff --git a/tests/rustdoc-json/enums/field_hidden.rs b/tests/rustdoc-json/enums/field_hidden.rs index 78a05431472..5c0d0ffd3df 100644 --- a/tests/rustdoc-json/enums/field_hidden.rs +++ b/tests/rustdoc-json/enums/field_hidden.rs @@ -5,8 +5,8 @@ // @has "$.index[*][?(@.name=='ParseError')]" // @has "$.index[*][?(@.name=='UnexpectedEndTag')]" -// @is "$.index[*][?(@.name=='UnexpectedEndTag')].inner.kind.tuple" [null] -// @is "$.index[*][?(@.name=='UnexpectedEndTag')].inner.discriminant" null +// @is "$.index[*][?(@.name=='UnexpectedEndTag')].inner.variant.kind.tuple" [null] +// @is "$.index[*][?(@.name=='UnexpectedEndTag')].inner.variant.discriminant" null pub enum ParseError { UnexpectedEndTag(#[doc(hidden)] u32), diff --git a/tests/rustdoc-json/enums/kind.rs b/tests/rustdoc-json/enums/kind.rs index 1787a859c8b..e283c074006 100644 --- a/tests/rustdoc-json/enums/kind.rs +++ b/tests/rustdoc-json/enums/kind.rs @@ -5,28 +5,28 @@ pub enum Foo { // @set Unit = "$.index[*][?(@.name=='Unit')].id" - // @is "$.index[*][?(@.name=='Unit')].inner.kind" '"plain"' + // @is "$.index[*][?(@.name=='Unit')].inner.variant.kind" '"plain"' Unit, // @set Named = "$.index[*][?(@.name=='Named')].id" - // @is "$.index[*][?(@.name=='Named')].inner.kind.struct" '{"fields": [], "fields_stripped": false}' + // @is "$.index[*][?(@.name=='Named')].inner.variant.kind.struct" '{"fields": [], "fields_stripped": false}' Named {}, // @set Tuple = "$.index[*][?(@.name=='Tuple')].id" - // @is "$.index[*][?(@.name=='Tuple')].inner.kind.tuple" [] + // @is "$.index[*][?(@.name=='Tuple')].inner.variant.kind.tuple" [] Tuple(), // @set NamedField = "$.index[*][?(@.name=='NamedField')].id" - // @set x = "$.index[*][?(@.name=='x' && @.kind=='struct_field')].id" - // @is "$.index[*][?(@.name=='NamedField')].inner.kind.struct.fields[*]" $x - // @is "$.index[*][?(@.name=='NamedField')].inner.kind.struct.fields_stripped" false + // @set x = "$.index[*][?(@.name=='x' && @.inner.struct_field)].id" + // @is "$.index[*][?(@.name=='NamedField')].inner.variant.kind.struct.fields[*]" $x + // @is "$.index[*][?(@.name=='NamedField')].inner.variant.kind.struct.fields_stripped" false NamedField { x: i32 }, // @set TupleField = "$.index[*][?(@.name=='TupleField')].id" - // @set tup_field = "$.index[*][?(@.name=='0' && @.kind=='struct_field')].id" - // @is "$.index[*][?(@.name=='TupleField')].inner.kind.tuple[*]" $tup_field + // @set tup_field = "$.index[*][?(@.name=='0' && @.inner.struct_field)].id" + // @is "$.index[*][?(@.name=='TupleField')].inner.variant.kind.tuple[*]" $tup_field TupleField(i32), } -// @is "$.index[*][?(@.name=='Foo')].inner.variants[0]" $Unit -// @is "$.index[*][?(@.name=='Foo')].inner.variants[1]" $Named -// @is "$.index[*][?(@.name=='Foo')].inner.variants[2]" $Tuple -// @is "$.index[*][?(@.name=='Foo')].inner.variants[3]" $NamedField -// @is "$.index[*][?(@.name=='Foo')].inner.variants[4]" $TupleField -// @count "$.index[*][?(@.name=='Foo')].inner.variants[*]" 5 +// @is "$.index[*][?(@.name=='Foo')].inner.enum.variants[0]" $Unit +// @is "$.index[*][?(@.name=='Foo')].inner.enum.variants[1]" $Named +// @is "$.index[*][?(@.name=='Foo')].inner.enum.variants[2]" $Tuple +// @is "$.index[*][?(@.name=='Foo')].inner.enum.variants[3]" $NamedField +// @is "$.index[*][?(@.name=='Foo')].inner.enum.variants[4]" $TupleField +// @count "$.index[*][?(@.name=='Foo')].inner.enum.variants[*]" 5 diff --git a/tests/rustdoc-json/enums/struct_field_hidden.rs b/tests/rustdoc-json/enums/struct_field_hidden.rs index de939cde2e7..2676c4e4157 100644 --- a/tests/rustdoc-json/enums/struct_field_hidden.rs +++ b/tests/rustdoc-json/enums/struct_field_hidden.rs @@ -9,8 +9,8 @@ pub enum Foo { // @set y = "$.index[*][?(@.name=='y')].id" y: i32, }, - // @is "$.index[*][?(@.name=='Variant')].inner.kind.struct.fields_stripped" true - // @is "$.index[*][?(@.name=='Variant')].inner.kind.struct.fields[0]" $b - // @is "$.index[*][?(@.name=='Variant')].inner.kind.struct.fields[1]" $y - // @count "$.index[*][?(@.name=='Variant')].inner.kind.struct.fields[*]" 2 + // @is "$.index[*][?(@.name=='Variant')].inner.variant.kind.struct.fields_stripped" true + // @is "$.index[*][?(@.name=='Variant')].inner.variant.kind.struct.fields[0]" $b + // @is "$.index[*][?(@.name=='Variant')].inner.variant.kind.struct.fields[1]" $y + // @count "$.index[*][?(@.name=='Variant')].inner.variant.kind.struct.fields[*]" 2 } diff --git a/tests/rustdoc-json/enums/tuple_fields_hidden.rs b/tests/rustdoc-json/enums/tuple_fields_hidden.rs index 70bfbb81826..3aeb0356420 100644 --- a/tests/rustdoc-json/enums/tuple_fields_hidden.rs +++ b/tests/rustdoc-json/enums/tuple_fields_hidden.rs @@ -14,47 +14,47 @@ // @set 3.3.1 = "$.index[*][?(@.docs=='3.3.1')].id" pub enum EnumWithStrippedTupleVariants { - // @count "$.index[*][?(@.name=='None')].inner.kind.tuple[*]" 0 + // @count "$.index[*][?(@.name=='None')].inner.variant.kind.tuple[*]" 0 None(), - // @count "$.index[*][?(@.name=='One')].inner.kind.tuple[*]" 1 - // @is "$.index[*][?(@.name=='One')].inner.kind.tuple[0]" $1.1.0 + // @count "$.index[*][?(@.name=='One')].inner.variant.kind.tuple[*]" 1 + // @is "$.index[*][?(@.name=='One')].inner.variant.kind.tuple[0]" $1.1.0 One(/** 1.1.0*/ bool), - // @count "$.index[*][?(@.name=='OneHidden')].inner.kind.tuple[*]" 1 - // @is "$.index[*][?(@.name=='OneHidden')].inner.kind.tuple[0]" null + // @count "$.index[*][?(@.name=='OneHidden')].inner.variant.kind.tuple[*]" 1 + // @is "$.index[*][?(@.name=='OneHidden')].inner.variant.kind.tuple[0]" null OneHidden(#[doc(hidden)] bool), - // @count "$.index[*][?(@.name=='Two')].inner.kind.tuple[*]" 2 - // @is "$.index[*][?(@.name=='Two')].inner.kind.tuple[0]" $2.1.0 - // @is "$.index[*][?(@.name=='Two')].inner.kind.tuple[1]" $2.1.1 + // @count "$.index[*][?(@.name=='Two')].inner.variant.kind.tuple[*]" 2 + // @is "$.index[*][?(@.name=='Two')].inner.variant.kind.tuple[0]" $2.1.0 + // @is "$.index[*][?(@.name=='Two')].inner.variant.kind.tuple[1]" $2.1.1 Two(/** 2.1.0*/ bool, /** 2.1.1*/ bool), - // @count "$.index[*][?(@.name=='TwoLeftHidden')].inner.kind.tuple[*]" 2 - // @is "$.index[*][?(@.name=='TwoLeftHidden')].inner.kind.tuple[0]" null - // @is "$.index[*][?(@.name=='TwoLeftHidden')].inner.kind.tuple[1]" $2.2.1 + // @count "$.index[*][?(@.name=='TwoLeftHidden')].inner.variant.kind.tuple[*]" 2 + // @is "$.index[*][?(@.name=='TwoLeftHidden')].inner.variant.kind.tuple[0]" null + // @is "$.index[*][?(@.name=='TwoLeftHidden')].inner.variant.kind.tuple[1]" $2.2.1 TwoLeftHidden(#[doc(hidden)] bool, /** 2.2.1*/ bool), - // @count "$.index[*][?(@.name=='TwoRightHidden')].inner.kind.tuple[*]" 2 - // @is "$.index[*][?(@.name=='TwoRightHidden')].inner.kind.tuple[0]" $2.3.0 - // @is "$.index[*][?(@.name=='TwoRightHidden')].inner.kind.tuple[1]" null + // @count "$.index[*][?(@.name=='TwoRightHidden')].inner.variant.kind.tuple[*]" 2 + // @is "$.index[*][?(@.name=='TwoRightHidden')].inner.variant.kind.tuple[0]" $2.3.0 + // @is "$.index[*][?(@.name=='TwoRightHidden')].inner.variant.kind.tuple[1]" null TwoRightHidden(/** 2.3.0*/ bool, #[doc(hidden)] bool), - // @count "$.index[*][?(@.name=='TwoBothHidden')].inner.kind.tuple[*]" 2 - // @is "$.index[*][?(@.name=='TwoBothHidden')].inner.kind.tuple[0]" null - // @is "$.index[*][?(@.name=='TwoBothHidden')].inner.kind.tuple[1]" null + // @count "$.index[*][?(@.name=='TwoBothHidden')].inner.variant.kind.tuple[*]" 2 + // @is "$.index[*][?(@.name=='TwoBothHidden')].inner.variant.kind.tuple[0]" null + // @is "$.index[*][?(@.name=='TwoBothHidden')].inner.variant.kind.tuple[1]" null TwoBothHidden(#[doc(hidden)] bool, #[doc(hidden)] bool), - // @count "$.index[*][?(@.name=='Three1')].inner.kind.tuple[*]" 3 - // @is "$.index[*][?(@.name=='Three1')].inner.kind.tuple[0]" null - // @is "$.index[*][?(@.name=='Three1')].inner.kind.tuple[1]" $3.1.1 - // @is "$.index[*][?(@.name=='Three1')].inner.kind.tuple[2]" $3.1.2 + // @count "$.index[*][?(@.name=='Three1')].inner.variant.kind.tuple[*]" 3 + // @is "$.index[*][?(@.name=='Three1')].inner.variant.kind.tuple[0]" null + // @is "$.index[*][?(@.name=='Three1')].inner.variant.kind.tuple[1]" $3.1.1 + // @is "$.index[*][?(@.name=='Three1')].inner.variant.kind.tuple[2]" $3.1.2 Three1(#[doc(hidden)] bool, /** 3.1.1*/ bool, /** 3.1.2*/ bool), - // @count "$.index[*][?(@.name=='Three2')].inner.kind.tuple[*]" 3 - // @is "$.index[*][?(@.name=='Three2')].inner.kind.tuple[0]" $3.2.0 - // @is "$.index[*][?(@.name=='Three2')].inner.kind.tuple[1]" null - // @is "$.index[*][?(@.name=='Three2')].inner.kind.tuple[2]" $3.2.2 + // @count "$.index[*][?(@.name=='Three2')].inner.variant.kind.tuple[*]" 3 + // @is "$.index[*][?(@.name=='Three2')].inner.variant.kind.tuple[0]" $3.2.0 + // @is "$.index[*][?(@.name=='Three2')].inner.variant.kind.tuple[1]" null + // @is "$.index[*][?(@.name=='Three2')].inner.variant.kind.tuple[2]" $3.2.2 Three2(/** 3.2.0*/ bool, #[doc(hidden)] bool, /** 3.2.2*/ bool), - // @count "$.index[*][?(@.name=='Three3')].inner.kind.tuple[*]" 3 - // @is "$.index[*][?(@.name=='Three3')].inner.kind.tuple[0]" $3.3.0 - // @is "$.index[*][?(@.name=='Three3')].inner.kind.tuple[1]" $3.3.1 - // @is "$.index[*][?(@.name=='Three3')].inner.kind.tuple[2]" null + // @count "$.index[*][?(@.name=='Three3')].inner.variant.kind.tuple[*]" 3 + // @is "$.index[*][?(@.name=='Three3')].inner.variant.kind.tuple[0]" $3.3.0 + // @is "$.index[*][?(@.name=='Three3')].inner.variant.kind.tuple[1]" $3.3.1 + // @is "$.index[*][?(@.name=='Three3')].inner.variant.kind.tuple[2]" null Three3(/** 3.3.0*/ bool, /** 3.3.1*/ bool, #[doc(hidden)] bool), } @@ -70,14 +70,14 @@ pub enum EnumWithStrippedTupleVariants { // @is "$.index[*][?(@.docs=='3.3.0')].name" '"0"' // @is "$.index[*][?(@.docs=='3.3.1')].name" '"1"' -// @is "$.index[*][?(@.docs=='1.1.0')].inner" '{"kind": "primitive", "inner": "bool"}' -// @is "$.index[*][?(@.docs=='2.1.0')].inner" '{"kind": "primitive", "inner": "bool"}' -// @is "$.index[*][?(@.docs=='2.1.1')].inner" '{"kind": "primitive", "inner": "bool"}' -// @is "$.index[*][?(@.docs=='2.2.1')].inner" '{"kind": "primitive", "inner": "bool"}' -// @is "$.index[*][?(@.docs=='2.3.0')].inner" '{"kind": "primitive", "inner": "bool"}' -// @is "$.index[*][?(@.docs=='3.1.1')].inner" '{"kind": "primitive", "inner": "bool"}' -// @is "$.index[*][?(@.docs=='3.1.2')].inner" '{"kind": "primitive", "inner": "bool"}' -// @is "$.index[*][?(@.docs=='3.2.0')].inner" '{"kind": "primitive", "inner": "bool"}' -// @is "$.index[*][?(@.docs=='3.2.2')].inner" '{"kind": "primitive", "inner": "bool"}' -// @is "$.index[*][?(@.docs=='3.3.0')].inner" '{"kind": "primitive", "inner": "bool"}' -// @is "$.index[*][?(@.docs=='3.3.1')].inner" '{"kind": "primitive", "inner": "bool"}' +// @is "$.index[*][?(@.docs=='1.1.0')].inner.struct_field" '{"primitive": "bool"}' +// @is "$.index[*][?(@.docs=='2.1.0')].inner.struct_field" '{"primitive": "bool"}' +// @is "$.index[*][?(@.docs=='2.1.1')].inner.struct_field" '{"primitive": "bool"}' +// @is "$.index[*][?(@.docs=='2.2.1')].inner.struct_field" '{"primitive": "bool"}' +// @is "$.index[*][?(@.docs=='2.3.0')].inner.struct_field" '{"primitive": "bool"}' +// @is "$.index[*][?(@.docs=='3.1.1')].inner.struct_field" '{"primitive": "bool"}' +// @is "$.index[*][?(@.docs=='3.1.2')].inner.struct_field" '{"primitive": "bool"}' +// @is "$.index[*][?(@.docs=='3.2.0')].inner.struct_field" '{"primitive": "bool"}' +// @is "$.index[*][?(@.docs=='3.2.2')].inner.struct_field" '{"primitive": "bool"}' +// @is "$.index[*][?(@.docs=='3.3.0')].inner.struct_field" '{"primitive": "bool"}' +// @is "$.index[*][?(@.docs=='3.3.1')].inner.struct_field" '{"primitive": "bool"}' diff --git a/tests/rustdoc-json/enums/use_glob.rs b/tests/rustdoc-json/enums/use_glob.rs index 62b8b832afd..49990ec5331 100644 --- a/tests/rustdoc-json/enums/use_glob.rs +++ b/tests/rustdoc-json/enums/use_glob.rs @@ -10,9 +10,9 @@ pub enum Color { Blue, } -// @set use_Color = "$.index[*][?(@.kind == 'import')].id" -// @is "$.index[*][?(@.kind == 'import')].inner.id" $Color -// @is "$.index[*][?(@.kind == 'import')].inner.glob" true +// @set use_Color = "$.index[*][?(@.inner.import)].id" +// @is "$.index[*][?(@.inner.import)].inner.import.id" $Color +// @is "$.index[*][?(@.inner.import)].inner.import.glob" true pub use Color::*; -// @ismany "$.index[*][?(@.name == 'use_glob')].inner.items[*]" $Color $use_Color +// @ismany "$.index[*][?(@.name == 'use_glob')].inner.module.items[*]" $Color $use_Color diff --git a/tests/rustdoc-json/enums/use_variant.rs b/tests/rustdoc-json/enums/use_variant.rs index 5f0d2b9b1ec..8190e1cbe81 100644 --- a/tests/rustdoc-json/enums/use_variant.rs +++ b/tests/rustdoc-json/enums/use_variant.rs @@ -6,10 +6,10 @@ pub enum AlwaysNone { // @set None = "$.index[*][?(@.name == 'None')].id" None, } -// @is "$.index[*][?(@.name == 'AlwaysNone')].inner.variants[*]" $None +// @is "$.index[*][?(@.name == 'AlwaysNone')].inner.enum.variants[*]" $None -// @set use_None = "$.index[*][?(@.kind == 'import')].id" -// @is "$.index[*][?(@.kind == 'import')].inner.id" $None +// @set use_None = "$.index[*][?(@.inner.import)].id" +// @is "$.index[*][?(@.inner.import)].inner.import.id" $None pub use AlwaysNone::None; -// @ismany "$.index[*][?(@.name == 'use_variant')].inner.items[*]" $AlwaysNone $use_None +// @ismany "$.index[*][?(@.name == 'use_variant')].inner.module.items[*]" $AlwaysNone $use_None diff --git a/tests/rustdoc-json/enums/use_variant_foreign.rs b/tests/rustdoc-json/enums/use_variant_foreign.rs index 11bb6ce1f3a..a79e899d85f 100644 --- a/tests/rustdoc-json/enums/use_variant_foreign.rs +++ b/tests/rustdoc-json/enums/use_variant_foreign.rs @@ -2,7 +2,7 @@ extern crate color; -// @is "$.index[*][?(@.inner.name == 'Red')].kind" '"import"' +// @has "$.index[*].inner.import[?(@.name == 'Red')]" pub use color::Color::Red; // @!has "$.index[*][?(@.name == 'Red')]" diff --git a/tests/rustdoc-json/enums/variant_struct.rs b/tests/rustdoc-json/enums/variant_struct.rs index bc870c502a0..fe40f1a5d5d 100644 --- a/tests/rustdoc-json/enums/variant_struct.rs +++ b/tests/rustdoc-json/enums/variant_struct.rs @@ -1,10 +1,10 @@ // @is "$.index[*][?(@.name=='EnumStruct')].visibility" \"public\" -// @is "$.index[*][?(@.name=='EnumStruct')].kind" \"enum\" +// @has "$.index[*][?(@.name=='EnumStruct')].inner.enum" pub enum EnumStruct { - // @is "$.index[*][?(@.name=='x')].kind" \"struct_field\" + // @has "$.index[*][?(@.name=='x')].inner.struct_field" // @set x = "$.index[*][?(@.name=='x')].id" - // @is "$.index[*][?(@.name=='y')].kind" \"struct_field\" + // @has "$.index[*][?(@.name=='y')].inner.struct_field" // @set y = "$.index[*][?(@.name=='y')].id" - // @ismany "$.index[*][?(@.name=='VariantS')].inner.kind.struct.fields[*]" $x $y + // @ismany "$.index[*][?(@.name=='VariantS')].inner.variant.kind.struct.fields[*]" $x $y VariantS { x: u32, y: String }, } diff --git a/tests/rustdoc-json/enums/variant_tuple_struct.rs b/tests/rustdoc-json/enums/variant_tuple_struct.rs index d1207bbfb18..358fc1079cb 100644 --- a/tests/rustdoc-json/enums/variant_tuple_struct.rs +++ b/tests/rustdoc-json/enums/variant_tuple_struct.rs @@ -1,10 +1,10 @@ // @is "$.index[*][?(@.name=='EnumTupleStruct')].visibility" \"public\" -// @is "$.index[*][?(@.name=='EnumTupleStruct')].kind" \"enum\" +// @has "$.index[*][?(@.name=='EnumTupleStruct')].inner.enum" pub enum EnumTupleStruct { - // @is "$.index[*][?(@.name=='0')].kind" \"struct_field\" + // @has "$.index[*][?(@.name=='0')].inner.struct_field" // @set f0 = "$.index[*][?(@.name=='0')].id" - // @is "$.index[*][?(@.name=='1')].kind" \"struct_field\" + // @has "$.index[*][?(@.name=='1')].inner.struct_field" // @set f1 = "$.index[*][?(@.name=='1')].id" - // @ismany "$.index[*][?(@.name=='VariantA')].inner.kind.tuple[*]" $f0 $f1 + // @ismany "$.index[*][?(@.name=='VariantA')].inner.variant.kind.tuple[*]" $f0 $f1 VariantA(u32, String), } diff --git a/tests/rustdoc-json/fn_pointer/abi.rs b/tests/rustdoc-json/fn_pointer/abi.rs index 6a30acc2cc3..77c0e82330f 100644 --- a/tests/rustdoc-json/fn_pointer/abi.rs +++ b/tests/rustdoc-json/fn_pointer/abi.rs @@ -2,23 +2,23 @@ #![feature(abi_vectorcall)] -// @is "$.index[*][?(@.name=='AbiRust')].inner.type.inner.header.abi" \"Rust\" +// @is "$.index[*][?(@.name=='AbiRust')].inner.typedef.type.function_pointer.header.abi" \"Rust\" pub type AbiRust = fn(); -// @is "$.index[*][?(@.name=='AbiC')].inner.type.inner.header.abi" '{"C": {"unwind": false}}' +// @is "$.index[*][?(@.name=='AbiC')].inner.typedef.type.function_pointer.header.abi" '{"C": {"unwind": false}}' pub type AbiC = extern "C" fn(); -// @is "$.index[*][?(@.name=='AbiSystem')].inner.type.inner.header.abi" '{"System": {"unwind": false}}' +// @is "$.index[*][?(@.name=='AbiSystem')].inner.typedef.type.function_pointer.header.abi" '{"System": {"unwind": false}}' pub type AbiSystem = extern "system" fn(); -// @is "$.index[*][?(@.name=='AbiCUnwind')].inner.type.inner.header.abi" '{"C": {"unwind": true}}' +// @is "$.index[*][?(@.name=='AbiCUnwind')].inner.typedef.type.function_pointer.header.abi" '{"C": {"unwind": true}}' pub type AbiCUnwind = extern "C-unwind" fn(); -// @is "$.index[*][?(@.name=='AbiSystemUnwind')].inner.type.inner.header.abi" '{"System": {"unwind": true}}' +// @is "$.index[*][?(@.name=='AbiSystemUnwind')].inner.typedef.type.function_pointer.header.abi" '{"System": {"unwind": true}}' pub type AbiSystemUnwind = extern "system-unwind" fn(); -// @is "$.index[*][?(@.name=='AbiVecorcall')].inner.type.inner.header.abi.Other" '"\"vectorcall\""' +// @is "$.index[*][?(@.name=='AbiVecorcall')].inner.typedef.type.function_pointer.header.abi.Other" '"\"vectorcall\""' pub type AbiVecorcall = extern "vectorcall" fn(); -// @is "$.index[*][?(@.name=='AbiVecorcallUnwind')].inner.type.inner.header.abi.Other" '"\"vectorcall-unwind\""' +// @is "$.index[*][?(@.name=='AbiVecorcallUnwind')].inner.typedef.type.function_pointer.header.abi.Other" '"\"vectorcall-unwind\""' pub type AbiVecorcallUnwind = extern "vectorcall-unwind" fn(); diff --git a/tests/rustdoc-json/fn_pointer/generics.rs b/tests/rustdoc-json/fn_pointer/generics.rs index a93b01ac2c4..48672f12da1 100644 --- a/tests/rustdoc-json/fn_pointer/generics.rs +++ b/tests/rustdoc-json/fn_pointer/generics.rs @@ -3,12 +3,11 @@ #![feature(no_core)] #![no_core] -// @count "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type.inner.decl.inputs[*]" 1 -// @is "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type.inner.decl.inputs[0][0]" '"val"' -// @is "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type.inner.decl.inputs[0][1].kind" '"borrowed_ref"' -// @is "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type.inner.decl.inputs[0][1].inner.lifetime" \"\'c\" -// @is "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type.inner.decl.output" '{ "kind": "primitive", "inner": "i32" }' -// @count "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type.inner.generic_params[*]" 1 -// @is "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type.inner.generic_params[0].name" \"\'c\" -// @is "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type.inner.generic_params[0].kind" '{ "lifetime": { "outlives": [] } }' +// @count "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.typedef.type.function_pointer.decl.inputs[*]" 1 +// @is "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.typedef.type.function_pointer.decl.inputs[0][0]" '"val"' +// @is "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.typedef.type.function_pointer.decl.inputs[0][1].borrowed_ref.lifetime" \"\'c\" +// @is "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.typedef.type.function_pointer.decl.output.primitive" \"i32\" +// @count "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.typedef.type.function_pointer.generic_params[*]" 1 +// @is "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.typedef.type.function_pointer.generic_params[0].name" \"\'c\" +// @is "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.typedef.type.function_pointer.generic_params[0].kind" '{ "lifetime": { "outlives": [] } }' pub type WithHigherRankTraitBounds = for<'c> fn(val: &'c i32) -> i32; diff --git a/tests/rustdoc-json/fn_pointer/qualifiers.rs b/tests/rustdoc-json/fn_pointer/qualifiers.rs index bd65bb3eefe..0ab776c21ed 100644 --- a/tests/rustdoc-json/fn_pointer/qualifiers.rs +++ b/tests/rustdoc-json/fn_pointer/qualifiers.rs @@ -1,9 +1,11 @@ -// @is "$.index[*][?(@.name=='FnPointer')].inner.type.inner.header.unsafe" false -// @is "$.index[*][?(@.name=='FnPointer')].inner.type.inner.header.const" false -// @is "$.index[*][?(@.name=='FnPointer')].inner.type.inner.header.async" false +// ignore-tidy-linelength + +// @is "$.index[*][?(@.name=='FnPointer')].inner.typedef.type.function_pointer.header.unsafe" false +// @is "$.index[*][?(@.name=='FnPointer')].inner.typedef.type.function_pointer.header.const" false +// @is "$.index[*][?(@.name=='FnPointer')].inner.typedef.type.function_pointer.header.async" false pub type FnPointer = fn(); -// @is "$.index[*][?(@.name=='UnsafePointer')].inner.type.inner.header.unsafe" true -// @is "$.index[*][?(@.name=='UnsafePointer')].inner.type.inner.header.const" false -// @is "$.index[*][?(@.name=='UnsafePointer')].inner.type.inner.header.async" false +// @is "$.index[*][?(@.name=='UnsafePointer')].inner.typedef.type.function_pointer.header.unsafe" true +// @is "$.index[*][?(@.name=='UnsafePointer')].inner.typedef.type.function_pointer.header.const" false +// @is "$.index[*][?(@.name=='UnsafePointer')].inner.typedef.type.function_pointer.header.async" false pub type UnsafePointer = unsafe fn(); diff --git a/tests/rustdoc-json/fns/abi.rs b/tests/rustdoc-json/fns/abi.rs index 7a5dbee730c..d7b98b5924b 100644 --- a/tests/rustdoc-json/fns/abi.rs +++ b/tests/rustdoc-json/fns/abi.rs @@ -2,23 +2,23 @@ #![feature(abi_vectorcall)] -// @is "$.index[*][?(@.name=='abi_rust')].inner.header.abi" \"Rust\" +// @is "$.index[*][?(@.name=='abi_rust')].inner.function.header.abi" \"Rust\" pub fn abi_rust() {} -// @is "$.index[*][?(@.name=='abi_c')].inner.header.abi" '{"C": {"unwind": false}}' +// @is "$.index[*][?(@.name=='abi_c')].inner.function.header.abi" '{"C": {"unwind": false}}' pub extern "C" fn abi_c() {} -// @is "$.index[*][?(@.name=='abi_system')].inner.header.abi" '{"System": {"unwind": false}}' +// @is "$.index[*][?(@.name=='abi_system')].inner.function.header.abi" '{"System": {"unwind": false}}' pub extern "system" fn abi_system() {} -// @is "$.index[*][?(@.name=='abi_c_unwind')].inner.header.abi" '{"C": {"unwind": true}}' +// @is "$.index[*][?(@.name=='abi_c_unwind')].inner.function.header.abi" '{"C": {"unwind": true}}' pub extern "C-unwind" fn abi_c_unwind() {} -// @is "$.index[*][?(@.name=='abi_system_unwind')].inner.header.abi" '{"System": {"unwind": true}}' +// @is "$.index[*][?(@.name=='abi_system_unwind')].inner.function.header.abi" '{"System": {"unwind": true}}' pub extern "system-unwind" fn abi_system_unwind() {} -// @is "$.index[*][?(@.name=='abi_vectorcall')].inner.header.abi.Other" '"\"vectorcall\""' +// @is "$.index[*][?(@.name=='abi_vectorcall')].inner.function.header.abi.Other" '"\"vectorcall\""' pub extern "vectorcall" fn abi_vectorcall() {} -// @is "$.index[*][?(@.name=='abi_vectorcall_unwind')].inner.header.abi.Other" '"\"vectorcall-unwind\""' +// @is "$.index[*][?(@.name=='abi_vectorcall_unwind')].inner.function.header.abi.Other" '"\"vectorcall-unwind\""' pub extern "vectorcall-unwind" fn abi_vectorcall_unwind() {} diff --git a/tests/rustdoc-json/fns/async_return.rs b/tests/rustdoc-json/fns/async_return.rs index b89781ca92d..8192f2e6ae6 100644 --- a/tests/rustdoc-json/fns/async_return.rs +++ b/tests/rustdoc-json/fns/async_return.rs @@ -5,32 +5,30 @@ use std::future::Future; -// @is "$.index[*][?(@.name=='get_int')].inner.decl.output" '{"inner": "i32", "kind": "primitive"}' -// @is "$.index[*][?(@.name=='get_int')].inner.header.async" false +// @is "$.index[*][?(@.name=='get_int')].inner.function.decl.output.primitive" \"i32\" +// @is "$.index[*][?(@.name=='get_int')].inner.function.header.async" false pub fn get_int() -> i32 { 42 } -// @is "$.index[*][?(@.name=='get_int_async')].inner.decl.output" '{"inner": "i32", "kind": "primitive"}' -// @is "$.index[*][?(@.name=='get_int_async')].inner.header.async" true +// @is "$.index[*][?(@.name=='get_int_async')].inner.function.decl.output.primitive" \"i32\" +// @is "$.index[*][?(@.name=='get_int_async')].inner.function.header.async" true pub async fn get_int_async() -> i32 { 42 } -// @is "$.index[*][?(@.name=='get_int_future')].inner.decl.output.kind" '"impl_trait"' -// @is "$.index[*][?(@.name=='get_int_future')].inner.decl.output.inner[0].trait_bound.trait.name" '"Future"' -// @is "$.index[*][?(@.name=='get_int_future')].inner.decl.output.inner[0].trait_bound.trait.args.angle_bracketed.bindings[0].name" '"Output"' -// @is "$.index[*][?(@.name=='get_int_future')].inner.decl.output.inner[0].trait_bound.trait.args.angle_bracketed.bindings[0].binding.equality.type" '{"inner": "i32", "kind": "primitive"}' -// @is "$.index[*][?(@.name=='get_int_future')].inner.header.async" false +// @is "$.index[*][?(@.name=='get_int_future')].inner.function.decl.output.impl_trait[0].trait_bound.trait.name" '"Future"' +// @is "$.index[*][?(@.name=='get_int_future')].inner.function.decl.output.impl_trait[0].trait_bound.trait.args.angle_bracketed.bindings[0].name" '"Output"' +// @is "$.index[*][?(@.name=='get_int_future')].inner.function.decl.output.impl_trait[0].trait_bound.trait.args.angle_bracketed.bindings[0].binding.equality.type.primitive" \"i32\" +// @is "$.index[*][?(@.name=='get_int_future')].inner.function.header.async" false pub fn get_int_future() -> impl Future<Output = i32> { async { 42 } } -// @is "$.index[*][?(@.name=='get_int_future_async')].inner.decl.output.kind" '"impl_trait"' -// @is "$.index[*][?(@.name=='get_int_future_async')].inner.decl.output.inner[0].trait_bound.trait.name" '"Future"' -// @is "$.index[*][?(@.name=='get_int_future_async')].inner.decl.output.inner[0].trait_bound.trait.args.angle_bracketed.bindings[0].name" '"Output"' -// @is "$.index[*][?(@.name=='get_int_future_async')].inner.decl.output.inner[0].trait_bound.trait.args.angle_bracketed.bindings[0].binding.equality.type" '{"inner": "i32", "kind": "primitive"}' -// @is "$.index[*][?(@.name=='get_int_future_async')].inner.header.async" true +// @is "$.index[*][?(@.name=='get_int_future_async')].inner.function.decl.output.impl_trait[0].trait_bound.trait.name" '"Future"' +// @is "$.index[*][?(@.name=='get_int_future_async')].inner.function.decl.output.impl_trait[0].trait_bound.trait.args.angle_bracketed.bindings[0].name" '"Output"' +// @is "$.index[*][?(@.name=='get_int_future_async')].inner.function.decl.output.impl_trait[0].trait_bound.trait.args.angle_bracketed.bindings[0].binding.equality.type.primitive" \"i32\" +// @is "$.index[*][?(@.name=='get_int_future_async')].inner.function.header.async" true pub async fn get_int_future_async() -> impl Future<Output = i32> { async { 42 } } diff --git a/tests/rustdoc-json/fns/extern_c_variadic.rs b/tests/rustdoc-json/fns/extern_c_variadic.rs index 33bebbab5e0..d6ea343b991 100644 --- a/tests/rustdoc-json/fns/extern_c_variadic.rs +++ b/tests/rustdoc-json/fns/extern_c_variadic.rs @@ -2,8 +2,8 @@ #![no_core] extern "C" { - // @is "$.index[*][?(@.name == 'not_variadic')].inner.decl.c_variadic" false + // @is "$.index[*][?(@.name == 'not_variadic')].inner.function.decl.c_variadic" false pub fn not_variadic(_: i32); - // @is "$.index[*][?(@.name == 'variadic')].inner.decl.c_variadic" true + // @is "$.index[*][?(@.name == 'variadic')].inner.function.decl.c_variadic" true pub fn variadic(_: i32, ...); } diff --git a/tests/rustdoc-json/fns/generic_args.rs b/tests/rustdoc-json/fns/generic_args.rs index eec295efec0..539d17f831e 100644 --- a/tests/rustdoc-json/fns/generic_args.rs +++ b/tests/rustdoc-json/fns/generic_args.rs @@ -9,59 +9,55 @@ pub trait Foo {} // @set generic_foo = "$.index[*][?(@.name=='GenericFoo')].id" pub trait GenericFoo<'a> {} -// @is "$.index[*][?(@.name=='generics')].inner.generics.where_predicates" "[]" -// @count "$.index[*][?(@.name=='generics')].inner.generics.params[*]" 1 -// @is "$.index[*][?(@.name=='generics')].inner.generics.params[0].name" '"F"' -// @is "$.index[*][?(@.name=='generics')].inner.generics.params[0].kind.type.default" 'null' -// @count "$.index[*][?(@.name=='generics')].inner.generics.params[0].kind.type.bounds[*]" 1 -// @is "$.index[*][?(@.name=='generics')].inner.generics.params[0].kind.type.bounds[0].trait_bound.trait.id" '$foo' -// @count "$.index[*][?(@.name=='generics')].inner.decl.inputs[*]" 1 -// @is "$.index[*][?(@.name=='generics')].inner.decl.inputs[0][0]" '"f"' -// @is "$.index[*][?(@.name=='generics')].inner.decl.inputs[0][1].kind" '"generic"' -// @is "$.index[*][?(@.name=='generics')].inner.decl.inputs[0][1].inner" '"F"' +// @is "$.index[*][?(@.name=='generics')].inner.function.generics.where_predicates" "[]" +// @count "$.index[*][?(@.name=='generics')].inner.function.generics.params[*]" 1 +// @is "$.index[*][?(@.name=='generics')].inner.function.generics.params[0].name" '"F"' +// @is "$.index[*][?(@.name=='generics')].inner.function.generics.params[0].kind.type.default" 'null' +// @count "$.index[*][?(@.name=='generics')].inner.function.generics.params[0].kind.type.bounds[*]" 1 +// @is "$.index[*][?(@.name=='generics')].inner.function.generics.params[0].kind.type.bounds[0].trait_bound.trait.id" '$foo' +// @count "$.index[*][?(@.name=='generics')].inner.function.decl.inputs[*]" 1 +// @is "$.index[*][?(@.name=='generics')].inner.function.decl.inputs[0][0]" '"f"' +// @is "$.index[*][?(@.name=='generics')].inner.function.decl.inputs[0][1].generic" '"F"' pub fn generics<F: Foo>(f: F) {} -// @is "$.index[*][?(@.name=='impl_trait')].inner.generics.where_predicates" "[]" -// @count "$.index[*][?(@.name=='impl_trait')].inner.generics.params[*]" 1 -// @is "$.index[*][?(@.name=='impl_trait')].inner.generics.params[0].name" '"impl Foo"' -// @is "$.index[*][?(@.name=='impl_trait')].inner.generics.params[0].kind.type.bounds[0].trait_bound.trait.id" $foo -// @count "$.index[*][?(@.name=='impl_trait')].inner.decl.inputs[*]" 1 -// @is "$.index[*][?(@.name=='impl_trait')].inner.decl.inputs[0][0]" '"f"' -// @is "$.index[*][?(@.name=='impl_trait')].inner.decl.inputs[0][1].kind" '"impl_trait"' -// @count "$.index[*][?(@.name=='impl_trait')].inner.decl.inputs[0][1].inner[*]" 1 -// @is "$.index[*][?(@.name=='impl_trait')].inner.decl.inputs[0][1].inner[0].trait_bound.trait.id" $foo +// @is "$.index[*][?(@.name=='impl_trait')].inner.function.generics.where_predicates" "[]" +// @count "$.index[*][?(@.name=='impl_trait')].inner.function.generics.params[*]" 1 +// @is "$.index[*][?(@.name=='impl_trait')].inner.function.generics.params[0].name" '"impl Foo"' +// @is "$.index[*][?(@.name=='impl_trait')].inner.function.generics.params[0].kind.type.bounds[0].trait_bound.trait.id" $foo +// @count "$.index[*][?(@.name=='impl_trait')].inner.function.decl.inputs[*]" 1 +// @is "$.index[*][?(@.name=='impl_trait')].inner.function.decl.inputs[0][0]" '"f"' +// @count "$.index[*][?(@.name=='impl_trait')].inner.function.decl.inputs[0][1].impl_trait[*]" 1 +// @is "$.index[*][?(@.name=='impl_trait')].inner.function.decl.inputs[0][1].impl_trait[0].trait_bound.trait.id" $foo pub fn impl_trait(f: impl Foo) {} -// @count "$.index[*][?(@.name=='where_clase')].inner.generics.params[*]" 3 -// @is "$.index[*][?(@.name=='where_clase')].inner.generics.params[0].name" '"F"' -// @is "$.index[*][?(@.name=='where_clase')].inner.generics.params[0].kind" '{"type": {"bounds": [], "default": null, "synthetic": false}}' -// @count "$.index[*][?(@.name=='where_clase')].inner.decl.inputs[*]" 3 -// @is "$.index[*][?(@.name=='where_clase')].inner.decl.inputs[0][0]" '"f"' -// @is "$.index[*][?(@.name=='where_clase')].inner.decl.inputs[0][1].kind" '"generic"' -// @is "$.index[*][?(@.name=='where_clase')].inner.decl.inputs[0][1].inner" '"F"' -// @count "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[*]" 3 +// @count "$.index[*][?(@.name=='where_clase')].inner.function.generics.params[*]" 3 +// @is "$.index[*][?(@.name=='where_clase')].inner.function.generics.params[0].name" '"F"' +// @is "$.index[*][?(@.name=='where_clase')].inner.function.generics.params[0].kind" '{"type": {"bounds": [], "default": null, "synthetic": false}}' +// @count "$.index[*][?(@.name=='where_clase')].inner.function.decl.inputs[*]" 3 +// @is "$.index[*][?(@.name=='where_clase')].inner.function.decl.inputs[0][0]" '"f"' +// @is "$.index[*][?(@.name=='where_clase')].inner.function.decl.inputs[0][1].generic" '"F"' +// @count "$.index[*][?(@.name=='where_clase')].inner.function.generics.where_predicates[*]" 3 -// @is "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[0].bound_predicate.type" '{"inner": "F", "kind": "generic"}' -// @count "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[0].bound_predicate.bounds[*]" 1 -// @is "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[0].bound_predicate.bounds[0].trait_bound.trait.id" $foo +// @is "$.index[*][?(@.name=='where_clase')].inner.function.generics.where_predicates[0].bound_predicate.type.generic" \"F\" +// @count "$.index[*][?(@.name=='where_clase')].inner.function.generics.where_predicates[0].bound_predicate.bounds[*]" 1 +// @is "$.index[*][?(@.name=='where_clase')].inner.function.generics.where_predicates[0].bound_predicate.bounds[0].trait_bound.trait.id" $foo -// @is "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[1].bound_predicate.type" '{"inner": "G", "kind": "generic"}' -// @count "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[1].bound_predicate.bounds[*]" 1 -// @is "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[1].bound_predicate.bounds[0].trait_bound.trait.id" $generic_foo -// @count "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[1].bound_predicate.bounds[0].trait_bound.generic_params[*]" 1 -// @is "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[1].bound_predicate.bounds[0].trait_bound.generic_params[0].name" \"\'a\" -// @is "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[1].bound_predicate.bounds[0].trait_bound.generic_params[0].kind" '{ "lifetime": { "outlives": [] } }' -// @is "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[1].bound_predicate.generic_params" "[]" +// @is "$.index[*][?(@.name=='where_clase')].inner.function.generics.where_predicates[1].bound_predicate.type.generic" \"G\" +// @count "$.index[*][?(@.name=='where_clase')].inner.function.generics.where_predicates[1].bound_predicate.bounds[*]" 1 +// @is "$.index[*][?(@.name=='where_clase')].inner.function.generics.where_predicates[1].bound_predicate.bounds[0].trait_bound.trait.id" $generic_foo +// @count "$.index[*][?(@.name=='where_clase')].inner.function.generics.where_predicates[1].bound_predicate.bounds[0].trait_bound.generic_params[*]" 1 +// @is "$.index[*][?(@.name=='where_clase')].inner.function.generics.where_predicates[1].bound_predicate.bounds[0].trait_bound.generic_params[0].name" \"\'a\" +// @is "$.index[*][?(@.name=='where_clase')].inner.function.generics.where_predicates[1].bound_predicate.bounds[0].trait_bound.generic_params[0].kind.lifetime.outlives" "[]" +// @is "$.index[*][?(@.name=='where_clase')].inner.function.generics.where_predicates[1].bound_predicate.generic_params" "[]" -// @is "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[2].bound_predicate.type.kind" '"borrowed_ref"' -// @is "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[2].bound_predicate.type.inner.lifetime" \"\'b\" -// @is "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[2].bound_predicate.type.inner.type" '{"inner": "H", "kind": "generic"}' -// @count "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[2].bound_predicate.bounds[*]" 1 -// @is "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[2].bound_predicate.bounds[0].trait_bound.trait.id" $foo -// @is "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[2].bound_predicate.bounds[0].trait_bound.generic_params" "[]" -// @count "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[2].bound_predicate.generic_params[*]" 1 -// @is "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[2].bound_predicate.generic_params[0].name" \"\'b\" -// @is "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[2].bound_predicate.generic_params[0].kind" '{ "lifetime": { "outlives": [] } }' +// @is "$.index[*][?(@.name=='where_clase')].inner.function.generics.where_predicates[2].bound_predicate.type.borrowed_ref.lifetime" \"\'b\" +// @is "$.index[*][?(@.name=='where_clase')].inner.function.generics.where_predicates[2].bound_predicate.type.borrowed_ref.type.generic" \"H\" +// @count "$.index[*][?(@.name=='where_clase')].inner.function.generics.where_predicates[2].bound_predicate.bounds[*]" 1 +// @is "$.index[*][?(@.name=='where_clase')].inner.function.generics.where_predicates[2].bound_predicate.bounds[0].trait_bound.trait.id" $foo +// @is "$.index[*][?(@.name=='where_clase')].inner.function.generics.where_predicates[2].bound_predicate.bounds[0].trait_bound.generic_params" "[]" +// @count "$.index[*][?(@.name=='where_clase')].inner.function.generics.where_predicates[2].bound_predicate.generic_params[*]" 1 +// @is "$.index[*][?(@.name=='where_clase')].inner.function.generics.where_predicates[2].bound_predicate.generic_params[0].name" \"\'b\" +// @is "$.index[*][?(@.name=='where_clase')].inner.function.generics.where_predicates[2].bound_predicate.generic_params[0].kind.lifetime.outlives" "[]" pub fn where_clase<F, G, H>(f: F, g: G, h: H) where F: Foo, diff --git a/tests/rustdoc-json/fns/generic_returns.rs b/tests/rustdoc-json/fns/generic_returns.rs index a9bc2d5d727..27d842066f4 100644 --- a/tests/rustdoc-json/fns/generic_returns.rs +++ b/tests/rustdoc-json/fns/generic_returns.rs @@ -3,15 +3,14 @@ #![feature(no_core)] #![no_core] -// @count "$.index[*][?(@.name=='generic_returns')].inner.items[*]" 2 +// @count "$.index[*][?(@.name=='generic_returns')].inner.module.items[*]" 2 // @set foo = "$.index[*][?(@.name=='Foo')].id" pub trait Foo {} -// @is "$.index[*][?(@.name=='get_foo')].inner.decl.inputs" [] -// @is "$.index[*][?(@.name=='get_foo')].inner.decl.output.kind" '"impl_trait"' -// @count "$.index[*][?(@.name=='get_foo')].inner.decl.output.inner[*]" 1 -// @is "$.index[*][?(@.name=='get_foo')].inner.decl.output.inner[0].trait_bound.trait.id" $foo +// @is "$.index[*][?(@.name=='get_foo')].inner.function.decl.inputs" [] +// @count "$.index[*][?(@.name=='get_foo')].inner.function.decl.output.impl_trait[*]" 1 +// @is "$.index[*][?(@.name=='get_foo')].inner.function.decl.output.impl_trait[0].trait_bound.trait.id" $foo pub fn get_foo() -> impl Foo { Fooer {} } diff --git a/tests/rustdoc-json/fns/generics.rs b/tests/rustdoc-json/fns/generics.rs index 7b70ff1df6b..5239a6f21f5 100644 --- a/tests/rustdoc-json/fns/generics.rs +++ b/tests/rustdoc-json/fns/generics.rs @@ -6,21 +6,20 @@ // @set wham_id = "$.index[*][?(@.name=='Wham')].id" pub trait Wham {} -// @is "$.index[*][?(@.name=='one_generic_param_fn')].inner.generics.where_predicates" [] -// @count "$.index[*][?(@.name=='one_generic_param_fn')].inner.generics.params[*]" 1 -// @is "$.index[*][?(@.name=='one_generic_param_fn')].inner.generics.params[0].name" '"T"' -// @is "$.index[*][?(@.name=='one_generic_param_fn')].inner.generics.params[0].kind.type.synthetic" false -// @is "$.index[*][?(@.name=='one_generic_param_fn')].inner.generics.params[0].kind.type.bounds[0].trait_bound.trait.id" $wham_id -// @is "$.index[*][?(@.name=='one_generic_param_fn')].inner.decl.inputs" '[["w", {"inner": "T", "kind": "generic"}]]' +// @is "$.index[*][?(@.name=='one_generic_param_fn')].inner.function.generics.where_predicates" [] +// @count "$.index[*][?(@.name=='one_generic_param_fn')].inner.function.generics.params[*]" 1 +// @is "$.index[*][?(@.name=='one_generic_param_fn')].inner.function.generics.params[0].name" '"T"' +// @is "$.index[*][?(@.name=='one_generic_param_fn')].inner.function.generics.params[0].kind.type.synthetic" false +// @is "$.index[*][?(@.name=='one_generic_param_fn')].inner.function.generics.params[0].kind.type.bounds[0].trait_bound.trait.id" $wham_id +// @is "$.index[*][?(@.name=='one_generic_param_fn')].inner.function.decl.inputs" '[["w", {"generic": "T"}]]' pub fn one_generic_param_fn<T: Wham>(w: T) {} -// @is "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.generics.where_predicates" [] -// @count "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.generics.params[*]" 1 -// @is "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.generics.params[0].name" '"impl Wham"' -// @is "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.generics.params[0].kind.type.synthetic" true -// @is "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.generics.params[0].kind.type.bounds[0].trait_bound.trait.id" $wham_id -// @count "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.decl.inputs[*]" 1 -// @is "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.decl.inputs[0][0]" '"w"' -// @is "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.decl.inputs[0][1].kind" '"impl_trait"' -// @is "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.decl.inputs[0][1].inner[0].trait_bound.trait.id" $wham_id +// @is "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.generics.where_predicates" [] +// @count "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.generics.params[*]" 1 +// @is "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.generics.params[0].name" '"impl Wham"' +// @is "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.generics.params[0].kind.type.synthetic" true +// @is "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.generics.params[0].kind.type.bounds[0].trait_bound.trait.id" $wham_id +// @count "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.decl.inputs[*]" 1 +// @is "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.decl.inputs[0][0]" '"w"' +// @is "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.decl.inputs[0][1].impl_trait[0].trait_bound.trait.id" $wham_id pub fn one_synthetic_generic_param_fn(w: impl Wham) {} diff --git a/tests/rustdoc-json/fns/pattern_arg.rs b/tests/rustdoc-json/fns/pattern_arg.rs index 32b7da0fae4..55f24797ac0 100644 --- a/tests/rustdoc-json/fns/pattern_arg.rs +++ b/tests/rustdoc-json/fns/pattern_arg.rs @@ -1,7 +1,7 @@ -// @is "$.index[*][?(@.name=='fst')].inner.decl.inputs[0][0]" '"(x, _)"' +// @is "$.index[*][?(@.name=='fst')].inner.function.decl.inputs[0][0]" '"(x, _)"' pub fn fst<X, Y>((x, _): (X, Y)) -> X { x } -// @is "$.index[*][?(@.name=='drop_int')].inner.decl.inputs[0][0]" '"_"' +// @is "$.index[*][?(@.name=='drop_int')].inner.function.decl.inputs[0][0]" '"_"' pub fn drop_int(_: i32) {} diff --git a/tests/rustdoc-json/fns/qualifiers.rs b/tests/rustdoc-json/fns/qualifiers.rs index 7ff54290042..643865a3f07 100644 --- a/tests/rustdoc-json/fns/qualifiers.rs +++ b/tests/rustdoc-json/fns/qualifiers.rs @@ -1,33 +1,33 @@ // edition:2018 -// @is "$.index[*][?(@.name=='nothing_fn')].inner.header.async" false -// @is "$.index[*][?(@.name=='nothing_fn')].inner.header.const" false -// @is "$.index[*][?(@.name=='nothing_fn')].inner.header.unsafe" false +// @is "$.index[*][?(@.name=='nothing_fn')].inner.function.header.async" false +// @is "$.index[*][?(@.name=='nothing_fn')].inner.function.header.const" false +// @is "$.index[*][?(@.name=='nothing_fn')].inner.function.header.unsafe" false pub fn nothing_fn() {} -// @is "$.index[*][?(@.name=='unsafe_fn')].inner.header.async" false -// @is "$.index[*][?(@.name=='unsafe_fn')].inner.header.const" false -// @is "$.index[*][?(@.name=='unsafe_fn')].inner.header.unsafe" true +// @is "$.index[*][?(@.name=='unsafe_fn')].inner.function.header.async" false +// @is "$.index[*][?(@.name=='unsafe_fn')].inner.function.header.const" false +// @is "$.index[*][?(@.name=='unsafe_fn')].inner.function.header.unsafe" true pub unsafe fn unsafe_fn() {} -// @is "$.index[*][?(@.name=='const_fn')].inner.header.async" false -// @is "$.index[*][?(@.name=='const_fn')].inner.header.const" true -// @is "$.index[*][?(@.name=='const_fn')].inner.header.unsafe" false +// @is "$.index[*][?(@.name=='const_fn')].inner.function.header.async" false +// @is "$.index[*][?(@.name=='const_fn')].inner.function.header.const" true +// @is "$.index[*][?(@.name=='const_fn')].inner.function.header.unsafe" false pub const fn const_fn() {} -// @is "$.index[*][?(@.name=='async_fn')].inner.header.async" true -// @is "$.index[*][?(@.name=='async_fn')].inner.header.const" false -// @is "$.index[*][?(@.name=='async_fn')].inner.header.unsafe" false +// @is "$.index[*][?(@.name=='async_fn')].inner.function.header.async" true +// @is "$.index[*][?(@.name=='async_fn')].inner.function.header.const" false +// @is "$.index[*][?(@.name=='async_fn')].inner.function.header.unsafe" false pub async fn async_fn() {} -// @is "$.index[*][?(@.name=='async_unsafe_fn')].inner.header.async" true -// @is "$.index[*][?(@.name=='async_unsafe_fn')].inner.header.const" false -// @is "$.index[*][?(@.name=='async_unsafe_fn')].inner.header.unsafe" true +// @is "$.index[*][?(@.name=='async_unsafe_fn')].inner.function.header.async" true +// @is "$.index[*][?(@.name=='async_unsafe_fn')].inner.function.header.const" false +// @is "$.index[*][?(@.name=='async_unsafe_fn')].inner.function.header.unsafe" true pub async unsafe fn async_unsafe_fn() {} -// @is "$.index[*][?(@.name=='const_unsafe_fn')].inner.header.async" false -// @is "$.index[*][?(@.name=='const_unsafe_fn')].inner.header.const" true -// @is "$.index[*][?(@.name=='const_unsafe_fn')].inner.header.unsafe" true +// @is "$.index[*][?(@.name=='const_unsafe_fn')].inner.function.header.async" false +// @is "$.index[*][?(@.name=='const_unsafe_fn')].inner.function.header.const" true +// @is "$.index[*][?(@.name=='const_unsafe_fn')].inner.function.header.unsafe" true pub const unsafe fn const_unsafe_fn() {} // It's impossible for a function to be both const and async, so no test for that diff --git a/tests/rustdoc-json/fns/return_type_alias.rs b/tests/rustdoc-json/fns/return_type_alias.rs index 2578bb49ad3..e8a7dce8b0a 100644 --- a/tests/rustdoc-json/fns/return_type_alias.rs +++ b/tests/rustdoc-json/fns/return_type_alias.rs @@ -3,8 +3,7 @@ /// @set foo = "$.index[*][?(@.name=='Foo')].id" pub type Foo = i32; -// @is "$.index[*][?(@.name=='demo')].inner.decl.output.kind" '"resolved_path"' -// @is "$.index[*][?(@.name=='demo')].inner.decl.output.inner.id" $foo +// @is "$.index[*][?(@.name=='demo')].inner.function.decl.output.resolved_path.id" $foo pub fn demo() -> Foo { 42 } diff --git a/tests/rustdoc-json/generic-associated-types/gats.rs b/tests/rustdoc-json/generic-associated-types/gats.rs index e5809783aec..99c57ff6540 100644 --- a/tests/rustdoc-json/generic-associated-types/gats.rs +++ b/tests/rustdoc-json/generic-associated-types/gats.rs @@ -9,34 +9,32 @@ pub trait Sized {} pub trait Display {} pub trait LendingIterator { - // @count "$.index[*][?(@.name=='LendingItem')].inner.generics.params[*]" 1 - // @is "$.index[*][?(@.name=='LendingItem')].inner.generics.params[*].name" \"\'a\" - // @count "$.index[*][?(@.name=='LendingItem')].inner.generics.where_predicates[*]" 1 - // @is "$.index[*][?(@.name=='LendingItem')].inner.generics.where_predicates[*].bound_predicate.type.inner" \"Self\" - // @is "$.index[*][?(@.name=='LendingItem')].inner.generics.where_predicates[*].bound_predicate.bounds[*].outlives" \"\'a\" - // @count "$.index[*][?(@.name=='LendingItem')].inner.bounds[*]" 1 + // @count "$.index[*][?(@.name=='LendingItem')].inner.assoc_type.generics.params[*]" 1 + // @is "$.index[*][?(@.name=='LendingItem')].inner.assoc_type.generics.params[*].name" \"\'a\" + // @count "$.index[*][?(@.name=='LendingItem')].inner.assoc_type.generics.where_predicates[*]" 1 + // @is "$.index[*][?(@.name=='LendingItem')].inner.assoc_type.generics.where_predicates[*].bound_predicate.type.generic" \"Self\" + // @is "$.index[*][?(@.name=='LendingItem')].inner.assoc_type.generics.where_predicates[*].bound_predicate.bounds[*].outlives" \"\'a\" + // @count "$.index[*][?(@.name=='LendingItem')].inner.assoc_type.bounds[*]" 1 type LendingItem<'a>: Display where Self: 'a; - // @is "$.index[*][?(@.name=='lending_next')].inner.decl.output.kind" \"qualified_path\" - // @count "$.index[*][?(@.name=='lending_next')].inner.decl.output.inner.args.angle_bracketed.args[*]" 1 - // @count "$.index[*][?(@.name=='lending_next')].inner.decl.output.inner.args.angle_bracketed.bindings[*]" 0 - // @is "$.index[*][?(@.name=='lending_next')].inner.decl.output.inner.self_type.inner" \"Self\" - // @is "$.index[*][?(@.name=='lending_next')].inner.decl.output.inner.name" \"LendingItem\" + // @count "$.index[*][?(@.name=='lending_next')].inner.function.decl.output.qualified_path.args.angle_bracketed.args[*]" 1 + // @count "$.index[*][?(@.name=='lending_next')].inner.function.decl.output.qualified_path.args.angle_bracketed.bindings[*]" 0 + // @is "$.index[*][?(@.name=='lending_next')].inner.function.decl.output.qualified_path.self_type.generic" \"Self\" + // @is "$.index[*][?(@.name=='lending_next')].inner.function.decl.output.qualified_path.name" \"LendingItem\" fn lending_next<'a>(&'a self) -> Self::LendingItem<'a>; } pub trait Iterator { - // @count "$.index[*][?(@.name=='Item')].inner.generics.params[*]" 0 - // @count "$.index[*][?(@.name=='Item')].inner.generics.where_predicates[*]" 0 - // @count "$.index[*][?(@.name=='Item')].inner.bounds[*]" 1 + // @count "$.index[*][?(@.name=='Item')].inner.assoc_type.generics.params[*]" 0 + // @count "$.index[*][?(@.name=='Item')].inner.assoc_type.generics.where_predicates[*]" 0 + // @count "$.index[*][?(@.name=='Item')].inner.assoc_type.bounds[*]" 1 type Item: Display; - // @is "$.index[*][?(@.name=='next')].inner.decl.output.kind" \"qualified_path\" - // @count "$.index[*][?(@.name=='next')].inner.decl.output.inner.args.angle_bracketed.args[*]" 0 - // @count "$.index[*][?(@.name=='next')].inner.decl.output.inner.args.angle_bracketed.bindings[*]" 0 - // @is "$.index[*][?(@.name=='next')].inner.decl.output.inner.self_type.inner" \"Self\" - // @is "$.index[*][?(@.name=='next')].inner.decl.output.inner.name" \"Item\" + // @count "$.index[*][?(@.name=='next')].inner.function.decl.output.qualified_path.args.angle_bracketed.args[*]" 0 + // @count "$.index[*][?(@.name=='next')].inner.function.decl.output.qualified_path.args.angle_bracketed.bindings[*]" 0 + // @is "$.index[*][?(@.name=='next')].inner.function.decl.output.qualified_path.self_type.generic" \"Self\" + // @is "$.index[*][?(@.name=='next')].inner.function.decl.output.qualified_path.name" \"Item\" fn next<'a>(&'a self) -> Self::Item; } diff --git a/tests/rustdoc-json/glob_import.rs b/tests/rustdoc-json/glob_import.rs index 00051b12199..f37ce0abb41 100644 --- a/tests/rustdoc-json/glob_import.rs +++ b/tests/rustdoc-json/glob_import.rs @@ -5,7 +5,7 @@ #![no_core] // @has "$.index[*][?(@.name=='glob')]" -// @has "$.index[*][?(@.kind=='import')].inner.name" \"*\" +// @has "$.index[*][?(@.inner.import)].inner.import.name" \"*\" mod m1 { diff --git a/tests/rustdoc-json/impls/auto.rs b/tests/rustdoc-json/impls/auto.rs index 50d85241427..ace37e5b3df 100644 --- a/tests/rustdoc-json/impls/auto.rs +++ b/tests/rustdoc-json/impls/auto.rs @@ -12,7 +12,8 @@ impl Foo { } // Testing spans, so all tests below code -// @is "$.index[*][?(@.kind=='impl' && @.inner.synthetic==true)].span" null // @is "$.index[*][?(@.docs=='has span')].span.begin" "[10, 0]" // @is "$.index[*][?(@.docs=='has span')].span.end" "[12, 1]" +// FIXME: this doesn't work due to https://github.com/freestrings/jsonpath/issues/91 +// is "$.index[*][?(@.inner.impl.synthetic==true)].span" null pub struct Foo; diff --git a/tests/rustdoc-json/impls/foreign_for_local.rs b/tests/rustdoc-json/impls/foreign_for_local.rs index 290c2d571e8..20e1cecd778 100644 --- a/tests/rustdoc-json/impls/foreign_for_local.rs +++ b/tests/rustdoc-json/impls/foreign_for_local.rs @@ -3,7 +3,7 @@ extern crate foreign_trait; /// ForeignTrait id hack pub use foreign_trait::ForeignTrait as _; -// @set ForeignTrait = "$.index[*][?(@.docs=='ForeignTrait id hack')].inner.id" +// @set ForeignTrait = "$.index[*][?(@.docs=='ForeignTrait id hack')].inner.import.id" pub struct LocalStruct; // @set LocalStruct = "$.index[*][?(@.name=='LocalStruct')].id" @@ -12,7 +12,7 @@ pub struct LocalStruct; impl foreign_trait::ForeignTrait for LocalStruct {} // @set impl = "$.index[*][?(@.docs=='foreign for local')].id" -// @is "$.index[*][?(@.docs=='foreign for local')].inner.for.inner.id" $LocalStruct -// @is "$.index[*][?(@.docs=='foreign for local')].inner.trait.id" $ForeignTrait +// @is "$.index[*][?(@.docs=='foreign for local')].inner.impl.for.resolved_path.id" $LocalStruct +// @is "$.index[*][?(@.docs=='foreign for local')].inner.impl.trait.id" $ForeignTrait -// @has "$.index[*][?(@.name=='LocalStruct')].inner.impls[*]" $impl +// @has "$.index[*][?(@.name=='LocalStruct')].inner.struct.impls[*]" $impl diff --git a/tests/rustdoc-json/impls/import_from_private.rs b/tests/rustdoc-json/impls/import_from_private.rs index fa88b6113a5..d2b0665dcaf 100644 --- a/tests/rustdoc-json/impls/import_from_private.rs +++ b/tests/rustdoc-json/impls/import_from_private.rs @@ -4,19 +4,19 @@ #![no_core] mod bar { - // @set baz = "$.index[*][?(@.kind=='struct')].id" + // @set baz = "$.index[*][?(@.inner.struct)].id" pub struct Baz; - // @set impl = "$.index[*][?(@.kind=='impl')].id" + // @set impl = "$.index[*][?(@.inner.impl)].id" impl Baz { - // @set doit = "$.index[*][?(@.kind=='function')].id" + // @set doit = "$.index[*][?(@.inner.function)].id" pub fn doit() {} } } -// @set import = "$.index[*][?(@.kind=='import')].id" +// @set import = "$.index[*][?(@.inner.import)].id" pub use bar::Baz; -// @is "$.index[*][?(@.kind=='module')].inner.items[*]" $import -// @is "$.index[*][?(@.kind=='import')].inner.id" $baz -// @is "$.index[*][?(@.kind=='struct')].inner.impls[*]" $impl -// @is "$.index[*][?(@.kind=='impl')].inner.items[*]" $doit +// @is "$.index[*].inner.module.items[*]" $import +// @is "$.index[*].inner.import.id" $baz +// @is "$.index[*].inner.struct.impls[*]" $impl +// @is "$.index[*].inner.impl.items[*]" $doit diff --git a/tests/rustdoc-json/impls/local_for_foreign.rs b/tests/rustdoc-json/impls/local_for_foreign.rs index 74f2f08b5a4..72352ce88da 100644 --- a/tests/rustdoc-json/impls/local_for_foreign.rs +++ b/tests/rustdoc-json/impls/local_for_foreign.rs @@ -3,7 +3,7 @@ extern crate foreign_struct; /// ForeignStruct id hack pub use foreign_struct::ForeignStruct as _; -// @set ForeignStruct = "$.index[*][?(@.docs=='ForeignStruct id hack')].inner.id" +// @set ForeignStruct = "$.index[*][?(@.docs=='ForeignStruct id hack')].inner.import.id" pub trait LocalTrait {} // @set LocalTrait = "$.index[*][?(@.name=='LocalTrait')].id" @@ -12,7 +12,7 @@ pub trait LocalTrait {} impl LocalTrait for foreign_struct::ForeignStruct {} // @set impl = "$.index[*][?(@.docs=='local for foreign')].id" -// @is "$.index[*][?(@.docs=='local for foreign')].inner.trait.id" $LocalTrait -// @is "$.index[*][?(@.docs=='local for foreign')].inner.for.inner.id" $ForeignStruct +// @is "$.index[*][?(@.docs=='local for foreign')].inner.impl.trait.id" $LocalTrait +// @is "$.index[*][?(@.docs=='local for foreign')].inner.impl.for.resolved_path.id" $ForeignStruct -// @is "$.index[*][?(@.name=='LocalTrait')].inner.implementations[*]" $impl +// @is "$.index[*][?(@.name=='LocalTrait')].inner.trait.implementations[*]" $impl diff --git a/tests/rustdoc-json/impls/local_for_local.rs b/tests/rustdoc-json/impls/local_for_local.rs index 93dedb7ec92..37a72000329 100644 --- a/tests/rustdoc-json/impls/local_for_local.rs +++ b/tests/rustdoc-json/impls/local_for_local.rs @@ -9,7 +9,7 @@ pub trait Trait {} /// impl impl Trait for Struct {} -// @is "$.index[*][?(@.name=='Struct')].inner.impls[*]" $impl -// @is "$.index[*][?(@.name=='Trait')].inner.implementations[*]" $impl -// @is "$.index[*][?(@.docs=='impl')].inner.trait.id" $trait -// @is "$.index[*][?(@.docs=='impl')].inner.for.inner.id" $struct +// @is "$.index[*][?(@.name=='Struct')].inner.struct.impls[*]" $impl +// @is "$.index[*][?(@.name=='Trait')].inner.trait.implementations[*]" $impl +// @is "$.index[*][?(@.docs=='impl')].inner.impl.trait.id" $trait +// @is "$.index[*][?(@.docs=='impl')].inner.impl.for.resolved_path.id" $struct diff --git a/tests/rustdoc-json/impls/local_for_local_primitive.rs b/tests/rustdoc-json/impls/local_for_local_primitive.rs index 8383dcc0482..769dd3f0a2c 100644 --- a/tests/rustdoc-json/impls/local_for_local_primitive.rs +++ b/tests/rustdoc-json/impls/local_for_local_primitive.rs @@ -5,14 +5,13 @@ // @set Local = "$.index[*][?(@.name=='Local')].id" pub trait Local {} -// @is "$.index[*][?(@.docs=='Local for bool')].inner.trait.id" $Local -// @is "$.index[*][?(@.docs=='Local for bool')].inner.for.kind" '"primitive"' -// @is "$.index[*][?(@.docs=='Local for bool')].inner.for.inner" '"bool"' +// @is "$.index[*][?(@.docs=='Local for bool')].inner.impl.trait.id" $Local +// @is "$.index[*][?(@.docs=='Local for bool')].inner.impl.for.primitive" '"bool"' /// Local for bool impl Local for bool {} // @set impl = "$.index[*][?(@.docs=='Local for bool')].id" -// @is "$.index[*][?(@.name=='Local')].inner.implementations[*]" $impl +// @is "$.index[*][?(@.name=='Local')].inner.trait.implementations[*]" $impl // FIXME(#101695): Test bool's `impls` include "Local for bool" // @has "$.index[*][?(@.name=='bool')]" diff --git a/tests/rustdoc-json/impls/local_for_primitive.rs b/tests/rustdoc-json/impls/local_for_primitive.rs index 7702a526fd8..85278c0e08c 100644 --- a/tests/rustdoc-json/impls/local_for_primitive.rs +++ b/tests/rustdoc-json/impls/local_for_primitive.rs @@ -2,6 +2,6 @@ pub trait Local {} // @set impl = "$.index[*][?(@.docs=='local for bool')].id" -// @is "$.index[*][?(@.name=='Local')].inner.implementations[*]" $impl +// @is "$.index[*][?(@.name=='Local')].inner.trait.implementations[*]" $impl /// local for bool impl Local for bool {} diff --git a/tests/rustdoc-json/lifetime/longest.rs b/tests/rustdoc-json/lifetime/longest.rs index 326dab8e57a..dc28258a8b6 100644 --- a/tests/rustdoc-json/lifetime/longest.rs +++ b/tests/rustdoc-json/lifetime/longest.rs @@ -3,30 +3,27 @@ #![feature(no_core)] #![no_core] -// @is "$.index[*][?(@.name=='longest')].inner.generics.params[0].name" \"\'a\" -// @is "$.index[*][?(@.name=='longest')].inner.generics.params[0].kind" '{"lifetime": {"outlives": []}}' -// @is "$.index[*][?(@.name=='longest')].inner.generics.params[0].kind" '{"lifetime": {"outlives": []}}' -// @count "$.index[*][?(@.name=='longest')].inner.generics.params[*]" 1 -// @is "$.index[*][?(@.name=='longest')].inner.generics.where_predicates" [] +// @is "$.index[*][?(@.name=='longest')].inner.function.generics.params[0].name" \"\'a\" +// @is "$.index[*][?(@.name=='longest')].inner.function.generics.params[0].kind" '{"lifetime": {"outlives": []}}' +// @is "$.index[*][?(@.name=='longest')].inner.function.generics.params[0].kind" '{"lifetime": {"outlives": []}}' +// @count "$.index[*][?(@.name=='longest')].inner.function.generics.params[*]" 1 +// @is "$.index[*][?(@.name=='longest')].inner.function.generics.where_predicates" [] -// @count "$.index[*][?(@.name=='longest')].inner.decl.inputs[*]" 2 -// @is "$.index[*][?(@.name=='longest')].inner.decl.inputs[0][0]" '"l"' -// @is "$.index[*][?(@.name=='longest')].inner.decl.inputs[1][0]" '"r"' +// @count "$.index[*][?(@.name=='longest')].inner.function.decl.inputs[*]" 2 +// @is "$.index[*][?(@.name=='longest')].inner.function.decl.inputs[0][0]" '"l"' +// @is "$.index[*][?(@.name=='longest')].inner.function.decl.inputs[1][0]" '"r"' -// @is "$.index[*][?(@.name=='longest')].inner.decl.inputs[0][1].kind" '"borrowed_ref"' -// @is "$.index[*][?(@.name=='longest')].inner.decl.inputs[0][1].inner.lifetime" \"\'a\" -// @is "$.index[*][?(@.name=='longest')].inner.decl.inputs[0][1].inner.mutable" false -// @is "$.index[*][?(@.name=='longest')].inner.decl.inputs[0][1].inner.type" '{"inner": "str", "kind": "primitive"}' +// @is "$.index[*][?(@.name=='longest')].inner.function.decl.inputs[0][1].borrowed_ref.lifetime" \"\'a\" +// @is "$.index[*][?(@.name=='longest')].inner.function.decl.inputs[0][1].borrowed_ref.mutable" false +// @is "$.index[*][?(@.name=='longest')].inner.function.decl.inputs[0][1].borrowed_ref.type.primitive" \"str\" -// @is "$.index[*][?(@.name=='longest')].inner.decl.inputs[1][1].kind" '"borrowed_ref"' -// @is "$.index[*][?(@.name=='longest')].inner.decl.inputs[1][1].inner.lifetime" \"\'a\" -// @is "$.index[*][?(@.name=='longest')].inner.decl.inputs[1][1].inner.mutable" false -// @is "$.index[*][?(@.name=='longest')].inner.decl.inputs[1][1].inner.type" '{"inner": "str", "kind": "primitive"}' +// @is "$.index[*][?(@.name=='longest')].inner.function.decl.inputs[1][1].borrowed_ref.lifetime" \"\'a\" +// @is "$.index[*][?(@.name=='longest')].inner.function.decl.inputs[1][1].borrowed_ref.mutable" false +// @is "$.index[*][?(@.name=='longest')].inner.function.decl.inputs[1][1].borrowed_ref.type.primitive" \"str\" -// @is "$.index[*][?(@.name=='longest')].inner.decl.output.kind" '"borrowed_ref"' -// @is "$.index[*][?(@.name=='longest')].inner.decl.output.inner.lifetime" \"\'a\" -// @is "$.index[*][?(@.name=='longest')].inner.decl.output.inner.mutable" false -// @is "$.index[*][?(@.name=='longest')].inner.decl.output.inner.type" '{"inner": "str", "kind": "primitive"}' +// @is "$.index[*][?(@.name=='longest')].inner.function.decl.output.borrowed_ref.lifetime" \"\'a\" +// @is "$.index[*][?(@.name=='longest')].inner.function.decl.output.borrowed_ref.mutable" false +// @is "$.index[*][?(@.name=='longest')].inner.function.decl.output.borrowed_ref.type.primitive" \"str\" pub fn longest<'a>(l: &'a str, r: &'a str) -> &'a str { if l.len() > r.len() { l } else { r } diff --git a/tests/rustdoc-json/lifetime/outlives.rs b/tests/rustdoc-json/lifetime/outlives.rs index e15a533efdc..6e105b382b0 100644 --- a/tests/rustdoc-json/lifetime/outlives.rs +++ b/tests/rustdoc-json/lifetime/outlives.rs @@ -3,21 +3,19 @@ #![feature(no_core)] #![no_core] -// @count "$.index[*][?(@.name=='foo')].inner.generics.params[*]" 3 -// @is "$.index[*][?(@.name=='foo')].inner.generics.where_predicates" [] -// @is "$.index[*][?(@.name=='foo')].inner.generics.params[0].name" \"\'a\" -// @is "$.index[*][?(@.name=='foo')].inner.generics.params[1].name" \"\'b\" -// @is "$.index[*][?(@.name=='foo')].inner.generics.params[2].name" '"T"' -// @is "$.index[*][?(@.name=='foo')].inner.generics.params[0].kind.lifetime.outlives" [] -// @is "$.index[*][?(@.name=='foo')].inner.generics.params[1].kind.lifetime.outlives" [\"\'a\"] -// @is "$.index[*][?(@.name=='foo')].inner.generics.params[2].kind.type.default" null -// @count "$.index[*][?(@.name=='foo')].inner.generics.params[2].kind.type.bounds[*]" 1 -// @is "$.index[*][?(@.name=='foo')].inner.generics.params[2].kind.type.bounds[0].outlives" \"\'b\" -// @is "$.index[*][?(@.name=='foo')].inner.decl.inputs[0][1].kind" '"borrowed_ref"' -// @is "$.index[*][?(@.name=='foo')].inner.decl.inputs[0][1].inner.lifetime" \"\'a\" -// @is "$.index[*][?(@.name=='foo')].inner.decl.inputs[0][1].inner.mutable" false -// @is "$.index[*][?(@.name=='foo')].inner.decl.inputs[0][1].inner.type.kind" '"borrowed_ref"' -// @is "$.index[*][?(@.name=='foo')].inner.decl.inputs[0][1].inner.type.inner.lifetime" \"\'b\" -// @is "$.index[*][?(@.name=='foo')].inner.decl.inputs[0][1].inner.type.inner.mutable" false -// @is "$.index[*][?(@.name=='foo')].inner.decl.inputs[0][1].inner.type.inner.type" '{"inner": "T", "kind": "generic"}' +// @count "$.index[*][?(@.name=='foo')].inner.function.generics.params[*]" 3 +// @is "$.index[*][?(@.name=='foo')].inner.function.generics.where_predicates" [] +// @is "$.index[*][?(@.name=='foo')].inner.function.generics.params[0].name" \"\'a\" +// @is "$.index[*][?(@.name=='foo')].inner.function.generics.params[1].name" \"\'b\" +// @is "$.index[*][?(@.name=='foo')].inner.function.generics.params[2].name" '"T"' +// @is "$.index[*][?(@.name=='foo')].inner.function.generics.params[0].kind.lifetime.outlives" [] +// @is "$.index[*][?(@.name=='foo')].inner.function.generics.params[1].kind.lifetime.outlives" [\"\'a\"] +// @is "$.index[*][?(@.name=='foo')].inner.function.generics.params[2].kind.type.default" null +// @count "$.index[*][?(@.name=='foo')].inner.function.generics.params[2].kind.type.bounds[*]" 1 +// @is "$.index[*][?(@.name=='foo')].inner.function.generics.params[2].kind.type.bounds[0].outlives" \"\'b\" +// @is "$.index[*][?(@.name=='foo')].inner.function.decl.inputs[0][1].borrowed_ref.lifetime" \"\'a\" +// @is "$.index[*][?(@.name=='foo')].inner.function.decl.inputs[0][1].borrowed_ref.mutable" false +// @is "$.index[*][?(@.name=='foo')].inner.function.decl.inputs[0][1].borrowed_ref.type.borrowed_ref.lifetime" \"\'b\" +// @is "$.index[*][?(@.name=='foo')].inner.function.decl.inputs[0][1].borrowed_ref.type.borrowed_ref.mutable" false +// @is "$.index[*][?(@.name=='foo')].inner.function.decl.inputs[0][1].borrowed_ref.type.borrowed_ref.type.generic" \"T\" pub fn foo<'a, 'b: 'a, T: 'b>(_: &'a &'b T) {} diff --git a/tests/rustdoc-json/methods/abi.rs b/tests/rustdoc-json/methods/abi.rs index fd03d92d65b..b8279298c1e 100644 --- a/tests/rustdoc-json/methods/abi.rs +++ b/tests/rustdoc-json/methods/abi.rs @@ -8,47 +8,47 @@ pub struct Foo; impl Foo { - // @is "$.index[*][?(@.name=='abi_rust')].inner.header.abi" \"Rust\" + // @is "$.index[*][?(@.name=='abi_rust')].inner.function.header.abi" \"Rust\" pub fn abi_rust() {} - // @is "$.index[*][?(@.name=='abi_c')].inner.header.abi" '{"C": {"unwind": false}}' + // @is "$.index[*][?(@.name=='abi_c')].inner.function.header.abi" '{"C": {"unwind": false}}' pub extern "C" fn abi_c() {} - // @is "$.index[*][?(@.name=='abi_system')].inner.header.abi" '{"System": {"unwind": false}}' + // @is "$.index[*][?(@.name=='abi_system')].inner.function.header.abi" '{"System": {"unwind": false}}' pub extern "system" fn abi_system() {} - // @is "$.index[*][?(@.name=='abi_c_unwind')].inner.header.abi" '{"C": {"unwind": true}}' + // @is "$.index[*][?(@.name=='abi_c_unwind')].inner.function.header.abi" '{"C": {"unwind": true}}' pub extern "C-unwind" fn abi_c_unwind() {} - // @is "$.index[*][?(@.name=='abi_system_unwind')].inner.header.abi" '{"System": {"unwind": true}}' + // @is "$.index[*][?(@.name=='abi_system_unwind')].inner.function.header.abi" '{"System": {"unwind": true}}' pub extern "system-unwind" fn abi_system_unwind() {} - // @is "$.index[*][?(@.name=='abi_vectorcall')].inner.header.abi.Other" '"\"vectorcall\""' + // @is "$.index[*][?(@.name=='abi_vectorcall')].inner.function.header.abi.Other" '"\"vectorcall\""' pub extern "vectorcall" fn abi_vectorcall() {} - // @is "$.index[*][?(@.name=='abi_vectorcall_unwind')].inner.header.abi.Other" '"\"vectorcall-unwind\""' + // @is "$.index[*][?(@.name=='abi_vectorcall_unwind')].inner.function.header.abi.Other" '"\"vectorcall-unwind\""' pub extern "vectorcall-unwind" fn abi_vectorcall_unwind() {} } pub trait Bar { - // @is "$.index[*][?(@.name=='trait_abi_rust')].inner.header.abi" \"Rust\" + // @is "$.index[*][?(@.name=='trait_abi_rust')].inner.function.header.abi" \"Rust\" fn trait_abi_rust() {} - // @is "$.index[*][?(@.name=='trait_abi_c')].inner.header.abi" '{"C": {"unwind": false}}' + // @is "$.index[*][?(@.name=='trait_abi_c')].inner.function.header.abi" '{"C": {"unwind": false}}' extern "C" fn trait_abi_c() {} - // @is "$.index[*][?(@.name=='trait_abi_system')].inner.header.abi" '{"System": {"unwind": false}}' + // @is "$.index[*][?(@.name=='trait_abi_system')].inner.function.header.abi" '{"System": {"unwind": false}}' extern "system" fn trait_abi_system() {} - // @is "$.index[*][?(@.name=='trait_abi_c_unwind')].inner.header.abi" '{"C": {"unwind": true}}' + // @is "$.index[*][?(@.name=='trait_abi_c_unwind')].inner.function.header.abi" '{"C": {"unwind": true}}' extern "C-unwind" fn trait_abi_c_unwind() {} - // @is "$.index[*][?(@.name=='trait_abi_system_unwind')].inner.header.abi" '{"System": {"unwind": true}}' + // @is "$.index[*][?(@.name=='trait_abi_system_unwind')].inner.function.header.abi" '{"System": {"unwind": true}}' extern "system-unwind" fn trait_abi_system_unwind() {} - // @is "$.index[*][?(@.name=='trait_abi_vectorcall')].inner.header.abi.Other" '"\"vectorcall\""' + // @is "$.index[*][?(@.name=='trait_abi_vectorcall')].inner.function.header.abi.Other" '"\"vectorcall\""' extern "vectorcall" fn trait_abi_vectorcall() {} - // @is "$.index[*][?(@.name=='trait_abi_vectorcall_unwind')].inner.header.abi.Other" '"\"vectorcall-unwind\""' + // @is "$.index[*][?(@.name=='trait_abi_vectorcall_unwind')].inner.function.header.abi.Other" '"\"vectorcall-unwind\""' extern "vectorcall-unwind" fn trait_abi_vectorcall_unwind() {} } diff --git a/tests/rustdoc-json/methods/qualifiers.rs b/tests/rustdoc-json/methods/qualifiers.rs index b9a5e56012e..65977bc826f 100644 --- a/tests/rustdoc-json/methods/qualifiers.rs +++ b/tests/rustdoc-json/methods/qualifiers.rs @@ -3,34 +3,34 @@ pub struct Foo; impl Foo { - // @is "$.index[*][?(@.name=='const_meth')].inner.header.async" false - // @is "$.index[*][?(@.name=='const_meth')].inner.header.const" true - // @is "$.index[*][?(@.name=='const_meth')].inner.header.unsafe" false + // @is "$.index[*][?(@.name=='const_meth')].inner.function.header.async" false + // @is "$.index[*][?(@.name=='const_meth')].inner.function.header.const" true + // @is "$.index[*][?(@.name=='const_meth')].inner.function.header.unsafe" false pub const fn const_meth() {} - // @is "$.index[*][?(@.name=='nothing_meth')].inner.header.async" false - // @is "$.index[*][?(@.name=='nothing_meth')].inner.header.const" false - // @is "$.index[*][?(@.name=='nothing_meth')].inner.header.unsafe" false + // @is "$.index[*][?(@.name=='nothing_meth')].inner.function.header.async" false + // @is "$.index[*][?(@.name=='nothing_meth')].inner.function.header.const" false + // @is "$.index[*][?(@.name=='nothing_meth')].inner.function.header.unsafe" false pub fn nothing_meth() {} - // @is "$.index[*][?(@.name=='unsafe_meth')].inner.header.async" false - // @is "$.index[*][?(@.name=='unsafe_meth')].inner.header.const" false - // @is "$.index[*][?(@.name=='unsafe_meth')].inner.header.unsafe" true + // @is "$.index[*][?(@.name=='unsafe_meth')].inner.function.header.async" false + // @is "$.index[*][?(@.name=='unsafe_meth')].inner.function.header.const" false + // @is "$.index[*][?(@.name=='unsafe_meth')].inner.function.header.unsafe" true pub unsafe fn unsafe_meth() {} - // @is "$.index[*][?(@.name=='async_meth')].inner.header.async" true - // @is "$.index[*][?(@.name=='async_meth')].inner.header.const" false - // @is "$.index[*][?(@.name=='async_meth')].inner.header.unsafe" false + // @is "$.index[*][?(@.name=='async_meth')].inner.function.header.async" true + // @is "$.index[*][?(@.name=='async_meth')].inner.function.header.const" false + // @is "$.index[*][?(@.name=='async_meth')].inner.function.header.unsafe" false pub async fn async_meth() {} - // @is "$.index[*][?(@.name=='async_unsafe_meth')].inner.header.async" true - // @is "$.index[*][?(@.name=='async_unsafe_meth')].inner.header.const" false - // @is "$.index[*][?(@.name=='async_unsafe_meth')].inner.header.unsafe" true + // @is "$.index[*][?(@.name=='async_unsafe_meth')].inner.function.header.async" true + // @is "$.index[*][?(@.name=='async_unsafe_meth')].inner.function.header.const" false + // @is "$.index[*][?(@.name=='async_unsafe_meth')].inner.function.header.unsafe" true pub async unsafe fn async_unsafe_meth() {} - // @is "$.index[*][?(@.name=='const_unsafe_meth')].inner.header.async" false - // @is "$.index[*][?(@.name=='const_unsafe_meth')].inner.header.const" true - // @is "$.index[*][?(@.name=='const_unsafe_meth')].inner.header.unsafe" true + // @is "$.index[*][?(@.name=='const_unsafe_meth')].inner.function.header.async" false + // @is "$.index[*][?(@.name=='const_unsafe_meth')].inner.function.header.const" true + // @is "$.index[*][?(@.name=='const_unsafe_meth')].inner.function.header.unsafe" true pub const unsafe fn const_unsafe_meth() {} // It's impossible for a method to be both const and async, so no test for that diff --git a/tests/rustdoc-json/nested.rs b/tests/rustdoc-json/nested.rs index ee2d2efa960..e012cfc5014 100644 --- a/tests/rustdoc-json/nested.rs +++ b/tests/rustdoc-json/nested.rs @@ -2,30 +2,31 @@ // compile-flags: --crate-version 1.0.0 // @is "$.crate_version" \"1.0.0\" -// @is "$.index[*][?(@.name=='nested')].kind" \"module\" -// @is "$.index[*][?(@.name=='nested')].inner.is_crate" true +// @has "$.index[*][?(@.name=='nested')].inner.module" +// @is "$.index[*][?(@.name=='nested')].inner.module.is_crate" true // @set l1_id = "$.index[*][?(@.name=='l1')].id" -// @ismany "$.index[*][?(@.name=='nested')].inner.items[*]" $l1_id +// @ismany "$.index[*][?(@.name=='nested')].inner.module.items[*]" $l1_id -// @is "$.index[*][?(@.name=='l1')].kind" \"module\" -// @is "$.index[*][?(@.name=='l1')].inner.is_crate" false +// @has "$.index[*][?(@.name=='l1')].inner.module" +// @is "$.index[*][?(@.name=='l1')].inner.module.is_crate" false pub mod l1 { - // @is "$.index[*][?(@.name=='l3')].kind" \"module\" - // @is "$.index[*][?(@.name=='l3')].inner.is_crate" false + // @has "$.index[*][?(@.name=='l3')].inner.module" + // @is "$.index[*][?(@.name=='l3')].inner.module.is_crate" false // @set l3_id = "$.index[*][?(@.name=='l3')].id" pub mod l3 { - // @is "$.index[*][?(@.name=='L4')].kind" \"struct\" - // @is "$.index[*][?(@.name=='L4')].inner.kind" \"unit\" + // @has "$.index[*][?(@.name=='L4')].inner.struct" + // @is "$.index[*][?(@.name=='L4')].inner.struct.kind" '"unit"' // @set l4_id = "$.index[*][?(@.name=='L4')].id" - // @ismany "$.index[*][?(@.name=='l3')].inner.items[*]" $l4_id + // @ismany "$.index[*][?(@.name=='l3')].inner.module.items[*]" $l4_id pub struct L4; } - // @is "$.index[*][?(@.inner.source=='l3::L4')].kind" \"import\" - // @is "$.index[*][?(@.inner.source=='l3::L4')].inner.glob" false - // @is "$.index[*][?(@.inner.source=='l3::L4')].inner.id" $l4_id - // @set l4_use_id = "$.index[*][?(@.inner.source=='l3::L4')].id" + // @is "$.index[*][?(@.inner.import)].inner.import.glob" false + // @is "$.index[*][?(@.inner.import)].inner.import.source" '"l3::L4"' + // @is "$.index[*][?(@.inner.import)].inner.import.glob" false + // @is "$.index[*][?(@.inner.import)].inner.import.id" $l4_id + // @set l4_use_id = "$.index[*][?(@.inner.import)].id" pub use l3::L4; } -// @ismany "$.index[*][?(@.name=='l1')].inner.items[*]" $l3_id $l4_use_id +// @ismany "$.index[*][?(@.name=='l1')].inner.module.items[*]" $l3_id $l4_use_id diff --git a/tests/rustdoc-json/non_lifetime_binders.rs b/tests/rustdoc-json/non_lifetime_binders.rs index ca5a008344a..cabee0b1caa 100644 --- a/tests/rustdoc-json/non_lifetime_binders.rs +++ b/tests/rustdoc-json/non_lifetime_binders.rs @@ -16,9 +16,9 @@ struct PhantomData<T_>; pub struct Wrapper<T_>(PhantomData<T_>); -// @count "$.index[*][?(@.name=='foo')].inner.generics.where_predicates[0].bound_predicate.generic_params[*]" 2 -// @is "$.index[*][?(@.name=='foo')].inner.generics.where_predicates[0].bound_predicate.generic_params[0].name" \"\'a\" -// @is "$.index[*][?(@.name=='foo')].inner.generics.where_predicates[0].bound_predicate.generic_params[0].kind" '{ "lifetime": { "outlives": [] } }' -// @is "$.index[*][?(@.name=='foo')].inner.generics.where_predicates[0].bound_predicate.generic_params[1].name" \"T\" -// @is "$.index[*][?(@.name=='foo')].inner.generics.where_predicates[0].bound_predicate.generic_params[1].kind" '{ "type": { "bounds": [], "default": null, "synthetic": false } }' +// @count "$.index[*][?(@.name=='foo')].inner.function.generics.where_predicates[0].bound_predicate.generic_params[*]" 2 +// @is "$.index[*][?(@.name=='foo')].inner.function.generics.where_predicates[0].bound_predicate.generic_params[0].name" \"\'a\" +// @is "$.index[*][?(@.name=='foo')].inner.function.generics.where_predicates[0].bound_predicate.generic_params[0].kind" '{ "lifetime": { "outlives": [] } }' +// @is "$.index[*][?(@.name=='foo')].inner.function.generics.where_predicates[0].bound_predicate.generic_params[1].name" \"T\" +// @is "$.index[*][?(@.name=='foo')].inner.function.generics.where_predicates[0].bound_predicate.generic_params[1].kind" '{ "type": { "bounds": [], "default": null, "synthetic": false } }' pub fn foo() where for<'a, T> &'a Wrapper<T>: Trait {} diff --git a/tests/rustdoc-json/primitives/local_primitive.rs b/tests/rustdoc-json/primitives/local_primitive.rs index 0cf479faf29..562c7e4d79b 100644 --- a/tests/rustdoc-json/primitives/local_primitive.rs +++ b/tests/rustdoc-json/primitives/local_primitive.rs @@ -14,7 +14,7 @@ mod prim_i32 {} // @set local_i32 = "$.index[*][?(@.name=='i32')].id" // @has "$.index[*][?(@.name=='local_primitive')]" -// @ismany "$.index[*][?(@.name=='local_primitive')].inner.items[*]" $local_i32 +// @ismany "$.index[*][?(@.name=='local_primitive')].inner.module.items[*]" $local_i32 // @is "$.index[*][?(@.name=='local_primitive')].links['prim@i32']" $local_i32 // Let's ensure the `prim_i32` module isn't present in the output JSON: diff --git a/tests/rustdoc-json/primitives/primitive_impls.rs b/tests/rustdoc-json/primitives/primitive_impls.rs index 85d179ee45f..4b7b7646b89 100644 --- a/tests/rustdoc-json/primitives/primitive_impls.rs +++ b/tests/rustdoc-json/primitives/primitive_impls.rs @@ -15,7 +15,7 @@ impl i32 { self } - // @is "$.index[*][?(@.docs=='Only core can do this')].inner.items[*]" $identity + // @is "$.index[*][?(@.docs=='Only core can do this')].inner.impl.items[*]" $identity } // @set Trait = "$.index[*][?(@.name=='Trait')].id" @@ -30,5 +30,5 @@ mod prim_i32 {} // @set i32 = "$.index[*][?(@.docs=='i32')].id" // @is "$.index[*][?(@.docs=='i32')].name" '"i32"' -// @is "$.index[*][?(@.docs=='i32')].inner.name" '"i32"' -// @ismany "$.index[*][?(@.docs=='i32')].inner.impls[*]" $impl_i32 $impl_trait_for_i32 +// @is "$.index[*][?(@.docs=='i32')].inner.primitive.name" '"i32"' +// @ismany "$.index[*][?(@.docs=='i32')].inner.primitive.impls[*]" $impl_i32 $impl_trait_for_i32 diff --git a/tests/rustdoc-json/primitives/primitive_type.rs b/tests/rustdoc-json/primitives/primitive_type.rs index 8024044bc05..5f251b3b890 100644 --- a/tests/rustdoc-json/primitives/primitive_type.rs +++ b/tests/rustdoc-json/primitives/primitive_type.rs @@ -1,22 +1,17 @@ #![feature(never_type)] // @is "$.index[*][?(@.name=='PrimNever')].visibility" \"public\" -// @is "$.index[*][?(@.name=='PrimNever')].inner.type.kind" \"primitive\" -// @is "$.index[*][?(@.name=='PrimNever')].inner.type.inner" \"never\" +// @is "$.index[*][?(@.name=='PrimNever')].inner.typedef.type.primitive" \"never\" pub type PrimNever = !; -// @is "$.index[*][?(@.name=='PrimStr')].inner.type.kind" \"primitive\" -// @is "$.index[*][?(@.name=='PrimStr')].inner.type.inner" \"str\" +// @is "$.index[*][?(@.name=='PrimStr')].inner.typedef.type.primitive" \"str\" pub type PrimStr = str; -// @is "$.index[*][?(@.name=='PrimBool')].inner.type.kind" \"primitive\" -// @is "$.index[*][?(@.name=='PrimBool')].inner.type.inner" \"bool\" +// @is "$.index[*][?(@.name=='PrimBool')].inner.typedef.type.primitive" \"bool\" pub type PrimBool = bool; -// @is "$.index[*][?(@.name=='PrimChar')].inner.type.kind" \"primitive\" -// @is "$.index[*][?(@.name=='PrimChar')].inner.type.inner" \"char\" +// @is "$.index[*][?(@.name=='PrimChar')].inner.typedef.type.primitive" \"char\" pub type PrimChar = char; -// @is "$.index[*][?(@.name=='PrimU8')].inner.type.kind" \"primitive\" -// @is "$.index[*][?(@.name=='PrimU8')].inner.type.inner" \"u8\" +// @is "$.index[*][?(@.name=='PrimU8')].inner.typedef.type.primitive" \"u8\" pub type PrimU8 = u8; diff --git a/tests/rustdoc-json/primitives/use_primitive.rs b/tests/rustdoc-json/primitives/use_primitive.rs index 5180a804f07..f8118b82094 100644 --- a/tests/rustdoc-json/primitives/use_primitive.rs +++ b/tests/rustdoc-json/primitives/use_primitive.rs @@ -13,8 +13,8 @@ mod usize {} // @!is "$.index[*][?(@.name=='checked_add')]" $local_crate_id // @!has "$.index[*][?(@.name=='is_ascii_uppercase')]" -// @is "$.index[*][?(@.kind=='import' && @.inner.name=='my_i32')].inner.id" null +// @is "$.index[*].inner.import[?(@.name=='my_i32')].id" null pub use i32 as my_i32; -// @is "$.index[*][?(@.kind=='import' && @.inner.name=='u32')].inner.id" null +// @is "$.index[*].inner.import[?(@.name=='u32')].id" null pub use u32; diff --git a/tests/rustdoc-json/reexport/doc_inline_external_crate.rs b/tests/rustdoc-json/reexport/doc_inline_external_crate.rs index 40b681d7dbb..1c5fed8c6b2 100644 --- a/tests/rustdoc-json/reexport/doc_inline_external_crate.rs +++ b/tests/rustdoc-json/reexport/doc_inline_external_crate.rs @@ -1,10 +1,11 @@ // Regression Test for https://github.com/rust-lang/rust/issues/110138 // aux-build: enum_with_discriminant.rs +// ignore-tidy-linelength #[doc(inline)] pub extern crate enum_with_discriminant; // @!has '$.index[*][?(@.docs == "Should not be inlined")]' -// @is '$.index[*][?(@.name == "enum_with_discriminant")].kind' '"extern_crate"' +// @has '$.index[*][?(@.name == "enum_with_discriminant")].inner.extern_crate' // @set enum_with_discriminant = '$.index[*][?(@.name == "enum_with_discriminant")].id' -// @is '$.index[*][?(@.name == "doc_inline_external_crate")].inner.items[*]' $enum_with_discriminant +// @is '$.index[*][?(@.name == "doc_inline_external_crate")].inner.module.items[*]' $enum_with_discriminant diff --git a/tests/rustdoc-json/reexport/export_extern_crate_as_self.rs b/tests/rustdoc-json/reexport/export_extern_crate_as_self.rs index f076feb7185..37ca279b352 100644 --- a/tests/rustdoc-json/reexport/export_extern_crate_as_self.rs +++ b/tests/rustdoc-json/reexport/export_extern_crate_as_self.rs @@ -7,5 +7,5 @@ // ignore-tidy-linelength -// @is "$.index[*][?(@.kind=='module')].name" \"export_extern_crate_as_self\" +// @is "$.index[*][?(@.inner.module)].name" \"export_extern_crate_as_self\" pub extern crate self as export_extern_crate_as_self; // Must be the same name as the crate already has diff --git a/tests/rustdoc-json/reexport/extern_crate_glob.rs b/tests/rustdoc-json/reexport/extern_crate_glob.rs index 8efb94fd3f1..553ee79b44e 100644 --- a/tests/rustdoc-json/reexport/extern_crate_glob.rs +++ b/tests/rustdoc-json/reexport/extern_crate_glob.rs @@ -6,5 +6,6 @@ extern crate enum_with_discriminant; pub use enum_with_discriminant::*; // @!has '$.index[*][?(@.docs == "Should not be inlined")]' -// @set use = '$.index[*][?(@.inner.name == "enum_with_discriminant")].id' -// @is '$.index[*][?(@.name == "extern_crate_glob")].inner.items[*]' $use +// @is '$.index[*][?(@.inner.import)].inner.import.name' \"enum_with_discriminant\" +// @set use = '$.index[*][?(@.inner.import)].id' +// @is '$.index[*][?(@.name == "extern_crate_glob")].inner.module.items[*]' $use diff --git a/tests/rustdoc-json/reexport/glob_collision.rs b/tests/rustdoc-json/reexport/glob_collision.rs index f91144dbfad..dee35ba78dc 100644 --- a/tests/rustdoc-json/reexport/glob_collision.rs +++ b/tests/rustdoc-json/reexport/glob_collision.rs @@ -3,26 +3,28 @@ #![feature(no_core)] #![no_core] -// @set m1 = "$.index[*][?(@.name == 'm1' && @.kind == 'module')].id" -// @is "$.index[*][?(@.name == 'm1' && @.kind == 'module')].inner.items" [] -// @is "$.index[*][?(@.name == 'm1' && @.kind == 'module')].inner.is_stripped" true +// @set m1 = "$.index[*][?(@.name == 'm1' && @.inner.module)].id" +// @is "$.index[*][?(@.name == 'm1')].inner.module.items" [] +// @is "$.index[*][?(@.name == 'm1')].inner.module.is_stripped" true mod m1 { pub fn f() {} } -// @set m2 = "$.index[*][?(@.name == 'm2' && @.kind == 'module')].id" -// @is "$.index[*][?(@.name == 'm2' && @.kind == 'module')].inner.items" [] -// @is "$.index[*][?(@.name == 'm2' && @.kind == 'module')].inner.is_stripped" true +// @set m2 = "$.index[*][?(@.name == 'm2' && @.inner.module)].id" +// @is "$.index[*][?(@.name == 'm2')].inner.module.items" [] +// @is "$.index[*][?(@.name == 'm2')].inner.module.is_stripped" true mod m2 { pub fn f(_: u8) {} } -// @set m1_use = "$.index[*][?(@.inner.name=='m1')].id" -// @is "$.index[*][?(@.inner.name=='m1')].inner.id" $m1 -// @is "$.index[*][?(@.inner.name=='m1')].inner.glob" true +// @set m1_use = "$.index[*][?(@.docs=='m1 re-export')].id" +// @is "$.index[*].inner.import[?(@.name=='m1')].id" $m1 +// @is "$.index[*].inner.import[?(@.name=='m1')].glob" true +/// m1 re-export pub use m1::*; -// @set m2_use = "$.index[*][?(@.inner.name=='m2')].id" -// @is "$.index[*][?(@.inner.name=='m2')].inner.id" $m2 -// @is "$.index[*][?(@.inner.name=='m2')].inner.glob" true +// @set m2_use = "$.index[*][?(@.docs=='m2 re-export')].id" +// @is "$.index[*].inner.import[?(@.name=='m2')].id" $m2 +// @is "$.index[*].inner.import[?(@.name=='m2')].glob" true +/// m2 re-export pub use m2::*; -// @ismany "$.index[*][?(@.inner.is_crate==true)].inner.items[*]" $m1_use $m2_use +// @ismany "$.index[*].inner.module[?(@.is_crate==true)].items[*]" $m1_use $m2_use diff --git a/tests/rustdoc-json/reexport/glob_empty_mod.rs b/tests/rustdoc-json/reexport/glob_empty_mod.rs index da68228352c..8b7150c8fd7 100644 --- a/tests/rustdoc-json/reexport/glob_empty_mod.rs +++ b/tests/rustdoc-json/reexport/glob_empty_mod.rs @@ -1,8 +1,8 @@ // Regression test for https://github.com/rust-lang/rust/issues/100973 -// @is "$.index[*][?(@.name=='m1' && @.kind == 'module')].inner.is_stripped" true +// @is "$.index[*][?(@.name=='m1' && @.inner.module)].inner.module.is_stripped" true // @set m1 = "$.index[*][?(@.name=='m1')].id" mod m1 {} -// @is "$.index[*][?(@.inner.name=='m1' && @.kind=='import')].inner.id" $m1 +// @is "$.index[*][?(@.inner.import)].inner.import.id" $m1 pub use m1::*; diff --git a/tests/rustdoc-json/reexport/glob_extern.rs b/tests/rustdoc-json/reexport/glob_extern.rs index 7a1e8c11ffa..78edfaf0aa8 100644 --- a/tests/rustdoc-json/reexport/glob_extern.rs +++ b/tests/rustdoc-json/reexport/glob_extern.rs @@ -3,8 +3,7 @@ #![no_core] #![feature(no_core)] -// @is "$.index[*][?(@.name=='mod1')].kind" \"module\" -// @is "$.index[*][?(@.name=='mod1')].inner.is_stripped" "true" +// @is "$.index[*][?(@.name=='mod1')].inner.module.is_stripped" "true" mod mod1 { extern "C" { // @set public_fn_id = "$.index[*][?(@.name=='public_fn')].id" @@ -12,12 +11,12 @@ mod mod1 { // @!has "$.index[*][?(@.name=='private_fn')]" fn private_fn(); } - // @ismany "$.index[*][?(@.name=='mod1')].inner.items[*]" $public_fn_id + // @ismany "$.index[*][?(@.name=='mod1')].inner.module.items[*]" $public_fn_id // @set mod1_id = "$.index[*][?(@.name=='mod1')].id" } -// @is "$.index[*][?(@.kind=='import')].inner.glob" true -// @is "$.index[*][?(@.kind=='import')].inner.id" $mod1_id -// @set use_id = "$.index[*][?(@.kind=='import')].id" -// @ismany "$.index[*][?(@.name=='glob_extern')].inner.items[*]" $use_id +// @is "$.index[*][?(@.inner.import)].inner.import.glob" true +// @is "$.index[*][?(@.inner.import)].inner.import.id" $mod1_id +// @set use_id = "$.index[*][?(@.inner.import)].id" +// @ismany "$.index[*][?(@.name=='glob_extern')].inner.module.items[*]" $use_id pub use mod1::*; diff --git a/tests/rustdoc-json/reexport/glob_private.rs b/tests/rustdoc-json/reexport/glob_private.rs index 3a83a20818b..ae4e87d23bc 100644 --- a/tests/rustdoc-json/reexport/glob_private.rs +++ b/tests/rustdoc-json/reexport/glob_private.rs @@ -3,11 +3,9 @@ #![no_core] #![feature(no_core)] -// @is "$.index[*][?(@.name=='mod1')].kind" \"module\" -// @is "$.index[*][?(@.name=='mod1')].inner.is_stripped" "true" +// @is "$.index[*][?(@.name=='mod1')].inner.module.is_stripped" "true" mod mod1 { - // @is "$.index[*][?(@.name=='mod2')].kind" \"module\" - // @is "$.index[*][?(@.name=='mod2')].inner.is_stripped" "true" + // @is "$.index[*][?(@.name=='mod2')].inner.module.is_stripped" "true" mod mod2 { // @set m2pub_id = "$.index[*][?(@.name=='Mod2Public')].id" pub struct Mod2Public; @@ -16,7 +14,9 @@ mod mod1 { struct Mod2Private; } - // @set mod2_use_id = "$.index[*][?(@.kind=='import' && @.inner.name=='mod2')].id" + // @set mod2_use_id = "$.index[*][?(@.docs=='Mod2 re-export')].id" + // @is "$.index[*][?(@.docs=='Mod2 re-export')].inner.import.name" \"mod2\" + /// Mod2 re-export pub use self::mod2::*; // @set m1pub_id = "$.index[*][?(@.name=='Mod1Public')].id" @@ -25,9 +25,11 @@ mod mod1 { struct Mod1Private; } -// @set mod1_use_id = "$.index[*][?(@.kind=='import' && @.inner.name=='mod1')].id" +// @set mod1_use_id = "$.index[*][?(@.docs=='Mod1 re-export')].id" +// @is "$.index[*][?(@.docs=='Mod1 re-export')].inner.import.name" \"mod1\" +/// Mod1 re-export pub use mod1::*; -// @ismany "$.index[*][?(@.name=='mod2')].inner.items[*]" $m2pub_id -// @ismany "$.index[*][?(@.name=='mod1')].inner.items[*]" $m1pub_id $mod2_use_id -// @ismany "$.index[*][?(@.name=='glob_private')].inner.items[*]" $mod1_use_id +// @ismany "$.index[*][?(@.name=='mod2')].inner.module.items[*]" $m2pub_id +// @ismany "$.index[*][?(@.name=='mod1')].inner.module.items[*]" $m1pub_id $mod2_use_id +// @ismany "$.index[*][?(@.name=='glob_private')].inner.module.items[*]" $mod1_use_id diff --git a/tests/rustdoc-json/reexport/in_root_and_mod.rs b/tests/rustdoc-json/reexport/in_root_and_mod.rs index 7b97ebf2129..f81445bd48d 100644 --- a/tests/rustdoc-json/reexport/in_root_and_mod.rs +++ b/tests/rustdoc-json/reexport/in_root_and_mod.rs @@ -7,10 +7,10 @@ mod foo { pub struct Foo; } -// @has "$.index[*][?(@.kind=='import' && @.inner.source=='foo::Foo')]" +// @has "$.index[*].inner[?(@.import.source=='foo::Foo')]" pub use foo::Foo; pub mod bar { - // @has "$.index[*][?(@.kind=='import' && @.inner.source=='crate::foo::Foo')]" + // @has "$.index[*].inner[?(@.import.source=='crate::foo::Foo')]" pub use crate::foo::Foo; } diff --git a/tests/rustdoc-json/reexport/in_root_and_mod_pub.rs b/tests/rustdoc-json/reexport/in_root_and_mod_pub.rs index f6d932d927b..c5c41b25f7e 100644 --- a/tests/rustdoc-json/reexport/in_root_and_mod_pub.rs +++ b/tests/rustdoc-json/reexport/in_root_and_mod_pub.rs @@ -3,18 +3,20 @@ pub mod foo { // @set bar_id = "$.index[*][?(@.name=='Bar')].id" - // @ismany "$.index[*][?(@.name=='foo')].inner.items[*]" $bar_id + // @ismany "$.index[*][?(@.name=='foo')].inner.module.items[*]" $bar_id pub struct Bar; } -// @set root_import_id = "$.index[*][?(@.inner.source=='foo::Bar')].id" -// @is "$.index[*][?(@.inner.source=='foo::Bar')].inner.id" $bar_id -// @has "$.index[*][?(@.name=='in_root_and_mod_pub')].inner.items[*]" $root_import_id +// @set root_import_id = "$.index[*][?(@.docs=='Outer re-export')].id" +// @is "$.index[*].inner[?(@.import.source=='foo::Bar')].import.id" $bar_id +// @has "$.index[*][?(@.name=='in_root_and_mod_pub')].inner.module.items[*]" $root_import_id +/// Outer re-export pub use foo::Bar; pub mod baz { - // @set baz_import_id = "$.index[*][?(@.inner.source=='crate::foo::Bar')].id" - // @is "$.index[*][?(@.inner.source=='crate::foo::Bar')].inner.id" $bar_id - // @ismany "$.index[*][?(@.name=='baz')].inner.items[*]" $baz_import_id + // @set baz_import_id = "$.index[*][?(@.docs=='Inner re-export')].id" + // @is "$.index[*].inner[?(@.import.source=='crate::foo::Bar')].import.id" $bar_id + // @ismany "$.index[*][?(@.name=='baz')].inner.module.items[*]" $baz_import_id + /// Inner re-export pub use crate::foo::Bar; } diff --git a/tests/rustdoc-json/reexport/macro.rs b/tests/rustdoc-json/reexport/macro.rs index b4882100f06..ac0632b98a9 100644 --- a/tests/rustdoc-json/reexport/macro.rs +++ b/tests/rustdoc-json/reexport/macro.rs @@ -9,7 +9,8 @@ macro_rules! repro { () => {}; } -// @set repro2_id = "$.index[*][?(@.inner.name=='repro2')].id" +// @set repro2_id = "$.index[*][?(@.docs=='Re-export')].id" +/// Re-export pub use crate::repro as repro2; -// @ismany "$.index[*][?(@.name=='macro')].inner.items[*]" $repro_id $repro2_id +// @ismany "$.index[*][?(@.name=='macro')].inner.module.items[*]" $repro_id $repro2_id diff --git a/tests/rustdoc-json/reexport/mod_not_included.rs b/tests/rustdoc-json/reexport/mod_not_included.rs index 7b7600ef20f..1c49f213d1a 100644 --- a/tests/rustdoc-json/reexport/mod_not_included.rs +++ b/tests/rustdoc-json/reexport/mod_not_included.rs @@ -9,6 +9,6 @@ mod m1 { pub use m1::x; -// @has "$.index[*][?(@.name=='x' && @.kind=='function')]" -// @has "$.index[*][?(@.kind=='import' && @.inner.name=='x')].inner.source" '"m1::x"' +// @has "$.index[*][?(@.name=='x' && @.inner.function)]" +// @has "$.index[*].inner[?(@.import.name=='x')].import.source" '"m1::x"' // @!has "$.index[*][?(@.name=='m1')]" diff --git a/tests/rustdoc-json/reexport/private_twice_one_inline.rs b/tests/rustdoc-json/reexport/private_twice_one_inline.rs index 687a3b2ac8b..8c8152bd16c 100644 --- a/tests/rustdoc-json/reexport/private_twice_one_inline.rs +++ b/tests/rustdoc-json/reexport/private_twice_one_inline.rs @@ -11,18 +11,18 @@ extern crate pub_struct as foo; #[doc(inline)] // @set crate_use_id = "$.index[*][?(@.docs=='Hack A')].id" -// @set foo_id = "$.index[*][?(@.docs=='Hack A')].inner.id" +// @set foo_id = "$.index[*][?(@.docs=='Hack A')].inner.import.id" /// Hack A pub use foo::Foo; // @set bar_id = "$.index[*][?(@.name=='bar')].id" pub mod bar { - // @is "$.index[*][?(@.docs=='Hack B')].inner.id" $foo_id + // @is "$.index[*][?(@.docs=='Hack B')].inner.import.id" $foo_id // @set bar_use_id = "$.index[*][?(@.docs=='Hack B')].id" - // @ismany "$.index[*][?(@.name=='bar')].inner.items[*]" $bar_use_id + // @ismany "$.index[*][?(@.name=='bar')].inner.module.items[*]" $bar_use_id /// Hack B pub use foo::Foo; } -// @ismany "$.index[*][?(@.kind=='import')].id" $crate_use_id $bar_use_id -// @ismany "$.index[*][?(@.name=='private_twice_one_inline')].inner.items[*]" $bar_id $crate_use_id +// @ismany "$.index[*][?(@.inner.import)].id" $crate_use_id $bar_use_id +// @ismany "$.index[*][?(@.name=='private_twice_one_inline')].inner.module.items[*]" $bar_id $crate_use_id diff --git a/tests/rustdoc-json/reexport/private_two_names.rs b/tests/rustdoc-json/reexport/private_two_names.rs index 9858538a9d0..cdcbf2a2bc8 100644 --- a/tests/rustdoc-json/reexport/private_two_names.rs +++ b/tests/rustdoc-json/reexport/private_two_names.rs @@ -8,15 +8,19 @@ // @!has "$.index[*][?(@.name=='style')]" mod style { - // @set color_struct_id = "$.index[*][?(@.kind=='struct' && @.name=='Color')].id" + // @set color_struct_id = "$.index[*][?(@.inner.struct && @.name=='Color')].id" pub struct Color; } -// @is "$.index[*][?(@.kind=='import' && @.inner.name=='Color')].inner.id" $color_struct_id -// @set color_export_id = "$.index[*][?(@.kind=='import' && @.inner.name=='Color')].id" +// @is "$.index[*][?(@.docs=='First re-export')].inner.import.id" $color_struct_id +// @is "$.index[*][?(@.docs=='First re-export')].inner.import.name" \"Color\" +// @set color_export_id = "$.index[*][?(@.docs=='First re-export')].id" +/// First re-export pub use style::Color; -// @is "$.index[*][?(@.kind=='import' && @.inner.name=='Colour')].inner.id" $color_struct_id -// @set colour_export_id = "$.index[*][?(@.kind=='import' && @.inner.name=='Colour')].id" +// @is "$.index[*][?(@.docs=='Second re-export')].inner.import.id" $color_struct_id +// @is "$.index[*][?(@.docs=='Second re-export')].inner.import.name" \"Colour\" +// @set colour_export_id = "$.index[*][?(@.docs=='Second re-export')].id" +/// Second re-export pub use style::Color as Colour; -// @ismany "$.index[*][?(@.name=='private_two_names')].inner.items[*]" $color_export_id $colour_export_id +// @ismany "$.index[*][?(@.name=='private_two_names')].inner.module.items[*]" $color_export_id $colour_export_id diff --git a/tests/rustdoc-json/reexport/reexport_method_from_private_module.rs b/tests/rustdoc-json/reexport/reexport_method_from_private_module.rs index 239b1a23b43..61075088af9 100644 --- a/tests/rustdoc-json/reexport/reexport_method_from_private_module.rs +++ b/tests/rustdoc-json/reexport/reexport_method_from_private_module.rs @@ -1,9 +1,9 @@ // Regression test for <https://github.com/rust-lang/rust/issues/102583>. // @set impl_S = "$.index[*][?(@.docs=='impl S')].id" -// @has "$.index[*][?(@.name=='S')].inner.impls[*]" $impl_S +// @has "$.index[*][?(@.name=='S')].inner.struct.impls[*]" $impl_S // @set is_present = "$.index[*][?(@.name=='is_present')].id" -// @is "$.index[*][?(@.docs=='impl S')].inner.items[*]" $is_present +// @is "$.index[*][?(@.docs=='impl S')].inner.impl.items[*]" $is_present // @!has "$.index[*][?(@.name=='hidden_impl')]" // @!has "$.index[*][?(@.name=='hidden_fn')]" diff --git a/tests/rustdoc-json/reexport/rename_private.rs b/tests/rustdoc-json/reexport/rename_private.rs index 8fd850f9b13..9114460231d 100644 --- a/tests/rustdoc-json/reexport/rename_private.rs +++ b/tests/rustdoc-json/reexport/rename_private.rs @@ -3,11 +3,11 @@ #![no_core] #![feature(no_core)] -// @!has "$.index[*][?(@.kind=='inner')]" +// @!has "$.index[*][?(@.name=='inner')]" mod inner { // @has "$.index[*][?(@.name=='Public')]" pub struct Public; } -// @is "$.index[*][?(@.kind=='import')].inner.name" \"NewName\" +// @is "$.index[*][?(@.inner.import)].inner.import.name" \"NewName\" pub use inner::Public as NewName; diff --git a/tests/rustdoc-json/reexport/rename_public.rs b/tests/rustdoc-json/reexport/rename_public.rs index e30907fe256..d0fd314bd8c 100644 --- a/tests/rustdoc-json/reexport/rename_public.rs +++ b/tests/rustdoc-json/reexport/rename_public.rs @@ -6,12 +6,13 @@ // @set inner_id = "$.index[*][?(@.name=='inner')].id" pub mod inner { // @set public_id = "$.index[*][?(@.name=='Public')].id" - // @ismany "$.index[*][?(@.name=='inner')].inner.items[*]" $public_id + // @ismany "$.index[*][?(@.name=='inner')].inner.module.items[*]" $public_id pub struct Public; } -// @set import_id = "$.index[*][?(@.inner.name=='NewName')].id" -// @!has "$.index[*][?(@.inner.name=='Public')]" -// @is "$.index[*][?(@.inner.name=='NewName')].inner.source" \"inner::Public\" +// @set import_id = "$.index[*][?(@.docs=='Re-export')].id" +// @!has "$.index[*].inner[?(@.import.name=='Public')]" +// @is "$.index[*].inner[?(@.import.name=='NewName')].import.source" \"inner::Public\" +/// Re-export pub use inner::Public as NewName; -// @ismany "$.index[*][?(@.name=='rename_public')].inner.items[*]" $inner_id $import_id +// @ismany "$.index[*][?(@.name=='rename_public')].inner.module.items[*]" $inner_id $import_id diff --git a/tests/rustdoc-json/reexport/same_name_different_types.rs b/tests/rustdoc-json/reexport/same_name_different_types.rs index 2314a4eb909..6a765b73301 100644 --- a/tests/rustdoc-json/reexport/same_name_different_types.rs +++ b/tests/rustdoc-json/reexport/same_name_different_types.rs @@ -16,10 +16,10 @@ pub mod nested { pub fn Foo() {} } -// @ismany "$.index[*][?(@.inner.name == 'Foo' && @.kind == 'import')].inner.id" $foo_fn $foo_struct -// @ismany "$.index[*][?(@.inner.name == 'Bar' && @.kind == 'import')].inner.id" $foo_fn $foo_struct +// @ismany "$.index[*].inner[?(@.import.name == 'Foo')].import.id" $foo_fn $foo_struct +// @ismany "$.index[*].inner[?(@.import.name == 'Bar')].import.id" $foo_fn $foo_struct -// @count "$.index[*][?(@.inner.name == 'Foo' && @.kind == 'import')]" 2 +// @count "$.index[*].inner[?(@.import.name == 'Foo')]" 2 pub use nested::Foo; -// @count "$.index[*][?(@.inner.name == 'Bar' && @.kind == 'import')]" 2 +// @count "$.index[*].inner[?(@.import.name == 'Bar')]" 2 pub use Foo as Bar; diff --git a/tests/rustdoc-json/reexport/same_type_reexported_more_than_once.rs b/tests/rustdoc-json/reexport/same_type_reexported_more_than_once.rs index 880dbdc4416..a00547dc348 100644 --- a/tests/rustdoc-json/reexport/same_type_reexported_more_than_once.rs +++ b/tests/rustdoc-json/reexport/same_type_reexported_more_than_once.rs @@ -11,11 +11,13 @@ mod inner { pub trait Trait {} } -// @set export_id = "$.index[*][?(@.inner.name=='Trait')].id" -// @is "$.index[*][?(@.inner.name=='Trait')].inner.id" $trait_id +// @set export_id = "$.index[*][?(@.docs=='First re-export')].id" +// @is "$.index[*].inner[?(@.import.name=='Trait')].import.id" $trait_id +/// First re-export pub use inner::Trait; -// @set reexport_id = "$.index[*][?(@.inner.name=='Reexport')].id" -// @is "$.index[*][?(@.inner.name=='Reexport')].inner.id" $trait_id +// @set reexport_id = "$.index[*][?(@.docs=='Second re-export')].id" +// @is "$.index[*].inner[?(@.import.name=='Reexport')].import.id" $trait_id +/// Second re-export pub use inner::Trait as Reexport; -// @ismany "$.index[*][?(@.name=='same_type_reexported_more_than_once')].inner.items[*]" $reexport_id $export_id +// @ismany "$.index[*][?(@.name=='same_type_reexported_more_than_once')].inner.module.items[*]" $reexport_id $export_id diff --git a/tests/rustdoc-json/reexport/simple_private.rs b/tests/rustdoc-json/reexport/simple_private.rs index d058ce0598d..462efee51bf 100644 --- a/tests/rustdoc-json/reexport/simple_private.rs +++ b/tests/rustdoc-json/reexport/simple_private.rs @@ -8,9 +8,9 @@ mod inner { pub struct Public; } -// @is "$.index[*][?(@.kind=='import')].inner.name" \"Public\" -// @is "$.index[*][?(@.kind=='import')].inner.id" $pub_id -// @set use_id = "$.index[*][?(@.kind=='import')].id" +// @is "$.index[*][?(@.inner.import)].inner.import.name" \"Public\" +// @is "$.index[*][?(@.inner.import)].inner.import.id" $pub_id +// @set use_id = "$.index[*][?(@.inner.import)].id" pub use inner::Public; -// @ismany "$.index[*][?(@.name=='simple_private')].inner.items[*]" $use_id +// @ismany "$.index[*][?(@.name=='simple_private')].inner.module.items[*]" $use_id diff --git a/tests/rustdoc-json/reexport/simple_public.rs b/tests/rustdoc-json/reexport/simple_public.rs index e64a0dcb769..1373f96f87f 100644 --- a/tests/rustdoc-json/reexport/simple_public.rs +++ b/tests/rustdoc-json/reexport/simple_public.rs @@ -7,12 +7,13 @@ pub mod inner { // @set public_id = "$.index[*][?(@.name=='Public')].id" - // @ismany "$.index[*][?(@.name=='inner')].inner.items[*]" $public_id + // @ismany "$.index[*][?(@.name=='inner')].inner.module.items[*]" $public_id pub struct Public; } -// @set import_id = "$.index[*][?(@.inner.name=='Public')].id" -// @is "$.index[*][?(@.inner.name=='Public')].inner.source" \"inner::Public\" +// @set import_id = "$.index[*][?(@.docs=='Outer')].id" +// @is "$.index[*][?(@.docs=='Outer')].inner.import.source" \"inner::Public\" +/// Outer pub use inner::Public; -// @ismany "$.index[*][?(@.name=='simple_public')].inner.items[*]" $import_id $inner_id +// @ismany "$.index[*][?(@.name=='simple_public')].inner.module.items[*]" $import_id $inner_id diff --git a/tests/rustdoc-json/return_private.rs b/tests/rustdoc-json/return_private.rs index a8d1fae30df..0ce81b5e59f 100644 --- a/tests/rustdoc-json/return_private.rs +++ b/tests/rustdoc-json/return_private.rs @@ -8,8 +8,8 @@ mod secret { pub struct Secret; } -// @is "$.index[*][?(@.name=='get_secret')].kind" \"function\" -// @is "$.index[*][?(@.name=='get_secret')].inner.decl.output.inner.name" \"secret::Secret\" +// @has "$.index[*][?(@.name=='get_secret')].inner.function" +// @is "$.index[*][?(@.name=='get_secret')].inner.function.decl.output.resolved_path.name" \"secret::Secret\" pub fn get_secret() -> secret::Secret { secret::Secret } diff --git a/tests/rustdoc-json/structs/plain_all_pub.rs b/tests/rustdoc-json/structs/plain_all_pub.rs index b86ab93c264..777260100d6 100644 --- a/tests/rustdoc-json/structs/plain_all_pub.rs +++ b/tests/rustdoc-json/structs/plain_all_pub.rs @@ -5,7 +5,7 @@ pub struct Demo { // @set x = "$.index[*][?(@.name=='x')].id" // @set y = "$.index[*][?(@.name=='y')].id" -// @is "$.index[*][?(@.name=='Demo')].inner.kind.plain.fields[0]" $x -// @is "$.index[*][?(@.name=='Demo')].inner.kind.plain.fields[1]" $y -// @count "$.index[*][?(@.name=='Demo')].inner.kind.plain.fields[*]" 2 -// @is "$.index[*][?(@.name=='Demo')].inner.kind.plain.fields_stripped" false +// @is "$.index[*][?(@.name=='Demo')].inner.struct.kind.plain.fields[0]" $x +// @is "$.index[*][?(@.name=='Demo')].inner.struct.kind.plain.fields[1]" $y +// @count "$.index[*][?(@.name=='Demo')].inner.struct.kind.plain.fields[*]" 2 +// @is "$.index[*][?(@.name=='Demo')].inner.struct.kind.plain.fields_stripped" false diff --git a/tests/rustdoc-json/structs/plain_doc_hidden.rs b/tests/rustdoc-json/structs/plain_doc_hidden.rs index 7800b55a481..1ff4489ef6b 100644 --- a/tests/rustdoc-json/structs/plain_doc_hidden.rs +++ b/tests/rustdoc-json/structs/plain_doc_hidden.rs @@ -6,6 +6,6 @@ pub struct Demo { // @set x = "$.index[*][?(@.name=='x')].id" // @!has "$.index[*][?(@.name=='y')].id" -// @is "$.index[*][?(@.name=='Demo')].inner.kind.plain.fields[0]" $x -// @count "$.index[*][?(@.name=='Demo')].inner.kind.plain.fields[*]" 1 -// @is "$.index[*][?(@.name=='Demo')].inner.kind.plain.fields_stripped" true +// @is "$.index[*][?(@.name=='Demo')].inner.struct.kind.plain.fields[0]" $x +// @count "$.index[*][?(@.name=='Demo')].inner.struct.kind.plain.fields[*]" 1 +// @is "$.index[*][?(@.name=='Demo')].inner.struct.kind.plain.fields_stripped" true diff --git a/tests/rustdoc-json/structs/plain_empty.rs b/tests/rustdoc-json/structs/plain_empty.rs index 1d01b8bc14a..c037c0251b9 100644 --- a/tests/rustdoc-json/structs/plain_empty.rs +++ b/tests/rustdoc-json/structs/plain_empty.rs @@ -1,5 +1,5 @@ // @is "$.index[*][?(@.name=='PlainEmpty')].visibility" \"public\" -// @is "$.index[*][?(@.name=='PlainEmpty')].kind" \"struct\" -// @is "$.index[*][?(@.name=='PlainEmpty')].inner.kind.plain.fields_stripped" false -// @is "$.index[*][?(@.name=='PlainEmpty')].inner.kind.plain.fields" [] +// @has "$.index[*][?(@.name=='PlainEmpty')].inner.struct" +// @is "$.index[*][?(@.name=='PlainEmpty')].inner.struct.kind.plain.fields_stripped" false +// @is "$.index[*][?(@.name=='PlainEmpty')].inner.struct.kind.plain.fields" [] pub struct PlainEmpty {} diff --git a/tests/rustdoc-json/structs/plain_pub_priv.rs b/tests/rustdoc-json/structs/plain_pub_priv.rs index 9b771224d97..ff061be62d1 100644 --- a/tests/rustdoc-json/structs/plain_pub_priv.rs +++ b/tests/rustdoc-json/structs/plain_pub_priv.rs @@ -4,6 +4,6 @@ pub struct Demo { } // @set x = "$.index[*][?(@.name=='x')].id" -// @is "$.index[*][?(@.name=='Demo')].inner.kind.plain.fields[0]" $x -// @count "$.index[*][?(@.name=='Demo')].inner.kind.plain.fields[*]" 1 -// @is "$.index[*][?(@.name=='Demo')].inner.kind.plain.fields_stripped" true +// @is "$.index[*][?(@.name=='Demo')].inner.struct.kind.plain.fields[0]" $x +// @count "$.index[*][?(@.name=='Demo')].inner.struct.kind.plain.fields[*]" 1 +// @is "$.index[*][?(@.name=='Demo')].inner.struct.kind.plain.fields_stripped" true diff --git a/tests/rustdoc-json/structs/tuple.rs b/tests/rustdoc-json/structs/tuple.rs index 6bdb753ee01..16ab95ed271 100644 --- a/tests/rustdoc-json/structs/tuple.rs +++ b/tests/rustdoc-json/structs/tuple.rs @@ -1,4 +1,4 @@ // @is "$.index[*][?(@.name=='Tuple')].visibility" \"public\" -// @is "$.index[*][?(@.name=='Tuple')].kind" \"struct\" -// @is "$.index[*][?(@.name=='Tuple')].inner.kind.tuple" '[null, null]' +// @has "$.index[*][?(@.name=='Tuple')].inner.struct" +// @is "$.index[*][?(@.name=='Tuple')].inner.struct.kind.tuple" '[null, null]' pub struct Tuple(u32, String); diff --git a/tests/rustdoc-json/structs/tuple_empty.rs b/tests/rustdoc-json/structs/tuple_empty.rs index 0ad6a89547f..4d4af8558bb 100644 --- a/tests/rustdoc-json/structs/tuple_empty.rs +++ b/tests/rustdoc-json/structs/tuple_empty.rs @@ -1,2 +1,2 @@ -// @is "$.index[*][?(@.name=='TupleUnit')].inner.kind.tuple" [] +// @is "$.index[*][?(@.name=='TupleUnit')].inner.struct.kind.tuple" [] pub struct TupleUnit(); diff --git a/tests/rustdoc-json/structs/tuple_pub_priv.rs b/tests/rustdoc-json/structs/tuple_pub_priv.rs index 9d5a1d1c8be..a669ba1dfcc 100644 --- a/tests/rustdoc-json/structs/tuple_pub_priv.rs +++ b/tests/rustdoc-json/structs/tuple_pub_priv.rs @@ -7,7 +7,7 @@ pub struct Demo( // @set field = "$.index[*][?(@.docs=='field')].id" -// @is "$.index[*][?(@.name=='Demo')].inner.kind.tuple[0]" null -// @is "$.index[*][?(@.name=='Demo')].inner.kind.tuple[1]" $field -// @is "$.index[*][?(@.name=='Demo')].inner.kind.tuple[2]" null -// @count "$.index[*][?(@.name=='Demo')].inner.kind.tuple[*]" 3 +// @is "$.index[*][?(@.name=='Demo')].inner.struct.kind.tuple[0]" null +// @is "$.index[*][?(@.name=='Demo')].inner.struct.kind.tuple[1]" $field +// @is "$.index[*][?(@.name=='Demo')].inner.struct.kind.tuple[2]" null +// @count "$.index[*][?(@.name=='Demo')].inner.struct.kind.tuple[*]" 3 diff --git a/tests/rustdoc-json/structs/unit.rs b/tests/rustdoc-json/structs/unit.rs index 26570971721..640d3fb7865 100644 --- a/tests/rustdoc-json/structs/unit.rs +++ b/tests/rustdoc-json/structs/unit.rs @@ -1,4 +1,4 @@ // @is "$.index[*][?(@.name=='Unit')].visibility" \"public\" -// @is "$.index[*][?(@.name=='Unit')].kind" \"struct\" -// @is "$.index[*][?(@.name=='Unit')].inner.kind" \"unit\" +// @has "$.index[*][?(@.name=='Unit')].inner.struct" +// @is "$.index[*][?(@.name=='Unit')].inner.struct.kind" \"unit\" pub struct Unit; diff --git a/tests/rustdoc-json/structs/with_generics.rs b/tests/rustdoc-json/structs/with_generics.rs index 00474800a0e..d721cbdbe25 100644 --- a/tests/rustdoc-json/structs/with_generics.rs +++ b/tests/rustdoc-json/structs/with_generics.rs @@ -1,13 +1,13 @@ use std::collections::HashMap; // @is "$.index[*][?(@.name=='WithGenerics')].visibility" \"public\" -// @is "$.index[*][?(@.name=='WithGenerics')].kind" \"struct\" -// @is "$.index[*][?(@.name=='WithGenerics')].inner.generics.params[0].name" \"T\" -// @is "$.index[*][?(@.name=='WithGenerics')].inner.generics.params[0].kind.type.bounds" [] -// @is "$.index[*][?(@.name=='WithGenerics')].inner.generics.params[1].name" \"U\" -// @is "$.index[*][?(@.name=='WithGenerics')].inner.generics.params[1].kind.type.bounds" [] -// @is "$.index[*][?(@.name=='WithGenerics')].inner.kind.plain.fields_stripped" true -// @is "$.index[*][?(@.name=='WithGenerics')].inner.kind.plain.fields" [] +// @has "$.index[*][?(@.name=='WithGenerics')].inner.struct" +// @is "$.index[*][?(@.name=='WithGenerics')].inner.struct.generics.params[0].name" \"T\" +// @is "$.index[*][?(@.name=='WithGenerics')].inner.struct.generics.params[0].kind.type.bounds" [] +// @is "$.index[*][?(@.name=='WithGenerics')].inner.struct.generics.params[1].name" \"U\" +// @is "$.index[*][?(@.name=='WithGenerics')].inner.struct.generics.params[1].kind.type.bounds" [] +// @is "$.index[*][?(@.name=='WithGenerics')].inner.struct.kind.plain.fields_stripped" true +// @is "$.index[*][?(@.name=='WithGenerics')].inner.struct.kind.plain.fields" [] pub struct WithGenerics<T, U> { stuff: Vec<T>, things: HashMap<U, U>, diff --git a/tests/rustdoc-json/structs/with_primitives.rs b/tests/rustdoc-json/structs/with_primitives.rs index 9c5a37f3957..e0285a9e688 100644 --- a/tests/rustdoc-json/structs/with_primitives.rs +++ b/tests/rustdoc-json/structs/with_primitives.rs @@ -1,9 +1,11 @@ +// ignore-tidy-linelength + // @is "$.index[*][?(@.name=='WithPrimitives')].visibility" \"public\" -// @is "$.index[*][?(@.name=='WithPrimitives')].kind" \"struct\" -// @is "$.index[*][?(@.name=='WithPrimitives')].inner.generics.params[0].name" \"\'a\" -// @is "$.index[*][?(@.name=='WithPrimitives')].inner.generics.params[0].kind.lifetime.outlives" [] -// @is "$.index[*][?(@.name=='WithPrimitives')].inner.kind.plain.fields_stripped" true -// @is "$.index[*][?(@.name=='WithPrimitives')].inner.kind.plain.fields" [] +// @has "$.index[*][?(@.name=='WithPrimitives')].inner.struct" +// @is "$.index[*][?(@.name=='WithPrimitives')].inner.struct.generics.params[0].name" \"\'a\" +// @is "$.index[*][?(@.name=='WithPrimitives')].inner.struct.generics.params[0].kind.lifetime.outlives" [] +// @is "$.index[*][?(@.name=='WithPrimitives')].inner.struct.kind.plain.fields_stripped" true +// @is "$.index[*][?(@.name=='WithPrimitives')].inner.struct.kind.plain.fields" [] pub struct WithPrimitives<'a> { num: u32, s: &'a str, diff --git a/tests/rustdoc-json/traits/has_body.rs b/tests/rustdoc-json/traits/has_body.rs index a57cb97d4a6..219e2d9bc54 100644 --- a/tests/rustdoc-json/traits/has_body.rs +++ b/tests/rustdoc-json/traits/has_body.rs @@ -1,21 +1,21 @@ // @has "$.index[*][?(@.name=='Foo')]" pub trait Foo { - // @is "$.index[*][?(@.name=='no_self')].inner.has_body" false + // @is "$.index[*][?(@.name=='no_self')].inner.function.has_body" false fn no_self(); - // @is "$.index[*][?(@.name=='move_self')].inner.has_body" false + // @is "$.index[*][?(@.name=='move_self')].inner.function.has_body" false fn move_self(self); - // @is "$.index[*][?(@.name=='ref_self')].inner.has_body" false + // @is "$.index[*][?(@.name=='ref_self')].inner.function.has_body" false fn ref_self(&self); - // @is "$.index[*][?(@.name=='no_self_def')].inner.has_body" true + // @is "$.index[*][?(@.name=='no_self_def')].inner.function.has_body" true fn no_self_def() {} - // @is "$.index[*][?(@.name=='move_self_def')].inner.has_body" true + // @is "$.index[*][?(@.name=='move_self_def')].inner.function.has_body" true fn move_self_def(self) {} - // @is "$.index[*][?(@.name=='ref_self_def')].inner.has_body" true + // @is "$.index[*][?(@.name=='ref_self_def')].inner.function.has_body" true fn ref_self_def(&self) {} } pub trait Bar: Clone { - // @is "$.index[*][?(@.name=='method')].inner.has_body" false + // @is "$.index[*][?(@.name=='method')].inner.function.has_body" false fn method(&self, param: usize); } diff --git a/tests/rustdoc-json/traits/implementors.rs b/tests/rustdoc-json/traits/implementors.rs index db3fe5df739..7d351ad61be 100644 --- a/tests/rustdoc-json/traits/implementors.rs +++ b/tests/rustdoc-json/traits/implementors.rs @@ -2,18 +2,18 @@ #![no_core] // @set wham = "$.index[*][?(@.name=='Wham')].id" -// @count "$.index[*][?(@.name=='Wham')].inner.implementations[*]" 1 -// @set gmWham = "$.index[*][?(@.name=='Wham')].inner.implementations[0]" +// @count "$.index[*][?(@.name=='Wham')].inner.trait.implementations[*]" 1 +// @set gmWham = "$.index[*][?(@.name=='Wham')].inner.trait.implementations[0]" pub trait Wham {} -// @count "$.index[*][?(@.name=='GeorgeMichael')].inner.impls[*]" 1 -// @is "$.index[*][?(@.name=='GeorgeMichael')].inner.impls[0]" $gmWham +// @count "$.index[*][?(@.name=='GeorgeMichael')].inner.struct.impls[*]" 1 +// @is "$.index[*][?(@.name=='GeorgeMichael')].inner.struct.impls[0]" $gmWham // @set gm = "$.index[*][?(@.name=='Wham')].id" // jsonpath_lib isnt expressive enough (for now) to get the "impl" item, so we // just check it isn't pointing to the type, but when you port to jsondocck-ng // check what the impl item is -// @!is "$.index[*][?(@.name=='Wham')].inner.implementations[0]" $gm +// @!is "$.index[*][?(@.name=='Wham')].inner.trait.implementations[0]" $gm pub struct GeorgeMichael {} impl Wham for GeorgeMichael {} diff --git a/tests/rustdoc-json/traits/supertrait.rs b/tests/rustdoc-json/traits/supertrait.rs index 4048fdd74b4..2123ac40427 100644 --- a/tests/rustdoc-json/traits/supertrait.rs +++ b/tests/rustdoc-json/traits/supertrait.rs @@ -8,19 +8,19 @@ pub trait Loud {} // @set very_loud_id = "$.index[*][?(@.name=='VeryLoud')].id" -// @count "$.index[*][?(@.name=='VeryLoud')].inner.bounds[*]" 1 -// @is "$.index[*][?(@.name=='VeryLoud')].inner.bounds[0].trait_bound.trait.id" $loud_id +// @count "$.index[*][?(@.name=='VeryLoud')].inner.trait.bounds[*]" 1 +// @is "$.index[*][?(@.name=='VeryLoud')].inner.trait.bounds[0].trait_bound.trait.id" $loud_id pub trait VeryLoud: Loud {} // @set sounds_good_id = "$.index[*][?(@.name=='SoundsGood')].id" pub trait SoundsGood {} -// @count "$.index[*][?(@.name=='MetalBand')].inner.bounds[*]" 2 -// @is "$.index[*][?(@.name=='MetalBand')].inner.bounds[0].trait_bound.trait.id" $very_loud_id -// @is "$.index[*][?(@.name=='MetalBand')].inner.bounds[1].trait_bound.trait.id" $sounds_good_id +// @count "$.index[*][?(@.name=='MetalBand')].inner.trait.bounds[*]" 2 +// @is "$.index[*][?(@.name=='MetalBand')].inner.trait.bounds[0].trait_bound.trait.id" $very_loud_id +// @is "$.index[*][?(@.name=='MetalBand')].inner.trait.bounds[1].trait_bound.trait.id" $sounds_good_id pub trait MetalBand: VeryLoud + SoundsGood {} -// @count "$.index[*][?(@.name=='DnabLatem')].inner.bounds[*]" 2 -// @is "$.index[*][?(@.name=='DnabLatem')].inner.bounds[1].trait_bound.trait.id" $very_loud_id -// @is "$.index[*][?(@.name=='DnabLatem')].inner.bounds[0].trait_bound.trait.id" $sounds_good_id +// @count "$.index[*][?(@.name=='DnabLatem')].inner.trait.bounds[*]" 2 +// @is "$.index[*][?(@.name=='DnabLatem')].inner.trait.bounds[1].trait_bound.trait.id" $very_loud_id +// @is "$.index[*][?(@.name=='DnabLatem')].inner.trait.bounds[0].trait_bound.trait.id" $sounds_good_id pub trait DnabLatem: SoundsGood + VeryLoud {} diff --git a/tests/rustdoc-json/traits/trait_alias.rs b/tests/rustdoc-json/traits/trait_alias.rs index 35db9296cdd..4fcc26f7de2 100644 --- a/tests/rustdoc-json/traits/trait_alias.rs +++ b/tests/rustdoc-json/traits/trait_alias.rs @@ -4,24 +4,24 @@ #![feature(trait_alias)] // @set Orig = "$.index[*][?(@.name == 'Orig')].id" -// @is "$.index[*][?(@.name == 'Orig')].kind" '"trait"' +// @has "$.index[*][?(@.name == 'Orig')].inner.trait" pub trait Orig<T> {} // @set Alias = "$.index[*][?(@.name == 'Alias')].id" -// @is "$.index[*][?(@.name == 'Alias')].kind" '"trait_alias"' -// @is "$.index[*][?(@.name == 'Alias')].inner.generics" '{"params": [], "where_predicates": []}' -// @count "$.index[*][?(@.name == 'Alias')].inner.params[*]" 1 -// @is "$.index[*][?(@.name == 'Alias')].inner.params[0].trait_bound.trait.id" $Orig -// @is "$.index[*][?(@.name == 'Alias')].inner.params[0].trait_bound.trait.args.angle_bracketed.args[0].type.inner" '"i32"' +// @has "$.index[*][?(@.name == 'Alias')].inner.trait_alias" +// @is "$.index[*][?(@.name == 'Alias')].inner.trait_alias.generics" '{"params": [], "where_predicates": []}' +// @count "$.index[*][?(@.name == 'Alias')].inner.trait_alias.params[*]" 1 +// @is "$.index[*][?(@.name == 'Alias')].inner.trait_alias.params[0].trait_bound.trait.id" $Orig +// @is "$.index[*][?(@.name == 'Alias')].inner.trait_alias.params[0].trait_bound.trait.args.angle_bracketed.args[0].type.primitive" '"i32"' pub trait Alias = Orig<i32>; pub struct Struct; impl Orig<i32> for Struct {} -// @is "$.index[*][?(@.name=='takes_alias')].inner.decl.inputs[0][1].kind" '"impl_trait"' -// @is "$.index[*][?(@.name=='takes_alias')].inner.decl.inputs[0][1].inner[0].trait_bound.trait.id" $Alias -// @is "$.index[*][?(@.name=='takes_alias')].inner.generics.params[0].kind.type.bounds[0].trait_bound.trait.id" $Alias +// @has "$.index[*][?(@.name=='takes_alias')].inner.function.decl.inputs[0][1].impl_trait" +// @is "$.index[*][?(@.name=='takes_alias')].inner.function.decl.inputs[0][1].impl_trait[0].trait_bound.trait.id" $Alias +// @is "$.index[*][?(@.name=='takes_alias')].inner.function.generics.params[0].kind.type.bounds[0].trait_bound.trait.id" $Alias pub fn takes_alias(_: impl Alias) {} // FIXME: Should the trait be mentioned in both the decl and generics? diff --git a/tests/rustdoc-json/type/dyn.rs b/tests/rustdoc-json/type/dyn.rs index eaf249252e3..e5ee60fad59 100644 --- a/tests/rustdoc-json/type/dyn.rs +++ b/tests/rustdoc-json/type/dyn.rs @@ -1,46 +1,46 @@ // ignore-tidy-linelength use std::fmt::Debug; -// @count "$.index[*][?(@.name=='dyn')].inner.items[*]" 3 +// @count "$.index[*][?(@.name=='dyn')].inner.module.items[*]" 3 // @set sync_int_gen = "$.index[*][?(@.name=='SyncIntGen')].id" // @set ref_fn = "$.index[*][?(@.name=='RefFn')].id" // @set weird_order = "$.index[*][?(@.name=='WeirdOrder')].id" -// @ismany "$.index[*][?(@.name=='dyn')].inner.items[*]" $sync_int_gen $ref_fn $weird_order +// @ismany "$.index[*][?(@.name=='dyn')].inner.module.items[*]" $sync_int_gen $ref_fn $weird_order -// @is "$.index[*][?(@.name=='SyncIntGen')].kind" \"typedef\" -// @is "$.index[*][?(@.name=='SyncIntGen')].inner.generics" '{"params": [], "where_predicates": []}' -// @is "$.index[*][?(@.name=='SyncIntGen')].inner.type.kind" \"resolved_path\" -// @is "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.name" \"Box\" -// @is "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.bindings" [] -// @count "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args" 1 -// @is "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.kind" \"dyn_trait\" -// @is "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.lifetime" \"\'static\" -// @count "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[*]" 3 -// @is "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[0].generic_params" [] -// @is "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[1].generic_params" [] -// @is "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[2].generic_params" [] -// @is "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[0].trait.name" '"Fn"' -// @is "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[1].trait.name" '"Send"' -// @is "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[2].trait.name" '"Sync"' -// @is "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[0].trait.args" '{"parenthesized": {"inputs": [],"output": {"inner": "i32","kind": "primitive"}}}' +// @has "$.index[*][?(@.name=='SyncIntGen')].inner.typedef" +// @is "$.index[*][?(@.name=='SyncIntGen')].inner.typedef.generics" '{"params": [], "where_predicates": []}' +// @has "$.index[*][?(@.name=='SyncIntGen')].inner.typedef.type.resolved_path" +// @is "$.index[*][?(@.name=='SyncIntGen')].inner.typedef.type.resolved_path.name" \"Box\" +// @is "$.index[*][?(@.name=='SyncIntGen')].inner.typedef.type.resolved_path.args.angle_bracketed.bindings" [] +// @count "$.index[*][?(@.name=='SyncIntGen')].inner.typedef.type.resolved_path.args.angle_bracketed.args" 1 +// @has "$.index[*][?(@.name=='SyncIntGen')].inner.typedef.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait" +// @is "$.index[*][?(@.name=='SyncIntGen')].inner.typedef.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.lifetime" \"\'static\" +// @count "$.index[*][?(@.name=='SyncIntGen')].inner.typedef.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[*]" 3 +// @is "$.index[*][?(@.name=='SyncIntGen')].inner.typedef.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[0].generic_params" [] +// @is "$.index[*][?(@.name=='SyncIntGen')].inner.typedef.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[1].generic_params" [] +// @is "$.index[*][?(@.name=='SyncIntGen')].inner.typedef.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[2].generic_params" [] +// @is "$.index[*][?(@.name=='SyncIntGen')].inner.typedef.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[0].trait.name" '"Fn"' +// @is "$.index[*][?(@.name=='SyncIntGen')].inner.typedef.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[1].trait.name" '"Send"' +// @is "$.index[*][?(@.name=='SyncIntGen')].inner.typedef.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[2].trait.name" '"Sync"' +// @is "$.index[*][?(@.name=='SyncIntGen')].inner.typedef.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[0].trait.args" '{"parenthesized": {"inputs": [],"output": {"primitive": "i32"}}}' pub type SyncIntGen = Box<dyn Fn() -> i32 + Send + Sync + 'static>; -// @is "$.index[*][?(@.name=='RefFn')].kind" \"typedef\" -// @is "$.index[*][?(@.name=='RefFn')].inner.generics" '{"params": [{"kind": {"lifetime": {"outlives": []}},"name": "'\''a"}],"where_predicates": []}' -// @is "$.index[*][?(@.name=='RefFn')].inner.type.kind" '"borrowed_ref"' -// @is "$.index[*][?(@.name=='RefFn')].inner.type.inner.mutable" 'false' -// @is "$.index[*][?(@.name=='RefFn')].inner.type.inner.lifetime" "\"'a\"" -// @is "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.kind" '"dyn_trait"' -// @is "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.inner.lifetime" null -// @count "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.inner.traits[*]" 1 -// @is "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.inner.traits[0].generic_params" '[{"kind": {"lifetime": {"outlives": []}},"name": "'\''b"}]' -// @is "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.inner.traits[0].trait.name" '"Fn"' -// @is "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.inner.traits[0].trait.args.parenthesized.inputs[0].kind" '"borrowed_ref"' -// @is "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.inner.traits[0].trait.args.parenthesized.inputs[0].inner.lifetime" "\"'b\"" -// @is "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.inner.traits[0].trait.args.parenthesized.output.kind" '"borrowed_ref"' -// @is "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.inner.traits[0].trait.args.parenthesized.output.inner.lifetime" "\"'b\"" +// @has "$.index[*][?(@.name=='RefFn')].inner.typedef" +// @is "$.index[*][?(@.name=='RefFn')].inner.typedef.generics" '{"params": [{"kind": {"lifetime": {"outlives": []}},"name": "'\''a"}],"where_predicates": []}' +// @has "$.index[*][?(@.name=='RefFn')].inner.typedef.type.borrowed_ref" +// @is "$.index[*][?(@.name=='RefFn')].inner.typedef.type.borrowed_ref.mutable" 'false' +// @is "$.index[*][?(@.name=='RefFn')].inner.typedef.type.borrowed_ref.lifetime" "\"'a\"" +// @has "$.index[*][?(@.name=='RefFn')].inner.typedef.type.borrowed_ref.type.dyn_trait" +// @is "$.index[*][?(@.name=='RefFn')].inner.typedef.type.borrowed_ref.type.dyn_trait.lifetime" null +// @count "$.index[*][?(@.name=='RefFn')].inner.typedef.type.borrowed_ref.type.dyn_trait.traits[*]" 1 +// @is "$.index[*][?(@.name=='RefFn')].inner.typedef.type.borrowed_ref.type.dyn_trait.traits[0].generic_params" '[{"kind": {"lifetime": {"outlives": []}},"name": "'\''b"}]' +// @is "$.index[*][?(@.name=='RefFn')].inner.typedef.type.borrowed_ref.type.dyn_trait.traits[0].trait.name" '"Fn"' +// @has "$.index[*][?(@.name=='RefFn')].inner.typedef.type.borrowed_ref.type.dyn_trait.traits[0].trait.args.parenthesized.inputs[0].borrowed_ref" +// @is "$.index[*][?(@.name=='RefFn')].inner.typedef.type.borrowed_ref.type.dyn_trait.traits[0].trait.args.parenthesized.inputs[0].borrowed_ref.lifetime" "\"'b\"" +// @has "$.index[*][?(@.name=='RefFn')].inner.typedef.type.borrowed_ref.type.dyn_trait.traits[0].trait.args.parenthesized.output.borrowed_ref" +// @is "$.index[*][?(@.name=='RefFn')].inner.typedef.type.borrowed_ref.type.dyn_trait.traits[0].trait.args.parenthesized.output.borrowed_ref.lifetime" "\"'b\"" pub type RefFn<'a> = &'a dyn for<'b> Fn(&'b i32) -> &'b i32; -// @is "$.index[*][?(@.name=='WeirdOrder')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[0].trait.name" '"Send"' -// @is "$.index[*][?(@.name=='WeirdOrder')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[1].trait.name" '"Debug"' +// @is "$.index[*][?(@.name=='WeirdOrder')].inner.typedef.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[0].trait.name" '"Send"' +// @is "$.index[*][?(@.name=='WeirdOrder')].inner.typedef.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[1].trait.name" '"Debug"' pub type WeirdOrder = Box<dyn Send + Debug>; diff --git a/tests/rustdoc-json/type/extern.rs b/tests/rustdoc-json/type/extern.rs index d287d5ebec5..c30146ce9e0 100644 --- a/tests/rustdoc-json/type/extern.rs +++ b/tests/rustdoc-json/type/extern.rs @@ -6,5 +6,4 @@ extern { } // @is "$.index[*][?(@.docs=='No inner information')].name" '"Foo"' -// @is "$.index[*][?(@.docs=='No inner information')].kind" '"foreign_type"' -// @!has "$.index[*][?(@.docs=='No inner information')].inner" +// @is "$.index[*][?(@.docs=='No inner information')].inner" \"foreign_type\" diff --git a/tests/rustdoc-json/type/fn_lifetime.rs b/tests/rustdoc-json/type/fn_lifetime.rs index d7216ec7675..424c5dce273 100644 --- a/tests/rustdoc-json/type/fn_lifetime.rs +++ b/tests/rustdoc-json/type/fn_lifetime.rs @@ -1,27 +1,26 @@ // ignore-tidy-linelength -// @is "$.index[*][?(@.name=='GenericFn')].kind" \"typedef\" +// @has "$.index[*][?(@.name=='GenericFn')].inner.typedef" -// @ismany "$.index[*][?(@.name=='GenericFn')].inner.generics.params[*].name" \"\'a\" -// @has "$.index[*][?(@.name=='GenericFn')].inner.generics.params[*].kind.lifetime" -// @count "$.index[*][?(@.name=='GenericFn')].inner.generics.params[*].kind.lifetime.outlives[*]" 0 -// @count "$.index[*][?(@.name=='GenericFn')].inner.generics.where_predicates[*]" 0 -// @is "$.index[*][?(@.name=='GenericFn')].inner.type.kind" \"function_pointer\" -// @count "$.index[*][?(@.name=='GenericFn')].inner.type.inner.generic_params[*]" 0 -// @count "$.index[*][?(@.name=='GenericFn')].inner.type.inner.decl.inputs[*]" 1 -// @is "$.index[*][?(@.name=='GenericFn')].inner.type.inner.decl.inputs[*][1].inner.lifetime" \"\'a\" -// @is "$.index[*][?(@.name=='GenericFn')].inner.type.inner.decl.output.inner.lifetime" \"\'a\" +// @ismany "$.index[*][?(@.name=='GenericFn')].inner.typedef.generics.params[*].name" \"\'a\" +// @has "$.index[*][?(@.name=='GenericFn')].inner.typedef.generics.params[*].kind.lifetime" +// @count "$.index[*][?(@.name=='GenericFn')].inner.typedef.generics.params[*].kind.lifetime.outlives[*]" 0 +// @count "$.index[*][?(@.name=='GenericFn')].inner.typedef.generics.where_predicates[*]" 0 +// @count "$.index[*][?(@.name=='GenericFn')].inner.typedef.type.function_pointer.generic_params[*]" 0 +// @count "$.index[*][?(@.name=='GenericFn')].inner.typedef.type.function_pointer.decl.inputs[*]" 1 +// @is "$.index[*][?(@.name=='GenericFn')].inner.typedef.type.function_pointer.decl.inputs[*][1].borrowed_ref.lifetime" \"\'a\" +// @is "$.index[*][?(@.name=='GenericFn')].inner.typedef.type.function_pointer.decl.output.borrowed_ref.lifetime" \"\'a\" pub type GenericFn<'a> = fn(&'a i32) -> &'a i32; -// @is "$.index[*][?(@.name=='ForAll')].kind" \"typedef\" -// @count "$.index[*][?(@.name=='ForAll')].inner.generics.params[*]" 0 -// @count "$.index[*][?(@.name=='ForAll')].inner.generics.where_predicates[*]" 0 -// @count "$.index[*][?(@.name=='ForAll')].inner.type.inner.generic_params[*]" 1 -// @is "$.index[*][?(@.name=='ForAll')].inner.type.inner.generic_params[*].name" \"\'a\" -// @has "$.index[*][?(@.name=='ForAll')].inner.type.inner.generic_params[*].kind.lifetime" -// @count "$.index[*][?(@.name=='ForAll')].inner.type.inner.generic_params[*].kind.lifetime.outlives[*]" 0 -// @count "$.index[*][?(@.name=='ForAll')].inner.type.inner.decl.inputs[*]" 1 -// @is "$.index[*][?(@.name=='ForAll')].inner.type.inner.decl.inputs[*][1].inner.lifetime" \"\'a\" -// @is "$.index[*][?(@.name=='ForAll')].inner.type.inner.decl.output.inner.lifetime" \"\'a\" +// @has "$.index[*][?(@.name=='ForAll')].inner.typedef" +// @count "$.index[*][?(@.name=='ForAll')].inner.typedef.generics.params[*]" 0 +// @count "$.index[*][?(@.name=='ForAll')].inner.typedef.generics.where_predicates[*]" 0 +// @count "$.index[*][?(@.name=='ForAll')].inner.typedef.type.function_pointer.generic_params[*]" 1 +// @is "$.index[*][?(@.name=='ForAll')].inner.typedef.type.function_pointer.generic_params[*].name" \"\'a\" +// @has "$.index[*][?(@.name=='ForAll')].inner.typedef.type.function_pointer.generic_params[*].kind.lifetime" +// @count "$.index[*][?(@.name=='ForAll')].inner.typedef.type.function_pointer.generic_params[*].kind.lifetime.outlives[*]" 0 +// @count "$.index[*][?(@.name=='ForAll')].inner.typedef.type.function_pointer.decl.inputs[*]" 1 +// @is "$.index[*][?(@.name=='ForAll')].inner.typedef.type.function_pointer.decl.inputs[*][1].borrowed_ref.lifetime" \"\'a\" +// @is "$.index[*][?(@.name=='ForAll')].inner.typedef.type.function_pointer.decl.output.borrowed_ref.lifetime" \"\'a\" pub type ForAll = for<'a> fn(&'a i32) -> &'a i32; diff --git a/tests/rustdoc-json/type/generic_default.rs b/tests/rustdoc-json/type/generic_default.rs index 9c6d4540bfb..b4642874532 100644 --- a/tests/rustdoc-json/type/generic_default.rs +++ b/tests/rustdoc-json/type/generic_default.rs @@ -9,25 +9,25 @@ pub enum Result<T, E> { // @set my_error = "$.index[*][?(@.name=='MyError')].id" pub struct MyError {} -// @is "$.index[*][?(@.name=='MyResult')].kind" \"typedef\" -// @count "$.index[*][?(@.name=='MyResult')].inner.generics.where_predicates[*]" 0 -// @count "$.index[*][?(@.name=='MyResult')].inner.generics.params[*]" 2 -// @is "$.index[*][?(@.name=='MyResult')].inner.generics.params[0].name" \"T\" -// @is "$.index[*][?(@.name=='MyResult')].inner.generics.params[1].name" \"E\" -// @has "$.index[*][?(@.name=='MyResult')].inner.generics.params[0].kind.type" -// @has "$.index[*][?(@.name=='MyResult')].inner.generics.params[1].kind.type" -// @count "$.index[*][?(@.name=='MyResult')].inner.generics.params[0].kind.type.bounds[*]" 0 -// @count "$.index[*][?(@.name=='MyResult')].inner.generics.params[1].kind.type.bounds[*]" 0 -// @is "$.index[*][?(@.name=='MyResult')].inner.generics.params[0].kind.type.default" null -// @is "$.index[*][?(@.name=='MyResult')].inner.generics.params[1].kind.type.default.kind" \"resolved_path\" -// @is "$.index[*][?(@.name=='MyResult')].inner.generics.params[1].kind.type.default.inner.id" $my_error -// @is "$.index[*][?(@.name=='MyResult')].inner.generics.params[1].kind.type.default.inner.name" \"MyError\" -// @is "$.index[*][?(@.name=='MyResult')].inner.type.kind" \"resolved_path\" -// @is "$.index[*][?(@.name=='MyResult')].inner.type.inner.id" $result -// @is "$.index[*][?(@.name=='MyResult')].inner.type.inner.name" \"Result\" -// @is "$.index[*][?(@.name=='MyResult')].inner.type.inner.args.angle_bracketed.bindings" [] -// @is "$.index[*][?(@.name=='MyResult')].inner.type.inner.args.angle_bracketed.args[0].type.kind" \"generic\" -// @is "$.index[*][?(@.name=='MyResult')].inner.type.inner.args.angle_bracketed.args[1].type.kind" \"generic\" -// @is "$.index[*][?(@.name=='MyResult')].inner.type.inner.args.angle_bracketed.args[0].type.inner" \"T\" -// @is "$.index[*][?(@.name=='MyResult')].inner.type.inner.args.angle_bracketed.args[1].type.inner" \"E\" +// @has "$.index[*][?(@.name=='MyResult')].inner.typedef" +// @count "$.index[*][?(@.name=='MyResult')].inner.typedef.generics.where_predicates[*]" 0 +// @count "$.index[*][?(@.name=='MyResult')].inner.typedef.generics.params[*]" 2 +// @is "$.index[*][?(@.name=='MyResult')].inner.typedef.generics.params[0].name" \"T\" +// @is "$.index[*][?(@.name=='MyResult')].inner.typedef.generics.params[1].name" \"E\" +// @has "$.index[*][?(@.name=='MyResult')].inner.typedef.generics.params[0].kind.type" +// @has "$.index[*][?(@.name=='MyResult')].inner.typedef.generics.params[1].kind.type" +// @count "$.index[*][?(@.name=='MyResult')].inner.typedef.generics.params[0].kind.type.bounds[*]" 0 +// @count "$.index[*][?(@.name=='MyResult')].inner.typedef.generics.params[1].kind.type.bounds[*]" 0 +// @is "$.index[*][?(@.name=='MyResult')].inner.typedef.generics.params[0].kind.type.default" null +// @has "$.index[*][?(@.name=='MyResult')].inner.typedef.generics.params[1].kind.type.default.resolved_path" +// @is "$.index[*][?(@.name=='MyResult')].inner.typedef.generics.params[1].kind.type.default.resolved_path.id" $my_error +// @is "$.index[*][?(@.name=='MyResult')].inner.typedef.generics.params[1].kind.type.default.resolved_path.name" \"MyError\" +// @has "$.index[*][?(@.name=='MyResult')].inner.typedef.type.resolved_path" +// @is "$.index[*][?(@.name=='MyResult')].inner.typedef.type.resolved_path.id" $result +// @is "$.index[*][?(@.name=='MyResult')].inner.typedef.type.resolved_path.name" \"Result\" +// @is "$.index[*][?(@.name=='MyResult')].inner.typedef.type.resolved_path.args.angle_bracketed.bindings" [] +// @has "$.index[*][?(@.name=='MyResult')].inner.typedef.type.resolved_path.args.angle_bracketed.args[0].type.generic" +// @has "$.index[*][?(@.name=='MyResult')].inner.typedef.type.resolved_path.args.angle_bracketed.args[1].type.generic" +// @is "$.index[*][?(@.name=='MyResult')].inner.typedef.type.resolved_path.args.angle_bracketed.args[0].type.generic" \"T\" +// @is "$.index[*][?(@.name=='MyResult')].inner.typedef.type.resolved_path.args.angle_bracketed.args[1].type.generic" \"E\" pub type MyResult<T, E = MyError> = Result<T, E>; diff --git a/tests/rustdoc-json/type/hrtb.rs b/tests/rustdoc-json/type/hrtb.rs index 2c4ee00d468..f7ac878ceaa 100644 --- a/tests/rustdoc-json/type/hrtb.rs +++ b/tests/rustdoc-json/type/hrtb.rs @@ -1,7 +1,7 @@ // ignore-tidy-linelength -// @is "$.index[*][?(@.name=='genfn')].inner.generics.where_predicates[0].bound_predicate.type" '{"inner": "F","kind": "generic"}' -// @is "$.index[*][?(@.name=='genfn')].inner.generics.where_predicates[0].bound_predicate.generic_params" '[{"kind": {"lifetime": {"outlives": []}},"name": "'\''a"},{"kind": {"lifetime": {"outlives": []}},"name": "'\''b"}]' +// @is "$.index[*][?(@.name=='genfn')].inner.function.generics.where_predicates[0].bound_predicate.type" '{"generic": "F"}' +// @is "$.index[*][?(@.name=='genfn')].inner.function.generics.where_predicates[0].bound_predicate.generic_params" '[{"kind": {"lifetime": {"outlives": []}},"name": "'\''a"},{"kind": {"lifetime": {"outlives": []}},"name": "'\''b"}]' pub fn genfn<F>(f: F) where for<'a, 'b> F: Fn(&'a i32, &'b i32), @@ -10,14 +10,12 @@ where f(&zero, &zero); } -// @is "$.index[*][?(@.name=='dynfn')].inner.generics" '{"params": [], "where_predicates": []}' -// @is "$.index[*][?(@.name=='dynfn')].inner.generics" '{"params": [], "where_predicates": []}' -// @is "$.index[*][?(@.name=='dynfn')].inner.decl.inputs[0][1].kind" '"borrowed_ref"' -// @is "$.index[*][?(@.name=='dynfn')].inner.decl.inputs[0][1].inner.type.kind" '"dyn_trait"' -// @is "$.index[*][?(@.name=='dynfn')].inner.decl.inputs[0][1].inner.type.inner.lifetime" null -// @count "$.index[*][?(@.name=='dynfn')].inner.decl.inputs[0][1].inner.type.inner.traits[*]" 1 -// @is "$.index[*][?(@.name=='dynfn')].inner.decl.inputs[0][1].inner.type.inner.traits[0].generic_params" '[{"kind": {"lifetime": {"outlives": []}},"name": "'\''a"},{"kind": {"lifetime": {"outlives": []}},"name": "'\''b"}]' -// @is "$.index[*][?(@.name=='dynfn')].inner.decl.inputs[0][1].inner.type.inner.traits[0].trait.name" '"Fn"' +// @is "$.index[*][?(@.name=='dynfn')].inner.function.generics" '{"params": [], "where_predicates": []}' +// @is "$.index[*][?(@.name=='dynfn')].inner.function.generics" '{"params": [], "where_predicates": []}' +// @is "$.index[*][?(@.name=='dynfn')].inner.function.decl.inputs[0][1].borrowed_ref.type.dyn_trait.lifetime" null +// @count "$.index[*][?(@.name=='dynfn')].inner.function.decl.inputs[0][1].borrowed_ref.type.dyn_trait.traits[*]" 1 +// @is "$.index[*][?(@.name=='dynfn')].inner.function.decl.inputs[0][1].borrowed_ref.type.dyn_trait.traits[0].generic_params" '[{"kind": {"lifetime": {"outlives": []}},"name": "'\''a"},{"kind": {"lifetime": {"outlives": []}},"name": "'\''b"}]' +// @is "$.index[*][?(@.name=='dynfn')].inner.function.decl.inputs[0][1].borrowed_ref.type.dyn_trait.traits[0].trait.name" '"Fn"' pub fn dynfn(f: &dyn for<'a, 'b> Fn(&'a i32, &'b i32)) { let zero = 0; f(&zero, &zero); diff --git a/tests/rustdoc-json/type/inherent_associated_type.rs b/tests/rustdoc-json/type/inherent_associated_type.rs index ed63def93df..64c6c53ce47 100644 --- a/tests/rustdoc-json/type/inherent_associated_type.rs +++ b/tests/rustdoc-json/type/inherent_associated_type.rs @@ -12,11 +12,9 @@ pub struct Owner; pub fn create() -> Owner::Metadata { OwnerMetadata } -// @is '$.index[*][?(@.name=="create")].inner.decl.output.kind' '"qualified_path"' -// @is '$.index[*][?(@.name=="create")].inner.decl.output.inner.name' '"Metadata"' -// @is '$.index[*][?(@.name=="create")].inner.decl.output.inner.trait' null -// @is '$.index[*][?(@.name=="create")].inner.decl.output.inner.self_type.kind' '"resolved_path"' -// @is '$.index[*][?(@.name=="create")].inner.decl.output.inner.self_type.inner.id' $Owner +// @is '$.index[*][?(@.name=="create")].inner.function.decl.output.qualified_path.name' '"Metadata"' +// @is '$.index[*][?(@.name=="create")].inner.function.decl.output.qualified_path.trait' null +// @is '$.index[*][?(@.name=="create")].inner.function.decl.output.qualified_path.self_type.resolved_path.id' $Owner /// impl impl Owner { @@ -24,6 +22,5 @@ impl Owner { pub type Metadata = OwnerMetadata; } // @set iat = '$.index[*][?(@.docs=="iat")].id' -// @is '$.index[*][?(@.docs=="impl")].inner.items[*]' $iat -// @is '$.index[*][?(@.docs=="iat")].kind' '"assoc_type"' -// @is '$.index[*][?(@.docs=="iat")].inner.default.inner.id' $OwnerMetadata +// @is '$.index[*][?(@.docs=="impl")].inner.impl.items[*]' $iat +// @is '$.index[*][?(@.docs=="iat")].inner.assoc_type.default.resolved_path.id' $OwnerMetadata diff --git a/tests/rustdoc-json/type/inherent_associated_type_bound.rs b/tests/rustdoc-json/type/inherent_associated_type_bound.rs index a089600b692..2e9b13d0cac 100644 --- a/tests/rustdoc-json/type/inherent_associated_type_bound.rs +++ b/tests/rustdoc-json/type/inherent_associated_type_bound.rs @@ -5,14 +5,12 @@ // @set Carrier = '$.index[*][?(@.name=="Carrier")].id' pub struct Carrier<'a>(&'a ()); -// @is '$.index[*][?(@.name=="User")].inner.type.kind' '"function_pointer"' -// @is '$.index[*][?(@.name=="User")].inner.type.inner.generic_params[*].name' \""'b"\" -// @is '$.index[*][?(@.name=="User")].inner.type.inner.decl.inputs[0][1].kind' '"qualified_path"' -// @is '$.index[*][?(@.name=="User")].inner.type.inner.decl.inputs[0][1].inner.self_type.inner.id' $Carrier -// @is '$.index[*][?(@.name=="User")].inner.type.inner.decl.inputs[0][1].inner.self_type.inner.args.angle_bracketed.args[0].lifetime' \""'b"\" -// @is '$.index[*][?(@.name=="User")].inner.type.inner.decl.inputs[0][1].inner.name' '"Focus"' -// @is '$.index[*][?(@.name=="User")].inner.type.inner.decl.inputs[0][1].inner.trait' null -// @is '$.index[*][?(@.name=="User")].inner.type.inner.decl.inputs[0][1].inner.args.angle_bracketed.args[0].type.inner' '"i32"' +// @is '$.index[*][?(@.name=="User")].inner.typedef.type.function_pointer.generic_params[*].name' \""'b"\" +// @is '$.index[*][?(@.name=="User")].inner.typedef.type.function_pointer.decl.inputs[0][1].qualified_path.self_type.resolved_path.id' $Carrier +// @is '$.index[*][?(@.name=="User")].inner.typedef.type.function_pointer.decl.inputs[0][1].qualified_path.self_type.resolved_path.args.angle_bracketed.args[0].lifetime' \""'b"\" +// @is '$.index[*][?(@.name=="User")].inner.typedef.type.function_pointer.decl.inputs[0][1].qualified_path.name' '"Focus"' +// @is '$.index[*][?(@.name=="User")].inner.typedef.type.function_pointer.decl.inputs[0][1].qualified_path.trait' null +// @is '$.index[*][?(@.name=="User")].inner.typedef.type.function_pointer.decl.inputs[0][1].qualified_path.args.angle_bracketed.args[0].type.primitive' '"i32"' pub type User = for<'b> fn(Carrier<'b>::Focus<i32>); diff --git a/tests/rustdoc-json/type/inherent_associated_type_projections.rs b/tests/rustdoc-json/type/inherent_associated_type_projections.rs index 30c68bfe56c..942e323efca 100644 --- a/tests/rustdoc-json/type/inherent_associated_type_projections.rs +++ b/tests/rustdoc-json/type/inherent_associated_type_projections.rs @@ -5,11 +5,10 @@ // @set Parametrized = '$.index[*][?(@.name=="Parametrized")].id' pub struct Parametrized<T>(T); -// @is '$.index[*][?(@.name=="Test")].inner.type.kind' '"qualified_path"' -// @is '$.index[*][?(@.name=="Test")].inner.type.inner.self_type.inner.id' $Parametrized -// @is '$.index[*][?(@.name=="Test")].inner.type.inner.self_type.inner.args.angle_bracketed.args[0].type' '{"inner": "i32", "kind": "primitive"}' -// @is '$.index[*][?(@.name=="Test")].inner.type.inner.name' '"Proj"' -// @is '$.index[*][?(@.name=="Test")].inner.type.inner.trait' null +// @is '$.index[*][?(@.name=="Test")].inner.typedef.type.qualified_path.self_type.resolved_path.id' $Parametrized +// @is '$.index[*][?(@.name=="Test")].inner.typedef.type.qualified_path.self_type.resolved_path.args.angle_bracketed.args[0].type.primitive' \"i32\" +// @is '$.index[*][?(@.name=="Test")].inner.typedef.type.qualified_path.name' '"Proj"' +// @is '$.index[*][?(@.name=="Test")].inner.typedef.type.qualified_path.trait' null pub type Test = Parametrized<i32>::Proj; /// param_bool @@ -29,5 +28,5 @@ impl Parametrized<i32> { // @set param_bool_proj = '$.index[*][?(@.docs=="param_bool_proj")].id' // @set param_i32_proj = '$.index[*][?(@.docs=="param_i32_proj")].id' -// @is '$.index[*][?(@.docs=="param_bool")].inner.items[*]' $param_bool_proj -// @is '$.index[*][?(@.docs=="param_i32")].inner.items[*]' $param_i32_proj +// @is '$.index[*][?(@.docs=="param_bool")].inner.impl.items[*]' $param_bool_proj +// @is '$.index[*][?(@.docs=="param_i32")].inner.impl.items[*]' $param_i32_proj diff --git a/tests/rustdoc-json/unions/impl.rs b/tests/rustdoc-json/unions/impl.rs index 4454a69ecd1..7456892df1b 100644 --- a/tests/rustdoc-json/unions/impl.rs +++ b/tests/rustdoc-json/unions/impl.rs @@ -1,15 +1,15 @@ #![no_std] // @is "$.index[*][?(@.name=='Ux')].visibility" \"public\" -// @is "$.index[*][?(@.name=='Ux')].kind" \"union\" +// @has "$.index[*][?(@.name=='Ux')].inner.union" pub union Ux { a: u32, b: u64 } // @is "$.index[*][?(@.name=='Num')].visibility" \"public\" -// @is "$.index[*][?(@.name=='Num')].kind" \"trait\" +// @has "$.index[*][?(@.name=='Num')].inner.trait" pub trait Num {} -// @count "$.index[*][?(@.name=='Ux')].inner.impls" 1 +// @count "$.index[*][?(@.name=='Ux')].inner.union.impls" 1 impl Num for Ux {} diff --git a/tests/rustdoc-json/unions/union.rs b/tests/rustdoc-json/unions/union.rs index c9df2b81c4b..22b70e1ce8c 100644 --- a/tests/rustdoc-json/unions/union.rs +++ b/tests/rustdoc-json/unions/union.rs @@ -1,6 +1,6 @@ // @has "$.index[*][?(@.name=='Union')].visibility" \"public\" -// @has "$.index[*][?(@.name=='Union')].kind" \"union\" -// @!has "$.index[*][?(@.name=='Union')].inner.struct_type" +// @has "$.index[*][?(@.name=='Union')].inner.union" +// @!has "$.index[*][?(@.name=='Union')].inner.union.struct_type" // @set Union = "$.index[*][?(@.name=='Union')].id" pub union Union { int: i32, @@ -8,8 +8,8 @@ pub union Union { } -// @is "$.index[*][?(@.name=='make_int_union')].inner.decl.output.kind" '"resolved_path"' -// @is "$.index[*][?(@.name=='make_int_union')].inner.decl.output.inner.id" $Union +// @has "$.index[*][?(@.name=='make_int_union')].inner.function.decl.output.resolved_path" +// @is "$.index[*][?(@.name=='make_int_union')].inner.function.decl.output.resolved_path.id" $Union pub fn make_int_union(int: i32) -> Union { Union { int } } diff --git a/tests/rustdoc-ui/intra-doc/issue-111189-resolution-ice.rs b/tests/rustdoc-ui/intra-doc/issue-111189-resolution-ice.rs new file mode 100644 index 00000000000..4e74278dc7b --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/issue-111189-resolution-ice.rs @@ -0,0 +1,10 @@ +// Regression test for <https://github.com/rust-lang/rust/issues/111189>. +// This test ensures that it doesn't crash. + +#![deny(warnings)] + +/// #[rustfmt::skip] +//~^ ERROR unresolved link to `rustfmt::skip` +/// #[clippy::whatever] +//~^ ERROR unresolved link to `clippy::whatever` +pub fn foo() {} diff --git a/tests/rustdoc-ui/intra-doc/issue-111189-resolution-ice.stderr b/tests/rustdoc-ui/intra-doc/issue-111189-resolution-ice.stderr new file mode 100644 index 00000000000..edd3dfa7d7e --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/issue-111189-resolution-ice.stderr @@ -0,0 +1,21 @@ +error: unresolved link to `rustfmt::skip` + --> $DIR/issue-111189-resolution-ice.rs:6:7 + | +LL | /// #[rustfmt::skip] + | ^^^^^^^^^^^^^ no item named `rustfmt` in scope + | +note: the lint level is defined here + --> $DIR/issue-111189-resolution-ice.rs:4:9 + | +LL | #![deny(warnings)] + | ^^^^^^^^ + = note: `#[deny(rustdoc::broken_intra_doc_links)]` implied by `#[deny(warnings)]` + +error: unresolved link to `clippy::whatever` + --> $DIR/issue-111189-resolution-ice.rs:8:7 + | +LL | /// #[clippy::whatever] + | ^^^^^^^^^^^^^^^^ no item named `clippy` in scope + +error: aborting due to 2 previous errors + diff --git a/tests/rustdoc-ui/unescaped_backticks.rs b/tests/rustdoc-ui/unescaped_backticks.rs index f1ad7c8d4c7..e99cd1f3d58 100644 --- a/tests/rustdoc-ui/unescaped_backticks.rs +++ b/tests/rustdoc-ui/unescaped_backticks.rs @@ -340,3 +340,15 @@ id! { /// level changes. pub mod tracing_macro {} } + +/// Regression test for <https://github.com/rust-lang/rust/issues/111117> +pub mod trillium_server_common { + /// One-indexed, because the first CloneCounter is included. If you don't + /// want the original to count, construct a [``CloneCounterObserver`] + /// instead and use [`CloneCounterObserver::counter`] to increment. + //~^ ERROR unescaped backtick + pub struct CloneCounter; + + /// This is used by the above. + pub struct CloneCounterObserver; +} diff --git a/tests/rustdoc-ui/unescaped_backticks.stderr b/tests/rustdoc-ui/unescaped_backticks.stderr index e629dbc34e9..bf1f18889c4 100644 --- a/tests/rustdoc-ui/unescaped_backticks.stderr +++ b/tests/rustdoc-ui/unescaped_backticks.stderr @@ -342,6 +342,18 @@ LL | | /// level changes. to this: [`rebuild_interest_cache\`][rebuild] is called after the value of the max error: unescaped backtick + --> $DIR/unescaped_backticks.rs:348:56 + | +LL | /// instead and use [`CloneCounterObserver::counter`] to increment. + | ^ + | + = help: the opening or closing backtick of an inline code may be missing +help: if you meant to use a literal backtick, escape it + | +LL | /// instead and use [`CloneCounterObserver::counter\`] to increment. + | + + +error: unescaped backtick --> $DIR/unescaped_backticks.rs:11:5 | LL | /// ` @@ -955,5 +967,5 @@ help: if you meant to use a literal backtick, escape it LL | /// | table`( | )\`body | | + -error: aborting due to 63 previous errors +error: aborting due to 64 previous errors diff --git a/tests/rustdoc/test-strikethrough.rs b/tests/rustdoc/test-strikethrough.rs index c7855729a98..58162153b9e 100644 --- a/tests/rustdoc/test-strikethrough.rs +++ b/tests/rustdoc/test-strikethrough.rs @@ -1,6 +1,13 @@ #![crate_name = "foo"] -// @has foo/fn.f.html -// @has - //del "Y" -/// ~~Y~~ +// Test that strikethrough works with single and double tildes and that it shows up on +// the item's dedicated page as well as the parent module's summary of items. + +// @has foo/index.html //del 'strike' +// @has foo/index.html //del 'through' + +// @has foo/fn.f.html //del 'strike' +// @has foo/fn.f.html //del 'through' + +/// ~~strike~~ ~through~ pub fn f() {} diff --git a/tests/ui/associated-inherent-types/inference.rs b/tests/ui/associated-inherent-types/inference.rs index 7d6d26003f6..ebd8e1d5594 100644 --- a/tests/ui/associated-inherent-types/inference.rs +++ b/tests/ui/associated-inherent-types/inference.rs @@ -3,7 +3,7 @@ #![feature(inherent_associated_types)] #![allow(incomplete_features)] -#![allow(drop_copy)] +#![allow(dropping_copy_types)] use std::convert::identity; diff --git a/tests/ui/associated-inherent-types/issue-111879-0.rs b/tests/ui/associated-inherent-types/issue-111879-0.rs new file mode 100644 index 00000000000..e37f7d34ab5 --- /dev/null +++ b/tests/ui/associated-inherent-types/issue-111879-0.rs @@ -0,0 +1,14 @@ +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +// Check that we don't crash when printing inherent projections in diagnostics. + +pub struct Carrier<'a>(&'a ()); + +pub type User = for<'b> fn(Carrier<'b>::Focus<i32>); + +impl<'a> Carrier<'a> { + pub type Focus<T> = &'a mut User; //~ ERROR overflow evaluating associated type +} + +fn main() {} diff --git a/tests/ui/associated-inherent-types/issue-111879-0.stderr b/tests/ui/associated-inherent-types/issue-111879-0.stderr new file mode 100644 index 00000000000..7bdbad44017 --- /dev/null +++ b/tests/ui/associated-inherent-types/issue-111879-0.stderr @@ -0,0 +1,8 @@ +error: overflow evaluating associated type `Carrier<'b>::Focus<i32>` + --> $DIR/issue-111879-0.rs:11:25 + | +LL | pub type Focus<T> = &'a mut User; + | ^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/tests/ui/associated-inherent-types/issue-111879-1.rs b/tests/ui/associated-inherent-types/issue-111879-1.rs new file mode 100644 index 00000000000..7acc4f94505 --- /dev/null +++ b/tests/ui/associated-inherent-types/issue-111879-1.rs @@ -0,0 +1,12 @@ +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +// Check that we don't crash when printing inherent projections in diagnostics. + +struct Foo<T>(T); + +impl<'a> Foo<fn(&'a ())> { + type Assoc = &'a (); +} + +fn main(_: for<'a> fn(Foo<fn(&'a ())>::Assoc)) {} //~ ERROR `main` function has wrong type diff --git a/tests/ui/associated-inherent-types/issue-111879-1.stderr b/tests/ui/associated-inherent-types/issue-111879-1.stderr new file mode 100644 index 00000000000..689b45e09aa --- /dev/null +++ b/tests/ui/associated-inherent-types/issue-111879-1.stderr @@ -0,0 +1,12 @@ +error[E0580]: `main` function has wrong type + --> $DIR/issue-111879-1.rs:12:1 + | +LL | fn main(_: for<'a> fn(Foo<fn(&'a ())>::Assoc)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ incorrect number of function parameters + | + = note: expected fn pointer `fn()` + found fn pointer `fn(for<'a> fn(Foo<fn(&'a ())>::Assoc))` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0580`. diff --git a/tests/ui/async-await/return-type-notation/supertrait-bound.rs b/tests/ui/async-await/return-type-notation/supertrait-bound.rs new file mode 100644 index 00000000000..19bcfe3046b --- /dev/null +++ b/tests/ui/async-await/return-type-notation/supertrait-bound.rs @@ -0,0 +1,11 @@ +// check-pass + +#![feature(return_position_impl_trait_in_trait, return_type_notation)] +//~^ WARN the feature `return_type_notation` is incomplete and may not be safe to use + +trait IntFactory { + fn stream(&self) -> impl Iterator<Item = i32>; +} +trait SendIntFactory: IntFactory<stream(): Send> + Send {} + +fn main() {} diff --git a/tests/ui/async-await/return-type-notation/supertrait-bound.stderr b/tests/ui/async-await/return-type-notation/supertrait-bound.stderr new file mode 100644 index 00000000000..c8cec4946b4 --- /dev/null +++ b/tests/ui/async-await/return-type-notation/supertrait-bound.stderr @@ -0,0 +1,11 @@ +warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/supertrait-bound.rs:3:49 + | +LL | #![feature(return_position_impl_trait_in_trait, return_type_notation)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/borrowck/borrowck-closures-slice-patterns-ok.rs b/tests/ui/borrowck/borrowck-closures-slice-patterns-ok.rs index 9163c8ed6fb..60128c9419d 100644 --- a/tests/ui/borrowck/borrowck-closures-slice-patterns-ok.rs +++ b/tests/ui/borrowck/borrowck-closures-slice-patterns-ok.rs @@ -1,7 +1,7 @@ // Check that closure captures for slice patterns are inferred correctly #![allow(unused_variables)] -#![allow(drop_ref)] +#![allow(dropping_references)] // run-pass diff --git a/tests/ui/borrowck/borrowck-field-sensitivity-rpass.rs b/tests/ui/borrowck/borrowck-field-sensitivity-rpass.rs index a88b323e0bf..78e965cc4bc 100644 --- a/tests/ui/borrowck/borrowck-field-sensitivity-rpass.rs +++ b/tests/ui/borrowck/borrowck-field-sensitivity-rpass.rs @@ -1,7 +1,7 @@ // run-pass #![allow(unused_mut)] #![allow(unused_variables)] -#![allow(drop_copy)] +#![allow(dropping_copy_types)] // pretty-expanded FIXME #23616 struct A { a: isize, b: Box<isize> } diff --git a/tests/ui/borrowck/borrowck-use-mut-borrow-rpass.rs b/tests/ui/borrowck/borrowck-use-mut-borrow-rpass.rs index 40c6bfeeb43..9acb1ec5e43 100644 --- a/tests/ui/borrowck/borrowck-use-mut-borrow-rpass.rs +++ b/tests/ui/borrowck/borrowck-use-mut-borrow-rpass.rs @@ -1,7 +1,7 @@ // run-pass // pretty-expanded FIXME #23616 -#![allow(drop_copy)] +#![allow(dropping_copy_types)] struct A { a: isize, b: Box<isize> } diff --git a/tests/ui/closures/2229_closure_analysis/migrations/issue-78720.rs b/tests/ui/closures/2229_closure_analysis/migrations/issue-78720.rs index bc7295a0826..98f8d5d4733 100644 --- a/tests/ui/closures/2229_closure_analysis/migrations/issue-78720.rs +++ b/tests/ui/closures/2229_closure_analysis/migrations/issue-78720.rs @@ -1,7 +1,7 @@ // run-pass #![warn(rust_2021_incompatible_closure_captures)] -#![allow(drop_ref, drop_copy)] +#![allow(dropping_references, dropping_copy_types)] fn main() { if let a = "" { diff --git a/tests/ui/closures/2229_closure_analysis/optimization/edge_case_run_pass.rs b/tests/ui/closures/2229_closure_analysis/optimization/edge_case_run_pass.rs index 0f15f664e75..5496d0e5fc7 100644 --- a/tests/ui/closures/2229_closure_analysis/optimization/edge_case_run_pass.rs +++ b/tests/ui/closures/2229_closure_analysis/optimization/edge_case_run_pass.rs @@ -3,7 +3,7 @@ #![allow(unused)] #![allow(dead_code)] -#![allow(drop_ref)] +#![allow(dropping_references)] struct Int(i32); struct B<'a>(&'a i32); diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/drop_then_use_fake_reads.rs b/tests/ui/closures/2229_closure_analysis/run_pass/drop_then_use_fake_reads.rs index a097424a021..b5e97ec1c1b 100644 --- a/tests/ui/closures/2229_closure_analysis/run_pass/drop_then_use_fake_reads.rs +++ b/tests/ui/closures/2229_closure_analysis/run_pass/drop_then_use_fake_reads.rs @@ -2,7 +2,7 @@ // check-pass #![feature(rustc_attrs)] -#![allow(drop_ref)] +#![allow(dropping_references)] fn main() { let mut x = 1; diff --git a/tests/ui/consts/const_forget.rs b/tests/ui/consts/const_forget.rs index acdd6a54cf4..f06149f2cb9 100644 --- a/tests/ui/consts/const_forget.rs +++ b/tests/ui/consts/const_forget.rs @@ -1,6 +1,6 @@ // check-pass -#![allow(forget_copy)] +#![allow(forgetting_copy_types)] use std::mem::forget; diff --git a/tests/ui/consts/issue-104155.rs b/tests/ui/consts/issue-104155.rs index b3821f467b6..7b375dc0566 100644 --- a/tests/ui/consts/issue-104155.rs +++ b/tests/ui/consts/issue-104155.rs @@ -1,6 +1,6 @@ // check-pass -#![allow(forget_copy)] +#![allow(forgetting_copy_types)] const _: () = core::mem::forget(Box::<u32>::default); const _: () = core::mem::forget(|| Box::<u32>::default()); diff --git a/tests/ui/crate-leading-sep.rs b/tests/ui/crate-leading-sep.rs index 8d1d0b4fcdf..fce97d9ba23 100644 --- a/tests/ui/crate-leading-sep.rs +++ b/tests/ui/crate-leading-sep.rs @@ -1,7 +1,7 @@ // run-pass // pretty-expanded FIXME #23616 -#![allow(drop_copy)] +#![allow(dropping_copy_types)] fn main() { use ::std::mem; diff --git a/tests/ui/drop/repeat-drop.rs b/tests/ui/drop/repeat-drop.rs index 659d35db657..0afb4bb11bc 100644 --- a/tests/ui/drop/repeat-drop.rs +++ b/tests/ui/drop/repeat-drop.rs @@ -1,7 +1,7 @@ // run-pass // needs-unwind -#![allow(drop_ref, drop_copy)] +#![allow(dropping_references, dropping_copy_types)] static mut CHECK: usize = 0; diff --git a/tests/ui/dyn-star/param-env-infer.current.stderr b/tests/ui/dyn-star/param-env-infer.current.stderr new file mode 100644 index 00000000000..b3af7be7950 --- /dev/null +++ b/tests/ui/dyn-star/param-env-infer.current.stderr @@ -0,0 +1,18 @@ +warning: the feature `dyn_star` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/param-env-infer.rs:5:12 + | +LL | #![feature(dyn_star, pointer_like_trait)] + | ^^^^^^^^ + | + = note: see issue #102425 <https://github.com/rust-lang/rust/issues/102425> for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0282]: type annotations needed + --> $DIR/param-env-infer.rs:13:10 + | +LL | t as _ + | ^ cannot infer type + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/dyn-star/param-env-infer.next.stderr b/tests/ui/dyn-star/param-env-infer.next.stderr new file mode 100644 index 00000000000..64d76bb04b1 --- /dev/null +++ b/tests/ui/dyn-star/param-env-infer.next.stderr @@ -0,0 +1,73 @@ +warning: the feature `dyn_star` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/param-env-infer.rs:5:12 + | +LL | #![feature(dyn_star, pointer_like_trait)] + | ^^^^^^^^ + | + = note: see issue #102425 <https://github.com/rust-lang/rust/issues/102425> for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0391]: cycle detected when computing type of `make_dyn_star::{opaque#0}` + --> $DIR/param-env-infer.rs:11:60 + | +LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: ...which requires borrow-checking `make_dyn_star`... + --> $DIR/param-env-infer.rs:11:1 + | +LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires promoting constants in MIR for `make_dyn_star`... + --> $DIR/param-env-infer.rs:11:1 + | +LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires preparing `make_dyn_star` for borrow checking... + --> $DIR/param-env-infer.rs:11:1 + | +LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires unsafety-checking `make_dyn_star`... + --> $DIR/param-env-infer.rs:11:1 + | +LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires building MIR for `make_dyn_star`... + --> $DIR/param-env-infer.rs:11:1 + | +LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires match-checking `make_dyn_star`... + --> $DIR/param-env-infer.rs:11:1 + | +LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires building THIR for `make_dyn_star`... + --> $DIR/param-env-infer.rs:11:1 + | +LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires type-checking `make_dyn_star`... + --> $DIR/param-env-infer.rs:11:1 + | +LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: ...which requires computing layout of `make_dyn_star::{opaque#0}`... + = note: ...which requires normalizing `make_dyn_star::{opaque#0}`... + = note: ...which again requires computing type of `make_dyn_star::{opaque#0}`, completing the cycle +note: cycle used when checking item types in top-level module + --> $DIR/param-env-infer.rs:5:1 + | +LL | / #![feature(dyn_star, pointer_like_trait)] +LL | | +LL | | +LL | | use std::fmt::Debug; +... | +LL | | +LL | | fn main() {} + | |____________^ + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/dyn-star/param-env-infer.rs b/tests/ui/dyn-star/param-env-infer.rs new file mode 100644 index 00000000000..1fb16d76853 --- /dev/null +++ b/tests/ui/dyn-star/param-env-infer.rs @@ -0,0 +1,17 @@ +// revisions: current next +//[next] compile-flags: -Ztrait-solver=next +// incremental + +#![feature(dyn_star, pointer_like_trait)] +//~^ WARN the feature `dyn_star` is incomplete and may not be safe to use and/or cause compiler crashes + +use std::fmt::Debug; +use std::marker::PointerLike; + +fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a { + //[next]~^ ERROR cycle detected when computing type of `make_dyn_star::{opaque#0}` + t as _ + //[current]~^ ERROR type annotations needed +} + +fn main() {} diff --git a/tests/ui/error-codes/E0746.fixed b/tests/ui/error-codes/E0746.fixed deleted file mode 100644 index ca8319aa020..00000000000 --- a/tests/ui/error-codes/E0746.fixed +++ /dev/null @@ -1,18 +0,0 @@ -// run-rustfix -#![allow(dead_code)] -struct Struct; -trait Trait {} -impl Trait for Struct {} -impl Trait for u32 {} - -fn foo() -> impl Trait { Struct } -//~^ ERROR E0746 - -fn bar() -> impl Trait { //~ ERROR E0746 - if true { - return 0; - } - 42 -} - -fn main() {} diff --git a/tests/ui/error-codes/E0746.rs b/tests/ui/error-codes/E0746.rs index bf5ba8fff56..86b5b7444d1 100644 --- a/tests/ui/error-codes/E0746.rs +++ b/tests/ui/error-codes/E0746.rs @@ -1,5 +1,5 @@ -// run-rustfix #![allow(dead_code)] + struct Struct; trait Trait {} impl Trait for Struct {} diff --git a/tests/ui/error-codes/E0746.stderr b/tests/ui/error-codes/E0746.stderr index 2153b59ad18..9fe90ab7bec 100644 --- a/tests/ui/error-codes/E0746.stderr +++ b/tests/ui/error-codes/E0746.stderr @@ -4,11 +4,14 @@ error[E0746]: return type cannot have an unboxed trait object LL | fn foo() -> dyn Trait { Struct } | ^^^^^^^^^ doesn't have a size known at compile-time | - = note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits> -help: use `impl Trait` as the return type, as all return paths are of type `Struct`, which implements `Trait` +help: return an `impl Trait` instead of a `dyn Trait`, if all returned values are the same type | LL | fn foo() -> impl Trait { Struct } - | ~~~~~~~~~~ + | ~~~~ +help: box the return type, and wrap all of the returned values in `Box::new` + | +LL | fn foo() -> Box<dyn Trait> { Box::new(Struct) } + | ++++ + +++++++++ + error[E0746]: return type cannot have an unboxed trait object --> $DIR/E0746.rs:11:13 @@ -16,11 +19,18 @@ error[E0746]: return type cannot have an unboxed trait object LL | fn bar() -> dyn Trait { | ^^^^^^^^^ doesn't have a size known at compile-time | - = note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits> -help: use `impl Trait` as the return type, as all return paths are of type `{integer}`, which implements `Trait` +help: return an `impl Trait` instead of a `dyn Trait`, if all returned values are the same type | LL | fn bar() -> impl Trait { - | ~~~~~~~~~~ + | ~~~~ +help: box the return type, and wrap all of the returned values in `Box::new` + | +LL ~ fn bar() -> Box<dyn Trait> { +LL | if true { +LL ~ return Box::new(0); +LL | } +LL ~ Box::new(42) + | error: aborting due to 2 previous errors diff --git a/tests/ui/explicit/explicit-call-to-supertrait-dtor.fixed b/tests/ui/explicit/explicit-call-to-supertrait-dtor.fixed index 0bc4feed329..bb093a4af4a 100644 --- a/tests/ui/explicit/explicit-call-to-supertrait-dtor.fixed +++ b/tests/ui/explicit/explicit-call-to-supertrait-dtor.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(drop_ref)] +#![allow(dropping_references)] struct Foo { x: isize diff --git a/tests/ui/explicit/explicit-call-to-supertrait-dtor.rs b/tests/ui/explicit/explicit-call-to-supertrait-dtor.rs index 26ae6698d66..1a9f89c054f 100644 --- a/tests/ui/explicit/explicit-call-to-supertrait-dtor.rs +++ b/tests/ui/explicit/explicit-call-to-supertrait-dtor.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(drop_ref)] +#![allow(dropping_references)] struct Foo { x: isize diff --git a/tests/ui/generator/drop-env.rs b/tests/ui/generator/drop-env.rs index cb46953dac3..137a407931a 100644 --- a/tests/ui/generator/drop-env.rs +++ b/tests/ui/generator/drop-env.rs @@ -4,7 +4,7 @@ //[nomiropt]compile-flags: -Z mir-opt-level=0 #![feature(generators, generator_trait)] -#![allow(drop_copy)] +#![allow(dropping_copy_types)] use std::ops::Generator; use std::pin::Pin; diff --git a/tests/ui/generator/issue-57017.rs b/tests/ui/generator/issue-57017.rs index 918d233bf4e..381897c77a5 100644 --- a/tests/ui/generator/issue-57017.rs +++ b/tests/ui/generator/issue-57017.rs @@ -5,7 +5,7 @@ // [drop_tracking_mir] build-pass #![feature(generators, negative_impls)] -#![allow(drop_ref, drop_copy)] +#![allow(dropping_references, dropping_copy_types)] macro_rules! type_combinations { ( diff --git a/tests/ui/generator/non-static-is-unpin.rs b/tests/ui/generator/non-static-is-unpin.rs index adba800e25a..a5dde3912cc 100644 --- a/tests/ui/generator/non-static-is-unpin.rs +++ b/tests/ui/generator/non-static-is-unpin.rs @@ -3,7 +3,7 @@ // run-pass #![feature(generators, generator_trait)] -#![allow(drop_copy)] +#![allow(dropping_copy_types)] use std::marker::{PhantomPinned, Unpin}; diff --git a/tests/ui/generator/resume-arg-size.rs b/tests/ui/generator/resume-arg-size.rs index 19618f8d0aa..195166f975b 100644 --- a/tests/ui/generator/resume-arg-size.rs +++ b/tests/ui/generator/resume-arg-size.rs @@ -1,5 +1,5 @@ #![feature(generators)] -#![allow(drop_copy)] +#![allow(dropping_copy_types)] // run-pass diff --git a/tests/ui/hygiene/stdlib-prelude-from-opaque-late.rs b/tests/ui/hygiene/stdlib-prelude-from-opaque-late.rs index 214267372bf..721bb7281c0 100644 --- a/tests/ui/hygiene/stdlib-prelude-from-opaque-late.rs +++ b/tests/ui/hygiene/stdlib-prelude-from-opaque-late.rs @@ -1,7 +1,7 @@ // check-pass #![feature(decl_macro)] -#![allow(drop_copy)] +#![allow(dropping_copy_types)] macro mac() { mod m { diff --git a/tests/ui/illegal-ufcs-drop.fixed b/tests/ui/illegal-ufcs-drop.fixed index 8783682dec4..c088c82791b 100644 --- a/tests/ui/illegal-ufcs-drop.fixed +++ b/tests/ui/illegal-ufcs-drop.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(drop_ref)] +#![allow(dropping_references)] struct Foo; diff --git a/tests/ui/illegal-ufcs-drop.rs b/tests/ui/illegal-ufcs-drop.rs index 29774306ec6..1389b112188 100644 --- a/tests/ui/illegal-ufcs-drop.rs +++ b/tests/ui/illegal-ufcs-drop.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(drop_ref)] +#![allow(dropping_references)] struct Foo; diff --git a/tests/ui/impl-trait/dyn-trait-return-should-be-impl-trait.rs b/tests/ui/impl-trait/dyn-trait-return-should-be-impl-trait.rs index cbf1daabe2b..af368203de0 100644 --- a/tests/ui/impl-trait/dyn-trait-return-should-be-impl-trait.rs +++ b/tests/ui/impl-trait/dyn-trait-return-should-be-impl-trait.rs @@ -26,7 +26,7 @@ fn bax() -> dyn Trait { //~ ERROR E0746 if true { Struct } else { - 42 //~ ERROR `if` and `else` have incompatible types + 42 } } fn bam() -> Box<dyn Trait> { diff --git a/tests/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr b/tests/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr index dc1e40ea560..ed9261d0de5 100644 --- a/tests/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr +++ b/tests/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr @@ -46,11 +46,10 @@ error[E0746]: return type cannot have an unboxed trait object LL | fn bap() -> Trait { Struct } | ^^^^^ doesn't have a size known at compile-time | - = note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits> -help: use `impl Trait` as the return type, as all return paths are of type `Struct`, which implements `Trait` +help: box the return type, and wrap all of the returned values in `Box::new` | -LL | fn bap() -> impl Trait { Struct } - | ~~~~~~~~~~ +LL | fn bap() -> Box<Trait> { Box::new(Struct) } + | ++++ + +++++++++ + error[E0746]: return type cannot have an unboxed trait object --> $DIR/dyn-trait-return-should-be-impl-trait.rs:15:13 @@ -58,11 +57,14 @@ error[E0746]: return type cannot have an unboxed trait object LL | fn ban() -> dyn Trait { Struct } | ^^^^^^^^^ doesn't have a size known at compile-time | - = note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits> -help: use `impl Trait` as the return type, as all return paths are of type `Struct`, which implements `Trait` +help: return an `impl Trait` instead of a `dyn Trait`, if all returned values are the same type | LL | fn ban() -> impl Trait { Struct } - | ~~~~~~~~~~ + | ~~~~ +help: box the return type, and wrap all of the returned values in `Box::new` + | +LL | fn ban() -> Box<dyn Trait> { Box::new(Struct) } + | ++++ + +++++++++ + error[E0746]: return type cannot have an unboxed trait object --> $DIR/dyn-trait-return-should-be-impl-trait.rs:17:13 @@ -70,14 +72,14 @@ error[E0746]: return type cannot have an unboxed trait object LL | fn bak() -> dyn Trait { unimplemented!() } | ^^^^^^^^^ doesn't have a size known at compile-time | -help: use `impl Trait` as the return type if all return paths have the same type but you want to expose only the trait in the signature +help: return an `impl Trait` instead of a `dyn Trait`, if all returned values are the same type | LL | fn bak() -> impl Trait { unimplemented!() } - | ~~~~~~~~~~ -help: use a boxed trait object if all return paths implement trait `Trait` + | ~~~~ +help: box the return type, and wrap all of the returned values in `Box::new` | -LL | fn bak() -> Box<dyn Trait> { unimplemented!() } - | ++++ + +LL | fn bak() -> Box<dyn Trait> { Box::new(unimplemented!()) } + | ++++ + +++++++++ + error[E0746]: return type cannot have an unboxed trait object --> $DIR/dyn-trait-return-should-be-impl-trait.rs:19:13 @@ -85,34 +87,18 @@ error[E0746]: return type cannot have an unboxed trait object LL | fn bal() -> dyn Trait { | ^^^^^^^^^ doesn't have a size known at compile-time | - = note: for information on trait objects, see <https://doc.rust-lang.org/book/ch17-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types> - = note: if all the returned values were of the same type you could use `impl Trait` as the return type - = note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits> - = note: you can create a new `enum` with a variant for each returned type -help: return a boxed trait object instead +help: return an `impl Trait` instead of a `dyn Trait`, if all returned values are the same type | -LL | fn bal() -> Box<dyn Trait> { - | ++++ + -help: ... and box this value +LL | fn bal() -> impl Trait { + | ~~~~ +help: box the return type, and wrap all of the returned values in `Box::new` | -LL | return Box::new(Struct); - | +++++++++ + -help: ... and box this value +LL ~ fn bal() -> Box<dyn Trait> { +LL | if true { +LL ~ return Box::new(Struct); +LL | } +LL ~ Box::new(42) | -LL | Box::new(42) - | +++++++++ + - -error[E0308]: `if` and `else` have incompatible types - --> $DIR/dyn-trait-return-should-be-impl-trait.rs:29:9 - | -LL | / if true { -LL | | Struct - | | ------ expected because of this -LL | | } else { -LL | | 42 - | | ^^ expected `Struct`, found integer -LL | | } - | |_____- `if` and `else` have incompatible types error[E0746]: return type cannot have an unboxed trait object --> $DIR/dyn-trait-return-should-be-impl-trait.rs:25:13 @@ -120,22 +106,18 @@ error[E0746]: return type cannot have an unboxed trait object LL | fn bax() -> dyn Trait { | ^^^^^^^^^ doesn't have a size known at compile-time | - = note: for information on trait objects, see <https://doc.rust-lang.org/book/ch17-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types> - = note: if all the returned values were of the same type you could use `impl Trait` as the return type - = note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits> - = note: you can create a new `enum` with a variant for each returned type -help: return a boxed trait object instead +help: return an `impl Trait` instead of a `dyn Trait`, if all returned values are the same type | -LL | fn bax() -> Box<dyn Trait> { - | ++++ + -help: ... and box this value +LL | fn bax() -> impl Trait { + | ~~~~ +help: box the return type, and wrap all of the returned values in `Box::new` | -LL | Box::new(Struct) - | +++++++++ + -help: ... and box this value +LL ~ fn bax() -> Box<dyn Trait> { +LL | if true { +LL ~ Box::new(Struct) +LL | } else { +LL ~ Box::new(42) | -LL | Box::new(42) - | +++++++++ + error[E0308]: mismatched types --> $DIR/dyn-trait-return-should-be-impl-trait.rs:34:16 @@ -279,11 +261,18 @@ error[E0746]: return type cannot have an unboxed trait object LL | fn bat() -> dyn Trait { | ^^^^^^^^^ doesn't have a size known at compile-time | - = note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits> -help: use `impl Trait` as the return type, as all return paths are of type `{integer}`, which implements `Trait` +help: return an `impl Trait` instead of a `dyn Trait`, if all returned values are the same type | LL | fn bat() -> impl Trait { - | ~~~~~~~~~~ + | ~~~~ +help: box the return type, and wrap all of the returned values in `Box::new` + | +LL ~ fn bat() -> Box<dyn Trait> { +LL | if true { +LL ~ return Box::new(0); +LL | } +LL ~ Box::new(42) + | error[E0746]: return type cannot have an unboxed trait object --> $DIR/dyn-trait-return-should-be-impl-trait.rs:66:13 @@ -291,13 +280,20 @@ error[E0746]: return type cannot have an unboxed trait object LL | fn bay() -> dyn Trait { | ^^^^^^^^^ doesn't have a size known at compile-time | - = note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits> -help: use `impl Trait` as the return type, as all return paths are of type `{integer}`, which implements `Trait` +help: return an `impl Trait` instead of a `dyn Trait`, if all returned values are the same type | LL | fn bay() -> impl Trait { - | ~~~~~~~~~~ + | ~~~~ +help: box the return type, and wrap all of the returned values in `Box::new` + | +LL ~ fn bay() -> Box<dyn Trait> { +LL | if true { +LL ~ Box::new(0) +LL | } else { +LL ~ Box::new(42) + | -error: aborting due to 20 previous errors +error: aborting due to 19 previous errors Some errors have detailed explanations: E0277, E0308, E0746. For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.rs b/tests/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.rs index fa7664a83ee..a8a6288eb56 100644 --- a/tests/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.rs +++ b/tests/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.rs @@ -77,7 +77,7 @@ fn hat() -> dyn std::fmt::Display { //~ ERROR return type cannot have an unboxed fn pug() -> dyn std::fmt::Display { //~ ERROR return type cannot have an unboxed trait object match 13 { 0 => 0i32, - 1 => 1u32, //~ ERROR `match` arms have incompatible types + 1 => 1u32, _ => 2u32, } } @@ -86,7 +86,7 @@ fn man() -> dyn std::fmt::Display { //~ ERROR return type cannot have an unboxed if false { 0i32 } else { - 1u32 //~ ERROR `if` and `else` have incompatible types + 1u32 } } diff --git a/tests/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr b/tests/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr index 3c65fd998c5..9205d74504f 100644 --- a/tests/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr +++ b/tests/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr @@ -171,39 +171,20 @@ error[E0746]: return type cannot have an unboxed trait object LL | fn hat() -> dyn std::fmt::Display { | ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - = note: for information on trait objects, see <https://doc.rust-lang.org/book/ch17-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types> - = note: if all the returned values were of the same type you could use `impl std::fmt::Display` as the return type - = note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits> - = note: you can create a new `enum` with a variant for each returned type -help: return a boxed trait object instead - | -LL | fn hat() -> Box<dyn std::fmt::Display> { - | ++++ + -help: ... and box this value - | -LL | return Box::new(0i32); - | +++++++++ + -help: ... and box this value - | -LL | Box::new(1u32) - | +++++++++ + - -error[E0308]: `match` arms have incompatible types - --> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:80:14 +help: return an `impl Trait` instead of a `dyn Trait`, if all returned values are the same type | -LL | / match 13 { -LL | | 0 => 0i32, - | | ---- this is found to be of type `i32` -LL | | 1 => 1u32, - | | ^^^^ expected `i32`, found `u32` -LL | | _ => 2u32, -LL | | } - | |_____- `match` arms have incompatible types +LL | fn hat() -> impl std::fmt::Display { + | ~~~~ +help: box the return type, and wrap all of the returned values in `Box::new` | -help: change the type of the numeric literal from `u32` to `i32` +LL ~ fn hat() -> Box<dyn std::fmt::Display> { +LL | match 13 { +LL | 0 => { +LL ~ return Box::new(0i32); +LL | } +LL | _ => { +LL ~ Box::new(1u32) | -LL | 1 => 1i32, - | ~~~ error[E0746]: return type cannot have an unboxed trait object --> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:77:13 @@ -211,43 +192,18 @@ error[E0746]: return type cannot have an unboxed trait object LL | fn pug() -> dyn std::fmt::Display { | ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - = note: for information on trait objects, see <https://doc.rust-lang.org/book/ch17-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types> - = note: if all the returned values were of the same type you could use `impl std::fmt::Display` as the return type - = note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits> - = note: you can create a new `enum` with a variant for each returned type -help: return a boxed trait object instead - | -LL | fn pug() -> Box<dyn std::fmt::Display> { - | ++++ + -help: ... and box this value +help: return an `impl Trait` instead of a `dyn Trait`, if all returned values are the same type | -LL | 0 => Box::new(0i32), - | +++++++++ + -help: ... and box this value - | -LL | 1 => Box::new(1u32), - | +++++++++ + -help: ... and box this value - | -LL | _ => Box::new(2u32), - | +++++++++ + - -error[E0308]: `if` and `else` have incompatible types - --> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:89:9 +LL | fn pug() -> impl std::fmt::Display { + | ~~~~ +help: box the return type, and wrap all of the returned values in `Box::new` | -LL | / if false { -LL | | 0i32 - | | ---- expected because of this -LL | | } else { -LL | | 1u32 - | | ^^^^ expected `i32`, found `u32` -LL | | } - | |_____- `if` and `else` have incompatible types - | -help: change the type of the numeric literal from `u32` to `i32` +LL ~ fn pug() -> Box<dyn std::fmt::Display> { +LL | match 13 { +LL ~ 0 => Box::new(0i32), +LL ~ 1 => Box::new(1u32), +LL ~ _ => Box::new(2u32), | -LL | 1i32 - | ~~~ error[E0746]: return type cannot have an unboxed trait object --> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:85:13 @@ -255,24 +211,20 @@ error[E0746]: return type cannot have an unboxed trait object LL | fn man() -> dyn std::fmt::Display { | ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - = note: for information on trait objects, see <https://doc.rust-lang.org/book/ch17-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types> - = note: if all the returned values were of the same type you could use `impl std::fmt::Display` as the return type - = note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits> - = note: you can create a new `enum` with a variant for each returned type -help: return a boxed trait object instead +help: return an `impl Trait` instead of a `dyn Trait`, if all returned values are the same type | -LL | fn man() -> Box<dyn std::fmt::Display> { - | ++++ + -help: ... and box this value +LL | fn man() -> impl std::fmt::Display { + | ~~~~ +help: box the return type, and wrap all of the returned values in `Box::new` | -LL | Box::new(0i32) - | +++++++++ + -help: ... and box this value +LL ~ fn man() -> Box<dyn std::fmt::Display> { +LL | if false { +LL ~ Box::new(0i32) +LL | } else { +LL ~ Box::new(1u32) | -LL | Box::new(1u32) - | +++++++++ + -error: aborting due to 14 previous errors +error: aborting due to 12 previous errors Some errors have detailed explanations: E0308, E0746. For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/imports/issue-109148.rs b/tests/ui/imports/issue-109148.rs new file mode 100644 index 00000000000..694cb494a15 --- /dev/null +++ b/tests/ui/imports/issue-109148.rs @@ -0,0 +1,15 @@ +// edition: 2021 + +// https://github.com/rust-lang/rust/pull/111761#issuecomment-1557777314 +macro_rules! m { + () => { + extern crate core as std; + //~^ ERROR macro-expanded `extern crate` items cannot shadow names passed with `--extern` + } +} + +m!(); + +use std::mem; + +fn main() {} diff --git a/tests/ui/imports/issue-109148.stderr b/tests/ui/imports/issue-109148.stderr new file mode 100644 index 00000000000..6cc1221cfe9 --- /dev/null +++ b/tests/ui/imports/issue-109148.stderr @@ -0,0 +1,13 @@ +error: macro-expanded `extern crate` items cannot shadow names passed with `--extern` + --> $DIR/issue-109148.rs:6:9 + | +LL | extern crate core as std; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | m!(); + | ---- in this macro invocation + | + = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + diff --git a/tests/ui/invalid/foo.natvis.xml b/tests/ui/invalid/foo.natvis.xml new file mode 100644 index 00000000000..c341a403902 --- /dev/null +++ b/tests/ui/invalid/foo.natvis.xml @@ -0,0 +1 @@ +<!-- empty --> diff --git a/tests/ui/invalid/invalid-debugger-visualizer-target.rs b/tests/ui/invalid/invalid-debugger-visualizer-target.rs index f9dd20dbfed..1efb9555c24 100644 --- a/tests/ui/invalid/invalid-debugger-visualizer-target.rs +++ b/tests/ui/invalid/invalid-debugger-visualizer-target.rs @@ -1,2 +1,2 @@ -#[debugger_visualizer(natvis_file = "../foo.natvis")] //~ ERROR attribute should be applied to a module +#[debugger_visualizer(natvis_file = "./foo.natvis.xml")] //~ ERROR attribute should be applied to a module fn main() {} diff --git a/tests/ui/invalid/invalid-debugger-visualizer-target.stderr b/tests/ui/invalid/invalid-debugger-visualizer-target.stderr index 7944f751859..c8a4d681379 100644 --- a/tests/ui/invalid/invalid-debugger-visualizer-target.stderr +++ b/tests/ui/invalid/invalid-debugger-visualizer-target.stderr @@ -1,8 +1,8 @@ error: attribute should be applied to a module --> $DIR/invalid-debugger-visualizer-target.rs:1:1 | -LL | #[debugger_visualizer(natvis_file = "../foo.natvis")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[debugger_visualizer(natvis_file = "./foo.natvis.xml")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/tests/ui/issues/issue-18107.stderr b/tests/ui/issues/issue-18107.stderr index 1669b550a9b..cf4e06316a7 100644 --- a/tests/ui/issues/issue-18107.stderr +++ b/tests/ui/issues/issue-18107.stderr @@ -4,14 +4,18 @@ error[E0746]: return type cannot have an unboxed trait object LL | dyn AbstractRenderer | ^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | -help: use `impl AbstractRenderer` as the return type if all return paths have the same type but you want to expose only the trait in the signature +help: return an `impl Trait` instead of a `dyn Trait`, if all returned values are the same type | LL | impl AbstractRenderer + | ~~~~ +help: box the return type, and wrap all of the returned values in `Box::new` | -help: use a boxed trait object if all return paths implement trait `AbstractRenderer` +LL ~ Box<dyn AbstractRenderer> +LL | +LL | { +LL | match 0 { +LL ~ _ => Box::new(unimplemented!()) | -LL | Box<dyn AbstractRenderer> - | ++++ + error: aborting due to previous error diff --git a/tests/ui/iterators/collect-into-slice.rs b/tests/ui/iterators/collect-into-slice.rs index 5a8aacb1a6d..045d40a6f71 100644 --- a/tests/ui/iterators/collect-into-slice.rs +++ b/tests/ui/iterators/collect-into-slice.rs @@ -14,4 +14,10 @@ fn main() { //~| NOTE doesn't have a size known at compile-time //~| NOTE doesn't have a size known at compile-time process_slice(&some_generated_vec); + + let some_generated_vec = (0..10).collect(); + //~^ ERROR a slice of type `&[i32]` cannot be built since we need to store the elements somewhere + //~| NOTE try explicitly collecting into a `Vec<{integer}>` + //~| NOTE required by a bound in `collect` + process_slice(some_generated_vec); } diff --git a/tests/ui/iterators/collect-into-slice.stderr b/tests/ui/iterators/collect-into-slice.stderr index 29fff8c51c6..07dc561f06a 100644 --- a/tests/ui/iterators/collect-into-slice.stderr +++ b/tests/ui/iterators/collect-into-slice.stderr @@ -28,6 +28,16 @@ LL | let some_generated_vec = (0..10).collect(); note: required by a bound in `collect` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL -error: aborting due to 3 previous errors +error[E0277]: a slice of type `&[i32]` cannot be built since we need to store the elements somewhere + --> $DIR/collect-into-slice.rs:18:38 + | +LL | let some_generated_vec = (0..10).collect(); + | ^^^^^^^ try explicitly collecting into a `Vec<{integer}>` + | + = help: the trait `FromIterator<{integer}>` is not implemented for `&[i32]` +note: required by a bound in `collect` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/lint/drop_copy.rs b/tests/ui/lint/dropping_copy_types.rs index 0adcd34505f..2937320e5d8 100644 --- a/tests/ui/lint/drop_copy.rs +++ b/tests/ui/lint/dropping_copy_types.rs @@ -1,6 +1,6 @@ // check-pass -#![warn(drop_copy)] +#![warn(dropping_copy_types)] use std::mem::drop; use std::vec::Vec; diff --git a/tests/ui/lint/drop_copy.stderr b/tests/ui/lint/dropping_copy_types.stderr index db8e89ad295..b6291aa5ed6 100644 --- a/tests/ui/lint/drop_copy.stderr +++ b/tests/ui/lint/dropping_copy_types.stderr @@ -1,5 +1,5 @@ warning: calls to `std::mem::drop` with a value that implements `Copy` does nothing - --> $DIR/drop_copy.rs:34:5 + --> $DIR/dropping_copy_types.rs:34:5 | LL | drop(s1); | ^^^^^--^ @@ -8,13 +8,13 @@ LL | drop(s1); | = note: use `let _ = ...` to ignore the expression or result note: the lint level is defined here - --> $DIR/drop_copy.rs:3:9 + --> $DIR/dropping_copy_types.rs:3:9 | -LL | #![warn(drop_copy)] - | ^^^^^^^^^ +LL | #![warn(dropping_copy_types)] + | ^^^^^^^^^^^^^^^^^^^ warning: calls to `std::mem::drop` with a value that implements `Copy` does nothing - --> $DIR/drop_copy.rs:35:5 + --> $DIR/dropping_copy_types.rs:35:5 | LL | drop(s2); | ^^^^^--^ @@ -24,7 +24,7 @@ LL | drop(s2); = note: use `let _ = ...` to ignore the expression or result warning: calls to `std::mem::drop` with a reference instead of an owned value does nothing - --> $DIR/drop_copy.rs:36:5 + --> $DIR/dropping_copy_types.rs:36:5 | LL | drop(s3); | ^^^^^--^ @@ -32,10 +32,10 @@ LL | drop(s3); | argument has type `&SomeStruct` | = note: use `let _ = ...` to ignore the expression or result - = note: `#[warn(drop_ref)]` on by default + = note: `#[warn(dropping_references)]` on by default warning: calls to `std::mem::drop` with a value that implements `Copy` does nothing - --> $DIR/drop_copy.rs:37:5 + --> $DIR/dropping_copy_types.rs:37:5 | LL | drop(s4); | ^^^^^--^ @@ -45,7 +45,7 @@ LL | drop(s4); = note: use `let _ = ...` to ignore the expression or result warning: calls to `std::mem::drop` with a reference instead of an owned value does nothing - --> $DIR/drop_copy.rs:38:5 + --> $DIR/dropping_copy_types.rs:38:5 | LL | drop(s5); | ^^^^^--^ @@ -55,7 +55,7 @@ LL | drop(s5); = note: use `let _ = ...` to ignore the expression or result warning: calls to `std::mem::drop` with a reference instead of an owned value does nothing - --> $DIR/drop_copy.rs:50:5 + --> $DIR/dropping_copy_types.rs:50:5 | LL | drop(a2); | ^^^^^--^ @@ -65,7 +65,7 @@ LL | drop(a2); = note: use `let _ = ...` to ignore the expression or result warning: calls to `std::mem::drop` with a reference instead of an owned value does nothing - --> $DIR/drop_copy.rs:52:5 + --> $DIR/dropping_copy_types.rs:52:5 | LL | drop(a4); | ^^^^^--^ @@ -75,7 +75,7 @@ LL | drop(a4); = note: use `let _ = ...` to ignore the expression or result warning: calls to `std::mem::drop` with a value that implements `Copy` does nothing - --> $DIR/drop_copy.rs:71:13 + --> $DIR/dropping_copy_types.rs:71:13 | LL | drop(println_and(13)); | ^^^^^---------------^ @@ -85,7 +85,7 @@ LL | drop(println_and(13)); = note: use `let _ = ...` to ignore the expression or result warning: calls to `std::mem::drop` with a value that implements `Copy` does nothing - --> $DIR/drop_copy.rs:74:14 + --> $DIR/dropping_copy_types.rs:74:14 | LL | 3 if drop(println_and(14)) == () => (), | ^^^^^---------------^ @@ -95,7 +95,7 @@ LL | 3 if drop(println_and(14)) == () => (), = note: use `let _ = ...` to ignore the expression or result warning: calls to `std::mem::drop` with a value that implements `Copy` does nothing - --> $DIR/drop_copy.rs:76:14 + --> $DIR/dropping_copy_types.rs:76:14 | LL | 4 => drop(2), | ^^^^^-^ diff --git a/tests/ui/lint/drop_ref.rs b/tests/ui/lint/dropping_references.rs index db4f7569f6f..0d5d484f451 100644 --- a/tests/ui/lint/drop_ref.rs +++ b/tests/ui/lint/dropping_references.rs @@ -1,6 +1,6 @@ // check-pass -#![warn(drop_ref)] +#![warn(dropping_references)] struct SomeStruct; diff --git a/tests/ui/lint/drop_ref.stderr b/tests/ui/lint/dropping_references.stderr index 04c988fe99d..7e25a46216e 100644 --- a/tests/ui/lint/drop_ref.stderr +++ b/tests/ui/lint/dropping_references.stderr @@ -1,5 +1,5 @@ warning: calls to `std::mem::drop` with a reference instead of an owned value does nothing - --> $DIR/drop_ref.rs:8:5 + --> $DIR/dropping_references.rs:8:5 | LL | drop(&SomeStruct); | ^^^^^-----------^ @@ -8,13 +8,13 @@ LL | drop(&SomeStruct); | = note: use `let _ = ...` to ignore the expression or result note: the lint level is defined here - --> $DIR/drop_ref.rs:3:9 + --> $DIR/dropping_references.rs:3:9 | -LL | #![warn(drop_ref)] - | ^^^^^^^^ +LL | #![warn(dropping_references)] + | ^^^^^^^^^^^^^^^^^^^ warning: calls to `std::mem::drop` with a reference instead of an owned value does nothing - --> $DIR/drop_ref.rs:11:5 + --> $DIR/dropping_references.rs:11:5 | LL | drop(&owned1); | ^^^^^-------^ @@ -24,7 +24,7 @@ LL | drop(&owned1); = note: use `let _ = ...` to ignore the expression or result warning: calls to `std::mem::drop` with a reference instead of an owned value does nothing - --> $DIR/drop_ref.rs:12:5 + --> $DIR/dropping_references.rs:12:5 | LL | drop(&&owned1); | ^^^^^--------^ @@ -34,7 +34,7 @@ LL | drop(&&owned1); = note: use `let _ = ...` to ignore the expression or result warning: calls to `std::mem::drop` with a reference instead of an owned value does nothing - --> $DIR/drop_ref.rs:13:5 + --> $DIR/dropping_references.rs:13:5 | LL | drop(&mut owned1); | ^^^^^-----------^ @@ -44,7 +44,7 @@ LL | drop(&mut owned1); = note: use `let _ = ...` to ignore the expression or result warning: calls to `std::mem::drop` with a reference instead of an owned value does nothing - --> $DIR/drop_ref.rs:17:5 + --> $DIR/dropping_references.rs:17:5 | LL | drop(reference1); | ^^^^^----------^ @@ -54,7 +54,7 @@ LL | drop(reference1); = note: use `let _ = ...` to ignore the expression or result warning: calls to `std::mem::drop` with a reference instead of an owned value does nothing - --> $DIR/drop_ref.rs:20:5 + --> $DIR/dropping_references.rs:20:5 | LL | drop(reference2); | ^^^^^----------^ @@ -64,7 +64,7 @@ LL | drop(reference2); = note: use `let _ = ...` to ignore the expression or result warning: calls to `std::mem::drop` with a reference instead of an owned value does nothing - --> $DIR/drop_ref.rs:23:5 + --> $DIR/dropping_references.rs:23:5 | LL | drop(reference3); | ^^^^^----------^ @@ -74,7 +74,7 @@ LL | drop(reference3); = note: use `let _ = ...` to ignore the expression or result warning: calls to `std::mem::drop` with a reference instead of an owned value does nothing - --> $DIR/drop_ref.rs:28:5 + --> $DIR/dropping_references.rs:28:5 | LL | drop(&val); | ^^^^^----^ @@ -84,7 +84,7 @@ LL | drop(&val); = note: use `let _ = ...` to ignore the expression or result warning: calls to `std::mem::drop` with a reference instead of an owned value does nothing - --> $DIR/drop_ref.rs:36:5 + --> $DIR/dropping_references.rs:36:5 | LL | std::mem::drop(&SomeStruct); | ^^^^^^^^^^^^^^^-----------^ @@ -94,7 +94,7 @@ LL | std::mem::drop(&SomeStruct); = note: use `let _ = ...` to ignore the expression or result warning: calls to `std::mem::drop` with a reference instead of an owned value does nothing - --> $DIR/drop_ref.rs:91:13 + --> $DIR/dropping_references.rs:91:13 | LL | drop(println_and(&13)); | ^^^^^----------------^ @@ -104,7 +104,7 @@ LL | drop(println_and(&13)); = note: use `let _ = ...` to ignore the expression or result warning: calls to `std::mem::drop` with a reference instead of an owned value does nothing - --> $DIR/drop_ref.rs:94:14 + --> $DIR/dropping_references.rs:94:14 | LL | 3 if drop(println_and(&14)) == () => (), | ^^^^^----------------^ @@ -114,7 +114,7 @@ LL | 3 if drop(println_and(&14)) == () => (), = note: use `let _ = ...` to ignore the expression or result warning: calls to `std::mem::drop` with a reference instead of an owned value does nothing - --> $DIR/drop_ref.rs:96:14 + --> $DIR/dropping_references.rs:96:14 | LL | 4 => drop(&2), | ^^^^^--^ diff --git a/tests/ui/lint/forget_copy.rs b/tests/ui/lint/forgetting_copy_types.rs index a6b17b76971..224c7bcd5f6 100644 --- a/tests/ui/lint/forget_copy.rs +++ b/tests/ui/lint/forgetting_copy_types.rs @@ -1,6 +1,6 @@ // check-pass -#![warn(forget_copy)] +#![warn(forgetting_copy_types)] use std::mem::forget; use std::vec::Vec; diff --git a/tests/ui/lint/forget_copy.stderr b/tests/ui/lint/forgetting_copy_types.stderr index 37bc8a8854e..36d1ef5c53e 100644 --- a/tests/ui/lint/forget_copy.stderr +++ b/tests/ui/lint/forgetting_copy_types.stderr @@ -1,5 +1,5 @@ warning: calls to `std::mem::forget` with a value that implements `Copy` does nothing - --> $DIR/forget_copy.rs:34:5 + --> $DIR/forgetting_copy_types.rs:34:5 | LL | forget(s1); | ^^^^^^^--^ @@ -8,13 +8,13 @@ LL | forget(s1); | = note: use `let _ = ...` to ignore the expression or result note: the lint level is defined here - --> $DIR/forget_copy.rs:3:9 + --> $DIR/forgetting_copy_types.rs:3:9 | -LL | #![warn(forget_copy)] - | ^^^^^^^^^^^ +LL | #![warn(forgetting_copy_types)] + | ^^^^^^^^^^^^^^^^^^^^^ warning: calls to `std::mem::forget` with a value that implements `Copy` does nothing - --> $DIR/forget_copy.rs:35:5 + --> $DIR/forgetting_copy_types.rs:35:5 | LL | forget(s2); | ^^^^^^^--^ @@ -24,7 +24,7 @@ LL | forget(s2); = note: use `let _ = ...` to ignore the expression or result warning: calls to `std::mem::forget` with a reference instead of an owned value does nothing - --> $DIR/forget_copy.rs:36:5 + --> $DIR/forgetting_copy_types.rs:36:5 | LL | forget(s3); | ^^^^^^^--^ @@ -32,10 +32,10 @@ LL | forget(s3); | argument has type `&SomeStruct` | = note: use `let _ = ...` to ignore the expression or result - = note: `#[warn(forget_ref)]` on by default + = note: `#[warn(forgetting_references)]` on by default warning: calls to `std::mem::forget` with a value that implements `Copy` does nothing - --> $DIR/forget_copy.rs:37:5 + --> $DIR/forgetting_copy_types.rs:37:5 | LL | forget(s4); | ^^^^^^^--^ @@ -45,7 +45,7 @@ LL | forget(s4); = note: use `let _ = ...` to ignore the expression or result warning: calls to `std::mem::forget` with a reference instead of an owned value does nothing - --> $DIR/forget_copy.rs:38:5 + --> $DIR/forgetting_copy_types.rs:38:5 | LL | forget(s5); | ^^^^^^^--^ @@ -55,7 +55,7 @@ LL | forget(s5); = note: use `let _ = ...` to ignore the expression or result warning: calls to `std::mem::forget` with a reference instead of an owned value does nothing - --> $DIR/forget_copy.rs:50:5 + --> $DIR/forgetting_copy_types.rs:50:5 | LL | forget(a2); | ^^^^^^^--^ @@ -65,7 +65,7 @@ LL | forget(a2); = note: use `let _ = ...` to ignore the expression or result warning: calls to `std::mem::forget` with a reference instead of an owned value does nothing - --> $DIR/forget_copy.rs:52:5 + --> $DIR/forgetting_copy_types.rs:52:5 | LL | forget(a3); | ^^^^^^^--^ @@ -75,7 +75,7 @@ LL | forget(a3); = note: use `let _ = ...` to ignore the expression or result warning: calls to `std::mem::forget` with a reference instead of an owned value does nothing - --> $DIR/forget_copy.rs:53:5 + --> $DIR/forgetting_copy_types.rs:53:5 | LL | forget(a4); | ^^^^^^^--^ diff --git a/tests/ui/lint/forget_ref.rs b/tests/ui/lint/forgetting_references.rs index 13f6d4be3d1..bd51e980031 100644 --- a/tests/ui/lint/forget_ref.rs +++ b/tests/ui/lint/forgetting_references.rs @@ -1,6 +1,6 @@ // check-pass -#![warn(forget_ref)] +#![warn(forgetting_references)] use std::mem::forget; diff --git a/tests/ui/lint/forget_ref.stderr b/tests/ui/lint/forgetting_references.stderr index 63fc7791980..5624b690789 100644 --- a/tests/ui/lint/forget_ref.stderr +++ b/tests/ui/lint/forgetting_references.stderr @@ -1,5 +1,5 @@ warning: calls to `std::mem::forget` with a reference instead of an owned value does nothing - --> $DIR/forget_ref.rs:10:5 + --> $DIR/forgetting_references.rs:10:5 | LL | forget(&SomeStruct); | ^^^^^^^-----------^ @@ -8,13 +8,13 @@ LL | forget(&SomeStruct); | = note: use `let _ = ...` to ignore the expression or result note: the lint level is defined here - --> $DIR/forget_ref.rs:3:9 + --> $DIR/forgetting_references.rs:3:9 | -LL | #![warn(forget_ref)] - | ^^^^^^^^^^ +LL | #![warn(forgetting_references)] + | ^^^^^^^^^^^^^^^^^^^^^ warning: calls to `std::mem::forget` with a reference instead of an owned value does nothing - --> $DIR/forget_ref.rs:13:5 + --> $DIR/forgetting_references.rs:13:5 | LL | forget(&owned); | ^^^^^^^------^ @@ -24,7 +24,7 @@ LL | forget(&owned); = note: use `let _ = ...` to ignore the expression or result warning: calls to `std::mem::forget` with a reference instead of an owned value does nothing - --> $DIR/forget_ref.rs:14:5 + --> $DIR/forgetting_references.rs:14:5 | LL | forget(&&owned); | ^^^^^^^-------^ @@ -34,7 +34,7 @@ LL | forget(&&owned); = note: use `let _ = ...` to ignore the expression or result warning: calls to `std::mem::forget` with a reference instead of an owned value does nothing - --> $DIR/forget_ref.rs:15:5 + --> $DIR/forgetting_references.rs:15:5 | LL | forget(&mut owned); | ^^^^^^^----------^ @@ -44,7 +44,7 @@ LL | forget(&mut owned); = note: use `let _ = ...` to ignore the expression or result warning: calls to `std::mem::forget` with a reference instead of an owned value does nothing - --> $DIR/forget_ref.rs:19:5 + --> $DIR/forgetting_references.rs:19:5 | LL | forget(&*reference1); | ^^^^^^^------------^ @@ -54,7 +54,7 @@ LL | forget(&*reference1); = note: use `let _ = ...` to ignore the expression or result warning: calls to `std::mem::forget` with a reference instead of an owned value does nothing - --> $DIR/forget_ref.rs:22:5 + --> $DIR/forgetting_references.rs:22:5 | LL | forget(reference2); | ^^^^^^^----------^ @@ -64,7 +64,7 @@ LL | forget(reference2); = note: use `let _ = ...` to ignore the expression or result warning: calls to `std::mem::forget` with a reference instead of an owned value does nothing - --> $DIR/forget_ref.rs:25:5 + --> $DIR/forgetting_references.rs:25:5 | LL | forget(reference3); | ^^^^^^^----------^ @@ -74,7 +74,7 @@ LL | forget(reference3); = note: use `let _ = ...` to ignore the expression or result warning: calls to `std::mem::forget` with a reference instead of an owned value does nothing - --> $DIR/forget_ref.rs:30:5 + --> $DIR/forgetting_references.rs:30:5 | LL | forget(&val); | ^^^^^^^----^ @@ -84,7 +84,7 @@ LL | forget(&val); = note: use `let _ = ...` to ignore the expression or result warning: calls to `std::mem::forget` with a reference instead of an owned value does nothing - --> $DIR/forget_ref.rs:38:5 + --> $DIR/forgetting_references.rs:38:5 | LL | std::mem::forget(&SomeStruct); | ^^^^^^^^^^^^^^^^^-----------^ diff --git a/tests/ui/lint/unused/auxiliary/must-use-foreign.rs b/tests/ui/lint/unused/auxiliary/must-use-foreign.rs new file mode 100644 index 00000000000..f773f09c382 --- /dev/null +++ b/tests/ui/lint/unused/auxiliary/must-use-foreign.rs @@ -0,0 +1,12 @@ +// edition:2021 + +use std::future::Future; + +pub struct Manager; + +impl Manager { + #[must_use] + pub async fn new() -> (Self, impl Future<Output = ()>) { + (Manager, async {}) + } +} diff --git a/tests/ui/lint/unused/must-use-foreign.rs b/tests/ui/lint/unused/must-use-foreign.rs new file mode 100644 index 00000000000..21a11058562 --- /dev/null +++ b/tests/ui/lint/unused/must-use-foreign.rs @@ -0,0 +1,15 @@ +// edition:2021 +// aux-build:must-use-foreign.rs +// check-pass + +extern crate must_use_foreign; + +use must_use_foreign::Manager; + +async fn async_main() { + Manager::new().await.1.await; +} + +fn main() { + let _ = async_main(); +} diff --git a/tests/ui/lint/unused/unused-async.rs b/tests/ui/lint/unused/unused-async.rs index 4be93aa155a..6355f47f037 100644 --- a/tests/ui/lint/unused/unused-async.rs +++ b/tests/ui/lint/unused/unused-async.rs @@ -33,7 +33,7 @@ async fn test() { foo().await; //~ ERROR unused output of future returned by `foo` that must be used bar(); //~ ERROR unused return value of `bar` that must be used //~^ ERROR unused implementer of `Future` that must be used - bar().await; //~ ERROR unused output of future returned by `bar` that must be used + bar().await; // ok, it's not an async fn baz(); //~ ERROR unused implementer of `Future` that must be used baz().await; // ok } diff --git a/tests/ui/lint/unused/unused-async.stderr b/tests/ui/lint/unused/unused-async.stderr index 1c3702ba265..e93a40658f3 100644 --- a/tests/ui/lint/unused/unused-async.stderr +++ b/tests/ui/lint/unused/unused-async.stderr @@ -52,17 +52,6 @@ help: use `let _ = ...` to ignore the resulting value LL | let _ = bar(); | +++++++ -error: unused output of future returned by `bar` that must be used - --> $DIR/unused-async.rs:36:5 - | -LL | bar().await; - | ^^^^^^^^^^^ - | -help: use `let _ = ...` to ignore the resulting value - | -LL | let _ = bar().await; - | +++++++ - error: unused implementer of `Future` that must be used --> $DIR/unused-async.rs:37:5 | @@ -71,5 +60,5 @@ LL | baz(); | = note: futures do nothing unless you `.await` or poll them -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui/liveness/liveness-unused.rs b/tests/ui/liveness/liveness-unused.rs index 8ef6ab1b6ff..ba635e6638c 100644 --- a/tests/ui/liveness/liveness-unused.rs +++ b/tests/ui/liveness/liveness-unused.rs @@ -1,7 +1,7 @@ #![warn(unused)] #![deny(unused_variables)] #![deny(unused_assignments)] -#![allow(dead_code, non_camel_case_types, trivial_numeric_casts, drop_copy)] +#![allow(dead_code, non_camel_case_types, trivial_numeric_casts, dropping_copy_types)] use std::ops::AddAssign; diff --git a/tests/ui/macros/issue-111749.rs b/tests/ui/macros/issue-111749.rs new file mode 100644 index 00000000000..6c45e4e8cd7 --- /dev/null +++ b/tests/ui/macros/issue-111749.rs @@ -0,0 +1,12 @@ +macro_rules! cbor_map { + ($key:expr) => { + $key.signum(); + }; +} + +fn main() { + cbor_map! { #[test(test)] 4}; + //~^ ERROR removing an expression is not supported in this position + //~| ERROR attribute must be of the form `#[test]` + //~| WARNING this was previously accepted by the compiler but is being phased out +} diff --git a/tests/ui/macros/issue-111749.stderr b/tests/ui/macros/issue-111749.stderr new file mode 100644 index 00000000000..7db2b8e6ad1 --- /dev/null +++ b/tests/ui/macros/issue-111749.stderr @@ -0,0 +1,18 @@ +error: removing an expression is not supported in this position + --> $DIR/issue-111749.rs:8:17 + | +LL | cbor_map! { #[test(test)] 4}; + | ^^^^^^^^^^^^^ + +error: attribute must be of the form `#[test]` + --> $DIR/issue-111749.rs:8:17 + | +LL | cbor_map! { #[test(test)] 4}; + | ^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571> + = note: `#[deny(ill_formed_attribute_input)]` on by default + +error: aborting due to 2 previous errors + diff --git a/tests/ui/macros/panic-temporaries-2018.rs b/tests/ui/macros/panic-temporaries-2018.rs new file mode 100644 index 00000000000..d914df38062 --- /dev/null +++ b/tests/ui/macros/panic-temporaries-2018.rs @@ -0,0 +1,55 @@ +// check-pass +// edition:2018 + +#![allow(non_fmt_panics, unreachable_code)] + +use std::fmt::{self, Display}; +use std::marker::PhantomData; + +struct NotSend { + marker: PhantomData<*const u8>, +} + +const NOT_SEND: NotSend = NotSend { marker: PhantomData }; + +impl Display for NotSend { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("this value does not implement Send") + } +} + +async fn f(_: u8) {} + +// Exercises this matcher in panic_2015: +// ($fmt:expr, $($arg:tt)+) => $crate::panicking::panic_fmt(...) +async fn panic_fmt() { + // Panic returns `!`, so the await is never reached, and in particular the + // temporaries inside the formatting machinery are not still alive at the + // await point. + let todo = "..."; + f(panic!("not yet implemented: {}", todo)).await; +} + +// Exercises ("{}", $arg:expr) => $crate::panicking::panic_display(&$arg) +async fn panic_display() { + f(panic!("{}", NOT_SEND)).await; +} + +// Exercises ($msg:expr) => $crate::panicking::panic_str($msg) +async fn panic_str() { + f(panic!((NOT_SEND, "...").1)).await; +} + +// Exercises ($msg:expr) => $crate::panicking::unreachable_display(&$msg) +async fn unreachable_display() { + f(unreachable!(NOT_SEND)).await; +} + +fn require_send(_: impl Send) {} + +fn main() { + require_send(panic_fmt()); + require_send(panic_display()); + require_send(panic_str()); + require_send(unreachable_display()); +} diff --git a/tests/ui/macros/panic-temporaries.rs b/tests/ui/macros/panic-temporaries.rs index 5b5b8b7c2d9..db65601fb73 100644 --- a/tests/ui/macros/panic-temporaries.rs +++ b/tests/ui/macros/panic-temporaries.rs @@ -3,17 +3,41 @@ #![allow(unreachable_code)] +use std::fmt::{self, Display}; +use std::marker::PhantomData; + +struct NotSend { + marker: PhantomData<*const u8>, +} + +const NOT_SEND: NotSend = NotSend { marker: PhantomData }; + +impl Display for NotSend { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("this value does not implement Send") + } +} + async fn f(_: u8) {} -async fn g() { - // Todo returns `!`, so the await is never reached, and in particular the +// Exercises this matcher in panic_2021: +// ($($t:tt)+) => $crate::panicking::panic_fmt(...) +async fn panic_fmt() { + // Panic returns `!`, so the await is never reached, and in particular the // temporaries inside the formatting machinery are not still alive at the // await point. - f(todo!("...")).await; + let todo = "..."; + f(panic!("not yet implemented: {}", todo)).await; +} + +// Exercises ("{}", $arg:expr) => $crate::panicking::panic_display(&$arg) +async fn panic_display() { + f(panic!("{}", NOT_SEND)).await; } fn require_send(_: impl Send) {} fn main() { - require_send(g()); + require_send(panic_fmt()); + require_send(panic_display()); } diff --git a/tests/ui/macros/parse-complex-macro-invoc-op.rs b/tests/ui/macros/parse-complex-macro-invoc-op.rs index c50dfdf0116..10810388d20 100644 --- a/tests/ui/macros/parse-complex-macro-invoc-op.rs +++ b/tests/ui/macros/parse-complex-macro-invoc-op.rs @@ -4,7 +4,7 @@ #![allow(unused_assignments)] #![allow(unused_variables)] #![allow(stable_features)] -#![allow(drop_copy)] +#![allow(dropping_copy_types)] // Test parsing binary operators after macro invocations. diff --git a/tests/ui/never_type/exhaustive_patterns.stderr b/tests/ui/never_type/exhaustive_patterns.stderr index 5fed903eb70..f7bf8581582 100644 --- a/tests/ui/never_type/exhaustive_patterns.stderr +++ b/tests/ui/never_type/exhaustive_patterns.stderr @@ -14,6 +14,7 @@ LL | enum Either<A, B> { LL | A(A), LL | B(inner::Wrapper<B>), | - not covered + = note: pattern `Either::B(_)` is currently uninhabited, but this variant contains private fields which may become inhabited in the future = note: the matched value is of type `Either<(), !>` help: you might want to use `if let` to ignore the variant that isn't matched | diff --git a/tests/ui/never_type/never-assign-dead-code.rs b/tests/ui/never_type/never-assign-dead-code.rs index e95a992d780..39df7de5a7f 100644 --- a/tests/ui/never_type/never-assign-dead-code.rs +++ b/tests/ui/never_type/never-assign-dead-code.rs @@ -3,7 +3,7 @@ // check-pass #![feature(never_type)] -#![allow(drop_copy)] +#![allow(dropping_copy_types)] #![warn(unused)] fn main() { diff --git a/tests/ui/nll/relate_tys/hr-fn-aba-as-aaa.rs b/tests/ui/nll/relate_tys/hr-fn-aba-as-aaa.rs index 73ceaeeb875..2e9eff59386 100644 --- a/tests/ui/nll/relate_tys/hr-fn-aba-as-aaa.rs +++ b/tests/ui/nll/relate_tys/hr-fn-aba-as-aaa.rs @@ -5,7 +5,7 @@ // check-pass // compile-flags:-Zno-leak-check -#![allow(drop_copy)] +#![allow(dropping_copy_types)] fn make_it() -> for<'a, 'b> fn(&'a u32, &'b u32) -> &'a u32 { panic!() diff --git a/tests/ui/nll/ty-outlives/projection-body.rs b/tests/ui/nll/ty-outlives/projection-body.rs index bff9058a507..722d6747102 100644 --- a/tests/ui/nll/ty-outlives/projection-body.rs +++ b/tests/ui/nll/ty-outlives/projection-body.rs @@ -3,7 +3,7 @@ // // check-pass -#![allow(drop_ref)] +#![allow(dropping_references)] trait MyTrait<'a> { type Output; diff --git a/tests/ui/offset-of/offset-of-arg-count.rs b/tests/ui/offset-of/offset-of-arg-count.rs index 92a205f14d9..31de45bc756 100644 --- a/tests/ui/offset-of/offset-of-arg-count.rs +++ b/tests/ui/offset-of/offset-of-arg-count.rs @@ -13,7 +13,7 @@ fn main() { offset_of!(S, f..); //~ ERROR no rules expected the token offset_of!(S, f..,); //~ ERROR no rules expected the token offset_of!(Lt<'static>, bar); // issue #111657 - + offset_of!(Lt<'_>, bar); // issue #111678 } struct S { f: u8, } diff --git a/tests/ui/offset-of/offset-of-dst-field.rs b/tests/ui/offset-of/offset-of-dst-field.rs index a0269ca2d12..3b8dc0b84a4 100644 --- a/tests/ui/offset-of/offset-of-dst-field.rs +++ b/tests/ui/offset-of/offset-of-dst-field.rs @@ -26,8 +26,24 @@ struct Gamma { z: Extern, } +struct Delta<T: ?Sized> { + x: u8, + y: u16, + z: T, +} + fn main() { offset_of!(Alpha, z); //~ ERROR the size for values of type offset_of!(Beta, z); //~ ERROR the size for values of type offset_of!(Gamma, z); //~ ERROR the size for values of type } + +fn delta() { + offset_of!(Delta<Alpha>, z); //~ ERROR the size for values of type + offset_of!(Delta<Extern>, z); //~ ERROR the size for values of type + offset_of!(Delta<dyn Trait>, z); //~ ERROR the size for values of type +} + +fn generic_with_maybe_sized<T: ?Sized>() -> usize { + offset_of!(Delta<T>, z) //~ ERROR the size for values of type +} diff --git a/tests/ui/offset-of/offset-of-dst-field.stderr b/tests/ui/offset-of/offset-of-dst-field.stderr index e6e0f499236..128c783d5dd 100644 --- a/tests/ui/offset-of/offset-of-dst-field.stderr +++ b/tests/ui/offset-of/offset-of-dst-field.stderr @@ -1,5 +1,5 @@ error[E0277]: the size for values of type `[u8]` cannot be known at compilation time - --> $DIR/offset-of-dst-field.rs:30:5 + --> $DIR/offset-of-dst-field.rs:36:5 | LL | offset_of!(Alpha, z); | ^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time @@ -8,7 +8,7 @@ LL | offset_of!(Alpha, z); = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the size for values of type `(dyn Trait + 'static)` cannot be known at compilation time - --> $DIR/offset-of-dst-field.rs:31:5 + --> $DIR/offset-of-dst-field.rs:37:5 | LL | offset_of!(Beta, z); | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time @@ -17,7 +17,7 @@ LL | offset_of!(Beta, z); = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the size for values of type `Extern` cannot be known at compilation time - --> $DIR/offset-of-dst-field.rs:32:5 + --> $DIR/offset-of-dst-field.rs:38:5 | LL | offset_of!(Gamma, z); | ^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time @@ -25,6 +25,53 @@ LL | offset_of!(Gamma, z); = help: the trait `Sized` is not implemented for `Extern` = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 3 previous errors +error[E0277]: the size for values of type `Extern` cannot be known at compilation time + --> $DIR/offset-of-dst-field.rs:43:5 + | +LL | offset_of!(Delta<Extern>, z); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `Extern` + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time + --> $DIR/offset-of-dst-field.rs:44:5 + | +LL | offset_of!(Delta<dyn Trait>, z); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `dyn Trait` + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/offset-of-dst-field.rs:42:5 + | +LL | offset_of!(Delta<Alpha>, z); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: within `Alpha`, the trait `Sized` is not implemented for `[u8]` +note: required because it appears within the type `Alpha` + --> $DIR/offset-of-dst-field.rs:5:8 + | +LL | struct Alpha { + | ^^^^^ + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `T` cannot be known at compilation time + --> $DIR/offset-of-dst-field.rs:48:5 + | +LL | fn generic_with_maybe_sized<T: ?Sized>() -> usize { + | - this type parameter needs to be `std::marker::Sized` +LL | offset_of!(Delta<T>, z) + | ^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider removing the `?Sized` bound to make the type parameter `Sized` + | +LL - fn generic_with_maybe_sized<T: ?Sized>() -> usize { +LL + fn generic_with_maybe_sized<T>() -> usize { + | + +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/offset-of/offset-of-inference.rs b/tests/ui/offset-of/offset-of-inference.rs new file mode 100644 index 00000000000..ba87574eae0 --- /dev/null +++ b/tests/ui/offset-of/offset-of-inference.rs @@ -0,0 +1,11 @@ +// Test that inference types in `offset_of!` don't ICE. + +#![feature(offset_of)] + +struct Foo<T> { + x: T, +} + +fn main() { + let _ = core::mem::offset_of!(Foo<_>, x); //~ ERROR: type annotations needed +} diff --git a/tests/ui/offset-of/offset-of-inference.stderr b/tests/ui/offset-of/offset-of-inference.stderr new file mode 100644 index 00000000000..2a520f6f906 --- /dev/null +++ b/tests/ui/offset-of/offset-of-inference.stderr @@ -0,0 +1,9 @@ +error[E0282]: type annotations needed + --> $DIR/offset-of-inference.rs:10:35 + | +LL | let _ = core::mem::offset_of!(Foo<_>, x); + | ^^^^^^ cannot infer type + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/offset-of/offset-of-output-type.rs b/tests/ui/offset-of/offset-of-output-type.rs new file mode 100644 index 00000000000..50c2e93841f --- /dev/null +++ b/tests/ui/offset-of/offset-of-output-type.rs @@ -0,0 +1,20 @@ +#![feature(offset_of)] + +use std::mem::offset_of; + +struct S { + v: u8, + w: u16, +} + + +fn main() { + let _: u8 = offset_of!(S, v); //~ ERROR mismatched types + let _: u16 = offset_of!(S, v); //~ ERROR mismatched types + let _: u32 = offset_of!(S, v); //~ ERROR mismatched types + let _: u64 = offset_of!(S, v); //~ ERROR mismatched types + let _: isize = offset_of!(S, v); //~ ERROR mismatched types + let _: usize = offset_of!(S, v); + + offset_of!(S, v) //~ ERROR mismatched types +} diff --git a/tests/ui/offset-of/offset-of-output-type.stderr b/tests/ui/offset-of/offset-of-output-type.stderr new file mode 100644 index 00000000000..6f8c9475029 --- /dev/null +++ b/tests/ui/offset-of/offset-of-output-type.stderr @@ -0,0 +1,64 @@ +error[E0308]: mismatched types + --> $DIR/offset-of-output-type.rs:12:17 + | +LL | let _: u8 = offset_of!(S, v); + | -- ^^^^^^^^^^^^^^^^ expected `u8`, found `usize` + | | + | expected due to this + | + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> $DIR/offset-of-output-type.rs:13:18 + | +LL | let _: u16 = offset_of!(S, v); + | --- ^^^^^^^^^^^^^^^^ expected `u16`, found `usize` + | | + | expected due to this + | + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> $DIR/offset-of-output-type.rs:14:18 + | +LL | let _: u32 = offset_of!(S, v); + | --- ^^^^^^^^^^^^^^^^ expected `u32`, found `usize` + | | + | expected due to this + | + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> $DIR/offset-of-output-type.rs:15:18 + | +LL | let _: u64 = offset_of!(S, v); + | --- ^^^^^^^^^^^^^^^^ expected `u64`, found `usize` + | | + | expected due to this + | + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> $DIR/offset-of-output-type.rs:16:20 + | +LL | let _: isize = offset_of!(S, v); + | ----- ^^^^^^^^^^^^^^^^ expected `isize`, found `usize` + | | + | expected due to this + | + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> $DIR/offset-of-output-type.rs:19:5 + | +LL | fn main() { + | - expected `()` because of default return type +... +LL | offset_of!(S, v) + | ^^^^^^^^^^^^^^^^ expected `()`, found `usize` + | + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/offset-of/offset-of-private.rs b/tests/ui/offset-of/offset-of-private.rs index 0291b7825ca..6b1a16ba62b 100644 --- a/tests/ui/offset-of/offset-of-private.rs +++ b/tests/ui/offset-of/offset-of-private.rs @@ -8,9 +8,21 @@ mod m { pub public: u8, private: u8, } + #[repr(C)] + pub struct FooTuple(pub u8, u8); + #[repr(C)] + struct Bar { + pub public: u8, + private: u8, + } } fn main() { offset_of!(m::Foo, public); offset_of!(m::Foo, private); //~ ERROR field `private` of struct `Foo` is private + offset_of!(m::FooTuple, 0); + offset_of!(m::FooTuple, 1); //~ ERROR field `1` of struct `FooTuple` is private + offset_of!(m::Bar, public); //~ ERROR struct `Bar` is private + offset_of!(m::Bar, private); //~ ERROR struct `Bar` is private + //~| ERROR field `private` of struct `Bar` is private } diff --git a/tests/ui/offset-of/offset-of-private.stderr b/tests/ui/offset-of/offset-of-private.stderr index 8a186dd5a02..0674b58f860 100644 --- a/tests/ui/offset-of/offset-of-private.stderr +++ b/tests/ui/offset-of/offset-of-private.stderr @@ -1,9 +1,46 @@ +error[E0603]: struct `Bar` is private + --> $DIR/offset-of-private.rs:25:19 + | +LL | offset_of!(m::Bar, public); + | ^^^ private struct + | +note: the struct `Bar` is defined here + --> $DIR/offset-of-private.rs:14:5 + | +LL | struct Bar { + | ^^^^^^^^^^ + +error[E0603]: struct `Bar` is private + --> $DIR/offset-of-private.rs:26:19 + | +LL | offset_of!(m::Bar, private); + | ^^^ private struct + | +note: the struct `Bar` is defined here + --> $DIR/offset-of-private.rs:14:5 + | +LL | struct Bar { + | ^^^^^^^^^^ + error[E0616]: field `private` of struct `Foo` is private - --> $DIR/offset-of-private.rs:15:24 + --> $DIR/offset-of-private.rs:22:24 | LL | offset_of!(m::Foo, private); | ^^^^^^^ private field -error: aborting due to previous error +error[E0616]: field `1` of struct `FooTuple` is private + --> $DIR/offset-of-private.rs:24:29 + | +LL | offset_of!(m::FooTuple, 1); + | ^ private field + +error[E0616]: field `private` of struct `Bar` is private + --> $DIR/offset-of-private.rs:26:24 + | +LL | offset_of!(m::Bar, private); + | ^^^^^^^ private field + +error: aborting due to 5 previous errors -For more information about this error, try `rustc --explain E0616`. +Some errors have detailed explanations: E0603, E0616. +For more information about an error, try `rustc --explain E0603`. diff --git a/tests/ui/offset-of/offset-of-self.rs b/tests/ui/offset-of/offset-of-self.rs new file mode 100644 index 00000000000..dbeef0e74dc --- /dev/null +++ b/tests/ui/offset-of/offset-of-self.rs @@ -0,0 +1,58 @@ +#![feature(offset_of)] + +use std::mem::offset_of; + +struct C<T> { + v: T, + w: T, +} + +struct S { + v: u8, + w: u16, +} + +impl S { + fn v_offs() -> usize { + offset_of!(Self, v) + } + fn v_offs_wrong_syntax() { + offset_of!(Self, Self::v); //~ ERROR no rules expected the token `::` + offset_of!(S, Self); //~ ERROR expected identifier, found keyword `Self` + //~| no field `Self` on type `S` + } + fn offs_in_c() -> usize { + offset_of!(C<Self>, w) + } + fn offs_in_c_colon() -> usize { + offset_of!(C::<Self>, w) + } +} + +mod m { + use std::mem::offset_of; + fn off() { + offset_of!(self::S, v); //~ ERROR cannot find type `S` in module + offset_of!(super::S, v); + offset_of!(crate::S, v); + } + impl super::n::T { + fn v_offs_self() -> usize { + offset_of!(Self, v) //~ ERROR field `v` of struct `T` is private + } + } +} + +mod n { + pub struct T { v: u8, } +} + +fn main() { + offset_of!(self::S, v); + offset_of!(Self, v); //~ ERROR cannot find type `Self` in this scope + + offset_of!(S, self); //~ ERROR expected identifier, found keyword `self` + //~| no field `self` on type `S` + offset_of!(S, v.self); //~ ERROR expected identifier, found keyword `self` + //~| no field `self` on type `u8` +} diff --git a/tests/ui/offset-of/offset-of-self.stderr b/tests/ui/offset-of/offset-of-self.stderr new file mode 100644 index 00000000000..df555463f98 --- /dev/null +++ b/tests/ui/offset-of/offset-of-self.stderr @@ -0,0 +1,79 @@ +error: no rules expected the token `::` + --> $DIR/offset-of-self.rs:20:30 + | +LL | offset_of!(Self, Self::v); + | ^^ no rules expected this token in macro call + | + = note: while trying to match sequence start + +error: expected identifier, found keyword `Self` + --> $DIR/offset-of-self.rs:21:23 + | +LL | offset_of!(S, Self); + | ^^^^ expected identifier, found keyword + +error: expected identifier, found keyword `self` + --> $DIR/offset-of-self.rs:54:19 + | +LL | offset_of!(S, self); + | ^^^^ expected identifier, found keyword + +error: expected identifier, found keyword `self` + --> $DIR/offset-of-self.rs:56:21 + | +LL | offset_of!(S, v.self); + | ^^^^ expected identifier, found keyword + +error[E0412]: cannot find type `S` in module `self` + --> $DIR/offset-of-self.rs:35:26 + | +LL | offset_of!(self::S, v); + | ^ not found in `self` + | +help: consider importing this struct + | +LL + use S; + | +help: if you import `S`, refer to it directly + | +LL - offset_of!(self::S, v); +LL + offset_of!(S, v); + | + +error[E0411]: cannot find type `Self` in this scope + --> $DIR/offset-of-self.rs:52:16 + | +LL | fn main() { + | ---- `Self` not allowed in a function +LL | offset_of!(self::S, v); +LL | offset_of!(Self, v); + | ^^^^ `Self` is only available in impls, traits, and type definitions + +error[E0609]: no field `Self` on type `S` + --> $DIR/offset-of-self.rs:21:23 + | +LL | offset_of!(S, Self); + | ^^^^ + +error[E0616]: field `v` of struct `T` is private + --> $DIR/offset-of-self.rs:41:30 + | +LL | offset_of!(Self, v) + | ^ private field + +error[E0609]: no field `self` on type `S` + --> $DIR/offset-of-self.rs:54:19 + | +LL | offset_of!(S, self); + | ^^^^ + +error[E0609]: no field `self` on type `u8` + --> $DIR/offset-of-self.rs:56:21 + | +LL | offset_of!(S, v.self); + | ^^^^ + +error: aborting due to 10 previous errors + +Some errors have detailed explanations: E0411, E0412, E0609, E0616. +For more information about an error, try `rustc --explain E0411`. diff --git a/tests/ui/or-patterns/or-patterns-default-binding-modes.rs b/tests/ui/or-patterns/or-patterns-default-binding-modes.rs index c138d99d303..df6aab0e6a8 100644 --- a/tests/ui/or-patterns/or-patterns-default-binding-modes.rs +++ b/tests/ui/or-patterns/or-patterns-default-binding-modes.rs @@ -3,8 +3,8 @@ // check-pass #![allow(irrefutable_let_patterns)] -#![allow(drop_copy)] -#![allow(drop_ref)] +#![allow(dropping_copy_types)] +#![allow(dropping_references)] fn main() { // A regression test for a mistake we made at one point: diff --git a/tests/ui/pattern/bindings-after-at/borrowck-pat-at-and-box-pass.rs b/tests/ui/pattern/bindings-after-at/borrowck-pat-at-and-box-pass.rs index 965204bf240..43b53b7cf1f 100644 --- a/tests/ui/pattern/bindings-after-at/borrowck-pat-at-and-box-pass.rs +++ b/tests/ui/pattern/bindings-after-at/borrowck-pat-at-and-box-pass.rs @@ -2,8 +2,8 @@ // Test `@` patterns combined with `box` patterns. -#![allow(drop_ref)] -#![allow(drop_copy)] +#![allow(dropping_references)] +#![allow(dropping_copy_types)] #![feature(box_patterns)] diff --git a/tests/ui/pattern/bindings-after-at/borrowck-pat-by-copy-bindings-in-at.rs b/tests/ui/pattern/bindings-after-at/borrowck-pat-by-copy-bindings-in-at.rs index 3eb5d2cbf54..1df51c0edd9 100644 --- a/tests/ui/pattern/bindings-after-at/borrowck-pat-by-copy-bindings-in-at.rs +++ b/tests/ui/pattern/bindings-after-at/borrowck-pat-by-copy-bindings-in-at.rs @@ -2,7 +2,7 @@ // Test `Copy` bindings in the rhs of `@` patterns. -#![allow(drop_copy)] +#![allow(dropping_copy_types)] #[derive(Copy, Clone)] struct C; diff --git a/tests/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern-pass.rs b/tests/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern-pass.rs index 0550238549e..204cd3e6657 100644 --- a/tests/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern-pass.rs +++ b/tests/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern-pass.rs @@ -1,6 +1,6 @@ // check-pass -#![allow(drop_ref)] +#![allow(dropping_references)] fn main() {} diff --git a/tests/ui/pattern/move-ref-patterns/move-ref-patterns-closure-captures-pass.rs b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-closure-captures-pass.rs index 788975d960a..4de1f653db0 100644 --- a/tests/ui/pattern/move-ref-patterns/move-ref-patterns-closure-captures-pass.rs +++ b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-closure-captures-pass.rs @@ -1,6 +1,6 @@ // check-pass -#![allow(drop_ref)] +#![allow(dropping_references)] fn main() { struct U; diff --git a/tests/ui/print_type_sizes/async.rs b/tests/ui/print_type_sizes/async.rs index c73268dc46a..f38a6e674da 100644 --- a/tests/ui/print_type_sizes/async.rs +++ b/tests/ui/print_type_sizes/async.rs @@ -3,7 +3,7 @@ // build-pass // ignore-pass -#![allow(drop_copy)] +#![allow(dropping_copy_types)] async fn wait() {} diff --git a/tests/ui/print_type_sizes/generator_discr_placement.rs b/tests/ui/print_type_sizes/generator_discr_placement.rs index a77a03f0a8a..6adc14f9b99 100644 --- a/tests/ui/print_type_sizes/generator_discr_placement.rs +++ b/tests/ui/print_type_sizes/generator_discr_placement.rs @@ -6,7 +6,7 @@ // Avoid emitting panic handlers, like the rest of these tests... #![feature(generators)] -#![allow(drop_copy)] +#![allow(dropping_copy_types)] pub fn foo() { let a = || { diff --git a/tests/ui/regions/type-param-outlives-reempty-issue-74429.rs b/tests/ui/regions/type-param-outlives-reempty-issue-74429.rs index af2bb09805a..0c1e9314441 100644 --- a/tests/ui/regions/type-param-outlives-reempty-issue-74429.rs +++ b/tests/ui/regions/type-param-outlives-reempty-issue-74429.rs @@ -3,7 +3,7 @@ // check-pass -#![allow(drop_copy)] +#![allow(dropping_copy_types)] use std::marker::PhantomData; diff --git a/tests/ui/resolve/issue-111727.rs b/tests/ui/resolve/issue-111727.rs new file mode 100644 index 00000000000..36f3081211d --- /dev/null +++ b/tests/ui/resolve/issue-111727.rs @@ -0,0 +1,5 @@ +// edition: 2021 + +fn main() { + std::any::Any::create(); //~ ERROR +} diff --git a/tests/ui/resolve/issue-111727.stderr b/tests/ui/resolve/issue-111727.stderr new file mode 100644 index 00000000000..bd748211ed3 --- /dev/null +++ b/tests/ui/resolve/issue-111727.stderr @@ -0,0 +1,9 @@ +error[E0599]: no function or associated item named `create` found for trait `Any` + --> $DIR/issue-111727.rs:4:20 + | +LL | std::any::Any::create(); + | ^^^^^^ function or associated item not found in `Any` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/rfc-2008-non-exhaustive/borrowck-exhaustive.rs b/tests/ui/rfc-2008-non-exhaustive/borrowck-exhaustive.rs index 8f45b989f13..b9ff24c7624 100644 --- a/tests/ui/rfc-2008-non-exhaustive/borrowck-exhaustive.rs +++ b/tests/ui/rfc-2008-non-exhaustive/borrowck-exhaustive.rs @@ -3,7 +3,7 @@ // check-pass -#![allow(drop_ref)] +#![allow(dropping_references)] // aux-build:monovariants.rs extern crate monovariants; diff --git a/tests/ui/rfc-2294-if-let-guard/feature-gate.rs b/tests/ui/rfc-2294-if-let-guard/feature-gate.rs index f0105e08e27..3beb20f0a37 100644 --- a/tests/ui/rfc-2294-if-let-guard/feature-gate.rs +++ b/tests/ui/rfc-2294-if-let-guard/feature-gate.rs @@ -10,10 +10,12 @@ fn _if_let_guard() { () if (let 0 = 1) => {} //~^ ERROR `let` expressions in this position are unstable //~| ERROR expected expression, found `let` statement + //~| ERROR `let` expressions are not supported here () if (((let 0 = 1))) => {} //~^ ERROR `let` expressions in this position are unstable //~| ERROR expected expression, found `let` statement + //~| ERROR `let` expressions are not supported here () if true && let 0 = 1 => {} //~^ ERROR `if let` guards are experimental @@ -26,16 +28,20 @@ fn _if_let_guard() { () if (let 0 = 1) && true => {} //~^ ERROR `let` expressions in this position are unstable //~| ERROR expected expression, found `let` statement + //~| ERROR `let` expressions are not supported here () if true && (let 0 = 1) => {} //~^ ERROR `let` expressions in this position are unstable //~| ERROR expected expression, found `let` statement + //~| ERROR `let` expressions are not supported here () if (let 0 = 1) && (let 0 = 1) => {} //~^ ERROR `let` expressions in this position are unstable //~| ERROR `let` expressions in this position are unstable //~| ERROR expected expression, found `let` statement //~| ERROR expected expression, found `let` statement + //~| ERROR `let` expressions are not supported here + //~| ERROR `let` expressions are not supported here () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} //~^ ERROR `if let` guards are experimental @@ -47,6 +53,10 @@ fn _if_let_guard() { //~| ERROR expected expression, found `let` statement //~| ERROR expected expression, found `let` statement //~| ERROR expected expression, found `let` statement + //~| ERROR `let` expressions are not supported here + //~| ERROR `let` expressions are not supported here + //~| ERROR `let` expressions are not supported here + () if let Range { start: _, end: _ } = (true..true) && false => {} //~^ ERROR `if let` guards are experimental @@ -68,9 +78,11 @@ fn _macros() { use_expr!((let 0 = 1 && 0 == 0)); //~^ ERROR `let` expressions in this position are unstable //~| ERROR expected expression, found `let` statement + //~| ERROR `let` expressions are not supported here use_expr!((let 0 = 1)); //~^ ERROR `let` expressions in this position are unstable //~| ERROR expected expression, found `let` statement + //~| ERROR `let` expressions are not supported here match () { #[cfg(FALSE)] () if let 0 = 1 => {} diff --git a/tests/ui/rfc-2294-if-let-guard/feature-gate.stderr b/tests/ui/rfc-2294-if-let-guard/feature-gate.stderr index 96fe11911b7..dc182ce464a 100644 --- a/tests/ui/rfc-2294-if-let-guard/feature-gate.stderr +++ b/tests/ui/rfc-2294-if-let-guard/feature-gate.stderr @@ -5,67 +5,67 @@ LL | () if (let 0 = 1) => {} | ^^^ error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:14:18 + --> $DIR/feature-gate.rs:15:18 | LL | () if (((let 0 = 1))) => {} | ^^^ error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:26:16 + --> $DIR/feature-gate.rs:28:16 | LL | () if (let 0 = 1) && true => {} | ^^^ error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:30:24 + --> $DIR/feature-gate.rs:33:24 | LL | () if true && (let 0 = 1) => {} | ^^^ error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:34:16 + --> $DIR/feature-gate.rs:38:16 | LL | () if (let 0 = 1) && (let 0 = 1) => {} | ^^^ error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:34:31 + --> $DIR/feature-gate.rs:38:31 | LL | () if (let 0 = 1) && (let 0 = 1) => {} | ^^^ error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:40:42 + --> $DIR/feature-gate.rs:46:42 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^ error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:40:55 + --> $DIR/feature-gate.rs:46:55 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^ error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:40:68 + --> $DIR/feature-gate.rs:46:68 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^ error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:68:16 + --> $DIR/feature-gate.rs:78:16 | LL | use_expr!((let 0 = 1 && 0 == 0)); | ^^^ error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:71:16 + --> $DIR/feature-gate.rs:82:16 | LL | use_expr!((let 0 = 1)); | ^^^ error: no rules expected the token `let` - --> $DIR/feature-gate.rs:80:15 + --> $DIR/feature-gate.rs:92:15 | LL | macro_rules! use_expr { | --------------------- when calling this macro @@ -74,11 +74,154 @@ LL | use_expr!(let 0 = 1); | ^^^ no rules expected this token in macro call | note: while trying to match meta-variable `$e:expr` - --> $DIR/feature-gate.rs:61:10 + --> $DIR/feature-gate.rs:71:10 | LL | ($e:expr) => { | ^^^^^^^ +error: `let` expressions are not supported here + --> $DIR/feature-gate.rs:10:16 + | +LL | () if (let 0 = 1) => {} + | ^^^^^^^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions +note: `let`s wrapped in parentheses are not supported in a context with let chains + --> $DIR/feature-gate.rs:10:16 + | +LL | () if (let 0 = 1) => {} + | ^^^^^^^^^ + +error: `let` expressions are not supported here + --> $DIR/feature-gate.rs:15:18 + | +LL | () if (((let 0 = 1))) => {} + | ^^^^^^^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions +note: `let`s wrapped in parentheses are not supported in a context with let chains + --> $DIR/feature-gate.rs:15:18 + | +LL | () if (((let 0 = 1))) => {} + | ^^^^^^^^^ + +error: `let` expressions are not supported here + --> $DIR/feature-gate.rs:28:16 + | +LL | () if (let 0 = 1) && true => {} + | ^^^^^^^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions +note: `let`s wrapped in parentheses are not supported in a context with let chains + --> $DIR/feature-gate.rs:28:16 + | +LL | () if (let 0 = 1) && true => {} + | ^^^^^^^^^ + +error: `let` expressions are not supported here + --> $DIR/feature-gate.rs:33:24 + | +LL | () if true && (let 0 = 1) => {} + | ^^^^^^^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions +note: `let`s wrapped in parentheses are not supported in a context with let chains + --> $DIR/feature-gate.rs:33:24 + | +LL | () if true && (let 0 = 1) => {} + | ^^^^^^^^^ + +error: `let` expressions are not supported here + --> $DIR/feature-gate.rs:38:16 + | +LL | () if (let 0 = 1) && (let 0 = 1) => {} + | ^^^^^^^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions +note: `let`s wrapped in parentheses are not supported in a context with let chains + --> $DIR/feature-gate.rs:38:16 + | +LL | () if (let 0 = 1) && (let 0 = 1) => {} + | ^^^^^^^^^ + +error: `let` expressions are not supported here + --> $DIR/feature-gate.rs:38:31 + | +LL | () if (let 0 = 1) && (let 0 = 1) => {} + | ^^^^^^^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions +note: `let`s wrapped in parentheses are not supported in a context with let chains + --> $DIR/feature-gate.rs:38:31 + | +LL | () if (let 0 = 1) && (let 0 = 1) => {} + | ^^^^^^^^^ + +error: `let` expressions are not supported here + --> $DIR/feature-gate.rs:46:42 + | +LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} + | ^^^^^^^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions +note: `let`s wrapped in parentheses are not supported in a context with let chains + --> $DIR/feature-gate.rs:46:42 + | +LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `let` expressions are not supported here + --> $DIR/feature-gate.rs:46:55 + | +LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} + | ^^^^^^^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions +note: `let`s wrapped in parentheses are not supported in a context with let chains + --> $DIR/feature-gate.rs:46:42 + | +LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `let` expressions are not supported here + --> $DIR/feature-gate.rs:46:68 + | +LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} + | ^^^^^^^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions +note: `let`s wrapped in parentheses are not supported in a context with let chains + --> $DIR/feature-gate.rs:46:42 + | +LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `let` expressions are not supported here + --> $DIR/feature-gate.rs:78:16 + | +LL | use_expr!((let 0 = 1 && 0 == 0)); + | ^^^^^^^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions +note: `let`s wrapped in parentheses are not supported in a context with let chains + --> $DIR/feature-gate.rs:78:16 + | +LL | use_expr!((let 0 = 1 && 0 == 0)); + | ^^^^^^^^^^^^^^^^^^^ + +error: `let` expressions are not supported here + --> $DIR/feature-gate.rs:82:16 + | +LL | use_expr!((let 0 = 1)); + | ^^^^^^^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions +note: `let`s wrapped in parentheses are not supported in a context with let chains + --> $DIR/feature-gate.rs:82:16 + | +LL | use_expr!((let 0 = 1)); + | ^^^^^^^^^ + error[E0658]: `if let` guards are experimental --> $DIR/feature-gate.rs:7:12 | @@ -90,7 +233,7 @@ LL | () if let 0 = 1 => {} = help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>` error[E0658]: `if let` guards are experimental - --> $DIR/feature-gate.rs:18:12 + --> $DIR/feature-gate.rs:20:12 | LL | () if true && let 0 = 1 => {} | ^^^^^^^^^^^^^^^^^^^^ @@ -100,7 +243,7 @@ LL | () if true && let 0 = 1 => {} = help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>` error[E0658]: `if let` guards are experimental - --> $DIR/feature-gate.rs:22:12 + --> $DIR/feature-gate.rs:24:12 | LL | () if let 0 = 1 && true => {} | ^^^^^^^^^^^^^^^^^^^^ @@ -110,7 +253,7 @@ LL | () if let 0 = 1 && true => {} = help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>` error[E0658]: `if let` guards are experimental - --> $DIR/feature-gate.rs:40:12 + --> $DIR/feature-gate.rs:46:12 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -120,7 +263,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = = help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>` error[E0658]: `if let` guards are experimental - --> $DIR/feature-gate.rs:51:12 + --> $DIR/feature-gate.rs:61:12 | LL | () if let Range { start: _, end: _ } = (true..true) && false => {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -130,7 +273,7 @@ LL | () if let Range { start: _, end: _ } = (true..true) && false => {} = help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>` error[E0658]: `if let` guards are experimental - --> $DIR/feature-gate.rs:76:12 + --> $DIR/feature-gate.rs:88:12 | LL | () if let 0 = 1 => {} | ^^^^^^^^^^^^ @@ -149,7 +292,7 @@ LL | () if (let 0 = 1) => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:14:18 + --> $DIR/feature-gate.rs:15:18 | LL | () if (((let 0 = 1))) => {} | ^^^^^^^^^ @@ -158,7 +301,7 @@ LL | () if (((let 0 = 1))) => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:18:23 + --> $DIR/feature-gate.rs:20:23 | LL | () if true && let 0 = 1 => {} | ^^^^^^^^^ @@ -167,7 +310,7 @@ LL | () if true && let 0 = 1 => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:22:15 + --> $DIR/feature-gate.rs:24:15 | LL | () if let 0 = 1 && true => {} | ^^^^^^^^^ @@ -176,7 +319,7 @@ LL | () if let 0 = 1 && true => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:26:16 + --> $DIR/feature-gate.rs:28:16 | LL | () if (let 0 = 1) && true => {} | ^^^^^^^^^ @@ -185,7 +328,7 @@ LL | () if (let 0 = 1) && true => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:30:24 + --> $DIR/feature-gate.rs:33:24 | LL | () if true && (let 0 = 1) => {} | ^^^^^^^^^ @@ -194,7 +337,7 @@ LL | () if true && (let 0 = 1) => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:34:16 + --> $DIR/feature-gate.rs:38:16 | LL | () if (let 0 = 1) && (let 0 = 1) => {} | ^^^^^^^^^ @@ -203,7 +346,7 @@ LL | () if (let 0 = 1) && (let 0 = 1) => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:34:31 + --> $DIR/feature-gate.rs:38:31 | LL | () if (let 0 = 1) && (let 0 = 1) => {} | ^^^^^^^^^ @@ -212,7 +355,7 @@ LL | () if (let 0 = 1) && (let 0 = 1) => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:40:15 + --> $DIR/feature-gate.rs:46:15 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ @@ -221,7 +364,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:40:28 + --> $DIR/feature-gate.rs:46:28 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ @@ -230,7 +373,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:40:42 + --> $DIR/feature-gate.rs:46:42 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ @@ -239,7 +382,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:40:55 + --> $DIR/feature-gate.rs:46:55 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ @@ -248,7 +391,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:40:68 + --> $DIR/feature-gate.rs:46:68 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ @@ -257,7 +400,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:51:15 + --> $DIR/feature-gate.rs:61:15 | LL | () if let Range { start: _, end: _ } = (true..true) && false => {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -266,7 +409,7 @@ LL | () if let Range { start: _, end: _ } = (true..true) && false => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:68:16 + --> $DIR/feature-gate.rs:78:16 | LL | use_expr!((let 0 = 1 && 0 == 0)); | ^^^^^^^^^ @@ -275,7 +418,7 @@ LL | use_expr!((let 0 = 1 && 0 == 0)); = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:71:16 + --> $DIR/feature-gate.rs:82:16 | LL | use_expr!((let 0 = 1)); | ^^^^^^^^^ @@ -283,6 +426,6 @@ LL | use_expr!((let 0 = 1)); = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable -error: aborting due to 34 previous errors +error: aborting due to 45 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/rfc-2361-dbg-macro/dbg-macro-expected-behavior.rs b/tests/ui/rfc-2361-dbg-macro/dbg-macro-expected-behavior.rs index 4c1562790d5..542be3942b7 100644 --- a/tests/ui/rfc-2361-dbg-macro/dbg-macro-expected-behavior.rs +++ b/tests/ui/rfc-2361-dbg-macro/dbg-macro-expected-behavior.rs @@ -4,7 +4,7 @@ // Tests ensuring that `dbg!(expr)` has the expected run-time behavior. // as well as some compile time properties we expect. -#![allow(drop_copy)] +#![allow(dropping_copy_types)] #[derive(Copy, Clone, Debug)] struct Unit; diff --git a/tests/ui/rfc-2497-if-let-chains/ast-validate-guards.rs b/tests/ui/rfc-2497-if-let-chains/ast-validate-guards.rs new file mode 100644 index 00000000000..e6dee2a1d06 --- /dev/null +++ b/tests/ui/rfc-2497-if-let-chains/ast-validate-guards.rs @@ -0,0 +1,23 @@ +#![feature(let_chains)] + +fn let_or_guard(x: Result<Option<i32>, ()>) { + match x { + Ok(opt) if let Some(4) = opt || false => {} + //~^ ERROR `let` expressions are not supported here + _ => {} + } +} + +fn hiding_unsafe_mod(x: Result<Option<i32>, ()>) { + match x { + Ok(opt) + if { + unsafe mod a {}; + //~^ ERROR module cannot be declared unsafe + false + } => {} + _ => {} + } +} + +fn main() {} diff --git a/tests/ui/rfc-2497-if-let-chains/ast-validate-guards.stderr b/tests/ui/rfc-2497-if-let-chains/ast-validate-guards.stderr new file mode 100644 index 00000000000..26850998cc4 --- /dev/null +++ b/tests/ui/rfc-2497-if-let-chains/ast-validate-guards.stderr @@ -0,0 +1,21 @@ +error: `let` expressions are not supported here + --> $DIR/ast-validate-guards.rs:5:20 + | +LL | Ok(opt) if let Some(4) = opt || false => {} + | ^^^^^^^^^^^^^^^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions +note: `||` operators are not supported in let chain expressions + --> $DIR/ast-validate-guards.rs:5:38 + | +LL | Ok(opt) if let Some(4) = opt || false => {} + | ^^ + +error: module cannot be declared unsafe + --> $DIR/ast-validate-guards.rs:15:17 + | +LL | unsafe mod a {}; + | ^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/rust-2018/remove-extern-crate.fixed b/tests/ui/rust-2018/remove-extern-crate.fixed index 4ed4d610025..209b91af1dd 100644 --- a/tests/ui/rust-2018/remove-extern-crate.fixed +++ b/tests/ui/rust-2018/remove-extern-crate.fixed @@ -5,7 +5,7 @@ // compile-flags:--extern remove_extern_crate #![warn(rust_2018_idioms)] -#![allow(drop_copy)] +#![allow(dropping_copy_types)] //~ WARNING unused extern crate // Shouldn't suggest changing to `use`, as `another_name` diff --git a/tests/ui/rust-2018/remove-extern-crate.rs b/tests/ui/rust-2018/remove-extern-crate.rs index 5dafdb2b7b7..ef3c2db696a 100644 --- a/tests/ui/rust-2018/remove-extern-crate.rs +++ b/tests/ui/rust-2018/remove-extern-crate.rs @@ -5,7 +5,7 @@ // compile-flags:--extern remove_extern_crate #![warn(rust_2018_idioms)] -#![allow(drop_copy)] +#![allow(dropping_copy_types)] extern crate core; //~ WARNING unused extern crate // Shouldn't suggest changing to `use`, as `another_name` diff --git a/tests/ui/self/arbitrary-self-from-method-substs.rs b/tests/ui/self/arbitrary-self-from-method-substs.rs new file mode 100644 index 00000000000..0f911a20842 --- /dev/null +++ b/tests/ui/self/arbitrary-self-from-method-substs.rs @@ -0,0 +1,16 @@ +#![feature(arbitrary_self_types)] + +use std::ops::Deref; + +struct Foo(u32); +impl Foo { + fn get<R: Deref<Target=Self>>(self: R) -> u32 { + self.0 + } +} + +fn main() { + let mut foo = Foo(1); + foo.get::<&Foo>(); + //~^ ERROR mismatched types +} diff --git a/tests/ui/self/arbitrary-self-from-method-substs.stderr b/tests/ui/self/arbitrary-self-from-method-substs.stderr new file mode 100644 index 00000000000..6c252fadf46 --- /dev/null +++ b/tests/ui/self/arbitrary-self-from-method-substs.stderr @@ -0,0 +1,9 @@ +error[E0308]: mismatched types + --> $DIR/arbitrary-self-from-method-substs.rs:14:5 + | +LL | foo.get::<&Foo>(); + | ^^^ expected `&Foo`, found `Foo` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/statics/issue-91050-1.rs b/tests/ui/statics/issue-91050-1.rs index f59bcf0b803..c6268dba567 100644 --- a/tests/ui/statics/issue-91050-1.rs +++ b/tests/ui/statics/issue-91050-1.rs @@ -12,7 +12,7 @@ // // In regular builds, the bad cast was UB, like "Invalid LLVMRustVisibility value!" -#![allow(drop_copy)] +#![allow(dropping_copy_types)] pub mod before { #[no_mangle] diff --git a/tests/ui/suggestions/issue-109854.rs b/tests/ui/suggestions/issue-109854.rs new file mode 100644 index 00000000000..dd4542dd71f --- /dev/null +++ b/tests/ui/suggestions/issue-109854.rs @@ -0,0 +1,12 @@ +fn generate_setter() { + String::with_capacity( + //~^ ERROR this function takes 1 argument but 3 arguments were supplied + generate_setter, + r#" +pub(crate) struct Person<T: Clone> {} +"#, + r#""#, + ); +} + +fn main() {} diff --git a/tests/ui/suggestions/issue-109854.stderr b/tests/ui/suggestions/issue-109854.stderr new file mode 100644 index 00000000000..621a3897165 --- /dev/null +++ b/tests/ui/suggestions/issue-109854.stderr @@ -0,0 +1,31 @@ +error[E0061]: this function takes 1 argument but 3 arguments were supplied + --> $DIR/issue-109854.rs:2:5 + | +LL | String::with_capacity( + | ^^^^^^^^^^^^^^^^^^^^^ +... +LL | / r#" +LL | | pub(crate) struct Person<T: Clone> {} +LL | | "#, + | |__- unexpected argument of type `&'static str` +LL | r#""#, + | ----- unexpected argument of type `&'static str` + | +note: expected `usize`, found fn item + --> $DIR/issue-109854.rs:4:5 + | +LL | generate_setter, + | ^^^^^^^^^^^^^^^ + = note: expected type `usize` + found fn item `fn() {generate_setter}` +note: associated function defined here + --> $SRC_DIR/alloc/src/string.rs:LL:COL +help: remove the extra arguments + | +LL - generate_setter, +LL + /* usize */, + | + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0061`. diff --git a/tests/ui/suggestions/issue-94171.rs b/tests/ui/suggestions/issue-94171.rs new file mode 100644 index 00000000000..cbb9f9cec72 --- /dev/null +++ b/tests/ui/suggestions/issue-94171.rs @@ -0,0 +1,5 @@ +fn L(]{match +(; {` +//~^^ ERROR mismatched closing delimiter +//~^^ ERROR unknown start of token +//~ ERROR this file contains an unclosed delimiter diff --git a/tests/ui/suggestions/issue-94171.stderr b/tests/ui/suggestions/issue-94171.stderr new file mode 100644 index 00000000000..b3440e46e8a --- /dev/null +++ b/tests/ui/suggestions/issue-94171.stderr @@ -0,0 +1,36 @@ +error: unknown start of token: ` + --> $DIR/issue-94171.rs:2:5 + | +LL | (; {` + | ^ + | +help: Unicode character '`' (Grave Accent) looks like ''' (Single Quote), but it is not + | +LL | (; {' + | ~ + +error: mismatched closing delimiter: `]` + --> $DIR/issue-94171.rs:1:5 + | +LL | fn L(]{match + | ^^ mismatched closing delimiter + | | + | unclosed delimiter + +error: this file contains an unclosed delimiter + --> $DIR/issue-94171.rs:5:52 + | +LL | fn L(]{match + | -- unclosed delimiter + | | + | missing open `[` for this delimiter +LL | (; {` + | - - unclosed delimiter + | | + | unclosed delimiter +... +LL | + | ^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/test-attrs/tests-listing-format-json-without-unstableopts.run.stderr b/tests/ui/test-attrs/tests-listing-format-json-without-unstableopts.run.stderr index 9f6276300a0..5d8b3755441 100644 --- a/tests/ui/test-attrs/tests-listing-format-json-without-unstableopts.run.stderr +++ b/tests/ui/test-attrs/tests-listing-format-json-without-unstableopts.run.stderr @@ -1 +1 @@ -error: The "json" format is only accepted on the nightly compiler +error: The "json" format is only accepted on the nightly compiler with -Z unstable-options diff --git a/tests/ui/traits/copy-guessing.rs b/tests/ui/traits/copy-guessing.rs index c1ed4c28a03..af25010e3bd 100644 --- a/tests/ui/traits/copy-guessing.rs +++ b/tests/ui/traits/copy-guessing.rs @@ -1,5 +1,5 @@ #![allow(dead_code)] -#![allow(drop_copy)] +#![allow(dropping_copy_types)] // "guessing" in trait selection can affect `copy_or_move`. Check that this // is correctly handled. I am not sure what is the "correct" behaviour, diff --git a/tests/ui/traits/cycle-cache-err-60010.stderr b/tests/ui/traits/cycle-cache-err-60010.stderr index 2ff16b4af38..aee41c43aef 100644 --- a/tests/ui/traits/cycle-cache-err-60010.stderr +++ b/tests/ui/traits/cycle-cache-err-60010.stderr @@ -1,9 +1,25 @@ -error[E0275]: overflow evaluating the requirement `RootDatabase: RefUnwindSafe` +error[E0275]: overflow evaluating the requirement `SalsaStorage: RefUnwindSafe` --> $DIR/cycle-cache-err-60010.rs:27:13 | LL | _parse: <ParseQuery as Query<RootDatabase>>::Data, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | +note: required because it appears within the type `PhantomData<SalsaStorage>` + --> $SRC_DIR/core/src/marker.rs:LL:COL +note: required because it appears within the type `Unique<SalsaStorage>` + --> $SRC_DIR/core/src/ptr/unique.rs:LL:COL +note: required because it appears within the type `Box<SalsaStorage>` + --> $SRC_DIR/alloc/src/boxed.rs:LL:COL +note: required because it appears within the type `Runtime<RootDatabase>` + --> $DIR/cycle-cache-err-60010.rs:23:8 + | +LL | struct Runtime<DB: Database> { + | ^^^^^^^ +note: required because it appears within the type `RootDatabase` + --> $DIR/cycle-cache-err-60010.rs:20:8 + | +LL | struct RootDatabase { + | ^^^^^^^^^^^^ note: required for `RootDatabase` to implement `SourceDatabase` --> $DIR/cycle-cache-err-60010.rs:44:9 | diff --git a/tests/ui/traits/impl-evaluation-order.rs b/tests/ui/traits/impl-evaluation-order.rs index 256ce992eef..2ce0b6b0df8 100644 --- a/tests/ui/traits/impl-evaluation-order.rs +++ b/tests/ui/traits/impl-evaluation-order.rs @@ -6,7 +6,7 @@ // check-pass -#![allow(drop_copy)] +#![allow(dropping_copy_types)] trait A { type B; diff --git a/tests/ui/traits/new-solver/alias_eq_dont_use_normalizes_to_if_substs_eq.rs b/tests/ui/traits/new-solver/alias_eq_dont_use_normalizes_to_if_substs_eq.rs index 531203d9c64..3c7fc7403b1 100644 --- a/tests/ui/traits/new-solver/alias_eq_dont_use_normalizes_to_if_substs_eq.rs +++ b/tests/ui/traits/new-solver/alias_eq_dont_use_normalizes_to_if_substs_eq.rs @@ -1,5 +1,8 @@ // compile-flags: -Ztrait-solver=next +// check-pass +// (should not pass, should be turned into a coherence-only test) + // check that when computing `alias-eq(<() as Foo<u16, T>>::Assoc, <() as Foo<?0, T>>::Assoc)` // we do not infer `?0 = u8` via the `for<STOP> (): Foo<u8, STOP>` impl or `?0 = u16` by // relating substs as either could be a valid solution. @@ -36,7 +39,6 @@ where { // `<() as Foo<u16, STOP>>::Assoc == <() as Foo<_, STOP>>::Assoc` let _: <() as Foo<u16, T>>::Assoc = output::<_, T>(); - //~^ error: type annotations needed // let _: <() as Foo<u16, T>>::Assoc = output::<u8, T>(); // OK // let _: <() as Foo<u16, T>>::Assoc = output::<u16, T>(); // OK diff --git a/tests/ui/traits/new-solver/alias_eq_dont_use_normalizes_to_if_substs_eq.stderr b/tests/ui/traits/new-solver/alias_eq_dont_use_normalizes_to_if_substs_eq.stderr deleted file mode 100644 index a6712332c37..00000000000 --- a/tests/ui/traits/new-solver/alias_eq_dont_use_normalizes_to_if_substs_eq.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0282]: type annotations needed - --> $DIR/alias_eq_dont_use_normalizes_to_if_substs_eq.rs:38:41 - | -LL | let _: <() as Foo<u16, T>>::Assoc = output::<_, T>(); - | ^^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `output` - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/traits/new-solver/alias_eq_substs_eq_not_intercrate.rs b/tests/ui/traits/new-solver/alias_eq_substs_eq_not_intercrate.rs index d4cc380fa21..b036411be83 100644 --- a/tests/ui/traits/new-solver/alias_eq_substs_eq_not_intercrate.rs +++ b/tests/ui/traits/new-solver/alias_eq_substs_eq_not_intercrate.rs @@ -1,5 +1,8 @@ // compile-flags: -Ztrait-solver=next +// check-pass +// (should not pass, should be turned into a coherence-only test) + // check that a `alias-eq(<?0 as TraitB>::Assoc, <T as TraitB>::Assoc)` goal fails. // FIXME(deferred_projection_equality): add a test that this is true during coherence @@ -14,7 +17,6 @@ fn needs_a<T: TraitB>() -> T::Assoc { fn bar<T: TraitB>() { let _: <_ as TraitB>::Assoc = needs_a::<T>(); - //~^ error: type annotations needed } fn main() {} diff --git a/tests/ui/traits/new-solver/alias_eq_substs_eq_not_intercrate.stderr b/tests/ui/traits/new-solver/alias_eq_substs_eq_not_intercrate.stderr deleted file mode 100644 index d063d8fce11..00000000000 --- a/tests/ui/traits/new-solver/alias_eq_substs_eq_not_intercrate.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0282]: type annotations needed - --> $DIR/alias_eq_substs_eq_not_intercrate.rs:16:12 - | -LL | let _: <_ as TraitB>::Assoc = needs_a::<T>(); - | ^^^^^^^^^^^^^^^^^^^^ cannot infer type for associated type `<_ as TraitB>::Assoc` - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/traits/new-solver/auto-with-drop_tracking_mir.rs b/tests/ui/traits/new-solver/auto-with-drop_tracking_mir.rs index f115e143318..e311a4af2f4 100644 --- a/tests/ui/traits/new-solver/auto-with-drop_tracking_mir.rs +++ b/tests/ui/traits/new-solver/auto-with-drop_tracking_mir.rs @@ -14,7 +14,7 @@ async fn foo() { #[cfg(fail)] let x = &NotSync; bar().await; - #[allow(drop_ref)] + #[allow(dropping_references)] drop(x); } diff --git a/tests/ui/traits/new-solver/normalize-rcvr-for-inherent.rs b/tests/ui/traits/new-solver/normalize-rcvr-for-inherent.rs new file mode 100644 index 00000000000..d70534feb07 --- /dev/null +++ b/tests/ui/traits/new-solver/normalize-rcvr-for-inherent.rs @@ -0,0 +1,25 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +// Verify that we can assemble inherent impl candidates on a possibly +// unnormalized self type. + +trait Foo { + type Assoc; +} +impl Foo for i32 { + type Assoc = Bar; +} + +struct Bar; +impl Bar { + fn method(&self) {} +} + +fn build<T: Foo>(_: T) -> T::Assoc { + todo!() +} + +fn main() { + build(1i32).method(); +} diff --git a/tests/ui/traits/new-solver/structural-resolve-field.rs b/tests/ui/traits/new-solver/structural-resolve-field.rs new file mode 100644 index 00000000000..01899c9ad64 --- /dev/null +++ b/tests/ui/traits/new-solver/structural-resolve-field.rs @@ -0,0 +1,13 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +#[derive(Default)] +struct Foo { + x: i32, +} + +fn main() { + let mut xs = <[Foo; 1]>::default(); + xs[0].x = 1; + (&mut xs[0]).x = 2; +} diff --git a/tests/ui/traits/solver-cycles/cycle-via-builtin-auto-trait-impl.rs b/tests/ui/traits/solver-cycles/cycle-via-builtin-auto-trait-impl.rs new file mode 100644 index 00000000000..d37943b929a --- /dev/null +++ b/tests/ui/traits/solver-cycles/cycle-via-builtin-auto-trait-impl.rs @@ -0,0 +1,34 @@ +//~ ERROR overflow +// A regression test for #111729 checking that we correctly +// track recursion depth for obligations returned by confirmation. +use std::panic::RefUnwindSafe; + +trait Database { + type Storage; +} +trait Query<DB> { + type Data; +} +struct ParseQuery; +struct RootDatabase { + _runtime: Runtime<RootDatabase>, +} + +impl<T: RefUnwindSafe> Database for T { + type Storage = SalsaStorage; +} +impl Database for RootDatabase { + type Storage = SalsaStorage; +} + +struct Runtime<DB: Database> { + _storage: Box<DB::Storage>, +} +struct SalsaStorage { + _parse: <ParseQuery as Query<RootDatabase>>::Data, +} + +impl<DB: Database> Query<DB> for ParseQuery { + type Data = RootDatabase; +} +fn main() {} diff --git a/tests/ui/traits/solver-cycles/cycle-via-builtin-auto-trait-impl.stderr b/tests/ui/traits/solver-cycles/cycle-via-builtin-auto-trait-impl.stderr new file mode 100644 index 00000000000..8f9ce3ef1e9 --- /dev/null +++ b/tests/ui/traits/solver-cycles/cycle-via-builtin-auto-trait-impl.stderr @@ -0,0 +1,24 @@ +error[E0275]: overflow evaluating the requirement `Runtime<RootDatabase>: RefUnwindSafe` + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_via_builtin_auto_trait_impl`) +note: required because it appears within the type `RootDatabase` + --> $DIR/cycle-via-builtin-auto-trait-impl.rs:13:8 + | +LL | struct RootDatabase { + | ^^^^^^^^^^^^ +note: required for `RootDatabase` to implement `Database` + --> $DIR/cycle-via-builtin-auto-trait-impl.rs:17:24 + | +LL | impl<T: RefUnwindSafe> Database for T { + | ------------- ^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here +note: required because it appears within the type `Runtime<RootDatabase>` + --> $DIR/cycle-via-builtin-auto-trait-impl.rs:24:8 + | +LL | struct Runtime<DB: Database> { + | ^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/trivial-bounds/trivial-bounds-inconsistent-copy.rs b/tests/ui/trivial-bounds/trivial-bounds-inconsistent-copy.rs index 6ed7667115a..f98c3164d7e 100644 --- a/tests/ui/trivial-bounds/trivial-bounds-inconsistent-copy.rs +++ b/tests/ui/trivial-bounds/trivial-bounds-inconsistent-copy.rs @@ -2,7 +2,7 @@ // Check tautalogically false `Copy` bounds #![feature(trivial_bounds)] -#![allow(drop_ref, drop_copy)] +#![allow(dropping_references, dropping_copy_types)] fn copy_string(t: String) -> String where String: Copy { //~ WARNING trivial_bounds is_copy(&t); diff --git a/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-2.rs b/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-2.rs new file mode 100644 index 00000000000..e5bfbfdae91 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-2.rs @@ -0,0 +1,15 @@ +#![feature(type_alias_impl_trait)] + +type Tait<'a> = impl Sized + 'a; + +fn foo<'a, 'b>() { + if false { + if { return } { + let y: Tait<'b> = 1i32; + //~^ ERROR concrete type differs from previous defining opaque type use + } + } + let x: Tait<'a> = (); +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-2.stderr b/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-2.stderr new file mode 100644 index 00000000000..f2eb7bc4dc7 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-2.stderr @@ -0,0 +1,14 @@ +error: concrete type differs from previous defining opaque type use + --> $DIR/different_defining_uses_never_type-2.rs:8:31 + | +LL | let y: Tait<'b> = 1i32; + | ^^^^ expected `()`, got `i32` + | +note: previous use here + --> $DIR/different_defining_uses_never_type-2.rs:12:23 + | +LL | let x: Tait<'a> = (); + | ^^ + +error: aborting due to previous error + diff --git a/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-3.rs b/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-3.rs new file mode 100644 index 00000000000..2b30a9cd57c --- /dev/null +++ b/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-3.rs @@ -0,0 +1,15 @@ +#![feature(type_alias_impl_trait)] + +type Tait<T> = impl Sized; + +fn foo<T, U>() { + if false { + if { return } { + let y: Tait<U> = 1i32; + //~^ ERROR concrete type differs from previous defining opaque type use + } + } + let x: Tait<T> = (); +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-3.stderr b/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-3.stderr new file mode 100644 index 00000000000..8fc2e22848c --- /dev/null +++ b/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-3.stderr @@ -0,0 +1,14 @@ +error: concrete type differs from previous defining opaque type use + --> $DIR/different_defining_uses_never_type-3.rs:8:30 + | +LL | let y: Tait<U> = 1i32; + | ^^^^ expected `()`, got `i32` + | +note: previous use here + --> $DIR/different_defining_uses_never_type-3.rs:12:22 + | +LL | let x: Tait<T> = (); + | ^^ + +error: aborting due to previous error + diff --git a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn.rs b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn.rs index da845e86147..9ae2c34b935 100644 --- a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn.rs +++ b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn.rs @@ -8,6 +8,7 @@ type X<A, B> = impl Into<&'static A>; fn f<A, B: 'static>(a: &'static A, b: B) -> (X<A, B>, X<B, A>) { //~^ ERROR the trait bound `&'static B: From<&A>` is not satisfied + //~| ERROR concrete type differs from previous defining opaque type use (a, a) } diff --git a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn.stderr b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn.stderr index 66a6b0bbf74..0d24d42ba62 100644 --- a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn.stderr +++ b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn.stderr @@ -10,6 +10,15 @@ help: consider introducing a `where` clause, but there might be an alternative b LL | fn f<A, B: 'static>(a: &'static A, b: B) -> (X<A, B>, X<B, A>) where &'static B: From<&A> { | ++++++++++++++++++++++++++ -error: aborting due to previous error +error: concrete type differs from previous defining opaque type use + --> $DIR/multiple-def-uses-in-one-fn.rs:9:45 + | +LL | fn f<A, B: 'static>(a: &'static A, b: B) -> (X<A, B>, X<B, A>) { + | ^^^^^^^^^^^^^^^^^^ + | | + | expected `&B`, got `&A` + | this expression supplies two conflicting concrete types for the same opaque type + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/uninhabited/uninhabited-irrefutable.rs b/tests/ui/uninhabited/uninhabited-irrefutable.rs index 4b001aca2d1..cfd60a8d903 100644 --- a/tests/ui/uninhabited/uninhabited-irrefutable.rs +++ b/tests/ui/uninhabited/uninhabited-irrefutable.rs @@ -16,7 +16,9 @@ struct NotSoSecretlyEmpty { } enum Foo { + //~^ NOTE `Foo` defined here A(foo::SecretlyEmpty), + //~^ NOTE not covered B(foo::NotSoSecretlyEmpty), C(NotSoSecretlyEmpty), D(u32, u32), @@ -27,4 +29,9 @@ fn main() { let Foo::D(_y, _z) = x; //~^ ERROR refutable pattern in local binding //~| `Foo::A(_)` not covered + //~| NOTE `let` bindings require an "irrefutable pattern" + //~| NOTE for more information + //~| NOTE pattern `Foo::A(_)` is currently uninhabited + //~| NOTE the matched value is of type `Foo` + //~| HELP you might want to use `let else` } diff --git a/tests/ui/uninhabited/uninhabited-irrefutable.stderr b/tests/ui/uninhabited/uninhabited-irrefutable.stderr index 8cafea555c1..daf75f51b5a 100644 --- a/tests/ui/uninhabited/uninhabited-irrefutable.stderr +++ b/tests/ui/uninhabited/uninhabited-irrefutable.stderr @@ -1,5 +1,5 @@ error[E0005]: refutable pattern in local binding - --> $DIR/uninhabited-irrefutable.rs:27:9 + --> $DIR/uninhabited-irrefutable.rs:29:9 | LL | let Foo::D(_y, _z) = x; | ^^^^^^^^^^^^^^ pattern `Foo::A(_)` not covered @@ -11,8 +11,10 @@ note: `Foo` defined here | LL | enum Foo { | ^^^ +LL | LL | A(foo::SecretlyEmpty), | - not covered + = note: pattern `Foo::A(_)` is currently uninhabited, but this variant contains private fields which may become inhabited in the future = note: the matched value is of type `Foo` help: you might want to use `let else` to handle the variant that isn't matched | diff --git a/tests/ui/unsized-locals/issue-67981.rs b/tests/ui/unsized-locals/issue-67981.rs new file mode 100644 index 00000000000..3eb6498e9dc --- /dev/null +++ b/tests/ui/unsized-locals/issue-67981.rs @@ -0,0 +1,9 @@ +#![feature(unsized_fn_params)] + +fn main() { + let f: fn([u8]) = |_| {}; + //~^ERROR the size for values of type `[u8]` cannot be known at compilation time + let slice: Box<[u8]> = Box::new([1; 8]); + + f(*slice); +} diff --git a/tests/ui/unsized-locals/issue-67981.stderr b/tests/ui/unsized-locals/issue-67981.stderr new file mode 100644 index 00000000000..a4b179ae2fd --- /dev/null +++ b/tests/ui/unsized-locals/issue-67981.stderr @@ -0,0 +1,15 @@ +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/issue-67981.rs:4:24 + | +LL | let f: fn([u8]) = |_| {}; + | ^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | let f: fn([u8]) = |&_| {}; + | + + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/unsized/box-instead-of-dyn-fn.rs b/tests/ui/unsized/box-instead-of-dyn-fn.rs index 2fa741bc1c5..321c2ebf5a1 100644 --- a/tests/ui/unsized/box-instead-of-dyn-fn.rs +++ b/tests/ui/unsized/box-instead-of-dyn-fn.rs @@ -8,7 +8,6 @@ fn print_on_or_the_other<'a>(a: i32, b: &'a String) -> dyn Fn() + 'a { move || println!("{a}") } else { Box::new(move || println!("{}", b)) - //~^ ERROR `if` and `else` have incompatible types } } diff --git a/tests/ui/unsized/box-instead-of-dyn-fn.stderr b/tests/ui/unsized/box-instead-of-dyn-fn.stderr index bfb7c3957f4..6087f5c5465 100644 --- a/tests/ui/unsized/box-instead-of-dyn-fn.stderr +++ b/tests/ui/unsized/box-instead-of-dyn-fn.stderr @@ -1,42 +1,21 @@ -error[E0308]: `if` and `else` have incompatible types - --> $DIR/box-instead-of-dyn-fn.rs:10:9 - | -LL | / if a % 2 == 0 { -LL | | move || println!("{a}") - | | ----------------------- - | | | - | | the expected closure - | | expected because of this -LL | | } else { -LL | | Box::new(move || println!("{}", b)) - | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected closure, found `Box<[closure@box-instead-of-dyn-fn.rs:10:18]>` -LL | | -LL | | } - | |_____- `if` and `else` have incompatible types - | - = note: expected closure `[closure@$DIR/box-instead-of-dyn-fn.rs:8:9: 8:16]` - found struct `Box<[closure@$DIR/box-instead-of-dyn-fn.rs:10:18: 10:25]>` - error[E0746]: return type cannot have an unboxed trait object --> $DIR/box-instead-of-dyn-fn.rs:5:56 | LL | fn print_on_or_the_other<'a>(a: i32, b: &'a String) -> dyn Fn() + 'a { | ^^^^^^^^^^^^^ doesn't have a size known at compile-time | - = note: for information on trait objects, see <https://doc.rust-lang.org/book/ch17-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types> - = note: if all the returned values were of the same type you could use `impl Fn() + 'a` as the return type - = note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits> - = note: you can create a new `enum` with a variant for each returned type -help: return a boxed trait object instead +help: return an `impl Trait` instead of a `dyn Trait`, if all returned values are the same type + | +LL | fn print_on_or_the_other<'a>(a: i32, b: &'a String) -> impl Fn() + 'a { + | ~~~~ +help: box the return type, and wrap all of the returned values in `Box::new` | -LL | fn print_on_or_the_other<'a>(a: i32, b: &'a String) -> Box<dyn Fn() + 'a> { - | ++++ + -help: ... and box this value +LL ~ fn print_on_or_the_other<'a>(a: i32, b: &'a String) -> Box<dyn Fn() + 'a> { +LL | +LL | if a % 2 == 0 { +LL ~ Box::new(move || println!("{a}")) | -LL | Box::new(move || println!("{a}")) - | +++++++++ + -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0308, E0746. -For more information about an error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0746`. diff --git a/tests/ui/unsized/issue-91801.stderr b/tests/ui/unsized/issue-91801.stderr index 8795aa1687f..da5c4322403 100644 --- a/tests/ui/unsized/issue-91801.stderr +++ b/tests/ui/unsized/issue-91801.stderr @@ -4,11 +4,10 @@ error[E0746]: return type cannot have an unboxed trait object LL | fn or<'a>(first: &'static Validator<'a>, second: &'static Validator<'a>) -> Validator<'a> { | ^^^^^^^^^^^^^ doesn't have a size known at compile-time | - = note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits> -help: use `impl Fn(&'a Something) -> Result<(), ()> + Send + Sync + 'a` as the return type, as all return paths are of type `Box<[closure@$DIR/issue-91801.rs:10:21: 10:70]>`, which implements `Fn(&'a Something) -> Result<(), ()> + Send + Sync + 'a` +help: box the return type, and wrap all of the returned values in `Box::new` | -LL | fn or<'a>(first: &'static Validator<'a>, second: &'static Validator<'a>) -> impl Fn(&'a Something) -> Result<(), ()> + Send + Sync + 'a { - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | fn or<'a>(first: &'static Validator<'a>, second: &'static Validator<'a>) -> Box<Validator<'a>> { + | ++++ + error: aborting due to previous error diff --git a/tests/ui/unsized/issue-91803.stderr b/tests/ui/unsized/issue-91803.stderr index 2dad9e89294..a43b8d0741f 100644 --- a/tests/ui/unsized/issue-91803.stderr +++ b/tests/ui/unsized/issue-91803.stderr @@ -4,11 +4,14 @@ error[E0746]: return type cannot have an unboxed trait object LL | fn or<'a>(first: &'static dyn Foo<'a>) -> dyn Foo<'a> { | ^^^^^^^^^^^ doesn't have a size known at compile-time | - = note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits> -help: use `impl Foo<'a>` as the return type, as all return paths are of type `Box<_>`, which implements `Foo<'a>` +help: return an `impl Trait` instead of a `dyn Trait`, if all returned values are the same type | LL | fn or<'a>(first: &'static dyn Foo<'a>) -> impl Foo<'a> { - | ~~~~~~~~~~~~ + | ~~~~ +help: box the return type, and wrap all of the returned values in `Box::new` + | +LL | fn or<'a>(first: &'static dyn Foo<'a>) -> Box<dyn Foo<'a>> { + | ++++ + error: aborting due to previous error diff --git a/tests/ui/use/use-self-type.stderr b/tests/ui/use/use-self-type.stderr index e6153941151..3da04a851f6 100644 --- a/tests/ui/use/use-self-type.stderr +++ b/tests/ui/use/use-self-type.stderr @@ -8,7 +8,7 @@ error[E0432]: unresolved import `Self` --> $DIR/use-self-type.rs:6:13 | LL | use Self::f; - | ^^^^ `Self` is only available in impls, traits, and type definitions + | ^^^^ `Self` cannot be used in imports error: aborting due to 2 previous errors diff --git a/triagebot.toml b/triagebot.toml index 046d732bd42..d7cd3ea1275 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -482,7 +482,6 @@ compiler-team = [ "@petrochenkov", "@davidtwco", "@oli-obk", - "@lcnr", "@wesleywiser", ] compiler-team-contributors = [ @@ -593,6 +592,7 @@ style-team = [ "/compiler/rustc_llvm" = ["@cuviper"] "/compiler/rustc_middle/src/mir" = ["compiler", "mir"] "/compiler/rustc_middle/src/traits" = ["compiler", "types"] +"/compiler/rustc_middle/src/ty" = ["compiler", "types"] "/compiler/rustc_const_eval/src/interpret" = ["compiler", "mir"] "/compiler/rustc_const_eval/src/transform" = ["compiler", "mir-opt"] "/compiler/rustc_mir_build/src/build" = ["compiler", "mir"] |
