diff options
407 files changed, 5953 insertions, 2050 deletions
diff --git a/Cargo.lock b/Cargo.lock index 361d237b3af..b141729a126 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -161,9 +161,6 @@ name = "anyhow" version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" -dependencies = [ - "backtrace", -] [[package]] name = "ar_archive_writer" diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 9d216ef3dd8..114b9835b98 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1633,6 +1633,9 @@ pub enum ExprKind { /// An `if` block, with an optional `else` block. /// /// `if expr { block } else { expr }` + /// + /// If present, the "else" expr is always `ExprKind::Block` (for `else`) or + /// `ExprKind::If` (for `else if`). If(P<Expr>, P<Block>, Option<P<Expr>>), /// A while loop, with an optional label. /// diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 8b1c63cd21d..9f9d1f9a556 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -55,8 +55,8 @@ use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle, StashKey}; use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId}; use rustc_hir::{ - self as hir, ConstArg, GenericArg, HirId, ItemLocalMap, LangItem, LifetimeSource, - LifetimeSyntax, ParamName, TraitCandidate, + self as hir, AngleBrackets, ConstArg, GenericArg, HirId, ItemLocalMap, LangItem, + LifetimeSource, LifetimeSyntax, ParamName, TraitCandidate, }; use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_macros::extension; @@ -1087,7 +1087,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { match arg { ast::GenericArg::Lifetime(lt) => GenericArg::Lifetime(self.lower_lifetime( lt, - LifetimeSource::Path { with_angle_brackets: true }, + LifetimeSource::Path { angle_brackets: hir::AngleBrackets::Full }, lt.ident.into(), )), ast::GenericArg::Type(ty) => { @@ -1779,13 +1779,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { &mut self, id: NodeId, span: Span, - with_angle_brackets: bool, + angle_brackets: AngleBrackets, ) -> &'hir hir::Lifetime { self.new_named_lifetime( id, id, Ident::new(kw::UnderscoreLifetime, span), - LifetimeSource::Path { with_angle_brackets }, + LifetimeSource::Path { angle_brackets }, LifetimeSyntax::Hidden, ) } diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index fabe40a9d04..5cda64ce7b4 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -432,27 +432,31 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // Note: these spans are used for diagnostics when they can't be inferred. // See rustc_resolve::late::lifetimes::LifetimeContext::add_missing_lifetime_specifiers_label - let (elided_lifetime_span, with_angle_brackets) = if generic_args.span.is_empty() { - // If there are no brackets, use the identifier span. + let (elided_lifetime_span, angle_brackets) = if generic_args.span.is_empty() { + // No brackets, e.g. `Path`: use an empty span just past the end of the identifier. // HACK: we use find_ancestor_inside to properly suggest elided spans in paths // originating from macros, since the segment's span might be from a macro arg. - (segment_ident_span.find_ancestor_inside(path_span).unwrap_or(path_span), false) - } else if generic_args.is_empty() { - // If there are brackets, but not generic arguments, then use the opening bracket - (generic_args.span.with_hi(generic_args.span.lo() + BytePos(1)), true) + ( + segment_ident_span.find_ancestor_inside(path_span).unwrap_or(path_span), + hir::AngleBrackets::Missing, + ) } else { - // Else use an empty span right after the opening bracket. - (generic_args.span.with_lo(generic_args.span.lo() + BytePos(1)).shrink_to_lo(), true) + // Brackets, e.g. `Path<>` or `Path<T>`: use an empty span just after the `<`. + ( + generic_args.span.with_lo(generic_args.span.lo() + BytePos(1)).shrink_to_lo(), + if generic_args.is_empty() { + hir::AngleBrackets::Empty + } else { + hir::AngleBrackets::Full + }, + ) }; generic_args.args.insert_many( 0, (start..end).map(|id| { - let l = self.lower_lifetime_hidden_in_path( - id, - elided_lifetime_span, - with_angle_brackets, - ); + let l = + self.lower_lifetime_hidden_in_path(id, elided_lifetime_span, angle_brackets); GenericArg::Lifetime(l) }), ); diff --git a/compiler/rustc_ast_pretty/src/pp.rs b/compiler/rustc_ast_pretty/src/pp.rs index 142c80b8e39..8a0dbadf18c 100644 --- a/compiler/rustc_ast_pretty/src/pp.rs +++ b/compiler/rustc_ast_pretty/src/pp.rs @@ -244,9 +244,6 @@ struct BufEntry { // forgotten will trigger a panic in `drop`. (Closing a box more than once // isn't possible because `BoxMarker` doesn't implement `Copy` or `Clone`.) // -// FIXME(nnethercote): the panic in `drop` is currently disabled because a few -// places fail to close their boxes. It can be enabled once they are fixed. -// // Note: it would be better to make open/close mismatching impossible and avoid // the need for this marker type altogether by having functions like // `with_ibox` that open a box, call a closure, and then close the box. That @@ -261,8 +258,7 @@ impl !Copy for BoxMarker {} impl Drop for BoxMarker { fn drop(&mut self) { - // FIXME(nnethercote): enable once the bad cases are fixed - //panic!("BoxMarker not ended with `Printer::end()`"); + panic!("BoxMarker not ended with `Printer::end()`"); } } diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index b5925fab7d9..28d5eb87c27 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -240,6 +240,11 @@ pub fn print_crate<'a>( let mut s = State { s: pp::Printer::new(), comments: Some(Comments::new(sm, filename, input)), ann }; + // We need to print shebang before anything else + // otherwise the resulting code will not compile + // and shebang will be useless. + s.maybe_print_shebang(); + if is_expanded && !krate.attrs.iter().any(|attr| attr.has_name(sym::no_core)) { // We need to print `#![no_std]` (and its feature gate) so that // compiling pretty-printed source won't inject libstd again. @@ -560,6 +565,20 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere self.word(st) } + fn maybe_print_shebang(&mut self) { + if let Some(cmnt) = self.peek_comment() { + // Comment is a shebang if it's: + // Isolated, starts with #! and doesn't continue with `[` + // See [rustc_lexer::strip_shebang] and [gather_comments] from pprust/state.rs for details + if cmnt.style == CommentStyle::Isolated + && cmnt.lines.first().map_or(false, |l| l.starts_with("#!")) + { + let cmnt = self.next_comment().unwrap(); + self.print_comment(cmnt); + } + } + } + fn print_inner_attributes(&mut self, attrs: &[ast::Attribute]) -> bool { self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, true) } diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 38cadc77b77..c9a7e2aebd0 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -13,7 +13,7 @@ use rustc_ast::{ use crate::pp::Breaks::Inconsistent; use crate::pprust::state::fixup::FixupContext; -use crate::pprust::state::{AnnNode, BoxMarker, INDENT_UNIT, PrintState, State}; +use crate::pprust::state::{AnnNode, INDENT_UNIT, PrintState, State}; impl<'a> State<'a> { fn print_else(&mut self, els: Option<&ast::Expr>) { @@ -485,12 +485,12 @@ impl<'a> State<'a> { self.print_block_with_attrs(body, attrs, cb, ib); } ast::ExprKind::Loop(blk, opt_label, _) => { + let cb = self.cbox(0); + let ib = self.ibox(0); if let Some(label) = opt_label { self.print_ident(label.ident); self.word_space(":"); } - let cb = self.cbox(0); - let ib = self.ibox(0); self.word_nbsp("loop"); self.print_block_with_attrs(blk, attrs, cb, ib); } @@ -542,15 +542,6 @@ impl<'a> State<'a> { self.print_fn_params_and_ret(fn_decl, true); self.space(); self.print_expr(body, FixupContext::default()); - // FIXME(nnethercote): Bogus. Reduce visibility of `ended` once it's fixed. - let fake_ib = BoxMarker; - self.end(fake_ib); - - // A box will be closed by print_expr, but we didn't want an overall - // wrapper so we closed the corresponding opening. so create an - // empty box to satisfy the close. - // FIXME(nnethercote): Bogus. - let _ib = self.ibox(0); } ast::ExprKind::Block(blk, opt_label) => { if let Some(label) = opt_label { diff --git a/compiler/rustc_borrowck/src/constraints/mod.rs b/compiler/rustc_borrowck/src/constraints/mod.rs index a52269df682..514bbfe359b 100644 --- a/compiler/rustc_borrowck/src/constraints/mod.rs +++ b/compiler/rustc_borrowck/src/constraints/mod.rs @@ -7,7 +7,7 @@ use rustc_middle::ty::{RegionVid, TyCtxt, VarianceDiagInfo}; use rustc_span::Span; use tracing::{debug, instrument}; -use crate::region_infer::{ConstraintSccs, RegionDefinition, RegionTracker}; +use crate::region_infer::{AnnotatedSccs, ConstraintSccs, RegionDefinition, SccAnnotations}; use crate::type_check::Locations; use crate::universal_regions::UniversalRegions; @@ -61,12 +61,14 @@ impl<'tcx> OutlivesConstraintSet<'tcx> { &self, static_region: RegionVid, definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>, - ) -> ConstraintSccs { + ) -> AnnotatedSccs { let constraint_graph = self.graph(definitions.len()); let region_graph = &constraint_graph.region_graph(self, static_region); - ConstraintSccs::new_with_annotation(®ion_graph, |r| { - RegionTracker::new(r, &definitions[r]) - }) + let mut annotation_visitor = SccAnnotations::new(definitions); + ( + ConstraintSccs::new_with_annotation(®ion_graph, &mut annotation_visitor), + annotation_visitor.scc_to_annotation, + ) } /// This method handles Universe errors by rewriting the constraint @@ -79,12 +81,12 @@ impl<'tcx> OutlivesConstraintSet<'tcx> { /// eventually go away. /// /// For a more precise definition, see the documentation for - /// [`RegionTracker::has_incompatible_universes()`]. + /// [`crate::region_infer::RegionTracker`]. /// /// This edge case used to be handled during constraint propagation /// by iterating over the strongly connected components in the constraint /// graph while maintaining a set of bookkeeping mappings similar - /// to what is stored in `RegionTracker` and manually adding 'sttaic as + /// to what is stored in `RegionTracker` and manually adding 'static as /// needed. /// /// It was rewritten as part of the Polonius project with the goal of moving @@ -108,9 +110,9 @@ impl<'tcx> OutlivesConstraintSet<'tcx> { &mut self, universal_regions: &UniversalRegions<'tcx>, definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>, - ) -> ConstraintSccs { + ) -> AnnotatedSccs { let fr_static = universal_regions.fr_static; - let sccs = self.compute_sccs(fr_static, definitions); + let (sccs, annotations) = self.compute_sccs(fr_static, definitions); // Changed to `true` if we added any constraints to `self` and need to // recompute SCCs. @@ -124,7 +126,7 @@ impl<'tcx> OutlivesConstraintSet<'tcx> { continue; } - let annotation = sccs.annotation(scc); + let annotation = annotations[scc]; // If this SCC participates in a universe violation, // e.g. if it reaches a region with a universe smaller than @@ -154,7 +156,7 @@ impl<'tcx> OutlivesConstraintSet<'tcx> { self.compute_sccs(fr_static, definitions) } else { // If we didn't add any back-edges; no more work needs doing - sccs + (sccs, annotations) } } } diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index c256051c122..b4ff3d66f3d 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -47,12 +47,13 @@ mod reverse_sccs; pub(crate) mod values; -pub(crate) type ConstraintSccs = Sccs<RegionVid, ConstraintSccIndex, RegionTracker>; +pub(crate) type ConstraintSccs = Sccs<RegionVid, ConstraintSccIndex>; +pub(crate) type AnnotatedSccs = (ConstraintSccs, IndexVec<ConstraintSccIndex, RegionTracker>); /// An annotation for region graph SCCs that tracks -/// the values of its elements. +/// the values of its elements. This annotates a single SCC. #[derive(Copy, Debug, Clone)] -pub struct RegionTracker { +pub(crate) struct RegionTracker { /// The largest universe of a placeholder reached from this SCC. /// This includes placeholders within this SCC. max_placeholder_universe_reached: UniverseIndex, @@ -97,6 +98,32 @@ impl scc::Annotation for RegionTracker { } } +/// A Visitor for SCC annotation construction. +pub(crate) struct SccAnnotations<'d, 'tcx, A: scc::Annotation> { + pub(crate) scc_to_annotation: IndexVec<ConstraintSccIndex, A>, + definitions: &'d IndexVec<RegionVid, RegionDefinition<'tcx>>, +} + +impl<'d, 'tcx, A: scc::Annotation> SccAnnotations<'d, 'tcx, A> { + pub(crate) fn new(definitions: &'d IndexVec<RegionVid, RegionDefinition<'tcx>>) -> Self { + Self { scc_to_annotation: IndexVec::new(), definitions } + } +} + +impl scc::Annotations<RegionVid> for SccAnnotations<'_, '_, RegionTracker> { + fn new(&self, element: RegionVid) -> RegionTracker { + RegionTracker::new(element, &self.definitions[element]) + } + + fn annotate_scc(&mut self, scc: ConstraintSccIndex, annotation: RegionTracker) { + let idx = self.scc_to_annotation.push(annotation); + assert!(idx == scc); + } + + type Ann = RegionTracker; + type SccIdx = ConstraintSccIndex; +} + impl RegionTracker { pub(crate) fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self { let (representative_is_placeholder, representative_is_existential) = match definition.origin @@ -166,6 +193,8 @@ pub struct RegionInferenceContext<'tcx> { /// compute the values of each region. constraint_sccs: ConstraintSccs, + scc_annotations: IndexVec<ConstraintSccIndex, RegionTracker>, + /// Reverse of the SCC constraint graph -- i.e., an edge `A -> B` exists if /// `B: A`. This is used to compute the universal regions that are required /// to outlive a given SCC. Computed lazily. @@ -446,7 +475,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { let definitions = create_definitions(infcx, &universal_regions); - let constraint_sccs = + let (constraint_sccs, scc_annotations) = outlives_constraints.add_outlives_static(&universal_regions, &definitions); let constraints = Frozen::freeze(outlives_constraints); let constraint_graph = Frozen::freeze(constraints.graph(definitions.len())); @@ -472,6 +501,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { constraints, constraint_graph, constraint_sccs, + scc_annotations, rev_scc_graph: None, member_constraints, member_constraints_applied: Vec::new(), @@ -798,7 +828,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // If the member region lives in a higher universe, we currently choose // the most conservative option by leaving it unchanged. - if !self.constraint_sccs().annotation(scc).min_universe().is_root() { + if !self.scc_universe(scc).is_root() { return; } @@ -874,8 +904,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// in `scc_a`. Used during constraint propagation, and only once /// the value of `scc_b` has been computed. fn universe_compatible(&self, scc_b: ConstraintSccIndex, scc_a: ConstraintSccIndex) -> bool { - let a_annotation = self.constraint_sccs().annotation(scc_a); - let b_annotation = self.constraint_sccs().annotation(scc_b); + let a_annotation = self.scc_annotations[scc_a]; + let b_annotation = self.scc_annotations[scc_b]; let a_universe = a_annotation.min_universe(); // If scc_b's declared universe is a subset of @@ -991,7 +1021,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { "lower_bound = {:?} r_scc={:?} universe={:?}", lower_bound, r_scc, - self.constraint_sccs.annotation(r_scc).min_universe() + self.scc_universe(r_scc) ); // If the type test requires that `T: 'a` where `'a` is a // placeholder from another universe, that effectively requires @@ -1472,7 +1502,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// The minimum universe of any variable reachable from this /// SCC, inside or outside of it. fn scc_universe(&self, scc: ConstraintSccIndex) -> UniverseIndex { - self.constraint_sccs().annotation(scc).min_universe() + self.scc_annotations[scc].min_universe() } /// Checks the final value for the free region `fr` to see if it @@ -2216,7 +2246,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// they *must* be equal (though not having the same repr does not /// mean they are unequal). fn scc_representative(&self, scc: ConstraintSccIndex) -> RegionVid { - self.constraint_sccs.annotation(scc).representative + self.scc_annotations[scc].representative } pub(crate) fn liveness_constraints(&self) -> &LivenessValues { diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 6412a537a79..507cbf20d89 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -273,6 +273,12 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea ("aarch64", "fpmr") => None, // only existed in 18 ("arm", "fp16") => Some(LLVMFeature::new("fullfp16")), // Filter out features that are not supported by the current LLVM version + ("loongarch64", "div32" | "lam-bh" | "lamcas" | "ld-seq-sa" | "scq") + if get_version().0 < 20 => + { + None + } + // Filter out features that are not supported by the current LLVM version ("riscv32" | "riscv64", "zacas") if get_version().0 < 20 => None, // Enable the evex512 target feature if an avx512 target feature is enabled. ("x86", s) if s.starts_with("avx512") => { diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index a09eec5dd74..e1f903726fb 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -816,7 +816,9 @@ impl<'a> Linker for GccLinker<'a> { writeln!(f, "EXPORTS")?; for symbol in symbols { debug!(" _{symbol}"); - writeln!(f, " {symbol}")?; + // Quote the name in case it's reserved by linker in some way + // (this accounts for names with dots in particular). + writeln!(f, " \"{symbol}\"")?; } }; if let Err(error) = res { diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 2bfab1f43bc..50fb08b2868 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -164,10 +164,10 @@ fn is_reachable_non_generic_provider_extern(tcx: TyCtxt<'_>, def_id: DefId) -> b tcx.reachable_non_generics(def_id.krate).contains_key(&def_id) } -fn exported_symbols_provider_local( - tcx: TyCtxt<'_>, +fn exported_symbols_provider_local<'tcx>( + tcx: TyCtxt<'tcx>, _: LocalCrate, -) -> &[(ExportedSymbol<'_>, SymbolExportInfo)] { +) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportInfo)] { if !tcx.sess.opts.output_types.should_codegen() { return &[]; } @@ -321,6 +321,38 @@ fn exported_symbols_provider_local( let cgus = tcx.collect_and_partition_mono_items(()).codegen_units; + // Do not export symbols that cannot be instantiated by downstream crates. + let reachable_set = tcx.reachable_set(()); + let is_local_to_current_crate = |ty: Ty<'_>| { + let no_refs = ty.peel_refs(); + let root_def_id = match no_refs.kind() { + ty::Closure(closure, _) => *closure, + ty::FnDef(def_id, _) => *def_id, + ty::Coroutine(def_id, _) => *def_id, + ty::CoroutineClosure(def_id, _) => *def_id, + ty::CoroutineWitness(def_id, _) => *def_id, + _ => return false, + }; + let Some(root_def_id) = root_def_id.as_local() else { + return false; + }; + + let is_local = !reachable_set.contains(&root_def_id); + is_local + }; + + let is_instantiable_downstream = + |did: Option<DefId>, generic_args: GenericArgsRef<'tcx>| { + generic_args + .types() + .chain(did.into_iter().map(move |did| tcx.type_of(did).skip_binder())) + .all(move |arg| { + arg.walk().all(|ty| { + ty.as_type().map_or(true, |ty| !is_local_to_current_crate(ty)) + }) + }) + }; + // The symbols created in this loop are sorted below it #[allow(rustc::potential_query_instability)] for (mono_item, data) in cgus.iter().flat_map(|cgu| cgu.items().iter()) { @@ -349,7 +381,12 @@ fn exported_symbols_provider_local( match *mono_item { MonoItem::Fn(Instance { def: InstanceKind::Item(def), args }) => { - if args.non_erasable_generics().next().is_some() { + let has_generics = args.non_erasable_generics().next().is_some(); + + let should_export = + has_generics && is_instantiable_downstream(Some(def), &args); + + if should_export { let symbol = ExportedSymbol::Generic(def, args); symbols.push(( symbol, @@ -364,14 +401,24 @@ fn exported_symbols_provider_local( MonoItem::Fn(Instance { def: InstanceKind::DropGlue(_, Some(ty)), args }) => { // A little sanity-check assert_eq!(args.non_erasable_generics().next(), Some(GenericArgKind::Type(ty))); - symbols.push(( - ExportedSymbol::DropGlue(ty), - SymbolExportInfo { - level: SymbolExportLevel::Rust, - kind: SymbolExportKind::Text, - used: false, - }, - )); + + // Drop glue did is always going to be non-local outside of libcore, thus we don't need to check it's locality (which includes invoking `type_of` query). + let should_export = match ty.kind() { + ty::Adt(_, args) => is_instantiable_downstream(None, args), + ty::Closure(_, args) => is_instantiable_downstream(None, args), + _ => true, + }; + + if should_export { + symbols.push(( + ExportedSymbol::DropGlue(ty), + SymbolExportInfo { + level: SymbolExportLevel::Rust, + kind: SymbolExportKind::Text, + used: false, + }, + )); + } } MonoItem::Fn(Instance { def: InstanceKind::AsyncDropGlueCtorShim(_, ty), diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index dd481e04abb..f4defd2aa13 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -12,6 +12,27 @@ const_eval_already_reported = const_eval_assume_false = `assume` called with `false` +const_eval_bad_pointer_op = {$operation -> + [MemoryAccess] memory access failed + [InboundsPointerArithmetic] in-bounds pointer arithmetic failed + *[Dereferenceable] pointer not dereferenceable +} +const_eval_bad_pointer_op_attempting = {const_eval_bad_pointer_op}: {$operation -> + [MemoryAccess] attempting to access {$inbounds_size -> + [1] 1 byte + *[x] {$inbounds_size} bytes + } + [InboundsPointerArithmetic] attempting to offset pointer by {$inbounds_size -> + [1] 1 byte + *[x] {$inbounds_size} bytes + } + *[Dereferenceable] pointer must {$inbounds_size -> + [0] point to some allocation + [1] be dereferenceable for 1 byte + *[x] be dereferenceable for {$inbounds_size} bytes + } + } + const_eval_bounds_check_failed = indexing out of bounds: the len is {$len} but the index is {$index} const_eval_call_nonzero_intrinsic = @@ -39,9 +60,9 @@ const_eval_copy_nonoverlapping_overlapping = `copy_nonoverlapping` called on overlapping ranges const_eval_dangling_int_pointer = - {$bad_pointer_message}: {const_eval_expected_inbounds_pointer}, but got {$pointer} which is a dangling pointer (it has no provenance) + {const_eval_bad_pointer_op_attempting}, but got {$pointer} which is a dangling pointer (it has no provenance) const_eval_dangling_null_pointer = - {$bad_pointer_message}: {const_eval_expected_inbounds_pointer}, but got a null pointer + {const_eval_bad_pointer_op_attempting}, but got null pointer const_eval_dangling_ptr_in_final = encountered dangling pointer in final value of {const_eval_intern_kind} const_eval_dead_local = @@ -77,21 +98,6 @@ const_eval_error = {$error_kind -> const_eval_exact_div_has_remainder = exact_div: {$a} cannot be divided by {$b} without remainder -const_eval_expected_inbounds_pointer = - expected a pointer to {$inbounds_size_abs -> - [0] some allocation - *[x] {$inbounds_size_is_neg -> - [false] {$inbounds_size_abs -> - [1] 1 byte of memory - *[x] {$inbounds_size_abs} bytes of memory - } - *[true] the end of {$inbounds_size_abs -> - [1] 1 byte of memory - *[x] {$inbounds_size_abs} bytes of memory - } - } - } - const_eval_extern_static = cannot access extern static `{$did}` const_eval_extern_type_field = `extern type` field does not have a known offset @@ -111,7 +117,6 @@ const_eval_frame_note_inner = inside {$where_ -> const_eval_frame_note_last = the failure occurred here -const_eval_in_bounds_test = out-of-bounds pointer use const_eval_incompatible_calling_conventions = calling a function with calling convention {$callee_conv} using calling convention {$caller_conv} @@ -206,7 +211,6 @@ const_eval_long_running = const_eval_max_num_nodes_in_const = maximum number of nodes exceeded in constant {$global_const_id} -const_eval_memory_access_test = memory access failed const_eval_memory_exhausted = tried to allocate more memory than available to compiler @@ -287,8 +291,6 @@ const_eval_offset_from_out_of_bounds = `{$name}` called on two different pointers where the memory range between them is not in-bounds of an allocation const_eval_offset_from_overflow = `{$name}` called when first pointer is too far ahead of second -const_eval_offset_from_test = - out-of-bounds `offset_from` origin const_eval_offset_from_underflow = `{$name}` called when first pointer is too far before second const_eval_offset_from_unsigned_overflow = @@ -312,27 +314,25 @@ const_eval_partial_pointer_overwrite = unable to overwrite parts of a pointer in memory at {$ptr} const_eval_pointer_arithmetic_overflow = overflowing pointer arithmetic: the total offset in bytes does not fit in an `isize` -const_eval_pointer_arithmetic_test = out-of-bounds pointer arithmetic + const_eval_pointer_out_of_bounds = - {$bad_pointer_message}: {const_eval_expected_inbounds_pointer}, but got {$pointer} {$ptr_offset_is_neg -> - [true] which points to before the beginning of the allocation - *[false] {$inbounds_size_is_neg -> - [true] {$ptr_offset_abs -> - [0] which is at the beginning of the allocation - *[other] which does not have enough space to the beginning of the allocation - } - *[false] {$alloc_size_minus_ptr_offset -> - [0] which is at or beyond the end of the allocation of size {$alloc_size -> + {const_eval_bad_pointer_op_attempting}, but got {$pointer} which {$inbounds_size_is_neg -> + [false] {$alloc_size_minus_ptr_offset -> + [0] is at or beyond the end of the allocation of size {$alloc_size -> [1] 1 byte *[x] {$alloc_size} bytes } - [1] which is only 1 byte from the end of the allocation - *[x] which is only {$alloc_size_minus_ptr_offset} bytes from the end of the allocation + [1] is only 1 byte from the end of the allocation + *[x] is only {$alloc_size_minus_ptr_offset} bytes from the end of the allocation + } + *[true] {$ptr_offset_abs -> + [0] is at the beginning of the allocation + *[other] is only {$ptr_offset_abs} bytes from the beginning of the allocation } - } } + const_eval_pointer_use_after_free = - {$bad_pointer_message}: {$alloc_id} has been freed, so this pointer is dangling + {const_eval_bad_pointer_op}: {$alloc_id} has been freed, so this pointer is dangling const_eval_ptr_as_bytes_1 = this code performed an operation that depends on the underlying bytes representing a pointer const_eval_ptr_as_bytes_2 = diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index 7756e51c4c5..1e5b98675c4 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -352,7 +352,7 @@ fn build_error_for_const_call<'tcx>( ); err } - _ if tcx.opt_parent(callee) == tcx.get_diagnostic_item(sym::ArgumentMethods) => { + _ if tcx.opt_parent(callee) == tcx.get_diagnostic_item(sym::FmtArgumentsNew) => { ccx.dcx().create_err(errors::NonConstFmtMacroCall { span, kind: ccx.const_kind(), diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 6472aaa5758..826ea0e58ec 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -5,15 +5,14 @@ use either::Either; use rustc_abi::WrappingRange; use rustc_errors::codes::*; use rustc_errors::{ - Diag, DiagArgValue, DiagCtxtHandle, DiagMessage, Diagnostic, EmissionGuarantee, Level, - MultiSpan, Subdiagnostic, + Diag, DiagArgValue, DiagMessage, Diagnostic, EmissionGuarantee, Level, MultiSpan, Subdiagnostic, }; use rustc_hir::ConstContext; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::mir::interpret::{ - CheckInAllocMsg, CtfeProvenance, ExpectedKind, InterpErrorKind, InvalidMetaKind, - InvalidProgramInfo, Misalignment, Pointer, PointerKind, ResourceExhaustionInfo, - UndefinedBehaviorInfo, UnsupportedOpInfo, ValidationErrorInfo, + CtfeProvenance, ExpectedKind, InterpErrorKind, InvalidMetaKind, InvalidProgramInfo, + Misalignment, Pointer, PointerKind, ResourceExhaustionInfo, UndefinedBehaviorInfo, + UnsupportedOpInfo, ValidationErrorInfo, }; use rustc_middle::ty::{self, Mutability, Ty}; use rustc_span::{Span, Symbol}; @@ -498,19 +497,6 @@ pub trait ReportErrorExt { } } -fn bad_pointer_message(msg: CheckInAllocMsg, dcx: DiagCtxtHandle<'_>) -> String { - use crate::fluent_generated::*; - - let msg = match msg { - CheckInAllocMsg::MemoryAccessTest => const_eval_memory_access_test, - CheckInAllocMsg::PointerArithmeticTest => const_eval_pointer_arithmetic_test, - CheckInAllocMsg::OffsetFromTest => const_eval_offset_from_test, - CheckInAllocMsg::InboundsTest => const_eval_in_bounds_test, - }; - - dcx.eagerly_translate_to_string(msg, [].into_iter()) -} - impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { fn diagnostic_message(&self) -> DiagMessage { use UndefinedBehaviorInfo::*; @@ -564,7 +550,6 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { fn add_args<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) { use UndefinedBehaviorInfo::*; - let dcx = diag.dcx; match self { Ub(_) => {} Custom(custom) => { @@ -612,12 +597,10 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { diag.arg("vtable_dyn_type", vtable_dyn_type.to_string()); } PointerUseAfterFree(alloc_id, msg) => { - diag.arg("alloc_id", alloc_id) - .arg("bad_pointer_message", bad_pointer_message(msg, dcx)); + diag.arg("alloc_id", alloc_id).arg("operation", format!("{:?}", msg)); } PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, inbounds_size, msg } => { diag.arg("alloc_size", alloc_size.bytes()); - diag.arg("bad_pointer_message", bad_pointer_message(msg, dcx)); diag.arg("pointer", { let mut out = format!("{:?}", alloc_id); if ptr_offset > 0 { @@ -627,14 +610,17 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { } out }); + diag.arg("inbounds_size", inbounds_size); diag.arg("inbounds_size_is_neg", inbounds_size < 0); diag.arg("inbounds_size_abs", inbounds_size.unsigned_abs()); + diag.arg("ptr_offset", ptr_offset); diag.arg("ptr_offset_is_neg", ptr_offset < 0); diag.arg("ptr_offset_abs", ptr_offset.unsigned_abs()); diag.arg( "alloc_size_minus_ptr_offset", alloc_size.bytes().saturating_sub(ptr_offset as u64), ); + diag.arg("operation", format!("{:?}", msg)); } DanglingIntPointer { addr, inbounds_size, msg } => { if addr != 0 { @@ -644,9 +630,10 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { ); } + diag.arg("inbounds_size", inbounds_size); diag.arg("inbounds_size_is_neg", inbounds_size < 0); diag.arg("inbounds_size_abs", inbounds_size.unsigned_abs()); - diag.arg("bad_pointer_message", bad_pointer_message(msg, dcx)); + diag.arg("operation", format!("{:?}", msg)); } AlignmentCheckFailed(Misalignment { required, has }, msg) => { diag.arg("required", required.bytes()); diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index d67b547ba1a..04a8ed1e0f1 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -348,7 +348,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Check that the memory between them is dereferenceable at all, starting from the // origin pointer: `dist` is `a - b`, so it is based on `b`. - self.check_ptr_access_signed(b, dist, CheckInAllocMsg::OffsetFromTest) + self.check_ptr_access_signed(b, dist, CheckInAllocMsg::Dereferenceable) .map_err_kind(|_| { // This could mean they point to different allocations, or they point to the same allocation // but not the entire range between the pointers is in-bounds. @@ -372,7 +372,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.check_ptr_access_signed( a, dist.checked_neg().unwrap(), // i64::MIN is impossible as no allocation can be that large - CheckInAllocMsg::OffsetFromTest, + CheckInAllocMsg::Dereferenceable, ) .map_err_kind(|_| { // Make the error more specific. @@ -651,7 +651,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { offset_bytes: i64, ) -> InterpResult<'tcx, Pointer<Option<M::Provenance>>> { // The offset must be in bounds starting from `ptr`. - self.check_ptr_access_signed(ptr, offset_bytes, CheckInAllocMsg::PointerArithmeticTest)?; + self.check_ptr_access_signed( + ptr, + offset_bytes, + CheckInAllocMsg::InboundsPointerArithmetic, + )?; // This also implies that there is no overflow, so we are done. interp_ok(ptr.wrapping_signed_offset(offset_bytes, self)) } diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 9d8130661b0..43bf48a9b96 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -351,7 +351,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { kind = "static_mem" ) } - None => err_ub!(PointerUseAfterFree(alloc_id, CheckInAllocMsg::MemoryAccessTest)), + None => err_ub!(PointerUseAfterFree(alloc_id, CheckInAllocMsg::MemoryAccess)), }) .into(); }; @@ -414,10 +414,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self, ptr, size, - CheckInAllocMsg::MemoryAccessTest, + CheckInAllocMsg::MemoryAccess, |this, alloc_id, offset, prov| { - let (size, align) = this - .get_live_alloc_size_and_align(alloc_id, CheckInAllocMsg::MemoryAccessTest)?; + let (size, align) = + this.get_live_alloc_size_and_align(alloc_id, CheckInAllocMsg::MemoryAccess)?; interp_ok((size, align, (alloc_id, offset, prov))) }, ) @@ -613,7 +613,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } Some(GlobalAlloc::Function { .. }) => throw_ub!(DerefFunctionPointer(id)), Some(GlobalAlloc::VTable(..)) => throw_ub!(DerefVTablePointer(id)), - None => throw_ub!(PointerUseAfterFree(id, CheckInAllocMsg::MemoryAccessTest)), + None => throw_ub!(PointerUseAfterFree(id, CheckInAllocMsg::MemoryAccess)), Some(GlobalAlloc::Static(def_id)) => { assert!(self.tcx.is_static(def_id)); // Thread-local statics do not have a constant address. They *must* be accessed via @@ -707,7 +707,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self, ptr, size_i64, - CheckInAllocMsg::MemoryAccessTest, + CheckInAllocMsg::MemoryAccess, |this, alloc_id, offset, prov| { let alloc = this.get_alloc_raw(alloc_id)?; interp_ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc))) @@ -809,7 +809,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self, ptr, size_i64, - CheckInAllocMsg::MemoryAccessTest, + CheckInAllocMsg::MemoryAccess, |this, alloc_id, offset, prov| { let (alloc, machine) = this.get_alloc_raw_mut(alloc_id)?; interp_ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc, machine))) @@ -1615,7 +1615,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { err_ub!(DanglingIntPointer { addr: offset, inbounds_size: size, - msg: CheckInAllocMsg::InboundsTest + msg: CheckInAllocMsg::Dereferenceable }) }) .into() diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index c86af5a9a4b..8f39afa642a 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -510,7 +510,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { self.ecx.check_ptr_access( place.ptr(), size, - CheckInAllocMsg::InboundsTest, // will anyway be replaced by validity message + CheckInAllocMsg::Dereferenceable, // will anyway be replaced by validity message ), self.path, Ub(DanglingIntPointer { addr: 0, .. }) => NullPtr { ptr_kind }, diff --git a/compiler/rustc_data_structures/src/graph/scc/mod.rs b/compiler/rustc_data_structures/src/graph/scc/mod.rs index e7c4ea3daae..518817ea0f5 100644 --- a/compiler/rustc_data_structures/src/graph/scc/mod.rs +++ b/compiler/rustc_data_structures/src/graph/scc/mod.rs @@ -10,10 +10,11 @@ use std::assert_matches::debug_assert_matches; use std::fmt::Debug; +use std::marker::PhantomData; use std::ops::Range; use rustc_index::{Idx, IndexSlice, IndexVec}; -use tracing::{debug, instrument}; +use tracing::{debug, instrument, trace}; use crate::fx::FxHashSet; use crate::graph::vec_graph::VecGraph; @@ -48,6 +49,25 @@ pub trait Annotation: Debug + Copy { } } +/// An accumulator for annotations. +pub trait Annotations<N: Idx> { + type Ann: Annotation; + type SccIdx: Idx + Ord; + + fn new(&self, element: N) -> Self::Ann; + fn annotate_scc(&mut self, scc: Self::SccIdx, annotation: Self::Ann); +} + +/// The nil annotation accumulator, which does nothing. +struct NoAnnotations<S: Idx + Ord>(PhantomData<S>); + +impl<N: Idx, S: Idx + Ord> Annotations<N> for NoAnnotations<S> { + type SccIdx = S; + type Ann = (); + fn new(&self, _element: N) {} + fn annotate_scc(&mut self, _scc: S, _annotation: ()) {} +} + /// The empty annotation, which does nothing. impl Annotation for () { fn merge_reached(self, _other: Self) -> Self { @@ -62,23 +82,20 @@ impl Annotation for () { /// the index type for the graph nodes and `S` is the index type for /// the SCCs. We can map from each node to the SCC that it /// participates in, and we also have the successors of each SCC. -pub struct Sccs<N: Idx, S: Idx, A: Annotation = ()> { +pub struct Sccs<N: Idx, S: Idx> { /// For each node, what is the SCC index of the SCC to which it /// belongs. scc_indices: IndexVec<N, S>, /// Data about all the SCCs. - scc_data: SccData<S, A>, + scc_data: SccData<S>, } /// Information about an invidividual SCC node. -struct SccDetails<A: Annotation> { +struct SccDetails { /// For this SCC, the range of `all_successors` where its /// successors can be found. range: Range<usize>, - - /// User-specified metadata about the SCC. - annotation: A, } // The name of this struct should discourage you from making it public and leaking @@ -87,10 +104,10 @@ struct SccDetails<A: Annotation> { // is difficult when it's publicly inspectable. // // Obey the law of Demeter! -struct SccData<S: Idx, A: Annotation> { +struct SccData<S: Idx> { /// Maps SCC indices to their metadata, including /// offsets into `all_successors`. - scc_details: IndexVec<S, SccDetails<A>>, + scc_details: IndexVec<S, SccDetails>, /// Contains the successors for all the Sccs, concatenated. The /// range of indices corresponding to a given SCC is found in its @@ -98,24 +115,18 @@ struct SccData<S: Idx, A: Annotation> { all_successors: Vec<S>, } -impl<N: Idx, S: Idx + Ord> Sccs<N, S, ()> { +impl<N: Idx, S: Idx + Ord> Sccs<N, S> { /// Compute SCCs without annotations. pub fn new(graph: &impl Successors<Node = N>) -> Self { - Self::new_with_annotation(graph, |_| ()) + Self::new_with_annotation(graph, &mut NoAnnotations(PhantomData::<S>)) } -} -impl<N: Idx, S: Idx + Ord, A: Annotation> Sccs<N, S, A> { /// Compute SCCs and annotate them with a user-supplied annotation - pub fn new_with_annotation<F: Fn(N) -> A>( + pub fn new_with_annotation<A: Annotations<N, SccIdx = S>>( graph: &impl Successors<Node = N>, - to_annotation: F, + annotations: &mut A, ) -> Self { - SccsConstruction::construct(graph, to_annotation) - } - - pub fn annotation(&self, scc: S) -> A { - self.scc_data.annotation(scc) + SccsConstruction::construct(graph, annotations) } pub fn scc_indices(&self) -> &IndexSlice<N, S> { @@ -160,7 +171,7 @@ impl<N: Idx, S: Idx + Ord, A: Annotation> Sccs<N, S, A> { } } -impl<N: Idx, S: Idx + Ord, A: Annotation> DirectedGraph for Sccs<N, S, A> { +impl<N: Idx, S: Idx + Ord> DirectedGraph for Sccs<N, S> { type Node = S; fn num_nodes(&self) -> usize { @@ -168,19 +179,19 @@ impl<N: Idx, S: Idx + Ord, A: Annotation> DirectedGraph for Sccs<N, S, A> { } } -impl<N: Idx, S: Idx + Ord, A: Annotation> NumEdges for Sccs<N, S, A> { +impl<N: Idx, S: Idx + Ord> NumEdges for Sccs<N, S> { fn num_edges(&self) -> usize { self.scc_data.all_successors.len() } } -impl<N: Idx, S: Idx + Ord, A: Annotation> Successors for Sccs<N, S, A> { +impl<N: Idx, S: Idx + Ord> Successors for Sccs<N, S> { fn successors(&self, node: S) -> impl Iterator<Item = Self::Node> { self.successors(node).iter().cloned() } } -impl<S: Idx, A: Annotation> SccData<S, A> { +impl<S: Idx> SccData<S> { /// Number of SCCs, fn len(&self) -> usize { self.scc_details.len() @@ -192,9 +203,8 @@ impl<S: Idx, A: Annotation> SccData<S, A> { } /// Creates a new SCC with `successors` as its successors and - /// the maximum weight of its internal nodes `scc_max_weight` and /// returns the resulting index. - fn create_scc(&mut self, successors: impl IntoIterator<Item = S>, annotation: A) -> S { + fn create_scc(&mut self, successors: impl IntoIterator<Item = S>) -> S { // Store the successors on `scc_successors_vec`, remembering // the range of indices. let all_successors_start = self.all_successors.len(); @@ -202,35 +212,28 @@ impl<S: Idx, A: Annotation> SccData<S, A> { let all_successors_end = self.all_successors.len(); debug!( - "create_scc({:?}) successors={:?}, annotation={:?}", + "create_scc({:?}) successors={:?}", self.len(), &self.all_successors[all_successors_start..all_successors_end], - annotation ); let range = all_successors_start..all_successors_end; - let metadata = SccDetails { range, annotation }; + let metadata = SccDetails { range }; self.scc_details.push(metadata) } - - fn annotation(&self, scc: S) -> A { - self.scc_details[scc].annotation - } } -struct SccsConstruction<'c, G, S, A, F> +struct SccsConstruction<'c, 'a, G, A> where G: DirectedGraph + Successors, - S: Idx, - A: Annotation, - F: Fn(G::Node) -> A, + A: Annotations<G::Node>, { graph: &'c G, /// The state of each node; used during walk to record the stack /// and after walk to record what cycle each node ended up being /// in. - node_states: IndexVec<G::Node, NodeState<G::Node, S, A>>, + node_states: IndexVec<G::Node, NodeState<G::Node, A::SccIdx, A::Ann>>, /// The stack of nodes that we are visiting as part of the DFS. node_stack: Vec<G::Node>, @@ -239,23 +242,21 @@ where /// position in this stack, and when we encounter a successor SCC, /// we push it on the stack. When we complete an SCC, we can pop /// everything off the stack that was found along the way. - successors_stack: Vec<S>, + successors_stack: Vec<A::SccIdx>, /// A set used to strip duplicates. As we accumulate successors /// into the successors_stack, we sometimes get duplicate entries. /// We use this set to remove those -- we also keep its storage /// around between successors to amortize memory allocation costs. - duplicate_set: FxHashSet<S>, + duplicate_set: FxHashSet<A::SccIdx>, - scc_data: SccData<S, A>, + scc_data: SccData<A::SccIdx>, - /// A function that constructs an initial SCC annotation - /// out of a single node. - to_annotation: F, + annotations: &'a mut A, } #[derive(Copy, Clone, Debug)] -enum NodeState<N, S, A> { +enum NodeState<N, S, A: Annotation> { /// This node has not yet been visited as part of the DFS. /// /// After SCC construction is complete, this state ought to be @@ -286,7 +287,7 @@ enum NodeState<N, S, A> { /// The state of walking a given node. #[derive(Copy, Clone, Debug)] -enum WalkReturn<S, A> { +enum WalkReturn<S, A: Annotation> { /// The walk found a cycle, but the entire component is not known to have /// been fully walked yet. We only know the minimum depth of this /// component in a minimum spanning tree of the graph. This component @@ -299,12 +300,10 @@ enum WalkReturn<S, A> { Complete { scc_index: S, annotation: A }, } -impl<'c, G, S, A, F> SccsConstruction<'c, G, S, A, F> +impl<'c, 'a, G, A> SccsConstruction<'c, 'a, G, A> where G: DirectedGraph + Successors, - S: Idx, - F: Fn(G::Node) -> A, - A: Annotation, + A: Annotations<G::Node>, { /// Identifies SCCs in the graph `G` and computes the resulting /// DAG. This uses a variant of [Tarjan's @@ -320,7 +319,7 @@ where /// Additionally, we keep track of a current annotation of the SCC. /// /// [wikipedia]: https://bit.ly/2EZIx84 - fn construct(graph: &'c G, to_annotation: F) -> Sccs<G::Node, S, A> { + fn construct(graph: &'c G, annotations: &'a mut A) -> Sccs<G::Node, A::SccIdx> { let num_nodes = graph.num_nodes(); let mut this = Self { @@ -330,7 +329,7 @@ where successors_stack: Vec::new(), scc_data: SccData { scc_details: IndexVec::new(), all_successors: Vec::new() }, duplicate_set: FxHashSet::default(), - to_annotation, + annotations, }; let scc_indices = graph @@ -346,7 +345,7 @@ where Sccs { scc_indices, scc_data: this.scc_data } } - fn start_walk_from(&mut self, node: G::Node) -> WalkReturn<S, A> { + fn start_walk_from(&mut self, node: G::Node) -> WalkReturn<A::SccIdx, A::Ann> { self.inspect_node(node).unwrap_or_else(|| self.walk_unvisited_node(node)) } @@ -362,7 +361,7 @@ where /// Otherwise, we are looking at a node that has already been /// completely visited. We therefore return `WalkReturn::Complete` /// with its associated SCC index. - fn inspect_node(&mut self, node: G::Node) -> Option<WalkReturn<S, A>> { + fn inspect_node(&mut self, node: G::Node) -> Option<WalkReturn<A::SccIdx, A::Ann>> { Some(match self.find_state(node) { NodeState::InCycle { scc_index, annotation } => { WalkReturn::Complete { scc_index, annotation } @@ -385,7 +384,7 @@ where /// of `r2` (and updates `r` to reflect current result). This is /// basically the "find" part of a standard union-find algorithm /// (with path compression). - fn find_state(&mut self, mut node: G::Node) -> NodeState<G::Node, S, A> { + fn find_state(&mut self, mut node: G::Node) -> NodeState<G::Node, A::SccIdx, A::Ann> { // To avoid recursion we temporarily reuse the `parent` of each // InCycleWith link to encode a downwards link while compressing // the path. After we have found the root or deepest node being @@ -408,7 +407,7 @@ where // a potentially derived version of the root state for non-root nodes in the chain. let (root_state, assigned_state) = { loop { - debug!("find_state(r = {node:?} in state {:?})", self.node_states[node]); + trace!("find_state(r = {node:?} in state {:?})", self.node_states[node]); match self.node_states[node] { // This must have been the first and only state since it is unexplored*; // no update needed! * Unless there is a bug :') @@ -482,7 +481,7 @@ where if previous_node == node { return root_state; } - debug!("Compressing {node:?} down to {previous_node:?} with state {assigned_state:?}"); + trace!("Compressing {node:?} down to {previous_node:?} with state {assigned_state:?}"); // Update to previous node in the link. match self.node_states[previous_node] { @@ -507,9 +506,9 @@ where /// Call this method when `inspect_node` has returned `None`. Having the /// caller decide avoids mutual recursion between the two methods and allows /// us to maintain an allocated stack for nodes on the path between calls. - #[instrument(skip(self, initial), level = "debug")] - fn walk_unvisited_node(&mut self, initial: G::Node) -> WalkReturn<S, A> { - debug!("Walk unvisited node: {initial:?}"); + #[instrument(skip(self, initial), level = "trace")] + fn walk_unvisited_node(&mut self, initial: G::Node) -> WalkReturn<A::SccIdx, A::Ann> { + trace!("Walk unvisited node: {initial:?}"); struct VisitingNodeFrame<G: DirectedGraph, Successors, A> { node: G::Node, successors: Option<Successors>, @@ -537,7 +536,7 @@ where successors_len: 0, min_cycle_root: initial, successor_node: initial, - current_component_annotation: (self.to_annotation)(initial), + current_component_annotation: self.annotations.new(initial), }]; let mut return_value = None; @@ -556,11 +555,7 @@ where let node = *node; let depth = *depth; - // node is definitely in the current component, add it to the annotation. - if node != initial { - current_component_annotation.update_scc((self.to_annotation)(node)); - } - debug!( + trace!( "Visiting {node:?} at depth {depth:?}, annotation: {current_component_annotation:?}" ); @@ -568,7 +563,7 @@ where Some(successors) => successors, None => { // This None marks that we still have the initialize this node's frame. - debug!(?depth, ?node); + trace!(?depth, ?node); debug_assert_matches!(self.node_states[node], NodeState::NotVisited); @@ -598,7 +593,7 @@ where return_value.take().into_iter().map(|walk| (*successor_node, Some(walk))); let successor_walk = successors.map(|successor_node| { - debug!(?node, ?successor_node); + trace!(?node, ?successor_node); (successor_node, self.inspect_node(successor_node)) }); for (successor_node, walk) in returned_walk.chain(successor_walk) { @@ -609,13 +604,13 @@ where min_depth: successor_min_depth, annotation: successor_annotation, }) => { - debug!( + trace!( "Cycle found from {node:?}, minimum depth: {successor_min_depth:?}, annotation: {successor_annotation:?}" ); // Track the minimum depth we can reach. assert!(successor_min_depth <= depth); if successor_min_depth < *min_depth { - debug!(?node, ?successor_min_depth); + trace!(?node, ?successor_min_depth); *min_depth = successor_min_depth; *min_cycle_root = successor_node; } @@ -627,20 +622,20 @@ where scc_index: successor_scc_index, annotation: successor_annotation, }) => { - debug!( + trace!( "Complete; {node:?} is root of complete-visited SCC idx {successor_scc_index:?} with annotation {successor_annotation:?}" ); // Push the completed SCC indices onto // the `successors_stack` for later. - debug!(?node, ?successor_scc_index); + trace!(?node, ?successor_scc_index); successors_stack.push(successor_scc_index); current_component_annotation.update_reachable(successor_annotation); } // `node` has no more (direct) successors; search recursively. None => { let depth = depth + 1; - debug!("Recursing down into {successor_node:?} at depth {depth:?}"); - debug!(?depth, ?successor_node); + trace!("Recursing down into {successor_node:?} at depth {depth:?}"); + trace!(?depth, ?successor_node); // Remember which node the return value will come from. frame.successor_node = successor_node; // Start a new stack frame, then step into it. @@ -652,14 +647,14 @@ where min_depth: depth, min_cycle_root: successor_node, successor_node, - current_component_annotation: (self.to_annotation)(successor_node), + current_component_annotation: self.annotations.new(successor_node), }); continue 'recurse; } } } - debug!("Finished walk from {node:?} with annotation: {current_component_annotation:?}"); + trace!("Finished walk from {node:?} with annotation: {current_component_annotation:?}"); // Completed walk, remove `node` from the stack. let r = self.node_stack.pop(); @@ -691,8 +686,9 @@ where debug!("Creating SCC rooted in {node:?} with successor {:?}", frame.successor_node); - let scc_index = - self.scc_data.create_scc(deduplicated_successors, current_component_annotation); + let scc_index = self.scc_data.create_scc(deduplicated_successors); + + self.annotations.annotate_scc(scc_index, current_component_annotation); self.node_states[node] = NodeState::InCycle { scc_index, annotation: current_component_annotation }; diff --git a/compiler/rustc_data_structures/src/graph/scc/tests.rs b/compiler/rustc_data_structures/src/graph/scc/tests.rs index 373f87bfdbc..8f04baf51f3 100644 --- a/compiler/rustc_data_structures/src/graph/scc/tests.rs +++ b/compiler/rustc_data_structures/src/graph/scc/tests.rs @@ -5,8 +5,31 @@ use crate::graph::tests::TestGraph; #[derive(Copy, Clone, Debug)] struct MaxReached(usize); -type UsizeSccs = Sccs<usize, usize, ()>; -type MaxReachedSccs = Sccs<usize, usize, MaxReached>; +struct Maxes(IndexVec<usize, MaxReached>, fn(usize) -> usize); +type UsizeSccs = Sccs<usize, usize>; + +impl Annotations<usize> for Maxes { + fn new(&self, element: usize) -> MaxReached { + MaxReached(self.1(element)) + } + + fn annotate_scc(&mut self, scc: usize, annotation: MaxReached) { + let i = self.0.push(annotation); + assert!(i == scc); + } + + type Ann = MaxReached; + type SccIdx = usize; +} + +impl Maxes { + fn annotation(&self, scc: usize) -> MaxReached { + self.0[scc] + } + fn new(mapping: fn(usize) -> usize) -> Self { + Self(IndexVec::new(), mapping) + } +} impl Annotation for MaxReached { fn merge_scc(self, other: Self) -> Self { @@ -14,7 +37,7 @@ impl Annotation for MaxReached { } fn merge_reached(self, other: Self) -> Self { - self.merge_scc(other) + Self(std::cmp::max(other.0, self.0)) } } @@ -24,17 +47,32 @@ impl PartialEq<usize> for MaxReached { } } -impl MaxReached { - fn from_usize(nr: usize) -> Self { - Self(nr) - } -} - #[derive(Copy, Clone, Debug)] struct MinMaxIn { min: usize, max: usize, } +struct MinMaxes(IndexVec<usize, MinMaxIn>, fn(usize) -> MinMaxIn); + +impl MinMaxes { + fn annotation(&self, scc: usize) -> MinMaxIn { + self.0[scc] + } +} + +impl Annotations<usize> for MinMaxes { + fn new(&self, element: usize) -> MinMaxIn { + self.1(element) + } + + fn annotate_scc(&mut self, scc: usize, annotation: MinMaxIn) { + let i = self.0.push(annotation); + assert!(i == scc); + } + + type Ann = MinMaxIn; + type SccIdx = usize; +} impl Annotation for MinMaxIn { fn merge_scc(self, other: Self) -> Self { @@ -261,67 +299,68 @@ fn bench_sccc(b: &mut test::Bencher) { #[test] fn test_max_self_loop() { let graph = TestGraph::new(0, &[(0, 0)]); - let sccs: MaxReachedSccs = - Sccs::new_with_annotation(&graph, |n| if n == 0 { MaxReached(17) } else { MaxReached(0) }); - assert_eq!(sccs.annotation(0), 17); + let mut annotations = Maxes(IndexVec::new(), |n| if n == 0 { 17 } else { 0 }); + Sccs::new_with_annotation(&graph, &mut annotations); + assert_eq!(annotations.0[0], 17); } #[test] fn test_max_branch() { let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (2, 4)]); - let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, MaxReached::from_usize); - assert_eq!(sccs.annotation(sccs.scc(0)), 4); - assert_eq!(sccs.annotation(sccs.scc(1)), 3); - assert_eq!(sccs.annotation(sccs.scc(2)), 4); + let mut annotations = Maxes(IndexVec::new(), |n| n); + let sccs = Sccs::new_with_annotation(&graph, &mut annotations); + assert_eq!(annotations.0[sccs.scc(0)], 4); + assert_eq!(annotations.0[sccs.scc(1)], 3); + assert_eq!(annotations.0[sccs.scc(2)], 4); } + #[test] fn test_single_cycle_max() { let graph = TestGraph::new(0, &[(0, 2), (2, 3), (2, 4), (4, 1), (1, 2)]); - let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, MaxReached::from_usize); - assert_eq!(sccs.annotation(sccs.scc(2)), 4); - assert_eq!(sccs.annotation(sccs.scc(0)), 4); -} - -#[test] -fn test_simple_cycle_max() { - let graph = TestGraph::new(0, &[(0, 1), (1, 2), (2, 0)]); - let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, MaxReached::from_usize); - assert_eq!(sccs.num_sccs(), 1); + let mut annotations = Maxes(IndexVec::new(), |n| n); + let sccs = Sccs::new_with_annotation(&graph, &mut annotations); + assert_eq!(annotations.0[sccs.scc(2)], 4); + assert_eq!(annotations.0[sccs.scc(0)], 4); } #[test] fn test_double_cycle_max() { let graph = TestGraph::new(0, &[(0, 1), (1, 2), (1, 4), (2, 3), (2, 4), (3, 5), (4, 1), (5, 4)]); - let sccs: MaxReachedSccs = - Sccs::new_with_annotation(&graph, |n| if n == 5 { MaxReached(2) } else { MaxReached(1) }); + let mut annotations = Maxes(IndexVec::new(), |n| if n == 5 { 2 } else { 1 }); + + let sccs = Sccs::new_with_annotation(&graph, &mut annotations); - assert_eq!(sccs.annotation(sccs.scc(0)).0, 2); + assert_eq!(annotations.0[sccs.scc(0)].0, 2); } #[test] fn test_bug_minimised() { let graph = TestGraph::new(0, &[(0, 3), (0, 1), (3, 2), (2, 3), (1, 4), (4, 5), (5, 4)]); - let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |n| match n { - 3 => MaxReached(1), - _ => MaxReached(0), + let mut annotations = Maxes(IndexVec::new(), |n| match n { + 3 => 1, + _ => 0, }); - assert_eq!(sccs.annotation(sccs.scc(2)), 1); - assert_eq!(sccs.annotation(sccs.scc(1)), 0); - assert_eq!(sccs.annotation(sccs.scc(4)), 0); + + let sccs = Sccs::new_with_annotation(&graph, &mut annotations); + assert_eq!(annotations.annotation(sccs.scc(2)), 1); + assert_eq!(annotations.annotation(sccs.scc(1)), 0); + assert_eq!(annotations.annotation(sccs.scc(4)), 0); } #[test] fn test_bug_max_leak_minimised() { let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (3, 0), (3, 4), (4, 3)]); - let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |w| match w { - 4 => MaxReached(1), - _ => MaxReached(0), + let mut annotations = Maxes(IndexVec::new(), |w| match w { + 4 => 1, + _ => 0, }); - assert_eq!(sccs.annotation(sccs.scc(2)), 0); - assert_eq!(sccs.annotation(sccs.scc(3)), 1); - assert_eq!(sccs.annotation(sccs.scc(0)), 1); + let sccs = Sccs::new_with_annotation(&graph, &mut annotations); + + assert_eq!(annotations.annotation(sccs.scc(2)), 0); + assert_eq!(annotations.annotation(sccs.scc(3)), 1); + assert_eq!(annotations.annotation(sccs.scc(0)), 1); } #[test] @@ -369,48 +408,49 @@ fn test_bug_max_leak() { (23, 24), ], ); - let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |w| match w { - 22 => MaxReached(1), - 24 => MaxReached(2), - 27 => MaxReached(2), - _ => MaxReached(0), + let mut annotations = Maxes::new(|w| match w { + 22 => 1, + 24 => 2, + 27 => 2, + _ => 0, }); - - assert_eq!(sccs.annotation(sccs.scc(2)), 0); - assert_eq!(sccs.annotation(sccs.scc(7)), 0); - assert_eq!(sccs.annotation(sccs.scc(8)), 2); - assert_eq!(sccs.annotation(sccs.scc(23)), 2); - assert_eq!(sccs.annotation(sccs.scc(3)), 2); - assert_eq!(sccs.annotation(sccs.scc(0)), 2); + let sccs = Sccs::new_with_annotation(&graph, &mut annotations); + + assert_eq!(annotations.annotation(sccs.scc(2)), 0); + assert_eq!(annotations.annotation(sccs.scc(7)), 0); + assert_eq!(annotations.annotation(sccs.scc(8)), 2); + assert_eq!(annotations.annotation(sccs.scc(23)), 2); + assert_eq!(annotations.annotation(sccs.scc(3)), 2); + assert_eq!(annotations.annotation(sccs.scc(0)), 2); } #[test] fn test_bug_max_zero_stick_shape() { let graph = TestGraph::new(0, &[(0, 1), (1, 2), (2, 3), (3, 2), (3, 4)]); - - let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |w| match w { - 4 => MaxReached(1), - _ => MaxReached(0), + let mut annotations = Maxes::new(|w| match w { + 4 => 1, + _ => 0, }); + let sccs = Sccs::new_with_annotation(&graph, &mut annotations); - assert_eq!(sccs.annotation(sccs.scc(0)), 1); - assert_eq!(sccs.annotation(sccs.scc(1)), 1); - assert_eq!(sccs.annotation(sccs.scc(2)), 1); - assert_eq!(sccs.annotation(sccs.scc(3)), 1); - assert_eq!(sccs.annotation(sccs.scc(4)), 1); + assert_eq!(annotations.annotation(sccs.scc(0)), 1); + assert_eq!(annotations.annotation(sccs.scc(1)), 1); + assert_eq!(annotations.annotation(sccs.scc(2)), 1); + assert_eq!(annotations.annotation(sccs.scc(3)), 1); + assert_eq!(annotations.annotation(sccs.scc(4)), 1); } #[test] fn test_min_max_in() { let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (3, 0), (3, 4), (4, 3), (3, 5)]); - let sccs: Sccs<usize, usize, MinMaxIn> = - Sccs::new_with_annotation(&graph, |w| MinMaxIn { min: w, max: w }); - - assert_eq!(sccs.annotation(sccs.scc(2)).min, 2); - assert_eq!(sccs.annotation(sccs.scc(2)).max, 2); - assert_eq!(sccs.annotation(sccs.scc(0)).min, 0); - assert_eq!(sccs.annotation(sccs.scc(0)).max, 4); - assert_eq!(sccs.annotation(sccs.scc(3)).min, 0); - assert_eq!(sccs.annotation(sccs.scc(3)).max, 4); - assert_eq!(sccs.annotation(sccs.scc(5)).min, 5); + let mut annotations = MinMaxes(IndexVec::new(), |w| MinMaxIn { min: w, max: w }); + let sccs = Sccs::new_with_annotation(&graph, &mut annotations); + + assert_eq!(annotations.annotation(sccs.scc(2)).min, 2); + assert_eq!(annotations.annotation(sccs.scc(2)).max, 2); + assert_eq!(annotations.annotation(sccs.scc(0)).min, 0); + assert_eq!(annotations.annotation(sccs.scc(0)).max, 4); + assert_eq!(annotations.annotation(sccs.scc(3)).min, 0); + assert_eq!(annotations.annotation(sccs.scc(3)).max, 4); + assert_eq!(annotations.annotation(sccs.scc(5)).min, 5); } diff --git a/compiler/rustc_error_codes/src/error_codes/E0207.md b/compiler/rustc_error_codes/src/error_codes/E0207.md index 5b35748f472..f80b0093ecc 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0207.md +++ b/compiler/rustc_error_codes/src/error_codes/E0207.md @@ -195,7 +195,7 @@ impl<'a> Contains for Foo { Please note that unconstrained lifetime parameters are not supported if they are being used by an associated type. -In cases where the associated type's lifetime is meant to be tied to the the +In cases where the associated type's lifetime is meant to be tied to the self type, and none of the methods on the trait need ownership or different mutability, then an option is to implement the trait on a borrowed type: diff --git a/compiler/rustc_error_codes/src/error_codes/E0253.md b/compiler/rustc_error_codes/src/error_codes/E0253.md index 705d1bfc53e..628f5e252fb 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0253.md +++ b/compiler/rustc_error_codes/src/error_codes/E0253.md @@ -1,9 +1,13 @@ +#### Note: this error code is no longer emitted by the compiler. + Attempt was made to import an unimportable type. This can happen when trying to import a type from a trait. Erroneous code example: -```compile_fail,E0253 +``` +#![feature(import_trait_associated_functions)] + mod foo { pub trait MyTrait { type SomeType; diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index af587ee5bdc..107aea4e5a4 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -36,17 +36,23 @@ pub(crate) use crate::hir_id::{HirId, ItemLocalId, ItemLocalMap, OwnerId}; use crate::intravisit::{FnKind, VisitorExt}; #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable_Generic)] +pub enum AngleBrackets { + /// E.g. `Path`. + Missing, + /// E.g. `Path<>`. + Empty, + /// E.g. `Path<T>`. + Full, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable_Generic)] pub enum LifetimeSource { /// E.g. `&Type`, `&'_ Type`, `&'a Type`, `&mut Type`, `&'_ mut Type`, `&'a mut Type` Reference, - /// E.g. `ContainsLifetime`, `ContainsLifetime<'_>`, `ContainsLifetime<'a>` - Path { - /// - true for `ContainsLifetime<'_>`, `ContainsLifetime<'a>`, - /// `ContainsLifetime<'_, T>`, `ContainsLifetime<'a, T>` - /// - false for `ContainsLifetime` - with_angle_brackets: bool, - }, + /// E.g. `ContainsLifetime`, `ContainsLifetime<>`, `ContainsLifetime<'_>`, + /// `ContainsLifetime<'a>` + Path { angle_brackets: AngleBrackets }, /// E.g. `impl Trait + '_`, `impl Trait + 'a` OutlivesBound, @@ -304,12 +310,17 @@ impl Lifetime { (Named | Anonymous, _) => (self.ident.span, format!("{new_lifetime}")), // The user wrote `Path<T>`, and omitted the `'_,`. - (Hidden, Path { with_angle_brackets: true }) => { + (Hidden, Path { angle_brackets: AngleBrackets::Full }) => { (self.ident.span, format!("{new_lifetime}, ")) } + // The user wrote `Path<>`, and omitted the `'_`.. + (Hidden, Path { angle_brackets: AngleBrackets::Empty }) => { + (self.ident.span, format!("{new_lifetime}")) + } + // The user wrote `Path` and omitted the `<'_>`. - (Hidden, Path { with_angle_brackets: false }) => { + (Hidden, Path { angle_brackets: AngleBrackets::Missing }) => { (self.ident.span.shrink_to_hi(), format!("<{new_lifetime}>")) } @@ -2730,6 +2741,9 @@ pub enum ExprKind<'hir> { /// An `if` block, with an optional else block. /// /// I.e., `if <expr> { <expr> } else { <expr> }`. + /// + /// The "then" expr is always `ExprKind::Block`. If present, the "else" expr is always + /// `ExprKind::Block` (for `else`) or `ExprKind::If` (for `else if`). If(&'hir Expr<'hir>, &'hir Expr<'hir>, Option<&'hir Expr<'hir>>), /// A conditionless loop (can be exited with `break`, `continue`, or `return`). /// diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index fcb7382549f..5e79e932015 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -1770,7 +1770,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span: Span, opt_self_ty: Option<Ty<'tcx>>, item_def_id: DefId, - trait_segment: &hir::PathSegment<'tcx>, + trait_segment: Option<&hir::PathSegment<'tcx>>, item_segment: &hir::PathSegment<'tcx>, ) -> Ty<'tcx> { match self.lower_qpath_shared( @@ -1795,7 +1795,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span: Span, opt_self_ty: Option<Ty<'tcx>>, item_def_id: DefId, - trait_segment: &hir::PathSegment<'tcx>, + trait_segment: Option<&hir::PathSegment<'tcx>>, item_segment: &hir::PathSegment<'tcx>, ) -> Const<'tcx> { match self.lower_qpath_shared( @@ -1820,7 +1820,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span: Span, opt_self_ty: Option<Ty<'tcx>>, item_def_id: DefId, - trait_segment: &hir::PathSegment<'tcx>, + trait_segment: Option<&hir::PathSegment<'tcx>>, item_segment: &hir::PathSegment<'tcx>, assoc_tag: ty::AssocTag, ) -> Result<(DefId, GenericArgsRef<'tcx>), ErrorGuaranteed> { @@ -1840,7 +1840,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { debug!(?self_ty); let trait_ref = - self.lower_mono_trait_ref(span, trait_def_id, self_ty, trait_segment, false); + self.lower_mono_trait_ref(span, trait_def_id, self_ty, trait_segment.unwrap(), false); debug!(?trait_ref); let item_args = @@ -2196,16 +2196,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } Res::Def(DefKind::AssocTy, def_id) => { - debug_assert!(path.segments.len() >= 2); - let _ = self.prohibit_generic_args( - path.segments[..path.segments.len() - 2].iter(), - GenericsArgsErrExtend::None, - ); + let trait_segment = if let [modules @ .., trait_, _item] = path.segments { + let _ = self.prohibit_generic_args(modules.iter(), GenericsArgsErrExtend::None); + Some(trait_) + } else { + None + }; self.lower_qpath_ty( span, opt_self_ty, def_id, - &path.segments[path.segments.len() - 2], + trait_segment, path.segments.last().unwrap(), ) } @@ -2413,16 +2414,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ty::Const::new_unevaluated(tcx, ty::UnevaluatedConst::new(did, args)) } Res::Def(DefKind::AssocConst, did) => { - debug_assert!(path.segments.len() >= 2); - let _ = self.prohibit_generic_args( - path.segments[..path.segments.len() - 2].iter(), - GenericsArgsErrExtend::None, - ); + let trait_segment = if let [modules @ .., trait_, _item] = path.segments { + let _ = self.prohibit_generic_args(modules.iter(), GenericsArgsErrExtend::None); + Some(trait_) + } else { + None + }; self.lower_qpath_const( span, opt_self_ty, did, - &path.segments[path.segments.len() - 2], + trait_segment, path.segments.last().unwrap(), ) } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 15e997aebcb..09bf84ab64f 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -110,6 +110,7 @@ impl<'a> State<'a> { } self.print_attr_item(&unparsed, unparsed.span); self.word("]"); + self.hardbreak() } hir::Attribute::Parsed(AttributeKind::DocComment { style, kind, comment, .. }) => { self.word(rustc_ast_pretty::pprust::state::doc_comment_to_string( @@ -183,7 +184,7 @@ impl<'a> State<'a> { Node::Ty(a) => self.print_type(a), Node::AssocItemConstraint(a) => self.print_assoc_item_constraint(a), Node::TraitRef(a) => self.print_trait_ref(a), - Node::OpaqueTy(o) => self.print_opaque_ty(o), + Node::OpaqueTy(_) => panic!("cannot print Node::OpaqueTy"), Node::Pat(a) => self.print_pat(a), Node::TyPat(a) => self.print_ty_pat(a), Node::PatField(a) => self.print_patfield(a), @@ -654,10 +655,11 @@ impl<'a> State<'a> { self.bclose(item.span, cb); } hir::ItemKind::GlobalAsm { asm, .. } => { - // FIXME(nnethercote): `ib` is unclosed - let (cb, _ib) = self.head("global_asm!"); + let (cb, ib) = self.head("global_asm!"); self.print_inline_asm(asm); - self.end(cb) + self.word(";"); + self.end(cb); + self.end(ib); } hir::ItemKind::TyAlias(ident, ty, generics) => { let (cb, ib) = self.head("type"); @@ -764,14 +766,6 @@ impl<'a> State<'a> { self.print_path(t.path, false); } - fn print_opaque_ty(&mut self, o: &hir::OpaqueTy<'_>) { - // FIXME(nnethercote): `cb` and `ib` are unclosed - let (_cb, _ib) = self.head("opaque"); - self.word("{"); - self.print_bounds("impl", o.bounds); - self.word("}"); - } - fn print_formal_generic_params(&mut self, generic_params: &[hir::GenericParam<'_>]) { if !generic_params.is_empty() { self.word("for"); @@ -1509,7 +1503,7 @@ impl<'a> State<'a> { } hir::ExprKind::DropTemps(init) => { // Print `{`: - let cb = self.cbox(INDENT_UNIT); + let cb = self.cbox(0); let ib = self.ibox(0); self.bopen(ib); @@ -1532,16 +1526,18 @@ impl<'a> State<'a> { self.print_if(test, blk, elseopt); } hir::ExprKind::Loop(blk, opt_label, _, _) => { + let cb = self.cbox(0); + let ib = self.ibox(0); if let Some(label) = opt_label { self.print_ident(label.ident); self.word_space(":"); } - let (cb, ib) = self.head("loop"); + self.word_nbsp("loop"); self.print_block(blk, cb, ib); } hir::ExprKind::Match(expr, arms, _) => { - let cb = self.cbox(INDENT_UNIT); - let ib = self.ibox(INDENT_UNIT); + let cb = self.cbox(0); + let ib = self.ibox(0); self.word_nbsp("match"); self.print_expr_as_cond(expr); self.space(); @@ -1572,15 +1568,6 @@ impl<'a> State<'a> { // This is a bare expression. self.ann.nested(self, Nested::Body(body)); - // FIXME(nnethercote): this is bogus - let fake_ib = BoxMarker; - self.end(fake_ib); - - // A box will be closed by `print_expr`, but we didn't want an overall - // wrapper so we closed the corresponding opening. so create an - // empty box to satisfy the close. - // FIXME(nnethercote): this is bogus, and `print_expr` is missing - let _ib = self.ibox(0); } hir::ExprKind::Block(blk, opt_label) => { if let Some(label) = opt_label { diff --git a/compiler/rustc_lint/src/autorefs.rs b/compiler/rustc_lint/src/autorefs.rs index 5dd26854c95..ddab13190be 100644 --- a/compiler/rustc_lint/src/autorefs.rs +++ b/compiler/rustc_lint/src/autorefs.rs @@ -75,10 +75,9 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitAutorefs { _ => return, }, ExprKind::Index(base, _, _) => base, - ExprKind::MethodCall(_, inner, _, _) - if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && cx.tcx.has_attr(def_id, sym::rustc_no_implicit_autorefs) => - { + ExprKind::MethodCall(_, inner, _, _) => { + // PERF: Checking of `#[rustc_no_implicit_refs]` is deferred below + // because checking for attribute is a bit costly. inner } ExprKind::Field(inner, _) => inner, @@ -99,6 +98,14 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitAutorefs { peel_place_mappers(inner).kind // 1. Deref of a raw pointer. && typeck.expr_ty(dereferenced).is_raw_ptr() + // PERF: 5. b. A method call annotated with `#[rustc_no_implicit_refs]` + && match expr.kind { + ExprKind::MethodCall(..) => matches!( + cx.typeck_results().type_dependent_def_id(expr.hir_id), + Some(def_id) if cx.tcx.has_attr(def_id, sym::rustc_no_implicit_autorefs) + ), + _ => true, + } { cx.emit_span_lint( DANGEROUS_IMPLICIT_AUTOREFS, diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 806bca78f78..50a27d7e84f 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -942,6 +942,22 @@ trait UnusedDelimLint { match s.kind { StmtKind::Let(ref local) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => { if let Some((init, els)) = local.kind.init_else_opt() { + if els.is_some() + && let ExprKind::Paren(paren) = &init.kind + && !init.span.eq_ctxt(paren.span) + { + // This branch prevents cases where parentheses wrap an expression + // resulting from macro expansion, such as: + // ``` + // macro_rules! x { + // () => { None::<i32> }; + // } + // let Some(_) = (x!{}) else { return }; + // // -> let Some(_) = (None::<i32>) else { return }; + // // ~ ~ No Lint + // ``` + return; + } let ctx = match els { None => UnusedDelimsCtx::AssignedValue, Some(_) => UnusedDelimsCtx::AssignedValueLetElse, diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index ebe8eb57f2c..d4a05fbbbc5 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -512,8 +512,13 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( #endif } +#if LLVM_VERSION_GE(21, 0) + TargetMachine *TM = TheTarget->createTargetMachine(Trip, CPU, Feature, + Options, RM, CM, OptLevel); +#else TargetMachine *TM = TheTarget->createTargetMachine( Trip.getTriple(), CPU, Feature, Options, RM, CM, OptLevel); +#endif return wrap(TM); } diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 890756a17ca..6ff3cac049b 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -221,13 +221,11 @@ pub enum InvalidProgramInfo<'tcx> { #[derive(Debug, Copy, Clone)] pub enum CheckInAllocMsg { /// We are access memory. - MemoryAccessTest, + MemoryAccess, /// We are doing pointer arithmetic. - PointerArithmeticTest, - /// We are doing pointer offset_from. - OffsetFromTest, + InboundsPointerArithmetic, /// None of the above -- generic/unspecific inbounds test. - InboundsTest, + Dereferenceable, } /// Details of which pointer is not aligned. diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 086ec529f33..b9a014d14c0 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -292,7 +292,10 @@ pub enum ExprKind<'tcx> { If { if_then_scope: region::Scope, cond: ExprId, + /// `then` is always `ExprKind::Block`. then: ExprId, + /// If present, the `else_opt` expr is always `ExprKind::Block` (for + /// `else`) or `ExprKind::If` (for `else if`). else_opt: Option<ExprId>, }, /// A function call. Method calls and overloaded operators are converted to plain function calls. diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 8e69ff568b9..4f00a85004d 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -553,8 +553,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let res = self.typeck_results.qpath_res(qpath, id); let (def_id, user_ty) = match res { - Res::Def(DefKind::Const, def_id) => (def_id, None), - Res::Def(DefKind::AssocConst, def_id) => { + Res::Def(DefKind::Const, def_id) | Res::Def(DefKind::AssocConst, def_id) => { (def_id, self.typeck_results.user_provided_types().get(id)) } diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 76dad6b3571..1e3744e19f5 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -205,6 +205,7 @@ //! this is not implemented however: a mono item will be produced //! regardless of whether it is actually needed or not. +use std::cell::OnceCell; use std::path::PathBuf; use rustc_attr_parsing::InlineAttr; @@ -348,6 +349,27 @@ impl<'tcx> Extend<Spanned<MonoItem<'tcx>>> for MonoItems<'tcx> { } } +fn collect_items_root<'tcx>( + tcx: TyCtxt<'tcx>, + starting_item: Spanned<MonoItem<'tcx>>, + state: &SharedState<'tcx>, + recursion_limit: Limit, +) { + if !state.visited.lock_mut().insert(starting_item.node) { + // We've been here already, no need to search again. + return; + } + let mut recursion_depths = DefIdMap::default(); + collect_items_rec( + tcx, + starting_item, + state, + &mut recursion_depths, + recursion_limit, + CollectionMode::UsedItems, + ); +} + /// Collect all monomorphized items reachable from `starting_point`, and emit a note diagnostic if a /// post-monomorphization error is encountered during a collection step. /// @@ -362,24 +384,6 @@ fn collect_items_rec<'tcx>( recursion_limit: Limit, mode: CollectionMode, ) { - if mode == CollectionMode::UsedItems { - if !state.visited.lock_mut().insert(starting_item.node) { - // We've been here already, no need to search again. - return; - } - } else { - if state.visited.lock().contains(&starting_item.node) { - // We've already done a *full* visit on this one, no need to do the "mention" visit. - return; - } - if !state.mentioned.lock_mut().insert(starting_item.node) { - // We've been here already, no need to search again. - return; - } - // There's some risk that we first do a 'mention' visit and then a full visit. But there's no - // harm in that, the mention visit will trigger all the queries and the results are cached. - } - let mut used_items = MonoItems::new(); let mut mentioned_items = MonoItems::new(); let recursion_depth_reset; @@ -536,6 +540,20 @@ fn collect_items_rec<'tcx>( state.usage_map.lock_mut().record_used(starting_item.node, &used_items); } + { + let mut visited = OnceCell::default(); + if mode == CollectionMode::UsedItems { + used_items + .items + .retain(|k, _| visited.get_mut_or_init(|| state.visited.lock_mut()).insert(*k)); + } + + let mut mentioned = OnceCell::default(); + mentioned_items.items.retain(|k, _| { + !visited.get_or_init(|| state.visited.lock()).contains(k) + && mentioned.get_mut_or_init(|| state.mentioned.lock_mut()).insert(*k) + }); + } if mode == CollectionMode::MentionedItems { assert!(used_items.is_empty(), "'mentioned' collection should never encounter used items"); } else { @@ -1689,15 +1707,7 @@ pub(crate) fn collect_crate_mono_items<'tcx>( tcx.sess.time("monomorphization_collector_graph_walk", || { par_for_each_in(roots, |root| { - let mut recursion_depths = DefIdMap::default(); - collect_items_rec( - tcx, - dummy_spanned(*root), - &state, - &mut recursion_depths, - recursion_limit, - CollectionMode::UsedItems, - ); + collect_items_root(tcx, dummy_spanned(*root), &state, recursion_limit); }); }); diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index 8469e0f17a6..1b484da698a 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -4,6 +4,7 @@ #![feature(file_buffered)] #![feature(if_let_guard)] #![feature(impl_trait_in_assoc_type)] +#![feature(once_cell_get_mut)] // tidy-alphabetical-end use rustc_hir::lang_items::LangItem; diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index a61d446a3a9..f04b167889f 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -625,6 +625,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { sym::naked, sym::instruction_set, sym::repr, + sym::rustc_std_internal_symbol, // code generation sym::cold, // documentation diff --git a/compiler/rustc_resolve/messages.ftl b/compiler/rustc_resolve/messages.ftl index 558a01713dc..38cdfa72a14 100644 --- a/compiler/rustc_resolve/messages.ftl +++ b/compiler/rustc_resolve/messages.ftl @@ -218,10 +218,6 @@ resolve_invalid_asm_sym = .label = is a local variable .help = `sym` operands must refer to either a function or a static -resolve_is_not_directly_importable = - `{$target}` is not directly importable - .label = cannot be imported directly - resolve_is_private = {$ident_descr} `{$ident}` is private .label = private {$ident_descr} @@ -231,9 +227,6 @@ resolve_item_was_behind_feature = resolve_item_was_cfg_out = the item is gated here -resolve_items_in_traits_are_not_importable = - items in traits are not importable - resolve_label_with_similar_name_reachable = a label with a similar name is reachable diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 363a75911ad..74daad08394 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1181,11 +1181,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } { let in_module_is_extern = !in_module.def_id().is_local(); in_module.for_each_child(self, |this, ident, ns, name_binding| { - // avoid non-importable candidates - if !name_binding.is_importable() - // FIXME(import_trait_associated_functions): remove this when `import_trait_associated_functions` is stable - || name_binding.is_assoc_const_or_fn() - && !this.tcx.features().import_trait_associated_functions() + // Avoid non-importable candidates. + if name_binding.is_assoc_item() + && !this.tcx.features().import_trait_associated_functions() { return; } diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index e26b300f13e..7fe74378b67 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -781,22 +781,6 @@ pub(crate) struct CannotGlobImportAllCrates { pub(crate) span: Span, } -#[derive(Diagnostic)] -#[diag(resolve_items_in_traits_are_not_importable)] -pub(crate) struct ItemsInTraitsAreNotImportable { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Diagnostic)] -#[diag(resolve_is_not_directly_importable, code = E0253)] -pub(crate) struct IsNotDirectlyImportable { - #[primary_span] - #[label] - pub(crate) span: Span, - pub(crate) target: Ident, -} - #[derive(Subdiagnostic)] #[suggestion( resolve_unexpected_res_change_ty_to_const_param_sugg, diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 762e08b2be5..816efd0d5fa 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -30,8 +30,7 @@ use crate::diagnostics::{DiagMode, Suggestion, import_candidates}; use crate::errors::{ CannotBeReexportedCratePublic, CannotBeReexportedCratePublicNS, CannotBeReexportedPrivate, CannotBeReexportedPrivateNS, CannotDetermineImportResolution, CannotGlobImportAllCrates, - ConsiderAddingMacroExport, ConsiderMarkingAsPub, IsNotDirectlyImportable, - ItemsInTraitsAreNotImportable, + ConsiderAddingMacroExport, ConsiderMarkingAsPub, }; use crate::{ AmbiguityError, AmbiguityKind, BindingKey, Finalize, ImportSuggestion, Module, @@ -835,11 +834,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let parent = import.parent_scope.module; match source_bindings[ns].get() { - Err(Undetermined) => indeterminate_count += 1, - // Don't update the resolution, because it was never added. - Err(Determined) if target.name == kw::Underscore => {} - Ok(binding) if binding.is_importable() => { - if binding.is_assoc_const_or_fn() + Ok(binding) => { + if binding.is_assoc_item() && !this.tcx.features().import_trait_associated_functions() { feature_err( @@ -850,21 +846,21 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ) .emit(); } + let imported_binding = this.import(binding, import); target_bindings[ns].set(Some(imported_binding)); this.define(parent, target, ns, imported_binding); } - source_binding @ (Ok(..) | Err(Determined)) => { - if source_binding.is_ok() { - this.dcx() - .create_err(IsNotDirectlyImportable { span: import.span, target }) - .emit(); + Err(Determined) => { + // Don't update the resolution for underscores, because it was never added. + if target.name != kw::Underscore { + let key = BindingKey::new(target, ns); + this.update_resolution(parent, key, false, |_, resolution| { + resolution.single_imports.swap_remove(&import); + }); } - let key = BindingKey::new(target, ns); - this.update_resolution(parent, key, false, |_, resolution| { - resolution.single_imports.swap_remove(&import); - }); } + Err(Undetermined) => indeterminate_count += 1, } } }); @@ -1428,10 +1424,17 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { return; }; - if module.is_trait() { - self.dcx().emit_err(ItemsInTraitsAreNotImportable { span: import.span }); - return; - } else if module == import.parent_scope.module { + if module.is_trait() && !self.tcx.features().import_trait_associated_functions() { + feature_err( + self.tcx.sess, + sym::import_trait_associated_functions, + import.span, + "`use` associated items of traits is unstable", + ) + .emit(); + } + + if module == import.parent_scope.module { return; } else if is_prelude { self.prelude = Some(module); diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index d2da3ac7d86..d23e588e2e3 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -949,14 +949,8 @@ impl<'ra> NameBindingData<'ra> { } } - fn is_importable(&self) -> bool { - !matches!(self.res(), Res::Def(DefKind::AssocTy, _)) - } - - // FIXME(import_trait_associated_functions): associate `const` or `fn` are not importable unless - // the feature `import_trait_associated_functions` is enable - fn is_assoc_const_or_fn(&self) -> bool { - matches!(self.res(), Res::Def(DefKind::AssocConst | DefKind::AssocFn, _)) + fn is_assoc_item(&self) -> bool { + matches!(self.res(), Res::Def(DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy, _)) } fn macro_kind(&self) -> Option<MacroKind> { diff --git a/compiler/rustc_span/src/span_encoding.rs b/compiler/rustc_span/src/span_encoding.rs index 9d6c7d2a42a..a4a47dc99b0 100644 --- a/compiler/rustc_span/src/span_encoding.rs +++ b/compiler/rustc_span/src/span_encoding.rs @@ -306,8 +306,21 @@ impl Span { /// Returns `true` if this span comes from any kind of macro, desugaring or inlining. #[inline] pub fn from_expansion(self) -> bool { - // If the span is fully inferred then ctxt > MAX_CTXT - self.inline_ctxt().map_or(true, |ctxt| !ctxt.is_root()) + let ctxt = match_span_kind! { + self, + // All branches here, except `InlineParent`, actually return `span.ctxt_or_parent_or_marker`. + // Since `Interned` is selected if the field contains `CTXT_INTERNED_MARKER` returning that value + // as the context allows the compiler to optimize out the branch that selects between either + // `Interned` and `PartiallyInterned`. + // + // Interned contexts can never be the root context and `CTXT_INTERNED_MARKER` has a different value + // than the root context so this works for checking is this is an expansion. + InlineCtxt(span) => SyntaxContext::from_u16(span.ctxt), + InlineParent(_span) => SyntaxContext::root(), + PartiallyInterned(span) => SyntaxContext::from_u16(span.ctxt), + Interned(_span) => SyntaxContext::from_u16(CTXT_INTERNED_MARKER), + }; + !ctxt.is_root() } /// Returns `true` if this is a dummy span with any hygienic context. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index ba3e6d7ca82..7a1fb36324b 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -174,7 +174,6 @@ symbols! { Arc, ArcWeak, Argument, - ArgumentMethods, ArrayIntoIter, AsMut, AsRef, @@ -249,6 +248,7 @@ symbols! { Error, File, FileType, + FmtArgumentsNew, Fn, FnMut, FnOnce, diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 69c8b9119ab..f856d3efc1c 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -102,6 +102,9 @@ impl Stability { // check whether they're named already elsewhere in rust // e.g. in stdarch and whether the given name matches LLVM's // if it doesn't, to_llvm_feature in llvm_util in rustc_codegen_llvm needs to be adapted. +// Additionally, if the feature is not available in older version of LLVM supported by the current +// rust, the same function must be updated to filter out these features to avoid triggering +// warnings. // // Also note that all target features listed here must be purely additive: for target_feature 1.1 to // be sound, we can never allow features like `+soft-float` (on x86) to be controlled on a @@ -398,7 +401,7 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("avx512cd", Unstable(sym::avx512_target_feature), &["avx512f"]), ("avx512dq", Unstable(sym::avx512_target_feature), &["avx512f"]), ("avx512f", Unstable(sym::avx512_target_feature), &["avx2", "fma", "f16c"]), - ("avx512fp16", Unstable(sym::avx512_target_feature), &["avx512bw", "avx512vl", "avx512dq"]), + ("avx512fp16", Unstable(sym::avx512_target_feature), &["avx512bw"]), ("avx512ifma", Unstable(sym::avx512_target_feature), &["avx512f"]), ("avx512vbmi", Unstable(sym::avx512_target_feature), &["avx512bw"]), ("avx512vbmi2", Unstable(sym::avx512_target_feature), &["avx512bw"]), @@ -507,7 +510,7 @@ static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("unaligned-vector-mem", Unstable(sym::riscv_target_feature), &[]), ("v", Unstable(sym::riscv_target_feature), &["zvl128b", "zve64d"]), ("za128rs", Unstable(sym::riscv_target_feature), &[]), - ("za64rs", Unstable(sym::riscv_target_feature), &[]), + ("za64rs", Unstable(sym::riscv_target_feature), &["za128rs"]), // Za64rs ⊃ Za128rs ("zaamo", Unstable(sym::riscv_target_feature), &[]), ("zabha", Unstable(sym::riscv_target_feature), &["zaamo"]), ("zacas", Unstable(sym::riscv_target_feature), &["zaamo"]), @@ -526,12 +529,20 @@ static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("zcmop", Unstable(sym::riscv_target_feature), &["zca"]), ("zdinx", Unstable(sym::riscv_target_feature), &["zfinx"]), ("zfa", Unstable(sym::riscv_target_feature), &["f"]), + ("zfbfmin", Unstable(sym::riscv_target_feature), &["f"]), // and a subset of Zfhmin ("zfh", Unstable(sym::riscv_target_feature), &["zfhmin"]), ("zfhmin", Unstable(sym::riscv_target_feature), &["f"]), ("zfinx", Unstable(sym::riscv_target_feature), &["zicsr"]), ("zhinx", Unstable(sym::riscv_target_feature), &["zhinxmin"]), ("zhinxmin", Unstable(sym::riscv_target_feature), &["zfinx"]), + ("zic64b", Unstable(sym::riscv_target_feature), &[]), + ("zicbom", Unstable(sym::riscv_target_feature), &[]), + ("zicbop", Unstable(sym::riscv_target_feature), &[]), ("zicboz", Unstable(sym::riscv_target_feature), &[]), + ("ziccamoa", Unstable(sym::riscv_target_feature), &[]), + ("ziccif", Unstable(sym::riscv_target_feature), &[]), + ("zicclsm", Unstable(sym::riscv_target_feature), &[]), + ("ziccrse", Unstable(sym::riscv_target_feature), &[]), ("zicntr", Unstable(sym::riscv_target_feature), &["zicsr"]), ("zicond", Unstable(sym::riscv_target_feature), &[]), ("zicsr", Unstable(sym::riscv_target_feature), &[]), @@ -558,6 +569,8 @@ static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("zve64d", Unstable(sym::riscv_target_feature), &["zve64f", "d"]), ("zve64f", Unstable(sym::riscv_target_feature), &["zve32f", "zve64x"]), ("zve64x", Unstable(sym::riscv_target_feature), &["zve32x", "zvl64b"]), + ("zvfbfmin", Unstable(sym::riscv_target_feature), &["zve32f"]), + ("zvfbfwma", Unstable(sym::riscv_target_feature), &["zfbfmin", "zvfbfmin"]), ("zvfh", Unstable(sym::riscv_target_feature), &["zvfhmin", "zve32f", "zfhmin"]), // Zvfh ⊃ Zvfhmin ("zvfhmin", Unstable(sym::riscv_target_feature), &["zve32f"]), ("zvkb", Unstable(sym::riscv_target_feature), &["zve32x"]), @@ -963,12 +976,12 @@ impl Target { // about what the intended ABI is. match &*self.llvm_abiname { "ilp32d" | "lp64d" => { - // Requires d (which implies f), incompatible with e. - FeatureConstraints { required: &["d"], incompatible: &["e"] } + // Requires d (which implies f), incompatible with e and zfinx. + FeatureConstraints { required: &["d"], incompatible: &["e", "zfinx"] } } "ilp32f" | "lp64f" => { - // Requires f, incompatible with e. - FeatureConstraints { required: &["f"], incompatible: &["e"] } + // Requires f, incompatible with e and zfinx. + FeatureConstraints { required: &["f"], incompatible: &["e", "zfinx"] } } "ilp32" | "lp64" => { // Requires nothing, incompatible with e. diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index ab2aa0ae469..970160ba212 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -15,6 +15,7 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::intravisit::Visitor; use rustc_hir::{self as hir, LangItem, Node}; use rustc_infer::infer::{InferOk, TypeTrace}; +use rustc_infer::traits::ImplSource; use rustc_infer::traits::solve::Goal; use rustc_middle::traits::SignatureMismatchData; use rustc_middle::traits::select::OverflowError; @@ -48,8 +49,8 @@ use crate::infer::{self, InferCtxt, InferCtxtExt as _}; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use crate::traits::{ MismatchedProjectionTypes, NormalizeExt, Obligation, ObligationCause, ObligationCauseCode, - ObligationCtxt, Overflow, PredicateObligation, SelectionError, SignatureMismatch, - TraitDynIncompatible, elaborate, + ObligationCtxt, Overflow, PredicateObligation, SelectionContext, SelectionError, + SignatureMismatch, TraitDynIncompatible, elaborate, specialization_graph, }; impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { @@ -1495,32 +1496,33 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - let secondary_span = (|| { + let secondary_span = self.probe(|_| { let ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj)) = predicate.kind().skip_binder() else { return None; }; - let mut associated_items = vec![]; - self.tcx.for_each_relevant_impl( - self.tcx.trait_of_item(proj.projection_term.def_id)?, - proj.projection_term.self_ty(), - |impl_def_id| { - associated_items.extend( - self.tcx.associated_items(impl_def_id).in_definition_order().find( - |assoc| { - assoc.trait_item_def_id == Some(proj.projection_term.def_id) - }, - ), - ); - }, - ); + let Ok(Some(ImplSource::UserDefined(impl_data))) = SelectionContext::new(self) + .poly_select(&obligation.with( + self.tcx, + predicate.kind().rebind(proj.projection_term.trait_ref(self.tcx)), + )) + else { + return None; + }; - let [associated_item]: &[ty::AssocItem] = &associated_items[..] else { + let Ok(node) = + specialization_graph::assoc_def(self.tcx, impl_data.impl_def_id, proj.def_id()) + else { return None; }; - match self.tcx.hir_get_if_local(associated_item.def_id) { + + if !node.is_final() { + return None; + } + + match self.tcx.hir_get_if_local(node.item.def_id) { Some( hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Type(_, Some(ty)), @@ -1543,7 +1545,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { )), _ => None, } - })(); + }); self.note_type_err( &mut diag, diff --git a/compiler/rustc_transmute/src/layout/dfa.rs b/compiler/rustc_transmute/src/layout/dfa.rs index 05afa28db31..6d072c336af 100644 --- a/compiler/rustc_transmute/src/layout/dfa.rs +++ b/compiler/rustc_transmute/src/layout/dfa.rs @@ -266,7 +266,7 @@ where } #[cfg(test)] - pub(crate) fn from_edges<B: Copy + Into<Byte>>( + pub(crate) fn from_edges<B: Clone + Into<Byte>>( start: u32, accept: u32, edges: &[(u32, B, u32)], @@ -275,8 +275,8 @@ where let accept = State(accept); let mut transitions: Map<State, Vec<(Byte, State)>> = Map::default(); - for (src, edge, dst) in edges.iter().copied() { - transitions.entry(State(src)).or_default().push((edge.into(), State(dst))); + for &(src, ref edge, dst) in edges.iter() { + transitions.entry(State(src)).or_default().push((edge.clone().into(), State(dst))); } let transitions = transitions @@ -401,12 +401,24 @@ mod edge_set { mut join: impl FnMut(Option<S>, Option<S>) -> S, ) -> EdgeSet<S> where - S: Copy, + S: Copy + Eq, { + let mut runs: SmallVec<[(Byte, S); 1]> = SmallVec::new(); let xs = self.runs.iter().copied(); let ys = other.runs.iter().copied(); - // FIXME(@joshlf): Merge contiguous runs with common destination. - EdgeSet { runs: union(xs, ys).map(|(range, (x, y))| (range, join(x, y))).collect() } + for (range, (x, y)) in union(xs, ys) { + let state = join(x, y); + match runs.last_mut() { + // Merge contiguous runs with a common destination. + Some(&mut (ref mut last_range, ref mut last_state)) + if last_range.end == range.start && *last_state == state => + { + last_range.end = range.end + } + _ => runs.push((range, state)), + } + } + EdgeSet { runs } } } } diff --git a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs index fbb4639dbd6..0227ad71ae6 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs @@ -314,7 +314,7 @@ mod union { let u = s.clone().union(t.clone(), new_state); let expected_u = - Dfa::from_edges(b, a, &[(b, 0, c), (b, 1, d), (d, 1, a), (d, 0, a), (c, 0, a)]); + Dfa::from_edges(b, a, &[(b, 0..=0, c), (b, 1..=1, d), (d, 0..=1, a), (c, 0..=0, a)]); assert_eq!(u, expected_u); diff --git a/library/core/Cargo.toml b/library/core/Cargo.toml index fe61f552a49..99e52d0ada0 100644 --- a/library/core/Cargo.toml +++ b/library/core/Cargo.toml @@ -31,7 +31,6 @@ level = "warn" check-cfg = [ 'cfg(bootstrap)', 'cfg(no_fp_fmt_parse)', - 'cfg(stdarch_intel_sde)', # core use #[path] imports to portable-simd `core_simd` crate # and to stdarch `core_arch` crate which messes-up with Cargo list # of declared features, we therefor expect any feature cfg diff --git a/library/core/src/alloc/mod.rs b/library/core/src/alloc/mod.rs index 9805cee1c33..9d608d5e83c 100644 --- a/library/core/src/alloc/mod.rs +++ b/library/core/src/alloc/mod.rs @@ -90,7 +90,7 @@ impl fmt::Display for AllocError { /// # Safety /// /// Memory blocks that are [*currently allocated*] by an allocator, -/// must point to valid memory, and retain their validity while until either: +/// must point to valid memory, and retain their validity until either: /// - the memory block is deallocated, or /// - the allocator is dropped. /// @@ -112,7 +112,9 @@ pub unsafe trait Allocator { /// /// The returned block of memory remains valid as long as it is [*currently allocated*] and the shorter of: /// - the borrow-checker lifetime of the allocator type itself. - /// - as long as at the allocator and all its clones has not been dropped. + /// - as long as the allocator and all its clones have not been dropped. + /// + /// [*currently allocated*]: #currently-allocated-memory /// /// # Errors /// diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 580f95eddce..4f7f8a5b84d 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -622,44 +622,9 @@ pub struct Arguments<'a> { args: &'a [rt::Argument<'a>], } -/// Used by the format_args!() macro to create a fmt::Arguments object. #[doc(hidden)] #[unstable(feature = "fmt_internals", issue = "none")] impl<'a> Arguments<'a> { - #[inline] - pub const fn new_const<const N: usize>(pieces: &'a [&'static str; N]) -> Self { - const { assert!(N <= 1) }; - Arguments { pieces, fmt: None, args: &[] } - } - - /// When using the format_args!() macro, this function is used to generate the - /// Arguments structure. - #[inline] - pub const fn new_v1<const P: usize, const A: usize>( - pieces: &'a [&'static str; P], - args: &'a [rt::Argument<'a>; A], - ) -> Arguments<'a> { - const { assert!(P >= A && P <= A + 1, "invalid args") } - Arguments { pieces, fmt: None, args } - } - - /// Specifies nonstandard formatting parameters. - /// - /// An `rt::UnsafeArg` is required because the following invariants must be held - /// in order for this function to be safe: - /// 1. The `pieces` slice must be at least as long as `fmt`. - /// 2. Every `rt::Placeholder::position` value within `fmt` must be a valid index of `args`. - /// 3. Every `rt::Count::Param` within `fmt` must contain a valid index of `args`. - #[inline] - pub const fn new_v1_formatted( - pieces: &'a [&'static str], - args: &'a [rt::Argument<'a>], - fmt: &'a [rt::Placeholder], - _unsafe_arg: rt::UnsafeArg, - ) -> Arguments<'a> { - Arguments { pieces, fmt: Some(fmt), args } - } - /// Estimates the length of the formatted text. /// /// This is intended to be used for setting initial `String` capacity diff --git a/library/core/src/fmt/rt.rs b/library/core/src/fmt/rt.rs index e409771362e..c2a8a39bcac 100644 --- a/library/core/src/fmt/rt.rs +++ b/library/core/src/fmt/rt.rs @@ -1,7 +1,10 @@ #![allow(missing_debug_implementations)] #![unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] -//! These are the lang items used by format_args!(). +//! All types and methods in this file are used by the compiler in +//! the expansion/lowering of format_args!(). +//! +//! Do not modify them without understanding the consequences for the format_args!() macro. use super::*; use crate::hint::unreachable_unchecked; @@ -110,46 +113,45 @@ macro_rules! argument_new { }; } -#[rustc_diagnostic_item = "ArgumentMethods"] impl Argument<'_> { #[inline] - pub fn new_display<T: Display>(x: &T) -> Argument<'_> { + pub const fn new_display<T: Display>(x: &T) -> Argument<'_> { argument_new!(T, x, <T as Display>::fmt) } #[inline] - pub fn new_debug<T: Debug>(x: &T) -> Argument<'_> { + pub const fn new_debug<T: Debug>(x: &T) -> Argument<'_> { argument_new!(T, x, <T as Debug>::fmt) } #[inline] - pub fn new_debug_noop<T: Debug>(x: &T) -> Argument<'_> { + pub const fn new_debug_noop<T: Debug>(x: &T) -> Argument<'_> { argument_new!(T, x, |_: &T, _| Ok(())) } #[inline] - pub fn new_octal<T: Octal>(x: &T) -> Argument<'_> { + pub const fn new_octal<T: Octal>(x: &T) -> Argument<'_> { argument_new!(T, x, <T as Octal>::fmt) } #[inline] - pub fn new_lower_hex<T: LowerHex>(x: &T) -> Argument<'_> { + pub const fn new_lower_hex<T: LowerHex>(x: &T) -> Argument<'_> { argument_new!(T, x, <T as LowerHex>::fmt) } #[inline] - pub fn new_upper_hex<T: UpperHex>(x: &T) -> Argument<'_> { + pub const fn new_upper_hex<T: UpperHex>(x: &T) -> Argument<'_> { argument_new!(T, x, <T as UpperHex>::fmt) } #[inline] - pub fn new_pointer<T: Pointer>(x: &T) -> Argument<'_> { + pub const fn new_pointer<T: Pointer>(x: &T) -> Argument<'_> { argument_new!(T, x, <T as Pointer>::fmt) } #[inline] - pub fn new_binary<T: Binary>(x: &T) -> Argument<'_> { + pub const fn new_binary<T: Binary>(x: &T) -> Argument<'_> { argument_new!(T, x, <T as Binary>::fmt) } #[inline] - pub fn new_lower_exp<T: LowerExp>(x: &T) -> Argument<'_> { + pub const fn new_lower_exp<T: LowerExp>(x: &T) -> Argument<'_> { argument_new!(T, x, <T as LowerExp>::fmt) } #[inline] - pub fn new_upper_exp<T: UpperExp>(x: &T) -> Argument<'_> { + pub const fn new_upper_exp<T: UpperExp>(x: &T) -> Argument<'_> { argument_new!(T, x, <T as UpperExp>::fmt) } #[inline] @@ -200,15 +202,8 @@ impl Argument<'_> { /// let f = format_args!("{}", "a"); /// println!("{f}"); /// ``` - /// - /// This function should _not_ be const, to make sure we don't accept - /// format_args!() and panic!() with arguments in const, even when not evaluated: - /// - /// ```compile_fail,E0015 - /// const _: () = if false { panic!("a {}", "a") }; - /// ``` #[inline] - pub fn none() -> [Self; 0] { + pub const fn none() -> [Self; 0] { [] } } @@ -229,3 +224,57 @@ impl UnsafeArg { Self { _private: () } } } + +/// Used by the format_args!() macro to create a fmt::Arguments object. +#[doc(hidden)] +#[unstable(feature = "fmt_internals", issue = "none")] +#[rustc_diagnostic_item = "FmtArgumentsNew"] +impl<'a> Arguments<'a> { + #[inline] + pub const fn new_const<const N: usize>(pieces: &'a [&'static str; N]) -> Self { + const { assert!(N <= 1) }; + Arguments { pieces, fmt: None, args: &[] } + } + + /// When using the format_args!() macro, this function is used to generate the + /// Arguments structure. + /// + /// This function should _not_ be const, to make sure we don't accept + /// format_args!() and panic!() with arguments in const, even when not evaluated: + /// + /// ```compile_fail,E0015 + /// const _: () = if false { panic!("a {}", "a") }; + /// ``` + #[inline] + pub fn new_v1<const P: usize, const A: usize>( + pieces: &'a [&'static str; P], + args: &'a [rt::Argument<'a>; A], + ) -> Arguments<'a> { + const { assert!(P >= A && P <= A + 1, "invalid args") } + Arguments { pieces, fmt: None, args } + } + + /// Specifies nonstandard formatting parameters. + /// + /// An `rt::UnsafeArg` is required because the following invariants must be held + /// in order for this function to be safe: + /// 1. The `pieces` slice must be at least as long as `fmt`. + /// 2. Every `rt::Placeholder::position` value within `fmt` must be a valid index of `args`. + /// 3. Every `rt::Count::Param` within `fmt` must contain a valid index of `args`. + /// + /// This function should _not_ be const, to make sure we don't accept + /// format_args!() and panic!() with arguments in const, even when not evaluated: + /// + /// ```compile_fail,E0015 + /// const _: () = if false { panic!("a {:1}", "a") }; + /// ``` + #[inline] + pub fn new_v1_formatted( + pieces: &'a [&'static str], + args: &'a [rt::Argument<'a>], + fmt: &'a [rt::Placeholder], + _unsafe_arg: rt::UnsafeArg, + ) -> Arguments<'a> { + Arguments { pieces, fmt: Some(fmt), args } + } +} diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 1ca23ab6eea..394a3ea6778 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -763,8 +763,6 @@ pub const fn cold_path() { /// /// Distribute values evenly between two buckets: /// ``` -/// #![feature(select_unpredictable)] -/// /// use std::hash::BuildHasher; /// use std::hint; /// @@ -780,7 +778,7 @@ pub const fn cold_path() { /// # assert_eq!(bucket_one.len() + bucket_two.len(), 1); /// ``` #[inline(always)] -#[unstable(feature = "select_unpredictable", issue = "133962")] +#[stable(feature = "select_unpredictable", since = "CURRENT_RUSTC_VERSION")] pub fn select_unpredictable<T>(condition: bool, true_val: T, false_val: T) -> T { // FIXME(https://github.com/rust-lang/unsafe-code-guidelines/issues/245): // Change this to use ManuallyDrop instead. diff --git a/library/core/src/intrinsics/simd.rs b/library/core/src/intrinsics/simd.rs index 6afe924f99d..86e3f8509ee 100644 --- a/library/core/src/intrinsics/simd.rs +++ b/library/core/src/intrinsics/simd.rs @@ -577,11 +577,9 @@ pub unsafe fn simd_select<M, T>(mask: M, if_true: T, if_false: T) -> T; /// For each element, if the bit in `mask` is `1`, select the element from /// `if_true`. If the corresponding bit in `mask` is `0`, select the element from /// `if_false`. +/// The remaining bits of the mask are ignored. /// /// The bitmask bit order matches `simd_bitmask`. -/// -/// # Safety -/// Padding bits must be all zero. #[rustc_intrinsic] #[rustc_nounwind] pub unsafe fn simd_select_bitmask<M, T>(m: M, yes: T, no: T) -> T; diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index d9534a44598..c68fd2115d6 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -1340,6 +1340,24 @@ pub trait Iterator { /// assert_eq!(iter.next(), Some(2)); /// assert_eq!(iter.next(), None); /// ``` + /// + /// Use [`by_ref`] to take from the iterator without consuming it, and then + /// continue using the original iterator: + /// + /// ``` + /// let mut words = ["hello", "world", "of", "Rust"].into_iter(); + /// + /// // Take the first two words. + /// let hello_world: Vec<_> = words.by_ref().take(2).collect(); + /// assert_eq!(hello_world, vec!["hello", "world"]); + /// + /// // Collect the rest of the words. + /// // We can only do this because we used `by_ref` earlier. + /// let of_rust: Vec<_> = words.collect(); + /// assert_eq!(of_rust, vec!["of", "Rust"]); + /// ``` + /// + /// [`by_ref`]: Iterator::by_ref #[inline] #[stable(feature = "rust1", since = "1.0.0")] fn take(self, n: usize) -> Take<Self> diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 16c0c118040..3ad6662bdd7 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1748,8 +1748,8 @@ pub(crate) mod builtin { /* compiler built-in */ } - /// Provide a list of type aliases and other opaque-type-containing type definitions. - /// This list will be used in the body of the item it is applied to define opaque + /// Provide a list of type aliases and other opaque-type-containing type definitions + /// to an item with a body. This list will be used in that body to define opaque /// types' hidden types. /// Can only be applied to things that have bodies. #[unstable( diff --git a/library/core/src/num/diy_float.rs b/library/core/src/num/diy_float.rs index ce7f6475d05..e054e7f3f10 100644 --- a/library/core/src/num/diy_float.rs +++ b/library/core/src/num/diy_float.rs @@ -21,61 +21,29 @@ pub struct Fp { impl Fp { /// Returns a correctly rounded product of itself and `other`. - pub fn mul(&self, other: &Fp) -> Fp { - const MASK: u64 = 0xffffffff; - let a = self.f >> 32; - let b = self.f & MASK; - let c = other.f >> 32; - let d = other.f & MASK; - let ac = a * c; - let bc = b * c; - let ad = a * d; - let bd = b * d; - let tmp = (bd >> 32) + (ad & MASK) + (bc & MASK) + (1 << 31) /* round */; - let f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32); + pub fn mul(self, other: Self) -> Self { + let (lo, hi) = self.f.widening_mul(other.f); + let f = hi + (lo >> 63) /* round */; let e = self.e + other.e + 64; - Fp { f, e } + Self { f, e } } /// Normalizes itself so that the resulting mantissa is at least `2^63`. - pub fn normalize(&self) -> Fp { - let mut f = self.f; - let mut e = self.e; - if f >> (64 - 32) == 0 { - f <<= 32; - e -= 32; - } - if f >> (64 - 16) == 0 { - f <<= 16; - e -= 16; - } - if f >> (64 - 8) == 0 { - f <<= 8; - e -= 8; - } - if f >> (64 - 4) == 0 { - f <<= 4; - e -= 4; - } - if f >> (64 - 2) == 0 { - f <<= 2; - e -= 2; - } - if f >> (64 - 1) == 0 { - f <<= 1; - e -= 1; - } + pub fn normalize(self) -> Self { + let lz = self.f.leading_zeros(); + let f = self.f << lz; + let e = self.e - lz as i16; debug_assert!(f >= (1 << 63)); - Fp { f, e } + Self { f, e } } /// Normalizes itself to have the shared exponent. /// It can only decrease the exponent (and thus increase the mantissa). - pub fn normalize_to(&self, e: i16) -> Fp { + pub fn normalize_to(self, e: i16) -> Self { let edelta = self.e - e; assert!(edelta >= 0); let edelta = edelta as usize; assert_eq!(self.f << edelta >> edelta, self.f); - Fp { f: self.f << edelta, e } + Self { f: self.f << edelta, e } } } diff --git a/library/core/src/num/flt2dec/strategy/grisu.rs b/library/core/src/num/flt2dec/strategy/grisu.rs index 2816de4c633..d3bbb0934e0 100644 --- a/library/core/src/num/flt2dec/strategy/grisu.rs +++ b/library/core/src/num/flt2dec/strategy/grisu.rs @@ -196,9 +196,9 @@ pub fn format_shortest_opt<'a>( let (minusk, cached) = cached_power(ALPHA - plus.e - 64, GAMMA - plus.e - 64); // scale fps. this gives the maximal error of 1 ulp (proved from Theorem 5.1). - let plus = plus.mul(&cached); - let minus = minus.mul(&cached); - let v = v.mul(&cached); + let plus = plus.mul(cached); + let minus = minus.mul(cached); + let v = v.mul(cached); debug_assert_eq!(plus.e, minus.e); debug_assert_eq!(plus.e, v.e); @@ -480,7 +480,7 @@ pub fn format_exact_opt<'a>( // normalize and scale `v`. let v = Fp { f: d.mant, e: d.exp }.normalize(); let (minusk, cached) = cached_power(ALPHA - v.e - 64, GAMMA - v.e - 64); - let v = v.mul(&cached); + let v = v.mul(cached); // divide `v` into integral and fractional parts. let e = -v.e as usize; diff --git a/library/coretests/benches/ascii.rs b/library/coretests/benches/ascii.rs index 3fe45aa360b..64bdc7fed11 100644 --- a/library/coretests/benches/ascii.rs +++ b/library/coretests/benches/ascii.rs @@ -354,7 +354,7 @@ static ASCII_CHARACTER_CLASS: [AsciiCharacterClass; 256] = [ ]; const ASCII_PATH: &[u8] = b"home/kyubey/rust/build/x86_64-unknown-linux-gnu/stage0/lib:/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0-tools/release/deps"; -const RUST_INCANTATION: &[u8] = br#"AR_x86_64_unknown_linux_gnu="ar" CARGO_INCREMENTAL="0" CARGO_PROFILE_RELEASE_DEBUG="1" CARGO_PROFILE_RELEASE_DEBUG_ASSERTIONS="false" CARGO_PROFILE_RELEASE_OVERFLOW_CHECKS="false" CARGO_TARGET_DIR="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0-std" CC_x86_64_unknown_linux_gnu="cc" CFG_COMPILER_HOST_TRIPLE="x86_64-unknown-linux-gnu" CFG_RELEASE_CHANNEL="dev" CFLAGS_x86_64_unknown_linux_gnu="-ffunction-sections -fdata-sections -fPIC -m64" CXXFLAGS_x86_64_unknown_linux_gnu="-ffunction-sections -fdata-sections -fPIC -m64" CXX_x86_64_unknown_linux_gnu="c++" LD_LIBRARY_PATH="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0-sysroot/lib/rustlib/x86_64-unknown-linux-gnu/lib" LIBC_CHECK_CFG="1" RANLIB_x86_64_unknown_linux_gnu="ar s" REAL_LIBRARY_PATH_VAR="LD_LIBRARY_PATH" RUSTBUILD_NATIVE_DIR="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/native" RUSTC="/home/kyubey/workspace/rust/build/bootstrap/debug/rustc" RUSTC_BOOTSTRAP="1" RUSTC_BREAK_ON_ICE="1" RUSTC_ERROR_METADATA_DST="/home/kyubey/workspace/rust/build/tmp/extended-error-metadata" RUSTC_FORCE_UNSTABLE="1" RUSTC_HOST_FUSE_LD_LLD="1" RUSTC_INSTALL_BINDIR="bin" RUSTC_LIBDIR="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0/lib" RUSTC_LINT_FLAGS="-Wrust_2018_idioms -Wunused_lifetimes -Wsemicolon_in_expressions_from_macros" RUSTC_REAL="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0/bin/rustc" RUSTC_SNAPSHOT="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0/bin/rustc" RUSTC_SNAPSHOT_LIBDIR="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0/lib" RUSTC_STAGE="0" RUSTC_SYSROOT="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0-sysroot" RUSTC_VERBOSE="0" RUSTDOC="/home/kyubey/workspace/rust/build/bootstrap/debug/rustdoc" RUSTDOCFLAGS="-C target-cpu=native --cfg=bootstrap -Csymbol-mangling-version=legacy -Zunstable-options -Zunstable-options --check-cfg=values(bootstrap) --check-cfg=values(stdarch_intel_sde) --check-cfg=values(no_fp_fmt_parse) --check-cfg=values(no_global_oom_handling) --check-cfg=values(no_rc) --check-cfg=values(no_sync) --check-cfg=values(freebsd12) --check-cfg=values(freebsd13) --check-cfg=values(backtrace_in_libstd) --check-cfg=values(target_env,\"libnx\") --check-cfg=values(target_arch,\"asmjs\",\"spirv\",\"nvptx\",\"xtensa\") -Clink-arg=-fuse-ld=lld -Clink-arg=-Wl,--threads=1 -Wrustdoc::invalid_codeblock_attributes --crate-version 1.72.0-dev -Zcrate-attr=doc(html_root_url=\"https://doc.rust-lang.org/nightly/\") -Zcrate-attr=warn(rust_2018_idioms)" RUSTDOC_FUSE_LD_LLD="1" RUSTDOC_LIBDIR="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0/lib" RUSTDOC_REAL="/path/to/nowhere/rustdoc/not/required" RUSTFLAGS="-C target-cpu=native --cfg=bootstrap -Csymbol-mangling-version=legacy -Zunstable-options -Zunstable-options --check-cfg=values(bootstrap) --check-cfg=values(stdarch_intel_sde) --check-cfg=values(no_fp_fmt_parse) --check-cfg=values(no_global_oom_handling) --check-cfg=values(no_rc) --check-cfg=values(no_sync) --check-cfg=values(freebsd12) --check-cfg=values(freebsd13) --check-cfg=values(backtrace_in_libstd) --check-cfg=values(target_env,\"libnx\") --check-cfg=values(target_arch,\"asmjs\",\"spirv\",\"nvptx\",\"xtensa\") -Zmacro-backtrace -Clink-args=-Wl,-z,origin -Clink-args=-Wl,-rpath,$ORIGIN/../lib -Clink-args=-fuse-ld=lld -Csplit-debuginfo=off -Cprefer-dynamic -Zinline-mir -Clto=off -Zcrate-attr=doc(html_root_url=\"https://doc.rust-lang.org/nightly/\")" RUST_COMPILER_RT_ROOT="/home/kyubey/workspace/rust/src/llvm-project/compiler-rt" RUST_TEST_THREADS="48" WINAPI_NO_BUNDLED_LIBRARIES="1" __CARGO_DEFAULT_LIB_METADATA="bootstrapstd" "/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0/bin/cargo" "bench" "--target" "x86_64-unknown-linux-gnu" "-Zcheck-cfg=names,values,output" "-Zbinary-dep-depinfo" "-j" "48" "--features" " panic-unwind backtrace compiler-builtins-c" "--manifest-path" "/home/kyubey/workspace/rust/library/sysroot/Cargo.toml" "-p" "core" "--" "bench_ascii_escape_display" "--quiet" "-Z" "unstable-options" "--format" "json""#; +const RUST_INCANTATION: &[u8] = br#"AR_x86_64_unknown_linux_gnu="ar" CARGO_INCREMENTAL="0" CARGO_PROFILE_RELEASE_DEBUG="1" CARGO_PROFILE_RELEASE_DEBUG_ASSERTIONS="false" CARGO_PROFILE_RELEASE_OVERFLOW_CHECKS="false" CARGO_TARGET_DIR="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0-std" CC_x86_64_unknown_linux_gnu="cc" CFG_COMPILER_HOST_TRIPLE="x86_64-unknown-linux-gnu" CFG_RELEASE_CHANNEL="dev" CFLAGS_x86_64_unknown_linux_gnu="-ffunction-sections -fdata-sections -fPIC -m64" CXXFLAGS_x86_64_unknown_linux_gnu="-ffunction-sections -fdata-sections -fPIC -m64" CXX_x86_64_unknown_linux_gnu="c++" LD_LIBRARY_PATH="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0-sysroot/lib/rustlib/x86_64-unknown-linux-gnu/lib" LIBC_CHECK_CFG="1" RANLIB_x86_64_unknown_linux_gnu="ar s" REAL_LIBRARY_PATH_VAR="LD_LIBRARY_PATH" RUSTBUILD_NATIVE_DIR="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/native" RUSTC="/home/kyubey/workspace/rust/build/bootstrap/debug/rustc" RUSTC_BOOTSTRAP="1" RUSTC_BREAK_ON_ICE="1" RUSTC_ERROR_METADATA_DST="/home/kyubey/workspace/rust/build/tmp/extended-error-metadata" RUSTC_FORCE_UNSTABLE="1" RUSTC_HOST_FUSE_LD_LLD="1" RUSTC_INSTALL_BINDIR="bin" RUSTC_LIBDIR="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0/lib" RUSTC_LINT_FLAGS="-Wrust_2018_idioms -Wunused_lifetimes -Wsemicolon_in_expressions_from_macros" RUSTC_REAL="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0/bin/rustc" RUSTC_SNAPSHOT="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0/bin/rustc" RUSTC_SNAPSHOT_LIBDIR="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0/lib" RUSTC_STAGE="0" RUSTC_SYSROOT="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0-sysroot" RUSTC_VERBOSE="0" RUSTDOC="/home/kyubey/workspace/rust/build/bootstrap/debug/rustdoc" RUSTDOCFLAGS="-C target-cpu=native --cfg=bootstrap -Csymbol-mangling-version=legacy -Zunstable-options -Zunstable-options --check-cfg=values(bootstrap) --check-cfg=values(no_fp_fmt_parse) --check-cfg=values(no_global_oom_handling) --check-cfg=values(no_rc) --check-cfg=values(no_sync) --check-cfg=values(freebsd12) --check-cfg=values(freebsd13) --check-cfg=values(backtrace_in_libstd) --check-cfg=values(target_env,\"libnx\") --check-cfg=values(target_arch,\"asmjs\",\"spirv\",\"nvptx\",\"xtensa\") -Clink-arg=-fuse-ld=lld -Clink-arg=-Wl,--threads=1 -Wrustdoc::invalid_codeblock_attributes --crate-version 1.72.0-dev -Zcrate-attr=doc(html_root_url=\"https://doc.rust-lang.org/nightly/\") -Zcrate-attr=warn(rust_2018_idioms)" RUSTDOC_FUSE_LD_LLD="1" RUSTDOC_LIBDIR="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0/lib" RUSTDOC_REAL="/path/to/nowhere/rustdoc/not/required" RUSTFLAGS="-C target-cpu=native --cfg=bootstrap -Csymbol-mangling-version=legacy -Zunstable-options -Zunstable-options --check-cfg=values(bootstrap) --check-cfg=values(no_fp_fmt_parse) --check-cfg=values(no_global_oom_handling) --check-cfg=values(no_rc) --check-cfg=values(no_sync) --check-cfg=values(freebsd12) --check-cfg=values(freebsd13) --check-cfg=values(backtrace_in_libstd) --check-cfg=values(target_env,\"libnx\") --check-cfg=values(target_arch,\"asmjs\",\"spirv\",\"nvptx\",\"xtensa\") -Zmacro-backtrace -Clink-args=-Wl,-z,origin -Clink-args=-Wl,-rpath,$ORIGIN/../lib -Clink-args=-fuse-ld=lld -Csplit-debuginfo=off -Cprefer-dynamic -Zinline-mir -Clto=off -Zcrate-attr=doc(html_root_url=\"https://doc.rust-lang.org/nightly/\")" RUST_COMPILER_RT_ROOT="/home/kyubey/workspace/rust/src/llvm-project/compiler-rt" RUST_TEST_THREADS="48" WINAPI_NO_BUNDLED_LIBRARIES="1" __CARGO_DEFAULT_LIB_METADATA="bootstrapstd" "/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0/bin/cargo" "bench" "--target" "x86_64-unknown-linux-gnu" "-Zcheck-cfg=names,values,output" "-Zbinary-dep-depinfo" "-j" "48" "--features" " panic-unwind backtrace compiler-builtins-c" "--manifest-path" "/home/kyubey/workspace/rust/library/sysroot/Cargo.toml" "-p" "core" "--" "bench_ascii_escape_display" "--quiet" "-Z" "unstable-options" "--format" "json""#; #[bench] fn bench_ascii_escape_display_no_escape(b: &mut Bencher) { diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index ef548971aaf..f52e338a085 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -68,7 +68,6 @@ #![feature(pointer_is_aligned_to)] #![feature(portable_simd)] #![feature(ptr_metadata)] -#![feature(select_unpredictable)] #![feature(slice_from_ptr_range)] #![feature(slice_internals)] #![feature(slice_partition_dedup)] diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 940b671c514..d7bd28b5279 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -121,7 +121,6 @@ debug_typeid = ["core/debug_typeid"] # https://github.com/rust-lang/stdarch/blob/master/crates/std_detect/Cargo.toml std_detect_file_io = ["std_detect/std_detect_file_io"] std_detect_dlsym_getauxval = ["std_detect/std_detect_dlsym_getauxval"] -std_detect_env_override = ["std_detect/std_detect_env_override"] # Enable using raw-dylib for Windows imports. # This will eventually be the default. diff --git a/library/std/src/env.rs b/library/std/src/env.rs index 1593969e114..ce2dc795220 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -12,9 +12,11 @@ use crate::error::Error; use crate::ffi::{OsStr, OsString}; +use crate::num::NonZero; +use crate::ops::Try; use crate::path::{Path, PathBuf}; use crate::sys::{env as env_imp, os as os_imp}; -use crate::{fmt, io, sys}; +use crate::{array, fmt, io, sys}; /// Returns the current working directory as a [`PathBuf`]. /// @@ -872,19 +874,36 @@ impl !Sync for Args {} #[stable(feature = "env", since = "1.0.0")] impl Iterator for Args { type Item = String; + fn next(&mut self) -> Option<String> { self.inner.next().map(|s| s.into_string().unwrap()) } + + #[inline] fn size_hint(&self) -> (usize, Option<usize>) { self.inner.size_hint() } + + // Methods which skip args cannot simply delegate to the inner iterator, + // because `env::args` states that we will "panic during iteration if any + // argument to the process is not valid Unicode". + // + // This offers two possible interpretations: + // - a skipped argument is never encountered "during iteration" + // - even a skipped argument is encountered "during iteration" + // + // As a panic can be observed, we err towards validating even skipped + // arguments for now, though this is not explicitly promised by the API. } #[stable(feature = "env", since = "1.0.0")] impl ExactSizeIterator for Args { + #[inline] fn len(&self) -> usize { self.inner.len() } + + #[inline] fn is_empty(&self) -> bool { self.inner.is_empty() } @@ -914,19 +933,65 @@ impl !Sync for ArgsOs {} #[stable(feature = "env", since = "1.0.0")] impl Iterator for ArgsOs { type Item = OsString; + + #[inline] fn next(&mut self) -> Option<OsString> { self.inner.next() } + + #[inline] + fn next_chunk<const N: usize>( + &mut self, + ) -> Result<[OsString; N], array::IntoIter<OsString, N>> { + self.inner.next_chunk() + } + + #[inline] fn size_hint(&self) -> (usize, Option<usize>) { self.inner.size_hint() } + + #[inline] + fn count(self) -> usize { + self.inner.len() + } + + #[inline] + fn last(self) -> Option<OsString> { + self.inner.last() + } + + #[inline] + fn advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>> { + self.inner.advance_by(n) + } + + #[inline] + fn try_fold<B, F, R>(&mut self, init: B, f: F) -> R + where + F: FnMut(B, Self::Item) -> R, + R: Try<Output = B>, + { + self.inner.try_fold(init, f) + } + + #[inline] + fn fold<B, F>(self, init: B, f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.inner.fold(init, f) + } } #[stable(feature = "env", since = "1.0.0")] impl ExactSizeIterator for ArgsOs { + #[inline] fn len(&self) -> usize { self.inner.len() } + + #[inline] fn is_empty(&self) -> bool { self.inner.is_empty() } @@ -934,9 +999,15 @@ impl ExactSizeIterator for ArgsOs { #[stable(feature = "env_iterators", since = "1.12.0")] impl DoubleEndedIterator for ArgsOs { + #[inline] fn next_back(&mut self) -> Option<OsString> { self.inner.next_back() } + + #[inline] + fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero<usize>> { + self.inner.advance_back_by(n) + } } #[stable(feature = "std_debug", since = "1.16.0")] diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index ce01175309a..72bdf03ee61 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -582,15 +582,25 @@ impl OsString { #[unstable(feature = "os_string_truncate", issue = "133262")] pub fn truncate(&mut self, len: usize) { self.as_os_str().inner.check_public_boundary(len); - self.inner.truncate(len); + // SAFETY: The length was just checked to be at a valid boundary. + unsafe { self.inner.truncate_unchecked(len) }; } - /// Provides plumbing to core `Vec::extend_from_slice`. - /// More well behaving alternative to allowing outer types - /// full mutable access to the core `Vec`. + /// Provides plumbing to `Vec::extend_from_slice` without giving full + /// mutable access to the `Vec`. + /// + /// # Safety + /// + /// The slice must be valid for the platform encoding (as described in + /// [`OsStr::from_encoded_bytes_unchecked`]). + /// + /// This bypasses the encoding-dependent surrogate joining, so `self` must + /// not end with a leading surrogate half and `other` must not start with + /// with a trailing surrogate half. #[inline] - pub(crate) fn extend_from_slice(&mut self, other: &[u8]) { - self.inner.extend_from_slice(other); + pub(crate) unsafe fn extend_from_slice_unchecked(&mut self, other: &[u8]) { + // SAFETY: Guaranteed by caller. + unsafe { self.inner.extend_from_slice_unchecked(other) }; } } diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 3340a5dc23d..3ff08e3a566 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -21,7 +21,6 @@ mod tests; 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; @@ -29,6 +28,7 @@ use crate::sync::Arc; use crate::sys::fs as fs_imp; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; use crate::time::SystemTime; +use crate::{error, fmt}; /// An object providing access to an open file on the filesystem. /// @@ -116,6 +116,22 @@ pub struct File { inner: fs_imp::File, } +/// An enumeration of possible errors which can occur while trying to acquire a lock +/// from the [`try_lock`] method and [`try_lock_shared`] method on a [`File`]. +/// +/// [`try_lock`]: File::try_lock +/// [`try_lock_shared`]: File::try_lock_shared +#[unstable(feature = "file_lock", issue = "130994")] +pub enum TryLockError { + /// The lock could not be acquired due to an I/O error on the file. The standard library will + /// not return an [`ErrorKind::WouldBlock`] error inside [`TryLockError::Error`] + /// + /// [`ErrorKind::WouldBlock`]: io::ErrorKind::WouldBlock + Error(io::Error), + /// The lock could not be acquired at this time because it is held by another handle/process. + WouldBlock, +} + /// Metadata information about a file. /// /// This structure is returned from the [`metadata`] or @@ -352,6 +368,30 @@ pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result inner(path.as_ref(), contents.as_ref()) } +#[unstable(feature = "file_lock", issue = "130994")] +impl error::Error for TryLockError {} + +#[unstable(feature = "file_lock", issue = "130994")] +impl fmt::Debug for TryLockError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + TryLockError::Error(err) => err.fmt(f), + TryLockError::WouldBlock => "WouldBlock".fmt(f), + } + } +} + +#[unstable(feature = "file_lock", issue = "130994")] +impl fmt::Display for TryLockError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + TryLockError::Error(_) => "lock acquisition failed due to I/O error", + TryLockError::WouldBlock => "lock acquisition failed because the operation would block", + } + .fmt(f) + } +} + impl File { /// Attempts to open a file in read-only mode. /// @@ -734,8 +774,8 @@ impl File { /// Try to acquire an exclusive lock on the file. /// - /// Returns `Ok(false)` if a different lock is already held on this file (via another - /// handle/descriptor). + /// Returns `Err(TryLockError::WouldBlock)` if a different lock is already held on this file + /// (via another handle/descriptor). /// /// This acquires an exclusive lock; no other file handle to this file may acquire another lock. /// @@ -777,23 +817,27 @@ impl File { /// /// ```no_run /// #![feature(file_lock)] - /// use std::fs::File; + /// use std::fs::{File, TryLockError}; /// /// fn main() -> std::io::Result<()> { /// let f = File::create("foo.txt")?; - /// f.try_lock()?; + /// match f.try_lock() { + /// Ok(_) => (), + /// Err(TryLockError::WouldBlock) => (), // Lock not acquired + /// Err(TryLockError::Error(err)) => return Err(err), + /// } /// Ok(()) /// } /// ``` #[unstable(feature = "file_lock", issue = "130994")] - pub fn try_lock(&self) -> io::Result<bool> { + pub fn try_lock(&self) -> Result<(), TryLockError> { self.inner.try_lock() } /// Try to acquire a shared (non-exclusive) lock on the file. /// - /// Returns `Ok(false)` if an exclusive lock is already held on this file (via another - /// handle/descriptor). + /// Returns `Err(TryLockError::WouldBlock)` if a different lock is already held on this file + /// (via another handle/descriptor). /// /// This acquires a shared lock; more than one file handle may hold a shared lock, but none may /// hold an exclusive lock at the same time. @@ -834,16 +878,21 @@ impl File { /// /// ```no_run /// #![feature(file_lock)] - /// use std::fs::File; + /// use std::fs::{File, TryLockError}; /// /// fn main() -> std::io::Result<()> { /// let f = File::open("foo.txt")?; - /// f.try_lock_shared()?; + /// match f.try_lock_shared() { + /// Ok(_) => (), + /// Err(TryLockError::WouldBlock) => (), // Lock not acquired + /// Err(TryLockError::Error(err)) => return Err(err), + /// } + /// /// Ok(()) /// } /// ``` #[unstable(feature = "file_lock", issue = "130994")] - pub fn try_lock_shared(&self) -> io::Result<bool> { + pub fn try_lock_shared(&self) -> Result<(), TryLockError> { self.inner.try_lock_shared() } @@ -2874,6 +2923,8 @@ pub fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> { /// /// Consider ignoring the error if validating the removal is not required for your use case. /// +/// This function may return [`io::ErrorKind::DirectoryNotEmpty`] if the directory is concurrently +/// written into, which typically indicates some contents were removed but not all. /// [`io::ErrorKind::NotFound`] is only returned if no removal occurs. /// /// [`fs::remove_file`]: remove_file diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 4712e58980c..46b0d832fec 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -1,6 +1,22 @@ use rand::RngCore; +#[cfg(any( + windows, + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_vendor = "apple", +))] +use crate::assert_matches::assert_matches; use crate::char::MAX_LEN_UTF8; +#[cfg(any( + windows, + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_vendor = "apple", +))] +use crate::fs::TryLockError; use crate::fs::{self, File, FileTimes, OpenOptions}; use crate::io::prelude::*; use crate::io::{BorrowedBuf, ErrorKind, SeekFrom}; @@ -223,8 +239,8 @@ fn file_lock_multiple_shared() { check!(f2.lock_shared()); check!(f1.unlock()); check!(f2.unlock()); - assert!(check!(f1.try_lock_shared())); - assert!(check!(f2.try_lock_shared())); + check!(f1.try_lock_shared()); + check!(f2.try_lock_shared()); } #[test] @@ -243,12 +259,12 @@ fn file_lock_blocking() { // Check that shared locks block exclusive locks check!(f1.lock_shared()); - assert!(!check!(f2.try_lock())); + assert_matches!(f2.try_lock(), Err(TryLockError::WouldBlock)); check!(f1.unlock()); // Check that exclusive locks block shared locks check!(f1.lock()); - assert!(!check!(f2.try_lock_shared())); + assert_matches!(f2.try_lock_shared(), Err(TryLockError::WouldBlock)); } #[test] @@ -267,9 +283,9 @@ fn file_lock_drop() { // Check that locks are released when the File is dropped check!(f1.lock_shared()); - assert!(!check!(f2.try_lock())); + assert_matches!(f2.try_lock(), Err(TryLockError::WouldBlock)); drop(f1); - assert!(check!(f2.try_lock())); + check!(f2.try_lock()); } #[test] @@ -288,10 +304,10 @@ fn file_lock_dup() { // Check that locks are not dropped if the File has been cloned check!(f1.lock_shared()); - assert!(!check!(f2.try_lock())); + assert_matches!(f2.try_lock(), Err(TryLockError::WouldBlock)); let cloned = check!(f1.try_clone()); drop(f1); - assert!(!check!(f2.try_lock())); + assert_matches!(f2.try_lock(), Err(TryLockError::WouldBlock)); drop(cloned) } @@ -307,9 +323,9 @@ fn file_lock_double_unlock() { // Check that both are released by unlock() check!(f1.lock()); check!(f1.lock_shared()); - assert!(!check!(f2.try_lock())); + assert_matches!(f2.try_lock(), Err(TryLockError::WouldBlock)); check!(f1.unlock()); - assert!(check!(f2.try_lock())); + check!(f2.try_lock()); } #[test] diff --git a/library/std/src/io/pipe.rs b/library/std/src/io/pipe.rs index c6b7b49a351..47243806cd2 100644 --- a/library/std/src/io/pipe.rs +++ b/library/std/src/io/pipe.rs @@ -2,7 +2,7 @@ use crate::io; use crate::sys::anonymous_pipe::{AnonPipe, pipe as pipe_inner}; use crate::sys_common::{FromInner, IntoInner}; -/// Create an anonymous pipe. +/// Creates an anonymous pipe. /// /// # Behavior /// @@ -108,7 +108,7 @@ impl IntoInner<AnonPipe> for PipeWriter { } impl PipeReader { - /// Create a new [`PipeReader`] instance that shares the same underlying file description. + /// Creates a new [`PipeReader`] instance that shares the same underlying file description. /// /// # Examples /// @@ -167,7 +167,7 @@ impl PipeReader { } impl PipeWriter { - /// Create a new [`PipeWriter`] instance that shares the same underlying file description. + /// Creates a new [`PipeWriter`] instance that shares the same underlying file description. /// /// # Examples /// diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index c9595b051e2..79b25040ef6 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -91,7 +91,7 @@ mod as_keyword {} /// /// When associated with `loop`, a break expression may be used to return a value from that loop. /// This is only valid with `loop` and not with any other type of loop. -/// If no value is specified, `break;` returns `()`. +/// If no value is specified for `break;` it returns `()`. /// Every `break` within a loop must return the same type. /// /// ```rust @@ -109,6 +109,33 @@ mod as_keyword {} /// println!("{result}"); /// ``` /// +/// It is also possible to exit from any *labelled* block returning the value early. +/// If no value is specified for `break;` it returns `()`. +/// +/// ```rust +/// let inputs = vec!["Cow", "Cat", "Dog", "Snake", "Cod"]; +/// +/// let mut results = vec![]; +/// for input in inputs { +/// let result = 'filter: { +/// if input.len() > 3 { +/// break 'filter Err("Too long"); +/// }; +/// +/// if !input.contains("C") { +/// break 'filter Err("No Cs"); +/// }; +/// +/// Ok(input.to_uppercase()) +/// }; +/// +/// results.push(result); +/// } +/// +/// // [Ok("COW"), Ok("CAT"), Err("No Cs"), Err("Too long"), Ok("COD")] +/// println!("{:?}", results) +/// ``` +/// /// For more details consult the [Reference on "break expression"] and the [Reference on "break and /// loop values"]. /// @@ -1206,6 +1233,28 @@ mod ref_keyword {} /// Ok(()) /// } /// ``` +/// +/// Within [closures] and [`async`] blocks, `return` returns a value from within the closure or +/// `async` block, not from the parent function: +/// +/// ```rust +/// fn foo() -> i32 { +/// let closure = || { +/// return 5; +/// }; +/// +/// let future = async { +/// return 10; +/// }; +/// +/// return 15; +/// } +/// +/// assert_eq!(foo(), 15); +/// ``` +/// +/// [closures]: ../book/ch13-01-closures.html +/// [`async`]: ../std/keyword.async.html mod return_keyword {} #[doc(keyword = "self")] @@ -2399,6 +2448,39 @@ mod while_keyword {} /// /// We have written an [async book] detailing `async`/`await` and trade-offs compared to using threads. /// +/// ## Control Flow +/// [`return`] statements and [`?`][try operator] operators within `async` blocks do not cause +/// a return from the parent function; rather, they cause the `Future` returned by the block to +/// return with that value. +/// +/// For example, the following Rust function will return `5`, causing `x` to take the [`!` type][never type]: +/// ```rust +/// #[expect(unused_variables)] +/// fn example() -> i32 { +/// let x = { +/// return 5; +/// }; +/// } +/// ``` +/// In contrast, the following asynchronous function assigns a `Future<Output = i32>` to `x`, and +/// only returns `5` when `x` is `.await`ed: +/// ```rust +/// async fn example() -> i32 { +/// let x = async { +/// return 5; +/// }; +/// +/// x.await +/// } +/// ``` +/// Code using `?` behaves similarly - it causes the `async` block to return a [`Result`] without +/// affecting the parent function. +/// +/// Note that you cannot use `break` or `continue` from within an `async` block to affect the +/// control flow of a loop in the parent function. +/// +/// Control flow in `async` blocks is documented further in the [async book][async book blocks]. +/// /// ## Editions /// /// `async` is a keyword from the 2018 edition onwards. @@ -2408,6 +2490,11 @@ mod while_keyword {} /// [`Future`]: future::Future /// [`.await`]: ../std/keyword.await.html /// [async book]: https://rust-lang.github.io/async-book/ +/// [`return`]: ../std/keyword.return.html +/// [try operator]: ../reference/expressions/operator-expr.html#r-expr.try +/// [never type]: ../reference/types/never.html +/// [`Result`]: result::Result +/// [async book blocks]: https://rust-lang.github.io/async-book/part-guide/more-async-await.html#async-blocks mod async_keyword {} #[doc(keyword = "await")] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index ba57ad9bae3..c011f9661ae 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -301,6 +301,8 @@ #![feature(formatting_options)] #![feature(if_let_guard)] #![feature(intra_doc_pointers)] +#![feature(iter_advance_by)] +#![feature(iter_next_chunk)] #![feature(lang_items)] #![feature(let_chains)] #![feature(link_cfg)] @@ -321,6 +323,7 @@ #![feature(strict_provenance_lints)] #![feature(thread_local)] #![feature(try_blocks)] +#![feature(try_trait_v2)] #![feature(type_alias_impl_trait)] // tidy-alphabetical-end // diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 7cd20c48d89..1a4a7aa7448 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -1529,11 +1529,13 @@ impl PathBuf { self.inner.truncate(end_file_stem.wrapping_sub(start)); // add the new extension, if any - let new = extension; + let new = extension.as_encoded_bytes(); if !new.is_empty() { self.inner.reserve_exact(new.len() + 1); - self.inner.push(OsStr::new(".")); - self.inner.push(new); + self.inner.push("."); + // SAFETY: Since a UTF-8 string was just pushed, it is not possible + // for the buffer to end with a surrogate half. + unsafe { self.inner.extend_from_slice_unchecked(new) }; } true @@ -1597,7 +1599,7 @@ impl PathBuf { Some(f) => f.as_encoded_bytes(), }; - let new = extension; + let new = extension.as_encoded_bytes(); if !new.is_empty() { // truncate until right after the file name // this is necessary for trimming the trailing slash @@ -1607,8 +1609,10 @@ impl PathBuf { // append the new extension self.inner.reserve_exact(new.len() + 1); - self.inner.push(OsStr::new(".")); - self.inner.push(new); + self.inner.push("."); + // SAFETY: Since a UTF-8 string was just pushed, it is not possible + // for the buffer to end with a surrogate half. + unsafe { self.inner.extend_from_slice_unchecked(new) }; } true @@ -2769,7 +2773,8 @@ impl Path { }; let mut new_path = PathBuf::with_capacity(new_capacity); - new_path.inner.extend_from_slice(slice_to_copy); + // SAFETY: The path is empty, so cannot have surrogate halves. + unsafe { new_path.inner.extend_from_slice_unchecked(slice_to_copy) }; new_path.set_extension(extension); new_path } diff --git a/library/std/src/sync/poison/mutex.rs b/library/std/src/sync/poison/mutex.rs index adb74bb6f3d..1c29c619edc 100644 --- a/library/std/src/sync/poison/mutex.rs +++ b/library/std/src/sync/poison/mutex.rs @@ -253,11 +253,11 @@ unsafe impl<T: ?Sized + Sync> Sync for MutexGuard<'_, T> {} /// The data protected by the mutex can be accessed through this guard via its /// [`Deref`] and [`DerefMut`] implementations. /// -/// This structure is created by the [`map`] and [`try_map`] methods on +/// This structure is created by the [`map`] and [`filter_map`] methods on /// [`MutexGuard`]. /// /// [`map`]: MutexGuard::map -/// [`try_map`]: MutexGuard::try_map +/// [`filter_map`]: MutexGuard::filter_map /// [`Condvar`]: crate::sync::Condvar #[must_use = "if unused the Mutex will immediately unlock"] #[must_not_suspend = "holding a MappedMutexGuard across suspend \ @@ -718,7 +718,7 @@ impl<'a, T: ?Sized> MutexGuard<'a, T> { U: ?Sized, { // SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. // The signature of the closure guarantees that it will not "leak" the lifetime of the reference // passed to it. If the closure panics, the guard will be dropped. let data = NonNull::from(f(unsafe { &mut *orig.lock.data.get() })); @@ -739,17 +739,16 @@ impl<'a, T: ?Sized> MutexGuard<'a, T> { /// The `Mutex` is already locked, so this cannot fail. /// /// This is an associated function that needs to be used as - /// `MutexGuard::try_map(...)`. A method would interfere with methods of the + /// `MutexGuard::filter_map(...)`. A method would interfere with methods of the /// same name on the contents of the `MutexGuard` used through `Deref`. - #[doc(alias = "filter_map")] #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn try_map<U, F>(orig: Self, f: F) -> Result<MappedMutexGuard<'a, U>, Self> + pub fn filter_map<U, F>(orig: Self, f: F) -> Result<MappedMutexGuard<'a, U>, Self> where F: FnOnce(&mut T) -> Option<&mut U>, U: ?Sized, { // SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. // The signature of the closure guarantees that it will not "leak" the lifetime of the reference // passed to it. If the closure panics, the guard will be dropped. match f(unsafe { &mut *orig.lock.data.get() }) { @@ -826,7 +825,7 @@ impl<'a, T: ?Sized> MappedMutexGuard<'a, T> { U: ?Sized, { // SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. // The signature of the closure guarantees that it will not "leak" the lifetime of the reference // passed to it. If the closure panics, the guard will be dropped. let data = NonNull::from(f(unsafe { orig.data.as_mut() })); @@ -847,17 +846,16 @@ impl<'a, T: ?Sized> MappedMutexGuard<'a, T> { /// The `Mutex` is already locked, so this cannot fail. /// /// This is an associated function that needs to be used as - /// `MappedMutexGuard::try_map(...)`. A method would interfere with methods of the + /// `MappedMutexGuard::filter_map(...)`. A method would interfere with methods of the /// same name on the contents of the `MutexGuard` used through `Deref`. - #[doc(alias = "filter_map")] #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn try_map<U, F>(mut orig: Self, f: F) -> Result<MappedMutexGuard<'a, U>, Self> + pub fn filter_map<U, F>(mut orig: Self, f: F) -> Result<MappedMutexGuard<'a, U>, Self> where F: FnOnce(&mut T) -> Option<&mut U>, U: ?Sized, { // SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. // The signature of the closure guarantees that it will not "leak" the lifetime of the reference // passed to it. If the closure panics, the guard will be dropped. match f(unsafe { orig.data.as_mut() }) { diff --git a/library/std/src/sync/poison/rwlock.rs b/library/std/src/sync/poison/rwlock.rs index a2abd4f692e..6976c0a64e2 100644 --- a/library/std/src/sync/poison/rwlock.rs +++ b/library/std/src/sync/poison/rwlock.rs @@ -147,11 +147,11 @@ unsafe impl<T: ?Sized + Sync> Sync for RwLockWriteGuard<'_, T> {} /// RAII structure used to release the shared read access of a lock when /// dropped, which can point to a subfield of the protected data. /// -/// This structure is created by the [`map`] and [`try_map`] methods +/// This structure is created by the [`map`] and [`filter_map`] methods /// on [`RwLockReadGuard`]. /// /// [`map`]: RwLockReadGuard::map -/// [`try_map`]: RwLockReadGuard::try_map +/// [`filter_map`]: RwLockReadGuard::filter_map #[must_use = "if unused the RwLock will immediately unlock"] #[must_not_suspend = "holding a MappedRwLockReadGuard across suspend \ points can cause deadlocks, delays, \ @@ -176,11 +176,11 @@ unsafe impl<T: ?Sized + Sync> Sync for MappedRwLockReadGuard<'_, T> {} /// RAII structure used to release the exclusive write access of a lock when /// dropped, which can point to a subfield of the protected data. /// -/// This structure is created by the [`map`] and [`try_map`] methods +/// This structure is created by the [`map`] and [`filter_map`] methods /// on [`RwLockWriteGuard`]. /// /// [`map`]: RwLockWriteGuard::map -/// [`try_map`]: RwLockWriteGuard::try_map +/// [`filter_map`]: RwLockWriteGuard::filter_map #[must_use = "if unused the RwLock will immediately unlock"] #[must_not_suspend = "holding a MappedRwLockWriteGuard across suspend \ points can cause deadlocks, delays, \ @@ -788,7 +788,7 @@ impl<T: ?Sized> Deref for MappedRwLockReadGuard<'_, T> { fn deref(&self) -> &T { // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. unsafe { self.data.as_ref() } } } @@ -799,7 +799,7 @@ impl<T: ?Sized> Deref for MappedRwLockWriteGuard<'_, T> { fn deref(&self) -> &T { // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. unsafe { self.data.as_ref() } } } @@ -808,7 +808,7 @@ impl<T: ?Sized> Deref for MappedRwLockWriteGuard<'_, T> { impl<T: ?Sized> DerefMut for MappedRwLockWriteGuard<'_, T> { fn deref_mut(&mut self) -> &mut T { // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. unsafe { self.data.as_mut() } } } @@ -838,7 +838,7 @@ impl<T: ?Sized> Drop for RwLockWriteGuard<'_, T> { impl<T: ?Sized> Drop for MappedRwLockReadGuard<'_, T> { fn drop(&mut self) { // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. unsafe { self.inner_lock.read_unlock(); } @@ -850,7 +850,7 @@ impl<T: ?Sized> Drop for MappedRwLockWriteGuard<'_, T> { fn drop(&mut self) { self.poison_flag.done(&self.poison); // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. unsafe { self.inner_lock.write_unlock(); } @@ -878,7 +878,7 @@ impl<'a, T: ?Sized> RwLockReadGuard<'a, T> { U: ?Sized, { // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. // The signature of the closure guarantees that it will not "leak" the lifetime of the reference // passed to it. If the closure panics, the guard will be dropped. let data = NonNull::from(f(unsafe { orig.data.as_ref() })); @@ -893,22 +893,21 @@ impl<'a, T: ?Sized> RwLockReadGuard<'a, T> { /// The `RwLock` is already locked for reading, so this cannot fail. /// /// This is an associated function that needs to be used as - /// `RwLockReadGuard::try_map(...)`. A method would interfere with methods + /// `RwLockReadGuard::filter_map(...)`. A method would interfere with methods /// of the same name on the contents of the `RwLockReadGuard` used through /// `Deref`. /// /// # Panics /// /// If the closure panics, the guard will be dropped (unlocked) and the RwLock will not be poisoned. - #[doc(alias = "filter_map")] #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn try_map<U, F>(orig: Self, f: F) -> Result<MappedRwLockReadGuard<'a, U>, Self> + pub fn filter_map<U, F>(orig: Self, f: F) -> Result<MappedRwLockReadGuard<'a, U>, Self> where F: FnOnce(&T) -> Option<&U>, U: ?Sized, { // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. // The signature of the closure guarantees that it will not "leak" the lifetime of the reference // passed to it. If the closure panics, the guard will be dropped. match f(unsafe { orig.data.as_ref() }) { @@ -943,7 +942,7 @@ impl<'a, T: ?Sized> MappedRwLockReadGuard<'a, T> { U: ?Sized, { // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. // The signature of the closure guarantees that it will not "leak" the lifetime of the reference // passed to it. If the closure panics, the guard will be dropped. let data = NonNull::from(f(unsafe { orig.data.as_ref() })); @@ -958,22 +957,21 @@ impl<'a, T: ?Sized> MappedRwLockReadGuard<'a, T> { /// The `RwLock` is already locked for reading, so this cannot fail. /// /// This is an associated function that needs to be used as - /// `MappedRwLockReadGuard::try_map(...)`. A method would interfere with + /// `MappedRwLockReadGuard::filter_map(...)`. A method would interfere with /// methods of the same name on the contents of the `MappedRwLockReadGuard` /// used through `Deref`. /// /// # Panics /// /// If the closure panics, the guard will be dropped (unlocked) and the RwLock will not be poisoned. - #[doc(alias = "filter_map")] #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn try_map<U, F>(orig: Self, f: F) -> Result<MappedRwLockReadGuard<'a, U>, Self> + pub fn filter_map<U, F>(orig: Self, f: F) -> Result<MappedRwLockReadGuard<'a, U>, Self> where F: FnOnce(&T) -> Option<&U>, U: ?Sized, { // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. // The signature of the closure guarantees that it will not "leak" the lifetime of the reference // passed to it. If the closure panics, the guard will be dropped. match f(unsafe { orig.data.as_ref() }) { @@ -1008,7 +1006,7 @@ impl<'a, T: ?Sized> RwLockWriteGuard<'a, T> { U: ?Sized, { // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. // The signature of the closure guarantees that it will not "leak" the lifetime of the reference // passed to it. If the closure panics, the guard will be dropped. let data = NonNull::from(f(unsafe { &mut *orig.lock.data.get() })); @@ -1029,22 +1027,21 @@ impl<'a, T: ?Sized> RwLockWriteGuard<'a, T> { /// The `RwLock` is already locked for writing, so this cannot fail. /// /// This is an associated function that needs to be used as - /// `RwLockWriteGuard::try_map(...)`. A method would interfere with methods + /// `RwLockWriteGuard::filter_map(...)`. A method would interfere with methods /// of the same name on the contents of the `RwLockWriteGuard` used through /// `Deref`. /// /// # Panics /// /// If the closure panics, the guard will be dropped (unlocked) and the RwLock will be poisoned. - #[doc(alias = "filter_map")] #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn try_map<U, F>(orig: Self, f: F) -> Result<MappedRwLockWriteGuard<'a, U>, Self> + pub fn filter_map<U, F>(orig: Self, f: F) -> Result<MappedRwLockWriteGuard<'a, U>, Self> where F: FnOnce(&mut T) -> Option<&mut U>, U: ?Sized, { // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. // The signature of the closure guarantees that it will not "leak" the lifetime of the reference // passed to it. If the closure panics, the guard will be dropped. match f(unsafe { &mut *orig.lock.data.get() }) { @@ -1147,7 +1144,7 @@ impl<'a, T: ?Sized> MappedRwLockWriteGuard<'a, T> { U: ?Sized, { // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. // The signature of the closure guarantees that it will not "leak" the lifetime of the reference // passed to it. If the closure panics, the guard will be dropped. let data = NonNull::from(f(unsafe { orig.data.as_mut() })); @@ -1168,22 +1165,21 @@ impl<'a, T: ?Sized> MappedRwLockWriteGuard<'a, T> { /// The `RwLock` is already locked for writing, so this cannot fail. /// /// This is an associated function that needs to be used as - /// `MappedRwLockWriteGuard::try_map(...)`. A method would interfere with + /// `MappedRwLockWriteGuard::filter_map(...)`. A method would interfere with /// methods of the same name on the contents of the `MappedRwLockWriteGuard` /// used through `Deref`. /// /// # Panics /// /// If the closure panics, the guard will be dropped (unlocked) and the RwLock will be poisoned. - #[doc(alias = "filter_map")] #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn try_map<U, F>(mut orig: Self, f: F) -> Result<MappedRwLockWriteGuard<'a, U>, Self> + pub fn filter_map<U, F>(mut orig: Self, f: F) -> Result<MappedRwLockWriteGuard<'a, U>, Self> where F: FnOnce(&mut T) -> Option<&mut U>, U: ?Sized, { // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. // The signature of the closure guarantees that it will not "leak" the lifetime of the reference // passed to it. If the closure panics, the guard will be dropped. match f(unsafe { orig.data.as_mut() }) { diff --git a/library/std/src/sys/args/common.rs b/library/std/src/sys/args/common.rs index 43ac5e95923..303b373ccf9 100644 --- a/library/std/src/sys/args/common.rs +++ b/library/std/src/sys/args/common.rs @@ -1,5 +1,7 @@ use crate::ffi::OsString; -use crate::{fmt, vec}; +use crate::num::NonZero; +use crate::ops::Try; +use crate::{array, fmt, vec}; pub struct Args { iter: vec::IntoIter<OsString>, @@ -9,6 +11,7 @@ impl !Send for Args {} impl !Sync for Args {} impl Args { + #[inline] pub(super) fn new(args: Vec<OsString>) -> Self { Args { iter: args.into_iter() } } @@ -22,22 +25,77 @@ impl fmt::Debug for Args { impl Iterator for Args { type Item = OsString; + + #[inline] fn next(&mut self) -> Option<OsString> { self.iter.next() } + + #[inline] + fn next_chunk<const N: usize>( + &mut self, + ) -> Result<[OsString; N], array::IntoIter<OsString, N>> { + self.iter.next_chunk() + } + + #[inline] fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() } -} -impl ExactSizeIterator for Args { - fn len(&self) -> usize { + #[inline] + fn count(self) -> usize { self.iter.len() } + + #[inline] + fn last(mut self) -> Option<OsString> { + self.iter.next_back() + } + + #[inline] + fn advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>> { + self.iter.advance_by(n) + } + + #[inline] + fn try_fold<B, F, R>(&mut self, init: B, f: F) -> R + where + F: FnMut(B, Self::Item) -> R, + R: Try<Output = B>, + { + self.iter.try_fold(init, f) + } + + #[inline] + fn fold<B, F>(self, init: B, f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.iter.fold(init, f) + } } impl DoubleEndedIterator for Args { + #[inline] fn next_back(&mut self) -> Option<OsString> { self.iter.next_back() } + + #[inline] + fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero<usize>> { + self.iter.advance_back_by(n) + } +} + +impl ExactSizeIterator for Args { + #[inline] + fn len(&self) -> usize { + self.iter.len() + } + + #[inline] + fn is_empty(&self) -> bool { + self.iter.is_empty() + } } diff --git a/library/std/src/sys/args/sgx.rs b/library/std/src/sys/args/sgx.rs index 2a4bc76aefb..f800500c22a 100644 --- a/library/std/src/sys/args/sgx.rs +++ b/library/std/src/sys/args/sgx.rs @@ -1,6 +1,8 @@ #![allow(fuzzy_provenance_casts)] // FIXME: this module systematically confuses pointers and integers use crate::ffi::OsString; +use crate::num::NonZero; +use crate::ops::Try; use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; use crate::sys::os_str::Buf; use crate::sys::pal::abi::usercalls::alloc; @@ -28,35 +30,81 @@ pub unsafe fn init(argc: isize, argv: *const *const u8) { pub fn args() -> Args { let args = unsafe { (ARGS.load(Ordering::Relaxed) as *const ArgsStore).as_ref() }; - if let Some(args) = args { Args(args.iter()) } else { Args([].iter()) } + let slice = args.map(|args| args.as_slice()).unwrap_or(&[]); + Args { iter: slice.iter() } } -pub struct Args(slice::Iter<'static, OsString>); +pub struct Args { + iter: slice::Iter<'static, OsString>, +} impl fmt::Debug for Args { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.as_slice().fmt(f) + self.iter.as_slice().fmt(f) } } impl Iterator for Args { type Item = OsString; + fn next(&mut self) -> Option<OsString> { - self.0.next().cloned() + self.iter.next().cloned() } + + #[inline] fn size_hint(&self) -> (usize, Option<usize>) { - self.0.size_hint() + self.iter.size_hint() } -} -impl ExactSizeIterator for Args { - fn len(&self) -> usize { - self.0.len() + #[inline] + fn count(self) -> usize { + self.iter.len() + } + + fn last(self) -> Option<OsString> { + self.iter.last().cloned() + } + + #[inline] + fn advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>> { + self.iter.advance_by(n) + } + + fn try_fold<B, F, R>(&mut self, init: B, f: F) -> R + where + F: FnMut(B, Self::Item) -> R, + R: Try<Output = B>, + { + self.iter.by_ref().cloned().try_fold(init, f) + } + + fn fold<B, F>(self, init: B, f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.iter.cloned().fold(init, f) } } impl DoubleEndedIterator for Args { fn next_back(&mut self) -> Option<OsString> { - self.0.next_back().cloned() + self.iter.next_back().cloned() + } + + #[inline] + fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero<usize>> { + self.iter.advance_back_by(n) + } +} + +impl ExactSizeIterator for Args { + #[inline] + fn len(&self) -> usize { + self.iter.len() + } + + #[inline] + fn is_empty(&self) -> bool { + self.iter.is_empty() } } diff --git a/library/std/src/sys/args/unsupported.rs b/library/std/src/sys/args/unsupported.rs index a2d75a61976..ecffc6d2641 100644 --- a/library/std/src/sys/args/unsupported.rs +++ b/library/std/src/sys/args/unsupported.rs @@ -15,22 +15,28 @@ impl fmt::Debug for Args { impl Iterator for Args { type Item = OsString; + + #[inline] fn next(&mut self) -> Option<OsString> { None } + + #[inline] fn size_hint(&self) -> (usize, Option<usize>) { (0, Some(0)) } } -impl ExactSizeIterator for Args { - fn len(&self) -> usize { - 0 - } -} - impl DoubleEndedIterator for Args { + #[inline] fn next_back(&mut self) -> Option<OsString> { None } } + +impl ExactSizeIterator for Args { + #[inline] + fn len(&self) -> usize { + 0 + } +} diff --git a/library/std/src/sys/exit_guard.rs b/library/std/src/sys/exit_guard.rs index 5a090f50666..bd70d178244 100644 --- a/library/std/src/sys/exit_guard.rs +++ b/library/std/src/sys/exit_guard.rs @@ -1,14 +1,5 @@ cfg_if::cfg_if! { if #[cfg(target_os = "linux")] { - /// pthread_t is a pointer on some platforms, - /// so we wrap it in this to impl Send + Sync. - #[derive(Clone, Copy)] - #[repr(transparent)] - struct PThread(libc::pthread_t); - // Safety: pthread_t is safe to send between threads - unsafe impl Send for PThread {} - // Safety: pthread_t is safe to share between threads - unsafe impl Sync for PThread {} /// Mitigation for <https://github.com/rust-lang/rust/issues/126600> /// /// On glibc, `libc::exit` has been observed to not always be thread-safe. @@ -30,28 +21,34 @@ cfg_if::cfg_if! { /// (waiting for the process to exit). #[cfg_attr(any(test, doctest), allow(dead_code))] pub(crate) fn unique_thread_exit() { - let this_thread_id = unsafe { libc::pthread_self() }; - use crate::sync::{Mutex, PoisonError}; - static EXITING_THREAD_ID: Mutex<Option<PThread>> = Mutex::new(None); - let mut exiting_thread_id = - EXITING_THREAD_ID.lock().unwrap_or_else(PoisonError::into_inner); - match *exiting_thread_id { - None => { + use crate::ffi::c_int; + use crate::ptr; + use crate::sync::atomic::AtomicPtr; + use crate::sync::atomic::Ordering::{Acquire, Relaxed}; + + static EXITING_THREAD_ID: AtomicPtr<c_int> = AtomicPtr::new(ptr::null_mut()); + + // We use the address of `errno` as a cheap and safe way to identify + // threads. As the C standard mandates that `errno` must have thread + // storage duration, we can rely on its address not changing over the + // lifetime of the thread. Additionally, accesses to `errno` are + // async-signal-safe, so this function is available in all imaginable + // circumstances. + let this_thread_id = crate::sys::os::errno_location(); + match EXITING_THREAD_ID.compare_exchange(ptr::null_mut(), this_thread_id, Acquire, Relaxed) { + Ok(_) => { // This is the first thread to call `unique_thread_exit`, - // and this is the first time it is called. - // Set EXITING_THREAD_ID to this thread's ID and return. - *exiting_thread_id = Some(PThread(this_thread_id)); - }, - Some(exiting_thread_id) if exiting_thread_id.0 == this_thread_id => { + // and this is the first time it is called. Continue exiting. + } + Err(exiting_thread_id) if exiting_thread_id == this_thread_id => { // This is the first thread to call `unique_thread_exit`, // but this is the second time it is called. // Abort the process. core::panicking::panic_nounwind("std::process::exit called re-entrantly") } - Some(_) => { + Err(_) => { // This is not the first thread to call `unique_thread_exit`. // Pause until the process exits. - drop(exiting_thread_id); loop { // Safety: libc::pause is safe to call. unsafe { libc::pause(); } diff --git a/library/std/src/sys/fs/hermit.rs b/library/std/src/sys/fs/hermit.rs index 99690abe8ed..a9774bef9e3 100644 --- a/library/std/src/sys/fs/hermit.rs +++ b/library/std/src/sys/fs/hermit.rs @@ -1,4 +1,5 @@ use crate::ffi::{CStr, OsStr, OsString, c_char}; +use crate::fs::TryLockError; use crate::io::{self, BorrowedCursor, Error, ErrorKind, IoSlice, IoSliceMut, SeekFrom}; use crate::os::hermit::ffi::OsStringExt; use crate::os::hermit::hermit_abi::{ @@ -12,7 +13,7 @@ use crate::sys::common::small_c_string::run_path_with_cstr; use crate::sys::fd::FileDesc; pub use crate::sys::fs::common::{copy, exists}; use crate::sys::time::SystemTime; -use crate::sys::{cvt, unsupported}; +use crate::sys::{cvt, unsupported, unsupported_err}; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; use crate::{fmt, mem}; @@ -366,12 +367,12 @@ impl File { unsupported() } - pub fn try_lock(&self) -> io::Result<bool> { - unsupported() + pub fn try_lock(&self) -> Result<(), TryLockError> { + Err(TryLockError::Error(unsupported_err())) } - pub fn try_lock_shared(&self) -> io::Result<bool> { - unsupported() + pub fn try_lock_shared(&self) -> Result<(), TryLockError> { + Err(TryLockError::Error(unsupported_err())) } pub fn unlock(&self) -> io::Result<()> { diff --git a/library/std/src/sys/fs/solid.rs b/library/std/src/sys/fs/solid.rs index 39de933b724..3bfb39bac95 100644 --- a/library/std/src/sys/fs/solid.rs +++ b/library/std/src/sys/fs/solid.rs @@ -2,6 +2,7 @@ use crate::ffi::{CStr, CString, OsStr, OsString}; use crate::fmt; +use crate::fs::TryLockError; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; use crate::mem::MaybeUninit; use crate::os::raw::{c_int, c_short}; @@ -11,7 +12,7 @@ use crate::sync::Arc; pub use crate::sys::fs::common::exists; use crate::sys::pal::{abi, error}; use crate::sys::time::SystemTime; -use crate::sys::unsupported; +use crate::sys::{unsupported, unsupported_err}; use crate::sys_common::ignore_notfound; type CIntNotMinusOne = core::num::niche_types::NotAllOnes<c_int>; @@ -352,12 +353,12 @@ impl File { unsupported() } - pub fn try_lock(&self) -> io::Result<bool> { - unsupported() + pub fn try_lock(&self) -> Result<(), TryLockError> { + Err(TryLockError::Error(unsupported_err())) } - pub fn try_lock_shared(&self) -> io::Result<bool> { - unsupported() + pub fn try_lock_shared(&self) -> Result<(), TryLockError> { + Err(TryLockError::Error(unsupported_err())) } pub fn unlock(&self) -> io::Result<()> { diff --git a/library/std/src/sys/fs/uefi.rs b/library/std/src/sys/fs/uefi.rs index d6ae86bd3d2..416c90b98b6 100644 --- a/library/std/src/sys/fs/uefi.rs +++ b/library/std/src/sys/fs/uefi.rs @@ -2,6 +2,7 @@ use r_efi::protocols::file; use crate::ffi::OsString; use crate::fmt; +use crate::fs::TryLockError; use crate::hash::Hash; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; use crate::path::{Path, PathBuf}; @@ -227,11 +228,11 @@ impl File { self.0 } - pub fn try_lock(&self) -> io::Result<bool> { + pub fn try_lock(&self) -> Result<(), TryLockError> { self.0 } - pub fn try_lock_shared(&self) -> io::Result<bool> { + pub fn try_lock_shared(&self) -> Result<(), TryLockError> { self.0 } diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 5af59a2a914..863358596c1 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -75,6 +75,7 @@ use libc::{dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, st use crate::ffi::{CStr, OsStr, OsString}; use crate::fmt::{self, Write as _}; +use crate::fs::TryLockError; use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd}; use crate::os::unix::prelude::*; @@ -1310,15 +1311,17 @@ impl File { target_os = "netbsd", target_vendor = "apple", ))] - pub fn try_lock(&self) -> io::Result<bool> { + pub fn try_lock(&self) -> Result<(), TryLockError> { let result = cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_EX | libc::LOCK_NB) }); - if let Err(ref err) = result { + if let Err(err) = result { if err.kind() == io::ErrorKind::WouldBlock { - return Ok(false); + Err(TryLockError::WouldBlock) + } else { + Err(TryLockError::Error(err)) } + } else { + Ok(()) } - result?; - return Ok(true); } #[cfg(not(any( @@ -1328,8 +1331,11 @@ impl File { target_os = "netbsd", target_vendor = "apple", )))] - pub fn try_lock(&self) -> io::Result<bool> { - Err(io::const_error!(io::ErrorKind::Unsupported, "try_lock() not supported")) + pub fn try_lock(&self) -> Result<(), TryLockError> { + Err(TryLockError::Error(io::const_error!( + io::ErrorKind::Unsupported, + "try_lock() not supported" + ))) } #[cfg(any( @@ -1339,15 +1345,17 @@ impl File { target_os = "netbsd", target_vendor = "apple", ))] - pub fn try_lock_shared(&self) -> io::Result<bool> { + pub fn try_lock_shared(&self) -> Result<(), TryLockError> { let result = cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_SH | libc::LOCK_NB) }); - if let Err(ref err) = result { + if let Err(err) = result { if err.kind() == io::ErrorKind::WouldBlock { - return Ok(false); + Err(TryLockError::WouldBlock) + } else { + Err(TryLockError::Error(err)) } + } else { + Ok(()) } - result?; - return Ok(true); } #[cfg(not(any( @@ -1357,8 +1365,11 @@ impl File { target_os = "netbsd", target_vendor = "apple", )))] - pub fn try_lock_shared(&self) -> io::Result<bool> { - Err(io::const_error!(io::ErrorKind::Unsupported, "try_lock_shared() not supported")) + pub fn try_lock_shared(&self) -> Result<(), TryLockError> { + Err(TryLockError::Error(io::const_error!( + io::ErrorKind::Unsupported, + "try_lock_shared() not supported" + ))) } #[cfg(any( diff --git a/library/std/src/sys/fs/unsupported.rs b/library/std/src/sys/fs/unsupported.rs index 45e93deffa3..0ff9533c047 100644 --- a/library/std/src/sys/fs/unsupported.rs +++ b/library/std/src/sys/fs/unsupported.rs @@ -1,5 +1,6 @@ use crate::ffi::OsString; use crate::fmt; +use crate::fs::TryLockError; use crate::hash::{Hash, Hasher}; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; use crate::path::{Path, PathBuf}; @@ -206,11 +207,11 @@ impl File { self.0 } - pub fn try_lock(&self) -> io::Result<bool> { + pub fn try_lock(&self) -> Result<(), TryLockError> { self.0 } - pub fn try_lock_shared(&self) -> io::Result<bool> { + pub fn try_lock_shared(&self) -> Result<(), TryLockError> { self.0 } diff --git a/library/std/src/sys/fs/wasi.rs b/library/std/src/sys/fs/wasi.rs index 773040571bc..ebfc7377a2e 100644 --- a/library/std/src/sys/fs/wasi.rs +++ b/library/std/src/sys/fs/wasi.rs @@ -1,4 +1,5 @@ use crate::ffi::{CStr, OsStr, OsString}; +use crate::fs::TryLockError; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; use crate::mem::{self, ManuallyDrop}; use crate::os::raw::c_int; @@ -10,7 +11,7 @@ use crate::sys::common::small_c_string::run_path_with_cstr; use crate::sys::fd::WasiFd; pub use crate::sys::fs::common::exists; use crate::sys::time::SystemTime; -use crate::sys::unsupported; +use crate::sys::{unsupported, unsupported_err}; use crate::sys_common::{AsInner, FromInner, IntoInner, ignore_notfound}; use crate::{fmt, iter, ptr}; @@ -461,12 +462,12 @@ impl File { unsupported() } - pub fn try_lock(&self) -> io::Result<bool> { - unsupported() + pub fn try_lock(&self) -> Result<(), TryLockError> { + Err(TryLockError::Error(unsupported_err())) } - pub fn try_lock_shared(&self) -> io::Result<bool> { - unsupported() + pub fn try_lock_shared(&self) -> Result<(), TryLockError> { + Err(TryLockError::Error(unsupported_err())) } pub fn unlock(&self) -> io::Result<()> { diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs index 9215f937567..9039fd00f5d 100644 --- a/library/std/src/sys/fs/windows.rs +++ b/library/std/src/sys/fs/windows.rs @@ -3,6 +3,7 @@ use crate::alloc::{Layout, alloc, dealloc}; use crate::borrow::Cow; use crate::ffi::{OsStr, OsString, c_void}; +use crate::fs::TryLockError; use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; use crate::mem::{self, MaybeUninit, offset_of}; use crate::os::windows::io::{AsHandle, BorrowedHandle}; @@ -399,7 +400,7 @@ impl File { self.acquire_lock(0) } - pub fn try_lock(&self) -> io::Result<bool> { + pub fn try_lock(&self) -> Result<(), TryLockError> { let result = cvt(unsafe { let mut overlapped = mem::zeroed(); c::LockFileEx( @@ -413,18 +414,18 @@ impl File { }); match result { - Ok(_) => Ok(true), + Ok(_) => Ok(()), Err(err) if err.raw_os_error() == Some(c::ERROR_IO_PENDING as i32) || err.raw_os_error() == Some(c::ERROR_LOCK_VIOLATION as i32) => { - Ok(false) + Err(TryLockError::WouldBlock) } - Err(err) => Err(err), + Err(err) => Err(TryLockError::Error(err)), } } - pub fn try_lock_shared(&self) -> io::Result<bool> { + pub fn try_lock_shared(&self) -> Result<(), TryLockError> { let result = cvt(unsafe { let mut overlapped = mem::zeroed(); c::LockFileEx( @@ -438,14 +439,14 @@ impl File { }); match result { - Ok(_) => Ok(true), + Ok(_) => Ok(()), Err(err) if err.raw_os_error() == Some(c::ERROR_IO_PENDING as i32) || err.raw_os_error() == Some(c::ERROR_LOCK_VIOLATION as i32) => { - Ok(false) + Err(TryLockError::WouldBlock) } - Err(err) => Err(err), + Err(err) => Err(TryLockError::Error(err)), } } diff --git a/library/std/src/sys/os_str/bytes.rs b/library/std/src/sys/os_str/bytes.rs index dfff2d3e5d3..4a8808c9230 100644 --- a/library/std/src/sys/os_str/bytes.rs +++ b/library/std/src/sys/os_str/bytes.rs @@ -216,19 +216,26 @@ impl Buf { self.as_slice().into_rc() } - /// Provides plumbing to core `Vec::truncate`. - /// More well behaving alternative to allowing outer types - /// full mutable access to the core `Vec`. - #[inline] - pub(crate) fn truncate(&mut self, len: usize) { + /// Provides plumbing to `Vec::truncate` without giving full mutable access + /// to the `Vec`. + /// + /// # Safety + /// + /// The length must be at an `OsStr` boundary, according to + /// `Slice::check_public_boundary`. + #[inline] + pub unsafe fn truncate_unchecked(&mut self, len: usize) { self.inner.truncate(len); } - /// Provides plumbing to core `Vec::extend_from_slice`. - /// More well behaving alternative to allowing outer types - /// full mutable access to the core `Vec`. + /// Provides plumbing to `Vec::extend_from_slice` without giving full + /// mutable access to the `Vec`. + /// + /// # Safety + /// + /// This encoding has no safety requirements. #[inline] - pub(crate) fn extend_from_slice(&mut self, other: &[u8]) { + pub unsafe fn extend_from_slice_unchecked(&mut self, other: &[u8]) { self.inner.extend_from_slice(other); } } diff --git a/library/std/src/sys/os_str/wtf8.rs b/library/std/src/sys/os_str/wtf8.rs index a32f5d40f6a..5174ea65d0c 100644 --- a/library/std/src/sys/os_str/wtf8.rs +++ b/library/std/src/sys/os_str/wtf8.rs @@ -195,19 +195,31 @@ impl Buf { self.as_slice().into_rc() } - /// Provides plumbing to core `Vec::truncate`. - /// More well behaving alternative to allowing outer types - /// full mutable access to the core `Vec`. - #[inline] - pub(crate) fn truncate(&mut self, len: usize) { + /// Provides plumbing to `Vec::truncate` without giving full mutable access + /// to the `Vec`. + /// + /// # Safety + /// + /// The length must be at an `OsStr` boundary, according to + /// `Slice::check_public_boundary`. + #[inline] + pub unsafe fn truncate_unchecked(&mut self, len: usize) { self.inner.truncate(len); } - /// Provides plumbing to core `Vec::extend_from_slice`. - /// More well behaving alternative to allowing outer types - /// full mutable access to the core `Vec`. + /// Provides plumbing to `Vec::extend_from_slice` without giving full + /// mutable access to the `Vec`. + /// + /// # Safety + /// + /// The slice must be valid for the platform encoding (as described in + /// [`Slice::from_encoded_bytes_unchecked`]). + /// + /// This bypasses the WTF-8 surrogate joining, so `self` must not end with a + /// leading surrogate half and `other` must not start with with a trailing + /// surrogate half. #[inline] - pub(crate) fn extend_from_slice(&mut self, other: &[u8]) { + pub unsafe fn extend_from_slice_unchecked(&mut self, other: &[u8]) { self.inner.extend_from_slice(other); } } diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index 4883303b88e..48609030aed 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -56,7 +56,7 @@ unsafe extern "C" { #[cfg_attr(target_os = "aix", link_name = "_Errno")] // SAFETY: this will always return the same pointer on a given thread. #[unsafe(ffi_const)] - fn errno_location() -> *mut c_int; + pub safe fn errno_location() -> *mut c_int; } /// Returns the platform-specific value of errno diff --git a/library/std/tests/sync/mutex.rs b/library/std/tests/sync/mutex.rs index 88fb448d1eb..ac82914d6de 100644 --- a/library/std/tests/sync/mutex.rs +++ b/library/std/tests/sync/mutex.rs @@ -409,13 +409,13 @@ fn panic_while_mapping_unlocked_poison() { let _ = panic::catch_unwind(|| { let guard = lock.lock().unwrap(); - let _guard = MutexGuard::try_map::<(), _>(guard, |_| panic!()); + let _guard = MutexGuard::filter_map::<(), _>(guard, |_| panic!()); }); match lock.try_lock() { - Ok(_) => panic!("panicking in a MutexGuard::try_map closure should poison the Mutex"), + Ok(_) => panic!("panicking in a MutexGuard::filter_map closure should poison the Mutex"), Err(TryLockError::WouldBlock) => { - panic!("panicking in a MutexGuard::try_map closure should unlock the mutex") + panic!("panicking in a MutexGuard::filter_map closure should unlock the mutex") } Err(TryLockError::Poisoned(_)) => {} } @@ -437,13 +437,15 @@ fn panic_while_mapping_unlocked_poison() { let _ = panic::catch_unwind(|| { let guard = lock.lock().unwrap(); let guard = MutexGuard::map::<(), _>(guard, |val| val); - let _guard = MappedMutexGuard::try_map::<(), _>(guard, |_| panic!()); + let _guard = MappedMutexGuard::filter_map::<(), _>(guard, |_| panic!()); }); match lock.try_lock() { - Ok(_) => panic!("panicking in a MappedMutexGuard::try_map closure should poison the Mutex"), + Ok(_) => { + panic!("panicking in a MappedMutexGuard::filter_map closure should poison the Mutex") + } Err(TryLockError::WouldBlock) => { - panic!("panicking in a MappedMutexGuard::try_map closure should unlock the mutex") + panic!("panicking in a MappedMutexGuard::filter_map closure should unlock the mutex") } Err(TryLockError::Poisoned(_)) => {} } diff --git a/library/std/tests/sync/rwlock.rs b/library/std/tests/sync/rwlock.rs index d2c784aefcf..1d55a176948 100644 --- a/library/std/tests/sync/rwlock.rs +++ b/library/std/tests/sync/rwlock.rs @@ -517,16 +517,20 @@ fn panic_while_mapping_read_unlocked_no_poison() { let _ = panic::catch_unwind(|| { let guard = lock.read().unwrap(); - let _guard = RwLockReadGuard::try_map::<(), _>(guard, |_| panic!()); + let _guard = RwLockReadGuard::filter_map::<(), _>(guard, |_| panic!()); }); match lock.try_write() { Ok(_) => {} Err(TryLockError::WouldBlock) => { - panic!("panicking in a RwLockReadGuard::try_map closure should release the read lock") + panic!( + "panicking in a RwLockReadGuard::filter_map closure should release the read lock" + ) } Err(TryLockError::Poisoned(_)) => { - panic!("panicking in a RwLockReadGuard::try_map closure should not poison the RwLock") + panic!( + "panicking in a RwLockReadGuard::filter_map closure should not poison the RwLock" + ) } } @@ -549,16 +553,16 @@ fn panic_while_mapping_read_unlocked_no_poison() { let _ = panic::catch_unwind(|| { let guard = lock.read().unwrap(); let guard = RwLockReadGuard::map::<(), _>(guard, |val| val); - let _guard = MappedRwLockReadGuard::try_map::<(), _>(guard, |_| panic!()); + let _guard = MappedRwLockReadGuard::filter_map::<(), _>(guard, |_| panic!()); }); match lock.try_write() { Ok(_) => {} Err(TryLockError::WouldBlock) => panic!( - "panicking in a MappedRwLockReadGuard::try_map closure should release the read lock" + "panicking in a MappedRwLockReadGuard::filter_map closure should release the read lock" ), Err(TryLockError::Poisoned(_)) => panic!( - "panicking in a MappedRwLockReadGuard::try_map closure should not poison the RwLock" + "panicking in a MappedRwLockReadGuard::filter_map closure should not poison the RwLock" ), } @@ -585,15 +589,17 @@ fn panic_while_mapping_write_unlocked_poison() { let _ = panic::catch_unwind(|| { let guard = lock.write().unwrap(); - let _guard = RwLockWriteGuard::try_map::<(), _>(guard, |_| panic!()); + let _guard = RwLockWriteGuard::filter_map::<(), _>(guard, |_| panic!()); }); match lock.try_write() { Ok(_) => { - panic!("panicking in a RwLockWriteGuard::try_map closure should poison the RwLock") + panic!("panicking in a RwLockWriteGuard::filter_map closure should poison the RwLock") } Err(TryLockError::WouldBlock) => { - panic!("panicking in a RwLockWriteGuard::try_map closure should release the write lock") + panic!( + "panicking in a RwLockWriteGuard::filter_map closure should release the write lock" + ) } Err(TryLockError::Poisoned(_)) => {} } @@ -617,15 +623,15 @@ fn panic_while_mapping_write_unlocked_poison() { let _ = panic::catch_unwind(|| { let guard = lock.write().unwrap(); let guard = RwLockWriteGuard::map::<(), _>(guard, |val| val); - let _guard = MappedRwLockWriteGuard::try_map::<(), _>(guard, |_| panic!()); + let _guard = MappedRwLockWriteGuard::filter_map::<(), _>(guard, |_| panic!()); }); match lock.try_write() { Ok(_) => panic!( - "panicking in a MappedRwLockWriteGuard::try_map closure should poison the RwLock" + "panicking in a MappedRwLockWriteGuard::filter_map closure should poison the RwLock" ), Err(TryLockError::WouldBlock) => panic!( - "panicking in a MappedRwLockWriteGuard::try_map closure should release the write lock" + "panicking in a MappedRwLockWriteGuard::filter_map closure should release the write lock" ), Err(TryLockError::Poisoned(_)) => {} } diff --git a/library/stdarch b/library/stdarch -Subproject 1245618ccf5b2df7ab1ebb0279b9f3f72667016 +Subproject f1c1839c0deb985a9f98cbd6b38a6d43f2df615 diff --git a/library/sysroot/Cargo.toml b/library/sysroot/Cargo.toml index ec6ae31507e..c149d513c32 100644 --- a/library/sysroot/Cargo.toml +++ b/library/sysroot/Cargo.toml @@ -31,5 +31,4 @@ panic_immediate_abort = ["std/panic_immediate_abort"] profiler = ["dep:profiler_builtins"] std_detect_file_io = ["std/std_detect_file_io"] std_detect_dlsym_getauxval = ["std/std_detect_dlsym_getauxval"] -std_detect_env_override = ["std/std_detect_env_override"] windows_raw_dylib = ["std/windows_raw_dylib"] diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index d4fccc535a6..a7a3b5a878c 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -2556,9 +2556,9 @@ fn prepare_cargo_test( // We skip everything on Miri as then this overwrites the libdir set up // by `Cargo::new` and that actually makes things go wrong. if builder.kind != Kind::Miri { - let mut dylib_path = dylib_path(); - dylib_path.insert(0, PathBuf::from(&*builder.sysroot_target_libdir(compiler, target))); - cargo.env(dylib_path_var(), env::join_paths(&dylib_path).unwrap()); + let mut dylib_paths = builder.rustc_lib_paths(compiler); + dylib_paths.push(PathBuf::from(&builder.sysroot_target_libdir(compiler, target))); + helpers::add_dylib_path(dylib_paths, &mut cargo); } if builder.remote_tested(target) { diff --git a/src/doc/rustc-dev-guide/.github/workflows/ci.yml b/src/doc/rustc-dev-guide/.github/workflows/ci.yml index 415d0dc397d..daf5223cbd4 100644 --- a/src/doc/rustc-dev-guide/.github/workflows/ci.yml +++ b/src/doc/rustc-dev-guide/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: if: github.repository == 'rust-lang/rustc-dev-guide' runs-on: ubuntu-latest env: - MDBOOK_VERSION: 0.4.21 + MDBOOK_VERSION: 0.4.48 MDBOOK_LINKCHECK2_VERSION: 0.9.1 MDBOOK_MERMAID_VERSION: 0.12.6 MDBOOK_TOC_VERSION: 0.11.2 diff --git a/src/doc/rustc-dev-guide/josh-sync/src/sync.rs b/src/doc/rustc-dev-guide/josh-sync/src/sync.rs index cd64be63670..41d96397faa 100644 --- a/src/doc/rustc-dev-guide/josh-sync/src/sync.rs +++ b/src/doc/rustc-dev-guide/josh-sync/src/sync.rs @@ -194,7 +194,7 @@ impl GitSync { ); println!( // Open PR with `subtree update` title to silence the `no-merges` triagebot check - " https://github.com/{UPSTREAM_REPO}/compare/{github_user}:{branch}?quick_pull=1&title=Rustc+dev+guide+subtree+update&body=r?+@ghost" + " https://github.com/{UPSTREAM_REPO}/compare/{github_user}:{branch}?quick_pull=1&title=rustc-dev-guide+subtree+update&body=r?+@ghost" ); drop(josh); diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index 67fa25f2228..66b4fe2bf3b 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -deb947971c8748f5c6203548ce4af9022f21eaf0 +0c33fe2c3d3eecadd17a84b110bb067288a64f1c diff --git a/src/doc/rustc-dev-guide/src/SUMMARY.md b/src/doc/rustc-dev-guide/src/SUMMARY.md index d6ec803a60a..31119496e75 100644 --- a/src/doc/rustc-dev-guide/src/SUMMARY.md +++ b/src/doc/rustc-dev-guide/src/SUMMARY.md @@ -157,6 +157,7 @@ - [ADTs and Generic Arguments](./ty_module/generic_arguments.md) - [Parameter types/consts/regions](./ty_module/param_ty_const_regions.md) - [`TypeFolder` and `TypeFoldable`](./ty-fold.md) +- [Aliases and Normalization](./normalization.md) - [Typing/Param Envs](./typing_parameter_envs.md) - [Type inference](./type-inference.md) - [Trait solving](./traits/resolution.md) @@ -176,7 +177,6 @@ - [Coinduction](./solve/coinduction.md) - [Caching](./solve/caching.md) - [Proof trees](./solve/proof-trees.md) - - [Normalization](./solve/normalization.md) - [Opaque types](./solve/opaque-types.md) - [Significant changes and quirks](./solve/significant-changes.md) - [`Unsize` and `CoerceUnsized` traits](./traits/unsize.md) diff --git a/src/doc/rustc-dev-guide/src/building/bootstrapping/intro.md b/src/doc/rustc-dev-guide/src/building/bootstrapping/intro.md index f72918c8377..7f53097824c 100644 --- a/src/doc/rustc-dev-guide/src/building/bootstrapping/intro.md +++ b/src/doc/rustc-dev-guide/src/building/bootstrapping/intro.md @@ -6,8 +6,8 @@ of the same compiler. This raises a chicken-and-egg paradox: where did the first compiler come from? It must have been written in a different language. In Rust's case it was -[written in OCaml][ocaml-compiler]. However it was abandoned long ago and the -only way to build a modern version of rustc is a slightly less modern +[written in OCaml][ocaml-compiler]. However, it was abandoned long ago, and the +only way to build a modern version of rustc is with a slightly less modern version. This is exactly how `x.py` works: it downloads the current beta release of diff --git a/src/doc/rustc-dev-guide/src/building/bootstrapping/what-bootstrapping-does.md b/src/doc/rustc-dev-guide/src/building/bootstrapping/what-bootstrapping-does.md index ffcfe259625..a2930b3e427 100644 --- a/src/doc/rustc-dev-guide/src/building/bootstrapping/what-bootstrapping-does.md +++ b/src/doc/rustc-dev-guide/src/building/bootstrapping/what-bootstrapping-does.md @@ -8,8 +8,8 @@ the same compiler. This raises a chicken-and-egg paradox: where did the first compiler come from? It must have been written in a different language. In Rust's case it was -[written in OCaml][ocaml-compiler]. However it was abandoned long ago and the -only way to build a modern version of `rustc` is a slightly less modern version. +[written in OCaml][ocaml-compiler]. However, it was abandoned long ago, and the +only way to build a modern version of `rustc` is with a slightly less modern version. This is exactly how [`./x.py`] works: it downloads the current beta release of `rustc`, then uses it to compile the new compiler. diff --git a/src/doc/rustc-dev-guide/src/compiler-src.md b/src/doc/rustc-dev-guide/src/compiler-src.md index c538fc8b788..00aa9622684 100644 --- a/src/doc/rustc-dev-guide/src/compiler-src.md +++ b/src/doc/rustc-dev-guide/src/compiler-src.md @@ -62,21 +62,20 @@ huge. There is also the `rustc` crate which is the actual binary (i.e. the [`rustc_driver`] crate, which drives the various parts of compilation in other crates. -The dependency structure of these crates is complex, but roughly it is +The dependency order of these crates is complex, but roughly it is something like this: -- `rustc` (the binary) calls [`rustc_driver::main`][main]. - - [`rustc_driver`] depends on a lot of other crates, but the main one is - [`rustc_interface`]. - - [`rustc_interface`] depends on most of the other compiler crates. It - is a fairly generic interface for driving the whole compilation. - - Most of the other `rustc_*` crates depend on [`rustc_middle`], - which defines a lot of central data structures in the compiler. - - [`rustc_middle`] and most of the other crates depend on a - handful of crates representing the early parts of the - compiler (e.g. the parser), fundamental data structures (e.g. - [`Span`]), or error reporting: [`rustc_data_structures`], - [`rustc_span`], [`rustc_errors`], etc. +1. `rustc` (the binary) calls [`rustc_driver::main`][main]. +1. [`rustc_driver`] depends on a lot of other crates, but the main one is + [`rustc_interface`]. +1. [`rustc_interface`] depends on most of the other compiler crates. It is a + fairly generic interface for driving the whole compilation. +1. Most of the other `rustc_*` crates depend on [`rustc_middle`], which defines + a lot of central data structures in the compiler. +1. [`rustc_middle`] and most of the other crates depend on a handful of crates + representing the early parts of the compiler (e.g. the parser), fundamental + data structures (e.g. [`Span`]), or error reporting: + [`rustc_data_structures`], [`rustc_span`], [`rustc_errors`], etc. [`rustc_data_structures`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_data_structures/index.html [`rustc_driver`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver/index.html @@ -87,8 +86,12 @@ something like this: [`Span`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html [main]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver/fn.main.html -You can see the exact dependencies by reading the [`Cargo.toml`] for the various -crates, just like a normal Rust crate. +You can see the exact dependencies by running `cargo tree`, +just like you would for any other Rust package: + +```console +cargo tree --package rustc_driver +``` One final thing: [`src/llvm-project`] is a submodule for our fork of LLVM. During bootstrapping, LLVM is built and the [`compiler/rustc_llvm`] crate diff --git a/src/doc/rustc-dev-guide/src/guides/editions.md b/src/doc/rustc-dev-guide/src/guides/editions.md index ea207167791..9a92d4ebcb5 100644 --- a/src/doc/rustc-dev-guide/src/guides/editions.md +++ b/src/doc/rustc-dev-guide/src/guides/editions.md @@ -193,6 +193,23 @@ When a user runs `cargo fix --edition`, cargo will pass the `--force-warn rust-2 flag to force all of these lints to appear during the edition migration. Cargo also passes `--cap-lints=allow` so that no other lints interfere with the edition migration. +Make sure that the example code sets the correct edition. The example should illustrate the previous edition, and show what the migration warning would look like. For example, this lint for a 2024 migration shows an example in 2021: + +```rust,ignore +declare_lint! { + /// The `keyword_idents_2024` lint detects ... + /// + /// ### Example + /// + /// ```rust,edition2021 + /// #![warn(keyword_idents_2024)] + /// fn gen() {} + /// ``` + /// + /// {{produces}} +} +``` + Migration lints can be either `Allow` or `Warn` by default. If it is `Allow`, users usually won't see this warning unless they are doing an edition migration manually or there is a problem during the migration. @@ -334,3 +351,40 @@ In general it is recommended to avoid these special cases except for very high v [into-iter]: https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html [panic-macro]: https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html [`non_fmt_panics`]: https://doc.rust-lang.org/nightly/rustc/lints/listing/warn-by-default.html#non-fmt-panics + +### Migrating the standard library edition + +Updating the edition of the standard library itself roughly involves the following process: + +- Wait until the newly stabilized edition has reached beta and the bootstrap compiler has been updated. +- Apply migration lints. This can be an involved process since some code is in external submodules[^std-submodules], and the standard library makes heavy use of conditional compilation. Also, running `cargo fix --edition` can be impractical on the standard library itself. One approach is to individually add `#![warn(...)]` at the top of each crate for each lint, run `./x check library`, apply the migrations, remove the `#![warn(...)]` and commit each migration separately. You'll likely need to run `./x check` with `--target` for many different targets to get full coverage (otherwise you'll likely spend days or weeks getting CI to pass)[^ed-docker]. See also the [advanced migration guide] for more tips. + - Apply migrations to [`backtrace-rs`]. [Example for 2024](https://github.com/rust-lang/backtrace-rs/pull/700). Note that this doesn't update the edition of the crate itself because that is published independently on crates.io, and that would otherwise restrict the minimum Rust version. Consider adding some `#![deny()]` attributes to avoid regressions until its edition gets updated. + - Apply migrations to [`stdarch`], and update its edition, and formatting. [Example for 2024](https://github.com/rust-lang/stdarch/pull/1710). + - Post PRs to update the backtrace and stdarch submodules, and wait for those to land. + - Apply migration lints to the standard library crates, and update their edition. I recommend working one crate at a time starting with `core`. [Example for 2024](https://github.com/rust-lang/rust/pull/138162). + +[^std-submodules]: This will hopefully change in the future to pull these submodules into `rust-lang/rust`. +[^ed-docker]: You'll also likely need to do a lot of testing for different targets, and this is where [docker testing](../tests/docker.md) comes in handy. + +[advanced migration guide]: https://doc.rust-lang.org/nightly/edition-guide/editions/advanced-migrations.html +[`backtrace-rs`]: https://github.com/rust-lang/backtrace-rs/ +[`stdarch`]: https://github.com/rust-lang/stdarch/ + +## Stabilizing an edition + +After the edition team has given the go-ahead, the process for stabilizing an edition is roughly: + +- Update [`LATEST_STABLE_EDITION`]. +- Update [`Edition::is_stable`]. +- Hunt and find any document that refers to edition by number, and update it: + - [`--edition` flag](https://github.com/rust-lang/rust/blob/master/src/doc/rustc/src/command-line-arguments.md#--edition-specify-the-edition-to-use) + - [Rustdoc attributes](https://github.com/rust-lang/rust/blob/master/src/doc/rustdoc/src/write-documentation/documentation-tests.md#attributes) +- Clean up any tests that use the `//@ edition` header to remove the `-Zunstable-options` flag to ensure they are indeed stable. Note: Ideally this should be automated, see [#133582]. +- Bless any tests that change. +- Update `lint-docs` to default to the new edition. + +See [example for 2024](https://github.com/rust-lang/rust/pull/133349). + +[`LATEST_STABLE_EDITION`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/edition/constant.LATEST_STABLE_EDITION.html +[`Edition::is_stable`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/edition/enum.Edition.html#method.is_stable +[#133582]: https://github.com/rust-lang/rust/issues/133582 diff --git a/src/doc/rustc-dev-guide/src/normalization.md b/src/doc/rustc-dev-guide/src/normalization.md new file mode 100644 index 00000000000..ef530ccc5ed --- /dev/null +++ b/src/doc/rustc-dev-guide/src/normalization.md @@ -0,0 +1,309 @@ +# Aliases and Normalization + +<!-- toc --> + +## Aliases + +In Rust there are a number of types that are considered equal to some "underlying" type, for example inherent associated types, trait associated types, free type aliases (`type Foo = u32`), and opaque types (`-> impl RPIT`). We consider such types to be "aliases", alias types are represented by the [`TyKind::Alias`][tykind_alias] variant, with the kind of alias tracked by the [`AliasTyKind`][aliaskind] enum. + +Normalization is the process of taking these alias types and replacing them with the underlying type that they are equal to. For example given some type alias `type Foo = u32`, normalizing `Foo` would give `u32`. + +The concept of an alias is not unique to *types* and the concept also applies to constants/const generics. However, right now in the compiler we don't really treat const aliases as a "first class concept" so this chapter mostly discusses things in the context of types (even though the concepts transfer just fine). + +[tykind_alias]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/enum.TyKind.html#variant.Alias +[aliaskind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/enum.AliasTyKind.html + +### Rigid, Ambiguous and Unnormalized Aliases + +Aliases can either be "rigid", "ambiguous", or simply unnormalized. + +We consider types to be rigid if their "shape" isn't going to change, for example `Box` is rigid as no amount of normalization can turn a `Box` into a `u32`, whereas `<vec::IntoIter<u32> as Iterator>::Item` is not rigid as it can be normalized to `u32`. + +Aliases are rigid when we will never be able to normalize them further. A concrete example of a *rigid* alias would be `<T as Iterator>::Item` in an environment where there is no `T: Iterator<Item = ...>` bound, only a `T: Iterator` bound: +```rust +fn foo<T: Iterator>() { + // This alias is *rigid* + let _: <T as Iterator>::Item; +} + +fn bar<T: Iterator<Item = u32>>() { + // This alias is *not* rigid as it can be normalized to `u32` + let _: <T as Iterator>::Item; +} +``` + +When an alias can't yet be normalized but may wind up normalizable in the [current environment](./typing_parameter_envs.md), we consider it to be an "ambiguous" alias. This can occur when an alias contains inference variables which prevent being able to determine how the trait is implemented: +```rust +fn foo<T: Iterator, U: Iterator>() { + // This alias is considered to be "ambiguous" + let _: <_ as Iterator>::Item; +} +``` + +The reason we call them "ambiguous" aliases is because its *ambiguous* whether this is a rigid alias or not. + +The source of the `_: Iterator` trait impl is *ambiguous* (i.e. unknown), it could be some `impl Iterator for u32` or it could be some `T: Iterator` trait bound, we don't know yet. Depending on why `_: Iterator` holds the alias could be an unnormalized alias or it could be a rigid alias; it's *ambiguous* what kind of alias this is. + +Finally, an alias can just be unnormalized, `<Vec<u32> as IntoIterator>::Iter` is an unnormalized alias as it can already be normalized to `std::vec::IntoIter<u32>`, it just hasn't been done yet. + +--- + +It is worth noting that Free and Inherent aliases cannot be rigid or ambiguous as naming them also implies having resolved the definition of the alias, which specifies the underlying type of the alias. + +### Diverging Aliases + +An alias is considered to "diverge" if its definition does not specify an underlying non-alias type to normalize to. A concrete example of diverging aliases: +```rust +type Diverges = Diverges; + +trait Trait { + type DivergingAssoc; +} +impl Trait for () { + type DivergingAssoc = <() as Trait>::DivergingAssoc; +} +``` +In this example both `Diverges` and `DivergingAssoc` are "trivial" cases of diverging type aliases where they have been defined as being equal to themselves. There is no underlying type that `Diverges` can ever be normalized to. + +We generally try to error when diverging aliases are defined, but this is entirely a "best effort" check. In the previous example the definitions are "simple enough" to be detected and so errors are emitted. However, in more complex cases, or cases where only some instantiations of generic parameters would result in a diverging alias, we don't emit an error: +```rust +trait Trait { + type DivergingAssoc<U: Trait>; +} +impl<T: ?Sized> Trait for T { + // This alias always diverges but we don't emit an error because + // the compiler can't "see" that. + type DivergingAssoc<U: Trait> = <U as Trait>::DivergingAssoc<U>; +} +``` + +Ultimately this means that we have no guarantee that aliases in the type system are non-diverging. As aliases may only diverge for some specific generic arguments, it also means that we only know whether an alias diverges once it is fully concrete. This means that codegen/const-evaluation also has to handle diverging aliases: +```rust +trait Trait { + type Diverges<U: Trait>; +} +impl<T: ?Sized> Trait for T { + type Diverges<U: Trait> = <U as Trait>::Diverges<U>; +} + +fn foo<T: Trait>() { + let a: T::Diverges<T>; +} + +fn main() { + foo::<()>(); +} +``` +In this example we only encounter an error from the diverging alias during codegen of `foo::<()>`, if the call to `foo` is removed then no compilation error will be emitted. + +### Opaque Types + +Opaque types are a relatively special kind of alias, and are covered in their own chapter: [Opaque types](./opaque-types-type-alias-impl-trait.md). + +### Const Aliases + +Unlike type aliases, const aliases are not represented directly in the type system, instead const aliases are always an anonymous body containing a path expression to a const item. This means that the only "const alias" in the type system is an anonymous unevaluated const body. + +As such there is no `ConstKind::Alias(AliasCtKind::Projection/Inherent/Free, _)`, instead we only have `ConstKind::Unevaluated` which is used for representing anonymous constants. + +```rust +fn foo<const N: usize>() {} + +const FREE_CONST: usize = 1 + 1; + +fn bar() { + foo::<{ FREE_CONST }>(); + // The const arg is represented with some anonymous constant: + // ```pseudo-rust + // const ANON: usize = FREE_CONST; + // foo::<ConstKind::Unevaluated(DefId(ANON), [])>(); + // ``` +} +``` + +This is likely to change as const generics functionality is improved, for example `feature(associated_const_equality)` and `feature(min_generic_const_args)` both require handling const aliases similarly to types (without an anonymous constant wrapping all const args). + +## What is Normalization + +### Structural vs Deep normalization + +There are two forms of normalization, structural (sometimes called *shallow*) and deep. Structural normalization should be thought of as only normalizing the "outermost" part of a type. On the other hand deep normalization will normalize *all* aliases in a type. + +In practice structural normalization can result in more than just the outer layer of the type being normalized, but this behaviour should not be relied upon. Unnormalizable non-rigid aliases making use of bound variables (`for<'a>`) cannot be normalized by either kind of normalization. + +As an example: conceptually, structurally normalizing the type `Vec<<u8 as Identity>::Assoc>` would be a no-op, whereas deeply normalizing would give `Vec<u8>`. In practice even structural normalization would give `Vec<u8>`, though, again, this should not be relied upon. + +Changing the alias to use bound variables will result in different behaviour; `Vec<for<'a> fn(<&'a u8 as Identity>::Assoc)>` would result in no change when structurally normalized, but would result in `Vec<for<'a> fn(&'a u8)>` when deeply normalized. + +### Core normalization logic + +Structurally normalizing aliases is a little bit more nuanced than replacing the alias with whatever it is defined as being equal to in its definition; the result of normalizing an alias should either be a rigid type or an inference variable (which will later be inferred to a rigid type). To accomplish this we do two things: + +First, when normalizing an ambiguous alias it is normalized to an inference variable instead of leaving it as-is, this has two main effects: +- Even though an inference variable is not a rigid type, it will always wind up inferred *to* a rigid type so we ensure that the result of normalization will not need to be normalized again +- Inference variables are used in all cases where a type is non-rigid, allowing the rest of the compiler to not have to deal with *both* ambiguous aliases *and* inference variables + +Secondly, instead of having normalization directly return the type specified in the definition of the alias, we normalize the type first before returning it[^1]. We do this so that normalization is idempotent/callers do not need to run it in a loop. + +```rust +#![feature(lazy_type_alias)] + +type Foo<T: Iterator> = Bar<T>; +type Bar<T: Iterator> = <T as Iterator>::Item; + +fn foo() { + let a_: Foo<_>; +} +``` + +In this example: +- Normalizing `Foo<?x>` would result in `Bar<?x>`, except we want to normalize aliases in the type `Foo` is defined as equal to +- Normalizing `Bar<?x>` would result in `<?x as Iterator>::Item`, except, again, we want to normalize aliases in the type `Bar` is defined as equal to +- Normalizing `<?x as Iterator>::Item` results in some new inference variable `?y`, as `<?x as Iterator>::Item` is an ambiguous alias +- The final result is that normalizing `Foo<?x>` results in `?y` + +## How to normalize + +When interfacing with the type system it will often be the case that it's necessary to request a type be normalized. There are a number of different entry points to the underlying normalization logic and each entry point should only be used in specific parts of the compiler. + +An additional complication is that the compiler is currently undergoing a transition from the old trait solver to the new trait solver. As part of this transition our approach to normalization in the compiler has changed somewhat significantly, resulting in some normalization entry points being "old solver only" slated for removal in the long-term once the new solver has stabilized. + +Here is a rough overview of the different entry points to normalization in the compiler: +- `infcx.at.structurally_normalize` +- `infcx.at.(deeply_)?normalize` +- `infcx.query_normalize` +- `tcx.normalize_erasing_regions` +- `traits::normalize_with_depth(_to)` +- `EvalCtxt::structurally_normalize` + +### Outside of the trait solver + +The [`InferCtxt`][infcx] type exposes the "main" ways to normalize during analysis: [`normalize`][normalize], [`deeply_normalize`][deeply_normalize] and [`structurally_normalize`][structurally_normalize]. These functions are often wrapped and re-exposed on various `InferCtxt` wrapper types, such as [`FnCtxt`][fcx] or [`ObligationCtxt`][ocx] with minor API tweaks to handle some arguments or parts of the return type automatically. + +#### Structural `InferCtxt` normalization + +[`infcx.at.structurally_normalize`][structurally_normalize] exposes structural normalization that is able to handle inference variables and regions. It should generally be used whenever inspecting the kind of a type. + +Inside of HIR Typeck there is a related method of normalization- [`fcx.structurally_resolve`][structurally_resolve], which will error if the type being resolved is an unresolved inference variable. When the new solver is enabled it will also attempt to structurally normalize the type. + +Due to this there is a pattern in HIR typeck where a type is first normalized via `normalize` (only normalizing in the old solver), and then `structurally_resolve`'d (only normalizing in the new solver). This pattern should be preferred over calling `structurally_normalize` during HIR typeck as `structurally_resolve` will attempt to make inference progress by evaluating goals whereas `structurally_normalize` does not. + +#### Deep `InferCtxt` normalization + +##### `infcx.at.(deeply_)?normalize` + +There are two ways to deeply normalize with an `InferCtxt`, `normalize` and `deeply_normalize`. The reason for this is that `normalize` is a "legacy" normalization entry point used only by the old solver, whereas `deeply_normalize` is intended to be the long term way to deeply normalize. Both of these methods can handle regions. + +When the new solver is stabilized the `infcx.at.normalize` function will be removed and everything will have been migrated to the new deep or structural normalization methods. For this reason the `normalize` function is a no-op under the new solver, making it suitable only when the old solver needs normalization but the new solver does not. + +Using `deeply_normalize` will result in errors being emitted when encountering ambiguous aliases[^2] as it is not possible to support normalizing *all* ambiguous aliases to inference variables[^3]. `deeply_normalize` should generally only be used in cases where we do not expect to encounter ambiguous aliases, for example when working with types from item signatures. + +##### `infcx.query_normalize` + +[`infcx.query_normalize`][query_norm] is very rarely used, it has almost all the same restrictions as `normalize_erasing_regions` (cannot handle inference variables, no diagnostics support) with the main difference being that it retains lifetime information. For this reason `normalize_erasing_regions` is the better choice in almost all circumstances as it is more efficient due to caching lifetime-erased queries. + +In practice `query_normalize` is used for normalization in the borrow checker, and elsewhere as a performance optimization over `infcx.normalize`. Once the new solver is stabilized it is expected that `query_normalize` can be removed from the compiler as the new solvers normalization implementation should be performant enough for it to not be a performance regression. + +##### `tcx.normalize_erasing_regions` + +[`normalize_erasing_regions`][norm_erasing_regions] is generally used by parts of the compiler that are not doing type system analysis. This normalization entry point does not handle inference variables, lifetimes, or any diagnostics. Lints and codegen make heavy use of this entry point as they typically are working with fully inferred aliases that can be assumed to be well formed (or at least, are not responsible for erroring on). + +[query_norm]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/at/struct.At.html#method.query_normalize +[norm_erasing_regions]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#method.normalize_erasing_regions +[normalize]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/at/struct.At.html#method.normalize +[deeply_normalize]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/traits/normalize/trait.NormalizeExt.html#tymethod.deeply_normalize +[structurally_normalize]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/traits/trait.StructurallyNormalizeExt.html#tymethod.structurally_normalize_ty +[infcx]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/struct.InferCtxt.html +[fcx]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/fn_ctxt/struct.FnCtxt.html +[ocx]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/traits/struct.ObligationCtxt.html +[structurally_resolve]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/fn_ctxt/struct.FnCtxt.html#method.structurally_resolve_type + +### Inside of the trait solver + +[`traits::normalize_with_depth(_to)`][norm_with_depth] and [`EvalCtxt::structurally_normalize`][eval_ctxt_structural_norm] are only used by the internals of the trait solvers (old and new respectively). It is effectively a raw entry point to the internals of how normalization is implemented by each trait solver. Other normalization entry points cannot be used from within the internals of trait solving as it wouldn't handle goal cycles and recursion depth correctly. + +[norm_with_depth]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/traits/normalize/fn.normalize_with_depth.html +[eval_ctxt_structural_norm]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_next_trait_solver/solve/struct.EvalCtxt.html#method.structurally_normalize_term + +## When/Where to normalize (Old vs New solver) + +One of the big changes between the old and new solver is our approach to when we expect aliases to be normalized. + +### Old solver + +All types are expected to be normalized as soon as possible, so that all types encountered in the type system are either rigid or an inference variable (which will later be inferred to a rigid term). + +As a concrete example: equality of aliases is implemented by assuming they're rigid and recursively equating the generic arguments of the alias. + +### New solver + +It's expected that all types potentially contain ambiguous or unnormalized aliases. Whenever an operation is performed that requires aliases to be normalized, it's the responsibility of that logic to normalize the alias (this means that matching on `ty.kind()` pretty much always has to structurally normalize first). + +As a concrete example: equality of aliases is implemented by a custom goal kind ([`PredicateKind::AliasRelate`][aliasrelate]) so that it can handle normalization of the aliases itself instead of assuming all alias types being equated are rigid. + +Despite this approach we still deeply normalize during [writeback][writeback] for performance/simplicity, so that types in the MIR can still be assumed to have been deeply normalized. + +[aliasrelate]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.PredicateKind.html#variant.AliasRelate +[writeback]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/writeback/index.html + +--- + +There were a few main issues with the old solver's approach to normalization that motivated changing things in the new solver: + +### Missing normalization calls + +It was a frequent occurrence that normalization calls would be missing, resulting in passing unnormalized types to APIs expecting everything to already be normalized. Treating ambiguous or unnormalized aliases as rigid would result in all sorts of weird errors from aliases not being considered equal to one another, or surprising inference guidance from equating unnormalized aliases' generic arguments. + +### Normalizing parameter environments + +Another problem was that it was not possible to normalize `ParamEnv`s correctly in the old solver as normalization itself would expect a normalized `ParamEnv` in order to give correct results. See the chapter on `ParamEnv`s for more information: [`Typing/ParamEnv`s: Normalizing all bounds](./typing_parameter_envs.md#normalizing-all-bounds) + +### Unnormalizable non-rigid aliases in higher ranked types + +Given a type such as `for<'a> fn(<?x as Trait<'a>::Assoc>)`, it is not possible to correctly handle this with the old solver's approach to normalization. + +If we were to normalize it to `for<'a> fn(?y)` and register a goal to normalize `for<'a> <?x as Trait<'a>>::Assoc -> ?y`, this would result in errors in cases where `<?x as Trait<'a>>::Assoc` normalized to `&'a u32`. The inference variable `?y` would be in a lower [universe][universes] than the placeholders made when instantiating the `for<'a>` binder. + +Leaving the alias unnormalized would also be wrong as the old solver expects all aliases to be rigid. This was a soundness bug before the new solver was stabilized in coherence: [relating projection substs is unsound during coherence](https://github.com/rust-lang/rust/issues/102048). + +Ultimately this means that it is not always possible to ensure all aliases inside of a value are rigid. + +[universes]: https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference/placeholders_and_universes.html#what-is-a-universe +[deeply_normalize]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/traits/normalize/trait.NormalizeExt.html#tymethod.deeply_normalize + +## Handling uses of diverging aliases + +Diverging aliases, like ambiguous aliases, are normalized to inference variables. As normalizing diverging aliases results in trait solver cycles, it always results in an error in the old solver. In the new solver it only results in an error if we wind up requiring all goals to hold in the current context. E.g. normalizing diverging aliases during HIR typeck will result in an error in both solvers. + +Alias well formedness doesn't require that the alias doesn't diverge[^4], this means that checking an alias is well formed isn't sufficient to cause an error to be emitted for diverging aliases; actually attempting to normalize the alias is required. + +Erroring on diverging aliases being a side effect of normalization means that it is very *arbitrary* whether we actually emit an error, it also differs between the old and new solver as we now normalize in less places. + +An example of the ad-hoc nature of erroring on diverging aliases causing "problems": +```rust +trait Trait { + type Diverges<D: Trait>; +} + +impl<T> Trait for T { + type Diverges<D: Trait> = D::Diverges<D>; +} + +struct Bar<T: ?Sized = <u8 as Trait>::Diverges<u8>>(Box<T>); +``` + +In this example a diverging alias is used but we happen to not emit an error as we never explicitly normalize the defaults of generic parameters. If the `?Sized` opt out is removed then an error is emitted because we wind up happening to normalize a `<u8 as Trait>::Diverges<u8>: Sized` goal which as a side effect results in erroring about the diverging alias. + +Const aliases differ from type aliases a bit here; well formedness of const aliases requires that they can be successfully evaluated (via [`ConstEvaluatable`][const_evaluatable] goals). This means that simply checking well formedness of const arguments is sufficient to error if they would fail to evaluate. It is somewhat unclear whether it would make sense to adopt this for type aliases too or if const aliases should stop requiring this for well formedness[^5]. + +[^1]: In the new solver this is done implicitly + +[^2]: There is a subtle difference in how ambiguous aliases in binders are handled between old and new solver. In the old solver we fail to error on some ambiguous aliases inside of higher ranked types whereas the new solver correctly errors. + +[^3]: Ambiguous aliases inside of binders cannot be normalized to inference variables, this will be covered more later. + +[^4]: As checking aliases are non-diverging cannot be done until they are fully concrete, this would either imply that we cant check aliases are well formed before codegen/const-evaluation or that aliases would go from being well-formed to not well-formed after monomorphization. + +[^5]: Const aliases certainly wouldn't be *less* sound than type aliases if we stopped doing this + +[const_evaluatable]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.ClauseKind.html#variant.ConstEvaluatable \ No newline at end of file diff --git a/src/doc/rustc-dev-guide/src/solve/normalization.md b/src/doc/rustc-dev-guide/src/solve/normalization.md deleted file mode 100644 index 99dc20c46b5..00000000000 --- a/src/doc/rustc-dev-guide/src/solve/normalization.md +++ /dev/null @@ -1,127 +0,0 @@ -# Normalization in the new solver - -> FIXME: Normalization has been changed significantly since this chapter was written. - -With the new solver we've made some fairly significant changes to normalization when compared -to the existing implementation. - -We now differentiate between "one-step normalization", "structural normalization" and -"deep normalization". - -## One-step normalization - -One-step normalization is implemented via `NormalizesTo` goals. Unlike other goals -in the trait solver, `NormalizesTo` always expects the term to be an unconstrained -inference variable[^opaques]. Think of it as a function, taking an alias as input -and returning its underlying value. If the alias is rigid, `NormalizesTo` fails and -returns `NoSolution`. This is the case for `<T as Trait>::Assoc` if there's a `T: Trait` -where-bound and for opaque types with `Reveal::UserFacing` unless they are in the -defining scope. We must not treat any aliases as rigid in coherence. - -The underlying value may itself be an unnormalized alias, e.g. -`NormalizesTo(<<() as Id>::This as Id>::This)` only returns `<() as Id>::This`, -even though that alias can be further normalized to `()`. As the term is -always an unconstrained inference variable, the expected term cannot influence -normalization, see [trait-system-refactor-initiative#22] for more. - -Only ever computing `NormalizesTo` goals with an unconstrained inference variable -requires special solver support. It is only used by `AliasRelate` goals and pending -`NormalizesTo` goals are tracked separately from other goals: [source][try-eval-norm]. -As the expected term is always erased in `NormalizesTo`, we have to return its -ambiguous nested goals to its caller as not doing so weakens inference. See -[#122687] for more details. - -[trait-system-refactor-initiative#22]: https://github.com/rust-lang/trait-system-refactor-initiative/issues/22 -[try-eval-norm]: https://github.com/rust-lang/rust/blob/2627e9f3012a97d3136b3e11bf6bd0853c38a534/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs#L523-L537 -[#122687]: https://github.com/rust-lang/rust/pull/122687 - -## `AliasRelate` and structural normalization - -We structurally normalize an alias by applying one-step normalization until -we end up with a rigid alias, ambiguity, or overflow. This is done by repeatedly -evaluating `NormalizesTo` goals inside of a snapshot: [source][structural_norm]. - -`AliasRelate(lhs, rhs)` is implemented by first structurally normalizing both the -`lhs` and the `rhs` and then relating the resulting rigid types (or inference -variables). Importantly, if `lhs` or `rhs` ends up as an alias, this alias can -now be treated as rigid and gets unified without emitting a nested `AliasRelate` -goal: [source][structural-relate]. - -This means that `AliasRelate` with an unconstrained `rhs` ends up functioning -similar to `NormalizesTo`, acting as a function which fully normalizes `lhs` -before assigning the resulting rigid type to an inference variable. This is used by -`fn structurally_normalize_ty` both [inside] and [outside] of the trait solver. -This has to be used whenever we match on the value of some type, both inside -and outside of the trait solver. - -<!-- -FIXME: structure, maybe we should have an "alias handling" chapter instead as -talking about normalization without explaining that doesn't make too much -sense. - -FIXME: it is likely that this will subtly change again by mostly moving structural -normalization into `NormalizesTo`. ---> - -[structural_norm]: https://github.com/rust-lang/rust/blob/2627e9f3012a97d3136b3e11bf6bd0853c38a534/compiler/rustc_trait_selection/src/solve/alias_relate.rs#L140-L175 -[structural-relate]: https://github.com/rust-lang/rust/blob/a0569fa8f91b5271e92d2f73fd252de7d3d05b9c/compiler/rustc_trait_selection/src/solve/alias_relate.rs#L88-L107 -[inside]: https://github.com/rust-lang/rust/blob/a0569fa8f91b5271e92d2f73fd252de7d3d05b9c/compiler/rustc_trait_selection/src/solve/mod.rs#L278-L299 -[outside]: https://github.com/rust-lang/rust/blob/a0569fa8f91b5271e92d2f73fd252de7d3d05b9c/compiler/rustc_trait_selection/src/traits/structural_normalize.rs#L17-L48 - -## Deep normalization - -By walking over a type, and using `fn structurally_normalize_ty` for each encountered -alias, it is possible to deeply normalize a type, normalizing all aliases as much as -possible. However, this only works for aliases referencing bound variables if they are -not ambiguous as we're unable to replace the alias with a corresponding inference -variable without leaking universes. - -<!-- -FIXME: we previously had to also be careful about instantiating the new inference -variable with another normalizeable alias. Due to our recent changes to generalization, -this should not be the case anymore. Equating an inference variable with an alias -now always uses `AliasRelate` to fully normalize the alias before instantiating the -inference variable: [source][generalize-no-alias] ---> - -[generalize-no-alias]: https://github.com/rust-lang/rust/blob/a0569fa8f91b5271e92d2f73fd252de7d3d05b9c/compiler/rustc_infer/src/infer/relate/generalize.rs#L353-L358 - -## Outside of the trait solver - -The core type system - relating types and trait solving - will not need deep -normalization with the new solver. There are still some areas which depend on it. -For these areas there is the function `At::deeply_normalize`. Without additional -trait solver support deep normalization does not always work in case of ambiguity. -Luckily deep normalization is currently only necessary in places where there is no ambiguity. -`At::deeply_normalize` immediately fails if there's ambiguity. - -If we only care about the outermost layer of types, we instead use -`At::structurally_normalize` or `FnCtxt::(try_)structurally_resolve_type`. -Unlike `At::deeply_normalize`, structural normalization is also used in cases where we -have to handle ambiguity. - -Because this may result in behavior changes depending on how the trait solver handles -ambiguity, it is safer to also require full normalization there. This happens in -`FnCtxt::structurally_resolve_type` which always emits a hard error if the self type ends -up as an inference variable. There are some existing places which have a fallback for -inference variables instead. These places use `try_structurally_resolve_type` instead. - -## Why deep normalization with ambiguity is hard - -Fully correct deep normalization is very challenging, especially with the new solver -given that we do not want to deeply normalize inside of the solver. Mostly deeply normalizing -but sometimes failing to do so is bound to cause very hard to minimize and understand bugs. -If possible, avoiding any reliance on deep normalization entirely therefore feels preferable. - -If the solver itself does not deeply normalize, any inference constraints returned by the -solver would require normalization. Handling this correctly is ugly. This also means that -we change goals we provide to the trait solver by "normalizing away" some projections. - -The way we (mostly) guarantee deep normalization with the old solver is by eagerly replacing -the projection with an inference variable and emitting a nested `Projection` goal. This works -as `Projection` goals in the old solver deeply normalize. Unless we add another `PredicateKind` -for deep normalization to the new solver we cannot emulate this behavior. This does not work -for projections with bound variables, sometimes leaving them unnormalized. An approach which -also supports projections with bound variables will be even more involved. - -[^opaques]: opaque types are currently handled a bit differently. this may change in the future diff --git a/src/doc/rustc-dev-guide/src/solve/significant-changes.md b/src/doc/rustc-dev-guide/src/solve/significant-changes.md index c82b5d46896..eac8f0318fb 100644 --- a/src/doc/rustc-dev-guide/src/solve/significant-changes.md +++ b/src/doc/rustc-dev-guide/src/solve/significant-changes.md @@ -106,4 +106,4 @@ their ambiguous nested goals are returned to the caller which then evaluates the See [#122687] for more details. [#122687]: https://github.com/rust-lang/rust/pull/122687 -[normalization]: ./normalization.md +[normalization]: ../normalization.md diff --git a/src/doc/rustc-dev-guide/triagebot.toml b/src/doc/rustc-dev-guide/triagebot.toml index 6232dbf05fd..53fa72469fd 100644 --- a/src/doc/rustc-dev-guide/triagebot.toml +++ b/src/doc/rustc-dev-guide/triagebot.toml @@ -13,3 +13,6 @@ allow-unauthenticated = [ # Automatically close and reopen PRs made by bots to run CI on them [bot-pull-requests] + +[behind-upstream] +days-threshold = 7 \ No newline at end of file diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index 4194abc8d57..d4fbfb12582 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -301,8 +301,6 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn let filename = FileName::anon_source_code(&wrapped_source); - // Any errors in parsing should also appear when the doctest is compiled for real, so just - // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr. let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); let fallback_bundle = rustc_errors::fallback_fluent_bundle( rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), @@ -311,7 +309,8 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn info.supports_color = HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle.clone()) .supports_color(); - + // Any errors in parsing should also appear when the doctest is compiled for real, so just + // send all the errors that the parser emits directly into a `Sink` instead of stderr. let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle); // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser @@ -339,9 +338,6 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn *prev_span_hi = hi; } - // Recurse through functions body. It is necessary because the doctest source code is - // wrapped in a function to limit the number of AST errors. If we don't recurse into - // functions, we would thing all top-level items (so basically nothing). fn check_item(item: &ast::Item, info: &mut ParseSourceInfo, crate_name: &Option<&str>) -> bool { let mut is_extern_crate = false; if !info.has_global_allocator @@ -351,8 +347,6 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn } match item.kind { ast::ItemKind::Fn(ref fn_item) if !info.has_main_fn => { - // We only push if it's the top item because otherwise, we would duplicate - // its content since the top-level item was already added. if fn_item.ident.name == sym::main { info.has_main_fn = true; } @@ -412,44 +406,41 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn let mut is_extern_crate = false; match stmt.kind { StmtKind::Item(ref item) => { - is_extern_crate = check_item(&item, &mut info, crate_name); - } - StmtKind::Expr(ref expr) => { - if matches!(expr.kind, ast::ExprKind::Err(_)) { - reset_error_count(&psess); - return Err(()); - } - has_non_items = true; + is_extern_crate = check_item(item, &mut info, crate_name); } // We assume that the macro calls will expand to item(s) even though they could - // expand to statements and expressions. And the simple fact that we're trying - // to retrieve a `main` function inside it is a terrible idea. + // expand to statements and expressions. StmtKind::MacCall(ref mac_call) => { - if info.has_main_fn { - continue; - } - let mut iter = mac_call.mac.args.tokens.iter(); - - while let Some(token) = iter.next() { - if let TokenTree::Token(token, _) = token - && let TokenKind::Ident(name, _) = token.kind - && name == kw::Fn - && let Some(TokenTree::Token(fn_token, _)) = iter.peek() - && let TokenKind::Ident(fn_name, _) = fn_token.kind - && fn_name == sym::main - && let Some(TokenTree::Delimited(_, _, Delimiter::Parenthesis, _)) = { - iter.next(); - iter.peek() + if !info.has_main_fn { + // For backward compatibility, we look for the token sequence `fn main(…)` + // in the macro input (!) to crudely detect main functions "masked by a + // wrapper macro". For the record, this is a horrible heuristic! + // See <https://github.com/rust-lang/rust/issues/56898>. + let mut iter = mac_call.mac.args.tokens.iter(); + while let Some(token) = iter.next() { + if let TokenTree::Token(token, _) = token + && let TokenKind::Ident(kw::Fn, _) = token.kind + && let Some(TokenTree::Token(ident, _)) = iter.peek() + && let TokenKind::Ident(sym::main, _) = ident.kind + && let Some(TokenTree::Delimited(.., Delimiter::Parenthesis, _)) = { + iter.next(); + iter.peek() + } + { + info.has_main_fn = true; + break; } - { - info.has_main_fn = true; - break; } } } - _ => { + StmtKind::Expr(ref expr) => { + if matches!(expr.kind, ast::ExprKind::Err(_)) { + reset_error_count(&psess); + return Err(()); + } has_non_items = true; } + StmtKind::Let(_) | StmtKind::Semi(_) | StmtKind::Empty => has_non_items = true, } // Weirdly enough, the `Stmt` span doesn't include its attributes, so we need to diff --git a/src/tools/clippy/clippy_lints/src/assigning_clones.rs b/src/tools/clippy/clippy_lints/src/assigning_clones.rs index 9acff676d4f..8b8b42bbf72 100644 --- a/src/tools/clippy/clippy_lints/src/assigning_clones.rs +++ b/src/tools/clippy/clippy_lints/src/assigning_clones.rs @@ -3,14 +3,13 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::mir::{PossibleBorrowerMap, enclosing_mir}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::sugg::Sugg; -use clippy_utils::{is_diag_trait_item, is_in_test, last_path_segment, local_is_initialized, path_to_local}; +use clippy_utils::{is_diag_trait_item, is_in_test, last_path_segment, local_is_initialized, path_to_local, sym}; use rustc_errors::Applicability; use rustc_hir::{self as hir, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir; use rustc_middle::ty::{self, Instance, Mutability}; use rustc_session::impl_lint_pass; -use rustc_span::symbol::sym; use rustc_span::{Span, SyntaxContext}; declare_clippy_lint! { @@ -86,9 +85,9 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones { && ctxt.is_root() && let which_trait = match fn_name { sym::clone if is_diag_trait_item(cx, fn_id, sym::Clone) => CloneTrait::Clone, - _ if fn_name.as_str() == "to_owned" - && is_diag_trait_item(cx, fn_id, sym::ToOwned) - && self.msrv.meets(cx, msrvs::CLONE_INTO) => + sym::to_owned + if is_diag_trait_item(cx, fn_id, sym::ToOwned) + && self.msrv.meets(cx, msrvs::CLONE_INTO) => { CloneTrait::ToOwned }, @@ -112,7 +111,7 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones { && resolved_assoc_items.in_definition_order().any(|assoc| match which_trait { CloneTrait::Clone => assoc.name() == sym::clone_from, - CloneTrait::ToOwned => assoc.name().as_str() == "clone_into", + CloneTrait::ToOwned => assoc.name() == sym::clone_into, } ) && !clone_source_borrows_from_dest(cx, lhs, rhs.span) diff --git a/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs b/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs index 457692ed5dc..4d64eec25d2 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs @@ -1,17 +1,15 @@ use super::BLANKET_CLIPPY_RESTRICTION_LINTS; use super::utils::extract_clippy_lint; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; +use clippy_utils::sym; use rustc_ast::MetaItemInner; use rustc_lint::{EarlyContext, Level, LintContext}; +use rustc_span::DUMMY_SP; use rustc_span::symbol::Symbol; -use rustc_span::{DUMMY_SP, sym}; pub(super) fn check(cx: &EarlyContext<'_>, name: Symbol, items: &[MetaItemInner]) { for lint in items { - if let Some(lint_name) = extract_clippy_lint(lint) - && lint_name.as_str() == "restriction" - && name != sym::allow - { + if name != sym::allow && extract_clippy_lint(lint) == Some(sym::restriction) { span_lint_and_help( cx, BLANKET_CLIPPY_RESTRICTION_LINTS, diff --git a/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs b/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs index 7fab97d3ea1..0edb50be8c7 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs @@ -73,7 +73,7 @@ fn check_deprecated_cfg_recursively(cx: &EarlyContext<'_>, attr: &rustc_ast::Met } fn check_cargo_clippy_attr(cx: &EarlyContext<'_>, item: &rustc_ast::MetaItem) { - if item.has_name(sym::feature) && item.value_str().is_some_and(|v| v.as_str() == "cargo-clippy") { + if item.has_name(sym::feature) && item.value_str() == Some(sym::cargo_clippy) { span_lint_and_sugg( cx, DEPRECATED_CLIPPY_CFG_ATTR, diff --git a/src/tools/clippy/clippy_lints/src/attrs/deprecated_semver.rs b/src/tools/clippy/clippy_lints/src/attrs/deprecated_semver.rs index 50943b36809..bd6459d6f9d 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/deprecated_semver.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/deprecated_semver.rs @@ -1,5 +1,6 @@ use super::DEPRECATED_SEMVER; use clippy_utils::diagnostics::span_lint; +use clippy_utils::sym; use rustc_ast::{LitKind, MetaItemLit}; use rustc_lint::EarlyContext; use rustc_span::Span; @@ -7,7 +8,7 @@ use semver::Version; pub(super) fn check(cx: &EarlyContext<'_>, span: Span, lit: &MetaItemLit) { if let LitKind::Str(is, _) = lit.kind - && (is.as_str() == "TBD" || Version::parse(is.as_str()).is_ok()) + && (is == sym::TBD || Version::parse(is.as_str()).is_ok()) { return; } diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs b/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs index 164d3540253..ba31a51f738 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::sugg::Sugg; +use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; @@ -19,7 +20,7 @@ pub(super) fn check( if let ty::Int(from) = cast_from.kind() && let ty::Uint(to) = cast_to.kind() && let ExprKind::MethodCall(method_path, receiver, [], _) = cast_expr.kind - && method_path.ident.name.as_str() == "abs" + && method_path.ident.name == sym::abs && msrv.meets(cx, msrvs::UNSIGNED_ABS) { let span = if from.bit_width() == to.bit_width() { 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 8742f5f1a0e..e92879b853d 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 @@ -1,9 +1,9 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::expr_or_init; use clippy_utils::source::snippet; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{get_discriminant_value, is_isize_or_usize}; +use clippy_utils::{expr_or_init, sym}; use rustc_abi::IntegerType; use rustc_errors::{Applicability, Diag}; use rustc_hir::def::{DefKind, Res}; @@ -73,7 +73,7 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b nbits }, ExprKind::MethodCall(method, _value, [], _) => { - if method.ident.name.as_str() == "signum" { + if method.ident.name == sym::signum { 0 // do not lint if cast comes from a `signum` function } else { nbits diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs index 3fca0f89707..01020f3eee2 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -1,11 +1,10 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_c_void; -use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant}; +use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, sym}; use rustc_hir::{Expr, ExprKind, GenericArg}; use rustc_lint::LateContext; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Ty}; -use rustc_span::sym; use super::CAST_PTR_ALIGNMENT; @@ -20,7 +19,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { ); lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); } else if let ExprKind::MethodCall(method_path, self_arg, [], _) = &expr.kind - && method_path.ident.name.as_str() == "cast" + && method_path.ident.name == sym::cast && let Some(generic_args) = method_path.args && let [GenericArg::Type(cast_to)] = generic_args.args // There probably is no obvious reason to do this, just to be consistent with `as` cases. diff --git a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs index ae994e94a32..8e8c55cf383 100644 --- a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs +++ b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs @@ -8,7 +8,9 @@ use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind, Lit, Node, Path, QPath, TyKind, UnOp}; use rustc_lint::{LateContext, LintContext}; +use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{self, FloatTy, InferTy, Ty}; +use rustc_span::{Symbol, sym}; use std::ops::ControlFlow; use super::UNNECESSARY_CAST; @@ -142,6 +144,33 @@ pub(super) fn check<'tcx>( } if cast_from.kind() == cast_to.kind() && !expr.span.in_external_macro(cx.sess().source_map()) { + enum MaybeParenOrBlock { + Paren, + Block, + Nothing, + } + + fn is_borrow_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + matches!(expr.kind, ExprKind::AddrOf(..)) + || cx + .typeck_results() + .expr_adjustments(expr) + .first() + .is_some_and(|adj| matches!(adj.kind, Adjust::Borrow(_))) + } + + fn is_in_allowed_macro(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + const ALLOWED_MACROS: &[Symbol] = &[ + sym::format_args_macro, + sym::assert_eq_macro, + sym::debug_assert_eq_macro, + sym::assert_ne_macro, + sym::debug_assert_ne_macro, + ]; + matches!(expr.span.ctxt().outer_expn_data().macro_def_id, Some(def_id) if + cx.tcx.get_diagnostic_name(def_id).is_some_and(|sym| ALLOWED_MACROS.contains(&sym))) + } + if let Some(id) = path_to_local(cast_expr) && !cx.tcx.hir_span(id).eq_ctxt(cast_expr.span) { @@ -150,15 +179,15 @@ pub(super) fn check<'tcx>( return false; } - // If the whole cast expression is a unary expression (`(*x as T)`) or an addressof - // expression (`(&x as T)`), then not surrounding the suggestion into a block risks us - // changing the precedence of operators if the cast expression is followed by an operation - // with higher precedence than the unary operator (`(*x as T).foo()` would become - // `*x.foo()`, which changes what the `*` applies on). - // The same is true if the expression encompassing the cast expression is a unary - // expression or an addressof expression. - let needs_block = matches!(cast_expr.kind, ExprKind::Unary(..) | ExprKind::AddrOf(..)) - || get_parent_expr(cx, expr).is_some_and(|e| matches!(e.kind, ExprKind::Unary(..) | ExprKind::AddrOf(..))); + // Changing `&(x as i32)` to `&x` would change the meaning of the code because the previous creates + // a reference to the temporary while the latter creates a reference to the original value. + let surrounding = match cx.tcx.parent_hir_node(expr.hir_id) { + Node::Expr(parent) if is_borrow_expr(cx, parent) && !is_in_allowed_macro(cx, parent) => { + MaybeParenOrBlock::Block + }, + Node::Expr(parent) if cast_expr.precedence() < parent.precedence() => MaybeParenOrBlock::Paren, + _ => MaybeParenOrBlock::Nothing, + }; span_lint_and_sugg( cx, @@ -166,10 +195,10 @@ pub(super) fn check<'tcx>( expr.span, format!("casting to the same type is unnecessary (`{cast_from}` -> `{cast_to}`)"), "try", - if needs_block { - format!("{{ {cast_str} }}") - } else { - cast_str + match surrounding { + MaybeParenOrBlock::Paren => format!("({cast_str})"), + MaybeParenOrBlock::Block => format!("{{ {cast_str} }}"), + MaybeParenOrBlock::Nothing => cast_str, }, Applicability::MachineApplicable, ); diff --git a/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs b/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs index c2aac7ca090..19f62e8bf79 100644 --- a/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs +++ b/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs @@ -5,8 +5,8 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::Span; use rustc_span::symbol::sym; +use rustc_span::{Span, kw}; declare_clippy_lint! { /// ### What it does @@ -105,12 +105,11 @@ fn contains_unhygienic_crate_reference(tts: &TokenStream) -> Option<Span> { fn is_crate_keyword(tt: &TokenTree) -> Option<Span> { if let TokenTree::Token( Token { - kind: TokenKind::Ident(symbol, _), + kind: TokenKind::Ident(kw::Crate, _), span, }, _, ) = tt - && symbol.as_str() == "crate" { Some(*span) } else { diff --git a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs index 3afb687040f..72f5eaf8a4b 100644 --- a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs +++ b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs @@ -68,6 +68,38 @@ fn is_structural_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: T } } +/// Check if the pattern has any type mismatch that would prevent it from being used in an equality +/// check. This can happen if the expr has a reference type and the corresponding pattern is a +/// literal. +fn contains_type_mismatch(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { + let mut result = false; + pat.walk(|p| { + if result { + return false; + } + + if p.span.in_external_macro(cx.sess().source_map()) { + return true; + } + + let adjust_pat = match p.kind { + PatKind::Or([p, ..]) => p, + _ => p, + }; + + if let Some(adjustments) = cx.typeck_results().pat_adjustments().get(adjust_pat.hir_id) + && adjustments.first().is_some_and(|first| first.source.is_ref()) + { + result = true; + return false; + } + + true + }); + + result +} + impl<'tcx> LateLintPass<'tcx> for PatternEquality { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let ExprKind::Let(let_expr) = expr.kind @@ -78,7 +110,7 @@ impl<'tcx> LateLintPass<'tcx> for PatternEquality { let pat_ty = cx.typeck_results().pat_ty(let_expr.pat); let mut applicability = Applicability::MachineApplicable; - if is_structural_partial_eq(cx, exp_ty, pat_ty) { + if is_structural_partial_eq(cx, exp_ty, pat_ty) && !contains_type_mismatch(cx, let_expr.pat) { let pat_str = match let_expr.pat.kind { PatKind::Struct(..) => format!( "({})", 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 d5f0659f842..553a00ed868 100644 --- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs @@ -3,7 +3,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::{ eq_expr_value, get_parent_expr, higher, is_in_const_context, is_inherent_method_call, is_no_std_crate, - numeric_literal, peel_blocks, sugg, + numeric_literal, peel_blocks, sugg, sym, }; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp}; @@ -435,7 +435,7 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { rhs, ) = expr.kind && let ExprKind::MethodCall(path, self_arg, [], _) = &lhs.kind - && path.ident.name.as_str() == "exp" + && path.ident.name == sym::exp && cx.typeck_results().expr_ty(lhs).is_floating_point() && let Some(value) = ConstEvalCtxt::new(cx).eval(rhs) && (F32(1.0) == value || F64(1.0) == value) diff --git a/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs b/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs index c8828c93615..5e2e2c9dbf7 100644 --- a/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs @@ -1,12 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::path_def_id; use clippy_utils::ty::is_c_void; +use clippy_utils::{path_def_id, sym}; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -41,7 +40,7 @@ impl LateLintPass<'_> for FromRawWithVoidPtr { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::Call(box_from_raw, [arg]) = expr.kind && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_from_raw.kind - && seg.ident.name.as_str() == "from_raw" + && seg.ident.name == sym::from_raw && let Some(type_str) = path_def_id(cx, ty).and_then(|id| def_id_matches_type(cx, id)) && let arg_kind = cx.typeck_results().expr_ty(arg).kind() && let ty::RawPtr(ty, _) = arg_kind diff --git a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs index 25b087e8484..b816963cc82 100644 --- a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs +++ b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs @@ -1,13 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; -use clippy_utils::{is_in_const_context, is_integer_literal}; +use clippy_utils::{is_in_const_context, is_integer_literal, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, PrimTy, QPath, TyKind, def}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; use rustc_session::declare_lint_pass; -use rustc_span::symbol::sym; declare_clippy_lint! { /// ### What it does @@ -53,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { // check if the second part of the path indeed calls the associated // function `from_str_radix` - && pathseg.ident.name.as_str() == "from_str_radix" + && pathseg.ident.name == sym::from_str_radix // check if the first part of the path is some integer primitive && let TyKind::Path(ty_qpath) = &ty.kind diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs index d2545e57652..4c17834c3ad 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs @@ -10,10 +10,10 @@ use rustc_middle::hir::nested_filter; use rustc_middle::ty::{Ty, TypeckResults}; use rustc_session::declare_lint_pass; use rustc_span::Span; -use rustc_span::symbol::sym; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{IntoSpan, SpanRangeExt, snippet}; +use clippy_utils::sym; use clippy_utils::ty::is_type_diagnostic_item; declare_clippy_lint! { @@ -326,6 +326,7 @@ impl<'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'_, '_, 'tcx> { fn visit_expr(&mut self, e: &'tcx Expr<'_>) { if let ExprKind::Call(fun, args) = e.kind && let ExprKind::Path(QPath::TypeRelative(ty, method)) = fun.kind + && matches!(method.ident.name, sym::new | sym::with_capacity) && let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind && let Some(ty_did) = ty_path.res.opt_def_id() { @@ -333,10 +334,11 @@ impl<'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'_, '_, 'tcx> { return; } - if self.cx.tcx.is_diagnostic_item(sym::HashMap, ty_did) { - if method.ident.name == sym::new { + match (self.cx.tcx.get_diagnostic_name(ty_did), method.ident.name) { + (Some(sym::HashMap), sym::new) => { self.suggestions.insert(e.span, "HashMap::default()".to_string()); - } else if method.ident.name.as_str() == "with_capacity" { + }, + (Some(sym::HashMap), sym::with_capacity) => { self.suggestions.insert( e.span, format!( @@ -344,11 +346,11 @@ impl<'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'_, '_, 'tcx> { snippet(self.cx, args[0].span, "capacity"), ), ); - } - } else if self.cx.tcx.is_diagnostic_item(sym::HashSet, ty_did) { - if method.ident.name == sym::new { + }, + (Some(sym::HashSet), sym::new) => { self.suggestions.insert(e.span, "HashSet::default()".to_string()); - } else if method.ident.name.as_str() == "with_capacity" { + }, + (Some(sym::HashSet), sym::with_capacity) => { self.suggestions.insert( e.span, format!( @@ -356,7 +358,8 @@ impl<'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'_, '_, 'tcx> { snippet(self.cx, args[0].span, "capacity"), ), ); - } + }, + _ => {}, } } diff --git a/src/tools/clippy/clippy_lints/src/infinite_iter.rs b/src/tools/clippy/clippy_lints/src/infinite_iter.rs index 427a1f82555..c4e10837bf1 100644 --- a/src/tools/clippy/clippy_lints/src/infinite_iter.rs +++ b/src/tools/clippy/clippy_lints/src/infinite_iter.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::higher; use clippy_utils::ty::{get_type_diagnostic_name, implements_trait}; +use clippy_utils::{higher, sym}; use rustc_hir::{BorrowKind, Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::symbol::sym; declare_clippy_lint! { /// ### What it does @@ -156,7 +155,7 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { .and(cap); } } - if method.ident.name.as_str() == "flat_map" + if method.ident.name == sym::flat_map && args.len() == 1 && let ExprKind::Closure(&Closure { body, .. }) = args[0].kind { @@ -224,7 +223,7 @@ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { return MaybeInfinite.and(is_infinite(cx, receiver)); } } - if method.ident.name.as_str() == "last" && args.is_empty() { + if method.ident.name == sym::last && args.is_empty() { let not_double_ended = cx .tcx .get_diagnostic_item(sym::DoubleEndedIterator) @@ -232,7 +231,7 @@ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { if not_double_ended { return is_infinite(cx, receiver); } - } else if method.ident.name.as_str() == "collect" { + } else if method.ident.name == sym::collect { let ty = cx.typeck_results().expr_ty(expr); if matches!( get_type_diagnostic_name(cx, ty), diff --git a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs index 173232c511a..900b20aa9cf 100644 --- a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs +++ b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs @@ -1,14 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::get_parent_as_impl; use clippy_utils::source::snippet; use clippy_utils::ty::{deref_chain, get_adt_inherent_method, implements_trait, make_normalized_projection}; +use clippy_utils::{get_parent_as_impl, sym}; use rustc_ast::Mutability; use rustc_errors::Applicability; use rustc_hir::{FnRetTy, ImplItemKind, ImplicitSelfKind, ItemKind, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::{self, Ty}; use rustc_session::declare_lint_pass; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -141,7 +140,7 @@ impl LateLintPass<'_> for IterWithoutIntoIter { ty.peel_refs().is_slice() || get_adt_inherent_method(cx, ty, expected_method_name).is_some() }) && let Some(iter_assoc_span) = imp.items.iter().find_map(|item| { - if item.ident.name.as_str() == "IntoIter" { + if item.ident.name == sym::IntoIter { Some(cx.tcx.hir_impl_item(item.id).expect_type().span) } else { None diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index 8c71d34c95f..aded31971ce 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -2,7 +2,9 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_the use clippy_utils::source::{SpanRangeExt, snippet_with_context}; use clippy_utils::sugg::{Sugg, has_enclosing_paren}; use clippy_utils::ty::implements_trait; -use clippy_utils::{fulfill_or_allowed, get_item_name, get_parent_as_impl, is_trait_method, peel_ref_operators, sym}; +use clippy_utils::{ + fulfill_or_allowed, get_parent_as_impl, is_trait_method, parent_item_name, peel_ref_operators, sym, +}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -533,9 +535,7 @@ fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_> if let (&ExprKind::MethodCall(method_path, receiver, [], _), ExprKind::Lit(lit)) = (&method.kind, &lit.kind) { // check if we are in an is_empty() method - if let Some(name) = get_item_name(cx, method) - && name.as_str() == "is_empty" - { + if parent_item_name(cx, method) == Some(sym::is_empty) { return; } diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 5fa8f6f4bf3..bc7fc60827a 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -729,6 +729,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_early_pass(|| Box::new(misc_early::MiscEarlyLints)); store.register_late_pass(|_| Box::new(redundant_closure_call::RedundantClosureCall)); store.register_early_pass(|| Box::new(unused_unit::UnusedUnit)); + store.register_late_pass(|_| Box::new(unused_unit::UnusedUnit)); store.register_late_pass(|_| Box::new(returns::Return)); store.register_late_pass(move |tcx| Box::new(collapsible_if::CollapsibleIf::new(tcx, conf))); store.register_late_pass(|_| Box::new(items_after_statements::ItemsAfterStatements)); diff --git a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs index 661b4b590d8..388034c39f5 100644 --- a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs +++ b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::Msrv; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; -use clippy_utils::{msrvs, path_to_local, std_or_core}; +use clippy_utils::{msrvs, path_to_local, std_or_core, sym}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -11,7 +11,6 @@ use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, Mutability, Node, Pat, PatKind, Stmt, StmtKind}; use rustc_lint::LateContext; use rustc_span::SyntaxContext; -use rustc_span::symbol::sym; /// Detects for loop pushing the same item into a Vec pub(super) fn check<'tcx>( @@ -187,8 +186,8 @@ fn get_vec_push<'tcx>( // Extract method being called and figure out the parameters for the method call && let ExprKind::MethodCall(path, self_expr, [pushed_item], _) = &semi_stmt.kind // Check that the method being called is push() on a Vec + && path.ident.name == sym::push && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym::Vec) - && path.ident.name.as_str() == "push" { return Some((self_expr, pushed_item, semi_stmt.span.ctxt())); } diff --git a/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs b/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs index 444ecd5d2bb..ed0cce754b9 100644 --- a/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs +++ b/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::{Sugg, has_enclosing_paren}; use clippy_utils::{SpanlessEq, sym}; use rustc_ast::{BinOpKind, LitIntType, LitKind, UnOp}; @@ -199,9 +199,9 @@ fn build_suggestion( } else { format!("{dividend_sugg_str}{type_suffix}") }; - let divisor_snippet = snippet_with_applicability(cx, rhs.span.source_callsite(), "..", applicability); + let divisor_snippet = snippet_with_context(cx, rhs.span, expr.span.ctxt(), "..", applicability); - let sugg = format!("{suggestion_before_div_ceil}.div_ceil({divisor_snippet})"); + let sugg = format!("{suggestion_before_div_ceil}.div_ceil({})", divisor_snippet.0); span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs index f71264a93ca..b3ee45cc020 100644 --- a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs +++ b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs @@ -3,12 +3,11 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::SpanRangeExt; use clippy_utils::visitors::{is_local_used, local_used_once}; -use clippy_utils::{is_trait_method, path_to_local_id}; +use clippy_utils::{is_trait_method, path_to_local_id, sym}; use rustc_errors::Applicability; use rustc_hir::{BindingMode, ExprKind, LetStmt, Node, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -66,7 +65,7 @@ impl LateLintPass<'_> for ManualHashOne { && let Some(init) = local.init && !init.span.from_expansion() && let ExprKind::MethodCall(seg, build_hasher, [], _) = init.kind - && seg.ident.name.as_str() == "build_hasher" + && seg.ident.name == sym::build_hasher && let Node::Stmt(local_stmt) = cx.tcx.parent_hir_node(local.hir_id) && let Node::Block(block) = cx.tcx.parent_hir_node(local_stmt.hir_id) @@ -94,7 +93,7 @@ impl LateLintPass<'_> for ManualHashOne { && let Node::Expr(finish_expr) = cx.tcx.parent_hir_node(path_expr.hir_id) && !finish_expr.span.from_expansion() && let ExprKind::MethodCall(seg, _, [], _) = finish_expr.kind - && seg.ident.name.as_str() == "finish" + && seg.ident.name == sym::finish && self.msrv.meets(cx, msrvs::BUILD_HASHER_HASH_ONE) { diff --git a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs index 8ab49bd2ea8..ac8c88f0205 100644 --- a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs +++ b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::sugg::Sugg; -use clippy_utils::{higher, is_in_const_context, path_to_local, peel_ref_operators}; +use clippy_utils::{higher, is_in_const_context, path_to_local, peel_ref_operators, sym}; use rustc_ast::LitKind::{Byte, Char}; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; @@ -11,7 +11,7 @@ use rustc_hir::{Expr, ExprKind, Lit, Node, Param, PatExpr, PatExprKind, PatKind, use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; -use rustc_span::{Span, sym}; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -103,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { check_is_ascii(cx, macro_call.span, recv, &range, None); } } else if let ExprKind::MethodCall(path, receiver, [arg], ..) = expr.kind - && path.ident.name.as_str() == "contains" + && path.ident.name == sym::contains && let Some(higher::Range { start: Some(start), end: Some(end), diff --git a/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs b/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs index e4ad3953b67..b365dbf088f 100644 --- a/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs +++ b/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs @@ -1,14 +1,14 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::msrvs::Msrv; -use clippy_utils::{is_none_arm, msrvs, peel_hir_expr_refs}; +use clippy_utils::{is_none_arm, msrvs, peel_hir_expr_refs, sym}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Arm, Expr, ExprKind, LangItem, Pat, PatKind, QPath, is_range_literal}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -76,7 +76,7 @@ impl LateLintPass<'_> for ManualOptionAsSlice { } }, ExprKind::MethodCall(seg, callee, [], _) => { - if seg.ident.name.as_str() == "unwrap_or_default" { + if seg.ident.name == sym::unwrap_or_default { check_map(cx, callee, span, self.msrv); } }, diff --git a/src/tools/clippy/clippy_lints/src/methods/double_ended_iterator_last.rs b/src/tools/clippy/clippy_lints/src/methods/double_ended_iterator_last.rs index e666f31217c..6d841853fbe 100644 --- a/src/tools/clippy/clippy_lints/src/methods/double_ended_iterator_last.rs +++ b/src/tools/clippy/clippy_lints/src/methods/double_ended_iterator_last.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::{has_non_owning_mutable_access, implements_trait}; -use clippy_utils::{is_mutable, is_trait_method, path_to_local}; +use clippy_utils::{is_mutable, is_trait_method, path_to_local, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, Node, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty::Instance; -use rustc_span::{Span, sym}; +use rustc_span::Span; use super::DOUBLE_ENDED_ITERATOR_LAST; @@ -24,7 +24,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, self_expr: &'_ Exp && let Ok(Some(fn_def)) = Instance::try_resolve(cx.tcx, cx.typing_env(), id, args) // find the provided definition of Iterator::last && let Some(item) = cx.tcx.get_diagnostic_item(sym::Iterator) - && let Some(last_def) = cx.tcx.provided_trait_methods(item).find(|m| m.name().as_str() == "last") + && let Some(last_def) = cx.tcx.provided_trait_methods(item).find(|m| m.name() == sym::last) // if the resolved method is the same as the provided definition && fn_def.def_id() == last_def.def_id && let self_ty = cx.typeck_results().expr_ty(self_expr) diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs index da123f13d46..4dd54cf1974 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs @@ -233,12 +233,12 @@ impl<'tcx> OffendingFilterExpr<'tcx> { // the latter only calls `effect` once let side_effect_expr_span = receiver.can_have_side_effects().then_some(receiver.span); - if cx.tcx.is_diagnostic_item(sym::Option, recv_ty.did()) && path.ident.name.as_str() == "is_some" { + if cx.tcx.is_diagnostic_item(sym::Option, recv_ty.did()) && path.ident.name == sym::is_some { Some(Self::IsSome { receiver, side_effect_expr_span, }) - } else if cx.tcx.is_diagnostic_item(sym::Result, recv_ty.did()) && path.ident.name.as_str() == "is_ok" { + } else if cx.tcx.is_diagnostic_item(sym::Result, recv_ty.did()) && path.ident.name == sym::is_ok { Some(Self::IsOk { receiver, side_effect_expr_span, diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index ad374dee516..10f4637d08f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -4370,11 +4370,10 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// - /// Detect functions that end with `Option::and_then` or `Result::and_then`, and suggest using a question mark (`?`) instead. + /// Detect functions that end with `Option::and_then` or `Result::and_then`, and suggest using + /// the `?` operator instead. /// /// ### Why is this bad? - /// /// The `and_then` method is used to chain a computation that returns an `Option` or a `Result`. /// This can be replaced with the `?` operator, which is more concise and idiomatic. /// diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs b/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs index 743aacf0588..f528f7f065c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs @@ -9,7 +9,7 @@ use super::utils::get_last_chain_binding_hir_id; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::paths::CHAR_IS_ASCII; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{match_def_path, path_to_local_id, peel_blocks}; +use clippy_utils::{match_def_path, path_to_local_id, peel_blocks, sym}; fn peels_expr_ref<'a, 'tcx>(mut expr: &'a Expr<'tcx>) -> &'a Expr<'tcx> { while let ExprKind::AddrOf(_, _, e) = expr.kind { @@ -32,7 +32,7 @@ fn handle_expr( // If we have `!is_ascii`, then only `.any()` should warn. And if the condition is // `is_ascii`, then only `.all()` should warn. if revert != is_all - && method.ident.name.as_str() == "is_ascii" + && method.ident.name == sym::is_ascii && path_to_local_id(receiver, first_param) && let char_arg_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs() && *char_arg_ty.kind() == ty::Char @@ -102,7 +102,7 @@ pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, && let body = cx.tcx.hir_body(body) && let Some(first_param) = body.params.first() && let ExprKind::MethodCall(method, mut recv, [], _) = recv.kind - && method.ident.name.as_str() == "chars" + && method.ident.name == sym::chars && let str_ty = cx.typeck_results().expr_ty_adjusted(recv).peel_refs() && *str_ty.kind() == ty::Str { 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 6efaba525e3..cd22583b8a2 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs @@ -9,7 +9,7 @@ use clippy_utils::ty::{ }; use clippy_utils::{ CaptureKind, can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, is_trait_method, path_to_local, - path_to_local_id, + path_to_local_id, sym, }; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, MultiSpan}; @@ -20,8 +20,8 @@ use rustc_hir::{ use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, AssocTag, ClauseKind, EarlyBinder, GenericArg, GenericArgKind, Ty}; +use rustc_span::Span; use rustc_span::symbol::Ident; -use rustc_span::{Span, sym}; const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed"; @@ -339,7 +339,7 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> { // Check function calls on our collection if let ExprKind::MethodCall(method_name, recv, args, _) = &expr.kind { if args.is_empty() - && method_name.ident.name.as_str() == "collect" + && method_name.ident.name == sym::collect && is_trait_method(self.cx, expr, sym::Iterator) { self.current_mutably_captured_ids = get_captured_ids(self.cx, self.cx.typeck_results().expr_ty(recv)); diff --git a/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs b/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs index fe999a3b5f8..407f2e80aff 100644 --- a/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs +++ b/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs @@ -1,17 +1,16 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::get_parent_expr; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_local_use_after_expr; +use clippy_utils::{get_parent_expr, sym}; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; -use rustc_span::sym; use super::READ_LINE_WITHOUT_TRIM; @@ -44,7 +43,7 @@ pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr< if let Some(parent) = get_parent_expr(cx, expr) { let data = if let ExprKind::MethodCall(segment, recv, args, span) = parent.kind { if args.is_empty() - && segment.ident.name.as_str() == "parse" + && segment.ident.name == sym::parse && let parse_result_ty = cx.typeck_results().expr_ty(parent) && is_type_diagnostic_item(cx, parse_result_ty, sym::Result) && let ty::Adt(_, substs) = parse_result_ty.kind() @@ -58,7 +57,7 @@ pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr< "calling `.parse()` on a string without trimming the trailing newline character", "checking", )) - } else if segment.ident.name.as_str() == "ends_with" + } else if segment.ident.name == sym::ends_with && recv.span == expr.span && let [arg] = args && expr_is_string_literal_without_trailing_newline(arg) diff --git a/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs b/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs index e8861935d42..91643b0dfef 100644 --- a/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs +++ b/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs @@ -55,7 +55,6 @@ pub(super) fn check<'tcx>( None => &body_snip, }; - let msg = "use the question mark operator instead of an `and_then` call"; let sugg = format!( "let {} = {}?;\n{}", arg_snip, @@ -63,5 +62,13 @@ pub(super) fn check<'tcx>( reindent_multiline(inner, false, indent_of(cx, expr.span)) ); - span_lint_and_sugg(cx, RETURN_AND_THEN, expr.span, msg, "try", sugg, applicability); + span_lint_and_sugg( + cx, + RETURN_AND_THEN, + expr.span, + "use the `?` operator instead of an `and_then` call", + "try", + sugg, + applicability, + ); } diff --git a/src/tools/clippy/clippy_lints/src/methods/str_split.rs b/src/tools/clippy/clippy_lints/src/methods/str_split.rs index 3586e11f56a..fb4ac7b3613 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_split.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_split.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; +use clippy_utils::sym; use clippy_utils::visitors::is_const_evaluatable; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -19,7 +20,7 @@ pub(super) fn check<'a>(cx: &LateContext<'a>, expr: &'_ Expr<'_>, split_recv: &' && !is_const_evaluatable(cx, trim_recv) && let ExprKind::Lit(split_lit) = split_arg.kind && (matches!(split_lit.node, LitKind::Char('\n')) - || matches!(split_lit.node, LitKind::Str(sym, _) if (sym.as_str() == "\n" || sym.as_str() == "\r\n"))) + || matches!(split_lit.node, LitKind::Str(sym::LF | sym::CRLF, _))) { let mut app = Applicability::MaybeIncorrect; span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs index f920f306bc1..79ed352193f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -3,13 +3,12 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_copy; use clippy_utils::usage::mutated_variables; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; -use clippy_utils::{is_res_lang_ctor, is_trait_method, path_res, path_to_local_id}; +use clippy_utils::{is_res_lang_ctor, is_trait_method, path_res, path_to_local_id, sym}; use core::ops::ControlFlow; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::sym; use super::{UNNECESSARY_FILTER_MAP, UNNECESSARY_FIND_MAP}; @@ -95,7 +94,7 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc (true, true) }, hir::ExprKind::MethodCall(segment, recv, [arg], _) => { - if segment.ident.name.as_str() == "then_some" + if segment.ident.name == sym::then_some && cx.typeck_results().expr_ty(recv).is_bool() && path_to_local_id(arg, arg_id) { diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index 206b0a8ae3c..87bb8d46a1d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -7,7 +7,7 @@ use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_ use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{ fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, is_expr_temporary_value, peel_middle_ty_refs, - return_ty, + return_ty, sym, }; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -20,7 +20,7 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; use rustc_middle::ty::{ self, ClauseKind, GenericArg, GenericArgKind, GenericArgsRef, ParamTy, ProjectionPredicate, TraitPredicate, Ty, }; -use rustc_span::{Symbol, sym}; +use rustc_span::Symbol; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{Obligation, ObligationCause}; @@ -312,8 +312,7 @@ fn check_string_from_utf8<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, /// call of a `to_owned`-like function is unnecessary. fn check_split_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool { if let Some(parent) = get_parent_expr(cx, expr) - && let Some((fn_name, argument_expr)) = get_fn_name_and_arg(cx, parent) - && fn_name.as_str() == "split" + && let Some((sym::split, argument_expr)) = get_fn_name_and_arg(cx, parent) && let Some(receiver_snippet) = receiver.span.get_source_text(cx) && let Some(arg_snippet) = argument_expr.span.get_source_text(cx) { @@ -614,8 +613,7 @@ fn has_lifetime(ty: Ty<'_>) -> bool { /// Returns true if the named method is `Iterator::cloned` or `Iterator::copied`. fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool { - (method_name.as_str() == "cloned" || method_name.as_str() == "copied") - && is_diag_trait_item(cx, method_def_id, sym::Iterator) + matches!(method_name, sym::cloned | sym::copied) && is_diag_trait_item(cx, method_def_id, sym::Iterator) } /// Returns true if the named method can be used to convert the receiver to its "owned" @@ -628,7 +626,7 @@ fn is_to_owned_like<'a>(cx: &LateContext<'a>, call_expr: &Expr<'a>, method_name: /// Returns true if the named method is `Cow::into_owned`. fn is_cow_into_owned(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool { - method_name.as_str() == "into_owned" && is_diag_item_method(cx, method_def_id, sym::Cow) + method_name == sym::into_owned && is_diag_item_method(cx, method_def_id, sym::Cow) } /// Returns true if the named method is `ToString::to_string` and it's called on a type that diff --git a/src/tools/clippy/clippy_lints/src/minmax.rs b/src/tools/clippy/clippy_lints/src/minmax.rs index ed89b3b3438..64eafc0ebcc 100644 --- a/src/tools/clippy/clippy_lints/src/minmax.rs +++ b/src/tools/clippy/clippy_lints/src/minmax.rs @@ -1,10 +1,9 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_trait_method; +use clippy_utils::{is_trait_method, sym}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::sym; use std::cmp::Ordering::{Equal, Greater, Less}; declare_clippy_lint! { @@ -79,12 +78,10 @@ fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinM }, ExprKind::MethodCall(path, receiver, args @ [_], _) => { if cx.typeck_results().expr_ty(receiver).is_floating_point() || is_trait_method(cx, expr, sym::Ord) { - if path.ident.name.as_str() == "max" { - fetch_const(cx, Some(receiver), args, MinMax::Max) - } else if path.ident.name.as_str() == "min" { - fetch_const(cx, Some(receiver), args, MinMax::Min) - } else { - None + match path.ident.name { + sym::max => fetch_const(cx, Some(receiver), args, MinMax::Max), + sym::min => fetch_const(cx, Some(receiver), args, MinMax::Min), + _ => None, } } else { None diff --git a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs index 1932d2d5f97..be7dd74fd62 100644 --- a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs +++ b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs @@ -1,9 +1,9 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_path_lang_item; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::{Visitable, for_each_expr}; +use clippy_utils::{is_path_lang_item, sym}; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::{DefKind, Res}; @@ -13,7 +13,7 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{Ty, TypeckResults}; use rustc_session::declare_lint_pass; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -116,7 +116,7 @@ fn should_lint<'tcx>( if path.ident.name == sym::debug_struct && is_type_diagnostic_item(cx, recv_ty, sym::Formatter) { has_debug_struct = true; - } else if path.ident.name.as_str() == "finish_non_exhaustive" + } else if path.ident.name == sym::finish_non_exhaustive && is_type_diagnostic_item(cx, recv_ty, sym::DebugStruct) { has_finish_non_exhaustive = true; diff --git a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs index 0e085585962..d9f4fb271fb 100644 --- a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::macros::root_macro_call_first_node; -use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id}; +use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id, sym}; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, LetStmt, Node, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -136,7 +136,7 @@ impl<'tcx> DivergenceVisitor<'_, 'tcx> { fn report_diverging_sub_expr(&mut self, e: &Expr<'_>) { if let Some(macro_call) = root_macro_call_first_node(self.cx, e) - && self.cx.tcx.item_name(macro_call.def_id).as_str() == "todo" + && self.cx.tcx.is_diagnostic_item(sym::todo_macro, macro_call.def_id) { return; } diff --git a/src/tools/clippy/clippy_lints/src/needless_for_each.rs b/src/tools/clippy/clippy_lints/src/needless_for_each.rs index 90b27f5dbac..7dd96f1f037 100644 --- a/src/tools/clippy/clippy_lints/src/needless_for_each.rs +++ b/src/tools/clippy/clippy_lints/src/needless_for_each.rs @@ -3,12 +3,12 @@ use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Block, BlockCheckMode, Closure, Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{Span, sym}; +use rustc_span::Span; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_trait_method; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::has_iter_method; +use clippy_utils::{is_trait_method, sym}; declare_clippy_lint! { /// ### What it does @@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { iter_recv.kind, ExprKind::Array(..) | ExprKind::Call(..) | ExprKind::Path(..) ) - && method_name.ident.name.as_str() == "for_each" + && method_name.ident.name == sym::for_each && is_trait_method(cx, expr, sym::Iterator) // Checks the type of the `iter` method receiver is NOT a user defined type. && has_iter_method(cx, cx.typeck_results().expr_ty(iter_recv)).is_some() diff --git a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs index 72b0a80260e..2a2160c3be2 100644 --- a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs @@ -1,6 +1,5 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::path_res; -use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Block, Body, Expr, ExprKind, LangItem, MatchSource, QPath}; @@ -9,52 +8,38 @@ use rustc_session::declare_lint_pass; declare_clippy_lint! { /// ### What it does - /// Suggests alternatives for useless applications of `?` in terminating expressions + /// Suggests replacing `Ok(x?)` or `Some(x?)` with `x` in return positions where the `?` operator + /// is not needed to convert the type of `x`. /// /// ### Why is this bad? /// There's no reason to use `?` to short-circuit when execution of the body will end there anyway. /// /// ### Example /// ```no_run - /// struct TO { - /// magic: Option<usize>, + /// # use std::num::ParseIntError; + /// fn f(s: &str) -> Option<usize> { + /// Some(s.find('x')?) /// } /// - /// fn f(to: TO) -> Option<usize> { - /// Some(to.magic?) + /// fn g(s: &str) -> Result<usize, ParseIntError> { + /// Ok(s.parse()?) /// } - /// - /// struct TR { - /// magic: Result<usize, bool>, - /// } - /// - /// fn g(tr: Result<TR, bool>) -> Result<usize, bool> { - /// tr.and_then(|t| Ok(t.magic?)) - /// } - /// /// ``` /// Use instead: /// ```no_run - /// struct TO { - /// magic: Option<usize>, + /// # use std::num::ParseIntError; + /// fn f(s: &str) -> Option<usize> { + /// s.find('x') /// } /// - /// fn f(to: TO) -> Option<usize> { - /// to.magic - /// } - /// - /// struct TR { - /// magic: Result<usize, bool>, - /// } - /// - /// fn g(tr: Result<TR, bool>) -> Result<usize, bool> { - /// tr.and_then(|t| t.magic) + /// fn g(s: &str) -> Result<usize, ParseIntError> { + /// s.parse() /// } /// ``` #[clippy::version = "1.51.0"] pub NEEDLESS_QUESTION_MARK, complexity, - "Suggest `value.inner_option` instead of `Some(value.inner_option?)`. The same goes for `Result<T, E>`." + "using `Ok(x?)` or `Some(x?)` where `x` would be equivalent" } declare_lint_pass!(NeedlessQuestionMark => [NEEDLESS_QUESTION_MARK]); @@ -111,10 +96,10 @@ fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::Call(path, [arg]) = expr.kind && let Res::Def(DefKind::Ctor(..), ctor_id) = path_res(cx, path) && let Some(variant_id) = cx.tcx.opt_parent(ctor_id) - && let sugg_remove = if cx.tcx.lang_items().option_some_variant() == Some(variant_id) { - "Some()" + && let variant = if cx.tcx.lang_items().option_some_variant() == Some(variant_id) { + "Some" } else if cx.tcx.lang_items().result_ok_variant() == Some(variant_id) { - "Ok()" + "Ok" } else { return; } @@ -126,14 +111,25 @@ fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { && let inner_ty = cx.typeck_results().expr_ty(inner_expr) && expr_ty == inner_ty { - span_lint_and_sugg( + span_lint_hir_and_then( cx, NEEDLESS_QUESTION_MARK, + expr.hir_id, expr.span, - "question mark operator is useless here", - format!("try removing question mark and `{sugg_remove}`"), - format!("{}", snippet(cx, inner_expr.span, r#""...""#)), - Applicability::MachineApplicable, + format!("enclosing `{variant}` and `?` operator are unneeded"), + |diag| { + diag.multipart_suggestion( + format!("remove the enclosing `{variant}` and `?` operator"), + vec![ + (expr.span.until(inner_expr.span), String::new()), + ( + inner_expr.span.shrink_to_hi().to(expr.span.shrink_to_hi()), + String::new(), + ), + ], + Applicability::MachineApplicable, + ); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs index 852c3885f56..23a1622f30f 100644 --- a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs +++ b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{SpanRangeExt, snippet_with_applicability}; +use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -43,12 +43,12 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { match &expr.kind { ExprKind::MethodCall(path, func, [param], _) => { if let Some(adt) = cx.typeck_results().expr_ty(func).peel_refs().ty_adt_def() - && ((path.ident.name.as_str() == "mode" + && ((path.ident.name == sym::mode && matches!( cx.tcx.get_diagnostic_name(adt.did()), Some(sym::FsOpenOptions | sym::DirBuilder) )) - || (path.ident.name.as_str() == "set_mode" + || (path.ident.name == sym::set_mode && cx.tcx.is_diagnostic_item(sym::FsPermissions, adt.did()))) && let ExprKind::Lit(_) = param.kind && param.span.eq_ctxt(expr.span) diff --git a/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs b/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs index 635f5678e2a..1b8ab1bdedf 100644 --- a/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs +++ b/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; +use clippy_utils::sym; use rustc_ast::ast::BinOpKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::declare_lint_pass; -use rustc_span::symbol::sym; declare_clippy_lint! { /// ### What it does @@ -72,7 +72,7 @@ fn check_non_zero_conversion(cx: &LateContext<'_>, expr: &Expr<'_>, applicabilit && let ExprKind::Path(qpath) = &func.kind && let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() && let ExprKind::MethodCall(rcv_path, receiver, [], _) = &arg.kind - && rcv_path.ident.name.as_str() == "get" + && rcv_path.ident.name == sym::get { let fn_name = cx.tcx.item_name(def_id); let target_ty = cx.typeck_results().expr_ty(expr); 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 01dc6a27c33..ded161c8576 100644 --- a/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs @@ -1,7 +1,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::get_item_name; use clippy_utils::sugg::Sugg; +use clippy_utils::{parent_item_name, sym}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::LateContext; @@ -34,7 +34,7 @@ pub(crate) fn check<'tcx>( return; } - if let Some(name) = get_item_name(cx, expr) { + if let Some(name) = parent_item_name(cx, expr) { let name = name.as_str(); if name == "eq" || name == "ne" || name == "is_nan" || name.starts_with("eq_") || name.ends_with("_eq") { return; @@ -106,7 +106,7 @@ fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } if let ExprKind::MethodCall(method_name, self_arg, [], _) = expr.kind - && method_name.ident.name.as_str() == "signum" + && method_name.ident.name == sym::signum // Check that the receiver of the signum() is a float (expressions[0] is the receiver of // the method call) { diff --git a/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs b/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs index dc142b6e157..da56a785007 100644 --- a/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs +++ b/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sym; use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::ast::LitKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -33,7 +33,7 @@ impl<'tcx> LateLintPass<'tcx> for PermissionsSetReadonlyFalse { if let ExprKind::MethodCall(path, receiver, [arg], _) = &expr.kind && let ExprKind::Lit(lit) = &arg.kind && LitKind::Bool(false) == lit.node - && path.ident.name.as_str() == "set_readonly" + && path.ident.name == sym::set_readonly && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::FsPermissions) { span_lint_and_then( diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs index 491961408ad..9149406642d 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -743,9 +743,9 @@ fn check_ptr_eq<'tcx>( } // Remove one level of usize conversion if any - let (left, right) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) { - (Some(lhs), Some(rhs)) => (lhs, rhs), - _ => (left, right), + let (left, right, usize_peeled) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) { + (Some(lhs), Some(rhs)) => (lhs, rhs, true), + _ => (left, right, false), }; // This lint concerns raw pointers @@ -754,7 +754,12 @@ fn check_ptr_eq<'tcx>( return; } - let (left_var, right_var) = (peel_raw_casts(cx, left, left_ty), peel_raw_casts(cx, right, right_ty)); + let ((left_var, left_casts_peeled), (right_var, right_casts_peeled)) = + (peel_raw_casts(cx, left, left_ty), peel_raw_casts(cx, right, right_ty)); + + if !(usize_peeled || left_casts_peeled || right_casts_peeled) { + return; + } let mut app = Applicability::MachineApplicable; let left_snip = Sugg::hir_with_context(cx, left_var, expr.span.ctxt(), "_", &mut app); @@ -787,8 +792,9 @@ fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_> } } -// Peel raw casts if the remaining expression can be coerced to it -fn peel_raw_casts<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expr_ty: Ty<'tcx>) -> &'tcx Expr<'tcx> { +// Peel raw casts if the remaining expression can be coerced to it, and whether casts have been +// peeled or not. +fn peel_raw_casts<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expr_ty: Ty<'tcx>) -> (&'tcx Expr<'tcx>, bool) { if !expr.span.from_expansion() && let ExprKind::Cast(inner, _) = expr.kind && let ty::RawPtr(target_ty, _) = expr_ty.kind() @@ -796,8 +802,8 @@ fn peel_raw_casts<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expr_ty: && let ty::RawPtr(inner_target_ty, _) | ty::Ref(_, inner_target_ty, _) = inner_ty.kind() && target_ty == inner_target_ty { - peel_raw_casts(cx, inner, inner_ty) + (peel_raw_casts(cx, inner, inner_ty).0, true) } else { - expr + (expr, false) } } diff --git a/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs b/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs index 7f74a2fff9f..d8d813f9846 100644 --- a/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs +++ b/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::SpanRangeExt; +use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::sym; use std::fmt; declare_clippy_lint! { @@ -97,7 +97,7 @@ fn expr_as_ptr_offset_call<'tcx>( if path_segment.ident.name == sym::offset { return Some((arg_0, arg_1, Method::Offset)); } - if path_segment.ident.name.as_str() == "wrapping_offset" { + if path_segment.ident.name == sym::wrapping_offset { return Some((arg_0, arg_1, Method::WrappingOffset)); } } diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index d318897443d..c02e5e0621c 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -10,7 +10,7 @@ use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{ eq_expr_value, higher, is_else_clause, is_in_const_context, is_lint_allowed, is_path_lang_item, is_res_lang_ctor, pat_and_expr_can_be_question_mark, path_res, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt, - span_contains_cfg, span_contains_comment, + span_contains_cfg, span_contains_comment, sym, }; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk}; @@ -22,15 +22,14 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; -use rustc_span::sym; use rustc_span::symbol::Symbol; declare_clippy_lint! { /// ### What it does - /// Checks for expressions that could be replaced by the question mark operator. + /// Checks for expressions that could be replaced by the `?` operator. /// /// ### Why is this bad? - /// Question mark usage is more idiomatic. + /// Using the `?` operator is shorter and more idiomatic. /// /// ### Example /// ```ignore @@ -47,7 +46,7 @@ declare_clippy_lint! { #[clippy::version = "pre 1.29.0"] pub QUESTION_MARK, style, - "checks for expressions that could be replaced by the question mark operator" + "checks for expressions that could be replaced by the `?` operator" } pub struct QuestionMark { @@ -207,8 +206,8 @@ fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_ is_type_diagnostic_item(cx, caller_ty, smbl) && expr_return_none_or_err(smbl, cx, if_then, caller, None) && match smbl { - sym::Option => call_sym.as_str() == "is_none", - sym::Result => call_sym.as_str() == "is_err", + sym::Option => call_sym == sym::is_none, + sym::Result => call_sym == sym::is_err, _ => false, } }, @@ -280,7 +279,7 @@ fn expr_return_none_or_err( /// } /// ``` /// -/// If it matches, it will suggest to use the question mark operator instead +/// If it matches, it will suggest to use the `?` operator instead fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr) && !is_else_clause(cx.tcx, expr) diff --git a/src/tools/clippy/clippy_lints/src/question_mark_used.rs b/src/tools/clippy/clippy_lints/src/question_mark_used.rs index 0a974bf9d2f..96ea485d769 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark_used.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark_used.rs @@ -7,10 +7,10 @@ use rustc_session::declare_lint_pass; declare_clippy_lint! { /// ### What it does - /// Checks for expressions that use the question mark operator and rejects them. + /// Checks for expressions that use the `?` operator and rejects them. /// /// ### Why restrict this? - /// Sometimes code wants to avoid the question mark operator because for instance a local + /// Sometimes code wants to avoid the `?` operator because for instance a local /// block requires a macro to re-throw errors to attach additional information to the /// error. /// @@ -27,7 +27,7 @@ declare_clippy_lint! { #[clippy::version = "1.69.0"] pub QUESTION_MARK_USED, restriction, - "complains if the question mark operator is used" + "checks if the `?` operator is used" } declare_lint_pass!(QuestionMarkUsed => [QUESTION_MARK_USED]); @@ -40,15 +40,9 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMarkUsed { } #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] - span_lint_and_then( - cx, - QUESTION_MARK_USED, - expr.span, - "question mark operator was used", - |diag| { - diag.help("consider using a custom macro or match expression"); - }, - ); + span_lint_and_then(cx, QUESTION_MARK_USED, expr.span, "the `?` operator was used", |diag| { + diag.help("consider using a custom macro or match expression"); + }); } } } diff --git a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs index d26288adb39..30a5fe4db27 100644 --- a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs @@ -3,14 +3,13 @@ use clippy_utils::macros::matching_root_macro_call; use clippy_utils::sugg::Sugg; use clippy_utils::{ SpanlessEq, get_enclosing_block, is_integer_literal, is_path_diagnostic_item, path_to_local, path_to_local_id, - span_contains_comment, + span_contains_comment, sym, }; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, PatKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::symbol::sym; declare_clippy_lint! { /// ### What it does @@ -248,7 +247,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { if self.initialization_found && let ExprKind::MethodCall(path, self_arg, [extend_arg], _) = expr.kind && path_to_local_id(self_arg, self.vec_alloc.local_id) - && path.ident.name.as_str() == "extend" + && path.ident.name == sym::extend && self.is_repeat_take(extend_arg) { self.slow_expression = Some(InitializationType::Extend(expr)); @@ -260,7 +259,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { if self.initialization_found && let ExprKind::MethodCall(path, self_arg, [len_arg, fill_arg], _) = expr.kind && path_to_local_id(self_arg, self.vec_alloc.local_id) - && path.ident.name.as_str() == "resize" + && path.ident.name == sym::resize // Check that is filled with 0 && is_integer_literal(fill_arg, 0) { @@ -282,7 +281,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { /// Returns `true` if give expression is `repeat(0).take(...)` fn is_repeat_take(&mut self, expr: &'tcx Expr<'tcx>) -> bool { if let ExprKind::MethodCall(take_path, recv, [len_arg], _) = expr.kind - && take_path.ident.name.as_str() == "take" + && take_path.ident.name == sym::take // Check that take is applied to `repeat(0)` && self.is_repeat_zero(recv) { diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs index 43a3e696105..af4d0d541f1 100644 --- a/src/tools/clippy/clippy_lints/src/strings.rs +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -286,7 +286,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { if !e.span.in_external_macro(cx.sess().source_map()) && let ExprKind::MethodCall(path, receiver, ..) = &e.kind - && path.ident.name.as_str() == "as_bytes" + && path.ident.name == sym::as_bytes && let ExprKind::Lit(lit) = &receiver.kind && let LitKind::Str(lit_content, _) = &lit.node { @@ -332,7 +332,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { } if let ExprKind::MethodCall(path, recv, [], _) = &e.kind - && path.ident.name.as_str() == "into_bytes" + && path.ident.name == sym::into_bytes && let ExprKind::MethodCall(path, recv, [], _) = &recv.kind && matches!(path.ident.name.as_str(), "to_owned" | "to_string") && let ExprKind::Lit(lit) = &recv.kind @@ -556,7 +556,7 @@ impl<'tcx> LateLintPass<'tcx> for TrimSplitWhitespace { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { let tyckres = cx.typeck_results(); if let ExprKind::MethodCall(path, split_recv, [], split_ws_span) = expr.kind - && path.ident.name.as_str() == "split_whitespace" + && path.ident.name == sym::split_whitespace && let Some(split_ws_def_id) = tyckres.type_dependent_def_id(expr.hir_id) && cx.tcx.is_diagnostic_item(sym::str_split_whitespace, split_ws_def_id) && let ExprKind::MethodCall(path, _trim_recv, [], trim_span) = split_recv.kind diff --git a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs index 9993e6ae18b..bb969bc802f 100644 --- a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs +++ b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::match_def_path; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{match_def_path, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -38,11 +38,11 @@ declare_lint_pass!(ToDigitIsSome => [TO_DIGIT_IS_SOME]); impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if let hir::ExprKind::MethodCall(is_some_path, to_digit_expr, [], _) = &expr.kind - && is_some_path.ident.name.as_str() == "is_some" + && is_some_path.ident.name == sym::is_some { let match_result = match &to_digit_expr.kind { hir::ExprKind::MethodCall(to_digits_path, char_arg, [radix_arg], _) => { - if to_digits_path.ident.name.as_str() == "to_digit" + if to_digits_path.ident.name == sym::to_digit && let char_arg_ty = cx.typeck_results().expr_ty_adjusted(char_arg) && *char_arg_ty.kind() == ty::Char { diff --git a/src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs b/src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs index 81c0a57083e..1ccab62708b 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_normalizable; -use clippy_utils::{eq_expr_value, path_to_local}; +use clippy_utils::{eq_expr_value, path_to_local, sym}; use rustc_abi::WrappingRange; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Node}; @@ -43,7 +43,7 @@ fn binops_with_local(cx: &LateContext<'_>, local_expr: &Expr<'_>, expr: &Expr<'_ binops_with_local(cx, local_expr, lhs) || binops_with_local(cx, local_expr, rhs) }, ExprKind::MethodCall(path, receiver, [arg], _) - if path.ident.name.as_str() == "contains" + if path.ident.name == sym::contains // ... `contains` called on some kind of range && let Some(receiver_adt) = cx.typeck_results().expr_ty(receiver).peel_refs().ty_adt_def() && let lang_items = cx.tcx.lang_items() @@ -81,7 +81,7 @@ pub(super) fn check<'tcx>( if let Some(then_some_call) = peel_parent_unsafe_blocks(cx, expr) && let ExprKind::MethodCall(path, receiver, [arg], _) = then_some_call.kind && cx.typeck_results().expr_ty(receiver).is_bool() - && path.ident.name.as_str() == "then_some" + && path.ident.name == sym::then_some && is_local_with_projections(transmutable) && binops_with_local(cx, transmutable, receiver) && is_normalizable(cx, cx.param_env, from_ty) diff --git a/src/tools/clippy/clippy_lints/src/types/vec_box.rs b/src/tools/clippy/clippy_lints/src/types/vec_box.rs index 769244c675e..f13042a6fa6 100644 --- a/src/tools/clippy/clippy_lints/src/types/vec_box.rs +++ b/src/tools/clippy/clippy_lints/src/types/vec_box.rs @@ -19,61 +19,58 @@ pub(super) fn check<'tcx>( def_id: DefId, box_size_threshold: u64, ) -> bool { - if cx.tcx.is_diagnostic_item(sym::Vec, def_id) { - if let Some(last) = last_path_segment(qpath).args - // Get the _ part of Vec<_> - && let Some(GenericArg::Type(ty)) = last.args.first() - // extract allocator from the Vec for later - && let vec_alloc_ty = last.args.get(1) - // ty is now _ at this point - && let TyKind::Path(ref ty_qpath) = ty.kind - && let res = cx.qpath_res(ty_qpath, ty.hir_id) - && let Some(def_id) = res.opt_def_id() - && Some(def_id) == cx.tcx.lang_items().owned_box() - // At this point, we know ty is Box<T>, now get T - && let Some(last) = last_path_segment(ty_qpath).args - && let Some(GenericArg::Type(boxed_ty)) = last.args.first() - // extract allocator from the Box for later - && let boxed_alloc_ty = last.args.get(1) - // we don't expect to encounter `_` here so ignore `GenericArg::Infer` is okay - && let ty_ty = lower_ty(cx.tcx, boxed_ty.as_unambig_ty()) - && !ty_ty.has_escaping_bound_vars() - && ty_ty.is_sized(cx.tcx, cx.typing_env()) - && let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes()) - && ty_ty_size < box_size_threshold - // https://github.com/rust-lang/rust-clippy/issues/7114 - && match (vec_alloc_ty, boxed_alloc_ty) { - (None, None) => true, - // this is in the event that we have something like - // Vec<_, Global>, in which case is equivalent to - // Vec<_> - (None, Some(GenericArg::Type(inner))) | (Some(GenericArg::Type(inner)), None) => { - if let TyKind::Path(path) = inner.kind - && let Some(did) = cx.qpath_res(&path, inner.hir_id).opt_def_id() { - cx.tcx.lang_items().get(LangItem::GlobalAlloc) == Some(did) - } else { - false - } - }, - (Some(GenericArg::Type(l)), Some(GenericArg::Type(r))) => - // we don't expect to encounter `_` here so ignore `GenericArg::Infer` is okay - lower_ty(cx.tcx, l.as_unambig_ty()) == lower_ty(cx.tcx, r.as_unambig_ty()), - _ => false - } - { - span_lint_and_sugg( - cx, - VEC_BOX, - hir_ty.span, - "`Vec<T>` is already on the heap, the boxing is unnecessary", - "try", - format!("Vec<{}>", snippet(cx, boxed_ty.span, "..")), - Applicability::Unspecified, - ); - true - } else { - false + if cx.tcx.is_diagnostic_item(sym::Vec, def_id) + && let Some(last) = last_path_segment(qpath).args + // Get the _ part of Vec<_> + && let Some(GenericArg::Type(ty)) = last.args.first() + // extract allocator from the Vec for later + && let vec_alloc_ty = last.args.get(1) + // ty is now _ at this point + && let TyKind::Path(ref ty_qpath) = ty.kind + && let res = cx.qpath_res(ty_qpath, ty.hir_id) + && let Some(def_id) = res.opt_def_id() + && Some(def_id) == cx.tcx.lang_items().owned_box() + // At this point, we know ty is Box<T>, now get T + && let Some(last) = last_path_segment(ty_qpath).args + && let Some(GenericArg::Type(boxed_ty)) = last.args.first() + // extract allocator from the Box for later + && let boxed_alloc_ty = last.args.get(1) + // we don't expect to encounter `_` here so ignore `GenericArg::Infer` is okay + && let ty_ty = lower_ty(cx.tcx, boxed_ty.as_unambig_ty()) + && !ty_ty.has_escaping_bound_vars() + && ty_ty.is_sized(cx.tcx, cx.typing_env()) + && let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes()) + && ty_ty_size < box_size_threshold + // https://github.com/rust-lang/rust-clippy/issues/7114 + && match (vec_alloc_ty, boxed_alloc_ty) { + (None, None) => true, + // this is in the event that we have something like + // Vec<_, Global>, in which case is equivalent to + // Vec<_> + (None, Some(GenericArg::Type(inner))) | (Some(GenericArg::Type(inner)), None) => { + if let TyKind::Path(path) = inner.kind + && let Some(did) = cx.qpath_res(&path, inner.hir_id).opt_def_id() { + cx.tcx.lang_items().get(LangItem::GlobalAlloc) == Some(did) + } else { + false + } + }, + (Some(GenericArg::Type(l)), Some(GenericArg::Type(r))) => + // we don't expect to encounter `_` here so ignore `GenericArg::Infer` is okay + lower_ty(cx.tcx, l.as_unambig_ty()) == lower_ty(cx.tcx, r.as_unambig_ty()), + _ => false } + { + span_lint_and_sugg( + cx, + VEC_BOX, + hir_ty.span, + "`Vec<T>` is already on the heap, the boxing is unnecessary", + "try", + format!("Vec<{}>", snippet(cx, boxed_ty.span, "..")), + Applicability::Unspecified, + ); + true } else { false } diff --git a/src/tools/clippy/clippy_lints/src/uninit_vec.rs b/src/tools/clippy/clippy_lints/src/uninit_vec.rs index 7803d5115c9..cee4a53f03c 100644 --- a/src/tools/clippy/clippy_lints/src/uninit_vec.rs +++ b/src/tools/clippy/clippy_lints/src/uninit_vec.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; use clippy_utils::ty::{is_type_diagnostic_item, is_uninit_value_valid_for_ty}; -use clippy_utils::{SpanlessEq, is_integer_literal, is_lint_allowed, path_to_local_id, peel_hir_expr_while}; +use clippy_utils::{SpanlessEq, is_integer_literal, is_lint_allowed, path_to_local_id, peel_hir_expr_while, sym}; use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, PathSegment, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; -use rustc_span::{Span, sym}; +use rustc_span::Span; // TODO: add `ReadBuf` (RFC 2930) in "How to fix" once it is available in std declare_clippy_lint! { @@ -187,7 +187,7 @@ fn extract_init_or_reserve_target<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt fn is_reserve(cx: &LateContext<'_>, path: &PathSegment<'_>, self_expr: &Expr<'_>) -> bool { is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr).peel_refs(), sym::Vec) - && path.ident.name.as_str() == "reserve" + && path.ident.name == sym::reserve } /// Returns self if the expression is `Vec::set_len()` @@ -209,7 +209,7 @@ fn extract_set_len_self<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Opt ExprKind::MethodCall(path, self_expr, [arg], _) => { let self_type = cx.typeck_results().expr_ty(self_expr).peel_refs(); if is_type_diagnostic_item(cx, self_type, sym::Vec) - && path.ident.name.as_str() == "set_len" + && path.ident.name == sym::set_len && !is_integer_literal(arg, 0) { Some((self_expr, expr.span)) diff --git a/src/tools/clippy/clippy_lints/src/unused_self.rs b/src/tools/clippy/clippy_lints/src/unused_self.rs index d0067b1a65e..12da891a71b 100644 --- a/src/tools/clippy/clippy_lints/src/unused_self.rs +++ b/src/tools/clippy/clippy_lints/src/unused_self.rs @@ -1,6 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::macros::root_macro_call_first_node; +use clippy_utils::sym; use clippy_utils::visitors::is_local_used; use rustc_hir::{Body, Impl, ImplItem, ImplItemKind, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -61,12 +62,10 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf { let assoc_item = cx.tcx.associated_item(impl_item.owner_id); let contains_todo = |cx, body: &'_ Body<'_>| -> bool { clippy_utils::visitors::for_each_expr_without_closures(body.value, |e| { - if let Some(macro_call) = root_macro_call_first_node(cx, e) { - if cx.tcx.item_name(macro_call.def_id).as_str() == "todo" { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(()) - } + if let Some(macro_call) = root_macro_call_first_node(cx, e) + && cx.tcx.is_diagnostic_item(sym::todo_macro, macro_call.def_id) + { + ControlFlow::Break(()) } else { ControlFlow::Continue(()) } diff --git a/src/tools/clippy/clippy_lints/src/unused_unit.rs b/src/tools/clippy/clippy_lints/src/unused_unit.rs index d5309aade7a..9859ddfdf7b 100644 --- a/src/tools/clippy/clippy_lints/src/unused_unit.rs +++ b/src/tools/clippy/clippy_lints/src/unused_unit.rs @@ -1,11 +1,18 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{SpanRangeExt, position_before_rarrow}; -use rustc_ast::visit::FnKind; -use rustc_ast::{ClosureBinder, ast}; +use clippy_utils::{is_never_expr, is_unit_expr}; +use rustc_ast::{Block, StmtKind}; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{ + AssocItemConstraintKind, Body, Expr, ExprKind, FnDecl, FnRetTy, GenericArgsParentheses, Node, PolyTraitRef, Term, + Ty, TyKind, +}; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{BytePos, Span}; +use rustc_span::edition::Edition; +use rustc_span::{BytePos, Span, sym}; declare_clippy_lint! { /// ### What it does @@ -34,27 +41,89 @@ declare_clippy_lint! { declare_lint_pass!(UnusedUnit => [UNUSED_UNIT]); -impl EarlyLintPass for UnusedUnit { - fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) { - if let ast::FnRetTy::Ty(ref ty) = kind.decl().output - && let ast::TyKind::Tup(ref vals) = ty.kind - && vals.is_empty() - && !ty.span.from_expansion() - && get_def(span) == get_def(ty.span) +impl<'tcx> LateLintPass<'tcx> for UnusedUnit { + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + kind: FnKind<'tcx>, + decl: &'tcx FnDecl<'tcx>, + body: &'tcx Body<'tcx>, + span: Span, + def_id: LocalDefId, + ) { + if let FnRetTy::Return(hir_ty) = decl.output + && is_unit_ty(hir_ty) + && !hir_ty.span.from_expansion() + && get_def(span) == get_def(hir_ty.span) { // implicit types in closure signatures are forbidden when `for<...>` is present - if let FnKind::Closure(&ClosureBinder::For { .. }, ..) = kind { + if let FnKind::Closure = kind + && let Node::Expr(expr) = cx.tcx.hir_node_by_def_id(def_id) + && let ExprKind::Closure(closure) = expr.kind + && !closure.bound_generic_params.is_empty() + { + return; + } + + // unit never type fallback is no longer supported since Rust 2024. For more information, + // see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/never-type-fallback.html> + if cx.tcx.sess.edition() >= Edition::Edition2024 + && let ExprKind::Block(block, _) = body.value.kind + && let Some(expr) = block.expr + && is_never_expr(cx, expr).is_some() + { return; } - lint_unneeded_unit_return(cx, ty, span); + lint_unneeded_unit_return(cx, hir_ty.span, span); } } - fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) { - if let Some(stmt) = block.stmts.last() - && let ast::StmtKind::Expr(ref expr) = stmt.kind + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if let ExprKind::Ret(Some(expr)) | ExprKind::Break(_, Some(expr)) = expr.kind && is_unit_expr(expr) + && !expr.span.from_expansion() + { + span_lint_and_sugg( + cx, + UNUSED_UNIT, + expr.span, + "unneeded `()`", + "remove the `()`", + String::new(), + Applicability::MachineApplicable, + ); + } + } + + fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>) { + let segments = &poly.trait_ref.path.segments; + + if segments.len() == 1 + && ["Fn", "FnMut", "FnOnce"].contains(&segments[0].ident.name.as_str()) + && let Some(args) = segments[0].args + && args.parenthesized == GenericArgsParentheses::ParenSugar + && let constraints = &args.constraints + && constraints.len() == 1 + && constraints[0].ident.name == sym::Output + && let AssocItemConstraintKind::Equality { term: Term::Ty(hir_ty) } = constraints[0].kind + && args.span_ext.hi() != poly.span.hi() + && !hir_ty.span.from_expansion() + && is_unit_ty(hir_ty) + { + lint_unneeded_unit_return(cx, hir_ty.span, poly.span); + } + } +} + +impl EarlyLintPass for UnusedUnit { + /// Check for unit expressions in blocks. This is left in the early pass because some macros + /// expand its inputs as-is, making it invisible to the late pass. See #4076. + fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) { + if let Some(stmt) = block.stmts.last() + && let StmtKind::Expr(expr) = &stmt.kind + && let rustc_ast::ExprKind::Tup(inner) = &expr.kind + && inner.is_empty() && let ctxt = block.span.ctxt() && stmt.span.ctxt() == ctxt && expr.span.ctxt() == ctxt @@ -72,39 +141,10 @@ impl EarlyLintPass for UnusedUnit { ); } } +} - fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { - match e.kind { - ast::ExprKind::Ret(Some(ref expr)) | ast::ExprKind::Break(_, Some(ref expr)) => { - if is_unit_expr(expr) && !expr.span.from_expansion() { - span_lint_and_sugg( - cx, - UNUSED_UNIT, - expr.span, - "unneeded `()`", - "remove the `()`", - String::new(), - Applicability::MachineApplicable, - ); - } - }, - _ => (), - } - } - - fn check_poly_trait_ref(&mut self, cx: &EarlyContext<'_>, poly: &ast::PolyTraitRef) { - let segments = &poly.trait_ref.path.segments; - - if segments.len() == 1 - && ["Fn", "FnMut", "FnOnce"].contains(&segments[0].ident.name.as_str()) - && let Some(args) = &segments[0].args - && let ast::GenericArgs::Parenthesized(generic_args) = &**args - && let ast::FnRetTy::Ty(ty) = &generic_args.output - && ty.kind.is_unit() - { - lint_unneeded_unit_return(cx, ty, generic_args.span); - } - } +fn is_unit_ty(ty: &Ty<'_>) -> bool { + matches!(ty.kind, TyKind::Tup([])) } // get the def site @@ -117,24 +157,15 @@ fn get_def(span: Span) -> Option<Span> { } } -// is this expr a `()` unit? -fn is_unit_expr(expr: &ast::Expr) -> bool { - if let ast::ExprKind::Tup(ref vals) = expr.kind { - vals.is_empty() - } else { - false - } -} - -fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { +fn lint_unneeded_unit_return(cx: &LateContext<'_>, ty_span: Span, span: Span) { let (ret_span, appl) = - span.with_hi(ty.span.hi()) + span.with_hi(ty_span.hi()) .get_source_text(cx) - .map_or((ty.span, Applicability::MaybeIncorrect), |src| { - position_before_rarrow(&src).map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { + .map_or((ty_span, Applicability::MaybeIncorrect), |src| { + position_before_rarrow(&src).map_or((ty_span, Applicability::MaybeIncorrect), |rpos| { ( #[expect(clippy::cast_possible_truncation)] - ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), + ty_span.with_lo(BytePos(span.lo().0 + rpos as u32)), Applicability::MachineApplicable, ) }) diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs index ce82b56eb94..ba140788bb5 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap.rs @@ -208,7 +208,7 @@ fn is_option_as_mut_use(tcx: TyCtxt<'_>, expr_id: HirId) -> bool { if let Node::Expr(mutating_expr) = tcx.parent_hir_node(expr_id) && let ExprKind::MethodCall(path, _, [], _) = mutating_expr.kind { - path.ident.name.as_str() == "as_mut" + path.ident.name == sym::as_mut } else { false } @@ -278,7 +278,7 @@ fn consume_option_as_ref<'tcx>(expr: &'tcx Expr<'tcx>) -> (&'tcx Expr<'tcx>, Opt if let ExprKind::MethodCall(path, recv, [], _) = expr.kind { if path.ident.name == sym::as_ref { (recv, Some(AsRefKind::AsRef)) - } else if path.ident.name.as_str() == "as_mut" { + } else if path.ident.name == sym::as_mut { (recv, Some(AsRefKind::AsMut)) } else { (expr, None) diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index 11c14c14777..f24c127c452 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -1,8 +1,8 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::is_in_test; use clippy_utils::macros::{FormatArgsStorage, MacroCall, format_arg_removal_span, root_macro_call_first_node}; use clippy_utils::source::{SpanRangeExt, expand_past_previous_comma}; +use clippy_utils::{is_in_test, sym}; use rustc_ast::token::LitKind; use rustc_ast::{ FormatArgPosition, FormatArgPositionKind, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder, @@ -12,7 +12,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; -use rustc_span::{BytePos, Span, sym}; +use rustc_span::{BytePos, Span}; declare_clippy_lint! { /// ### What it does @@ -359,7 +359,7 @@ fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool { } fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) { - let Some(FormatArgsPiece::Literal(last)) = format_args.template.last() else { + let Some(&FormatArgsPiece::Literal(last)) = format_args.template.last() else { return; }; @@ -401,7 +401,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &Ma return; }; - if format_args.template.len() == 1 && last.as_str() == "\n" { + if format_args.template.len() == 1 && last == sym::LF { // print!("\n"), write!(f, "\n") diag.multipart_suggestion( @@ -427,9 +427,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &Ma } fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) { - if let [FormatArgsPiece::Literal(literal)] = &format_args.template[..] - && literal.as_str() == "\n" - { + if let [FormatArgsPiece::Literal(sym::LF)] = &format_args.template[..] { let mut span = format_args.span; let lint = if name == "writeln" { diff --git a/src/tools/clippy/clippy_lints/src/zombie_processes.rs b/src/tools/clippy/clippy_lints/src/zombie_processes.rs index 39c1aab8967..09f1084fe70 100644 --- a/src/tools/clippy/clippy_lints/src/zombie_processes.rs +++ b/src/tools/clippy/clippy_lints/src/zombie_processes.rs @@ -4,6 +4,7 @@ use clippy_utils::{fn_def_id, get_enclosing_block, path_to_local_id}; use rustc_ast::Mutability; use rustc_ast::visit::visit_opt; use rustc_errors::Applicability; +use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_local}; use rustc_hir::{Expr, ExprKind, HirId, LetStmt, Node, PatKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -68,6 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for ZombieProcesses { let mut vis = WaitFinder { cx, local_id, + body_id: cx.tcx.hir_enclosing_body_owner(expr.hir_id), state: VisitorState::WalkUpToLocal, early_return: None, missing_wait_branch: None, @@ -129,6 +131,7 @@ struct MaybeWait(Span); struct WaitFinder<'a, 'tcx> { cx: &'a LateContext<'tcx>, local_id: HirId, + body_id: LocalDefId, state: VisitorState, early_return: Option<Span>, // When joining two if branches where one of them doesn't call `wait()`, stores its span for more targeted help @@ -186,7 +189,7 @@ impl<'tcx> Visitor<'tcx> for WaitFinder<'_, 'tcx> { } } else { match ex.kind { - ExprKind::Ret(e) => { + ExprKind::Ret(e) if self.cx.tcx.hir_enclosing_body_owner(ex.hir_id) == self.body_id => { visit_opt!(self, visit_expr, e); if self.early_return.is_none() { self.early_return = Some(ex.span); diff --git a/src/tools/clippy/clippy_lints_internal/src/lib.rs b/src/tools/clippy/clippy_lints_internal/src/lib.rs index 1c42f4112f9..b02d378619c 100644 --- a/src/tools/clippy/clippy_lints_internal/src/lib.rs +++ b/src/tools/clippy/clippy_lints_internal/src/lib.rs @@ -2,6 +2,7 @@ #![allow( clippy::missing_docs_in_private_items, clippy::must_use_candidate, + clippy::symbol_as_str, rustc::diagnostic_outside_of_impl, rustc::untranslatable_diagnostic )] @@ -31,12 +32,12 @@ extern crate rustc_span; mod almost_standard_lint_formulation; mod collapsible_calls; -mod interning_literals; mod invalid_paths; mod lint_without_lint_pass; mod msrv_attr_impl; mod outer_expn_data_pass; mod produce_ice; +mod symbols; mod unnecessary_def_path; mod unsorted_clippy_utils_paths; @@ -45,7 +46,6 @@ use rustc_lint::{Lint, LintStore}; static LINTS: &[&Lint] = &[ almost_standard_lint_formulation::ALMOST_STANDARD_LINT_FORMULATION, collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS, - interning_literals::INTERNING_LITERALS, invalid_paths::INVALID_PATHS, lint_without_lint_pass::DEFAULT_LINT, lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE, @@ -54,6 +54,8 @@ static LINTS: &[&Lint] = &[ msrv_attr_impl::MISSING_MSRV_ATTR_IMPL, outer_expn_data_pass::OUTER_EXPN_EXPN_DATA, produce_ice::PRODUCE_ICE, + symbols::INTERNING_LITERALS, + symbols::SYMBOL_AS_STR, unnecessary_def_path::UNNECESSARY_DEF_PATH, unsorted_clippy_utils_paths::UNSORTED_CLIPPY_UTILS_PATHS, ]; @@ -65,7 +67,7 @@ pub fn register_lints(store: &mut LintStore) { store.register_early_pass(|| Box::new(produce_ice::ProduceIce)); store.register_late_pass(|_| Box::new(collapsible_calls::CollapsibleCalls)); store.register_late_pass(|_| Box::new(invalid_paths::InvalidPaths)); - store.register_late_pass(|_| Box::<interning_literals::InterningDefinedSymbol>::default()); + store.register_late_pass(|_| Box::<symbols::Symbols>::default()); store.register_late_pass(|_| Box::<lint_without_lint_pass::LintWithoutLintPass>::default()); store.register_late_pass(|_| Box::<unnecessary_def_path::UnnecessaryDefPath>::default()); store.register_late_pass(|_| Box::new(outer_expn_data_pass::OuterExpnDataPass)); diff --git a/src/tools/clippy/clippy_lints_internal/src/interning_literals.rs b/src/tools/clippy/clippy_lints_internal/src/symbols.rs index 6cee3744234..c64e5821916 100644 --- a/src/tools/clippy/clippy_lints_internal/src/interning_literals.rs +++ b/src/tools/clippy/clippy_lints_internal/src/symbols.rs @@ -1,7 +1,7 @@ -use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::match_type; -use clippy_utils::{def_path_def_ids, paths}; +use clippy_utils::{def_path_def_ids, match_def_path, paths}; +use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -11,8 +11,8 @@ use rustc_lint_defs::declare_tool_lint; use rustc_middle::mir::ConstValue; use rustc_middle::ty; use rustc_session::impl_lint_pass; -use rustc_span::sym; use rustc_span::symbol::Symbol; +use rustc_span::{Span, sym}; declare_tool_lint! { /// ### What it does @@ -36,15 +36,37 @@ declare_tool_lint! { report_in_external_macro: true } +declare_tool_lint! { + /// ### What it does + /// Checks for calls to `Symbol::as_str` + /// + /// ### Why is this bad? + /// It's faster and easier to use the symbol constant. If one doesn't exist it can be added to `clippy_utils/src/sym.rs` + /// + /// ### Example + /// ```rust,ignore + /// symbol.as_str() == "foo" + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// symbol == sym::foo + /// ``` + pub clippy::SYMBOL_AS_STR, + Warn, + "calls to `Symbol::as_str`", + report_in_external_macro: true +} + #[derive(Default)] -pub struct InterningDefinedSymbol { +pub struct Symbols { // Maps the symbol to the import path symbol_map: FxHashMap<u32, (&'static str, Symbol)>, } -impl_lint_pass!(InterningDefinedSymbol => [INTERNING_LITERALS]); +impl_lint_pass!(Symbols => [INTERNING_LITERALS, SYMBOL_AS_STR]); -impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { +impl<'tcx> LateLintPass<'tcx> for Symbols { fn check_crate(&mut self, cx: &LateContext<'_>) { let modules = [ ("kw", &paths::KW_MODULE[..]), @@ -77,7 +99,8 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { if let ExprKind::Call(func, [arg]) = &expr.kind && let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind() && cx.tcx.is_diagnostic_item(sym::SymbolIntern, *def_id) - && let Some(Constant::Str(arg)) = ConstEvalCtxt::new(cx).eval_simple(arg) + && let ExprKind::Lit(lit) = arg.kind + && let LitKind::Str(name, _) = lit.node { span_lint_and_then( cx, @@ -85,18 +108,62 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { expr.span, "interning a string literal", |diag| { - let value = Symbol::intern(&arg).as_u32(); - let (message, path) = if let Some((prefix, name)) = self.symbol_map.get(&value) { - ("use the preinterned symbol", format!("{prefix}::{name}")) - } else { - ( - "add the symbol to `clippy_utils/src/sym.rs` and use it", - format!("sym::{}", arg.replace(|ch: char| !ch.is_alphanumeric(), "_")), - ) - }; + let (message, path) = suggestion(&mut self.symbol_map, name); diag.span_suggestion_verbose(expr.span, message, path, Applicability::MaybeIncorrect); }, ); } + + if let ExprKind::Binary(_, lhs, rhs) = expr.kind { + check_binary(cx, lhs, rhs, &mut self.symbol_map); + check_binary(cx, rhs, lhs, &mut self.symbol_map); + } + } +} + +fn check_binary( + cx: &LateContext<'_>, + lhs: &Expr<'_>, + rhs: &Expr<'_>, + symbols: &mut FxHashMap<u32, (&'static str, Symbol)>, +) { + if let Some(removal_span) = as_str_span(cx, lhs) + && let ExprKind::Lit(lit) = rhs.kind + && let LitKind::Str(name, _) = lit.node + { + span_lint_and_then(cx, SYMBOL_AS_STR, lhs.span, "converting a Symbol to a string", |diag| { + let (message, path) = suggestion(symbols, name); + diag.multipart_suggestion_verbose( + message, + vec![(removal_span, String::new()), (rhs.span, path)], + Applicability::MachineApplicable, + ); + }); + } +} + +fn suggestion(symbols: &mut FxHashMap<u32, (&'static str, Symbol)>, name: Symbol) -> (&'static str, String) { + if let Some((prefix, name)) = symbols.get(&name.as_u32()) { + ("use the preinterned symbol", format!("{prefix}::{name}")) + } else { + ( + "add the symbol to `clippy_utils/src/sym.rs` and use it", + format!("sym::{}", name.as_str().replace(|ch: char| !ch.is_alphanumeric(), "_")), + ) + } +} + +/// ```ignore +/// symbol.as_str() +/// // ^^^^^^^^ +/// ``` +fn as_str_span(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Span> { + if let ExprKind::MethodCall(_, recv, [], _) = expr.kind + && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && match_def_path(cx, method_def_id, &paths::SYMBOL_AS_STR) + { + Some(recv.span.shrink_to_hi().to(expr.span.shrink_to_hi())) + } else { + None } } diff --git a/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md index aceff14a159..66192f866fa 100644 --- a/src/tools/clippy/clippy_utils/README.md +++ b/src/tools/clippy/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: <!-- begin autogenerated nightly --> ``` -nightly-2025-04-22 +nightly-2025-05-01 ``` <!-- end autogenerated nightly --> diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index d4e66ebd8e1..dbb99348290 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -3,14 +3,14 @@ #![deny(clippy::missing_docs_in_private_items)] use crate::consts::{ConstEvalCtxt, Constant}; -use crate::is_expn_of; use crate::ty::is_type_diagnostic_item; +use crate::{is_expn_of, sym}; use rustc_ast::ast; use rustc_hir as hir; use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath, StructTailExpr}; use rustc_lint::LateContext; -use rustc_span::{Span, sym, symbol}; +use rustc_span::{Span, symbol}; /// The essential nodes of a desugared for loop as well as the entire span: /// `for pat in arg { body }` becomes `(pat, arg, body)`. Returns `(pat, arg, body, span)`. @@ -474,7 +474,7 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) - return Some(VecInitKind::New); } else if name.ident.name == symbol::kw::Default { return Some(VecInitKind::Default); - } else if name.ident.name.as_str() == "with_capacity" { + } else if name.ident.name == sym::with_capacity { let arg = args.first()?; return match ConstEvalCtxt::new(cx).eval_simple(arg) { Some(Constant::Int(num)) => Some(VecInitKind::WithConstCapacity(num)), diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 17368a7530d..c37231d0931 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -1118,8 +1118,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_const_arg(e); }, TyPatKind::Or(variants) => { - for variant in variants.iter() { - self.hash_ty_pat(variant) + for variant in variants { + self.hash_ty_pat(variant); } }, TyPatKind::Err(_) => {}, diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 264b9b0406d..187dfa4dda8 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -1412,7 +1412,7 @@ pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { } /// Gets the name of the item the expression is in, if available. -pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> { +pub fn parent_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> { let parent_id = cx.tcx.hir_get_parent_item(expr.hir_id).def_id; match cx.tcx.hir_node_by_def_id(parent_id) { Node::Item(item) => item.kind.ident().map(|ident| ident.name), @@ -2088,7 +2088,7 @@ pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool { let path = cx.get_def_path(did); // libc is meant to be used as a flat list of names, but they're all actually defined in different // modules based on the target platform. Ignore everything but crate name and the item name. - path.first().is_some_and(|s| s.as_str() == "libc") && path.last().is_some_and(|s| s.as_str() == name) + path.first().is_some_and(|s| *s == sym::libc) && path.last().is_some_and(|s| s.as_str() == name) } /// Returns the list of condition expressions and the list of blocks in a @@ -3101,7 +3101,7 @@ pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span { sm.span_take_while(span, |&ch| ch == ' ' || ch == ';') } -/// Returns whether the given let pattern and else body can be turned into a question mark +/// Returns whether the given let pattern and else body can be turned into the `?` operator /// /// For this example: /// ```ignore @@ -3124,8 +3124,7 @@ pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span { /// ``` /// /// We output `Some(a)` in the first instance, and `Some(FooBar { a, b })` in the second, because -/// the question mark operator is applicable here. Callers have to check whether we are in a -/// constant or not. +/// the `?` operator is applicable here. Callers have to check whether we are in a constant or not. pub fn pat_and_expr_can_be_question_mark<'a, 'hir>( cx: &LateContext<'_>, pat: &'a Pat<'hir>, diff --git a/src/tools/clippy/clippy_utils/src/sym.rs b/src/tools/clippy/clippy_utils/src/sym.rs index 1a30b473d10..38f077134c0 100644 --- a/src/tools/clippy/clippy_utils/src/sym.rs +++ b/src/tools/clippy/clippy_utils/src/sym.rs @@ -30,33 +30,75 @@ macro_rules! generate { } generate! { + abs, as_bytes, as_deref_mut, as_deref, as_mut, Binary, + build_hasher, + cargo_clippy: "cargo-clippy", Cargo_toml: "Cargo.toml", + cast, + chars, CLIPPY_ARGS, CLIPPY_CONF_DIR, + clone_into, cloned, + collect, contains, copied, + CRLF: "\r\n", Current, + ends_with, + exp, + extend, + finish_non_exhaustive, + finish, + flat_map, + for_each, + from_raw, + from_str_radix, get, insert, int_roundings, + into_bytes, + into_owned, IntoIter, + is_ascii, is_empty, + is_err, + is_none, is_ok, is_some, + last, + LF: "\n", LowerExp, LowerHex, + max, + min, + mode, msrv, Octal, or_default, + parse, + push, regex, + reserve, + resize, + restriction, rustfmt_skip, + set_len, + set_mode, + set_readonly, + signum, + split_whitespace, + split, Start, + take, + TBD, + then_some, + to_digit, to_owned, unused_extern_crates, unwrap_err, @@ -66,4 +108,6 @@ generate! { V4, V6, Weak, + with_capacity, + wrapping_offset, } diff --git a/src/tools/clippy/rust-toolchain.toml b/src/tools/clippy/rust-toolchain.toml index d2f79da1a54..39c7f0e4ad5 100644 --- a/src/tools/clippy/rust-toolchain.toml +++ b/src/tools/clippy/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-04-22" +channel = "nightly-2025-05-01" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/tools/clippy/tests/ui-internal/symbol_as_str.fixed b/src/tools/clippy/tests/ui-internal/symbol_as_str.fixed new file mode 100644 index 00000000000..3e26732836c --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/symbol_as_str.fixed @@ -0,0 +1,21 @@ +#![feature(rustc_private)] + +extern crate rustc_span; + +use clippy_utils::sym; +use rustc_span::{Symbol, kw}; + +fn f(s: Symbol) { + s == sym::f32; + //~^ symbol_as_str + s == sym::proc_dash_macro; + //~^ symbol_as_str + s == kw::SelfLower; + //~^ symbol_as_str + s == sym::msrv; + //~^ symbol_as_str + s == sym::Cargo_toml; + //~^ symbol_as_str + sym::get == s; + //~^ symbol_as_str +} diff --git a/src/tools/clippy/tests/ui-internal/symbol_as_str.rs b/src/tools/clippy/tests/ui-internal/symbol_as_str.rs new file mode 100644 index 00000000000..334c32d1898 --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/symbol_as_str.rs @@ -0,0 +1,21 @@ +#![feature(rustc_private)] + +extern crate rustc_span; + +use clippy_utils::sym; +use rustc_span::{Symbol, kw}; + +fn f(s: Symbol) { + s.as_str() == "f32"; + //~^ symbol_as_str + s.as_str() == "proc-macro"; + //~^ symbol_as_str + s.as_str() == "self"; + //~^ symbol_as_str + s.as_str() == "msrv"; + //~^ symbol_as_str + s.as_str() == "Cargo.toml"; + //~^ symbol_as_str + "get" == s.as_str(); + //~^ symbol_as_str +} diff --git a/src/tools/clippy/tests/ui-internal/symbol_as_str.stderr b/src/tools/clippy/tests/ui-internal/symbol_as_str.stderr new file mode 100644 index 00000000000..39f81f3833c --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/symbol_as_str.stderr @@ -0,0 +1,76 @@ +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str.rs:9:5 + | +LL | s.as_str() == "f32"; + | ^^^^^^^^^^ + | + = note: `-D clippy::symbol-as-str` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::symbol_as_str)]` +help: use the preinterned symbol + | +LL - s.as_str() == "f32"; +LL + s == sym::f32; + | + +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str.rs:11:5 + | +LL | s.as_str() == "proc-macro"; + | ^^^^^^^^^^ + | +help: use the preinterned symbol + | +LL - s.as_str() == "proc-macro"; +LL + s == sym::proc_dash_macro; + | + +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str.rs:13:5 + | +LL | s.as_str() == "self"; + | ^^^^^^^^^^ + | +help: use the preinterned symbol + | +LL - s.as_str() == "self"; +LL + s == kw::SelfLower; + | + +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str.rs:15:5 + | +LL | s.as_str() == "msrv"; + | ^^^^^^^^^^ + | +help: use the preinterned symbol + | +LL - s.as_str() == "msrv"; +LL + s == sym::msrv; + | + +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str.rs:17:5 + | +LL | s.as_str() == "Cargo.toml"; + | ^^^^^^^^^^ + | +help: use the preinterned symbol + | +LL - s.as_str() == "Cargo.toml"; +LL + s == sym::Cargo_toml; + | + +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str.rs:19:14 + | +LL | "get" == s.as_str(); + | ^^^^^^^^^^ + | +help: use the preinterned symbol + | +LL - "get" == s.as_str(); +LL + sym::get == s; + | + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui-internal/symbol_as_str_unfixable.rs b/src/tools/clippy/tests/ui-internal/symbol_as_str_unfixable.rs new file mode 100644 index 00000000000..635f28007e9 --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/symbol_as_str_unfixable.rs @@ -0,0 +1,15 @@ +//@no-rustfix: paths that don't exist yet +#![feature(rustc_private)] + +extern crate rustc_span; + +use rustc_span::Symbol; + +fn f(s: Symbol) { + s.as_str() == "xyz123"; + //~^ symbol_as_str + s.as_str() == "with-dash"; + //~^ symbol_as_str + s.as_str() == "with.dot"; + //~^ symbol_as_str +} diff --git a/src/tools/clippy/tests/ui-internal/symbol_as_str_unfixable.stderr b/src/tools/clippy/tests/ui-internal/symbol_as_str_unfixable.stderr new file mode 100644 index 00000000000..5349983ca51 --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/symbol_as_str_unfixable.stderr @@ -0,0 +1,40 @@ +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str_unfixable.rs:9:5 + | +LL | s.as_str() == "xyz123"; + | ^^^^^^^^^^ + | + = note: `-D clippy::symbol-as-str` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::symbol_as_str)]` +help: add the symbol to `clippy_utils/src/sym.rs` and use it + | +LL - s.as_str() == "xyz123"; +LL + s == sym::xyz123; + | + +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str_unfixable.rs:11:5 + | +LL | s.as_str() == "with-dash"; + | ^^^^^^^^^^ + | +help: add the symbol to `clippy_utils/src/sym.rs` and use it + | +LL - s.as_str() == "with-dash"; +LL + s == sym::with_dash; + | + +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str_unfixable.rs:13:5 + | +LL | s.as_str() == "with.dot"; + | ^^^^^^^^^^ + | +help: add the symbol to `clippy_utils/src/sym.rs` and use it + | +LL - s.as_str() == "with.dot"; +LL + s == sym::with_dot; + | + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions.fixed b/src/tools/clippy/tests/ui/blocks_in_conditions.fixed index e696896538e..6ae5b0cb2f0 100644 --- a/src/tools/clippy/tests/ui/blocks_in_conditions.fixed +++ b/src/tools/clippy/tests/ui/blocks_in_conditions.fixed @@ -4,7 +4,6 @@ #![allow( unused, unnecessary_transmutes, - clippy::let_and_return, clippy::needless_if, clippy::missing_transmute_annotations )] diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions.rs b/src/tools/clippy/tests/ui/blocks_in_conditions.rs index 8c8f3249b8a..3fd06062072 100644 --- a/src/tools/clippy/tests/ui/blocks_in_conditions.rs +++ b/src/tools/clippy/tests/ui/blocks_in_conditions.rs @@ -4,7 +4,6 @@ #![allow( unused, unnecessary_transmutes, - clippy::let_and_return, clippy::needless_if, clippy::missing_transmute_annotations )] diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions.stderr b/src/tools/clippy/tests/ui/blocks_in_conditions.stderr index 41ff59c683e..282c42a98bf 100644 --- a/src/tools/clippy/tests/ui/blocks_in_conditions.stderr +++ b/src/tools/clippy/tests/ui/blocks_in_conditions.stderr @@ -1,5 +1,5 @@ error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> tests/ui/blocks_in_conditions.rs:31:5 + --> tests/ui/blocks_in_conditions.rs:30:5 | LL | / if { LL | | @@ -20,13 +20,13 @@ LL ~ }; if res { | error: omit braces around single expression condition - --> tests/ui/blocks_in_conditions.rs:43:8 + --> tests/ui/blocks_in_conditions.rs:42:8 | LL | if { true } { 6 } else { 10 } | ^^^^^^^^ help: try: `true` error: this boolean expression can be simplified - --> tests/ui/blocks_in_conditions.rs:49:8 + --> tests/ui/blocks_in_conditions.rs:48:8 | LL | if true && x == 3 { 6 } else { 10 } | ^^^^^^^^^^^^^^ help: try: `x == 3` diff --git a/src/tools/clippy/tests/ui/equatable_if_let.fixed b/src/tools/clippy/tests/ui/equatable_if_let.fixed index 166b1387ba2..ce8b67f9ca7 100644 --- a/src/tools/clippy/tests/ui/equatable_if_let.fixed +++ b/src/tools/clippy/tests/ui/equatable_if_let.fixed @@ -103,3 +103,39 @@ fn main() { external!({ if let 2 = $a {} }); } + +mod issue8710 { + fn str_ref(cs: &[char]) { + if matches!(cs.iter().next(), Some('i')) { + //~^ equatable_if_let + } else { + todo!(); + } + } + + fn i32_ref(cs: &[i32]) { + if matches!(cs.iter().next(), Some(1)) { + //~^ equatable_if_let + } else { + todo!(); + } + } + + fn enum_ref() { + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] + enum MyEnum { + A(i32), + B, + } + + fn get_enum() -> Option<&'static MyEnum> { + todo!() + } + + if matches!(get_enum(), Some(MyEnum::B)) { + //~^ equatable_if_let + } else { + todo!(); + } + } +} diff --git a/src/tools/clippy/tests/ui/equatable_if_let.rs b/src/tools/clippy/tests/ui/equatable_if_let.rs index 09c2483ae6d..ff09533f265 100644 --- a/src/tools/clippy/tests/ui/equatable_if_let.rs +++ b/src/tools/clippy/tests/ui/equatable_if_let.rs @@ -103,3 +103,39 @@ fn main() { external!({ if let 2 = $a {} }); } + +mod issue8710 { + fn str_ref(cs: &[char]) { + if let Some('i') = cs.iter().next() { + //~^ equatable_if_let + } else { + todo!(); + } + } + + fn i32_ref(cs: &[i32]) { + if let Some(1) = cs.iter().next() { + //~^ equatable_if_let + } else { + todo!(); + } + } + + fn enum_ref() { + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] + enum MyEnum { + A(i32), + B, + } + + fn get_enum() -> Option<&'static MyEnum> { + todo!() + } + + if let Some(MyEnum::B) = get_enum() { + //~^ equatable_if_let + } else { + todo!(); + } + } +} diff --git a/src/tools/clippy/tests/ui/equatable_if_let.stderr b/src/tools/clippy/tests/ui/equatable_if_let.stderr index 81e0e15a5c7..dd1832ad68b 100644 --- a/src/tools/clippy/tests/ui/equatable_if_let.stderr +++ b/src/tools/clippy/tests/ui/equatable_if_let.stderr @@ -85,5 +85,23 @@ error: this pattern matching can be expressed using equality LL | if let inline!("abc") = "abc" { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"abc" == inline!("abc")` -error: aborting due to 14 previous errors +error: this pattern matching can be expressed using `matches!` + --> tests/ui/equatable_if_let.rs:109:12 + | +LL | if let Some('i') = cs.iter().next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(cs.iter().next(), Some('i'))` + +error: this pattern matching can be expressed using `matches!` + --> tests/ui/equatable_if_let.rs:117:12 + | +LL | if let Some(1) = cs.iter().next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(cs.iter().next(), Some(1))` + +error: this pattern matching can be expressed using `matches!` + --> tests/ui/equatable_if_let.rs:135:12 + | +LL | if let Some(MyEnum::B) = get_enum() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(get_enum(), Some(MyEnum::B))` + +error: aborting due to 17 previous errors diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.fixed b/src/tools/clippy/tests/ui/manual_div_ceil.fixed index 57fe8917afe..58ee6978fc1 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil.fixed +++ b/src/tools/clippy/tests/ui/manual_div_ceil.fixed @@ -1,5 +1,21 @@ #![warn(clippy::manual_div_ceil)] +macro_rules! y { + () => { + let x = 33u32; + let _ = x.div_ceil(8); + //~^ manual_div_ceil + let _ = x.div_ceil(8); + //~^ manual_div_ceil + }; +} + +macro_rules! eight { + () => { + 8 + }; +} + fn main() { let x = 7_u32; let y = 4_u32; @@ -32,6 +48,13 @@ fn main() { let _ = (z as i32 + (y_i - 1)) / y_i; let _ = (7_u32 as i32 + (y_i - 1)) / y_i; let _ = (7_u32 as i32 + (4 - 1)) / 4; + + // Test lint with macro + y!(); + + // Also test if RHS should be result of macro expansion + let _ = 33u32.div_ceil(eight!()); + //~^ manual_div_ceil } fn issue_13843() { diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.rs b/src/tools/clippy/tests/ui/manual_div_ceil.rs index ec343513e5c..aa0d81b22a0 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil.rs +++ b/src/tools/clippy/tests/ui/manual_div_ceil.rs @@ -1,5 +1,21 @@ #![warn(clippy::manual_div_ceil)] +macro_rules! y { + () => { + let x = 33u32; + let _ = (x + 7) / 8; + //~^ manual_div_ceil + let _ = (7 + x) / 8; + //~^ manual_div_ceil + }; +} + +macro_rules! eight { + () => { + 8 + }; +} + fn main() { let x = 7_u32; let y = 4_u32; @@ -32,6 +48,13 @@ fn main() { let _ = (z as i32 + (y_i - 1)) / y_i; let _ = (7_u32 as i32 + (y_i - 1)) / y_i; let _ = (7_u32 as i32 + (4 - 1)) / 4; + + // Test lint with macro + y!(); + + // Also test if RHS should be result of macro expansion + let _ = (33u32 + 7) / eight!(); + //~^ manual_div_ceil } fn issue_13843() { diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.stderr b/src/tools/clippy/tests/ui/manual_div_ceil.stderr index 8e14ab27426..9be5a19bf39 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil.stderr +++ b/src/tools/clippy/tests/ui/manual_div_ceil.stderr @@ -1,5 +1,5 @@ error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:9:13 + --> tests/ui/manual_div_ceil.rs:25:13 | LL | let _ = (x + (y - 1)) / y; | ^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)` @@ -8,94 +8,122 @@ LL | let _ = (x + (y - 1)) / y; = help: to override `-D warnings` add `#[allow(clippy::manual_div_ceil)]` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:11:13 + --> tests/ui/manual_div_ceil.rs:27:13 | LL | let _ = ((y - 1) + x) / y; | ^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:13:13 + --> tests/ui/manual_div_ceil.rs:29:13 | LL | let _ = (x + y - 1) / y; | ^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:16:13 + --> tests/ui/manual_div_ceil.rs:32:13 | LL | let _ = (7_u32 + (4 - 1)) / 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `7_u32.div_ceil(4)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:18:13 + --> tests/ui/manual_div_ceil.rs:34:13 | LL | let _ = (7_i32 as u32 + (4 - 1)) / 4; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `(7_i32 as u32).div_ceil(4)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:39:13 + --> tests/ui/manual_div_ceil.rs:6:17 + | +LL | let _ = (x + 7) / 8; + | ^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` +... +LL | y!(); + | ---- in this macro invocation + | + = note: this error originates in the macro `y` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:8:17 + | +LL | let _ = (7 + x) / 8; + | ^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` +... +LL | y!(); + | ---- in this macro invocation + | + = note: this error originates in the macro `y` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:56:13 + | +LL | let _ = (33u32 + 7) / eight!(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `33u32.div_ceil(eight!())` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:62:13 | LL | let _ = (2048 + x - 1) / x; | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(x)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:43:13 + --> tests/ui/manual_div_ceil.rs:66:13 | LL | let _ = (2048usize + x - 1) / x; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048usize.div_ceil(x)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:47:13 + --> tests/ui/manual_div_ceil.rs:70:13 | LL | let _ = (2048_usize + x - 1) / x; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(x)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:51:13 + --> tests/ui/manual_div_ceil.rs:74:13 | LL | let _ = (x + 4 - 1) / 4; | ^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(4)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:54:18 + --> tests/ui/manual_div_ceil.rs:77:18 | LL | let _: u32 = (2048 + 6 - 1) / 6; | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_u32.div_ceil(6)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:56:20 + --> tests/ui/manual_div_ceil.rs:79:20 | LL | let _: usize = (2048 + 6 - 1) / 6; | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(6)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:58:18 + --> tests/ui/manual_div_ceil.rs:81:18 | LL | let _: u32 = (0x2048 + 0x6 - 1) / 0x6; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `0x2048_u32.div_ceil(0x6)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:61:13 + --> tests/ui/manual_div_ceil.rs:84:13 | LL | let _ = (2048 + 6u32 - 1) / 6u32; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_u32.div_ceil(6u32)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:64:13 + --> tests/ui/manual_div_ceil.rs:87:13 | LL | let _ = (1_000_000 + 6u32 - 1) / 6u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `1_000_000_u32.div_ceil(6u32)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:70:13 + --> tests/ui/manual_div_ceil.rs:93:13 | LL | let _ = (x + 7) / 8; | ^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:72:13 + --> tests/ui/manual_div_ceil.rs:95:13 | LL | let _ = (7 + x) / 8; | ^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` -error: aborting due to 16 previous errors +error: aborting due to 19 previous errors diff --git a/src/tools/clippy/tests/ui/needless_if.fixed b/src/tools/clippy/tests/ui/needless_if.fixed index 6208ca19b82..347dbff7c59 100644 --- a/src/tools/clippy/tests/ui/needless_if.fixed +++ b/src/tools/clippy/tests/ui/needless_if.fixed @@ -46,9 +46,7 @@ fn main() { if let true = true && true {} - if true - && let true = true - {} + if true && let true = true {} // Can lint nested `if let`s ({ //~^ needless_if diff --git a/src/tools/clippy/tests/ui/needless_if.rs b/src/tools/clippy/tests/ui/needless_if.rs index b459ff877be..5e0f2a14408 100644 --- a/src/tools/clippy/tests/ui/needless_if.rs +++ b/src/tools/clippy/tests/ui/needless_if.rs @@ -46,9 +46,7 @@ fn main() { if let true = true && true {} - if true - && let true = true - {} + if true && let true = true {} // Can lint nested `if let`s if { //~^ needless_if diff --git a/src/tools/clippy/tests/ui/needless_if.stderr b/src/tools/clippy/tests/ui/needless_if.stderr index eeb8d044526..62cdf245944 100644 --- a/src/tools/clippy/tests/ui/needless_if.stderr +++ b/src/tools/clippy/tests/ui/needless_if.stderr @@ -31,7 +31,7 @@ LL + }); | error: this `if` branch is empty - --> tests/ui/needless_if.rs:53:5 + --> tests/ui/needless_if.rs:51:5 | LL | / if { LL | | @@ -57,19 +57,19 @@ LL + } && true); | error: this `if` branch is empty - --> tests/ui/needless_if.rs:98:5 + --> tests/ui/needless_if.rs:96:5 | LL | if { maybe_side_effect() } {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `({ maybe_side_effect() });` error: this `if` branch is empty - --> tests/ui/needless_if.rs:101:5 + --> tests/ui/needless_if.rs:99:5 | LL | if { maybe_side_effect() } && true {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `({ maybe_side_effect() } && true);` error: this `if` branch is empty - --> tests/ui/needless_if.rs:106:5 + --> tests/ui/needless_if.rs:104:5 | LL | if true {} | ^^^^^^^^^^ help: you can remove it: `true;` diff --git a/src/tools/clippy/tests/ui/needless_late_init.fixed b/src/tools/clippy/tests/ui/needless_late_init.fixed index 391d4bc3fcc..f832752ccd7 100644 --- a/src/tools/clippy/tests/ui/needless_late_init.fixed +++ b/src/tools/clippy/tests/ui/needless_late_init.fixed @@ -246,9 +246,7 @@ fn does_not_lint() { } let x; - if true - && let Some(n) = Some("let chains too") - { + if true && let Some(n) = Some("let chains too") { x = 1; } else { x = 2; diff --git a/src/tools/clippy/tests/ui/needless_late_init.rs b/src/tools/clippy/tests/ui/needless_late_init.rs index 6096e8300e1..a52fbf52923 100644 --- a/src/tools/clippy/tests/ui/needless_late_init.rs +++ b/src/tools/clippy/tests/ui/needless_late_init.rs @@ -246,9 +246,7 @@ fn does_not_lint() { } let x; - if true - && let Some(n) = Some("let chains too") - { + if true && let Some(n) = Some("let chains too") { x = 1; } else { x = 2; diff --git a/src/tools/clippy/tests/ui/needless_late_init.stderr b/src/tools/clippy/tests/ui/needless_late_init.stderr index e7c36136847..b24c1275881 100644 --- a/src/tools/clippy/tests/ui/needless_late_init.stderr +++ b/src/tools/clippy/tests/ui/needless_late_init.stderr @@ -276,7 +276,7 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:302:5 + --> tests/ui/needless_late_init.rs:300:5 | LL | let r; | ^^^^^^ created here diff --git a/src/tools/clippy/tests/ui/needless_question_mark.stderr b/src/tools/clippy/tests/ui/needless_question_mark.stderr index 55da4f28976..8516cee48e6 100644 --- a/src/tools/clippy/tests/ui/needless_question_mark.stderr +++ b/src/tools/clippy/tests/ui/needless_question_mark.stderr @@ -1,100 +1,188 @@ -error: question mark operator is useless here +error: enclosing `Some` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:20:12 | LL | return Some(to.magic?); - | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic` + | ^^^^^^^^^^^^^^^ | = note: `-D clippy::needless-question-mark` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_question_mark)]` +help: remove the enclosing `Some` and `?` operator + | +LL - return Some(to.magic?); +LL + return to.magic; + | -error: question mark operator is useless here +error: enclosing `Some` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:29:12 | LL | return Some(to.magic?) - | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic` + | ^^^^^^^^^^^^^^^ + | +help: remove the enclosing `Some` and `?` operator + | +LL - return Some(to.magic?) +LL + return to.magic + | -error: question mark operator is useless here +error: enclosing `Some` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:35:5 | LL | Some(to.magic?) - | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic` + | ^^^^^^^^^^^^^^^ + | +help: remove the enclosing `Some` and `?` operator + | +LL - Some(to.magic?) +LL + to.magic + | -error: question mark operator is useless here +error: enclosing `Some` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:41:21 | LL | to.and_then(|t| Some(t.magic?)) - | ^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `t.magic` + | ^^^^^^^^^^^^^^ + | +help: remove the enclosing `Some` and `?` operator + | +LL - to.and_then(|t| Some(t.magic?)) +LL + to.and_then(|t| t.magic) + | -error: question mark operator is useless here +error: enclosing `Some` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:51:9 | LL | Some(t.magic?) - | ^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `t.magic` + | ^^^^^^^^^^^^^^ + | +help: remove the enclosing `Some` and `?` operator + | +LL - Some(t.magic?) +LL + t.magic + | -error: question mark operator is useless here +error: enclosing `Ok` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:57:12 | LL | return Ok(tr.magic?); - | ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic` + | ^^^^^^^^^^^^^ + | +help: remove the enclosing `Ok` and `?` operator + | +LL - return Ok(tr.magic?); +LL + return tr.magic; + | -error: question mark operator is useless here +error: enclosing `Ok` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:65:12 | LL | return Ok(tr.magic?) - | ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic` + | ^^^^^^^^^^^^^ + | +help: remove the enclosing `Ok` and `?` operator + | +LL - return Ok(tr.magic?) +LL + return tr.magic + | -error: question mark operator is useless here +error: enclosing `Ok` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:70:5 | LL | Ok(tr.magic?) - | ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic` + | ^^^^^^^^^^^^^ + | +help: remove the enclosing `Ok` and `?` operator + | +LL - Ok(tr.magic?) +LL + tr.magic + | -error: question mark operator is useless here +error: enclosing `Ok` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:75:21 | LL | tr.and_then(|t| Ok(t.magic?)) - | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic` + | ^^^^^^^^^^^^ + | +help: remove the enclosing `Ok` and `?` operator + | +LL - tr.and_then(|t| Ok(t.magic?)) +LL + tr.and_then(|t| t.magic) + | -error: question mark operator is useless here +error: enclosing `Ok` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:84:9 | LL | Ok(t.magic?) - | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic` + | ^^^^^^^^^^^^ + | +help: remove the enclosing `Ok` and `?` operator + | +LL - Ok(t.magic?) +LL + t.magic + | -error: question mark operator is useless here +error: enclosing `Ok` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:92:16 | LL | return Ok(t.magic?); - | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic` + | ^^^^^^^^^^^^ + | +help: remove the enclosing `Ok` and `?` operator + | +LL - return Ok(t.magic?); +LL + return t.magic; + | -error: question mark operator is useless here +error: enclosing `Some` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:128:27 | LL | || -> Option<_> { Some(Some($expr)?) }() - | ^^^^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `Some($expr)` + | ^^^^^^^^^^^^^^^^^^ ... LL | let _x = some_and_qmark_in_macro!(x?); | ---------------------------- in this macro invocation | = note: this error originates in the macro `some_and_qmark_in_macro` (in Nightly builds, run with -Z macro-backtrace for more info) +help: remove the enclosing `Some` and `?` operator + | +LL - || -> Option<_> { Some(Some($expr)?) }() +LL + || -> Option<_> { Some($expr) }() + | -error: question mark operator is useless here +error: enclosing `Some` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:140:5 | LL | Some(to.magic?) - | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic` + | ^^^^^^^^^^^^^^^ + | +help: remove the enclosing `Some` and `?` operator + | +LL - Some(to.magic?) +LL + to.magic + | -error: question mark operator is useless here +error: enclosing `Ok` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:149:5 | LL | Ok(s.magic?) - | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `s.magic` + | ^^^^^^^^^^^^ + | +help: remove the enclosing `Ok` and `?` operator + | +LL - Ok(s.magic?) +LL + s.magic + | -error: question mark operator is useless here +error: enclosing `Some` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:154:7 | LL | { Some(a?) } - | ^^^^^^^^ help: try removing question mark and `Some()`: `a` + | ^^^^^^^^ + | +help: remove the enclosing `Some` and `?` operator + | +LL - { Some(a?) } +LL + { a } + | error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/ptr_eq.fixed b/src/tools/clippy/tests/ui/ptr_eq.fixed index 484ff307323..9629b3eea58 100644 --- a/src/tools/clippy/tests/ui/ptr_eq.fixed +++ b/src/tools/clippy/tests/ui/ptr_eq.fixed @@ -23,23 +23,25 @@ fn main() { //~^ ptr_eq let _ = std::ptr::eq(a, b); //~^ ptr_eq - let _ = std::ptr::eq(a.as_ptr(), b as *const _); - //~^ ptr_eq - let _ = std::ptr::eq(a.as_ptr(), b.as_ptr()); - //~^ ptr_eq - // Do not lint + // Do not lint: the rhs conversion is needed + let _ = a.as_ptr() == b as *const _; + // Do not lint: we have two raw pointers already + let _ = a.as_ptr() == b.as_ptr(); + + // Do not lint let _ = mac!(a, b); let _ = another_mac!(a, b); let a = &mut [1, 2, 3]; let b = &mut [1, 2, 3]; - let _ = std::ptr::eq(a.as_mut_ptr(), b as *mut [i32] as *mut _); - //~^ ptr_eq - let _ = std::ptr::eq(a.as_mut_ptr(), b.as_mut_ptr()); - //~^ ptr_eq + // Do not lint: the rhs conversion is needed + let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; + + // Do not lint: we have two raw pointers already + let _ = a.as_mut_ptr() == b.as_mut_ptr(); let _ = a == b; let _ = core::ptr::eq(a, b); @@ -51,8 +53,12 @@ fn main() { let _ = !std::ptr::eq(x, y); //~^ ptr_eq - #[allow(clippy::eq_op)] - let _issue14337 = std::ptr::eq(main as *const (), main as *const ()); + #[expect(clippy::eq_op)] + // Do not lint: casts are needed to not change type + let _issue14337 = main as *const () == main as *const (); + + // Do not peel the content of macros + let _ = std::ptr::eq(mac!(cast a), mac!(cast b)); //~^ ptr_eq // Do not peel the content of macros diff --git a/src/tools/clippy/tests/ui/ptr_eq.rs b/src/tools/clippy/tests/ui/ptr_eq.rs index f28707cc3e9..2b741d8df46 100644 --- a/src/tools/clippy/tests/ui/ptr_eq.rs +++ b/src/tools/clippy/tests/ui/ptr_eq.rs @@ -23,23 +23,25 @@ fn main() { //~^ ptr_eq let _ = a as *const _ == b as *const _; //~^ ptr_eq + + // Do not lint: the rhs conversion is needed let _ = a.as_ptr() == b as *const _; - //~^ ptr_eq + + // Do not lint: we have two raw pointers already let _ = a.as_ptr() == b.as_ptr(); - //~^ ptr_eq // Do not lint - let _ = mac!(a, b); let _ = another_mac!(a, b); let a = &mut [1, 2, 3]; let b = &mut [1, 2, 3]; + // Do not lint: the rhs conversion is needed let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; - //~^ ptr_eq + + // Do not lint: we have two raw pointers already let _ = a.as_mut_ptr() == b.as_mut_ptr(); - //~^ ptr_eq let _ = a == b; let _ = core::ptr::eq(a, b); @@ -51,8 +53,12 @@ fn main() { let _ = x as *const u32 != y as *mut u32 as *const u32; //~^ ptr_eq - #[allow(clippy::eq_op)] + #[expect(clippy::eq_op)] + // Do not lint: casts are needed to not change type let _issue14337 = main as *const () == main as *const (); + + // Do not peel the content of macros + let _ = mac!(cast a) as *const _ == mac!(cast b) as *const _; //~^ ptr_eq // Do not peel the content of macros diff --git a/src/tools/clippy/tests/ui/ptr_eq.stderr b/src/tools/clippy/tests/ui/ptr_eq.stderr index 906831b9e03..e7340624b59 100644 --- a/src/tools/clippy/tests/ui/ptr_eq.stderr +++ b/src/tools/clippy/tests/ui/ptr_eq.stderr @@ -14,52 +14,28 @@ LL | let _ = a as *const _ == b as *const _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a, b)` error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:26:13 - | -LL | let _ = a.as_ptr() == b as *const _; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a.as_ptr(), b as *const _)` - -error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:28:13 - | -LL | let _ = a.as_ptr() == b.as_ptr(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a.as_ptr(), b.as_ptr())` - -error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:39:13 - | -LL | let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a.as_mut_ptr(), b as *mut [i32] as *mut _)` - -error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:41:13 - | -LL | let _ = a.as_mut_ptr() == b.as_mut_ptr(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a.as_mut_ptr(), b.as_mut_ptr())` - -error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:48:13 + --> tests/ui/ptr_eq.rs:50:13 | LL | let _ = x as *const u32 == y as *mut u32 as *const u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(x, y)` error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:51:13 + --> tests/ui/ptr_eq.rs:53:13 | LL | let _ = x as *const u32 != y as *mut u32 as *const u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!std::ptr::eq(x, y)` error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:55:23 + --> tests/ui/ptr_eq.rs:61:13 | -LL | let _issue14337 = main as *const () == main as *const (); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(main as *const (), main as *const ())` +LL | let _ = mac!(cast a) as *const _ == mac!(cast b) as *const _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(mac!(cast a), mac!(cast b))` error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:59:13 + --> tests/ui/ptr_eq.rs:65:13 | LL | let _ = mac!(cast a) as *const _ == mac!(cast b) as *const _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(mac!(cast a), mac!(cast b))` -error: aborting due to 10 previous errors +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/ptr_eq_no_std.fixed b/src/tools/clippy/tests/ui/ptr_eq_no_std.fixed index d8ee4ea88f8..48cbad62e1a 100644 --- a/src/tools/clippy/tests/ui/ptr_eq_no_std.fixed +++ b/src/tools/clippy/tests/ui/ptr_eq_no_std.fixed @@ -32,23 +32,25 @@ fn main() { //~^ ptr_eq let _ = core::ptr::eq(a, b); //~^ ptr_eq - let _ = core::ptr::eq(a.as_ptr(), b as *const _); - //~^ ptr_eq - let _ = core::ptr::eq(a.as_ptr(), b.as_ptr()); - //~^ ptr_eq - // Do not lint + // Do not lint: the rhs conversion is needed + let _ = a.as_ptr() == b as *const _; + + // Do not lint: we have two raw pointers already + let _ = a.as_ptr() == b.as_ptr(); + // Do not lint let _ = mac!(a, b); let _ = another_mac!(a, b); let a = &mut [1, 2, 3]; let b = &mut [1, 2, 3]; - let _ = core::ptr::eq(a.as_mut_ptr(), b as *mut [i32] as *mut _); - //~^ ptr_eq - let _ = core::ptr::eq(a.as_mut_ptr(), b.as_mut_ptr()); - //~^ ptr_eq + // Do not lint: the rhs conversion is needed + let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; + + // Do not lint: we have two raw pointers already + let _ = a.as_mut_ptr() == b.as_mut_ptr(); let _ = a == b; let _ = core::ptr::eq(a, b); diff --git a/src/tools/clippy/tests/ui/ptr_eq_no_std.rs b/src/tools/clippy/tests/ui/ptr_eq_no_std.rs index a236314c29b..3827178640e 100644 --- a/src/tools/clippy/tests/ui/ptr_eq_no_std.rs +++ b/src/tools/clippy/tests/ui/ptr_eq_no_std.rs @@ -32,23 +32,25 @@ fn main() { //~^ ptr_eq let _ = a as *const _ == b as *const _; //~^ ptr_eq + + // Do not lint: the rhs conversion is needed let _ = a.as_ptr() == b as *const _; - //~^ ptr_eq + + // Do not lint: we have two raw pointers already let _ = a.as_ptr() == b.as_ptr(); - //~^ ptr_eq // Do not lint - let _ = mac!(a, b); let _ = another_mac!(a, b); let a = &mut [1, 2, 3]; let b = &mut [1, 2, 3]; + // Do not lint: the rhs conversion is needed let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; - //~^ ptr_eq + + // Do not lint: we have two raw pointers already let _ = a.as_mut_ptr() == b.as_mut_ptr(); - //~^ ptr_eq let _ = a == b; let _ = core::ptr::eq(a, b); diff --git a/src/tools/clippy/tests/ui/ptr_eq_no_std.stderr b/src/tools/clippy/tests/ui/ptr_eq_no_std.stderr index 5b8135dc8e8..8c7b1ff7666 100644 --- a/src/tools/clippy/tests/ui/ptr_eq_no_std.stderr +++ b/src/tools/clippy/tests/ui/ptr_eq_no_std.stderr @@ -13,29 +13,5 @@ error: use `core::ptr::eq` when comparing raw pointers LL | let _ = a as *const _ == b as *const _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::eq(a, b)` -error: use `core::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq_no_std.rs:35:13 - | -LL | let _ = a.as_ptr() == b as *const _; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::eq(a.as_ptr(), b as *const _)` - -error: use `core::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq_no_std.rs:37:13 - | -LL | let _ = a.as_ptr() == b.as_ptr(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::eq(a.as_ptr(), b.as_ptr())` - -error: use `core::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq_no_std.rs:48:13 - | -LL | let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::eq(a.as_mut_ptr(), b as *mut [i32] as *mut _)` - -error: use `core::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq_no_std.rs:50:13 - | -LL | let _ = a.as_mut_ptr() == b.as_mut_ptr(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::eq(a.as_mut_ptr(), b.as_mut_ptr())` - -error: aborting due to 6 previous errors +error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/question_mark.fixed b/src/tools/clippy/tests/ui/question_mark.fixed index 6bd071d07f5..507bc2b29d8 100644 --- a/src/tools/clippy/tests/ui/question_mark.fixed +++ b/src/tools/clippy/tests/ui/question_mark.fixed @@ -301,6 +301,11 @@ fn pattern() -> Result<(), PatternedError> { res } +fn expect_expr(a: Option<usize>) -> Option<usize> { + #[expect(clippy::needless_question_mark)] + Some(a?) +} + fn main() {} // `?` is not the same as `return None;` if inside of a try block diff --git a/src/tools/clippy/tests/ui/question_mark.rs b/src/tools/clippy/tests/ui/question_mark.rs index dd093c9bf48..64b51b849ed 100644 --- a/src/tools/clippy/tests/ui/question_mark.rs +++ b/src/tools/clippy/tests/ui/question_mark.rs @@ -371,6 +371,11 @@ fn pattern() -> Result<(), PatternedError> { res } +fn expect_expr(a: Option<usize>) -> Option<usize> { + #[expect(clippy::needless_question_mark)] + Some(a?) +} + fn main() {} // `?` is not the same as `return None;` if inside of a try block diff --git a/src/tools/clippy/tests/ui/question_mark.stderr b/src/tools/clippy/tests/ui/question_mark.stderr index 8fe04b895ce..d8ce4420aee 100644 --- a/src/tools/clippy/tests/ui/question_mark.stderr +++ b/src/tools/clippy/tests/ui/question_mark.stderr @@ -198,7 +198,7 @@ LL | | } | |_____^ help: replace it with: `func_returning_result()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:390:13 + --> tests/ui/question_mark.rs:395:13 | LL | / if a.is_none() { LL | | @@ -208,7 +208,7 @@ LL | | } | |_____________^ help: replace it with: `a?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:451:5 + --> tests/ui/question_mark.rs:456:5 | LL | / let Some(v) = bar.foo.owned.clone() else { LL | | return None; @@ -216,7 +216,7 @@ LL | | }; | |______^ help: replace it with: `let v = bar.foo.owned.clone()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:466:5 + --> tests/ui/question_mark.rs:471:5 | LL | / let Some(ref x) = foo.opt_x else { LL | | return None; @@ -224,7 +224,7 @@ LL | | }; | |______^ help: replace it with: `let x = foo.opt_x.as_ref()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:476:5 + --> tests/ui/question_mark.rs:481:5 | LL | / let Some(ref mut x) = foo.opt_x else { LL | | return None; @@ -232,7 +232,7 @@ LL | | }; | |______^ help: replace it with: `let x = foo.opt_x.as_mut()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:487:5 + --> tests/ui/question_mark.rs:492:5 | LL | / let Some(ref x @ ref y) = foo.opt_x else { LL | | return None; @@ -240,7 +240,7 @@ LL | | }; | |______^ help: replace it with: `let x @ y = foo.opt_x.as_ref()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:491:5 + --> tests/ui/question_mark.rs:496:5 | LL | / let Some(ref x @ WrapperStructWithString(_)) = bar else { LL | | return None; @@ -248,7 +248,7 @@ LL | | }; | |______^ help: replace it with: `let x @ &WrapperStructWithString(_) = bar.as_ref()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:495:5 + --> tests/ui/question_mark.rs:500:5 | LL | / let Some(ref mut x @ WrapperStructWithString(_)) = bar else { LL | | return None; @@ -256,7 +256,7 @@ LL | | }; | |______^ help: replace it with: `let x @ &mut WrapperStructWithString(_) = bar.as_mut()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:517:5 + --> tests/ui/question_mark.rs:522:5 | LL | / if arg.is_none() { LL | | @@ -265,7 +265,7 @@ LL | | } | |_____^ help: replace it with: `arg?;` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:521:15 + --> tests/ui/question_mark.rs:526:15 | LL | let val = match arg { | _______________^ @@ -276,7 +276,7 @@ LL | | }; | |_____^ help: try instead: `arg?` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:531:5 + --> tests/ui/question_mark.rs:536:5 | LL | / let Some(a) = *a else { LL | | return None; diff --git a/src/tools/clippy/tests/ui/question_mark_used.stderr b/src/tools/clippy/tests/ui/question_mark_used.stderr index 53cb59c0216..82f0d325040 100644 --- a/src/tools/clippy/tests/ui/question_mark_used.stderr +++ b/src/tools/clippy/tests/ui/question_mark_used.stderr @@ -1,4 +1,4 @@ -error: question mark operator was used +error: the `?` operator was used --> tests/ui/question_mark_used.rs:11:5 | LL | other_function()?; diff --git a/src/tools/clippy/tests/ui/return_and_then.stderr b/src/tools/clippy/tests/ui/return_and_then.stderr index cc611c3dba6..a7acbe7b340 100644 --- a/src/tools/clippy/tests/ui/return_and_then.stderr +++ b/src/tools/clippy/tests/ui/return_and_then.stderr @@ -1,4 +1,4 @@ -error: use the question mark operator instead of an `and_then` call +error: use the `?` operator instead of an `and_then` call --> tests/ui/return_and_then.rs:5:9 | LL | / opt.and_then(|n| { @@ -20,7 +20,7 @@ LL + ret += n; LL + if n > 1 { Some(ret) } else { None } | -error: use the question mark operator instead of an `and_then` call +error: use the `?` operator instead of an `and_then` call --> tests/ui/return_and_then.rs:14:9 | LL | opt.and_then(|n| test_opt_block(Some(n))) @@ -32,7 +32,7 @@ LL ~ let n = opt?; LL + test_opt_block(Some(n)) | -error: use the question mark operator instead of an `and_then` call +error: use the `?` operator instead of an `and_then` call --> tests/ui/return_and_then.rs:19:9 | LL | gen_option(1).and_then(|n| test_opt_block(Some(n))) @@ -44,7 +44,7 @@ LL ~ let n = gen_option(1)?; LL + test_opt_block(Some(n)) | -error: use the question mark operator instead of an `and_then` call +error: use the `?` operator instead of an `and_then` call --> tests/ui/return_and_then.rs:24:9 | LL | opt.and_then(|n| if n > 1 { Ok(n + 1) } else { Err(n) }) @@ -56,7 +56,7 @@ LL ~ let n = opt?; LL + if n > 1 { Ok(n + 1) } else { Err(n) } | -error: use the question mark operator instead of an `and_then` call +error: use the `?` operator instead of an `and_then` call --> tests/ui/return_and_then.rs:29:9 | LL | opt.and_then(|n| test_res_block(Ok(n))) @@ -68,7 +68,7 @@ LL ~ let n = opt?; LL + test_res_block(Ok(n)) | -error: use the question mark operator instead of an `and_then` call +error: use the `?` operator instead of an `and_then` call --> tests/ui/return_and_then.rs:35:9 | LL | Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None }) @@ -80,7 +80,7 @@ LL ~ let x = Some("")?; LL + if x.len() > 2 { Some(3) } else { None } | -error: use the question mark operator instead of an `and_then` call +error: use the `?` operator instead of an `and_then` call --> tests/ui/return_and_then.rs:41:9 | LL | / Some(match (vec![1, 2, 3], vec![1, 2, 4]) { diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.fixed b/src/tools/clippy/tests/ui/unnecessary_cast.fixed index ba167e79a30..91ff4b9ee77 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_cast.fixed @@ -266,7 +266,21 @@ mod fixable { // Issue #11968: The suggestion for this lint removes the parentheses and leave the code as // `*x.pow(2)` which tries to dereference the return value rather than `x`. fn issue_11968(x: &usize) -> usize { - { *x }.pow(2) + (*x).pow(2) + //~^ unnecessary_cast + } + + #[allow(clippy::cast_lossless)] + fn issue_14640() { + let x = 5usize; + let vec: Vec<u64> = vec![1, 2, 3, 4, 5]; + assert_eq!(vec.len(), x); + //~^ unnecessary_cast + + let _ = (5i32 as i64).abs(); + //~^ unnecessary_cast + + let _ = 5i32 as i64; //~^ unnecessary_cast } } diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.rs b/src/tools/clippy/tests/ui/unnecessary_cast.rs index 0f90a8b0596..5444a914db1 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast.rs +++ b/src/tools/clippy/tests/ui/unnecessary_cast.rs @@ -269,4 +269,18 @@ mod fixable { (*x as usize).pow(2) //~^ unnecessary_cast } + + #[allow(clippy::cast_lossless)] + fn issue_14640() { + let x = 5usize; + let vec: Vec<u64> = vec![1, 2, 3, 4, 5]; + assert_eq!(vec.len(), x as usize); + //~^ unnecessary_cast + + let _ = (5i32 as i64 as i64).abs(); + //~^ unnecessary_cast + + let _ = 5i32 as i64 as i64; + //~^ unnecessary_cast + } } diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.stderr b/src/tools/clippy/tests/ui/unnecessary_cast.stderr index c83770c1a29..3e3c5eb81c1 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_cast.stderr @@ -245,7 +245,25 @@ error: casting to the same type is unnecessary (`usize` -> `usize`) --> tests/ui/unnecessary_cast.rs:269:9 | LL | (*x as usize).pow(2) - | ^^^^^^^^^^^^^ help: try: `{ *x }` + | ^^^^^^^^^^^^^ help: try: `(*x)` -error: aborting due to 41 previous errors +error: casting to the same type is unnecessary (`usize` -> `usize`) + --> tests/ui/unnecessary_cast.rs:277:31 + | +LL | assert_eq!(vec.len(), x as usize); + | ^^^^^^^^^^ help: try: `x` + +error: casting to the same type is unnecessary (`i64` -> `i64`) + --> tests/ui/unnecessary_cast.rs:280:17 + | +LL | let _ = (5i32 as i64 as i64).abs(); + | ^^^^^^^^^^^^^^^^^^^^ help: try: `(5i32 as i64)` + +error: casting to the same type is unnecessary (`i64` -> `i64`) + --> tests/ui/unnecessary_cast.rs:283:17 + | +LL | let _ = 5i32 as i64 as i64; + | ^^^^^^^^^^^^^^^^^^ help: try: `5i32 as i64` + +error: aborting due to 44 previous errors diff --git a/src/tools/clippy/tests/ui/unused_unit.edition2021.fixed b/src/tools/clippy/tests/ui/unused_unit.edition2021.fixed new file mode 100644 index 00000000000..93dd58b8e9d --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_unit.edition2021.fixed @@ -0,0 +1,146 @@ +//@revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 + +// The output for humans should just highlight the whole span without showing +// the suggested replacement, but we also want to test that suggested +// replacement only removes one set of parentheses, rather than naïvely +// stripping away any starting or ending parenthesis characters—hence this +// test of the JSON error format. + +#![feature(custom_inner_attributes)] +#![feature(closure_lifetime_binder)] +#![rustfmt::skip] + +#![deny(clippy::unused_unit)] +#![allow(dead_code)] +#![allow(clippy::from_over_into)] + +struct Unitter; +impl Unitter { + #[allow(clippy::no_effect)] + pub fn get_unit<F: Fn(), G>(&self, f: F, _g: G) + //~^ unused_unit + //~| unused_unit + where G: Fn() { + //~^ unused_unit + let _y: &dyn Fn() = &f; + //~^ unused_unit + (); // this should not lint, as it's not in return type position + } +} + +impl Into<()> for Unitter { + #[rustfmt::skip] + fn into(self) { + //~^ unused_unit + + //~^ unused_unit + } +} + +trait Trait { + fn redundant<F: FnOnce(), G, H>(&self, _f: F, _g: G, _h: H) + //~^ unused_unit + where + G: FnMut(), + //~^ unused_unit + H: Fn(); + //~^ unused_unit +} + +impl Trait for Unitter { + fn redundant<F: FnOnce(), G, H>(&self, _f: F, _g: G, _h: H) + //~^ unused_unit + where + G: FnMut(), + //~^ unused_unit + H: Fn() {} + //~^ unused_unit +} + +fn return_unit() { } +//~^ unused_unit +//~| unused_unit + +#[allow(clippy::needless_return)] +#[allow(clippy::never_loop)] +#[allow(clippy::unit_cmp)] +fn main() { + let u = Unitter; + assert_eq!(u.get_unit(|| {}, return_unit), u.into()); + return_unit(); + loop { + break; + //~^ unused_unit + } + return; + //~^ unused_unit +} + +// https://github.com/rust-lang/rust-clippy/issues/4076 +fn foo() { + macro_rules! foo { + (recv($r:expr) -> $res:pat => $body:expr) => { + $body + } + } + + foo! { + recv(rx) -> _x => () + } +} + +#[rustfmt::skip] +fn test(){} +//~^ unused_unit + +#[rustfmt::skip] +fn test2(){} +//~^ unused_unit + +#[rustfmt::skip] +fn test3(){} +//~^ unused_unit + +fn macro_expr() { + macro_rules! e { + () => (()); + } + e!() +} + +mod issue9748 { + fn main() { + let _ = for<'a> |_: &'a u32| -> () {}; + } +} + +mod issue9949 { + fn main() { + #[doc = "documentation"] + () + } +} + +mod issue14577 { + trait Unit {} + impl Unit for () {} + + fn run<R: Unit>(f: impl FnOnce() -> R) { + f(); + } + + #[allow(dependency_on_unit_never_type_fallback)] + fn bar() { + run(|| { todo!() }); + //~[edition2021]^ unused_unit + } + + struct UnitStruct; + impl UnitStruct { + fn apply<F: for<'c> Fn(&'c mut Self)>(&mut self, f: F) { + todo!() + } + } +} \ No newline at end of file diff --git a/src/tools/clippy/tests/ui/unused_unit.edition2021.stderr b/src/tools/clippy/tests/ui/unused_unit.edition2021.stderr new file mode 100644 index 00000000000..13cc20d4d7a --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_unit.edition2021.stderr @@ -0,0 +1,128 @@ +error: unneeded unit expression + --> tests/ui/unused_unit.rs:37:9 + | +LL | () + | ^^ help: remove the final `()` + | +note: the lint level is defined here + --> tests/ui/unused_unit.rs:15:9 + | +LL | #![deny(clippy::unused_unit)] + | ^^^^^^^^^^^^^^^^^^^ + +error: unneeded unit expression + --> tests/ui/unused_unit.rs:62:26 + | +LL | fn return_unit() -> () { () } + | ^^ help: remove the final `()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:22:28 + | +LL | pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) -> () + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:25:18 + | +LL | where G: Fn() -> () { + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:22:58 + | +LL | pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) -> () + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:27:26 + | +LL | let _y: &dyn Fn() -> () = &f; + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:35:18 + | +LL | fn into(self) -> () { + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:43:29 + | +LL | fn redundant<F: FnOnce() -> (), G, H>(&self, _f: F, _g: G, _h: H) + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:46:19 + | +LL | G: FnMut() -> (), + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:48:16 + | +LL | H: Fn() -> (); + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:53:29 + | +LL | fn redundant<F: FnOnce() -> (), G, H>(&self, _f: F, _g: G, _h: H) + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:56:19 + | +LL | G: FnMut() -> (), + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:58:16 + | +LL | H: Fn() -> () {} + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:62:17 + | +LL | fn return_unit() -> () { () } + | ^^^^^^ help: remove the `-> ()` + +error: unneeded `()` + --> tests/ui/unused_unit.rs:74:14 + | +LL | break(); + | ^^ help: remove the `()` + +error: unneeded `()` + --> tests/ui/unused_unit.rs:77:11 + | +LL | return(); + | ^^ help: remove the `()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:95:10 + | +LL | fn test()->(){} + | ^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:99:11 + | +LL | fn test2() ->(){} + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:103:11 + | +LL | fn test3()-> (){} + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:136:15 + | +LL | run(|| -> () { todo!() }); + | ^^^^^^ help: remove the `-> ()` + +error: aborting due to 20 previous errors + diff --git a/src/tools/clippy/tests/ui/unused_unit.edition2024.fixed b/src/tools/clippy/tests/ui/unused_unit.edition2024.fixed new file mode 100644 index 00000000000..987d901b97d --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_unit.edition2024.fixed @@ -0,0 +1,146 @@ +//@revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 + +// The output for humans should just highlight the whole span without showing +// the suggested replacement, but we also want to test that suggested +// replacement only removes one set of parentheses, rather than naïvely +// stripping away any starting or ending parenthesis characters—hence this +// test of the JSON error format. + +#![feature(custom_inner_attributes)] +#![feature(closure_lifetime_binder)] +#![rustfmt::skip] + +#![deny(clippy::unused_unit)] +#![allow(dead_code)] +#![allow(clippy::from_over_into)] + +struct Unitter; +impl Unitter { + #[allow(clippy::no_effect)] + pub fn get_unit<F: Fn(), G>(&self, f: F, _g: G) + //~^ unused_unit + //~| unused_unit + where G: Fn() { + //~^ unused_unit + let _y: &dyn Fn() = &f; + //~^ unused_unit + (); // this should not lint, as it's not in return type position + } +} + +impl Into<()> for Unitter { + #[rustfmt::skip] + fn into(self) { + //~^ unused_unit + + //~^ unused_unit + } +} + +trait Trait { + fn redundant<F: FnOnce(), G, H>(&self, _f: F, _g: G, _h: H) + //~^ unused_unit + where + G: FnMut(), + //~^ unused_unit + H: Fn(); + //~^ unused_unit +} + +impl Trait for Unitter { + fn redundant<F: FnOnce(), G, H>(&self, _f: F, _g: G, _h: H) + //~^ unused_unit + where + G: FnMut(), + //~^ unused_unit + H: Fn() {} + //~^ unused_unit +} + +fn return_unit() { } +//~^ unused_unit +//~| unused_unit + +#[allow(clippy::needless_return)] +#[allow(clippy::never_loop)] +#[allow(clippy::unit_cmp)] +fn main() { + let u = Unitter; + assert_eq!(u.get_unit(|| {}, return_unit), u.into()); + return_unit(); + loop { + break; + //~^ unused_unit + } + return; + //~^ unused_unit +} + +// https://github.com/rust-lang/rust-clippy/issues/4076 +fn foo() { + macro_rules! foo { + (recv($r:expr) -> $res:pat => $body:expr) => { + $body + } + } + + foo! { + recv(rx) -> _x => () + } +} + +#[rustfmt::skip] +fn test(){} +//~^ unused_unit + +#[rustfmt::skip] +fn test2(){} +//~^ unused_unit + +#[rustfmt::skip] +fn test3(){} +//~^ unused_unit + +fn macro_expr() { + macro_rules! e { + () => (()); + } + e!() +} + +mod issue9748 { + fn main() { + let _ = for<'a> |_: &'a u32| -> () {}; + } +} + +mod issue9949 { + fn main() { + #[doc = "documentation"] + () + } +} + +mod issue14577 { + trait Unit {} + impl Unit for () {} + + fn run<R: Unit>(f: impl FnOnce() -> R) { + f(); + } + + #[allow(dependency_on_unit_never_type_fallback)] + fn bar() { + run(|| -> () { todo!() }); + //~[edition2021]^ unused_unit + } + + struct UnitStruct; + impl UnitStruct { + fn apply<F: for<'c> Fn(&'c mut Self)>(&mut self, f: F) { + todo!() + } + } +} \ No newline at end of file diff --git a/src/tools/clippy/tests/ui/unused_unit.edition2024.stderr b/src/tools/clippy/tests/ui/unused_unit.edition2024.stderr new file mode 100644 index 00000000000..a79e70e066b --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_unit.edition2024.stderr @@ -0,0 +1,122 @@ +error: unneeded unit expression + --> tests/ui/unused_unit.rs:37:9 + | +LL | () + | ^^ help: remove the final `()` + | +note: the lint level is defined here + --> tests/ui/unused_unit.rs:15:9 + | +LL | #![deny(clippy::unused_unit)] + | ^^^^^^^^^^^^^^^^^^^ + +error: unneeded unit expression + --> tests/ui/unused_unit.rs:62:26 + | +LL | fn return_unit() -> () { () } + | ^^ help: remove the final `()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:22:28 + | +LL | pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) -> () + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:25:18 + | +LL | where G: Fn() -> () { + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:22:58 + | +LL | pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) -> () + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:27:26 + | +LL | let _y: &dyn Fn() -> () = &f; + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:35:18 + | +LL | fn into(self) -> () { + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:43:29 + | +LL | fn redundant<F: FnOnce() -> (), G, H>(&self, _f: F, _g: G, _h: H) + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:46:19 + | +LL | G: FnMut() -> (), + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:48:16 + | +LL | H: Fn() -> (); + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:53:29 + | +LL | fn redundant<F: FnOnce() -> (), G, H>(&self, _f: F, _g: G, _h: H) + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:56:19 + | +LL | G: FnMut() -> (), + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:58:16 + | +LL | H: Fn() -> () {} + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:62:17 + | +LL | fn return_unit() -> () { () } + | ^^^^^^ help: remove the `-> ()` + +error: unneeded `()` + --> tests/ui/unused_unit.rs:74:14 + | +LL | break(); + | ^^ help: remove the `()` + +error: unneeded `()` + --> tests/ui/unused_unit.rs:77:11 + | +LL | return(); + | ^^ help: remove the `()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:95:10 + | +LL | fn test()->(){} + | ^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:99:11 + | +LL | fn test2() ->(){} + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:103:11 + | +LL | fn test3()-> (){} + | ^^^^^ help: remove the `-> ()` + +error: aborting due to 19 previous errors + diff --git a/src/tools/clippy/tests/ui/unused_unit.fixed b/src/tools/clippy/tests/ui/unused_unit.fixed index e3c02681c9f..6668bf90c09 100644 --- a/src/tools/clippy/tests/ui/unused_unit.fixed +++ b/src/tools/clippy/tests/ui/unused_unit.fixed @@ -120,3 +120,24 @@ mod issue9949 { () } } + +#[clippy::msrv = "1.85"] +mod issue14577 { + trait Unit {} + impl Unit for () {} + + fn run<R: Unit>(f: impl FnOnce() -> R) { + f(); + } + + fn bar() { + run(|| -> () { todo!() }); + } + + struct UnitStruct; + impl UnitStruct { + fn apply<F: for<'c> Fn(&'c mut Self)>(&mut self, f: F) { + todo!() + } + } +} \ No newline at end of file diff --git a/src/tools/clippy/tests/ui/unused_unit.rs b/src/tools/clippy/tests/ui/unused_unit.rs index 4353026c594..b7645f7b6a2 100644 --- a/src/tools/clippy/tests/ui/unused_unit.rs +++ b/src/tools/clippy/tests/ui/unused_unit.rs @@ -1,4 +1,6 @@ - +//@revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 // The output for humans should just highlight the whole span without showing // the suggested replacement, but we also want to test that suggested @@ -120,3 +122,25 @@ mod issue9949 { () } } + +mod issue14577 { + trait Unit {} + impl Unit for () {} + + fn run<R: Unit>(f: impl FnOnce() -> R) { + f(); + } + + #[allow(dependency_on_unit_never_type_fallback)] + fn bar() { + run(|| -> () { todo!() }); + //~[edition2021]^ unused_unit + } + + struct UnitStruct; + impl UnitStruct { + fn apply<F: for<'c> Fn(&'c mut Self)>(&mut self, f: F) { + todo!() + } + } +} \ No newline at end of file diff --git a/src/tools/clippy/tests/ui/unused_unit.stderr b/src/tools/clippy/tests/ui/unused_unit.stderr index 172fe065502..366f2142095 100644 --- a/src/tools/clippy/tests/ui/unused_unit.stderr +++ b/src/tools/clippy/tests/ui/unused_unit.stderr @@ -1,8 +1,8 @@ -error: unneeded unit return type - --> tests/ui/unused_unit.rs:20:58 +error: unneeded unit expression + --> tests/ui/unused_unit.rs:35:9 | -LL | pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) -> () - | ^^^^^^ help: remove the `-> ()` +LL | () + | ^^ help: remove the final `()` | note: the lint level is defined here --> tests/ui/unused_unit.rs:13:9 @@ -10,6 +10,12 @@ note: the lint level is defined here LL | #![deny(clippy::unused_unit)] | ^^^^^^^^^^^^^^^^^^^ +error: unneeded unit expression + --> tests/ui/unused_unit.rs:60:26 + | +LL | fn return_unit() -> () { () } + | ^^ help: remove the final `()` + error: unneeded unit return type --> tests/ui/unused_unit.rs:20:28 | @@ -23,6 +29,12 @@ LL | where G: Fn() -> () { | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type + --> tests/ui/unused_unit.rs:20:58 + | +LL | pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) -> () + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type --> tests/ui/unused_unit.rs:25:26 | LL | let _y: &dyn Fn() -> () = &f; @@ -34,12 +46,6 @@ error: unneeded unit return type LL | fn into(self) -> () { | ^^^^^^ help: remove the `-> ()` -error: unneeded unit expression - --> tests/ui/unused_unit.rs:35:9 - | -LL | () - | ^^ help: remove the final `()` - error: unneeded unit return type --> tests/ui/unused_unit.rs:41:29 | @@ -82,12 +88,6 @@ error: unneeded unit return type LL | fn return_unit() -> () { () } | ^^^^^^ help: remove the `-> ()` -error: unneeded unit expression - --> tests/ui/unused_unit.rs:60:26 - | -LL | fn return_unit() -> () { () } - | ^^ help: remove the final `()` - error: unneeded `()` --> tests/ui/unused_unit.rs:72:14 | diff --git a/src/tools/clippy/tests/ui/zombie_processes.rs b/src/tools/clippy/tests/ui/zombie_processes.rs index 25bbc02ffb7..395f9dd2def 100644 --- a/src/tools/clippy/tests/ui/zombie_processes.rs +++ b/src/tools/clippy/tests/ui/zombie_processes.rs @@ -176,3 +176,25 @@ fn return_wait() -> ExitStatus { let mut x = Command::new("").spawn().unwrap(); return x.wait().unwrap(); } + +mod issue14677 { + use std::io; + use std::process::Command; + + fn do_something<F: Fn() -> Result<(), ()>>(f: F) { + todo!() + } + + fn foo() { + let mut child = Command::new("true").spawn().unwrap(); + let some_condition = true; + do_something(|| { + if some_condition { + return Err(()); + } + Ok(()) + }); + child.kill().unwrap(); + child.wait().unwrap(); + } +} diff --git a/src/tools/clippy/util/etc/pre-commit.sh b/src/tools/clippy/util/etc/pre-commit.sh index 5dd2ba3d5f5..528f8953b25 100755 --- a/src/tools/clippy/util/etc/pre-commit.sh +++ b/src/tools/clippy/util/etc/pre-commit.sh @@ -6,7 +6,6 @@ set -e # Update lints cargo dev update_lints git add clippy_lints/src/lib.rs -git add clippy_lints/src/lib.*.rs # Formatting: # Git will not automatically add the formatted code to the staged changes once diff --git a/src/tools/features-status-dump/Cargo.toml b/src/tools/features-status-dump/Cargo.toml index 35be71a46e5..b2976f14a01 100644 --- a/src/tools/features-status-dump/Cargo.toml +++ b/src/tools/features-status-dump/Cargo.toml @@ -5,7 +5,7 @@ license = "MIT OR Apache-2.0" edition = "2021" [dependencies] -anyhow = { version = "1", features = ["backtrace"] } +anyhow = { version = "1" } clap = { version = "4", features = ["derive"] } serde = { version = "1.0.125", features = [ "derive" ] } serde_json = "1.0.59" diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs index 18a5a0612bb..0704ed199db 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs @@ -594,7 +594,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx, Option<Provenance>> { let this = self.eval_context_mut(); // Ensure we bail out if the pointer goes out-of-bounds (see miri#1050). - this.check_ptr_access(place.ptr(), size, CheckInAllocMsg::InboundsTest)?; + this.check_ptr_access(place.ptr(), size, CheckInAllocMsg::Dereferenceable)?; // It is crucial that this gets called on all code paths, to ensure we track tag creation. let log_creation = |this: &MiriInterpCx<'tcx>, diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index f39a606513d..65042f26680 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -197,7 +197,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Make sure the new permission makes sense as the initial permission of a fresh tag. assert!(new_perm.initial_state.is_initial()); // Ensure we bail out if the pointer goes out-of-bounds (see miri#1050). - this.check_ptr_access(place.ptr(), ptr_size, CheckInAllocMsg::InboundsTest)?; + this.check_ptr_access(place.ptr(), ptr_size, CheckInAllocMsg::Dereferenceable)?; // It is crucial that this gets called on all code paths, to ensure we track tag creation. let log_creation = |this: &MiriInterpCx<'tcx>, diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs index de5da6ec898..c9250ba1b81 100644 --- a/src/tools/miri/src/intrinsics/simd.rs +++ b/src/tools/miri/src/intrinsics/simd.rs @@ -506,7 +506,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; let dest_len = u32::try_from(dest_len).unwrap(); - let bitmask_len = u32::try_from(bitmask_len).unwrap(); for i in 0..dest_len { let bit_i = simd_bitmask_index(i, dest_len, this.data_layout().endian); let mask = mask & 1u64.strict_shl(bit_i); @@ -517,17 +516,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let val = if mask != 0 { yes } else { no }; this.write_immediate(*val, &dest)?; } - for i in dest_len..bitmask_len { - // If the mask is "padded", ensure that padding is all-zero. - // This deliberately does not use `simd_bitmask_index`; these bits are outside - // the bitmask. It does not matter in which order we check them. - let mask = mask & 1u64.strict_shl(i); - if mask != 0 { - throw_ub_format!( - "a SIMD bitmask less than 8 bits long must be filled with 0s for the remaining bits" - ); - } - } + // The remaining bits of the mask are ignored. } // Converts a "vector of bool" into a bitmask. "bitmask" => { diff --git a/src/tools/miri/src/shims/unix/android/thread.rs b/src/tools/miri/src/shims/unix/android/thread.rs index c7e2c4d507b..30ec0aefcbf 100644 --- a/src/tools/miri/src/shims/unix/android/thread.rs +++ b/src/tools/miri/src/shims/unix/android/thread.rs @@ -42,7 +42,7 @@ pub fn prctl<'tcx>( ecx.check_ptr_access( name.to_pointer(ecx)?, Size::from_bytes(TASK_COMM_LEN), - CheckInAllocMsg::MemoryAccessTest, + CheckInAllocMsg::MemoryAccess, )?; let res = ecx.pthread_getname_np(thread, name, len, /* truncate*/ false)?; assert_eq!(res, ThreadNameResult::Ok); diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index 41be9df7e2d..156814a26fa 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -226,7 +226,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { trace!("Reading from FD {}, size {}", fd_num, count); // Check that the *entire* buffer is actually valid memory. - this.check_ptr_access(buf, Size::from_bytes(count), CheckInAllocMsg::MemoryAccessTest)?; + this.check_ptr_access(buf, Size::from_bytes(count), CheckInAllocMsg::MemoryAccess)?; // We cap the number of read bytes to the largest value that we are able to fit in both the // host's and target's `isize`. This saves us from having to handle overflows later. @@ -292,7 +292,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Isolation check is done via `FileDescription` trait. // Check that the *entire* buffer is actually valid memory. - this.check_ptr_access(buf, Size::from_bytes(count), CheckInAllocMsg::MemoryAccessTest)?; + this.check_ptr_access(buf, Size::from_bytes(count), CheckInAllocMsg::MemoryAccess)?; // We cap the number of written bytes to the largest value that we are able to fit in both the // host's and target's `isize`. This saves us from having to handle overflows later. diff --git a/src/tools/miri/tests/fail-dep/libc/affinity.stderr b/src/tools/miri/tests/fail-dep/libc/affinity.stderr index 5a226c6a44b..cc3daa47e00 100644 --- a/src/tools/miri/tests/fail-dep/libc/affinity.stderr +++ b/src/tools/miri/tests/fail-dep/libc/affinity.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: memory access failed: expected a pointer to 129 bytes of memory, but got ALLOC which is only 128 bytes from the end of the allocation +error: Undefined Behavior: memory access failed: attempting to access 129 bytes, but got ALLOC which is only 128 bytes from the end of the allocation --> tests/fail-dep/libc/affinity.rs:LL:CC | LL | let err = unsafe { sched_setaffinity(PID, size_of::<cpu_set_t>() + 1, &cpuset) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 129 bytes of memory, but got ALLOC which is only 128 bytes from the end of the allocation + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 129 bytes, but got ALLOC which is only 128 bytes from the end of the allocation | = 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-dep/libc/memchr_null.stderr b/src/tools/miri/tests/fail-dep/libc/memchr_null.stderr index 6d3ff176c35..5690277a046 100644 --- a/src/tools/miri/tests/fail-dep/libc/memchr_null.stderr +++ b/src/tools/miri/tests/fail-dep/libc/memchr_null.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: out-of-bounds pointer use: expected a pointer to some allocation, but got a null pointer +error: Undefined Behavior: pointer not dereferenceable: pointer must point to some allocation, but got null pointer --> tests/fail-dep/libc/memchr_null.rs:LL:CC | LL | libc::memchr(ptr::null(), 0, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: expected a pointer to some allocation, but got a null pointer + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pointer not dereferenceable: pointer must point to some allocation, but got null pointer | = 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-dep/libc/memcmp_null.stderr b/src/tools/miri/tests/fail-dep/libc/memcmp_null.stderr index a4ca205c377..bd3a0719fa3 100644 --- a/src/tools/miri/tests/fail-dep/libc/memcmp_null.stderr +++ b/src/tools/miri/tests/fail-dep/libc/memcmp_null.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: out-of-bounds pointer use: expected a pointer to some allocation, but got a null pointer +error: Undefined Behavior: pointer not dereferenceable: pointer must point to some allocation, but got null pointer --> tests/fail-dep/libc/memcmp_null.rs:LL:CC | LL | libc::memcmp(ptr::null(), ptr::null(), 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: expected a pointer to some allocation, but got a null pointer + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pointer not dereferenceable: pointer must point to some allocation, but got null pointer | = 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-dep/libc/memcmp_zero.stderr b/src/tools/miri/tests/fail-dep/libc/memcmp_zero.stderr index d7b046c1823..2044c154761 100644 --- a/src/tools/miri/tests/fail-dep/libc/memcmp_zero.stderr +++ b/src/tools/miri/tests/fail-dep/libc/memcmp_zero.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: out-of-bounds pointer use: expected a pointer to some allocation, but got 0x2a[noalloc] which is a dangling pointer (it has no provenance) +error: Undefined Behavior: pointer not dereferenceable: pointer must point to some allocation, but got 0x2a[noalloc] which is a dangling pointer (it has no provenance) --> tests/fail-dep/libc/memcmp_zero.rs:LL:CC | LL | libc::memcmp(ptr.cast(), ptr.cast(), 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: expected a pointer to some allocation, but got 0x2a[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pointer not dereferenceable: pointer must point to some allocation, but got 0x2a[noalloc] which is a dangling pointer (it has no provenance) | = 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-dep/libc/memcpy_zero.stderr b/src/tools/miri/tests/fail-dep/libc/memcpy_zero.stderr index 336113e3440..789e9daf43b 100644 --- a/src/tools/miri/tests/fail-dep/libc/memcpy_zero.stderr +++ b/src/tools/miri/tests/fail-dep/libc/memcpy_zero.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: out-of-bounds pointer use: expected a pointer to some allocation, but got 0x17[noalloc] which is a dangling pointer (it has no provenance) +error: Undefined Behavior: pointer not dereferenceable: pointer must point to some allocation, but got 0x17[noalloc] which is a dangling pointer (it has no provenance) --> tests/fail-dep/libc/memcpy_zero.rs:LL:CC | LL | libc::memcpy(to.cast(), from.cast(), 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: expected a pointer to some allocation, but got 0x17[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pointer not dereferenceable: pointer must point to some allocation, but got 0x17[noalloc] which is a dangling pointer (it has no provenance) | = 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-dep/libc/memrchr_null.stderr b/src/tools/miri/tests/fail-dep/libc/memrchr_null.stderr index ce759f3e17a..27e7a9855d1 100644 --- a/src/tools/miri/tests/fail-dep/libc/memrchr_null.stderr +++ b/src/tools/miri/tests/fail-dep/libc/memrchr_null.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: out-of-bounds pointer use: expected a pointer to some allocation, but got a null pointer +error: Undefined Behavior: pointer not dereferenceable: pointer must point to some allocation, but got null pointer --> tests/fail-dep/libc/memrchr_null.rs:LL:CC | LL | libc::memrchr(ptr::null(), 0, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: expected a pointer to some allocation, but got a null pointer + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pointer not dereferenceable: pointer must point to some allocation, but got null pointer | = 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/both_borrows/issue-miri-1050-1.rs b/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.rs index 49de3dd0b10..dd7dae9cecf 100644 --- a/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.rs +++ b/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.rs @@ -1,6 +1,6 @@ //@revisions: stack tree //@[tree]compile-flags: -Zmiri-tree-borrows -//@error-in-other-file: expected a pointer to 4 bytes of memory +//@error-in-other-file: pointer not dereferenceable fn main() { unsafe { diff --git a/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.stack.stderr b/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.stack.stderr index cd27bb818e7..0e6d838dfff 100644 --- a/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.stack.stderr +++ b/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.stack.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got ALLOC which is only 2 bytes from the end of the allocation +error: Undefined Behavior: pointer not dereferenceable: pointer must be dereferenceable for 4 bytes, but got ALLOC which is only 2 bytes from the end of the allocation --> RUSTLIB/alloc/src/boxed.rs:LL:CC | LL | Box(unsafe { Unique::new_unchecked(raw) }, alloc) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got ALLOC which is only 2 bytes from the end of the allocation + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pointer not dereferenceable: pointer must be dereferenceable for 4 bytes, but got ALLOC which is only 2 bytes from the end of the allocation | = 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/both_borrows/issue-miri-1050-1.tree.stderr b/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.tree.stderr index cd27bb818e7..0e6d838dfff 100644 --- a/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.tree.stderr +++ b/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.tree.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got ALLOC which is only 2 bytes from the end of the allocation +error: Undefined Behavior: pointer not dereferenceable: pointer must be dereferenceable for 4 bytes, but got ALLOC which is only 2 bytes from the end of the allocation --> RUSTLIB/alloc/src/boxed.rs:LL:CC | LL | Box(unsafe { Unique::new_unchecked(raw) }, alloc) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got ALLOC which is only 2 bytes from the end of the allocation + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pointer not dereferenceable: pointer must be dereferenceable for 4 bytes, but got ALLOC which is only 2 bytes from the end of the allocation | = 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/both_borrows/issue-miri-1050-2.stack.stderr b/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-2.stack.stderr index 04e5765371e..861173f5496 100644 --- a/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-2.stack.stderr +++ b/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-2.stack.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got 0x4[noalloc] which is a dangling pointer (it has no provenance) +error: Undefined Behavior: pointer not dereferenceable: pointer must be dereferenceable for 4 bytes, but got 0x4[noalloc] which is a dangling pointer (it has no provenance) --> RUSTLIB/alloc/src/boxed.rs:LL:CC | LL | Box(unsafe { Unique::new_unchecked(raw) }, alloc) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got 0x4[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pointer not dereferenceable: pointer must be dereferenceable for 4 bytes, but got 0x4[noalloc] which is a dangling pointer (it has no provenance) | = 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/both_borrows/issue-miri-1050-2.tree.stderr b/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-2.tree.stderr index 04e5765371e..861173f5496 100644 --- a/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-2.tree.stderr +++ b/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-2.tree.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got 0x4[noalloc] which is a dangling pointer (it has no provenance) +error: Undefined Behavior: pointer not dereferenceable: pointer must be dereferenceable for 4 bytes, but got 0x4[noalloc] which is a dangling pointer (it has no provenance) --> RUSTLIB/alloc/src/boxed.rs:LL:CC | LL | Box(unsafe { Unique::new_unchecked(raw) }, alloc) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got 0x4[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pointer not dereferenceable: pointer must be dereferenceable for 4 bytes, but got 0x4[noalloc] which is a dangling pointer (it has no provenance) | = 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/dangling_pointers/dangling_pointer_offset.rs b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_offset.rs index 65eca07a070..54b3280e71a 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_offset.rs +++ b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_offset.rs @@ -6,6 +6,6 @@ fn main() { let b = Box::new(42); &*b as *const i32 }; - let x = unsafe { p.offset(42) }; //~ ERROR: /out-of-bounds pointer arithmetic: .* has been freed/ + let x = unsafe { p.offset(42) }; //~ ERROR: /in-bounds pointer arithmetic failed: .* has been freed/ panic!("this should never print: {:?}", x); } diff --git a/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_offset.stderr b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_offset.stderr index 076d6880461..fd1a5e7faaf 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_offset.stderr +++ b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_offset.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: out-of-bounds pointer arithmetic: ALLOC has been freed, so this pointer is dangling +error: Undefined Behavior: in-bounds pointer arithmetic failed: ALLOC has been freed, so this pointer is dangling --> tests/fail/dangling_pointers/dangling_pointer_offset.rs:LL:CC | LL | let x = unsafe { p.offset(42) }; - | ^^^^^^^^^^^^ out-of-bounds pointer arithmetic: ALLOC has been freed, so this pointer is dangling + | ^^^^^^^^^^^^ in-bounds pointer arithmetic failed: ALLOC has been freed, so this pointer is dangling | = 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/dangling_pointers/dangling_pointer_project_underscore_let.rs b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_project_underscore_let.rs index 22a5ce8ea74..02ea09efab6 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_project_underscore_let.rs +++ b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_project_underscore_let.rs @@ -7,6 +7,6 @@ fn main() { &*b as *const i32 as *const (u8, u8, u8, u8) }; unsafe { - let _ = (*p).1; //~ ERROR: out-of-bounds pointer arithmetic + let _ = (*p).1; //~ ERROR: in-bounds pointer arithmetic failed } } diff --git a/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_project_underscore_let.stderr b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_project_underscore_let.stderr index ffb525e3981..ffb8bc9507b 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_project_underscore_let.stderr +++ b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_project_underscore_let.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: out-of-bounds pointer arithmetic: ALLOC has been freed, so this pointer is dangling +error: Undefined Behavior: in-bounds pointer arithmetic failed: ALLOC has been freed, so this pointer is dangling --> tests/fail/dangling_pointers/dangling_pointer_project_underscore_let.rs:LL:CC | LL | let _ = (*p).1; - | ^^^^^^ out-of-bounds pointer arithmetic: ALLOC has been freed, so this pointer is dangling + | ^^^^^^ in-bounds pointer arithmetic failed: ALLOC has been freed, so this pointer is dangling | = 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/dangling_pointers/dangling_pointer_project_underscore_let_type_annotation.rs b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_project_underscore_let_type_annotation.rs index fc10a826c1e..7ab295cb6c6 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_project_underscore_let_type_annotation.rs +++ b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_project_underscore_let_type_annotation.rs @@ -7,6 +7,6 @@ fn main() { &*b as *const i32 as *const (u8, u8, u8, u8) }; unsafe { - let _: u8 = (*p).1; //~ ERROR: out-of-bounds pointer arithmetic + let _: u8 = (*p).1; //~ ERROR: in-bounds pointer arithmetic failed } } diff --git a/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_project_underscore_let_type_annotation.stderr b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_project_underscore_let_type_annotation.stderr index 14dfa43b2d6..cf3e1db13d3 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_project_underscore_let_type_annotation.stderr +++ b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_project_underscore_let_type_annotation.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: out-of-bounds pointer arithmetic: ALLOC has been freed, so this pointer is dangling +error: Undefined Behavior: in-bounds pointer arithmetic failed: ALLOC has been freed, so this pointer is dangling --> tests/fail/dangling_pointers/dangling_pointer_project_underscore_let_type_annotation.rs:LL:CC | LL | let _: u8 = (*p).1; - | ^^^^^^ out-of-bounds pointer arithmetic: ALLOC has been freed, so this pointer is dangling + | ^^^^^^ in-bounds pointer arithmetic failed: ALLOC has been freed, so this pointer is dangling | = 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/dangling_pointers/dangling_pointer_project_underscore_match.rs b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_project_underscore_match.rs index 8541da84857..2855dd13fdc 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_project_underscore_match.rs +++ b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_project_underscore_match.rs @@ -8,7 +8,7 @@ fn main() { }; unsafe { match (*p).1 { - //~^ ERROR: out-of-bounds pointer arithmetic + //~^ ERROR: in-bounds pointer arithmetic failed _ => {} } } diff --git a/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_project_underscore_match.stderr b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_project_underscore_match.stderr index ff39e147573..e2d04433b63 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_project_underscore_match.stderr +++ b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_project_underscore_match.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: out-of-bounds pointer arithmetic: ALLOC has been freed, so this pointer is dangling +error: Undefined Behavior: in-bounds pointer arithmetic failed: ALLOC has been freed, so this pointer is dangling --> tests/fail/dangling_pointers/dangling_pointer_project_underscore_match.rs:LL:CC | LL | match (*p).1 { - | ^^^^^^ out-of-bounds pointer arithmetic: ALLOC has been freed, so this pointer is dangling + | ^^^^^^ in-bounds pointer arithmetic failed: ALLOC has been freed, so this pointer is dangling | = 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/dangling_pointers/dangling_pointer_to_raw_pointer.stderr b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_to_raw_pointer.stderr index 99194d6e072..5a2b85696ab 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_to_raw_pointer.stderr +++ b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_to_raw_pointer.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got 0x10[noalloc] which is a dangling pointer (it has no provenance) +error: Undefined Behavior: pointer not dereferenceable: pointer must be dereferenceable for 4 bytes, but got 0x10[noalloc] which is a dangling pointer (it has no provenance) --> tests/fail/dangling_pointers/dangling_pointer_to_raw_pointer.rs:LL:CC | LL | unsafe { &(*x).0 as *const i32 } - | ^^^^^^^ out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got 0x10[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^ pointer not dereferenceable: pointer must be dereferenceable for 4 bytes, but got 0x10[noalloc] which is a dangling pointer (it has no provenance) | = 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/dangling_pointers/deref-invalid-ptr.stderr b/src/tools/miri/tests/fail/dangling_pointers/deref-invalid-ptr.stderr index 09a201983b1..ad4280c2d74 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/deref-invalid-ptr.stderr +++ b/src/tools/miri/tests/fail/dangling_pointers/deref-invalid-ptr.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got 0x10[noalloc] which is a dangling pointer (it has no provenance) +error: Undefined Behavior: pointer not dereferenceable: pointer must be dereferenceable for 4 bytes, but got 0x10[noalloc] which is a dangling pointer (it has no provenance) --> tests/fail/dangling_pointers/deref-invalid-ptr.rs:LL:CC | LL | let _y = unsafe { &*x as *const u32 }; - | ^^^ out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got 0x10[noalloc] which is a dangling pointer (it has no provenance) + | ^^^ pointer not dereferenceable: pointer must be dereferenceable for 4 bytes, but got 0x10[noalloc] which is a dangling pointer (it has no provenance) | = 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/dangling_pointers/null_pointer_deref.stderr b/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref.stderr index d87a8bc59e9..3135db9dc6d 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref.stderr +++ b/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: memory access failed: expected a pointer to 4 bytes of memory, but got a null pointer +error: Undefined Behavior: memory access failed: attempting to access 4 bytes, but got null pointer --> tests/fail/dangling_pointers/null_pointer_deref.rs:LL:CC | LL | let x: i32 = unsafe { *std::ptr::null() }; - | ^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got a null pointer + | ^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 4 bytes, but got null pointer | = 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/dangling_pointers/null_pointer_write.stderr b/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write.stderr index 39d861a6388..012b38ee5a6 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write.stderr +++ b/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: memory access failed: expected a pointer to 4 bytes of memory, but got a null pointer +error: Undefined Behavior: memory access failed: attempting to access 4 bytes, but got null pointer --> tests/fail/dangling_pointers/null_pointer_write.rs:LL:CC | LL | unsafe { *std::ptr::null_mut() = 0i32 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got a null pointer + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 4 bytes, but got null pointer | = 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/dangling_pointers/out_of_bounds_project.rs b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_project.rs index b596ba428ae..7a2ad483d01 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_project.rs +++ b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_project.rs @@ -7,6 +7,6 @@ fn main() { let ptr = addr_of!(v).cast::<(u32, u32, u32)>(); unsafe { let _field = addr_of!((*ptr).1); // still just in-bounds - let _field = addr_of!((*ptr).2); //~ ERROR: out-of-bounds pointer arithmetic + let _field = addr_of!((*ptr).2); //~ ERROR: in-bounds pointer arithmetic failed } } diff --git a/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_project.stderr b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_project.stderr index 27a437c7483..c11ccdb45a7 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_project.stderr +++ b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_project.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: out-of-bounds pointer arithmetic: expected a pointer to 8 bytes of memory, but got ALLOC which is only 4 bytes from the end of the allocation +error: Undefined Behavior: in-bounds pointer arithmetic failed: attempting to offset pointer by 8 bytes, but got ALLOC which is only 4 bytes from the end of the allocation --> tests/fail/dangling_pointers/out_of_bounds_project.rs:LL:CC | LL | let _field = addr_of!((*ptr).2); - | ^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to 8 bytes of memory, but got ALLOC which is only 4 bytes from the end of the allocation + | ^^^^^^^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by 8 bytes, but got ALLOC which is only 4 bytes from the end of the allocation | = 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/dangling_pointers/out_of_bounds_read.rs b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read.rs index 595a229baa5..78a7b7e460b 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read.rs +++ b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read.rs @@ -1,6 +1,6 @@ fn main() { let v: Vec<u16> = vec![1, 2]; // This read is also misaligned. We make sure that the OOB message has priority. - let x = unsafe { *v.as_ptr().wrapping_byte_add(5) }; //~ ERROR: expected a pointer to 2 bytes of memory + let x = unsafe { *v.as_ptr().wrapping_byte_add(5) }; //~ ERROR: attempting to access 2 bytes panic!("this should never print: {}", x); } diff --git a/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read.stderr b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read.stderr index 813bcef54f1..1de4b806da2 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read.stderr +++ b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: memory access failed: expected a pointer to 2 bytes of memory, but got ALLOC+0x5 which is at or beyond the end of the allocation of size 4 bytes +error: Undefined Behavior: memory access failed: attempting to access 2 bytes, but got ALLOC+0x5 which is at or beyond the end of the allocation of size 4 bytes --> tests/fail/dangling_pointers/out_of_bounds_read.rs:LL:CC | LL | let x = unsafe { *v.as_ptr().wrapping_byte_add(5) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 2 bytes of memory, but got ALLOC+0x5 which is at or beyond the end of the allocation of size 4 bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 2 bytes, but got ALLOC+0x5 which is at or beyond the end of the allocation of size 4 bytes | = 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/dangling_pointers/out_of_bounds_write.rs b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_write.rs index 054e1c66cc1..f83601b44f8 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_write.rs +++ b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_write.rs @@ -1,5 +1,5 @@ fn main() { let mut v: Vec<u16> = vec![1, 2]; // This read is also misaligned. We make sure that the OOB message has priority. - unsafe { *v.as_mut_ptr().wrapping_byte_add(5) = 0 }; //~ ERROR: expected a pointer to 2 bytes of memory + unsafe { *v.as_mut_ptr().wrapping_byte_add(5) = 0 }; //~ ERROR: attempting to access 2 bytes } diff --git a/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_write.stderr b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_write.stderr index 1056a739a43..db16d70704e 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_write.stderr +++ b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_write.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: memory access failed: expected a pointer to 2 bytes of memory, but got ALLOC+0x5 which is at or beyond the end of the allocation of size 4 bytes +error: Undefined Behavior: memory access failed: attempting to access 2 bytes, but got ALLOC+0x5 which is at or beyond the end of the allocation of size 4 bytes --> tests/fail/dangling_pointers/out_of_bounds_write.rs:LL:CC | LL | unsafe { *v.as_mut_ptr().wrapping_byte_add(5) = 0 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 2 bytes of memory, but got ALLOC+0x5 which is at or beyond the end of the allocation of size 4 bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 2 bytes, but got ALLOC+0x5 which is at or beyond the end of the allocation of size 4 bytes | = 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/dangling_pointers/storage_dead_dangling.stderr b/src/tools/miri/tests/fail/dangling_pointers/storage_dead_dangling.stderr index 9061121494d..e97427ab7eb 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/storage_dead_dangling.stderr +++ b/src/tools/miri/tests/fail/dangling_pointers/storage_dead_dangling.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) +error: Undefined Behavior: pointer not dereferenceable: pointer must be dereferenceable for 4 bytes, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) --> tests/fail/dangling_pointers/storage_dead_dangling.rs:LL:CC | LL | let _ref = unsafe { &mut *(LEAK as *mut i32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^^ pointer not dereferenceable: pointer must be dereferenceable for 4 bytes, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) | = 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/dangling_pointers/wild_pointer_deref.stderr b/src/tools/miri/tests/fail/dangling_pointers/wild_pointer_deref.stderr index 3e7aac4724d..79e27fa3461 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/wild_pointer_deref.stderr +++ b/src/tools/miri/tests/fail/dangling_pointers/wild_pointer_deref.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: memory access failed: expected a pointer to 4 bytes of memory, but got 0x2c[noalloc] which is a dangling pointer (it has no provenance) +error: Undefined Behavior: memory access failed: attempting to access 4 bytes, but got 0x2c[noalloc] which is a dangling pointer (it has no provenance) --> tests/fail/dangling_pointers/wild_pointer_deref.rs:LL:CC | LL | let x = unsafe { *p }; - | ^^ memory access failed: expected a pointer to 4 bytes of memory, but got 0x2c[noalloc] which is a dangling pointer (it has no provenance) + | ^^ memory access failed: attempting to access 4 bytes, but got 0x2c[noalloc] which is a dangling pointer (it has no provenance) | = 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/function_pointers/cast_int_to_fn_ptr.stderr b/src/tools/miri/tests/fail/function_pointers/cast_int_to_fn_ptr.stderr index f2d9933188d..37d3beefcd7 100644 --- a/src/tools/miri/tests/fail/function_pointers/cast_int_to_fn_ptr.stderr +++ b/src/tools/miri/tests/fail/function_pointers/cast_int_to_fn_ptr.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: out-of-bounds pointer use: expected a pointer to some allocation, but got 0x2a[noalloc] which is a dangling pointer (it has no provenance) +error: Undefined Behavior: pointer not dereferenceable: pointer must point to some allocation, but got 0x2a[noalloc] which is a dangling pointer (it has no provenance) --> tests/fail/function_pointers/cast_int_to_fn_ptr.rs:LL:CC | LL | g(42) - | ^^^^^ out-of-bounds pointer use: expected a pointer to some allocation, but got 0x2a[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^ pointer not dereferenceable: pointer must point to some allocation, but got 0x2a[noalloc] which is a dangling pointer (it has no provenance) | = 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/intrinsics/ptr_offset_int_plus_int.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_offset_int_plus_int.stderr index c87ce321784..b7ed36f6428 100644 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_int_plus_int.stderr +++ b/src/tools/miri/tests/fail/intrinsics/ptr_offset_int_plus_int.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: out-of-bounds pointer arithmetic: expected a pointer to 1 byte of memory, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) +error: Undefined Behavior: in-bounds pointer arithmetic failed: attempting to offset pointer by 1 byte, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) --> tests/fail/intrinsics/ptr_offset_int_plus_int.rs:LL:CC | LL | let _val = (1 as *mut u8).offset(1); - | ^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to 1 byte of memory, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by 1 byte, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) | = 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/intrinsics/ptr_offset_int_plus_ptr.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_offset_int_plus_ptr.stderr index 78239d50137..29d9e1c64bd 100644 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_int_plus_ptr.stderr +++ b/src/tools/miri/tests/fail/intrinsics/ptr_offset_int_plus_ptr.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: out-of-bounds pointer arithmetic: expected a pointer to $BYTES bytes of memory, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) +error: Undefined Behavior: in-bounds pointer arithmetic failed: attempting to offset pointer by $BYTES bytes, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) --> tests/fail/intrinsics/ptr_offset_int_plus_ptr.rs:LL:CC | LL | let _val = (1 as *mut u8).offset(ptr as isize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to $BYTES bytes of memory, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by $BYTES bytes, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) | = 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/intrinsics/ptr_offset_out_of_bounds.rs b/src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds.rs index 905fc678f6d..e44d398c998 100644 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds.rs +++ b/src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds.rs @@ -1,6 +1,6 @@ fn main() { let v = [0i8; 4]; let x = &v as *const i8; - let x = unsafe { x.offset(5) }; //~ERROR: expected a pointer to 5 bytes of memory + let x = unsafe { x.offset(5) }; //~ERROR: is only 4 bytes from the end of the allocation panic!("this should never print: {:?}", x); } diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds.stderr index 4f6b45b897b..143fae8587b 100644 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds.stderr +++ b/src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: out-of-bounds pointer arithmetic: expected a pointer to 5 bytes of memory, but got ALLOC which is only 4 bytes from the end of the allocation +error: Undefined Behavior: in-bounds pointer arithmetic failed: attempting to offset pointer by 5 bytes, but got ALLOC which is only 4 bytes from the end of the allocation --> tests/fail/intrinsics/ptr_offset_out_of_bounds.rs:LL:CC | LL | let x = unsafe { x.offset(5) }; - | ^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to 5 bytes of memory, but got ALLOC which is only 4 bytes from the end of the allocation + | ^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by 5 bytes, but got ALLOC which is only 4 bytes from the end of the allocation | = 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/intrinsics/ptr_offset_out_of_bounds_neg.rs b/src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds_neg.rs index bd1d5c064c0..5ad17373566 100644 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds_neg.rs +++ b/src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds_neg.rs @@ -1,6 +1,6 @@ fn main() { let v = [0i8; 4]; let x = &v as *const i8; - let x = unsafe { x.offset(-1) }; //~ERROR: expected a pointer to the end of 1 byte of memory + let x = unsafe { x.offset(-1) }; //~ERROR: is at the beginning of the allocation panic!("this should never print: {:?}", x); } diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds_neg.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds_neg.stderr index 2dd4c943e86..14163d92404 100644 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds_neg.stderr +++ b/src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds_neg.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: out-of-bounds pointer arithmetic: expected a pointer to the end of 1 byte of memory, but got ALLOC which is at the beginning of the allocation +error: Undefined Behavior: in-bounds pointer arithmetic failed: attempting to offset pointer by -1 bytes, but got ALLOC which is at the beginning of the allocation --> tests/fail/intrinsics/ptr_offset_out_of_bounds_neg.rs:LL:CC | LL | let x = unsafe { x.offset(-1) }; - | ^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to the end of 1 byte of memory, but got ALLOC which is at the beginning of the allocation + | ^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by -1 bytes, but got ALLOC which is at the beginning of the allocation | = 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/intrinsics/ptr_offset_overflow.rs b/src/tools/miri/tests/fail/intrinsics/ptr_offset_overflow.rs index 68394312232..6767b1f117a 100644 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_overflow.rs +++ b/src/tools/miri/tests/fail/intrinsics/ptr_offset_overflow.rs @@ -3,6 +3,6 @@ fn main() { let v = [0i8; 4]; let x = &v as *const i8; - let x = unsafe { x.offset(isize::MIN) }; //~ERROR: out-of-bounds pointer arithmetic + let x = unsafe { x.offset(isize::MIN) }; //~ERROR: in-bounds pointer arithmetic failed panic!("this should never print: {:?}", x); } diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_overflow.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_offset_overflow.stderr index d03c9f870e2..af08bfb3c94 100644 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_overflow.stderr +++ b/src/tools/miri/tests/fail/intrinsics/ptr_offset_overflow.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: out-of-bounds pointer arithmetic: expected a pointer to the end of $BYTES bytes of memory, but got ALLOC which is at the beginning of the allocation +error: Undefined Behavior: in-bounds pointer arithmetic failed: attempting to offset pointer by -$BYTES bytes, but got ALLOC which is at the beginning of the allocation --> tests/fail/intrinsics/ptr_offset_overflow.rs:LL:CC | LL | let x = unsafe { x.offset(isize::MIN) }; - | ^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to the end of $BYTES bytes of memory, but got ALLOC which is at the beginning of the allocation + | ^^^^^^^^^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by -$BYTES bytes, but got ALLOC which is at the beginning of the allocation | = 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/intrinsics/simd-gather.rs b/src/tools/miri/tests/fail/intrinsics/simd-gather.rs index b8373952451..45a3dfa5772 100644 --- a/src/tools/miri/tests/fail/intrinsics/simd-gather.rs +++ b/src/tools/miri/tests/fail/intrinsics/simd-gather.rs @@ -6,6 +6,6 @@ fn main() { let vec: &[i8] = &[10, 11, 12, 13, 14, 15, 16, 17, 18]; let idxs = Simd::from_array([9, 3, 0, 17]); let _result = Simd::gather_select_unchecked(&vec, Mask::splat(true), idxs, Simd::splat(0)); - //~^ERROR: expected a pointer to 1 byte of memory + //~^ERROR: attempting to access 1 byte } } diff --git a/src/tools/miri/tests/fail/intrinsics/simd-gather.stderr b/src/tools/miri/tests/fail/intrinsics/simd-gather.stderr index ee1c9009610..e91d5d2185f 100644 --- a/src/tools/miri/tests/fail/intrinsics/simd-gather.stderr +++ b/src/tools/miri/tests/fail/intrinsics/simd-gather.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: memory access failed: expected a pointer to 1 byte of memory, but got ALLOC+0x9 which is at or beyond the end of the allocation of size 9 bytes +error: Undefined Behavior: memory access failed: attempting to access 1 byte, but got ALLOC+0x9 which is at or beyond the end of the allocation of size 9 bytes --> tests/fail/intrinsics/simd-gather.rs:LL:CC | LL | let _result = Simd::gather_select_unchecked(&vec, Mask::splat(true), idxs, Simd::splat(0)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 1 byte of memory, but got ALLOC+0x9 which is at or beyond the end of the allocation of size 9 bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 1 byte, but got ALLOC+0x9 which is at or beyond the end of the allocation of size 9 bytes | = 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/intrinsics/simd-scatter.rs b/src/tools/miri/tests/fail/intrinsics/simd-scatter.rs index bb8c9dbe4c7..4e727edd624 100644 --- a/src/tools/miri/tests/fail/intrinsics/simd-scatter.rs +++ b/src/tools/miri/tests/fail/intrinsics/simd-scatter.rs @@ -6,7 +6,7 @@ fn main() { let mut vec: Vec<i8> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; let idxs = Simd::from_array([9, 3, 0, 17]); Simd::from_array([-27, 82, -41, 124]).scatter_select_unchecked( - //~^ERROR: expected a pointer to 1 byte of memory + //~^ERROR: attempting to access 1 byte &mut vec, Mask::splat(true), idxs, diff --git a/src/tools/miri/tests/fail/intrinsics/simd-scatter.stderr b/src/tools/miri/tests/fail/intrinsics/simd-scatter.stderr index aaacb94f458..56c8e7b38b6 100644 --- a/src/tools/miri/tests/fail/intrinsics/simd-scatter.stderr +++ b/src/tools/miri/tests/fail/intrinsics/simd-scatter.stderr @@ -1,4 +1,4 @@ -error: Undefined Behavior: memory access failed: expected a pointer to 1 byte of memory, but got ALLOC+0x9 which is at or beyond the end of the allocation of size 9 bytes +error: Undefined Behavior: memory access failed: attempting to access 1 byte, but got ALLOC+0x9 which is at or beyond the end of the allocation of size 9 bytes --> tests/fail/intrinsics/simd-scatter.rs:LL:CC | LL | / Simd::from_array([-27, 82, -41, 124]).scatter_select_unchecked( @@ -7,7 +7,7 @@ LL | | &mut vec, LL | | Mask::splat(true), LL | | idxs, LL | | ); - | |_________^ memory access failed: expected a pointer to 1 byte of memory, but got ALLOC+0x9 which is at or beyond the end of the allocation of size 9 bytes + | |_________^ memory access failed: attempting to access 1 byte, but got ALLOC+0x9 which is at or beyond the end of the allocation of size 9 bytes | = 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/intrinsics/simd-select-bitmask-invalid.rs b/src/tools/miri/tests/fail/intrinsics/simd-select-bitmask-invalid.rs deleted file mode 100644 index 409098ac3b5..00000000000 --- a/src/tools/miri/tests/fail/intrinsics/simd-select-bitmask-invalid.rs +++ /dev/null @@ -1,15 +0,0 @@ -#![feature(core_intrinsics, repr_simd)] - -use std::intrinsics::simd::simd_select_bitmask; - -#[repr(simd)] -#[allow(non_camel_case_types)] -#[derive(Copy, Clone)] -struct i32x2([i32; 2]); - -fn main() { - unsafe { - let x = i32x2([0, 1]); - simd_select_bitmask(0b11111111u8, x, x); //~ERROR: bitmask less than 8 bits long must be filled with 0s for the remaining bits - } -} diff --git a/src/tools/miri/tests/fail/intrinsics/simd-select-bitmask-invalid.stderr b/src/tools/miri/tests/fail/intrinsics/simd-select-bitmask-invalid.stderr deleted file mode 100644 index 9acb51d8c5f..00000000000 --- a/src/tools/miri/tests/fail/intrinsics/simd-select-bitmask-invalid.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: a SIMD bitmask less than 8 bits long must be filled with 0s for the remaining bits - --> tests/fail/intrinsics/simd-select-bitmask-invalid.rs:LL:CC - | -LL | simd_select_bitmask(0b11111111u8, x, x); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ a SIMD bitmask less than 8 bits long must be filled with 0s for the remaining bits - | - = 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 - = note: BACKTRACE: - = note: inside `main` at tests/fail/intrinsics/simd-select-bitmask-invalid.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/provenance/int_copy_looses_provenance3.stderr b/src/tools/miri/tests/fail/provenance/int_copy_looses_provenance3.stderr index 62e3bd2e954..4e741fe8329 100644 --- a/src/tools/miri/tests/fail/provenance/int_copy_looses_provenance3.stderr +++ b/src/tools/miri/tests/fail/provenance/int_copy_looses_provenance3.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: memory access failed: expected a pointer to 4 bytes of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) +error: Undefined Behavior: memory access failed: attempting to access 4 bytes, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) --> tests/fail/provenance/int_copy_looses_provenance3.rs:LL:CC | LL | let _val = unsafe { *ptr }; - | ^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^ memory access failed: attempting to access 4 bytes, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) | = 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/provenance/pointer_partial_overwrite.stderr b/src/tools/miri/tests/fail/provenance/pointer_partial_overwrite.stderr index 6bc92fffd5a..370f9463b73 100644 --- a/src/tools/miri/tests/fail/provenance/pointer_partial_overwrite.stderr +++ b/src/tools/miri/tests/fail/provenance/pointer_partial_overwrite.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: memory access failed: expected a pointer to 4 bytes of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) +error: Undefined Behavior: memory access failed: attempting to access 4 bytes, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) --> tests/fail/provenance/pointer_partial_overwrite.rs:LL:CC | LL | let x = *p; - | ^^ memory access failed: expected a pointer to 4 bytes of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) + | ^^ memory access failed: attempting to access 4 bytes, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) | = 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/provenance/provenance_transmute.stderr b/src/tools/miri/tests/fail/provenance/provenance_transmute.stderr index 7403f4382de..38e2e19009a 100644 --- a/src/tools/miri/tests/fail/provenance/provenance_transmute.stderr +++ b/src/tools/miri/tests/fail/provenance/provenance_transmute.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: memory access failed: expected a pointer to 1 byte of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) +error: Undefined Behavior: memory access failed: attempting to access 1 byte, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) --> tests/fail/provenance/provenance_transmute.rs:LL:CC | LL | let _val = *left_ptr; - | ^^^^^^^^^ memory access failed: expected a pointer to 1 byte of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^ memory access failed: attempting to access 1 byte, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) | = 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/provenance/ptr_copy_loses_partial_provenance0.stderr b/src/tools/miri/tests/fail/provenance/ptr_copy_loses_partial_provenance0.stderr index 5ed83951c60..5225ab32865 100644 --- a/src/tools/miri/tests/fail/provenance/ptr_copy_loses_partial_provenance0.stderr +++ b/src/tools/miri/tests/fail/provenance/ptr_copy_loses_partial_provenance0.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: memory access failed: expected a pointer to 4 bytes of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) +error: Undefined Behavior: memory access failed: attempting to access 4 bytes, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) --> tests/fail/provenance/ptr_copy_loses_partial_provenance0.rs:LL:CC | LL | let _val = *ptr; - | ^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^ memory access failed: attempting to access 4 bytes, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) | = 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/provenance/ptr_copy_loses_partial_provenance1.stderr b/src/tools/miri/tests/fail/provenance/ptr_copy_loses_partial_provenance1.stderr index 3675653cbe7..c17c98fa105 100644 --- a/src/tools/miri/tests/fail/provenance/ptr_copy_loses_partial_provenance1.stderr +++ b/src/tools/miri/tests/fail/provenance/ptr_copy_loses_partial_provenance1.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: memory access failed: expected a pointer to 4 bytes of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) +error: Undefined Behavior: memory access failed: attempting to access 4 bytes, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) --> tests/fail/provenance/ptr_copy_loses_partial_provenance1.rs:LL:CC | LL | let _val = *ptr; - | ^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^ memory access failed: attempting to access 4 bytes, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) | = 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/provenance/ptr_int_unexposed.stderr b/src/tools/miri/tests/fail/provenance/ptr_int_unexposed.stderr index 1b6518612ef..78290d4ed63 100644 --- a/src/tools/miri/tests/fail/provenance/ptr_int_unexposed.stderr +++ b/src/tools/miri/tests/fail/provenance/ptr_int_unexposed.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: memory access failed: expected a pointer to 4 bytes of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) +error: Undefined Behavior: memory access failed: attempting to access 4 bytes, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) --> tests/fail/provenance/ptr_int_unexposed.rs:LL:CC | LL | assert_eq!(unsafe { *ptr }, 3); - | ^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^ memory access failed: attempting to access 4 bytes, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) | = 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/provenance/ptr_invalid.stderr b/src/tools/miri/tests/fail/provenance/ptr_invalid.stderr index 84347ec7a11..ff73fbb9d1b 100644 --- a/src/tools/miri/tests/fail/provenance/ptr_invalid.stderr +++ b/src/tools/miri/tests/fail/provenance/ptr_invalid.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: memory access failed: expected a pointer to 4 bytes of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) +error: Undefined Behavior: memory access failed: attempting to access 4 bytes, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) --> tests/fail/provenance/ptr_invalid.rs:LL:CC | LL | let _val = unsafe { *xptr_invalid }; - | ^^^^^^^^^^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^ memory access failed: attempting to access 4 bytes, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) | = 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/provenance/ptr_invalid_offset.stderr b/src/tools/miri/tests/fail/provenance/ptr_invalid_offset.stderr index 3910bc4df4b..07556540f13 100644 --- a/src/tools/miri/tests/fail/provenance/ptr_invalid_offset.stderr +++ b/src/tools/miri/tests/fail/provenance/ptr_invalid_offset.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: out-of-bounds pointer arithmetic: expected a pointer to 1 byte of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) +error: Undefined Behavior: in-bounds pointer arithmetic failed: attempting to offset pointer by 1 byte, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) --> tests/fail/provenance/ptr_invalid_offset.rs:LL:CC | LL | let _ = unsafe { roundtrip.offset(1) }; - | ^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to 1 byte of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by 1 byte, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) | = 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/rc_as_ptr.stderr b/src/tools/miri/tests/fail/rc_as_ptr.stderr index 0fcb0faf497..e1d0e5780a0 100644 --- a/src/tools/miri/tests/fail/rc_as_ptr.stderr +++ b/src/tools/miri/tests/fail/rc_as_ptr.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: out-of-bounds pointer use: ALLOC has been freed, so this pointer is dangling +error: Undefined Behavior: pointer not dereferenceable: ALLOC has been freed, so this pointer is dangling --> tests/fail/rc_as_ptr.rs:LL:CC | LL | assert_eq!(42, **unsafe { &*Weak::as_ptr(&weak) }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: ALLOC has been freed, so this pointer is dangling + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pointer not dereferenceable: ALLOC has been freed, so this pointer is dangling | = 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/reading_half_a_pointer.stderr b/src/tools/miri/tests/fail/reading_half_a_pointer.stderr index 92179644169..61fb9cd4e52 100644 --- a/src/tools/miri/tests/fail/reading_half_a_pointer.stderr +++ b/src/tools/miri/tests/fail/reading_half_a_pointer.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: memory access failed: expected a pointer to 1 byte of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) +error: Undefined Behavior: memory access failed: attempting to access 1 byte, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) --> tests/fail/reading_half_a_pointer.rs:LL:CC | LL | let _val = *x; - | ^^ memory access failed: expected a pointer to 1 byte of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) + | ^^ memory access failed: attempting to access 1 byte, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) | = 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/shims/backtrace/bad-backtrace-ptr.rs b/src/tools/miri/tests/fail/shims/backtrace/bad-backtrace-ptr.rs index 75f7aae9718..a398eb1ae0c 100644 --- a/src/tools/miri/tests/fail/shims/backtrace/bad-backtrace-ptr.rs +++ b/src/tools/miri/tests/fail/shims/backtrace/bad-backtrace-ptr.rs @@ -4,6 +4,6 @@ extern "Rust" { fn main() { unsafe { - miri_resolve_frame(std::ptr::null_mut(), 0); //~ ERROR: got a null pointer + miri_resolve_frame(std::ptr::null_mut(), 0); //~ ERROR: null pointer } } diff --git a/src/tools/miri/tests/fail/shims/backtrace/bad-backtrace-ptr.stderr b/src/tools/miri/tests/fail/shims/backtrace/bad-backtrace-ptr.stderr index 7ae9558fad7..126f41fbb0e 100644 --- a/src/tools/miri/tests/fail/shims/backtrace/bad-backtrace-ptr.stderr +++ b/src/tools/miri/tests/fail/shims/backtrace/bad-backtrace-ptr.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: out-of-bounds pointer use: expected a pointer to some allocation, but got a null pointer +error: Undefined Behavior: pointer not dereferenceable: pointer must point to some allocation, but got null pointer --> tests/fail/shims/backtrace/bad-backtrace-ptr.rs:LL:CC | LL | miri_resolve_frame(std::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: expected a pointer to some allocation, but got a null pointer + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pointer not dereferenceable: pointer must point to some allocation, but got null pointer | = 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/zst_local_oob.rs b/src/tools/miri/tests/fail/zst_local_oob.rs index ab48b7d330b..2bf184c72ba 100644 --- a/src/tools/miri/tests/fail/zst_local_oob.rs +++ b/src/tools/miri/tests/fail/zst_local_oob.rs @@ -1,5 +1,5 @@ fn main() { // make sure ZST locals cannot be accessed let x = &() as *const () as *const i8; - let _val = unsafe { *x }; //~ ERROR: expected a pointer to 1 byte of memory + let _val = unsafe { *x }; //~ ERROR: attempting to access 1 byte } diff --git a/src/tools/miri/tests/fail/zst_local_oob.stderr b/src/tools/miri/tests/fail/zst_local_oob.stderr index 26911948eff..e9423096226 100644 --- a/src/tools/miri/tests/fail/zst_local_oob.stderr +++ b/src/tools/miri/tests/fail/zst_local_oob.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: memory access failed: expected a pointer to 1 byte of memory, but got ALLOC which is at or beyond the end of the allocation of size 0 bytes +error: Undefined Behavior: memory access failed: attempting to access 1 byte, but got ALLOC which is at or beyond the end of the allocation of size 0 bytes --> tests/fail/zst_local_oob.rs:LL:CC | LL | let _val = unsafe { *x }; - | ^^ memory access failed: expected a pointer to 1 byte of memory, but got ALLOC which is at or beyond the end of the allocation of size 0 bytes + | ^^ memory access failed: attempting to access 1 byte, but got ALLOC which is at or beyond the end of the allocation of size 0 bytes | = 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/pass/intrinsics/portable-simd.rs b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs index cc753dac215..e14ce51f35a 100644 --- a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs +++ b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs @@ -331,6 +331,19 @@ fn simd_mask() { ); assert_eq!(selected1, i32x4::from_array([0, 0, 0, 1])); assert_eq!(selected2, selected1); + // Non-zero "padding" (the extra bits) is also allowed. + let selected1 = simd_select_bitmask::<u8, _>( + if cfg!(target_endian = "little") { 0b11111000 } else { 0b11110001 }, + i32x4::splat(1), // yes + i32x4::splat(0), // no + ); + let selected2 = simd_select_bitmask::<[u8; 1], _>( + if cfg!(target_endian = "little") { [0b11111000] } else { [0b11110001] }, + i32x4::splat(1), // yes + i32x4::splat(0), // no + ); + assert_eq!(selected1, i32x4::from_array([0, 0, 0, 1])); + assert_eq!(selected2, selected1); } // Non-power-of-2 multi-byte mask. diff --git a/src/tools/rustdoc-gui-test/src/config.rs b/src/tools/rustdoc-gui-test/src/config.rs index 950e2fa478d..b9d08a0a295 100644 --- a/src/tools/rustdoc-gui-test/src/config.rs +++ b/src/tools/rustdoc-gui-test/src/config.rs @@ -20,8 +20,8 @@ pub(crate) struct Config { impl Config { pub(crate) fn from_args(args: Vec<String>) -> Self { let mut opts = Options::new(); - opts.reqopt("", "nodejs", "absolute path of nodejs", "PATH") - .reqopt("", "npm", "absolute path of npm", "PATH") + opts.optopt("", "nodejs", "absolute path of nodejs", "PATH") + .optopt("", "npm", "absolute path of npm", "PATH") .reqopt("", "out-dir", "output path of doc compilation", "PATH") .reqopt("", "rust-src", "root source of the rust source", "PATH") .reqopt( @@ -47,9 +47,18 @@ impl Config { Err(f) => panic!("{:?}", f), }; + let Some(nodejs) = matches.opt_str("nodejs").map(PathBuf::from) else { + eprintln!("`nodejs` was not provided. If not available, please install it"); + std::process::exit(1); + }; + let Some(npm) = matches.opt_str("npm").map(PathBuf::from) else { + eprintln!("`npm` was not provided. If not available, please install it"); + std::process::exit(1); + }; + Self { - nodejs: matches.opt_str("nodejs").map(PathBuf::from).expect("nodejs isn't available"), - npm: matches.opt_str("npm").map(PathBuf::from).expect("npm isn't available"), + nodejs, + npm, rust_src: matches.opt_str("rust-src").map(PathBuf::from).unwrap(), out_dir: matches.opt_str("out-dir").map(PathBuf::from).unwrap(), initial_cargo: matches.opt_str("initial-cargo").map(PathBuf::from).unwrap(), diff --git a/tests/codegen/intrinsics/select_unpredictable.rs b/tests/codegen/intrinsics/select_unpredictable.rs index 2db4ae174b3..ad7120c6fb8 100644 --- a/tests/codegen/intrinsics/select_unpredictable.rs +++ b/tests/codegen/intrinsics/select_unpredictable.rs @@ -1,7 +1,6 @@ //@ compile-flags: -Copt-level=3 -Zmerge-functions=disabled #![feature(core_intrinsics)] -#![feature(select_unpredictable)] #![crate_type = "lib"] /* Test the intrinsic */ diff --git a/tests/crashes/138156.rs b/tests/crashes/138156.rs new file mode 100644 index 00000000000..48c6455627f --- /dev/null +++ b/tests/crashes/138156.rs @@ -0,0 +1,42 @@ +//@ known-bug: #138156 + +#![feature(generic_const_exprs)] + +#[derive(Default)] +pub struct GenId<const IDX: usize>; + +pub trait IndexTrait: Default { + const IDX: usize; +} +pub trait ToplogyIndex { + type Idx: IndexTrait; +} + +#[derive(Default)] +pub struct Expression<T: ToplogyIndex> { + pub data: T, +} + +fn i<T: ToplogyIndex, const IDX0: usize, const IDX1: usize>(s: Expression<T>) -> + Expression<GenId<{ IDX0 | IDX1 }>> +where + GenId<{ IDX0 | IDX1 }>: ToplogyIndex, +{ + Expression::default() +} + +pub fn sum<In: ToplogyIndex>(s: Expression<In>) -> Expression<In> +where + [(); In::Idx::IDX]:, +{ + s +} + +fn param_position<In: ToplogyIndex>(s: Expression<In>) +where + GenId<{ 1 | 2 }>: ToplogyIndex, +{ + sum(i::<_, 1, 2>(s)); +} + +fn main() {} diff --git a/tests/crashes/138240.rs b/tests/crashes/138240.rs new file mode 100644 index 00000000000..6ffb7868bd5 --- /dev/null +++ b/tests/crashes/138240.rs @@ -0,0 +1,9 @@ +//@ known-bug: #138240 +//@edition:2024 +#![feature(min_generic_const_args)] +#![feature(inherent_associated_types)] +async fn _CF() -> Box<[u8; Box::b]> { + Box::new(true) +} + +fn main() {} diff --git a/tests/crashes/138265.rs b/tests/crashes/138265.rs new file mode 100644 index 00000000000..f6c8ea74889 --- /dev/null +++ b/tests/crashes/138265.rs @@ -0,0 +1,12 @@ +//@ known-bug: #138265 + +#![feature(coerce_unsized)] +#![crate_type = "lib"] +impl<A> std::ops::CoerceUnsized<A> for A {} +pub fn f() { + [0; { + let mut c = &0; + c = &0; + 0 + }] +} diff --git a/tests/crashes/138266.rs b/tests/crashes/138266.rs new file mode 100644 index 00000000000..9a4de9abcff --- /dev/null +++ b/tests/crashes/138266.rs @@ -0,0 +1,7 @@ +//@ known-bug: #138266 +//@compile-flags: --crate-type=lib +#![feature(min_generic_const_args)] +#![feature(inherent_associated_types)] +pub fn f(mut x: [u8; Box::b]) { + x[72] = 1; +} diff --git a/tests/crashes/138359.rs b/tests/crashes/138359.rs new file mode 100644 index 00000000000..d4376d536ee --- /dev/null +++ b/tests/crashes/138359.rs @@ -0,0 +1,8 @@ +//@ known-bug: #138359 +#![feature(min_generic_const_args)] +#![feature(inherent_associated_types)] +struct a(Box<[u8; Box::b]>); +impl a { + fn c(self) { self.0.da } +} +fn main() {} diff --git a/tests/crashes/138361.rs b/tests/crashes/138361.rs new file mode 100644 index 00000000000..8661ed37474 --- /dev/null +++ b/tests/crashes/138361.rs @@ -0,0 +1,6 @@ +//@ known-bug: #138361 + +fn main() { + [0; loop{}]; + std::mem::transmute(4) +} diff --git a/tests/crashes/138510.rs b/tests/crashes/138510.rs new file mode 100644 index 00000000000..f429e8bb33b --- /dev/null +++ b/tests/crashes/138510.rs @@ -0,0 +1,7 @@ +//@ known-bug: #138510 +fn main() +where + #[repr()] + _: Sized, +{ +} diff --git a/tests/crashes/138534.rs b/tests/crashes/138534.rs new file mode 100644 index 00000000000..80f9cd22518 --- /dev/null +++ b/tests/crashes/138534.rs @@ -0,0 +1,6 @@ +//@ known-bug: #138534 +//@compile-flags: -Zunpretty=expanded +#[repr(bool)] +pub enum TopFg { + Bar, +} diff --git a/tests/crashes/138564.rs b/tests/crashes/138564.rs new file mode 100644 index 00000000000..b10f75f8cdd --- /dev/null +++ b/tests/crashes/138564.rs @@ -0,0 +1,26 @@ +//@ known-bug: #138564 +//@compile-flags: -Copt-level=0 -Cdebuginfo=2 --crate-type lib +#![feature(unsize, dispatch_from_dyn, arbitrary_self_types)] + +use std::marker::Unsize; +use std::ops::{Deref, DispatchFromDyn}; + +#[repr(align(16))] +pub struct MyPointer<T: ?Sized>(*const T); + +impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<MyPointer<U>> for MyPointer<T> {} +impl<T: ?Sized> Deref for MyPointer<T> { + type Target = T; + fn deref(&self) -> &T { + unimplemented!() + } +} + +pub trait Trait { + fn foo(self: MyPointer<Self>) {} +} + +// make sure some usage of `<dyn Trait>::foo` makes it to codegen +pub fn user() -> *const () { + <dyn Trait>::foo as *const () +} diff --git a/tests/crashes/138707.rs b/tests/crashes/138707.rs new file mode 100644 index 00000000000..4d9a82500ec --- /dev/null +++ b/tests/crashes/138707.rs @@ -0,0 +1,37 @@ +//@ known-bug: #138707 +//@edition:2024 +//@compile-flags: --crate-type lib +use core::marker::PhantomData; + +struct LeftReflector<S> { + _phantom: PhantomData<S>, +} + +struct DefaultAllocator {} + +trait Allocator<R> { + type Buffer; +} + +struct U2 {} + +impl Allocator<U2> for DefaultAllocator { + type Buffer = [u8; 2]; +} + +impl<R> From<R> for LeftReflector<<DefaultAllocator as Allocator<R>>::Buffer> +where + DefaultAllocator: Allocator<R>, +{ + fn from(_: R) -> Self { + todo!() + } +} + +fn ice<D>(a: U2) +where + DefaultAllocator: Allocator<D>, +{ + // ICE + let _ = LeftReflector::from(a); +} diff --git a/tests/crashes/138738.rs b/tests/crashes/138738.rs new file mode 100644 index 00000000000..74e5effa56f --- /dev/null +++ b/tests/crashes/138738.rs @@ -0,0 +1,7 @@ +//@ known-bug: #138738 +//@ only-x86_64 + +#![feature(abi_ptx)] +fn main() { + let a = unsafe { core::mem::transmute::<usize, extern "ptx-kernel" fn(i32)>(4) }(2); +} diff --git a/tests/crashes/139089.rs b/tests/crashes/139089.rs new file mode 100644 index 00000000000..3326aa6ad98 --- /dev/null +++ b/tests/crashes/139089.rs @@ -0,0 +1,2 @@ +//@ known-bug: #139089 +pub fn foo3(x: &Vec<u8>) { x.push(0); } diff --git a/tests/crashes/139120.rs b/tests/crashes/139120.rs new file mode 100644 index 00000000000..f946f010c44 --- /dev/null +++ b/tests/crashes/139120.rs @@ -0,0 +1,29 @@ +//@ known-bug: #139120 + + + +pub trait Foo { + type Bar<'a>; +} + +pub struct FooImpl {} + +impl Foo for FooImpl { + type Bar<'a> = (); +} + +pub trait FooFn { + fn bar(&self); +} + +impl<T: Foo> FooFn for fn(T, T::Bar<'_>) { + fn bar(&self) {} +} + +fn foo<T: Foo>(f: fn(T, T::Bar<'_>)) { + let _: &dyn FooFn = &f; +} + +fn main() { + foo(|_: FooImpl, _| {}); +} diff --git a/tests/crashes/139381.rs b/tests/crashes/139381.rs new file mode 100644 index 00000000000..6757b584e82 --- /dev/null +++ b/tests/crashes/139381.rs @@ -0,0 +1,13 @@ +//@ known-bug: #139381 +//@ needs-rustc-debug-assertions +trait A<'a> { + type Assoc: ?Sized; +} + +impl<'a> A<'a> for () { + type Assoc = &'a (); +} + +fn hello() -> impl for<'a> A<'a, Assoc: Into<u8> + 'static + Copy> { + () +} diff --git a/tests/crashes/139387.rs b/tests/crashes/139387.rs new file mode 100644 index 00000000000..133643ad084 --- /dev/null +++ b/tests/crashes/139387.rs @@ -0,0 +1,15 @@ +//@ known-bug: #139387 +//@ needs-rustc-debug-assertions + +trait A { + fn method() -> impl Sized; +} +trait B { + fn method(Hash: Wrap<impl Beta<U: Copy + for<'a> Epsilon<'_, SI1: Eta>>>) -> impl Sized; +} + +fn ambiguous<T: A + B>() +where + T::method(..): Send, +{ +} diff --git a/tests/crashes/139409.rs b/tests/crashes/139409.rs new file mode 100644 index 00000000000..68cbfa153de --- /dev/null +++ b/tests/crashes/139409.rs @@ -0,0 +1,12 @@ +//@ known-bug: #139409 +//@ compile-flags: -Znext-solver=globally + +fn main() { + trait B<C> {} + impl<C> B<C> for () {} + trait D<C, E>: B<C> + B<E> { + fn f(&self) {} + } + impl<C, E> D<C, E> for () {} + (&() as &dyn D<&(), &()>).f() +} diff --git a/tests/crashes/139462.rs b/tests/crashes/139462.rs new file mode 100644 index 00000000000..05bb246d7be --- /dev/null +++ b/tests/crashes/139462.rs @@ -0,0 +1,8 @@ +//@ known-bug: #139462 +//@ compile-flags: -Cdebuginfo=2 +#![feature(unsafe_binders)] +use std::unsafe_binder::wrap_binder; +fn main() { + let foo = 0; + let foo: unsafe<'a> &'a u32 = unsafe { wrap_binder!(&foo) }; +} diff --git a/tests/crashes/139556.rs b/tests/crashes/139556.rs new file mode 100644 index 00000000000..60dc8d7c3af --- /dev/null +++ b/tests/crashes/139556.rs @@ -0,0 +1,13 @@ +//@ known-bug: #139556 + +trait T {} + +type Alias<'a> = impl T; + +struct S; +impl<'a> T for &'a S {} + +#[define_opaque(Alias)] +fn with_positive(fun: impl Fn(Alias<'_>)) { + with_positive(|&n| ()); +} diff --git a/tests/crashes/139570.rs b/tests/crashes/139570.rs new file mode 100644 index 00000000000..9c001aaf848 --- /dev/null +++ b/tests/crashes/139570.rs @@ -0,0 +1,4 @@ +//@ known-bug: #139570 +fn main() { + |(1, 42), ()| yield; +} diff --git a/tests/crashes/139596.rs b/tests/crashes/139596.rs new file mode 100644 index 00000000000..590cfddf83e --- /dev/null +++ b/tests/crashes/139596.rs @@ -0,0 +1,10 @@ +//@ known-bug: #139596 + +#![feature(min_generic_const_args)] +struct Colour; + +struct Led<const C: Colour>; + +fn main() { + Led::<{ Colour}>; +} diff --git a/tests/crashes/139659.rs b/tests/crashes/139659.rs new file mode 100644 index 00000000000..7fc33f7e6a7 --- /dev/null +++ b/tests/crashes/139659.rs @@ -0,0 +1,29 @@ +//@ known-bug: #139659 +//@compile-flags: -Cdebuginfo=2 -Copt-level=0 --crate-type lib +trait Trait { + type Output; +} + +impl<O, F: Fn() -> O> Trait for F { + type Output = O; +} + +struct Wrap<P>(P); +struct WrapOutput<O>(O); + +impl<P: Trait> Trait for Wrap<P> { + type Output = WrapOutput<P::Output>; +} + +fn wrap<P: Trait>(x: P) -> impl Trait { + Wrap(x) +} + +fn consume<P: Trait>(_: P) -> P::Output { + unimplemented!() +} + +pub fn recurse() -> impl Sized { + consume(wrap(recurse)) +} +pub fn main() {} diff --git a/tests/crashes/139738.rs b/tests/crashes/139738.rs new file mode 100644 index 00000000000..c0e7307de6c --- /dev/null +++ b/tests/crashes/139738.rs @@ -0,0 +1,3 @@ +//@ known-bug: #139738 +#![feature(generic_const_exprs)] +fn b<'a>() -> impl IntoIterator<[(); (|_: &'a u8| 0, 0).1]> {} diff --git a/tests/crashes/139815.rs b/tests/crashes/139815.rs new file mode 100644 index 00000000000..9094acdc94b --- /dev/null +++ b/tests/crashes/139815.rs @@ -0,0 +1,14 @@ +//@ known-bug: #139815 + +#![feature(generic_const_exprs)] +fn is_123<const N: usize>( + x: [u32; { + N + 1; + 5 + }], +) -> bool { + match x { + [1, 2] => true, + _ => false, + } +} diff --git a/tests/crashes/139817.rs b/tests/crashes/139817.rs new file mode 100644 index 00000000000..d439ed4cacb --- /dev/null +++ b/tests/crashes/139817.rs @@ -0,0 +1,8 @@ +//@ known-bug: #139817 +fn enum_upvar() { + type T = impl Copy; + let foo: T = Some((42, std::marker::PhantomData::<T>)); + let x = move || match foo { + None => (), + }; +} diff --git a/tests/crashes/139825.rs b/tests/crashes/139825.rs new file mode 100644 index 00000000000..8c5b6b80f0b --- /dev/null +++ b/tests/crashes/139825.rs @@ -0,0 +1,5 @@ +//@ known-bug: #139825 +//@compile-flags: --check-cfg=cfg(docsrs,test) --crate-type lib +struct a +where + for<#[cfg(b)] c> u8:; diff --git a/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff b/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff index a1df868cde0..33f1ad9bef4 100644 --- a/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff +++ b/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff @@ -121,7 +121,7 @@ StorageDead(_18); _16 = &_17; _15 = &(*_16); - _11 = Arguments::<'_>::new_v1::<3, 2>(move _12, move _15) -> [return: bb5, unwind unreachable]; + _11 = core::fmt::rt::<impl Arguments<'_>>::new_v1::<3, 2>(move _12, move _15) -> [return: bb5, unwind unreachable]; } bb5: { diff --git a/tests/pretty/hir-delegation.pp b/tests/pretty/hir-delegation.pp index 872a6a45aed..e452cee6365 100644 --- a/tests/pretty/hir-delegation.pp +++ b/tests/pretty/hir-delegation.pp @@ -2,7 +2,8 @@ //@ pretty-mode:hir //@ pp-exact:hir-delegation.pp -#![allow(incomplete_features)]#![feature(fn_delegation)] +#![allow(incomplete_features)] +#![feature(fn_delegation)] #[prelude_import] use ::std::prelude::rust_2015::*; #[macro_use] diff --git a/tests/pretty/issue-4264.pp b/tests/pretty/issue-4264.pp index 3cff6ca33da..eb808f7122a 100644 --- a/tests/pretty/issue-4264.pp +++ b/tests/pretty/issue-4264.pp @@ -34,7 +34,7 @@ fn bar() ({ ((::alloc::fmt::format as for<'a> fn(Arguments<'a>) -> String {format})(((format_arguments::new_const as - fn(&[&'static str; 1]) -> Arguments<'_> {Arguments::<'_>::new_const::<1>})((&([("test" + fn(&[&'static str; 1]) -> Arguments<'_> {core::fmt::rt::<impl Arguments<'_>>::new_const::<1>})((&([("test" as &str)] as [&str; 1]) as &[&str; 1])) as Arguments<'_>)) as String) } as String)) as String); diff --git a/tests/pretty/shebang-at-top.pp b/tests/pretty/shebang-at-top.pp new file mode 100644 index 00000000000..a2797252636 --- /dev/null +++ b/tests/pretty/shebang-at-top.pp @@ -0,0 +1,12 @@ +#!/usr/bin/env rust +#![feature(prelude_import)] +#![no_std] +#[prelude_import] +use ::std::prelude::rust_2015::*; +#[macro_use] +extern crate std; +//@ pretty-mode:expanded +//@ pp-exact:shebang-at-top.pp +//@ pretty-compare-only + +fn main() {} diff --git a/tests/pretty/shebang-at-top.rs b/tests/pretty/shebang-at-top.rs new file mode 100644 index 00000000000..8bfa925fa1a --- /dev/null +++ b/tests/pretty/shebang-at-top.rs @@ -0,0 +1,6 @@ +#!/usr/bin/env rust +//@ pretty-mode:expanded +//@ pp-exact:shebang-at-top.pp +//@ pretty-compare-only + +fn main() {} diff --git a/tests/rustdoc-json/attrs/automatically_derived.rs b/tests/rustdoc-json/attrs/automatically_derived.rs index 4e1ab3d145e..6c90d638649 100644 --- a/tests/rustdoc-json/attrs/automatically_derived.rs +++ b/tests/rustdoc-json/attrs/automatically_derived.rs @@ -9,5 +9,5 @@ impl Default for Manual { } } -//@ is '$.index[?(@.inner.impl.for.resolved_path.path == "Derive" && @.inner.impl.trait.path == "Default")].attrs' '["#[automatically_derived]"]' +//@ is '$.index[?(@.inner.impl.for.resolved_path.path == "Derive" && @.inner.impl.trait.path == "Default")].attrs' '["#[automatically_derived]\n"]' //@ is '$.index[?(@.inner.impl.for.resolved_path.path == "Manual" && @.inner.impl.trait.path == "Default")].attrs' '[]' diff --git a/tests/rustdoc-json/attrs/export_name_2021.rs b/tests/rustdoc-json/attrs/export_name_2021.rs index 254e9f6ef5b..4e6526419bd 100644 --- a/tests/rustdoc-json/attrs/export_name_2021.rs +++ b/tests/rustdoc-json/attrs/export_name_2021.rs @@ -1,6 +1,6 @@ //@ edition: 2021 #![no_std] -//@ is "$.index[?(@.name=='example')].attrs" '["#[export_name = \"altered\"]"]' +//@ is "$.index[?(@.name=='example')].attrs" '["#[export_name = \"altered\"]\n"]' #[export_name = "altered"] pub extern "C" fn example() {} diff --git a/tests/rustdoc-json/attrs/export_name_2024.rs b/tests/rustdoc-json/attrs/export_name_2024.rs index 8129c109306..f6a2a92b5bc 100644 --- a/tests/rustdoc-json/attrs/export_name_2024.rs +++ b/tests/rustdoc-json/attrs/export_name_2024.rs @@ -4,6 +4,6 @@ // The representation of `#[unsafe(export_name = ..)]` in rustdoc in edition 2024 // is still `#[export_name = ..]` without the `unsafe` attribute wrapper. -//@ is "$.index[?(@.name=='example')].attrs" '["#[export_name = \"altered\"]"]' +//@ is "$.index[?(@.name=='example')].attrs" '["#[export_name = \"altered\"]\n"]' #[unsafe(export_name = "altered")] pub extern "C" fn example() {} diff --git a/tests/rustdoc-json/attrs/must_use.rs b/tests/rustdoc-json/attrs/must_use.rs index 64df8e5f509..20696dce712 100644 --- a/tests/rustdoc-json/attrs/must_use.rs +++ b/tests/rustdoc-json/attrs/must_use.rs @@ -1,9 +1,9 @@ #![no_std] -//@ is "$.index[?(@.name=='example')].attrs" '["#[must_use]"]' +//@ is "$.index[?(@.name=='example')].attrs" '["#[must_use]\n"]' #[must_use] pub fn example() -> impl Iterator<Item = i64> {} -//@ is "$.index[?(@.name=='explicit_message')].attrs" '["#[must_use = \"does nothing if you do not use it\"]"]' +//@ is "$.index[?(@.name=='explicit_message')].attrs" '["#[must_use = \"does nothing if you do not use it\"]\n"]' #[must_use = "does nothing if you do not use it"] pub fn explicit_message() -> impl Iterator<Item = i64> {} diff --git a/tests/rustdoc-json/attrs/no_mangle_2021.rs b/tests/rustdoc-json/attrs/no_mangle_2021.rs index 588be7256db..10a372572ae 100644 --- a/tests/rustdoc-json/attrs/no_mangle_2021.rs +++ b/tests/rustdoc-json/attrs/no_mangle_2021.rs @@ -1,6 +1,6 @@ //@ edition: 2021 #![no_std] -//@ is "$.index[?(@.name=='example')].attrs" '["#[no_mangle]"]' +//@ is "$.index[?(@.name=='example')].attrs" '["#[no_mangle]\n"]' #[no_mangle] pub extern "C" fn example() {} diff --git a/tests/rustdoc-json/attrs/no_mangle_2024.rs b/tests/rustdoc-json/attrs/no_mangle_2024.rs index 0d500e20e6c..8f3a14cbecb 100644 --- a/tests/rustdoc-json/attrs/no_mangle_2024.rs +++ b/tests/rustdoc-json/attrs/no_mangle_2024.rs @@ -4,6 +4,6 @@ // The representation of `#[unsafe(no_mangle)]` in rustdoc in edition 2024 // is still `#[no_mangle]` without the `unsafe` attribute wrapper. -//@ is "$.index[?(@.name=='example')].attrs" '["#[no_mangle]"]' +//@ is "$.index[?(@.name=='example')].attrs" '["#[no_mangle]\n"]' #[unsafe(no_mangle)] pub extern "C" fn example() {} diff --git a/tests/rustdoc-json/attrs/non_exhaustive.rs b/tests/rustdoc-json/attrs/non_exhaustive.rs index b95f1a8171f..3064b86422d 100644 --- a/tests/rustdoc-json/attrs/non_exhaustive.rs +++ b/tests/rustdoc-json/attrs/non_exhaustive.rs @@ -1,18 +1,18 @@ #![no_std] -//@ is "$.index[?(@.name=='MyEnum')].attrs" '["#[non_exhaustive]"]' +//@ is "$.index[?(@.name=='MyEnum')].attrs" '["#[non_exhaustive]\n"]' #[non_exhaustive] pub enum MyEnum { First, } pub enum NonExhaustiveVariant { - //@ is "$.index[?(@.name=='Variant')].attrs" '["#[non_exhaustive]"]' + //@ is "$.index[?(@.name=='Variant')].attrs" '["#[non_exhaustive]\n"]' #[non_exhaustive] Variant(i64), } -//@ is "$.index[?(@.name=='MyStruct')].attrs" '["#[non_exhaustive]"]' +//@ is "$.index[?(@.name=='MyStruct')].attrs" '["#[non_exhaustive]\n"]' #[non_exhaustive] pub struct MyStruct { pub x: i64, diff --git a/tests/rustdoc-json/keyword_private.rs b/tests/rustdoc-json/keyword_private.rs index fea546c9fb6..5e9a2c10163 100644 --- a/tests/rustdoc-json/keyword_private.rs +++ b/tests/rustdoc-json/keyword_private.rs @@ -5,7 +5,7 @@ //@ !has "$.index[?(@.name=='match')]" //@ has "$.index[?(@.name=='foo')]" -//@ is "$.index[?(@.name=='foo')].attrs" '["#[doc(keyword = \"match\")]"]' +//@ is "$.index[?(@.name=='foo')].attrs" '["#[doc(keyword = \"match\")]\n"]' //@ is "$.index[?(@.name=='foo')].docs" '"this is a test!"' #[doc(keyword = "match")] /// this is a test! @@ -13,7 +13,7 @@ pub mod foo {} //@ !has "$.index[?(@.name=='break')]" //@ has "$.index[?(@.name=='bar')]" -//@ is "$.index[?(@.name=='bar')].attrs" '["#[doc(keyword = \"break\")]"]' +//@ is "$.index[?(@.name=='bar')].attrs" '["#[doc(keyword = \"break\")]\n"]' //@ is "$.index[?(@.name=='bar')].docs" '"hello"' #[doc(keyword = "break")] /// hello diff --git a/tests/rustdoc-ui/doctest/auxiliary/items.rs b/tests/rustdoc-ui/doctest/auxiliary/items.rs new file mode 100644 index 00000000000..40d4eb261e5 --- /dev/null +++ b/tests/rustdoc-ui/doctest/auxiliary/items.rs @@ -0,0 +1 @@ +fn item() {} diff --git a/tests/rustdoc-ui/doctest/auxiliary/macro-after-main.rs b/tests/rustdoc-ui/doctest/auxiliary/macro-after-main.rs deleted file mode 100644 index ed7584b7425..00000000000 --- a/tests/rustdoc-ui/doctest/auxiliary/macro-after-main.rs +++ /dev/null @@ -1 +0,0 @@ -use std::string::String; diff --git a/tests/rustdoc-ui/doctest/macro-after-main.rs b/tests/rustdoc-ui/doctest/macro-after-main.rs deleted file mode 100644 index 0a42343f1c2..00000000000 --- a/tests/rustdoc-ui/doctest/macro-after-main.rs +++ /dev/null @@ -1,16 +0,0 @@ -// This test checks a corner case where the macro calls used to be skipped, -// making them considered as statement, and therefore some cases where -// `include!` macro was then put into a function body, making the doctest -// compilation fail. - -//@ compile-flags:--test -//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR" -//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" -//@ check-pass - -//! ``` -//! include!("./auxiliary/macro-after-main.rs"); -//! -//! fn main() {} -//! eprintln!(); -//! ``` diff --git a/tests/rustdoc-ui/doctest/macro-after-main.stdout b/tests/rustdoc-ui/doctest/macro-after-main.stdout deleted file mode 100644 index 72ffe2b5a27..00000000000 --- a/tests/rustdoc-ui/doctest/macro-after-main.stdout +++ /dev/null @@ -1,6 +0,0 @@ - -running 1 test -test $DIR/macro-after-main.rs - (line 11) ... ok - -test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME - diff --git a/tests/rustdoc-ui/doctest/main-alongside-macro-calls.fail.stdout b/tests/rustdoc-ui/doctest/main-alongside-macro-calls.fail.stdout new file mode 100644 index 00000000000..65989a8ef47 --- /dev/null +++ b/tests/rustdoc-ui/doctest/main-alongside-macro-calls.fail.stdout @@ -0,0 +1,60 @@ + +running 4 tests +test $DIR/main-alongside-macro-calls.rs - (line 19) ... ok +test $DIR/main-alongside-macro-calls.rs - (line 24) ... ok +test $DIR/main-alongside-macro-calls.rs - (line 28) ... FAILED +test $DIR/main-alongside-macro-calls.rs - (line 33) ... FAILED + +failures: + +---- $DIR/main-alongside-macro-calls.rs - (line 28) stdout ---- +error: macros that expand to items must be delimited with braces or followed by a semicolon + --> $DIR/main-alongside-macro-calls.rs:30:1 + | +LL | println!(); + | ^^^^^^^^^^ + | + = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: macro expansion ignores `{` and any tokens following + --> $SRC_DIR/std/src/macros.rs:LL:COL + | + ::: $DIR/main-alongside-macro-calls.rs:30:1 + | +LL | println!(); + | ---------- caused by the macro expansion here + | + = note: the usage of `print!` is likely invalid in item context + +error: aborting due to 2 previous errors + +Couldn't compile the test. +---- $DIR/main-alongside-macro-calls.rs - (line 33) stdout ---- +error: macros that expand to items must be delimited with braces or followed by a semicolon + --> $DIR/main-alongside-macro-calls.rs:34:1 + | +LL | println!(); + | ^^^^^^^^^^ + | + = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: macro expansion ignores `{` and any tokens following + --> $SRC_DIR/std/src/macros.rs:LL:COL + | + ::: $DIR/main-alongside-macro-calls.rs:34:1 + | +LL | println!(); + | ---------- caused by the macro expansion here + | + = note: the usage of `print!` is likely invalid in item context + +error: aborting due to 2 previous errors + +Couldn't compile the test. + +failures: + $DIR/main-alongside-macro-calls.rs - (line 28) + $DIR/main-alongside-macro-calls.rs - (line 33) + +test result: FAILED. 2 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/doctest/main-alongside-macro-calls.pass.stdout b/tests/rustdoc-ui/doctest/main-alongside-macro-calls.pass.stdout new file mode 100644 index 00000000000..93a4bbd8736 --- /dev/null +++ b/tests/rustdoc-ui/doctest/main-alongside-macro-calls.pass.stdout @@ -0,0 +1,9 @@ + +running 4 tests +test $DIR/main-alongside-macro-calls.rs - (line 19) ... ok +test $DIR/main-alongside-macro-calls.rs - (line 24) ... ok +test $DIR/main-alongside-macro-calls.rs - (line 28) - compile fail ... ok +test $DIR/main-alongside-macro-calls.rs - (line 33) - compile fail ... ok + +test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/doctest/main-alongside-macro-calls.rs b/tests/rustdoc-ui/doctest/main-alongside-macro-calls.rs new file mode 100644 index 00000000000..b455d8b0cc3 --- /dev/null +++ b/tests/rustdoc-ui/doctest/main-alongside-macro-calls.rs @@ -0,0 +1,44 @@ +// This test ensures that if there is are any macro calls alongside a `main` function, +// it will indeed consider the `main` function as the program entry point and *won't* +// generate its own `main` function to wrap everything even though macro calls are +// valid in statement contexts, too, and could just as well expand to statements or +// expressions (we don't perform any macro expansion to find `main`, see also +// <https://github.com/rust-lang/rust/issues/57415>). +// +// See <./main-alongside-stmts.rs> for comparison. +// +//@ compile-flags:--test --test-args --test-threads=1 +//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR" +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ revisions: pass fail +//@[pass] check-pass +//@[fail] failure-status: 101 + +// Regression test for <https://github.com/rust-lang/rust/pull/140220#issuecomment-2831872920>: + +//! ``` +//! fn main() {} +//! include!("./auxiliary/items.rs"); +//! ``` +//! +//! ``` +//! include!("./auxiliary/items.rs"); +//! fn main() {} +//! ``` + +// Regression test for <https://github.com/rust-lang/rust/issues/140412>: +// We test the "same" thing twice: Once via `compile_fail` to more closely mirror the reported +// regression and once without it to make sure that it leads to the expected rustc errors, +// namely `println!(…)` not being valid in item contexts. + +#![cfg_attr(pass, doc = " ```compile_fail")] +#![cfg_attr(fail, doc = " ```")] +//! fn main() {} +//! println!(); +//! ``` +//! +#![cfg_attr(pass, doc = " ```compile_fail")] +#![cfg_attr(fail, doc = " ```")] +//! println!(); +//! fn main() {} +//! ``` diff --git a/tests/rustdoc-ui/doctest/main-alongside-stmts.rs b/tests/rustdoc-ui/doctest/main-alongside-stmts.rs new file mode 100644 index 00000000000..5965f928cdd --- /dev/null +++ b/tests/rustdoc-ui/doctest/main-alongside-stmts.rs @@ -0,0 +1,33 @@ +// This test ensures that if there is are any statements alongside a `main` function, +// it will not consider the `main` function as the program entry point but instead +// will generate its own `main` function to wrap everything as it needs to reside in a +// module where only *items* are permitted syntactically. +// +// See <./main-alongside-macro-calls.rs> for comparison. +// +// This is a regression test for: +// * <https://github.com/rust-lang/rust/issues/140162> +// * <https://github.com/rust-lang/rust/issues/139651> +// +//@ compile-flags:--test --test-args --test-threads=1 +//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR" +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass + +//! ``` +//! # if cfg!(miri) { return; } +//! use std::ops::Deref; +//! +//! fn main() { +//! assert!(false); +//! } +//! ``` +//! +//! ``` +//! let x = 2; +//! assert_eq!(x, 2); +//! +//! fn main() { +//! assert!(false); +//! } +//! ``` diff --git a/tests/rustdoc-ui/doctest/main-alongside-stmts.stdout b/tests/rustdoc-ui/doctest/main-alongside-stmts.stdout new file mode 100644 index 00000000000..9b9a3fe8a68 --- /dev/null +++ b/tests/rustdoc-ui/doctest/main-alongside-stmts.stdout @@ -0,0 +1,7 @@ + +running 2 tests +test $DIR/main-alongside-stmts.rs - (line 17) ... ok +test $DIR/main-alongside-stmts.rs - (line 26) ... ok + +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/doctest/test-main-alongside-exprs.rs b/tests/rustdoc-ui/doctest/test-main-alongside-exprs.rs deleted file mode 100644 index ee2299c0fd8..00000000000 --- a/tests/rustdoc-ui/doctest/test-main-alongside-exprs.rs +++ /dev/null @@ -1,22 +0,0 @@ -// This test ensures that if there is an expression alongside a `main` -// function, it will not consider the entire code to be part of the `main` -// function and will generate its own function to wrap everything. -// -// This is a regression test for: -// * <https://github.com/rust-lang/rust/issues/140162> -// * <https://github.com/rust-lang/rust/issues/139651> -//@ compile-flags:--test -//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR" -//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" -//@ check-pass - -#![crate_name = "foo"] - -//! ``` -//! # if cfg!(miri) { return; } -//! use std::ops::Deref; -//! -//! fn main() { -//! println!("Hi!"); -//! } -//! ``` diff --git a/tests/rustdoc-ui/doctest/test-main-alongside-exprs.stdout b/tests/rustdoc-ui/doctest/test-main-alongside-exprs.stdout deleted file mode 100644 index 90d7c3546bf..00000000000 --- a/tests/rustdoc-ui/doctest/test-main-alongside-exprs.stdout +++ /dev/null @@ -1,6 +0,0 @@ - -running 1 test -test $DIR/test-main-alongside-exprs.rs - (line 15) ... ok - -test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME - diff --git a/tests/ui-fulldeps/stable-mir/check_attribute.rs b/tests/ui-fulldeps/stable-mir/check_attribute.rs index 81d5399d88a..e4cc7b104b6 100644 --- a/tests/ui-fulldeps/stable-mir/check_attribute.rs +++ b/tests/ui-fulldeps/stable-mir/check_attribute.rs @@ -35,12 +35,12 @@ fn test_stable_mir() -> ControlFlow<()> { fn test_tool(items: &CrateItems) { let rustfmt_fn = *get_item(&items, "do_not_format").unwrap(); let rustfmt_attrs = rustfmt_fn.tool_attrs(&["rustfmt".to_string(), "skip".to_string()]); - assert_eq!(rustfmt_attrs[0].as_str(), "#[rustfmt::skip]"); + assert_eq!(rustfmt_attrs[0].as_str(), "#[rustfmt::skip]\n"); let clippy_fn = *get_item(&items, "complex_fn").unwrap(); let clippy_attrs = clippy_fn.tool_attrs(&["clippy".to_string(), "cyclomatic_complexity".to_string()]); - assert_eq!(clippy_attrs[0].as_str(), "#[clippy::cyclomatic_complexity = \"100\"]"); + assert_eq!(clippy_attrs[0].as_str(), "#[clippy::cyclomatic_complexity = \"100\"]\n"); } fn get_item<'a>( diff --git a/tests/ui/abi/compatibility.rs b/tests/ui/abi/compatibility.rs index be649029c86..68706f1e821 100644 --- a/tests/ui/abi/compatibility.rs +++ b/tests/ui/abi/compatibility.rs @@ -40,7 +40,6 @@ //@ revisions: loongarch64 //@[loongarch64] compile-flags: --target loongarch64-unknown-linux-gnu //@[loongarch64] needs-llvm-components: loongarch -//@[loongarch64] min-llvm-version: 20 //FIXME: wasm is disabled due to <https://github.com/rust-lang/rust/issues/115666>. //FIXME @ revisions: wasm //FIXME @[wasm] compile-flags: --target wasm32-unknown-unknown diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr index c88f3af7642..0e544119650 100644 --- a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr +++ b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr @@ -1,35 +1,35 @@ error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:23:18 + --> $DIR/bad-reg.rs:22:18 | LL | asm!("", out("$r0") _); | ^^^^^^^^^^^^ error: invalid register `$tp`: reserved for TLS - --> $DIR/bad-reg.rs:25:18 + --> $DIR/bad-reg.rs:24:18 | LL | asm!("", out("$tp") _); | ^^^^^^^^^^^^ error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:27:18 + --> $DIR/bad-reg.rs:26:18 | LL | asm!("", out("$sp") _); | ^^^^^^^^^^^^ error: invalid register `$r21`: reserved by the ABI - --> $DIR/bad-reg.rs:29:18 + --> $DIR/bad-reg.rs:28:18 | LL | asm!("", out("$r21") _); | ^^^^^^^^^^^^^ error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:31:18 + --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("$fp") _); | ^^^^^^^^^^^^ error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:33:18 + --> $DIR/bad-reg.rs:32:18 | LL | asm!("", out("$r31") _); | ^^^^^^^^^^^^^ diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr index cb8e55a9722..6d0410dc6a1 100644 --- a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr +++ b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr @@ -1,59 +1,59 @@ error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:23:18 + --> $DIR/bad-reg.rs:22:18 | LL | asm!("", out("$r0") _); | ^^^^^^^^^^^^ error: invalid register `$tp`: reserved for TLS - --> $DIR/bad-reg.rs:25:18 + --> $DIR/bad-reg.rs:24:18 | LL | asm!("", out("$tp") _); | ^^^^^^^^^^^^ error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:27:18 + --> $DIR/bad-reg.rs:26:18 | LL | asm!("", out("$sp") _); | ^^^^^^^^^^^^ error: invalid register `$r21`: reserved by the ABI - --> $DIR/bad-reg.rs:29:18 + --> $DIR/bad-reg.rs:28:18 | LL | asm!("", out("$r21") _); | ^^^^^^^^^^^^^ error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:31:18 + --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("$fp") _); | ^^^^^^^^^^^^ error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:33:18 + --> $DIR/bad-reg.rs:32:18 | LL | asm!("", out("$r31") _); | ^^^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:37:26 + --> $DIR/bad-reg.rs:36:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:39:26 + --> $DIR/bad-reg.rs:38:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:41:26 + --> $DIR/bad-reg.rs:40:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:43:26 + --> $DIR/bad-reg.rs:42:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ diff --git a/tests/ui/asm/loongarch/bad-reg.rs b/tests/ui/asm/loongarch/bad-reg.rs index db1c778e5a2..685b460bc92 100644 --- a/tests/ui/asm/loongarch/bad-reg.rs +++ b/tests/ui/asm/loongarch/bad-reg.rs @@ -1,7 +1,6 @@ //@ add-core-stubs //@ needs-asm-support //@ revisions: loongarch64_lp64d loongarch64_lp64s -//@ min-llvm-version: 20 //@[loongarch64_lp64d] compile-flags: --target loongarch64-unknown-linux-gnu //@[loongarch64_lp64d] needs-llvm-components: loongarch //@[loongarch64_lp64s] compile-flags: --target loongarch64-unknown-none-softfloat diff --git a/tests/ui/asm/naked-functions.rs b/tests/ui/asm/naked-functions.rs index 1eeb716e98a..cb5fde9a80b 100644 --- a/tests/ui/asm/naked-functions.rs +++ b/tests/ui/asm/naked-functions.rs @@ -2,7 +2,7 @@ //@ ignore-nvptx64 //@ ignore-spirv -#![feature(asm_unwind, linkage)] +#![feature(asm_unwind, linkage, rustc_attrs)] #![crate_type = "lib"] use std::arch::{asm, naked_asm}; @@ -225,3 +225,9 @@ pub extern "C" fn compatible_doc_attributes() { pub extern "C" fn compatible_linkage() { naked_asm!("", options(raw)); } + +#[rustc_std_internal_symbol] +#[unsafe(naked)] +pub extern "C" fn rustc_std_internal_symbol() { + naked_asm!("", options(raw)); +} diff --git a/tests/ui/associated-types/mismatch-two-relevant-impls.rs b/tests/ui/associated-types/mismatch-two-relevant-impls.rs new file mode 100644 index 00000000000..58fd567c278 --- /dev/null +++ b/tests/ui/associated-types/mismatch-two-relevant-impls.rs @@ -0,0 +1,20 @@ +trait Tr { + type Assoc; +} + +struct W<T>(T); + +impl Tr for W<i32> { + type Assoc = u32; +} + +impl Tr for W<u32> { + type Assoc = i32; +} + +fn needs_unit<T: Tr<Assoc = ()>>() {} + +fn main() { + needs_unit::<W<i32>>(); + //~^ ERROR type mismatch resolving `<W<i32> as Tr>::Assoc == ()` +} diff --git a/tests/ui/associated-types/mismatch-two-relevant-impls.stderr b/tests/ui/associated-types/mismatch-two-relevant-impls.stderr new file mode 100644 index 00000000000..2a1f3ef23ca --- /dev/null +++ b/tests/ui/associated-types/mismatch-two-relevant-impls.stderr @@ -0,0 +1,20 @@ +error[E0271]: type mismatch resolving `<W<i32> as Tr>::Assoc == ()` + --> $DIR/mismatch-two-relevant-impls.rs:18:18 + | +LL | needs_unit::<W<i32>>(); + | ^^^^^^ type mismatch resolving `<W<i32> as Tr>::Assoc == ()` + | +note: expected this to be `()` + --> $DIR/mismatch-two-relevant-impls.rs:8:18 + | +LL | type Assoc = u32; + | ^^^ +note: required by a bound in `needs_unit` + --> $DIR/mismatch-two-relevant-impls.rs:15:21 + | +LL | fn needs_unit<T: Tr<Assoc = ()>>() {} + | ^^^^^^^^^^ required by this bound in `needs_unit` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/check-cfg/target_feature.stderr b/tests/ui/check-cfg/target_feature.stderr index 4f7b8345e86..712ce941c54 100644 --- a/tests/ui/check-cfg/target_feature.stderr +++ b/tests/ui/check-cfg/target_feature.stderr @@ -324,12 +324,20 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `zcmop` `zdinx` `zfa` +`zfbfmin` `zfh` `zfhmin` `zfinx` `zhinx` `zhinxmin` +`zic64b` +`zicbom` +`zicbop` `zicboz` +`ziccamoa` +`ziccif` +`zicclsm` +`ziccrse` `zicntr` `zicond` `zicsr` @@ -356,6 +364,8 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `zve64d` `zve64f` `zve64x` +`zvfbfmin` +`zvfbfwma` `zvfh` `zvfhmin` `zvkb` diff --git a/tests/ui/const-ptr/forbidden_slices.stderr b/tests/ui/const-ptr/forbidden_slices.stderr index 6f2da272ad3..c73d2ca938c 100644 --- a/tests/ui/const-ptr/forbidden_slices.stderr +++ b/tests/ui/const-ptr/forbidden_slices.stderr @@ -114,7 +114,7 @@ error[E0080]: could not evaluate static initializer --> $DIR/forbidden_slices.rs:54:25 | LL | from_ptr_range(ptr..ptr.add(2)) // errors inside libcore - | ^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to 8 bytes of memory, but got ALLOC10 which is only 4 bytes from the end of the allocation + | ^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by 8 bytes, but got ALLOC10 which is only 4 bytes from the end of the allocation | note: inside `std::ptr::const_ptr::<impl *const u32>::add` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -169,7 +169,7 @@ error[E0080]: could not evaluate static initializer --> $DIR/forbidden_slices.rs:79:25 | LL | from_ptr_range(ptr..ptr.add(1)) - | ^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to 8 bytes of memory, but got ALLOC11+0x1 which is only 7 bytes from the end of the allocation + | ^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by 8 bytes, but got ALLOC11+0x1 which is only 7 bytes from the end of the allocation | note: inside `std::ptr::const_ptr::<impl *const u64>::add` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL diff --git a/tests/ui/const-ptr/out_of_bounds_read.stderr b/tests/ui/const-ptr/out_of_bounds_read.stderr index b396fc4d71b..1d625a26b78 100644 --- a/tests/ui/const-ptr/out_of_bounds_read.stderr +++ b/tests/ui/const-ptr/out_of_bounds_read.stderr @@ -2,7 +2,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/out_of_bounds_read.rs:8:33 | LL | const _READ: u32 = unsafe { ptr::read(PAST_END_PTR) }; - | ^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got ALLOC0+0x4 which is at or beyond the end of the allocation of size 4 bytes + | ^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 4 bytes, but got ALLOC0+0x4 which is at or beyond the end of the allocation of size 4 bytes | note: inside `std::ptr::read::<u32>` --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL @@ -11,7 +11,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/out_of_bounds_read.rs:10:39 | LL | const _CONST_READ: u32 = unsafe { PAST_END_PTR.read() }; - | ^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got ALLOC0+0x4 which is at or beyond the end of the allocation of size 4 bytes + | ^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 4 bytes, but got ALLOC0+0x4 which is at or beyond the end of the allocation of size 4 bytes | note: inside `std::ptr::const_ptr::<impl *const u32>::read` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -22,7 +22,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/out_of_bounds_read.rs:12:37 | LL | const _MUT_READ: u32 = unsafe { (PAST_END_PTR as *mut u32).read() }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got ALLOC0+0x4 which is at or beyond the end of the allocation of size 4 bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 4 bytes, but got ALLOC0+0x4 which is at or beyond the end of the allocation of size 4 bytes | note: inside `std::ptr::mut_ptr::<impl *mut u32>::read` --> $SRC_DIR/core/src/ptr/mut_ptr.rs:LL:COL diff --git a/tests/ui/consts/const-compare-bytes-ub.stderr b/tests/ui/consts/const-compare-bytes-ub.stderr index 9ef5c8ad43a..0e77310c6ba 100644 --- a/tests/ui/consts/const-compare-bytes-ub.stderr +++ b/tests/ui/consts/const-compare-bytes-ub.stderr @@ -2,31 +2,31 @@ error[E0080]: evaluation of constant value failed --> $DIR/const-compare-bytes-ub.rs:9:9 | LL | compare_bytes(0 as *const u8, 2 as *const u8, 1) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 1 byte of memory, but got a null pointer + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 1 byte, but got null pointer error[E0080]: evaluation of constant value failed --> $DIR/const-compare-bytes-ub.rs:13:9 | LL | compare_bytes(1 as *const u8, 0 as *const u8, 1) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 1 byte of memory, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 1 byte, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) error[E0080]: evaluation of constant value failed --> $DIR/const-compare-bytes-ub.rs:17:9 | LL | compare_bytes(1 as *const u8, 2 as *const u8, 1) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 1 byte of memory, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 1 byte, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) error[E0080]: evaluation of constant value failed --> $DIR/const-compare-bytes-ub.rs:21:9 | LL | compare_bytes([1, 2, 3].as_ptr(), [1, 2, 3, 4].as_ptr(), 4) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got ALLOC0 which is only 3 bytes from the end of the allocation + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 4 bytes, but got ALLOC0 which is only 3 bytes from the end of the allocation error[E0080]: evaluation of constant value failed --> $DIR/const-compare-bytes-ub.rs:25:9 | LL | compare_bytes([1, 2, 3, 4].as_ptr(), [1, 2, 3].as_ptr(), 4) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got ALLOC1 which is only 3 bytes from the end of the allocation + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 4 bytes, but got ALLOC1 which is only 3 bytes from the end of the allocation error[E0080]: evaluation of constant value failed --> $DIR/const-compare-bytes-ub.rs:29:9 diff --git a/tests/ui/consts/const-deref-ptr.stderr b/tests/ui/consts/const-deref-ptr.stderr index 070685e0b9d..37502864947 100644 --- a/tests/ui/consts/const-deref-ptr.stderr +++ b/tests/ui/consts/const-deref-ptr.stderr @@ -2,7 +2,7 @@ error[E0080]: could not evaluate static initializer --> $DIR/const-deref-ptr.rs:4:29 | LL | static C: u64 = unsafe {*(0xdeadbeef as *const u64)}; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 8 bytes of memory, but got 0xdeadbeef[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 8 bytes, but got 0xdeadbeef[noalloc] which is a dangling pointer (it has no provenance) error: aborting due to 1 previous error diff --git a/tests/ui/consts/const-eval/const_raw_ptr_ops2.rs b/tests/ui/consts/const-eval/const_raw_ptr_ops2.rs index ca96cfd9d19..0e88aa80c79 100644 --- a/tests/ui/consts/const-eval/const_raw_ptr_ops2.rs +++ b/tests/ui/consts/const-eval/const_raw_ptr_ops2.rs @@ -5,6 +5,6 @@ const Z: i32 = unsafe { *(&1 as *const i32) }; // bad, will thus error in miri const Z2: i32 = unsafe { *(42 as *const i32) }; //~ ERROR evaluation of constant value failed -//~| NOTE is a dangling pointer +//~| NOTE dangling pointer const Z3: i32 = unsafe { *(44 as *const i32) }; //~ ERROR evaluation of constant value failed -//~| NOTE is a dangling pointer +//~| NOTE dangling pointer diff --git a/tests/ui/consts/const-eval/const_raw_ptr_ops2.stderr b/tests/ui/consts/const-eval/const_raw_ptr_ops2.stderr index b0c864652e5..a8a5560ccb9 100644 --- a/tests/ui/consts/const-eval/const_raw_ptr_ops2.stderr +++ b/tests/ui/consts/const-eval/const_raw_ptr_ops2.stderr @@ -2,13 +2,13 @@ error[E0080]: evaluation of constant value failed --> $DIR/const_raw_ptr_ops2.rs:7:26 | LL | const Z2: i32 = unsafe { *(42 as *const i32) }; - | ^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got 0x2a[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 4 bytes, but got 0x2a[noalloc] which is a dangling pointer (it has no provenance) error[E0080]: evaluation of constant value failed --> $DIR/const_raw_ptr_ops2.rs:9:26 | LL | const Z3: i32 = unsafe { *(44 as *const i32) }; - | ^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got 0x2c[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 4 bytes, but got 0x2c[noalloc] which is a dangling pointer (it has no provenance) error: aborting due to 2 previous errors diff --git a/tests/ui/consts/const-eval/format.stderr b/tests/ui/consts/const-eval/format.stderr index 4c4cbb372a7..2f202705b7f 100644 --- a/tests/ui/consts/const-eval/format.stderr +++ b/tests/ui/consts/const-eval/format.stderr @@ -1,16 +1,16 @@ error[E0015]: cannot call non-const formatting macro in constant functions - --> $DIR/format.rs:2:13 + --> $DIR/format.rs:2:5 | LL | panic!("{:?}", 0); - | ^^^^ + | ^^^^^^^^^^^^^^^^^ | = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants error[E0015]: cannot call non-const formatting macro in constant functions - --> $DIR/format.rs:7:15 + --> $DIR/format.rs:7:5 | LL | println!("{:?}", 0); - | ^^^^ + | ^^^^^^^^^^^^^^^^^^^ | = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/consts/const-eval/nonnull_as_ref_ub.stderr b/tests/ui/consts/const-eval/nonnull_as_ref_ub.stderr index bd6dafb9366..1996cd2721e 100644 --- a/tests/ui/consts/const-eval/nonnull_as_ref_ub.stderr +++ b/tests/ui/consts/const-eval/nonnull_as_ref_ub.stderr @@ -2,7 +2,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/nonnull_as_ref_ub.rs:4:29 | LL | const _: () = assert!(42 == *unsafe { NON_NULL.as_ref() }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 1 byte of memory, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 1 byte, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) error: aborting due to 1 previous error diff --git a/tests/ui/consts/const-eval/raw-pointer-ub.rs b/tests/ui/consts/const-eval/raw-pointer-ub.rs index 478e93a910e..13a95f9b78f 100644 --- a/tests/ui/consts/const-eval/raw-pointer-ub.rs +++ b/tests/ui/consts/const-eval/raw-pointer-ub.rs @@ -39,7 +39,7 @@ const OOB: () = unsafe { let mem = [0u32; 1]; let ptr = mem.as_ptr().cast::<u64>(); let _val = *ptr; //~ERROR: evaluation of constant value failed - //~^NOTE: expected a pointer to 8 bytes of memory + //~^NOTE: is only 4 bytes from the end of the allocation }; fn main() {} diff --git a/tests/ui/consts/const-eval/raw-pointer-ub.stderr b/tests/ui/consts/const-eval/raw-pointer-ub.stderr index 4fff293b2ee..ed5793c84c5 100644 --- a/tests/ui/consts/const-eval/raw-pointer-ub.stderr +++ b/tests/ui/consts/const-eval/raw-pointer-ub.stderr @@ -31,7 +31,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/raw-pointer-ub.rs:41:16 | LL | let _val = *ptr; - | ^^^^ memory access failed: expected a pointer to 8 bytes of memory, but got ALLOC0 which is only 4 bytes from the end of the allocation + | ^^^^ memory access failed: attempting to access 8 bytes, but got ALLOC0 which is only 4 bytes from the end of the allocation error: aborting due to 5 previous errors diff --git a/tests/ui/consts/const-eval/ub-nonnull.stderr b/tests/ui/consts/const-eval/ub-nonnull.stderr index c2cafbf60bc..75dd0443ca4 100644 --- a/tests/ui/consts/const-eval/ub-nonnull.stderr +++ b/tests/ui/consts/const-eval/ub-nonnull.stderr @@ -13,7 +13,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/ub-nonnull.rs:22:29 | LL | let out_of_bounds_ptr = &ptr[255]; - | ^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to 255 bytes of memory, but got ALLOC1 which is only 1 byte from the end of the allocation + | ^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by 255 bytes, but got ALLOC1 which is only 1 byte from the end of the allocation error[E0080]: it is undefined behavior to use this value --> $DIR/ub-nonnull.rs:26:1 diff --git a/tests/ui/consts/copy-intrinsic.rs b/tests/ui/consts/copy-intrinsic.rs index 5ae46787800..da483d671f9 100644 --- a/tests/ui/consts/copy-intrinsic.rs +++ b/tests/ui/consts/copy-intrinsic.rs @@ -32,7 +32,7 @@ const COPY_OOB_1: () = unsafe { copy_nonoverlapping(0x100 as *const i32, dangle, 0); // Non-zero-sized copy is not. copy_nonoverlapping(0x100 as *const i32, dangle, 1); //~ ERROR evaluation of constant value failed [E0080] - //~| NOTE got 0x100[noalloc] which is a dangling pointer + //~| NOTE which is a dangling pointer }; const COPY_OOB_2: () = unsafe { let x = 0i32; @@ -41,7 +41,7 @@ const COPY_OOB_2: () = unsafe { copy_nonoverlapping(dangle, 0x100 as *mut i32, 0); // Non-zero-sized copy is not. copy_nonoverlapping(dangle, 0x100 as *mut i32, 1); //~ ERROR evaluation of constant value failed [E0080] - //~| NOTE +0x28 which is at or beyond the end of the allocation + //~| NOTE is at or beyond the end of the allocation of size 4 bytes }; const COPY_SIZE_OVERFLOW: () = unsafe { diff --git a/tests/ui/consts/copy-intrinsic.stderr b/tests/ui/consts/copy-intrinsic.stderr index 41af3a2cd2d..13321b5703a 100644 --- a/tests/ui/consts/copy-intrinsic.stderr +++ b/tests/ui/consts/copy-intrinsic.stderr @@ -2,13 +2,13 @@ error[E0080]: evaluation of constant value failed --> $DIR/copy-intrinsic.rs:34:5 | LL | copy_nonoverlapping(0x100 as *const i32, dangle, 1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got 0x100[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 4 bytes, but got 0x100[noalloc] which is a dangling pointer (it has no provenance) error[E0080]: evaluation of constant value failed --> $DIR/copy-intrinsic.rs:43:5 | LL | copy_nonoverlapping(dangle, 0x100 as *mut i32, 1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got ALLOC0+0x28 which is at or beyond the end of the allocation of size 4 bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 4 bytes, but got ALLOC0+0x28 which is at or beyond the end of the allocation of size 4 bytes error[E0080]: evaluation of constant value failed --> $DIR/copy-intrinsic.rs:50:5 diff --git a/tests/ui/consts/offset_ub.stderr b/tests/ui/consts/offset_ub.stderr index a247ad25465..699b63dfd66 100644 --- a/tests/ui/consts/offset_ub.stderr +++ b/tests/ui/consts/offset_ub.stderr @@ -2,7 +2,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/offset_ub.rs:8:46 | LL | pub const BEFORE_START: *const u8 = unsafe { (&0u8 as *const u8).offset(-1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to the end of 1 byte of memory, but got ALLOC0 which is at the beginning of the allocation + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by -$BYTES bytes, but got ALLOC0 which is at the beginning of the allocation | note: inside `std::ptr::const_ptr::<impl *const u8>::offset` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -11,7 +11,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/offset_ub.rs:9:43 | LL | pub const AFTER_END: *const u8 = unsafe { (&0u8 as *const u8).offset(2) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to $BYTES bytes of memory, but got ALLOC1 which is only 1 byte from the end of the allocation + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by $BYTES bytes, but got ALLOC1 which is only 1 byte from the end of the allocation | note: inside `std::ptr::const_ptr::<impl *const u8>::offset` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -20,7 +20,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/offset_ub.rs:10:45 | LL | pub const AFTER_ARRAY: *const u8 = unsafe { [0u8; 100].as_ptr().offset(101) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to $BYTES bytes of memory, but got ALLOC2 which is only $BYTES bytes from the end of the allocation + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by $BYTES bytes, but got ALLOC2 which is only $BYTES bytes from the end of the allocation | note: inside `std::ptr::const_ptr::<impl *const u8>::offset` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -47,7 +47,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/offset_ub.rs:14:56 | LL | pub const OVERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (usize::MAX as *const u8).offset(2) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to $BYTES bytes of memory, but got 0xf..f[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by $BYTES bytes, but got 0xf..f[noalloc] which is a dangling pointer (it has no provenance) | note: inside `std::ptr::const_ptr::<impl *const u8>::offset` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -56,7 +56,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/offset_ub.rs:15:57 | LL | pub const UNDERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (1 as *const u8).offset(-2) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to the end of $BYTES bytes of memory, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by -$BYTES bytes, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) | note: inside `std::ptr::const_ptr::<impl *const u8>::offset` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -65,7 +65,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/offset_ub.rs:16:49 | LL | pub const NEGATIVE_OFFSET: *const u8 = unsafe { [0u8; 1].as_ptr().wrapping_offset(-2).offset(-2) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to the end of $BYTES bytes of memory, but got ALLOC3-0x2 which points to before the beginning of the allocation + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by -$BYTES bytes, but got ALLOC3-0x2 which is only $BYTES bytes from the beginning of the allocation | note: inside `std::ptr::const_ptr::<impl *const u8>::offset` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -74,7 +74,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/offset_ub.rs:18:50 | LL | pub const ZERO_SIZED_ALLOC: *const u8 = unsafe { [0u8; 0].as_ptr().offset(1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to 1 byte of memory, but got ALLOC4 which is at or beyond the end of the allocation of size $BYTES bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by 1 byte, but got ALLOC4 which is at or beyond the end of the allocation of size $BYTES bytes | note: inside `std::ptr::const_ptr::<impl *const u8>::offset` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -83,7 +83,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/offset_ub.rs:19:42 | LL | pub const DANGLING: *const u8 = unsafe { ptr::NonNull::<u8>::dangling().as_ptr().offset(4) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to $BYTES bytes of memory, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by $BYTES bytes, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) | note: inside `std::ptr::mut_ptr::<impl *mut u8>::offset` --> $SRC_DIR/core/src/ptr/mut_ptr.rs:LL:COL @@ -92,7 +92,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/offset_ub.rs:22:47 | LL | pub const UNDERFLOW_ABS: *const u8 = unsafe { (usize::MAX as *const u8).offset(isize::MIN) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to the end of $BYTES bytes of memory, but got 0xf..f[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by -$BYTES bytes, but got 0xf..f[noalloc] which is a dangling pointer (it has no provenance) | note: inside `std::ptr::const_ptr::<impl *const u8>::offset` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL diff --git a/tests/ui/error-codes/E0253.rs b/tests/ui/error-codes/E0253.rs deleted file mode 100644 index 8284f791c64..00000000000 --- a/tests/ui/error-codes/E0253.rs +++ /dev/null @@ -1,10 +0,0 @@ -mod foo { - pub trait MyTrait { - type SomeType; - } -} - -use foo::MyTrait::SomeType; - //~^ ERROR E0253 - -fn main() {} diff --git a/tests/ui/error-codes/E0253.stderr b/tests/ui/error-codes/E0253.stderr deleted file mode 100644 index 954dbc81693..00000000000 --- a/tests/ui/error-codes/E0253.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0253]: `SomeType` is not directly importable - --> $DIR/E0253.rs:7:5 - | -LL | use foo::MyTrait::SomeType; - | ^^^^^^^^^^^^^^^^^^^^^^ cannot be imported directly - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0253`. diff --git a/tests/ui/feature-gates/feature-gate-import-trait-associated-functions.rs b/tests/ui/feature-gates/feature-gate-import-trait-associated-functions.rs index aec13fb0202..c2c010eaba6 100644 --- a/tests/ui/feature-gates/feature-gate-import-trait-associated-functions.rs +++ b/tests/ui/feature-gates/feature-gate-import-trait-associated-functions.rs @@ -60,4 +60,7 @@ fn f() { let t: Option<S> = DEFAULT; } +trait Glob {} +use Glob::*; //~ ERROR `use` associated items of traits is unstable + fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-import-trait-associated-functions.stderr b/tests/ui/feature-gates/feature-gate-import-trait-associated-functions.stderr index d342f5bd551..fca3cef3e20 100644 --- a/tests/ui/feature-gates/feature-gate-import-trait-associated-functions.stderr +++ b/tests/ui/feature-gates/feature-gate-import-trait-associated-functions.stderr @@ -48,6 +48,16 @@ LL | use super::A::{self, DEFAULT, new}; = help: add `#![feature(import_trait_associated_functions)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 5 previous errors +error[E0658]: `use` associated items of traits is unstable + --> $DIR/feature-gate-import-trait-associated-functions.rs:64:5 + | +LL | use Glob::*; + | ^^^^^^^ + | + = note: see issue #134691 <https://github.com/rust-lang/rust/issues/134691> for more information + = help: add `#![feature(import_trait_associated_functions)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/generic-const-items/user_type_annotations_pattern.rs b/tests/ui/generic-const-items/user_type_annotations_pattern.rs new file mode 100644 index 00000000000..aa3846df2bc --- /dev/null +++ b/tests/ui/generic-const-items/user_type_annotations_pattern.rs @@ -0,0 +1,14 @@ +#![feature(generic_const_items)] +#![expect(incomplete_features)] + +const FOO<'a: 'static>: usize = 10; + +fn bar<'a>() { + match 10_usize { + FOO::<'a> => todo!(), + //~^ ERROR: lifetime may not live long enough + _ => todo!(), + } +} + +fn main() {} diff --git a/tests/ui/generic-const-items/user_type_annotations_pattern.stderr b/tests/ui/generic-const-items/user_type_annotations_pattern.stderr new file mode 100644 index 00000000000..e15be275d29 --- /dev/null +++ b/tests/ui/generic-const-items/user_type_annotations_pattern.stderr @@ -0,0 +1,11 @@ +error: lifetime may not live long enough + --> $DIR/user_type_annotations_pattern.rs:8:9 + | +LL | fn bar<'a>() { + | -- lifetime `'a` defined here +LL | match 10_usize { +LL | FOO::<'a> => todo!(), + | ^^^^^^^^^ requires that `'a` must outlive `'static` + +error: aborting due to 1 previous error + diff --git a/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.rs b/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.rs new file mode 100644 index 00000000000..339277fec37 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.rs @@ -0,0 +1,12 @@ +// Regression test for ICE from issue #140545 +// The error message is confusing and wrong, but that's a different problem (#139350) +//@ edition:2018 + +trait Foo {} +fn a(x: impl Foo) -> impl Foo { + if true { x } else { a(a(x)) } + //~^ ERROR: expected generic type parameter, found `impl Foo` [E0792] + //~| ERROR: type parameter `impl Foo` is part of concrete type but not used in parameter list for the `impl Trait` type alias +} + +fn main(){} diff --git a/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.stderr b/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.stderr new file mode 100644 index 00000000000..1b02811e31b --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.stderr @@ -0,0 +1,17 @@ +error[E0792]: expected generic type parameter, found `impl Foo` + --> $DIR/double-wrap-with-defining-use.rs:7:26 + | +LL | fn a(x: impl Foo) -> impl Foo { + | -------- this generic parameter must be used with a generic type parameter +LL | if true { x } else { a(a(x)) } + | ^^^^^^^ + +error: type parameter `impl Foo` is part of concrete type but not used in parameter list for the `impl Trait` type alias + --> $DIR/double-wrap-with-defining-use.rs:7:26 + | +LL | if true { x } else { a(a(x)) } + | ^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/imports/issue-30560.rs b/tests/ui/imports/issue-30560.rs index d8d4ca608f1..fe5d17c1873 100644 --- a/tests/ui/imports/issue-30560.rs +++ b/tests/ui/imports/issue-30560.rs @@ -3,7 +3,4 @@ use Alias::*; //~ ERROR unresolved import `Alias` [E0432] use std::io::Result::*; //~ ERROR unresolved import `std::io::Result` [E0432] -trait T {} -use T::*; //~ ERROR items in traits are not importable - fn main() {} diff --git a/tests/ui/imports/issue-30560.stderr b/tests/ui/imports/issue-30560.stderr index 69cfd4c06a8..89492261cba 100644 --- a/tests/ui/imports/issue-30560.stderr +++ b/tests/ui/imports/issue-30560.stderr @@ -1,9 +1,3 @@ -error: items in traits are not importable - --> $DIR/issue-30560.rs:7:5 - | -LL | use T::*; - | ^^^^ - error[E0432]: unresolved import `Alias` --> $DIR/issue-30560.rs:2:5 | @@ -16,6 +10,6 @@ error[E0432]: unresolved import `std::io::Result` LL | use std::io::Result::*; | ^^^^^^ `Result` is a type alias, not a module -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0432`. diff --git a/tests/ui/linking/weird-export-names.rs b/tests/ui/linking/weird-export-names.rs new file mode 100644 index 00000000000..8fb2dc6bf5e --- /dev/null +++ b/tests/ui/linking/weird-export-names.rs @@ -0,0 +1,10 @@ +//@ build-pass +//@ needs-crate-type: cdylib + +#![crate_type = "cdylib"] + +#[export_name = "foo.0123"] +pub extern "C" fn foo() {} + +#[export_name = "EXPORTS"] +pub extern "C" fn bar() {} diff --git a/tests/ui/lint/unused-parens-for-macro-call-with-brace.fixed b/tests/ui/lint/unused-parens-for-macro-call-with-brace.fixed new file mode 100644 index 00000000000..4c9995fcb61 --- /dev/null +++ b/tests/ui/lint/unused-parens-for-macro-call-with-brace.fixed @@ -0,0 +1,28 @@ +//@ run-rustfix + +#![deny(unused_parens)] + +fn main() { + macro_rules! x { + () => { None::<i32> }; + } + + let Some(_) = (x!{}) else { return }; // no error + let Some(_) = (x!{}) else { return }; + //~^ ERROR: unnecessary parentheses around assigned value + + let Some(_) = (x!{}) else { return }; + //~^ ERROR: unnecessary parentheses around pattern + + let _ = x!{}; + let _ = x!{}; + //~^ ERROR: unnecessary parentheses around assigned value + + if let Some(_) = x!{} {}; + if let Some(_) = x!{} {}; + //~^ ERROR: unnecessary parentheses around `let` scrutinee expression + + while let Some(_) = x!{} {}; + while let Some(_) = x!{} {}; + //~^ ERROR: unnecessary parentheses around `let` scrutinee expression +} diff --git a/tests/ui/lint/unused-parens-for-macro-call-with-brace.rs b/tests/ui/lint/unused-parens-for-macro-call-with-brace.rs new file mode 100644 index 00000000000..59e215a48cc --- /dev/null +++ b/tests/ui/lint/unused-parens-for-macro-call-with-brace.rs @@ -0,0 +1,28 @@ +//@ run-rustfix + +#![deny(unused_parens)] + +fn main() { + macro_rules! x { + () => { None::<i32> }; + } + + let Some(_) = (x!{}) else { return }; // no error + let Some(_) = ((x!{})) else { return }; + //~^ ERROR: unnecessary parentheses around assigned value + + let Some((_)) = (x!{}) else { return }; + //~^ ERROR: unnecessary parentheses around pattern + + let _ = x!{}; + let _ = (x!{}); + //~^ ERROR: unnecessary parentheses around assigned value + + if let Some(_) = x!{} {}; + if let Some(_) = (x!{}) {}; + //~^ ERROR: unnecessary parentheses around `let` scrutinee expression + + while let Some(_) = x!{} {}; + while let Some(_) = (x!{}) {}; + //~^ ERROR: unnecessary parentheses around `let` scrutinee expression +} diff --git a/tests/ui/lint/unused-parens-for-macro-call-with-brace.stderr b/tests/ui/lint/unused-parens-for-macro-call-with-brace.stderr new file mode 100644 index 00000000000..8d3b4fe493e --- /dev/null +++ b/tests/ui/lint/unused-parens-for-macro-call-with-brace.stderr @@ -0,0 +1,67 @@ +error: unnecessary parentheses around assigned value + --> $DIR/unused-parens-for-macro-call-with-brace.rs:11:19 + | +LL | let Some(_) = ((x!{})) else { return }; + | ^ ^ + | +note: the lint level is defined here + --> $DIR/unused-parens-for-macro-call-with-brace.rs:3:9 + | +LL | #![deny(unused_parens)] + | ^^^^^^^^^^^^^ +help: remove these parentheses + | +LL - let Some(_) = ((x!{})) else { return }; +LL + let Some(_) = (x!{}) else { return }; + | + +error: unnecessary parentheses around pattern + --> $DIR/unused-parens-for-macro-call-with-brace.rs:14:14 + | +LL | let Some((_)) = (x!{}) else { return }; + | ^ ^ + | +help: remove these parentheses + | +LL - let Some((_)) = (x!{}) else { return }; +LL + let Some(_) = (x!{}) else { return }; + | + +error: unnecessary parentheses around assigned value + --> $DIR/unused-parens-for-macro-call-with-brace.rs:18:13 + | +LL | let _ = (x!{}); + | ^ ^ + | +help: remove these parentheses + | +LL - let _ = (x!{}); +LL + let _ = x!{}; + | + +error: unnecessary parentheses around `let` scrutinee expression + --> $DIR/unused-parens-for-macro-call-with-brace.rs:22:22 + | +LL | if let Some(_) = (x!{}) {}; + | ^ ^ + | +help: remove these parentheses + | +LL - if let Some(_) = (x!{}) {}; +LL + if let Some(_) = x!{} {}; + | + +error: unnecessary parentheses around `let` scrutinee expression + --> $DIR/unused-parens-for-macro-call-with-brace.rs:26:25 + | +LL | while let Some(_) = (x!{}) {}; + | ^ ^ + | +help: remove these parentheses + | +LL - while let Some(_) = (x!{}) {}; +LL + while let Some(_) = x!{} {}; + | + +error: aborting due to 5 previous errors + diff --git a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.rs b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.rs index 443a7e3835e..f5c3da847c7 100644 --- a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.rs +++ b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.rs @@ -49,6 +49,17 @@ mod alone_in_path { //~| ERROR missing lifetime specifier } +mod alone_in_path2 { + trait Foo<'a> { fn next(&mut self) -> Option<&'a ()>; } + + fn f(_: impl Foo<>) {} + //~^ ERROR anonymous lifetimes in `impl Trait` are unstable + + fn g(mut x: impl Foo<>) -> Option<&()> { x.next() } + //~^ ERROR anonymous lifetimes in `impl Trait` are unstable + //~| ERROR missing lifetime specifier +} + mod in_path { trait Foo<'a, T> { fn next(&mut self) -> Option<&'a T>; } diff --git a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr index 24013c85c87..92996ca8467 100644 --- a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr +++ b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr @@ -108,7 +108,28 @@ LL + fn g(mut x: impl Foo) -> Option<()> { x.next() } | error[E0106]: missing lifetime specifier - --> $DIR/impl-trait-missing-lifetime-gated.rs:58:41 + --> $DIR/impl-trait-missing-lifetime-gated.rs:58:39 + | +LL | fn g(mut x: impl Foo<>) -> Option<&()> { x.next() } + | ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` + | +LL | fn g(mut x: impl Foo<>) -> Option<&'static ()> { x.next() } + | +++++++ +help: consider introducing a named lifetime parameter + | +LL | fn g<'a>(mut x: impl Foo<>) -> Option<&'a ()> { x.next() } + | ++++ ++ +help: alternatively, you might want to return an owned value + | +LL - fn g(mut x: impl Foo<>) -> Option<&()> { x.next() } +LL + fn g(mut x: impl Foo<>) -> Option<()> { x.next() } + | + +error[E0106]: missing lifetime specifier + --> $DIR/impl-trait-missing-lifetime-gated.rs:69:41 | LL | fn g(mut x: impl Foo<()>) -> Option<&()> { x.next() } | ^ expected named lifetime parameter @@ -129,7 +150,7 @@ LL + fn g(mut x: impl Foo<()>) -> Option<()> { x.next() } | warning: elided lifetime has a name - --> $DIR/impl-trait-missing-lifetime-gated.rs:64:57 + --> $DIR/impl-trait-missing-lifetime-gated.rs:75:57 | LL | fn resolved_anonymous<'a, T: 'a>(f: impl Fn(&'a str) -> &T) { | -- lifetime `'a` declared here ^ this elided lifetime gets resolved as `'a` @@ -219,6 +240,32 @@ LL | fn g<'a>(mut x: impl Foo<'a>) -> Option<&()> { x.next() } error[E0658]: anonymous lifetimes in `impl Trait` are unstable --> $DIR/impl-trait-missing-lifetime-gated.rs:55:22 | +LL | fn f(_: impl Foo<>) {} + | ^ expected named lifetime parameter + | + = help: add `#![feature(anonymous_lifetime_in_impl_trait)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +help: consider introducing a named lifetime parameter + | +LL | fn f<'a>(_: impl Foo<'a>) {} + | ++++ ++ + +error[E0658]: anonymous lifetimes in `impl Trait` are unstable + --> $DIR/impl-trait-missing-lifetime-gated.rs:58:26 + | +LL | fn g(mut x: impl Foo<>) -> Option<&()> { x.next() } + | ^ expected named lifetime parameter + | + = help: add `#![feature(anonymous_lifetime_in_impl_trait)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +help: consider introducing a named lifetime parameter + | +LL | fn g<'a>(mut x: impl Foo<'a>) -> Option<&()> { x.next() } + | ++++ ++ + +error[E0658]: anonymous lifetimes in `impl Trait` are unstable + --> $DIR/impl-trait-missing-lifetime-gated.rs:66:22 + | LL | fn f(_: impl Foo<()>) {} | ^ expected named lifetime parameter | @@ -230,7 +277,7 @@ LL | fn f<'a>(_: impl Foo<'a, ()>) {} | ++++ +++ error[E0658]: anonymous lifetimes in `impl Trait` are unstable - --> $DIR/impl-trait-missing-lifetime-gated.rs:58:26 + --> $DIR/impl-trait-missing-lifetime-gated.rs:69:26 | LL | fn g(mut x: impl Foo<()>) -> Option<&()> { x.next() } | ^ expected named lifetime parameter @@ -242,7 +289,7 @@ help: consider introducing a named lifetime parameter LL | fn g<'a>(mut x: impl Foo<'a, ()>) -> Option<&()> { x.next() } | ++++ +++ -error: aborting due to 14 previous errors; 1 warning emitted +error: aborting due to 17 previous errors; 1 warning emitted Some errors have detailed explanations: E0106, E0658. For more information about an error, try `rustc --explain E0106`. diff --git a/tests/ui/target-feature/abi-incompatible-target-feature-attribute.riscv.stderr b/tests/ui/target-feature/abi-incompatible-target-feature-attribute.riscv.stderr new file mode 100644 index 00000000000..49c5479275f --- /dev/null +++ b/tests/ui/target-feature/abi-incompatible-target-feature-attribute.riscv.stderr @@ -0,0 +1,8 @@ +error: target feature `d` cannot be enabled with `#[target_feature]`: this feature is incompatible with the target ABI + --> $DIR/abi-incompatible-target-feature-attribute.rs:15:90 + | +LL | #[cfg_attr(x86, target_feature(enable = "soft-float"))] #[cfg_attr(riscv, target_feature(enable = "d"))] + | ^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/target-feature/abi-incompatible-target-feature-attribute.rs b/tests/ui/target-feature/abi-incompatible-target-feature-attribute.rs new file mode 100644 index 00000000000..a8733440759 --- /dev/null +++ b/tests/ui/target-feature/abi-incompatible-target-feature-attribute.rs @@ -0,0 +1,17 @@ +//! Ensure ABI-incompatible features cannot be enabled via `#[target_feature]`. +// ignore-tidy-linelength +//@ compile-flags: --crate-type=lib +//@ revisions: x86 riscv +//@[x86] compile-flags: --target=x86_64-unknown-linux-gnu +//@[x86] needs-llvm-components: x86 +//@[riscv] compile-flags: --target=riscv32e-unknown-none-elf +//@[riscv] needs-llvm-components: riscv +#![feature(no_core, lang_items, riscv_target_feature, x87_target_feature)] +#![no_core] + +#[lang = "sized"] +pub trait Sized {} + +#[cfg_attr(x86, target_feature(enable = "soft-float"))] #[cfg_attr(riscv, target_feature(enable = "d"))] +//~^ERROR: cannot be enabled with +pub unsafe fn my_fun() {} diff --git a/tests/ui/target-feature/abi-incompatible-target-feature-attribute.x86.stderr b/tests/ui/target-feature/abi-incompatible-target-feature-attribute.x86.stderr new file mode 100644 index 00000000000..81471fd7e30 --- /dev/null +++ b/tests/ui/target-feature/abi-incompatible-target-feature-attribute.x86.stderr @@ -0,0 +1,8 @@ +error: target feature `soft-float` cannot be enabled with `#[target_feature]`: this feature is incompatible with the target ABI + --> $DIR/abi-incompatible-target-feature-attribute.rs:15:32 + | +LL | #[cfg_attr(x86, target_feature(enable = "soft-float"))] #[cfg_attr(riscv, target_feature(enable = "d"))] + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.riscv.stderr b/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.riscv.stderr new file mode 100644 index 00000000000..2dca0c22033 --- /dev/null +++ b/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.riscv.stderr @@ -0,0 +1,19 @@ +warning: target feature `d` must be disabled to ensure that the ABI of the current target can be implemented correctly + | + = note: 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 #116344 <https://github.com/rust-lang/rust/issues/116344> + +warning: unstable feature specified for `-Ctarget-feature`: `d` + | + = note: this feature is not stably supported; its behavior can change in the future + +warning: unstable feature specified for `-Ctarget-feature`: `f` + | + = note: this feature is not stably supported; its behavior can change in the future + +warning: unstable feature specified for `-Ctarget-feature`: `zicsr` + | + = note: this feature is not stably supported; its behavior can change in the future + +warning: 4 warnings emitted + diff --git a/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.rs b/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.rs new file mode 100644 index 00000000000..68e1d3b9ddc --- /dev/null +++ b/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.rs @@ -0,0 +1,22 @@ +//! Ensure ABI-incompatible features cannot be enabled via `-Ctarget-feature`. +// These are just warnings for now. +//@ check-pass +//@ compile-flags: --crate-type=lib +//@ revisions: x86 riscv +//@[x86] compile-flags: --target=x86_64-unknown-linux-gnu -Ctarget-feature=+soft-float +//@[x86] needs-llvm-components: x86 +//@[riscv] compile-flags: --target=riscv32e-unknown-none-elf -Ctarget-feature=+d +//@[riscv] needs-llvm-components: riscv + +#![feature(no_core, lang_items, riscv_target_feature)] +#![no_core] + +#[lang = "sized"] +pub trait Sized {} +#[lang = "freeze"] +pub trait Freeze {} + +//~? WARN must be disabled to ensure that the ABI of the current target can be implemented correctly +//~? WARN unstable feature specified for `-Ctarget-feature` +//[riscv]~? WARN unstable feature specified for `-Ctarget-feature` +//[riscv]~? WARN unstable feature specified for `-Ctarget-feature` diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag.stderr b/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.x86.stderr index e49672f33b9..e49672f33b9 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag.stderr +++ b/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.x86.stderr diff --git a/tests/ui/target-feature/allowed-softfloat-target-feature-flag-disable.rs b/tests/ui/target-feature/abi-irrelevant-target-feature-flag-disable.rs index 7368ef120fa..0013d033b9c 100644 --- a/tests/ui/target-feature/allowed-softfloat-target-feature-flag-disable.rs +++ b/tests/ui/target-feature/abi-irrelevant-target-feature-flag-disable.rs @@ -1,3 +1,6 @@ +//! `x87` is a required target feature on some x86 targets, but not on this one as this one +//! uses soft-floats. So ensure disabling the target feature here (which is a NOP) does +//! not trigger a warning. //@ compile-flags: --target=x86_64-unknown-none --crate-type=lib //@ needs-llvm-components: x86 //@ compile-flags: -Ctarget-feature=-x87 diff --git a/tests/ui/target-feature/allowed-softfloat-target-feature-flag-disable.stderr b/tests/ui/target-feature/abi-irrelevant-target-feature-flag-disable.stderr index 309b64afd92..309b64afd92 100644 --- a/tests/ui/target-feature/allowed-softfloat-target-feature-flag-disable.stderr +++ b/tests/ui/target-feature/abi-irrelevant-target-feature-flag-disable.stderr diff --git a/tests/ui/target-feature/allowed-softfloat-target-feature-attribute.rs b/tests/ui/target-feature/abi-required-target-feature-attribute.rs index 8b60820cc9b..95723c57f94 100644 --- a/tests/ui/target-feature/allowed-softfloat-target-feature-attribute.rs +++ b/tests/ui/target-feature/abi-required-target-feature-attribute.rs @@ -1,3 +1,5 @@ +//! Enabling a target feature that is anyway required changes nothing, so this is allowed +//! for `#[target_feature]`. //@ compile-flags: --target=x86_64-unknown-none --crate-type=lib //@ needs-llvm-components: x86 //@ build-pass diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.stderr b/tests/ui/target-feature/abi-required-target-feature-flag-disable.aarch64.stderr index b1186d5d5dc..b1186d5d5dc 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.stderr +++ b/tests/ui/target-feature/abi-required-target-feature-flag-disable.aarch64.stderr diff --git a/tests/ui/target-feature/abi-required-target-feature-flag-disable.loongarch.stderr b/tests/ui/target-feature/abi-required-target-feature-flag-disable.loongarch.stderr new file mode 100644 index 00000000000..35102e0571f --- /dev/null +++ b/tests/ui/target-feature/abi-required-target-feature-flag-disable.loongarch.stderr @@ -0,0 +1,11 @@ +warning: target feature `d` must be enabled to ensure that the ABI of the current target can be implemented correctly + | + = note: 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 #116344 <https://github.com/rust-lang/rust/issues/116344> + +warning: unstable feature specified for `-Ctarget-feature`: `d` + | + = note: this feature is not stably supported; its behavior can change in the future + +warning: 2 warnings emitted + diff --git a/tests/ui/target-feature/abi-required-target-feature-flag-disable.riscv.stderr b/tests/ui/target-feature/abi-required-target-feature-flag-disable.riscv.stderr new file mode 100644 index 00000000000..35102e0571f --- /dev/null +++ b/tests/ui/target-feature/abi-required-target-feature-flag-disable.riscv.stderr @@ -0,0 +1,11 @@ +warning: target feature `d` must be enabled to ensure that the ABI of the current target can be implemented correctly + | + = note: 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 #116344 <https://github.com/rust-lang/rust/issues/116344> + +warning: unstable feature specified for `-Ctarget-feature`: `d` + | + = note: this feature is not stably supported; its behavior can change in the future + +warning: 2 warnings emitted + diff --git a/tests/ui/target-feature/abi-required-target-feature-flag-disable.rs b/tests/ui/target-feature/abi-required-target-feature-flag-disable.rs new file mode 100644 index 00000000000..c3ce05baa64 --- /dev/null +++ b/tests/ui/target-feature/abi-required-target-feature-flag-disable.rs @@ -0,0 +1,27 @@ +//! Ensure ABI-required features cannot be disabled via `-Ctarget-feature`. +//! Also covers the case of a feature indirectly disabling another via feature implications. +//@ compile-flags: --crate-type=lib +//@ revisions: x86 x86-implied aarch64 riscv loongarch +//@[x86] compile-flags: --target=x86_64-unknown-linux-gnu -Ctarget-feature=-x87 +//@[x86] needs-llvm-components: x86 +//@[x86-implied] compile-flags: --target=x86_64-unknown-linux-gnu -Ctarget-feature=-sse +//@[x86-implied] needs-llvm-components: x86 +//@[aarch64] compile-flags: --target=aarch64-unknown-linux-gnu -Ctarget-feature=-neon +//@[aarch64] needs-llvm-components: aarch64 +//@[riscv] compile-flags: --target=riscv64gc-unknown-none-elf -Ctarget-feature=-d +//@[riscv] needs-llvm-components: riscv +//@[loongarch] compile-flags: --target=loongarch64-unknown-none -Ctarget-feature=-d +//@[loongarch] needs-llvm-components: loongarch +// For now this is just a warning. +//@ build-pass +// Remove some LLVM warnings that only show up sometimes. +//@ normalize-stderr: "\n[^\n]*(target-abi|lp64f)[^\n]*" -> "" + +#![feature(no_core, lang_items)] +#![no_core] + +#[lang = "sized"] +pub trait Sized {} + +//~? WARN must be enabled to ensure that the ABI of the current target can be implemented correctly +//[x86,riscv,loongarch]~? WARN unstable feature specified for `-Ctarget-feature` diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.stderr b/tests/ui/target-feature/abi-required-target-feature-flag-disable.x86-implied.stderr index 7ec8b04cfce..7ec8b04cfce 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.stderr +++ b/tests/ui/target-feature/abi-required-target-feature-flag-disable.x86-implied.stderr diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.stderr b/tests/ui/target-feature/abi-required-target-feature-flag-disable.x86.stderr index 02398d27501..02398d27501 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.stderr +++ b/tests/ui/target-feature/abi-required-target-feature-flag-disable.x86.stderr diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.rs b/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute-e-d.rs index 215e64979f7..215e64979f7 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.rs +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute-e-d.rs diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.stderr b/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute-e-d.stderr index bfe767e5ffb..84d27463b38 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.stderr +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute-e-d.stderr @@ -1,5 +1,5 @@ error: target feature `d` cannot be enabled with `#[target_feature]`: this feature is incompatible with the target ABI - --> $DIR/forbidden-hardfloat-target-feature-attribute.rs:10:18 + --> $DIR/forbidden-hardfloat-target-feature-attribute-e-d.rs:10:18 | LL | #[target_feature(enable = "d")] | ^^^^^^^^^^^^ diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute-f-zfinx.rs b/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute-f-zfinx.rs new file mode 100644 index 00000000000..d74f4a1d4b1 --- /dev/null +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute-f-zfinx.rs @@ -0,0 +1,12 @@ +//! Ensure ABI-incompatible features cannot be enabled via `#[target_feature]`. +//@ compile-flags: --target=riscv64gc-unknown-linux-gnu --crate-type=lib +//@ needs-llvm-components: riscv +#![feature(no_core, lang_items, riscv_target_feature)] +#![no_core] + +#[lang = "sized"] +pub trait Sized {} + +#[target_feature(enable = "zdinx")] +//~^ERROR: cannot be enabled with +pub unsafe fn my_fun() {} diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute-f-zfinx.stderr b/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute-f-zfinx.stderr new file mode 100644 index 00000000000..af0e53f34f2 --- /dev/null +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute-f-zfinx.stderr @@ -0,0 +1,8 @@ +error: target feature `zfinx` cannot be enabled with `#[target_feature]`: this feature is incompatible with the target ABI + --> $DIR/forbidden-hardfloat-target-feature-attribute-f-zfinx.rs:10:18 + | +LL | #[target_feature(enable = "zdinx")] + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.rs b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.rs deleted file mode 100644 index 12e7e3bc45b..00000000000 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.rs +++ /dev/null @@ -1,15 +0,0 @@ -//! Ensure that if disabling a target feature implies disabling an ABI-required target feature, -//! we complain. -//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib -//@ needs-llvm-components: x86 -//@ compile-flags: -Ctarget-feature=-sse -// For now this is just a warning. -//@ build-pass - -#![feature(no_core, lang_items)] -#![no_core] - -#[lang = "sized"] -pub trait Sized {} - -//~? WARN target feature `sse2` must be enabled to ensure that the ABI of the current target can be implemented correctly diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.rs b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.rs deleted file mode 100644 index 33e4f12694f..00000000000 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ compile-flags: --target=aarch64-unknown-linux-gnu --crate-type=lib -//@ needs-llvm-components: aarch64 -//@ compile-flags: -Ctarget-feature=-neon -// For now this is just a warning. -//@ build-pass - -#![feature(no_core, lang_items)] -#![no_core] - -#[lang = "sized"] -pub trait Sized {} - -//~? WARN target feature `neon` must be enabled to ensure that the ABI of the current target can be implemented correctly diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.rs b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.rs deleted file mode 100644 index e1bd25ffad1..00000000000 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.rs +++ /dev/null @@ -1,15 +0,0 @@ -//! Ensure ABI-required features cannot be disabled via `-Ctarget-feature`. -//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib -//@ needs-llvm-components: x86 -//@ compile-flags: -Ctarget-feature=-x87 -// For now this is just a warning. -//@ build-pass - -#![feature(no_core, lang_items)] -#![no_core] - -#[lang = "sized"] -pub trait Sized {} - -//~? WARN target feature `x87` must be enabled to ensure that the ABI of the current target can be implemented correctly -//~? WARN unstable feature specified for `-Ctarget-feature`: `x87` diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag.rs b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag.rs deleted file mode 100644 index 4ccc6e0e941..00000000000 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag.rs +++ /dev/null @@ -1,15 +0,0 @@ -//! Ensure ABI-incompatible features cannot be enabled via `-Ctarget-feature`. -//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib -//@ needs-llvm-components: x86 -//@ compile-flags: -Ctarget-feature=+soft-float -// For now this is just a warning. -//@ build-pass - -#![feature(no_core, lang_items, riscv_target_feature)] -#![no_core] - -#[lang = "sized"] -pub trait Sized {} - -//~? WARN target feature `soft-float` must be disabled to ensure that the ABI of the current target can be implemented correctl -//~? WARN unstable feature specified for `-Ctarget-feature`: `soft-float` diff --git a/tests/ui/traits/trait-impl-self-mismatch.rs b/tests/ui/traits/trait-impl-self-mismatch.rs new file mode 100644 index 00000000000..54226cf2c9a --- /dev/null +++ b/tests/ui/traits/trait-impl-self-mismatch.rs @@ -0,0 +1,19 @@ +//@compile-flags: -Zvalidate-mir -Zinline-mir -Zinline-mir-threshold=300 + +//! Ensure that a trait method implemented with the wrong signature +//! correctly triggers a compile error and not an ICE. +//! Regression test for <https://github.com/rust-lang/rust/issues/133065>. + +trait Bar { + fn bar(&self) {} +} + +impl<T> Bar for T { + fn bar() { //~ ERROR method `bar` has a `&self` declaration in the trait, but not in the impl + let _ = "Hello".bytes().nth(3); + } +} + +fn main() { + ().bar(); +} diff --git a/tests/ui/traits/trait-impl-self-mismatch.stderr b/tests/ui/traits/trait-impl-self-mismatch.stderr new file mode 100644 index 00000000000..4ee06787c7d --- /dev/null +++ b/tests/ui/traits/trait-impl-self-mismatch.stderr @@ -0,0 +1,12 @@ +error[E0186]: method `bar` has a `&self` declaration in the trait, but not in the impl + --> $DIR/trait-impl-self-mismatch.rs:12:5 + | +LL | fn bar(&self) {} + | ------------- `&self` used in trait +... +LL | fn bar() { + | ^^^^^^^^ expected `&self` in impl + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0186`. diff --git a/tests/ui/unpretty/exhaustive-asm.expanded.stdout b/tests/ui/unpretty/exhaustive-asm.expanded.stdout new file mode 100644 index 00000000000..92829b0ab15 --- /dev/null +++ b/tests/ui/unpretty/exhaustive-asm.expanded.stdout @@ -0,0 +1,33 @@ +#![feature(prelude_import)] +#[prelude_import] +use std::prelude::rust_2024::*; +#[macro_use] +extern crate std; +//@ revisions: expanded hir +//@[expanded]compile-flags: -Zunpretty=expanded +//@[expanded]check-pass +//@[hir]compile-flags: -Zunpretty=hir +//@[hir]check-pass +//@ edition:2024 +//@ only-x86_64 +// +// asm parts of exhaustive.rs. Separate because we only run this on x86_64. + +mod expressions { + /// ExprKind::InlineAsm + fn expr_inline_asm() { + let x; + asm!("mov {1}, {0}\nshl {1}, 1\nshl {0}, 2\nadd {0}, {1}", + inout(reg) + x, + out(reg) + _); + } +} + +mod items { + /// ItemKind::GlobalAsm + mod item_global_asm { + global_asm! (".globl my_asm_func"); + } +} diff --git a/tests/ui/unpretty/exhaustive-asm.hir.stdout b/tests/ui/unpretty/exhaustive-asm.hir.stdout new file mode 100644 index 00000000000..810db69bff1 --- /dev/null +++ b/tests/ui/unpretty/exhaustive-asm.hir.stdout @@ -0,0 +1,32 @@ +#[prelude_import] +use std::prelude::rust_2024::*; +#[macro_use] +extern crate std; +//@ revisions: expanded hir +//@[expanded]compile-flags: -Zunpretty=expanded +//@[expanded]check-pass +//@[hir]compile-flags: -Zunpretty=hir +//@[hir]check-pass +//@ edition:2024 +//@ only-x86_64 +// +// asm parts of exhaustive.rs. Separate because we only run this on x86_64. + +mod expressions { + /// ExprKind::InlineAsm + fn expr_inline_asm() { + let x; + asm!("mov {1}, {0}\nshl {1}, 1\nshl {0}, 2\nadd {0}, {1}", + inout(reg) + x, + out(reg) + _); + } +} + +mod items { + /// ItemKind::GlobalAsm + mod item_global_asm {/// ItemKind::GlobalAsm + global_asm! (".globl my_asm_func"); + } +} diff --git a/tests/ui/unpretty/exhaustive-asm.rs b/tests/ui/unpretty/exhaustive-asm.rs new file mode 100644 index 00000000000..74a45447a20 --- /dev/null +++ b/tests/ui/unpretty/exhaustive-asm.rs @@ -0,0 +1,31 @@ +//@ revisions: expanded hir +//@[expanded]compile-flags: -Zunpretty=expanded +//@[expanded]check-pass +//@[hir]compile-flags: -Zunpretty=hir +//@[hir]check-pass +//@ edition:2024 +//@ only-x86_64 +// +// asm parts of exhaustive.rs. Separate because we only run this on x86_64. + +mod expressions { + /// ExprKind::InlineAsm + fn expr_inline_asm() { + let x; + core::arch::asm!( + "mov {tmp}, {x}", + "shl {tmp}, 1", + "shl {x}, 2", + "add {x}, {tmp}", + x = inout(reg) x, + tmp = out(reg) _, + ); + } +} + +mod items { + /// ItemKind::GlobalAsm + mod item_global_asm { + core::arch::global_asm!(".globl my_asm_func"); + } +} diff --git a/tests/ui/unpretty/expanded-exhaustive.stdout b/tests/ui/unpretty/exhaustive.expanded.stdout index c6ffbb0d316..9712ba58e62 100644 --- a/tests/ui/unpretty/expanded-exhaustive.stdout +++ b/tests/ui/unpretty/exhaustive.expanded.stdout @@ -1,7 +1,13 @@ #![feature(prelude_import)] -//@ compile-flags: -Zunpretty=expanded +//@ revisions: expanded hir +//@[expanded]compile-flags: -Zunpretty=expanded +//@[expanded]check-pass +//@[hir]compile-flags: -Zunpretty=hir +//@[hir]check-fail //@ edition:2024 -//@ check-pass + +// Note: the HIR revision includes a `.stderr` file because there are some +// errors that only occur once we get past the AST. #![feature(auto_traits)] #![feature(box_patterns)] @@ -211,7 +217,10 @@ mod expressions { } /// ExprKind::Await - fn expr_await() { let fut; fut.await; } + fn expr_await() { + let fut; + fut.await; + } /// ExprKind::TryBlock fn expr_try_block() { try {} try { return; } } @@ -242,7 +251,9 @@ mod expressions { } /// ExprKind::Underscore - fn expr_underscore() { _; } + fn expr_underscore() { + _; + } /// ExprKind::Path fn expr_path() { @@ -275,16 +286,8 @@ mod expressions { /// ExprKind::Ret fn expr_ret() { return; return true; } - /// ExprKind::InlineAsm - fn expr_inline_asm() { - let x; - asm!("mov {1}, {0}\nshl {1}, 1\nshl {0}, 2\nadd {0}, {1}", - inout(reg) - x, - out(reg) - _); - } + /// ExprKind::InlineAsm: see exhaustive-asm.rs /// ExprKind::OffsetOf fn expr_offset_of() { @@ -300,65 +303,12 @@ mod expressions { - - - - - - // ... - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // concat_idents is deprecated @@ -450,10 +400,7 @@ mod items { unsafe extern "C++" {} unsafe extern "C" {} } - /// ItemKind::GlobalAsm - mod item_global_asm { - global_asm! (".globl my_asm_func"); - } + /// ItemKind::GlobalAsm: see exhaustive-asm.rs /// ItemKind::TyAlias mod item_ty_alias { pub type Type<'a> where T: 'a = T; diff --git a/tests/ui/unpretty/exhaustive.hir.stderr b/tests/ui/unpretty/exhaustive.hir.stderr new file mode 100644 index 00000000000..58f7ff0f598 --- /dev/null +++ b/tests/ui/unpretty/exhaustive.hir.stderr @@ -0,0 +1,172 @@ +error[E0697]: closures cannot be static + --> $DIR/exhaustive.rs:211:9 + | +LL | static || value; + | ^^^^^^^^^ + +error[E0697]: closures cannot be static + --> $DIR/exhaustive.rs:212:9 + | +LL | static move || value; + | ^^^^^^^^^^^^^^ + +error[E0728]: `await` is only allowed inside `async` functions and blocks + --> $DIR/exhaustive.rs:241:13 + | +LL | fn expr_await() { + | --------------- this is not `async` +LL | let fut; +LL | fut.await; + | ^^^^^ only allowed inside `async` functions and blocks + +error: in expressions, `_` can only be used on the left-hand side of an assignment + --> $DIR/exhaustive.rs:290:9 + | +LL | _; + | ^ `_` not allowed here + +error[E0214]: parenthesized type parameters may only be used with a `Fn` trait + --> $DIR/exhaustive.rs:300:9 + | +LL | x::(); + | ^^^^^ only `Fn` traits may use parentheses + +error[E0214]: parenthesized type parameters may only be used with a `Fn` trait + --> $DIR/exhaustive.rs:301:9 + | +LL | x::(T, T) -> T; + | ^^^^^^^^^^^^^^ only `Fn` traits may use parentheses + | +help: use angle brackets instead + | +LL - x::(T, T) -> T; +LL + x::<T, T> -> T; + | + +error[E0214]: parenthesized type parameters may only be used with a `Fn` trait + --> $DIR/exhaustive.rs:302:9 + | +LL | crate::() -> ()::expressions::() -> ()::expr_path; + | ^^^^^^^^^^^^^^^ only `Fn` traits may use parentheses + +error[E0214]: parenthesized type parameters may only be used with a `Fn` trait + --> $DIR/exhaustive.rs:302:26 + | +LL | crate::() -> ()::expressions::() -> ()::expr_path; + | ^^^^^^^^^^^^^^^^^^^^^ only `Fn` traits may use parentheses + +error[E0214]: parenthesized type parameters may only be used with a `Fn` trait + --> $DIR/exhaustive.rs:305:9 + | +LL | core::()::marker::()::PhantomData; + | ^^^^^^^^ only `Fn` traits may use parentheses + +error[E0214]: parenthesized type parameters may only be used with a `Fn` trait + --> $DIR/exhaustive.rs:305:19 + | +LL | core::()::marker::()::PhantomData; + | ^^^^^^^^^^ only `Fn` traits may use parentheses + +error: `yield` can only be used in `#[coroutine]` closures, or `gen` blocks + --> $DIR/exhaustive.rs:392:9 + | +LL | yield; + | ^^^^^ + | +help: use `#[coroutine]` to make this closure a coroutine + | +LL | #[coroutine] fn expr_yield() { + | ++++++++++++ + +error[E0703]: invalid ABI: found `C++` + --> $DIR/exhaustive.rs:472:23 + | +LL | unsafe extern "C++" {} + | ^^^^^ invalid ABI + | + = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions + +error: `..` patterns are not allowed here + --> $DIR/exhaustive.rs:679:13 + | +LL | let ..; + | ^^ + | + = note: only allowed in tuple, tuple struct, and slice patterns + +error[E0214]: parenthesized type parameters may only be used with a `Fn` trait + --> $DIR/exhaustive.rs:794:16 + | +LL | let _: T() -> !; + | ^^^^^^^^ only `Fn` traits may use parentheses + +error[E0562]: `impl Trait` is not allowed in the type of variable bindings + --> $DIR/exhaustive.rs:809:16 + | +LL | let _: impl Send; + | ^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + = help: add `#![feature(impl_trait_in_bindings)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0562]: `impl Trait` is not allowed in the type of variable bindings + --> $DIR/exhaustive.rs:810:16 + | +LL | let _: impl Send + 'static; + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + = help: add `#![feature(impl_trait_in_bindings)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0562]: `impl Trait` is not allowed in the type of variable bindings + --> $DIR/exhaustive.rs:811:16 + | +LL | let _: impl 'static + Send; + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + = help: add `#![feature(impl_trait_in_bindings)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0562]: `impl Trait` is not allowed in the type of variable bindings + --> $DIR/exhaustive.rs:812:16 + | +LL | let _: impl ?Sized; + | ^^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + = help: add `#![feature(impl_trait_in_bindings)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0562]: `impl Trait` is not allowed in the type of variable bindings + --> $DIR/exhaustive.rs:813:16 + | +LL | let _: impl ~const Clone; + | ^^^^^^^^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + = help: add `#![feature(impl_trait_in_bindings)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0562]: `impl Trait` is not allowed in the type of variable bindings + --> $DIR/exhaustive.rs:814:16 + | +LL | let _: impl for<'a> Send; + | ^^^^^^^^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + = help: add `#![feature(impl_trait_in_bindings)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 20 previous errors + +Some errors have detailed explanations: E0214, E0562, E0697, E0703, E0728. +For more information about an error, try `rustc --explain E0214`. diff --git a/tests/ui/unpretty/exhaustive.hir.stdout b/tests/ui/unpretty/exhaustive.hir.stdout new file mode 100644 index 00000000000..c20f123b16e --- /dev/null +++ b/tests/ui/unpretty/exhaustive.hir.stdout @@ -0,0 +1,724 @@ +//@ revisions: expanded hir +//@[expanded]compile-flags: -Zunpretty=expanded +//@[expanded]check-pass +//@[hir]compile-flags: -Zunpretty=hir +//@[hir]check-fail +//@ edition:2024 + +// Note: the HIR revision includes a `.stderr` file because there are some +// errors that only occur once we get past the AST. + +#![feature(auto_traits)] +#![feature(box_patterns)] +#![feature(builtin_syntax)] +#![feature(concat_idents)] +#![feature(const_trait_impl)] +#![feature(decl_macro)] +#![feature(deref_patterns)] +#![feature(dyn_star)] +#![feature(explicit_tail_calls)] +#![feature(gen_blocks)] +#![feature(more_qualified_paths)] +#![feature(never_patterns)] +#![feature(never_type)] +#![feature(pattern_types)] +#![feature(pattern_type_macro)] +#![feature(prelude_import)] +#![feature(specialization)] +#![feature(trace_macros)] +#![feature(trait_alias)] +#![feature(try_blocks)] +#![feature(yeet_expr)] +#![allow(incomplete_features)] +#[prelude_import] +use std::prelude::rust_2024::*; +#[macro_use] +extern crate std; + +#[prelude_import] +use self::prelude::*; + +mod prelude { + use std::prelude::rust_2024::*; + + type T = _; + + trait Trait { + const + CONST: + (); + } +} + +//! inner single-line doc comment +/*! + * inner multi-line doc comment + */ +#[doc = "inner doc attribute"] +#[allow(dead_code, unused_variables)] +#[no_std] +mod attributes {//! inner single-line doc comment + /*! + * inner multi-line doc comment + */ + #![doc = "inner doc attribute"] + #![allow(dead_code, unused_variables)] + #![no_std] + + /// outer single-line doc comment + /** + * outer multi-line doc comment + */ + #[doc = "outer doc attribute"] + #[doc = "macro"] + #[allow()] + #[attr = Repr([ReprC])] + struct Struct; +} + +mod expressions { + /// ExprKind::Array + fn expr_array() { + []; + [true]; + [true]; + [true, true]; + ["long........................................................................"]; + ["long............................................................", + true]; + } + + /// ExprKind::ConstBlock + fn expr_const_block() { + const { }; + const { 1 }; + const + { + struct S; + }; + } + + /// ExprKind::Call + fn expr_call() { + let f; + f(); + f::<u8>(); + f::<1>(); + f::<'static, u8, 1>(); + f(true); + f(true); + ()(); + } + + /// ExprKind::MethodCall + fn expr_method_call() { + let x; + x.f(); + x.f::<u8>(); + x.collect::<Vec<_>>(); + } + + /// ExprKind::Tup + fn expr_tup() { (); (true,); (true, false); (true, false); } + + /// ExprKind::Binary + fn expr_binary() { + let (a, b, c, d, x, y); + true || false; + true || false && false; + a < 1 && 2 < b && c > 3 && 4 > d; + a & b & !c; + a + b * c - d + -1 * -2 - -3; + x = !y; + } + + /// ExprKind::Unary + fn expr_unary() { let expr; *expr; !expr; -expr; } + + /// ExprKind::Lit + fn expr_lit() { 'x'; 1000i8; 1.00000000000000000000001; } + + /// ExprKind::Cast + fn expr_cast() { let expr; expr as T; expr as T<u8>; } + + /// ExprKind::Type + fn expr_type() { let expr; type_ascribe!(expr, T); } + + /// ExprKind::Let + fn expr_let() { + let b; + if let Some(a) = b { } + if let _ = true && false { } + if let _ = (true && false) { } + } + + /// ExprKind::If + fn expr_if() { + if true { } + if !true { } + if let true = true { } else { } + if true { } else if false { } + if true { } else if false { } else { } + if true { return; } else if false { 0 } else { 0 } + } + + /// ExprKind::While + fn expr_while() { + loop { if false { } else { break; } } + 'a: loop { if false { } else { break; } } + loop { if let true = true { } else { break; } } + } + + /// ExprKind::ForLoop + fn expr_for_loop() { + let x; + { + let _t = + match #[lang = "into_iter"](x) { + mut iter => + loop { + match #[lang = "next"](&mut iter) { + #[lang = "None"] {} => break, + #[lang = "Some"] { 0: _ } => { } + } + }, + }; + _t + }; + { + let _t = + match #[lang = "into_iter"](x) { + mut iter => + 'a: loop { + match #[lang = "next"](&mut iter) { + #[lang = "None"] {} => break, + #[lang = "Some"] { 0: _ } => { } + } + }, + }; + _t + } + } + + /// ExprKind::Loop + fn expr_loop() { loop { } 'a: loop { } } + + /// ExprKind::Match + fn expr_match() { + let value; + match value { } + match value { ok => 1, } + match value { ok => 1, err => 0, } + } + + /// ExprKind::Closure + fn expr_closure() { + let value; + || { }; + |x| { }; + |x: u8| { }; + || (); + move || value; + || |mut _task_context: ResumeTy| { { let _t = value; _t } }; + move || |mut _task_context: ResumeTy| { { let _t = value; _t } }; + || value; + move || value; + || |mut _task_context: ResumeTy| { { let _t = value; _t } }; + move || |mut _task_context: ResumeTy| { { let _t = value; _t } }; + || -> u8 { value }; + 1 + (|| { }); + } + + /// ExprKind::Block + fn expr_block() { + { } + unsafe { } + 'a: { } + #[allow()] + { } + #[allow()] + { } + } + + /// ExprKind::Gen + fn expr_gen() { + |mut _task_context: ResumeTy| { }; + move |mut _task_context: ResumeTy| { }; + || { }; + move || { }; + |mut _task_context: ResumeTy| { }; + move |mut _task_context: ResumeTy| { }; + } + + /// ExprKind::Await + fn expr_await() { + let fut; + { + fut; + (/*ERROR*/) + }; + } + + /// ExprKind::TryBlock + fn expr_try_block() { + { #[lang = "from_output"](()) } + { return; #[lang = "from_output"](()) } + } + + /// ExprKind::Assign + fn expr_assign() { let expr; expr = true; } + + /// ExprKind::AssignOp + fn expr_assign_op() { let expr; expr += true; } + + /// ExprKind::Field + fn expr_field() { let expr; expr.field; expr.0; } + + /// ExprKind::Index + fn expr_index() { let expr; expr[true]; } + + /// ExprKind::Range + fn expr_range() { + let (lo, hi); + #[lang = "RangeFull"] { }; + #[lang = "RangeTo"] { end: hi }; + #[lang = "RangeFrom"] { start: lo }; + #[lang = "Range"] { start: lo, end: hi }; + #[lang = "Range"] { start: lo, end: hi }; + #[lang = "RangeToInclusive"] { end: hi }; + #[lang = "range_inclusive_new"](lo, hi); + #[lang = "range_inclusive_new"](-2, -1); + } + + /// ExprKind::Underscore + fn expr_underscore() { + (/*ERROR*/); + } + + /// ExprKind::Path + fn expr_path() { + let x; + crate::expressions::expr_path; + crate::expressions::expr_path::<'static>; + <T as Default>::default; + <T as ::core::default::Default>::default; + x; + x::<T, T>; + crate::expressions::expr_path; + core::marker::PhantomData; + } + + /// ExprKind::AddrOf + fn expr_addr_of() { + let expr; + &expr; + &mut expr; + &raw const expr; + &raw mut expr; + } + + /// ExprKind::Break + fn expr_break() { 'a: { break; break 'a; break true; break 'a true; } } + + /// ExprKind::Continue + fn expr_continue() { 'a: { continue; continue 'a; } } + + /// ExprKind::Ret + fn expr_ret() { return; return true; } + + + /// ExprKind::InlineAsm: see exhaustive-asm.rs + /// ExprKind::OffsetOf + fn expr_offset_of() { + + + + + + + + + + + + + + // ... + + + + + + // concat_idents is deprecated + + + + + { offset_of!(T, field) }; + } + /// ExprKind::MacCall + fn expr_mac_call() { "..."; "..."; "..."; } + /// ExprKind::Struct + fn expr_struct() { + struct Struct { + } + let (x, base); + Struct { }; + <Struct as ToOwned>::Owned { }; + Struct { .. }; + Struct { ..base }; + Struct { x }; + Struct { x, ..base }; + Struct { x: true }; + Struct { x: true, .. }; + Struct { x: true, ..base }; + Struct { 0: true, ..base }; + } + /// ExprKind::Repeat + fn expr_repeat() { [(); 0]; } + /// ExprKind::Paren + fn expr_paren() { let expr; expr; } + /// ExprKind::Try + fn expr_try() { + let expr; + match #[lang = "branch"](expr) { + #[lang = "Break"] { 0: residual } => #[allow(unreachable_code)] + return #[lang = "from_residual"](residual), + #[lang = "Continue"] { 0: val } => #[allow(unreachable_code)] + val, + }; + } + /// ExprKind::Yield + fn expr_yield() { yield (); yield true; } + /// ExprKind::Yeet + fn expr_yeet() { + return #[lang = "from_yeet"](()); + return #[lang = "from_yeet"](0); + } + /// ExprKind::Become + fn expr_become() { become true; } + /// ExprKind::IncludedBytes + fn expr_include_bytes() { + b"data for include_bytes in ../expanded-exhaustive.rs\n"; + } + /// ExprKind::FormatArgs + fn expr_format_args() { + let expr; + format_arguments::new_const(&[]); + format_arguments::new_v1(&[""], + &[format_argument::new_display(&expr)]); + } +} +mod items { + /// ItemKind::ExternCrate + mod item_extern_crate {/// ItemKind::ExternCrate + extern crate core; + extern crate self as unpretty; + extern crate core as _; + } + /// ItemKind::Use + mod item_use {/// ItemKind::Use + use ::{}; + use crate::expressions; + use crate::items::item_use; + use core::*; + } + /// ItemKind::Static + mod item_static {/// ItemKind::Static + static A: () = { }; + static mut B: () = { }; + } + /// ItemKind::Const + mod item_const {/// ItemKind::Const + const A: () = { }; + trait TraitItems { + const + B: + (); + const + C: + () + = + { }; + } + } + /// ItemKind::Fn + mod item_fn {/// ItemKind::Fn + const unsafe extern "C" fn f() { } + async unsafe extern "C" fn g() + -> + /*impl Trait*/ |mut _task_context: ResumeTy| + { { let _t = { }; _t } } + fn h<'a, T>() where T: 'a { } + trait TraitItems { + unsafe extern "C" fn f(); + } + impl TraitItems for _ { + unsafe extern "C" fn f() { } + } + } + /// ItemKind::Mod + mod item_mod {/// ItemKind::Mod + } + /// ItemKind::ForeignMod + mod item_foreign_mod {/// ItemKind::ForeignMod + extern "Rust" { } + extern "C" { } + } + /// ItemKind::GlobalAsm: see exhaustive-asm.rs + /// ItemKind::TyAlias + mod item_ty_alias {/// ItemKind::GlobalAsm: see exhaustive-asm.rs + /// ItemKind::TyAlias + type Type<'a> where T: 'a = T; + } + /// ItemKind::Enum + mod item_enum {/// ItemKind::Enum + enum Void { } + enum Empty { + Unit, + Tuple(), + Struct { + }, + } + enum Generic<'a, T> where T: 'a { + Tuple(T), + Struct { + t: T, + }, + } + } + /// ItemKind::Struct + mod item_struct {/// ItemKind::Struct + struct Unit; + struct Tuple(); + struct Newtype(Unit); + struct Struct { + } + struct Generic<'a, T> where T: 'a { + t: T, + } + } + /// ItemKind::Union + mod item_union {/// ItemKind::Union + union Generic<'a, T> where T: 'a { + t: T, + } + } + /// ItemKind::Trait + mod item_trait {/// ItemKind::Trait + auto unsafe trait Send { } + trait Trait<'a>: Sized where Self: 'a { } + } + /// ItemKind::TraitAlias + mod item_trait_alias {/// ItemKind::TraitAlias + trait Trait<T> = Sized where for<'a> T: 'a; + } + /// ItemKind::Impl + mod item_impl {/// ItemKind::Impl + impl () { } + impl <T> () { } + impl Default for () { } + impl const <T> Default for () { } + } + /// ItemKind::MacCall + mod item_mac_call {/// ItemKind::MacCall + } + /// ItemKind::MacroDef + mod item_macro_def {/// ItemKind::MacroDef + macro_rules! mac { () => {...}; } + macro stringify { () => {} } + } + /// ItemKind::Delegation + /*! FIXME: todo */ + mod item_delegation {/// ItemKind::Delegation + /*! FIXME: todo */ + } + /// ItemKind::DelegationMac + /*! FIXME: todo */ + mod item_delegation_mac {/// ItemKind::DelegationMac + /*! FIXME: todo */ + } +} +mod patterns { + /// PatKind::Missing + fn pat_missing() { let _: for fn(u32, T, &'_ str); } + /// PatKind::Wild + fn pat_wild() { let _; } + /// PatKind::Ident + fn pat_ident() { + let x; + let ref x; + let mut x; + let ref mut x; + let ref mut x@_; + } + /// PatKind::Struct + fn pat_struct() { + let T {}; + let T::<T> {}; + let T::<'static> {}; + let T { x }; + let T { x: _x }; + let T { .. }; + let T { x, .. }; + let T { x: _x, .. }; + let T { 0: _x, .. }; + let <T as ToOwned>::Owned {}; + } + /// PatKind::TupleStruct + fn pat_tuple_struct() { + struct Tuple(); + let Tuple(); + let Tuple::<T>(); + let Tuple::<'static>(); + let Tuple(x); + let Tuple(..); + let Tuple(x, ..); + } + /// PatKind::Or + fn pat_or() { let true | false; let true; let true | false; } + /// PatKind::Path + fn pat_path() { + let core::marker::PhantomData; + let core::marker::PhantomData::<T>; + let core::marker::PhantomData::<'static>; + let <T as Trait>::CONST; + } + /// PatKind::Tuple + fn pat_tuple() { let (); let (true,); let (true, false); } + /// PatKind::Box + fn pat_box() { let box pat; } + /// PatKind::Deref + fn pat_deref() { let deref!(pat); } + /// PatKind::Ref + fn pat_ref() { let &pat; let &mut pat; } + /// PatKind::Expr + fn pat_expr() { let 1000i8; let -""; } + /// PatKind::Range + fn pat_range() { let ..1; let 0...; let 0..1; let 0...1; let -2...-1; } + /// PatKind::Slice + fn pat_slice() { let []; let [true]; let [true]; let [true, false]; } + /// PatKind::Rest + fn pat_rest() { let _; } + /// PatKind::Never + fn pat_never() { let !; let Some(!); } + /// PatKind::Paren + fn pat_paren() { let pat; } + /// PatKind::MacCall + fn pat_mac_call() { let ""; let ""; let ""; } +} +mod statements { + /// StmtKind::Let + fn stmt_let() { + let _; + let _ = true; + let _: T = true; + let _ = true else { return; }; + } + /// StmtKind::Item + fn stmt_item() { + struct Struct { + } + struct Unit; + } + /// StmtKind::Expr + fn stmt_expr() { () } + /// StmtKind::Semi + fn stmt_semi() { 1 + 1; } + /// StmtKind::Empty + fn stmt_empty() { } + /// StmtKind::MacCall + fn stmt_mac_call() { "..."; "..."; "..."; } +} +mod types { + /// TyKind::Slice + fn ty_slice() { let _: [T]; } + /// TyKind::Array + fn ty_array() { let _: [T; 0]; } + /// TyKind::Ptr + fn ty_ptr() { let _: *const T; let _: *mut T; } + /// TyKind::Ref + fn ty_ref() { + let _: &T; + let _: &mut T; + let _: &'static T; + let _: &'static mut [T]; + let _: &T<T<T<T<T>>>>; + let _: &T<T<T<T<T>>>>; + } + /// TyKind::BareFn + fn ty_bare_fn() { + let _: fn(); + let _: fn() -> (); + let _: fn(T); + let _: fn(t: T); + let _: fn(); + let _: for<'a> fn(); + } + /// TyKind::Never + fn ty_never() { let _: !; } + /// TyKind::Tup + fn ty_tup() { let _: (); let _: (T,); let _: (T, T); } + /// TyKind::Path + fn ty_path() { + let _: T; + let _: T<'static>; + let _: T<T>; + let _: T<T>; + let _: T; + let _: <T as ToOwned>::Owned; + } + /// TyKind::TraitObject + fn ty_trait_object() { + let _: dyn Send; + let _: dyn Send + 'static; + let _: dyn Send + 'static; + let _: dyn for<'a> Send; + let _: dyn* Send; + } + /// TyKind::ImplTrait + const fn ty_impl_trait() { + let _: (/*ERROR*/); + let _: (/*ERROR*/); + let _: (/*ERROR*/); + let _: (/*ERROR*/); + let _: (/*ERROR*/); + let _: (/*ERROR*/); + } + /// TyKind::Paren + fn ty_paren() { let _: T; } + /// TyKind::Typeof + /*! unused for now */ + fn ty_typeof() { } + /// TyKind::Infer + fn ty_infer() { let _: _; } + /// TyKind::ImplicitSelf + /*! there is no syntax for this */ + fn ty_implicit_self() { } + /// TyKind::MacCall + #[expect(deprecated)] + fn ty_mac_call() { let _: T; let _: T; let _: T; } + /// TyKind::CVarArgs + /*! FIXME: todo */ + fn ty_c_var_args() { } + /// TyKind::Pat + fn ty_pat() { let _: u32 is 1..=RangeMax; } +} +mod visibilities { + /// VisibilityKind::Public + mod visibility_public {/// VisibilityKind::Public + struct Pub; + } + /// VisibilityKind::Restricted + mod visibility_restricted {/// VisibilityKind::Restricted + struct PubCrate; + struct PubSelf; + struct PubSuper; + struct PubInCrate; + struct PubInSelf; + struct PubInSuper; + struct PubInCrateVisibilities; + struct PubInSelfSuper; + struct PubInSuperMod; + } +} diff --git a/tests/ui/unpretty/expanded-exhaustive.rs b/tests/ui/unpretty/exhaustive.rs index 5697f615b97..60ad3564689 100644 --- a/tests/ui/unpretty/expanded-exhaustive.rs +++ b/tests/ui/unpretty/exhaustive.rs @@ -1,6 +1,12 @@ -//@ compile-flags: -Zunpretty=expanded +//@ revisions: expanded hir +//@[expanded]compile-flags: -Zunpretty=expanded +//@[expanded]check-pass +//@[hir]compile-flags: -Zunpretty=hir +//@[hir]check-fail //@ edition:2024 -//@ check-pass + +// Note: the HIR revision includes a `.stderr` file because there are some +// errors that only occur once we get past the AST. #![feature(auto_traits)] #![feature(box_patterns)] @@ -202,8 +208,8 @@ mod expressions { move || value; async || value; async move || value; - static || value; - static move || value; + static || value; //[hir]~ ERROR closures cannot be static + static move || value; //[hir]~ ERROR closures cannot be static (static async || value); (static async move || value); || -> u8 { value }; @@ -232,7 +238,7 @@ mod expressions { /// ExprKind::Await fn expr_await() { let fut; - fut.await; + fut.await; //[hir]~ ERROR `await` is only allowed } /// ExprKind::TryBlock @@ -281,7 +287,7 @@ mod expressions { /// ExprKind::Underscore fn expr_underscore() { - _; + _; //[hir]~ ERROR in expressions, `_` can only } /// ExprKind::Path @@ -291,10 +297,14 @@ mod expressions { crate::expressions::expr_path::<'static>; <T as Default>::default; <T as ::core::default::Default>::default::<>; - x::(); - x::(T, T) -> T; + x::(); //[hir]~ ERROR parenthesized type parameters + x::(T, T) -> T; //[hir]~ ERROR parenthesized type parameters crate::() -> ()::expressions::() -> ()::expr_path; + //[hir]~^ ERROR parenthesized type parameters + //[hir]~| ERROR parenthesized type parameters core::()::marker::()::PhantomData; + //[hir]~^ ERROR parenthesized type parameters + //[hir]~| ERROR parenthesized type parameters } /// ExprKind::AddrOf @@ -330,18 +340,7 @@ mod expressions { return true; } - /// ExprKind::InlineAsm - fn expr_inline_asm() { - let x; - core::arch::asm!( - "mov {tmp}, {x}", - "shl {tmp}, 1", - "shl {x}, 2", - "add {x}, {tmp}", - x = inout(reg) x, - tmp = out(reg) _, - ); - } + /// ExprKind::InlineAsm: see exhaustive-asm.rs /// ExprKind::OffsetOf fn expr_offset_of() { @@ -390,7 +389,7 @@ mod expressions { /// ExprKind::Yield fn expr_yield() { - yield; + yield; //[hir]~ ERROR `yield` can only be used yield true; } @@ -470,14 +469,11 @@ mod items { /// ItemKind::ForeignMod mod item_foreign_mod { - unsafe extern "C++" {} + unsafe extern "C++" {} //[hir]~ ERROR invalid ABI unsafe extern "C" {} } - /// ItemKind::GlobalAsm - mod item_global_asm { - core::arch::global_asm!(".globl my_asm_func"); - } + /// ItemKind::GlobalAsm: see exhaustive-asm.rs /// ItemKind::TyAlias mod item_ty_alias { @@ -680,7 +676,7 @@ mod patterns { /// PatKind::Rest fn pat_rest() { - let ..; + let ..; //[hir]~ ERROR `..` patterns are not allowed here } /// PatKind::Never @@ -795,7 +791,7 @@ mod types { let _: T<'static>; let _: T<T>; let _: T::<T>; - let _: T() -> !; + let _: T() -> !; //[hir]~ ERROR parenthesized type parameters let _: <T as ToOwned>::Owned; } @@ -810,12 +806,12 @@ mod types { /// TyKind::ImplTrait const fn ty_impl_trait() { - let _: impl Send; - let _: impl Send + 'static; - let _: impl 'static + Send; - let _: impl ?Sized; - let _: impl ~const Clone; - let _: impl for<'a> Send; + let _: impl Send; //[hir]~ ERROR `impl Trait` is not allowed + let _: impl Send + 'static; //[hir]~ ERROR `impl Trait` is not allowed + let _: impl 'static + Send; //[hir]~ ERROR `impl Trait` is not allowed + let _: impl ?Sized; //[hir]~ ERROR `impl Trait` is not allowed + let _: impl ~const Clone; //[hir]~ ERROR `impl Trait` is not allowed + let _: impl for<'a> Send; //[hir]~ ERROR `impl Trait` is not allowed } /// TyKind::Paren diff --git a/tests/ui/unpretty/expanded-interpolation.rs b/tests/ui/unpretty/interpolation-expanded.rs index 95280f97dac..95280f97dac 100644 --- a/tests/ui/unpretty/expanded-interpolation.rs +++ b/tests/ui/unpretty/interpolation-expanded.rs diff --git a/tests/ui/unpretty/expanded-interpolation.stdout b/tests/ui/unpretty/interpolation-expanded.stdout index d46b46b67f4..d46b46b67f4 100644 --- a/tests/ui/unpretty/expanded-interpolation.stdout +++ b/tests/ui/unpretty/interpolation-expanded.stdout diff --git a/tests/ui/use/import_trait_associated_item_bad.rs b/tests/ui/use/import_trait_associated_item_bad.rs new file mode 100644 index 00000000000..d3d2a8e83cd --- /dev/null +++ b/tests/ui/use/import_trait_associated_item_bad.rs @@ -0,0 +1,18 @@ +#![feature(import_trait_associated_functions)] +#![feature(min_generic_const_args)] +#![allow(incomplete_features)] + +trait Trait { + type AssocTy; + const CONST: usize; +} + +use Trait::AssocTy; +type Alias1 = AssocTy; //~ ERROR ambiguous associated type +type Alias2 = self::AssocTy; //~ ERROR ambiguous associated type + +use Trait::CONST; +type Alias3 = [u8; CONST]; //~ ERROR ambiguous associated constant +type Alias4 = [u8; self::CONST]; //~ ERROR ambiguous associated constant + +fn main() {} diff --git a/tests/ui/use/import_trait_associated_item_bad.stderr b/tests/ui/use/import_trait_associated_item_bad.stderr new file mode 100644 index 00000000000..d5cd5d37bd7 --- /dev/null +++ b/tests/ui/use/import_trait_associated_item_bad.stderr @@ -0,0 +1,49 @@ +error[E0223]: ambiguous associated type + --> $DIR/import_trait_associated_item_bad.rs:11:15 + | +LL | type Alias1 = AssocTy; + | ^^^^^^^ + | +help: if there were a type named `Example` that implemented `Trait`, you could use the fully-qualified path + | +LL | type Alias1 = <Example as Trait>::AssocTy; + | ++++++++++++++++++++ + +error[E0223]: ambiguous associated type + --> $DIR/import_trait_associated_item_bad.rs:12:15 + | +LL | type Alias2 = self::AssocTy; + | ^^^^^^^^^^^^^ + | +help: if there were a type named `Example` that implemented `Trait`, you could use the fully-qualified path + | +LL - type Alias2 = self::AssocTy; +LL + type Alias2 = <Example as Trait>::AssocTy; + | + +error[E0223]: ambiguous associated constant + --> $DIR/import_trait_associated_item_bad.rs:15:20 + | +LL | type Alias3 = [u8; CONST]; + | ^^^^^ + | +help: if there were a type named `Example` that implemented `Trait`, you could use the fully-qualified path + | +LL | type Alias3 = [u8; <Example as Trait>::CONST]; + | ++++++++++++++++++++ + +error[E0223]: ambiguous associated constant + --> $DIR/import_trait_associated_item_bad.rs:16:20 + | +LL | type Alias4 = [u8; self::CONST]; + | ^^^^^^^^^^^ + | +help: if there were a type named `Example` that implemented `Trait`, you could use the fully-qualified path + | +LL - type Alias4 = [u8; self::CONST]; +LL + type Alias4 = [u8; <Example as Trait>::CONST]; + | + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0223`. diff --git a/tests/ui/use/import_trait_associated_item_glob.rs b/tests/ui/use/import_trait_associated_item_glob.rs new file mode 100644 index 00000000000..8a712514ed5 --- /dev/null +++ b/tests/ui/use/import_trait_associated_item_glob.rs @@ -0,0 +1,17 @@ +//@ check-pass + +#![feature(import_trait_associated_functions)] + +trait Trait: Default { + fn f() -> Self { Default::default() } + fn g() -> Self { Default::default() } +} + +impl Trait for u8 {} + +use Trait::*; + +fn main() { + let _: u8 = f(); + let _: u8 = g(); +} diff --git a/tests/ui/use/use-from-trait-xc.rs b/tests/ui/use/use-from-trait-xc.rs index b030892aa26..220bf8cad46 100644 --- a/tests/ui/use/use-from-trait-xc.rs +++ b/tests/ui/use/use-from-trait-xc.rs @@ -6,7 +6,7 @@ use use_from_trait_xc::Trait::foo; //~^ ERROR `use` associated items of traits is unstable [E0658] use use_from_trait_xc::Trait::Assoc; -//~^ ERROR `Assoc` is not directly importable +//~^ ERROR `use` associated items of traits is unstable [E0658] use use_from_trait_xc::Trait::CONST; //~^ ERROR `use` associated items of traits is unstable [E0658] diff --git a/tests/ui/use/use-from-trait-xc.stderr b/tests/ui/use/use-from-trait-xc.stderr index 0f8440aa530..c8ee29b18d0 100644 --- a/tests/ui/use/use-from-trait-xc.stderr +++ b/tests/ui/use/use-from-trait-xc.stderr @@ -8,11 +8,15 @@ LL | use use_from_trait_xc::Trait::foo; = help: add `#![feature(import_trait_associated_functions)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0253]: `Assoc` is not directly importable +error[E0658]: `use` associated items of traits is unstable --> $DIR/use-from-trait-xc.rs:8:5 | LL | use use_from_trait_xc::Trait::Assoc; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot be imported directly + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #134691 <https://github.com/rust-lang/rust/issues/134691> for more information + = help: add `#![feature(import_trait_associated_functions)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `use` associated items of traits is unstable --> $DIR/use-from-trait-xc.rs:11:5 @@ -74,5 +78,5 @@ LL | struct Foo; error: aborting due to 9 previous errors -Some errors have detailed explanations: E0253, E0432, E0603, E0658. -For more information about an error, try `rustc --explain E0253`. +Some errors have detailed explanations: E0432, E0603, E0658. +For more information about an error, try `rustc --explain E0432`. diff --git a/tests/ui/use/use-from-trait.rs b/tests/ui/use/use-from-trait.rs index 89b7aaa4ba3..cab661a29e8 100644 --- a/tests/ui/use/use-from-trait.rs +++ b/tests/ui/use/use-from-trait.rs @@ -1,5 +1,5 @@ use Trait::foo; //~ ERROR `use` associated items of traits is unstable [E0658] -use Trait::Assoc; //~ ERROR `Assoc` is not directly importable +use Trait::Assoc; //~ ERROR `use` associated items of traits is unstable [E0658] use Trait::C; //~ ERROR `use` associated items of traits is unstable [E0658] use Foo::new; //~ ERROR unresolved import `Foo` [E0432] diff --git a/tests/ui/use/use-from-trait.stderr b/tests/ui/use/use-from-trait.stderr index 2dd78a35452..63cfcc20750 100644 --- a/tests/ui/use/use-from-trait.stderr +++ b/tests/ui/use/use-from-trait.stderr @@ -8,11 +8,15 @@ LL | use Trait::foo; = help: add `#![feature(import_trait_associated_functions)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0253]: `Assoc` is not directly importable +error[E0658]: `use` associated items of traits is unstable --> $DIR/use-from-trait.rs:2:5 | LL | use Trait::Assoc; - | ^^^^^^^^^^^^ cannot be imported directly + | ^^^^^^^^^^^^ + | + = note: see issue #134691 <https://github.com/rust-lang/rust/issues/134691> for more information + = help: add `#![feature(import_trait_associated_functions)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `use` associated items of traits is unstable --> $DIR/use-from-trait.rs:3:5 @@ -38,5 +42,5 @@ LL | use Foo::C2; error: aborting due to 5 previous errors -Some errors have detailed explanations: E0253, E0432, E0658. -For more information about an error, try `rustc --explain E0253`. +Some errors have detailed explanations: E0432, E0658. +For more information about an error, try `rustc --explain E0432`. |
