diff options
243 files changed, 3539 insertions, 1327 deletions
diff --git a/.mailmap b/.mailmap index cd617ca2f42..213aa6eff66 100644 --- a/.mailmap +++ b/.mailmap @@ -43,6 +43,7 @@ Brian Anderson <banderson@mozilla.com> <andersrb@gmail.com> Brian Anderson <banderson@mozilla.com> <banderson@mozilla.org> Brian Dawn <brian.t.dawn@gmail.com> Brian Leibig <brian@brianleibig.com> Brian Leibig <brian.leibig@gmail.com> +Noah Lev <camelidcamel@gmail.com> Noah Lev <camelidcamel@gmail.com> <37223377+camelid@users.noreply.github.com> Carl-Anton Ingmarsson <mail@carlanton.se> <ca.ingmarsson@gmail.com> Carol (Nichols || Goulding) <carol.nichols@gmail.com> <193874+carols10cents@users.noreply.github.com> diff --git a/Cargo.lock b/Cargo.lock index da65b376d19..62734bfaf62 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2195,9 +2195,9 @@ dependencies = [ [[package]] name = "minifier" -version = "0.0.39" +version = "0.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cdf618de5c9c98d4a7b2e0d1f1e44f82a19196cfd94040bb203621c25d28d98" +checksum = "5594542d20834f2b974f5e5fb8e0cf1c67a2119dcadc29ef5d93a081fb30cc08" dependencies = [ "macro-utils", ] diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index ffec28a395f..8061e0b83c0 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -10,8 +10,8 @@ )] #![feature(box_syntax)] #![feature(box_patterns)] +// On bootstrap bump, this will likely have to become const_fn_unsize #![cfg_attr(bootstrap, feature(const_fn))] // For the `transmute` in `P::new` -#![cfg_attr(not(bootstrap), feature(const_fn_unsize))] // For the `transmute` in `P::new` #![feature(const_fn_transmute)] #![feature(const_panic)] #![feature(crate_visibility_modifier)] diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index c0f2920652f..6b132e4ff0f 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -2,6 +2,7 @@ use rustc_span::DUMMY_SP; +use rustc_ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::ErrorReported; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; @@ -175,6 +176,7 @@ pub(crate) fn codegen_const_value<'tcx>( let mut alloc = Allocation::from_bytes( std::iter::repeat(0).take(size.bytes_usize()).collect::<Vec<u8>>(), align, + Mutability::Not, ); alloc.write_scalar(fx, alloc_range(Size::ZERO, size), x.into()).unwrap(); let alloc = fx.tcx.intern_const_alloc(alloc); diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index eef71e096a5..945406aed4b 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -283,6 +283,8 @@ declare_features! ( (accepted, non_ascii_idents, "1.53.0", Some(55467), None), /// Allows arbitrary expressions in key-value attributes at parse time. (accepted, extended_key_value_attributes, "1.54.0", Some(78835), None), + /// Allows unsizing coercions in `const fn`. + (accepted, const_fn_unsize, "1.54.0", Some(64992), None), // ------------------------------------------------------------------------- // feature-group-end: accepted features diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 2cef46a844a..62d81304134 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -644,9 +644,6 @@ declare_features! ( /// Allows trait bounds in `const fn`. (active, const_fn_trait_bound, "1.53.0", Some(57563), None), - /// Allows unsizing coercions in `const fn`. - (active, const_fn_unsize, "1.53.0", Some(64992), None), - /// Allows `async {}` expressions in const contexts. (active, const_async_blocks, "1.53.0", Some(85368), None), diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 622eaf57578..ee3902991e9 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -99,8 +99,12 @@ impl AllocRange { // The constructors are all without extra; the extra gets added by a machine hook later. impl<Tag> Allocation<Tag> { - /// Creates a read-only allocation initialized by the given bytes - pub fn from_bytes<'a>(slice: impl Into<Cow<'a, [u8]>>, align: Align) -> Self { + /// Creates an allocation initialized by the given bytes + pub fn from_bytes<'a>( + slice: impl Into<Cow<'a, [u8]>>, + align: Align, + mutability: Mutability, + ) -> Self { let bytes = slice.into().into_owned(); let size = Size::from_bytes(bytes.len()); Self { @@ -108,13 +112,13 @@ impl<Tag> Allocation<Tag> { relocations: Relocations::new(), init_mask: InitMask::new(size, true), align, - mutability: Mutability::Not, + mutability, extra: (), } } - pub fn from_byte_aligned_bytes<'a>(slice: impl Into<Cow<'a, [u8]>>) -> Self { - Allocation::from_bytes(slice, Align::ONE) + pub fn from_bytes_byte_aligned_immutable<'a>(slice: impl Into<Cow<'a, [u8]>>) -> Self { + Allocation::from_bytes(slice, Align::ONE, Mutability::Not) } pub fn uninit(size: Size, align: Align) -> Self { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index dde5cbadbd9..790463d6fc6 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1072,7 +1072,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Allocates a read-only byte or string literal for `mir::interpret`. pub fn allocate_bytes(self, bytes: &[u8]) -> interpret::AllocId { // Create an allocation that just contains these bytes. - let alloc = interpret::Allocation::from_byte_aligned_bytes(bytes); + let alloc = interpret::Allocation::from_bytes_byte_aligned_immutable(bytes); let alloc = self.intern_const_alloc(alloc); self.create_memory_alloc(alloc) } diff --git a/compiler/rustc_mir/src/const_eval/eval_queries.rs b/compiler/rustc_mir/src/const_eval/eval_queries.rs index d51adc8864d..a12185393de 100644 --- a/compiler/rustc_mir/src/const_eval/eval_queries.rs +++ b/compiler/rustc_mir/src/const_eval/eval_queries.rs @@ -169,8 +169,9 @@ pub(super) fn op_to_const<'tcx>( (ecx.tcx.global_alloc(ptr.alloc_id).unwrap_memory(), ptr.offset.bytes()) } Scalar::Int { .. } => ( - ecx.tcx - .intern_const_alloc(Allocation::from_byte_aligned_bytes(b"" as &[u8])), + ecx.tcx.intern_const_alloc(Allocation::from_bytes_byte_aligned_immutable( + b"" as &[u8], + )), 0, ), }; diff --git a/compiler/rustc_mir/src/interpret/intrinsics/caller_location.rs b/compiler/rustc_mir/src/interpret/intrinsics/caller_location.rs index 2b996cf62a3..792a4749108 100644 --- a/compiler/rustc_mir/src/interpret/intrinsics/caller_location.rs +++ b/compiler/rustc_mir/src/interpret/intrinsics/caller_location.rs @@ -1,5 +1,6 @@ use std::convert::TryFrom; +use rustc_ast::Mutability; use rustc_hir::lang_items::LangItem; use rustc_middle::mir::TerminatorKind; use rustc_middle::ty::subst::Subst; @@ -79,7 +80,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { line: u32, col: u32, ) -> MPlaceTy<'tcx, M::PointerTag> { - let file = self.allocate_str(&filename.as_str(), MemoryKind::CallerLocation); + let file = + self.allocate_str(&filename.as_str(), MemoryKind::CallerLocation, Mutability::Not); let line = Scalar::from_u32(line); let col = Scalar::from_u32(col); diff --git a/compiler/rustc_mir/src/interpret/intrinsics/type_name.rs b/compiler/rustc_mir/src/interpret/intrinsics/type_name.rs index ae5e78ee33f..4978cc3606d 100644 --- a/compiler/rustc_mir/src/interpret/intrinsics/type_name.rs +++ b/compiler/rustc_mir/src/interpret/intrinsics/type_name.rs @@ -197,6 +197,6 @@ impl Write for AbsolutePathPrinter<'_> { /// Directly returns an `Allocation` containing an absolute path representation of the given type. crate fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> &'tcx Allocation { let path = AbsolutePathPrinter { tcx, path: String::new() }.print_type(ty).unwrap().path; - let alloc = Allocation::from_byte_aligned_bytes(path.into_bytes()); + let alloc = Allocation::from_bytes_byte_aligned_immutable(path.into_bytes()); tcx.intern_const_alloc(alloc) } diff --git a/compiler/rustc_mir/src/interpret/machine.rs b/compiler/rustc_mir/src/interpret/machine.rs index e7d7c38cc8f..0ca99da7304 100644 --- a/compiler/rustc_mir/src/interpret/machine.rs +++ b/compiler/rustc_mir/src/interpret/machine.rs @@ -132,6 +132,11 @@ pub trait Machine<'mir, 'tcx>: Sized { /// Whether to enforce the validity invariant fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool; + /// Whether function calls should be [ABI](Abi)-checked. + fn enforce_abi(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { + true + } + /// Entry point for obtaining the MIR of anything that should get evaluated. /// So not just functions and shims, but also const/static initializers, anonymous /// constants, ... diff --git a/compiler/rustc_mir/src/interpret/memory.rs b/compiler/rustc_mir/src/interpret/memory.rs index 7fb7c51b0b5..77de19ac674 100644 --- a/compiler/rustc_mir/src/interpret/memory.rs +++ b/compiler/rustc_mir/src/interpret/memory.rs @@ -219,9 +219,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { pub fn allocate_bytes( &mut self, bytes: &[u8], + align: Align, kind: MemoryKind<M::MemoryKind>, + mutability: Mutability, ) -> Pointer<M::PointerTag> { - let alloc = Allocation::from_byte_aligned_bytes(bytes); + let alloc = Allocation::from_bytes(bytes, align, mutability); self.allocate_with(alloc, kind) } @@ -321,6 +323,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { } }; + if alloc.mutability == Mutability::Not { + throw_ub_format!("deallocating immutable allocation {}", ptr.alloc_id); + } if alloc_kind != kind { throw_ub_format!( "deallocating {}, which is {} memory, using {} deallocation operation", @@ -625,9 +630,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // Need to make a copy, even if `get_global_alloc` is able // to give us a cheap reference. let alloc = Self::get_global_alloc(memory_extra, tcx, id, /*is_write*/ true)?; - if alloc.mutability == Mutability::Not { - throw_ub!(WriteToReadOnly(id)) - } let kind = M::GLOBAL_KIND.expect( "I got a global allocation that I have to copy but the machine does \ not expect that to happen", @@ -673,8 +675,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { pub fn get_alloc_extra_mut<'a>( &'a mut self, id: AllocId, - ) -> InterpResult<'tcx, &'a mut M::AllocExtra> { - Ok(&mut self.get_raw_mut(id)?.0.extra) + ) -> InterpResult<'tcx, (&'a mut M::AllocExtra, &'a mut M::MemoryExtra)> { + let (alloc, memory_extra) = self.get_raw_mut(id)?; + Ok((&mut alloc.extra, memory_extra)) } /// Obtain the size and alignment of an allocation, even if that allocation has diff --git a/compiler/rustc_mir/src/interpret/place.rs b/compiler/rustc_mir/src/interpret/place.rs index ef603b51554..79aaff1c5eb 100644 --- a/compiler/rustc_mir/src/interpret/place.rs +++ b/compiler/rustc_mir/src/interpret/place.rs @@ -6,6 +6,7 @@ use std::convert::TryFrom; use std::fmt::Debug; use std::hash::Hash; +use rustc_ast::Mutability; use rustc_macros::HashStable; use rustc_middle::mir; use rustc_middle::ty::layout::{PrimitiveExt, TyAndLayout}; @@ -1024,18 +1025,23 @@ where MPlaceTy::from_aligned_ptr(ptr, layout) } - /// Returns a wide MPlace. + /// Returns a wide MPlace of type `&'static [mut] str` to a new 1-aligned allocation. pub fn allocate_str( &mut self, str: &str, kind: MemoryKind<M::MemoryKind>, + mutbl: Mutability, ) -> MPlaceTy<'tcx, M::PointerTag> { - let ptr = self.memory.allocate_bytes(str.as_bytes(), kind); + let ptr = self.memory.allocate_bytes(str.as_bytes(), Align::ONE, kind, mutbl); let meta = Scalar::from_machine_usize(u64::try_from(str.len()).unwrap(), self); let mplace = MemPlace { ptr: ptr.into(), align: Align::ONE, meta: MemPlaceMeta::Meta(meta) }; - let layout = self.layout_of(self.tcx.mk_static_str()).unwrap(); + let ty = self.tcx.mk_ref( + self.tcx.lifetimes.re_static, + ty::TypeAndMut { ty: self.tcx.types.str_, mutbl }, + ); + let layout = self.layout_of(ty).unwrap(); MPlaceTy { mplace, layout } } diff --git a/compiler/rustc_mir/src/interpret/terminator.rs b/compiler/rustc_mir/src/interpret/terminator.rs index a7fcb41f74a..a3dc8aaef32 100644 --- a/compiler/rustc_mir/src/interpret/terminator.rs +++ b/compiler/rustc_mir/src/interpret/terminator.rs @@ -232,26 +232,28 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // ABI check let check_abi = |this: &Self, instance_ty: Ty<'tcx>| -> InterpResult<'tcx> { - let callee_abi = match instance_ty.kind() { - ty::FnDef(..) => instance_ty.fn_sig(*this.tcx).abi(), - ty::Closure(..) => Abi::RustCall, - ty::Generator(..) => Abi::Rust, - _ => span_bug!(this.cur_span(), "unexpected callee ty: {:?}", instance_ty), - }; - let normalize_abi = |abi| match abi { - Abi::Rust | Abi::RustCall | Abi::RustIntrinsic | Abi::PlatformIntrinsic => - // These are all the same ABI, really. - { - Abi::Rust + if M::enforce_abi(this) { + let callee_abi = match instance_ty.kind() { + ty::FnDef(..) => instance_ty.fn_sig(*this.tcx).abi(), + ty::Closure(..) => Abi::RustCall, + ty::Generator(..) => Abi::Rust, + _ => span_bug!(this.cur_span(), "unexpected callee ty: {:?}", instance_ty), + }; + let normalize_abi = |abi| match abi { + Abi::Rust | Abi::RustCall | Abi::RustIntrinsic | Abi::PlatformIntrinsic => + // These are all the same ABI, really. + { + Abi::Rust + } + abi => abi, + }; + if normalize_abi(caller_abi) != normalize_abi(callee_abi) { + throw_ub_format!( + "calling a function with ABI {} using caller ABI {}", + callee_abi.name(), + caller_abi.name() + ) } - abi => abi, - }; - if normalize_abi(caller_abi) != normalize_abi(callee_abi) { - throw_ub_format!( - "calling a function with ABI {} using caller ABI {}", - callee_abi.name(), - caller_abi.name() - ) } Ok(()) }; diff --git a/compiler/rustc_mir/src/transform/check_consts/ops.rs b/compiler/rustc_mir/src/transform/check_consts/ops.rs index 16f5f14f563..dcbc9c523dc 100644 --- a/compiler/rustc_mir/src/transform/check_consts/ops.rs +++ b/compiler/rustc_mir/src/transform/check_consts/ops.rs @@ -541,30 +541,6 @@ impl NonConstOp for UnionAccess { } } -/// See [#64992]. -/// -/// [#64992]: https://github.com/rust-lang/rust/issues/64992 -#[derive(Debug)] -pub struct UnsizingCast; -impl NonConstOp for UnsizingCast { - fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { - if ccx.const_kind() != hir::ConstContext::ConstFn { - Status::Allowed - } else { - Status::Unstable(sym::const_fn_unsize) - } - } - - fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { - feature_err( - &ccx.tcx.sess.parse_sess, - sym::const_fn_unsize, - span, - "unsizing casts to types besides slices are not allowed in const fn", - ) - } -} - // Types that cannot appear in the signature or locals of a `const fn`. pub mod ty { use super::*; diff --git a/compiler/rustc_mir/src/transform/check_consts/validation.rs b/compiler/rustc_mir/src/transform/check_consts/validation.rs index 63fc66f2b9f..41d9d0d04b5 100644 --- a/compiler/rustc_mir/src/transform/check_consts/validation.rs +++ b/compiler/rustc_mir/src/transform/check_consts/validation.rs @@ -10,9 +10,7 @@ use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceC use rustc_middle::mir::*; use rustc_middle::ty::cast::CastTy; use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::{ - self, adjustment::PointerCast, Instance, InstanceDef, Ty, TyCtxt, TypeAndMut, -}; +use rustc_middle::ty::{self, adjustment::PointerCast, Instance, InstanceDef, Ty, TyCtxt}; use rustc_middle::ty::{Binder, TraitPredicate, TraitRef}; use rustc_span::{sym, Span, Symbol}; use rustc_trait_selection::traits::error_reporting::InferCtxtExt; @@ -636,17 +634,9 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { _, ) => self.check_op(ops::FnPtrCast), - Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), _, cast_ty) => { - if let Some(TypeAndMut { ty, .. }) = cast_ty.builtin_deref(true) { - let unsized_ty = self.tcx.struct_tail_erasing_lifetimes(ty, self.param_env); - - // Casting/coercing things to slices is fine. - if let ty::Slice(_) | ty::Str = unsized_ty.kind() { - return; - } - } - - self.check_op(ops::UnsizingCast); + Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), _, _) => { + // Nothing to check here (`check_local_or_return_ty` ensures no trait objects occur + // in the type of any local, which also excludes casts). } Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => { diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 8c2c81c8628..aa8193dab5d 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -7,8 +7,11 @@ use rustc_middle::ty::{self, TyCtxt}; use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE}; use rustc_session::lint::Level; use rustc_span::def_id::{DefId, LocalDefId}; +use rustc_span::symbol::Symbol; use rustc_span::Span; +use std::ops::Bound; + struct UnsafetyVisitor<'a, 'tcx> { tcx: TyCtxt<'tcx>, thir: &'a Thir<'tcx>, @@ -19,6 +22,10 @@ struct UnsafetyVisitor<'a, 'tcx> { /// `unsafe` block, and whether it has been used. safety_context: SafetyContext, body_unsafety: BodyUnsafety, + /// The `#[target_feature]` attributes of the body. Used for checking + /// calls to functions with `#[target_feature]` (RFC 2396). + body_target_features: &'tcx Vec<Symbol>, + is_const: bool, } impl<'tcx> UnsafetyVisitor<'_, 'tcx> { @@ -148,11 +155,55 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { ExprKind::Call { fun, ty: _, args: _, from_hir_call: _, fn_span: _ } => { if self.thir[fun].ty.fn_sig(self.tcx).unsafety() == hir::Unsafety::Unsafe { self.requires_unsafe(expr.span, CallToUnsafeFunction); + } else if let &ty::FnDef(func_did, _) = self.thir[fun].ty.kind() { + // If the called function has target features the calling function hasn't, + // the call requires `unsafe`. + if !self + .tcx + .codegen_fn_attrs(func_did) + .target_features + .iter() + .all(|feature| self.body_target_features.contains(feature)) + { + self.requires_unsafe(expr.span, CallToFunctionWith); + } + } + } + ExprKind::Deref { arg } => { + if let ExprKind::StaticRef { def_id, .. } = self.thir[arg].kind { + if self.tcx.is_mutable_static(def_id) { + self.requires_unsafe(expr.span, UseOfMutableStatic); + } else if self.tcx.is_foreign_item(def_id) { + self.requires_unsafe(expr.span, UseOfExternStatic); + } + } else if self.thir[arg].ty.is_unsafe_ptr() { + self.requires_unsafe(expr.span, DerefOfRawPointer); } } ExprKind::InlineAsm { .. } | ExprKind::LlvmInlineAsm { .. } => { self.requires_unsafe(expr.span, UseOfInlineAssembly); } + ExprKind::Adt { + adt_def, + variant_index: _, + substs: _, + user_ty: _, + fields: _, + base: _, + } => match self.tcx.layout_scalar_valid_range(adt_def.did) { + (Bound::Unbounded, Bound::Unbounded) => {} + _ => self.requires_unsafe(expr.span, InitializingTypeWith), + }, + ExprKind::Cast { source } => { + let source = &self.thir[source]; + if self.tcx.features().const_raw_ptr_to_usize_cast + && self.is_const + && (source.ty.is_unsafe_ptr() || source.ty.is_fn_ptr()) + && expr.ty.is_integral() + { + self.requires_unsafe(expr.span, CastOfPointerToInt); + } + } _ => {} } @@ -195,15 +246,10 @@ impl BodyUnsafety { enum UnsafeOpKind { CallToUnsafeFunction, UseOfInlineAssembly, - #[allow(dead_code)] // FIXME InitializingTypeWith, - #[allow(dead_code)] // FIXME CastOfPointerToInt, - #[allow(dead_code)] // FIXME UseOfMutableStatic, - #[allow(dead_code)] // FIXME UseOfExternStatic, - #[allow(dead_code)] // FIXME DerefOfRawPointer, #[allow(dead_code)] // FIXME AssignToDroppingUnionField, @@ -213,7 +259,6 @@ enum UnsafeOpKind { MutationOfLayoutConstrainedField, #[allow(dead_code)] // FIXME BorrowOfLayoutConstrainedField, - #[allow(dead_code)] // FIXME CallToFunctionWith, } @@ -287,6 +332,7 @@ pub fn check_unsafety<'tcx>( tcx: TyCtxt<'tcx>, thir: &Thir<'tcx>, expr: ExprId, + def_id: LocalDefId, hir_id: hir::HirId, ) { let body_unsafety = tcx.hir().fn_sig_by_hir_id(hir_id).map_or(BodyUnsafety::Safe, |fn_sig| { @@ -296,10 +342,23 @@ pub fn check_unsafety<'tcx>( BodyUnsafety::Safe } }); + let body_target_features = &tcx.codegen_fn_attrs(def_id).target_features; let safety_context = if body_unsafety.is_unsafe() { SafetyContext::UnsafeFn } else { SafetyContext::Safe }; - let mut visitor = - UnsafetyVisitor { tcx, thir, safety_context, hir_context: hir_id, body_unsafety }; + let is_const = match tcx.hir().body_owner_kind(hir_id) { + hir::BodyOwnerKind::Closure => false, + hir::BodyOwnerKind::Fn => tcx.is_const_fn_raw(def_id.to_def_id()), + hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => true, + }; + let mut visitor = UnsafetyVisitor { + tcx, + thir, + safety_context, + hir_context: hir_id, + body_unsafety, + body_target_features, + is_const, + }; visitor.visit_expr(&thir[expr]); } @@ -311,7 +370,7 @@ crate fn thir_check_unsafety_inner<'tcx>( let body_id = tcx.hir().body_owned_by(hir_id); let body = tcx.hir().body(body_id); let (thir, expr) = cx::build_thir(tcx, def, &body.value); - check_unsafety(tcx, &thir, expr, hir_id); + check_unsafety(tcx, &thir, expr, def.did, hir_id); } crate fn thir_check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) { diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index ac93d042970..d62fd161e2f 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -25,14 +25,14 @@ crate fn lit_to_const<'tcx>( let lit = match (lit, &ty.kind()) { (ast::LitKind::Str(s, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_str() => { let s = s.as_str(); - let allocation = Allocation::from_byte_aligned_bytes(s.as_bytes()); + let allocation = Allocation::from_bytes_byte_aligned_immutable(s.as_bytes()); let allocation = tcx.intern_const_alloc(allocation); ConstValue::Slice { data: allocation, start: 0, end: s.len() } } (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Slice(_)) => { - let allocation = Allocation::from_byte_aligned_bytes(data as &[u8]); + let allocation = Allocation::from_bytes_byte_aligned_immutable(data as &[u8]); let allocation = tcx.intern_const_alloc(allocation); ConstValue::Slice { data: allocation, start: 0, end: data.len() } } diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_none.rs b/compiler/rustc_target/src/spec/aarch64_unknown_none.rs index c9f622820de..de92b167f00 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_none.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_none.rs @@ -16,7 +16,6 @@ pub fn target() -> Target { executables: true, relocation_model: RelocModel::Static, disable_redzone: true, - linker_is_gnu: true, max_atomic_width: Some(128), panic_strategy: PanicStrategy::Abort, unsupported_abis: super::arm_base::unsupported_abis(), diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs index 0811871c993..2566eeae14a 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs @@ -16,7 +16,6 @@ pub fn target() -> Target { executables: true, relocation_model: RelocModel::Static, disable_redzone: true, - linker_is_gnu: true, max_atomic_width: Some(128), panic_strategy: PanicStrategy::Abort, unsupported_abis: super::arm_base::unsupported_abis(), diff --git a/compiler/rustc_target/src/spec/apple_base.rs b/compiler/rustc_target/src/spec/apple_base.rs index bc2ec670901..45c5c1a16e9 100644 --- a/compiler/rustc_target/src/spec/apple_base.rs +++ b/compiler/rustc_target/src/spec/apple_base.rs @@ -22,6 +22,7 @@ pub fn opts(os: &str) -> TargetOptions { // macOS has -dead_strip, which doesn't rely on function_sections function_sections: false, dynamic_linking: true, + linker_is_gnu: false, executables: true, families: vec!["unix".to_string()], is_like_osx: true, diff --git a/compiler/rustc_target/src/spec/avr_gnu_base.rs b/compiler/rustc_target/src/spec/avr_gnu_base.rs index 69ccce875ab..2cb2661a526 100644 --- a/compiler/rustc_target/src/spec/avr_gnu_base.rs +++ b/compiler/rustc_target/src/spec/avr_gnu_base.rs @@ -16,7 +16,6 @@ pub fn target(target_cpu: String) -> Target { linker: Some("avr-gcc".to_owned()), executables: true, - linker_is_gnu: true, eh_frame_header: false, pre_link_args: vec![(LinkerFlavor::Gcc, vec![format!("-mmcu={}", target_cpu)])] .into_iter() diff --git a/compiler/rustc_target/src/spec/dragonfly_base.rs b/compiler/rustc_target/src/spec/dragonfly_base.rs index fb94498c131..e13a640d4d2 100644 --- a/compiler/rustc_target/src/spec/dragonfly_base.rs +++ b/compiler/rustc_target/src/spec/dragonfly_base.rs @@ -6,7 +6,6 @@ pub fn opts() -> TargetOptions { dynamic_linking: true, executables: true, families: vec!["unix".to_string()], - linker_is_gnu: true, has_rpath: true, position_independent_executables: true, relro_level: RelroLevel::Full, diff --git a/compiler/rustc_target/src/spec/freebsd_base.rs b/compiler/rustc_target/src/spec/freebsd_base.rs index 5d3c28e5f29..bef2fce7c83 100644 --- a/compiler/rustc_target/src/spec/freebsd_base.rs +++ b/compiler/rustc_target/src/spec/freebsd_base.rs @@ -6,7 +6,6 @@ pub fn opts() -> TargetOptions { dynamic_linking: true, executables: true, families: vec!["unix".to_string()], - linker_is_gnu: true, has_rpath: true, position_independent_executables: true, eliminate_frame_pointer: false, // FIXME 43575 diff --git a/compiler/rustc_target/src/spec/fuchsia_base.rs b/compiler/rustc_target/src/spec/fuchsia_base.rs index 13264dffeb4..23a2e65749e 100644 --- a/compiler/rustc_target/src/spec/fuchsia_base.rs +++ b/compiler/rustc_target/src/spec/fuchsia_base.rs @@ -27,7 +27,6 @@ pub fn opts() -> TargetOptions { executables: true, families: vec!["unix".to_string()], is_like_fuchsia: true, - linker_is_gnu: true, pre_link_args, pre_link_objects: crt_objects::new(&[ (LinkOutputKind::DynamicNoPicExe, &["Scrt1.o"]), diff --git a/compiler/rustc_target/src/spec/haiku_base.rs b/compiler/rustc_target/src/spec/haiku_base.rs index fae56f6a82d..2b95523d6f7 100644 --- a/compiler/rustc_target/src/spec/haiku_base.rs +++ b/compiler/rustc_target/src/spec/haiku_base.rs @@ -7,7 +7,6 @@ pub fn opts() -> TargetOptions { executables: true, families: vec!["unix".to_string()], relro_level: RelroLevel::Full, - linker_is_gnu: true, ..Default::default() } } diff --git a/compiler/rustc_target/src/spec/hermit_base.rs b/compiler/rustc_target/src/spec/hermit_base.rs index ad013047e6a..75ca1f79b12 100644 --- a/compiler/rustc_target/src/spec/hermit_base.rs +++ b/compiler/rustc_target/src/spec/hermit_base.rs @@ -13,7 +13,6 @@ pub fn opts() -> TargetOptions { linker: Some("rust-lld".to_owned()), executables: true, has_elf_tls: true, - linker_is_gnu: true, pre_link_args, panic_strategy: PanicStrategy::Abort, position_independent_executables: true, diff --git a/compiler/rustc_target/src/spec/hermit_kernel_base.rs b/compiler/rustc_target/src/spec/hermit_kernel_base.rs index 6d18a14d6ae..c55a46e69a8 100644 --- a/compiler/rustc_target/src/spec/hermit_kernel_base.rs +++ b/compiler/rustc_target/src/spec/hermit_kernel_base.rs @@ -14,7 +14,6 @@ pub fn opts() -> TargetOptions { linker: Some("rust-lld".to_owned()), executables: true, has_elf_tls: true, - linker_is_gnu: true, pre_link_args, panic_strategy: PanicStrategy::Abort, position_independent_executables: true, diff --git a/compiler/rustc_target/src/spec/illumos_base.rs b/compiler/rustc_target/src/spec/illumos_base.rs index 2b8e046c46b..9d9da50be7a 100644 --- a/compiler/rustc_target/src/spec/illumos_base.rs +++ b/compiler/rustc_target/src/spec/illumos_base.rs @@ -33,6 +33,7 @@ pub fn opts() -> TargetOptions { has_rpath: true, families: vec!["unix".to_string()], is_like_solaris: true, + linker_is_gnu: false, limit_rdylib_exports: false, // Linker doesn't support this eliminate_frame_pointer: false, eh_frame_header: false, diff --git a/compiler/rustc_target/src/spec/l4re_base.rs b/compiler/rustc_target/src/spec/l4re_base.rs index 65c343a5f21..f6e3102f617 100644 --- a/compiler/rustc_target/src/spec/l4re_base.rs +++ b/compiler/rustc_target/src/spec/l4re_base.rs @@ -20,6 +20,7 @@ pub fn opts() -> TargetOptions { executables: true, panic_strategy: PanicStrategy::Abort, linker: Some("ld".to_string()), + linker_is_gnu: false, families: vec!["unix".to_string()], ..Default::default() } diff --git a/compiler/rustc_target/src/spec/linux_base.rs b/compiler/rustc_target/src/spec/linux_base.rs index 184659e22d9..af81bc714c7 100644 --- a/compiler/rustc_target/src/spec/linux_base.rs +++ b/compiler/rustc_target/src/spec/linux_base.rs @@ -6,7 +6,6 @@ pub fn opts() -> TargetOptions { dynamic_linking: true, executables: true, families: vec!["unix".to_string()], - linker_is_gnu: true, has_rpath: true, position_independent_executables: true, relro_level: RelroLevel::Full, diff --git a/compiler/rustc_target/src/spec/linux_kernel_base.rs b/compiler/rustc_target/src/spec/linux_kernel_base.rs index 64f47b4aa9b..145aa4a5894 100644 --- a/compiler/rustc_target/src/spec/linux_kernel_base.rs +++ b/compiler/rustc_target/src/spec/linux_kernel_base.rs @@ -8,7 +8,6 @@ pub fn opts() -> TargetOptions { // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved stack_probes: StackProbeType::Call, eliminate_frame_pointer: false, - linker_is_gnu: true, position_independent_executables: true, needs_plt: true, relro_level: RelroLevel::Full, diff --git a/compiler/rustc_target/src/spec/mipsel_sony_psp.rs b/compiler/rustc_target/src/spec/mipsel_sony_psp.rs index 08c290e6ff1..dc14e4bdf94 100644 --- a/compiler/rustc_target/src/spec/mipsel_sony_psp.rs +++ b/compiler/rustc_target/src/spec/mipsel_sony_psp.rs @@ -21,7 +21,6 @@ pub fn target() -> Target { cpu: "mips2".to_string(), executables: true, linker: Some("rust-lld".to_owned()), - linker_is_gnu: true, relocation_model: RelocModel::Static, // PSP FPU only supports single precision floats. diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 4bffd6e8ddd..f1bd8ff237d 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1086,7 +1086,7 @@ pub struct TargetOptions { /// Version of DWARF to use if not using the default. /// Useful because some platforms (osx, bsd) only want up to DWARF2. pub dwarf_version: Option<u32>, - /// Whether the linker support GNU-like arguments such as -O. Defaults to false. + /// Whether the linker support GNU-like arguments such as -O. Defaults to true. pub linker_is_gnu: bool, /// The MinGW toolchain has a known issue that prevents it from correctly /// handling COFF object files with more than 2<sup>15</sup> sections. Since each weak @@ -1307,7 +1307,7 @@ impl Default for TargetOptions { is_like_fuchsia: false, is_like_wasm: false, dwarf_version: None, - linker_is_gnu: false, + linker_is_gnu: true, allows_weak_linkage: true, has_rpath: false, no_default_libraries: true, diff --git a/compiler/rustc_target/src/spec/msvc_base.rs b/compiler/rustc_target/src/spec/msvc_base.rs index 4ed7685ca07..f1ed4c154d2 100644 --- a/compiler/rustc_target/src/spec/msvc_base.rs +++ b/compiler/rustc_target/src/spec/msvc_base.rs @@ -16,6 +16,7 @@ pub fn opts() -> TargetOptions { is_like_windows: true, is_like_msvc: true, lld_flavor: LldFlavor::Link, + linker_is_gnu: false, pre_link_args, abi_return_struct_as_int: true, emit_debug_gdb_scripts: false, diff --git a/compiler/rustc_target/src/spec/netbsd_base.rs b/compiler/rustc_target/src/spec/netbsd_base.rs index 602fb6eb641..6bb6083d47c 100644 --- a/compiler/rustc_target/src/spec/netbsd_base.rs +++ b/compiler/rustc_target/src/spec/netbsd_base.rs @@ -6,7 +6,6 @@ pub fn opts() -> TargetOptions { dynamic_linking: true, executables: true, families: vec!["unix".to_string()], - linker_is_gnu: true, no_default_libraries: false, has_rpath: true, position_independent_executables: true, diff --git a/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs b/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs index 15d8e4843f9..97960a75b09 100644 --- a/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs +++ b/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs @@ -14,6 +14,7 @@ pub fn target() -> Target { linker_flavor: LinkerFlavor::PtxLinker, // The linker can be installed from `crates.io`. linker: Some("rust-ptx-linker".to_string()), + linker_is_gnu: false, // With `ptx-linker` approach, it can be later overridden via link flags. cpu: "sm_30".to_string(), diff --git a/compiler/rustc_target/src/spec/openbsd_base.rs b/compiler/rustc_target/src/spec/openbsd_base.rs index 8f33bacd922..29b415e7726 100644 --- a/compiler/rustc_target/src/spec/openbsd_base.rs +++ b/compiler/rustc_target/src/spec/openbsd_base.rs @@ -6,7 +6,6 @@ pub fn opts() -> TargetOptions { dynamic_linking: true, executables: true, families: vec!["unix".to_string()], - linker_is_gnu: true, has_rpath: true, abi_return_struct_as_int: true, position_independent_executables: true, diff --git a/compiler/rustc_target/src/spec/redox_base.rs b/compiler/rustc_target/src/spec/redox_base.rs index 72052b9e2e2..fcf5db3746d 100644 --- a/compiler/rustc_target/src/spec/redox_base.rs +++ b/compiler/rustc_target/src/spec/redox_base.rs @@ -7,7 +7,6 @@ pub fn opts() -> TargetOptions { dynamic_linking: true, executables: true, families: vec!["unix".to_string()], - linker_is_gnu: true, has_rpath: true, position_independent_executables: true, relro_level: RelroLevel::Full, diff --git a/compiler/rustc_target/src/spec/solaris_base.rs b/compiler/rustc_target/src/spec/solaris_base.rs index 4c922eb5cea..bc32b501688 100644 --- a/compiler/rustc_target/src/spec/solaris_base.rs +++ b/compiler/rustc_target/src/spec/solaris_base.rs @@ -8,6 +8,7 @@ pub fn opts() -> TargetOptions { has_rpath: true, families: vec!["unix".to_string()], is_like_solaris: true, + linker_is_gnu: false, limit_rdylib_exports: false, // Linker doesn't support this eh_frame_header: false, diff --git a/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs b/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs index ef58824f381..f996009f830 100644 --- a/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs +++ b/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs @@ -28,7 +28,6 @@ pub fn target() -> Target { options: TargetOptions { linker_flavor: LinkerFlavor::Ld, linker: Some("arm-none-eabi-ld".to_string()), - linker_is_gnu: true, // extra args passed to the external assembler (assuming `arm-none-eabi-as`): // * activate t32/a32 interworking diff --git a/compiler/rustc_target/src/spec/vxworks_base.rs b/compiler/rustc_target/src/spec/vxworks_base.rs index 0e8e87f2dff..a91e7717865 100644 --- a/compiler/rustc_target/src/spec/vxworks_base.rs +++ b/compiler/rustc_target/src/spec/vxworks_base.rs @@ -10,7 +10,6 @@ pub fn opts() -> TargetOptions { dynamic_linking: true, executables: true, families: vec!["unix".to_string()], - linker_is_gnu: true, has_rpath: true, has_elf_tls: true, crt_static_default: true, diff --git a/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs b/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs index ddf28b423f0..302139395d3 100644 --- a/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs +++ b/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs @@ -34,7 +34,6 @@ pub fn target() -> Target { // functionality, and a .wasm file. exe_suffix: ".js".to_string(), linker: None, - linker_is_gnu: true, is_like_emscripten: true, panic_strategy: PanicStrategy::Unwind, post_link_args, diff --git a/compiler/rustc_target/src/spec/windows_gnu_base.rs b/compiler/rustc_target/src/spec/windows_gnu_base.rs index 5808391ee06..35a52896f6f 100644 --- a/compiler/rustc_target/src/spec/windows_gnu_base.rs +++ b/compiler/rustc_target/src/spec/windows_gnu_base.rs @@ -66,7 +66,6 @@ pub fn opts() -> TargetOptions { // FIXME(#13846) this should be enabled for windows function_sections: false, linker: Some("gcc".to_string()), - linker_is_gnu: true, dynamic_linking: true, executables: true, dll_prefix: String::new(), diff --git a/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs b/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs index 90705c526f4..06eb33d8d82 100644 --- a/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs +++ b/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs @@ -57,7 +57,6 @@ pub fn target() -> Target { vendor: "fortanix".into(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), executables: true, - linker_is_gnu: true, linker: Some("rust-lld".to_owned()), max_atomic_width: Some(64), cpu: "x86-64".into(), diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs index 4de8216884a..96569ae0e77 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs @@ -1522,7 +1522,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = tcx.type_of(impl_def_id); let impl_ty = self.instantiate_type_scheme(span, &substs, ty); - match self.at(&self.misc(span), self.param_env).sup(impl_ty, self_ty) { + match self.at(&self.misc(span), self.param_env).eq(impl_ty, self_ty) { Ok(ok) => self.register_infer_ok_obligations(ok), Err(_) => { self.tcx.sess.delay_span_bug( diff --git a/library/alloc/src/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs index a5481fd175e..5dda8c47688 100644 --- a/library/alloc/src/collections/linked_list.rs +++ b/library/alloc/src/collections/linked_list.rs @@ -442,27 +442,6 @@ impl<T> LinkedList<T> { } } - /// Moves all elements from `other` to the begin of the list. - #[unstable(feature = "linked_list_prepend", issue = "none")] - pub fn prepend(&mut self, other: &mut Self) { - match self.head { - None => mem::swap(self, other), - Some(mut head) => { - // `as_mut` is okay here because we have exclusive access to the entirety - // of both lists. - if let Some(mut other_tail) = other.tail.take() { - unsafe { - head.as_mut().prev = Some(other_tail); - other_tail.as_mut().next = Some(head); - } - - self.head = other.head.take(); - self.len += mem::replace(&mut other.len, 0); - } - } - } - } - /// Provides a forward iterator. /// /// # Examples diff --git a/library/core/src/ops/control_flow.rs b/library/core/src/ops/control_flow.rs index dbb51540bd4..f99eb9777d8 100644 --- a/library/core/src/ops/control_flow.rs +++ b/library/core/src/ops/control_flow.rs @@ -63,6 +63,7 @@ pub enum ControlFlow<B, C = ()> { } #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")] +#[cfg(bootstrap)] impl<B, C> ops::TryV1 for ControlFlow<B, C> { type Output = C; type Error = B; diff --git a/library/core/src/ops/mod.rs b/library/core/src/ops/mod.rs index beef74de617..139a8c0eec9 100644 --- a/library/core/src/ops/mod.rs +++ b/library/core/src/ops/mod.rs @@ -147,6 +147,7 @@ mod function; mod generator; mod index; mod range; +#[cfg(bootstrap)] mod r#try; mod try_trait; mod unsize; @@ -187,6 +188,7 @@ pub use self::range::{Bound, RangeBounds, RangeInclusive, RangeToInclusive}; pub use self::r#try::Try; #[unstable(feature = "try_trait_transition", reason = "for bootstrap", issue = "none")] +#[cfg(bootstrap)] pub(crate) use self::r#try::Try as TryV1; #[unstable(feature = "try_trait_v2", issue = "84277")] diff --git a/library/core/src/option.rs b/library/core/src/option.rs index cfc1768dee9..cfb73ed0395 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -1641,9 +1641,11 @@ impl<A, V: FromIterator<A>> FromIterator<Option<A>> for Option<V> { #[rustc_diagnostic_item = "none_error"] #[unstable(feature = "try_trait", issue = "42327")] #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] +#[cfg(bootstrap)] pub struct NoneError; #[unstable(feature = "try_trait", issue = "42327")] +#[cfg(bootstrap)] impl<T> ops::TryV1 for Option<T> { type Output = T; type Error = NoneError; diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 21e406194bc..babd0a0b552 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -1627,6 +1627,7 @@ impl<A, E, V: FromIterator<A>> FromIterator<Result<A, E>> for Result<V, E> { } #[unstable(feature = "try_trait", issue = "42327")] +#[cfg(bootstrap)] impl<T, E> ops::TryV1 for Result<T, E> { type Output = T; type Error = E; diff --git a/library/core/src/task/poll.rs b/library/core/src/task/poll.rs index 188639e7962..9cf89623d88 100644 --- a/library/core/src/task/poll.rs +++ b/library/core/src/task/poll.rs @@ -129,6 +129,7 @@ impl<T> From<T> for Poll<T> { } #[stable(feature = "futures_api", since = "1.36.0")] +#[cfg(bootstrap)] impl<T, E> ops::TryV1 for Poll<Result<T, E>> { type Output = Poll<T>; type Error = E; @@ -184,6 +185,7 @@ impl<T, E, F: From<E>> ops::FromResidual<Result<convert::Infallible, E>> for Pol } #[stable(feature = "futures_api", since = "1.36.0")] +#[cfg(bootstrap)] impl<T, E> ops::TryV1 for Poll<Option<Result<T, E>>> { type Output = Poll<Option<T>>; type Error = E; diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index db12d79c00c..16bce6e35fe 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -44,7 +44,6 @@ #![feature(str_internals)] #![feature(test)] #![feature(trusted_len)] -#![feature(try_trait)] #![feature(try_trait_v2)] #![feature(slice_internals)] #![feature(slice_partition_dedup)] diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index a8f3412c611..b838a655346 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -526,7 +526,12 @@ pub trait Read { /// /// 1. This reader has reached its "end of file" and will likely no longer /// be able to produce bytes. Note that this does not mean that the - /// reader will *always* no longer be able to produce bytes. + /// reader will *always* no longer be able to produce bytes. As an example, + /// on Linux, this method will call the `recv` syscall for a [`TcpStream`], + /// where returning zero indicates the connection was shut down correctly. While + /// for [`File`], it is possible to reach the end of file and get zero as result, + /// but if more data is appended to the file, future calls to `read` will return + /// more data. /// 2. The buffer specified was 0 bytes in length. /// /// It is not an error if the returned value `n` is smaller than the buffer size, @@ -568,6 +573,7 @@ pub trait Read { /// /// [`Ok(n)`]: Ok /// [`File`]: crate::fs::File + /// [`TcpStream`]: crate::net::TcpStream /// /// ```no_run /// use std::io; diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs index 9cf51be2836..913c71d4108 100644 --- a/library/std/src/os/unix/fs.rs +++ b/library/std/src/os/unix/fs.rs @@ -906,7 +906,7 @@ impl DirBuilderExt for fs::DirBuilder { /// } /// ``` #[unstable(feature = "unix_chroot", issue = "84715")] -#[cfg(not(target_os = "fuchsia"))] +#[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))] pub fn chroot<P: AsRef<Path>>(dir: P) -> io::Result<()> { sys::fs::chroot(dir.as_ref()) } diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs index 96ab32104ea..d05b2ffc4e8 100644 --- a/library/std/src/primitive_docs.rs +++ b/library/std/src/primitive_docs.rs @@ -345,6 +345,9 @@ mod prim_never {} mod prim_char {} #[doc(primitive = "unit")] +#[doc(alias = "(")] +#[doc(alias = ")")] +#[doc(alias = "()")] // /// The `()` type, also called "unit". /// diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index ef14865fbcd..f8ca67c844c 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -1329,7 +1329,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { Ok(bytes_copied as u64) } -#[cfg(not(target_os = "fuchsia"))] +#[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))] pub fn chroot(dir: &Path) -> io::Result<()> { let dir = cstr(dir)?; cvt(unsafe { libc::chroot(dir.as_ptr()) })?; diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 7f8065e7500..ed55e1aa715 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -495,7 +495,7 @@ impl ExitStatus { pub fn exit_ok(&self) -> Result<(), ExitStatusError> { // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is - // true on all actual versios of Unix, is widely assumed, and is specified in SuS + // true on all actual versions of Unix, is widely assumed, and is specified in SuS // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html . If it is not // true for a platform pretending to be Unix, the tests (our doctests, and also // procsss_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too. diff --git a/library/std/src/sys/unix/process/process_vxworks.rs b/library/std/src/sys/unix/process/process_vxworks.rs index eecdb624b9c..c17822f5125 100644 --- a/library/std/src/sys/unix/process/process_vxworks.rs +++ b/library/std/src/sys/unix/process/process_vxworks.rs @@ -1,5 +1,8 @@ +use crate::convert::{TryFrom, TryInto}; use crate::fmt; use crate::io::{self, Error, ErrorKind}; +use crate::num::NonZeroI32; +use crate::os::raw::NonZero_c_int; use crate::sys; use crate::sys::cvt; use crate::sys::process::process_common::*; @@ -187,8 +190,16 @@ impl ExitStatus { libc::WIFEXITED(self.0) } - pub fn success(&self) -> bool { - self.code() == Some(0) + pub fn exit_ok(&self) -> Result<(), ExitStatusError> { + // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is + // true on all actual versions of Unix, is widely assumed, and is specified in SuS + // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html . If it is not + // true for a platform pretending to be Unix, the tests (our doctests, and also + // procsss_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too. + match NonZero_c_int::try_from(self.0) { + Ok(failure) => Err(ExitStatusError(failure)), + Err(_) => Ok(()), + } } pub fn code(&self) -> Option<i32> { @@ -235,3 +246,18 @@ impl fmt::Display for ExitStatus { } } } + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitStatusError(NonZero_c_int); + +impl Into<ExitStatus> for ExitStatusError { + fn into(self) -> ExitStatus { + ExitStatus(self.0.into()) + } +} + +impl ExitStatusError { + pub fn code(self) -> Option<NonZeroI32> { + ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap()) + } +} diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index 50d6e8cf27a..b7efc884473 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -631,7 +631,7 @@ pub struct timeval { pub tv_usec: c_long, } -// Functions forbidden when targeting UWP +// Desktop specific functions & types cfg_if::cfg_if! { if #[cfg(not(target_vendor = "uwp"))] { pub const EXCEPTION_CONTINUE_SEARCH: LONG = 0; @@ -645,7 +645,7 @@ if #[cfg(not(target_vendor = "uwp"))] { pub ExceptionRecord: *mut EXCEPTION_RECORD, pub ExceptionAddress: LPVOID, pub NumberParameters: DWORD, - pub ExceptionInformation: [LPVOID; EXCEPTION_MAXIMUM_PARAMETERS] + pub ExceptionInformation: [LPVOID; EXCEPTION_MAXIMUM_PARAMETERS], } pub enum CONTEXT {} @@ -656,8 +656,8 @@ if #[cfg(not(target_vendor = "uwp"))] { pub ContextRecord: *mut CONTEXT, } - pub type PVECTORED_EXCEPTION_HANDLER = extern "system" - fn(ExceptionInfo: *mut EXCEPTION_POINTERS) -> LONG; + pub type PVECTORED_EXCEPTION_HANDLER = + extern "system" fn(ExceptionInfo: *mut EXCEPTION_POINTERS) -> LONG; #[repr(C)] #[derive(Copy, Clone)] @@ -691,44 +691,66 @@ if #[cfg(not(target_vendor = "uwp"))] { pub const TOKEN_READ: DWORD = 0x20008; + #[link(name = "advapi32")] extern "system" { + // Forbidden when targeting UWP #[link_name = "SystemFunction036"] pub fn RtlGenRandom(RandomBuffer: *mut u8, RandomBufferLength: ULONG) -> BOOLEAN; - pub fn ReadConsoleW(hConsoleInput: HANDLE, - lpBuffer: LPVOID, - nNumberOfCharsToRead: DWORD, - lpNumberOfCharsRead: LPDWORD, - pInputControl: PCONSOLE_READCONSOLE_CONTROL) -> BOOL; + // Allowed but unused by UWP + pub fn OpenProcessToken( + ProcessHandle: HANDLE, + DesiredAccess: DWORD, + TokenHandle: *mut HANDLE, + ) -> BOOL; + } - pub fn WriteConsoleW(hConsoleOutput: HANDLE, - lpBuffer: LPCVOID, - nNumberOfCharsToWrite: DWORD, - lpNumberOfCharsWritten: LPDWORD, - lpReserved: LPVOID) -> BOOL; + #[link(name = "userenv")] + extern "system" { + // Allowed but unused by UWP + pub fn GetUserProfileDirectoryW( + hToken: HANDLE, + lpProfileDir: LPWSTR, + lpcchSize: *mut DWORD, + ) -> BOOL; + } - pub fn GetConsoleMode(hConsoleHandle: HANDLE, - lpMode: LPDWORD) -> BOOL; + #[link(name = "kernel32")] + extern "system" { + // Functions forbidden when targeting UWP + pub fn ReadConsoleW( + hConsoleInput: HANDLE, + lpBuffer: LPVOID, + nNumberOfCharsToRead: DWORD, + lpNumberOfCharsRead: LPDWORD, + pInputControl: PCONSOLE_READCONSOLE_CONTROL, + ) -> BOOL; + + pub fn WriteConsoleW( + hConsoleOutput: HANDLE, + lpBuffer: LPCVOID, + nNumberOfCharsToWrite: DWORD, + lpNumberOfCharsWritten: LPDWORD, + lpReserved: LPVOID, + ) -> BOOL; + + pub fn GetConsoleMode(hConsoleHandle: HANDLE, lpMode: LPDWORD) -> BOOL; // Allowed but unused by UWP - pub fn OpenProcessToken(ProcessHandle: HANDLE, - DesiredAccess: DWORD, - TokenHandle: *mut HANDLE) -> BOOL; - pub fn GetUserProfileDirectoryW(hToken: HANDLE, - lpProfileDir: LPWSTR, - lpcchSize: *mut DWORD) -> BOOL; - pub fn GetFileInformationByHandle(hFile: HANDLE, - lpFileInformation: LPBY_HANDLE_FILE_INFORMATION) - -> BOOL; - pub fn SetHandleInformation(hObject: HANDLE, - dwMask: DWORD, - dwFlags: DWORD) -> BOOL; - pub fn AddVectoredExceptionHandler(FirstHandler: ULONG, - VectoredHandler: PVECTORED_EXCEPTION_HANDLER) - -> LPVOID; - pub fn CreateHardLinkW(lpSymlinkFileName: LPCWSTR, - lpTargetFileName: LPCWSTR, - lpSecurityAttributes: LPSECURITY_ATTRIBUTES) - -> BOOL; + pub fn GetFileInformationByHandle( + hFile: HANDLE, + lpFileInformation: LPBY_HANDLE_FILE_INFORMATION, + ) -> BOOL; + pub fn SetHandleInformation(hObject: HANDLE, dwMask: DWORD, dwFlags: DWORD) -> BOOL; + pub fn AddVectoredExceptionHandler( + FirstHandler: ULONG, + VectoredHandler: PVECTORED_EXCEPTION_HANDLER, + ) -> LPVOID; + pub fn CreateHardLinkW( + lpSymlinkFileName: LPCWSTR, + lpTargetFileName: LPCWSTR, + lpSecurityAttributes: LPSECURITY_ATTRIBUTES, + ) -> BOOL; + pub fn SetThreadStackGuarantee(_size: *mut c_ulong) -> BOOL; } } } @@ -747,55 +769,32 @@ if #[cfg(target_vendor = "uwp")] { pub Directory: BOOLEAN, } + #[link(name = "bcrypt")] extern "system" { - pub fn GetFileInformationByHandleEx(hFile: HANDLE, - fileInfoClass: FILE_INFO_BY_HANDLE_CLASS, - lpFileInformation: LPVOID, - dwBufferSize: DWORD) -> BOOL; - pub fn BCryptGenRandom(hAlgorithm: LPVOID, pBuffer: *mut u8, - cbBuffer: ULONG, dwFlags: ULONG) -> LONG; + pub fn BCryptGenRandom( + hAlgorithm: LPVOID, + pBuffer: *mut u8, + cbBuffer: ULONG, + dwFlags: ULONG, + ) -> LONG; + } + #[link(name = "kernel32")] + extern "system" { + pub fn GetFileInformationByHandleEx( + hFile: HANDLE, + fileInfoClass: FILE_INFO_BY_HANDLE_CLASS, + lpFileInformation: LPVOID, + dwBufferSize: DWORD, + ) -> BOOL; } } } // Shared between Desktop & UWP + +#[link(name = "kernel32")] extern "system" { - pub fn WSAStartup(wVersionRequested: WORD, lpWSAData: LPWSADATA) -> c_int; - pub fn WSACleanup() -> c_int; - pub fn WSAGetLastError() -> c_int; - pub fn WSADuplicateSocketW( - s: SOCKET, - dwProcessId: DWORD, - lpProtocolInfo: LPWSAPROTOCOL_INFO, - ) -> c_int; - pub fn WSASend( - s: SOCKET, - lpBuffers: LPWSABUF, - dwBufferCount: DWORD, - lpNumberOfBytesSent: LPDWORD, - dwFlags: DWORD, - lpOverlapped: LPWSAOVERLAPPED, - lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE, - ) -> c_int; - pub fn WSARecv( - s: SOCKET, - lpBuffers: LPWSABUF, - dwBufferCount: DWORD, - lpNumberOfBytesRecvd: LPDWORD, - lpFlags: LPDWORD, - lpOverlapped: LPWSAOVERLAPPED, - lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE, - ) -> c_int; pub fn GetCurrentProcessId() -> DWORD; - pub fn WSASocketW( - af: c_int, - kind: c_int, - protocol: c_int, - lpProtocolInfo: LPWSAPROTOCOL_INFO, - g: GROUP, - dwFlags: DWORD, - ) -> SOCKET; - pub fn ioctlsocket(s: SOCKET, cmd: c_long, argp: *mut c_ulong) -> c_int; pub fn InitializeCriticalSection(CriticalSection: *mut CRITICAL_SECTION); pub fn EnterCriticalSection(CriticalSection: *mut CRITICAL_SECTION); pub fn TryEnterCriticalSection(CriticalSection: *mut CRITICAL_SECTION) -> BOOL; @@ -882,28 +881,6 @@ extern "system" { pub fn DeleteFileW(lpPathName: LPCWSTR) -> BOOL; pub fn GetCurrentDirectoryW(nBufferLength: DWORD, lpBuffer: LPWSTR) -> DWORD; pub fn SetCurrentDirectoryW(lpPathName: LPCWSTR) -> BOOL; - - pub fn closesocket(socket: SOCKET) -> c_int; - pub fn recv(socket: SOCKET, buf: *mut c_void, len: c_int, flags: c_int) -> c_int; - pub fn send(socket: SOCKET, buf: *const c_void, len: c_int, flags: c_int) -> c_int; - pub fn recvfrom( - socket: SOCKET, - buf: *mut c_void, - len: c_int, - flags: c_int, - addr: *mut SOCKADDR, - addrlen: *mut c_int, - ) -> c_int; - pub fn sendto( - socket: SOCKET, - buf: *const c_void, - len: c_int, - flags: c_int, - addr: *const SOCKADDR, - addrlen: c_int, - ) -> c_int; - pub fn shutdown(socket: SOCKET, how: c_int) -> c_int; - pub fn accept(socket: SOCKET, address: *mut SOCKADDR, address_len: *mut c_int) -> SOCKET; pub fn DuplicateHandle( hSourceProcessHandle: HANDLE, hSourceHandle: HANDLE, @@ -950,32 +927,6 @@ extern "system" { pub fn FindFirstFileW(fileName: LPCWSTR, findFileData: LPWIN32_FIND_DATAW) -> HANDLE; pub fn FindNextFileW(findFile: HANDLE, findFileData: LPWIN32_FIND_DATAW) -> BOOL; pub fn FindClose(findFile: HANDLE) -> BOOL; - pub fn getsockopt( - s: SOCKET, - level: c_int, - optname: c_int, - optval: *mut c_char, - optlen: *mut c_int, - ) -> c_int; - pub fn setsockopt( - s: SOCKET, - level: c_int, - optname: c_int, - optval: *const c_void, - optlen: c_int, - ) -> c_int; - pub fn getsockname(socket: SOCKET, address: *mut SOCKADDR, address_len: *mut c_int) -> c_int; - pub fn getpeername(socket: SOCKET, address: *mut SOCKADDR, address_len: *mut c_int) -> c_int; - pub fn bind(socket: SOCKET, address: *const SOCKADDR, address_len: socklen_t) -> c_int; - pub fn listen(socket: SOCKET, backlog: c_int) -> c_int; - pub fn connect(socket: SOCKET, address: *const SOCKADDR, len: c_int) -> c_int; - pub fn getaddrinfo( - node: *const c_char, - service: *const c_char, - hints: *const ADDRINFOA, - res: *mut *mut ADDRINFOA, - ) -> c_int; - pub fn freeaddrinfo(res: *mut ADDRINFOA); pub fn GetProcAddress(handle: HMODULE, name: LPCSTR) -> *mut c_void; pub fn GetModuleHandleA(lpModuleName: LPCSTR) -> HMODULE; @@ -1012,47 +963,23 @@ extern "system" { lpNumberOfBytesTransferred: LPDWORD, bWait: BOOL, ) -> BOOL; - pub fn select( - nfds: c_int, - readfds: *mut fd_set, - writefds: *mut fd_set, - exceptfds: *mut fd_set, - timeout: *const timeval, - ) -> c_int; - - // >= Vista / Server 2008 - // https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createsymboliclinkw pub fn CreateSymbolicLinkW( lpSymlinkFileName: LPCWSTR, lpTargetFileName: LPCWSTR, dwFlags: DWORD, ) -> BOOLEAN; - - // >= Vista / Server 2008 - // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew pub fn GetFinalPathNameByHandleW( hFile: HANDLE, lpszFilePath: LPCWSTR, cchFilePath: DWORD, dwFlags: DWORD, ) -> DWORD; - - // >= Vista / Server 2003 - // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadstackguarantee - #[cfg(not(target_vendor = "uwp"))] - pub fn SetThreadStackGuarantee(_size: *mut c_ulong) -> BOOL; - - // >= Vista / Server 2008 - // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-setfileinformationbyhandle pub fn SetFileInformationByHandle( hFile: HANDLE, FileInformationClass: FILE_INFO_BY_HANDLE_CLASS, lpFileInformation: LPVOID, dwBufferSize: DWORD, ) -> BOOL; - - // >= Vista / Server 2008 - // https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-sleepconditionvariablesrw pub fn SleepConditionVariableSRW( ConditionVariable: PCONDITION_VARIABLE, SRWLock: PSRWLOCK, @@ -1060,13 +987,9 @@ extern "system" { Flags: ULONG, ) -> BOOL; - // >= Vista / Server 2008 - // https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-wakeconditionvariable pub fn WakeConditionVariable(ConditionVariable: PCONDITION_VARIABLE); pub fn WakeAllConditionVariable(ConditionVariable: PCONDITION_VARIABLE); - // >= Vista / Server 2008 - // https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-acquiresrwlockexclusive pub fn AcquireSRWLockExclusive(SRWLock: PSRWLOCK); pub fn AcquireSRWLockShared(SRWLock: PSRWLOCK); pub fn ReleaseSRWLockExclusive(SRWLock: PSRWLOCK); @@ -1075,6 +998,99 @@ extern "system" { pub fn TryAcquireSRWLockShared(SRWLock: PSRWLOCK) -> BOOLEAN; } +#[link(name = "ws2_32")] +extern "system" { + pub fn WSAStartup(wVersionRequested: WORD, lpWSAData: LPWSADATA) -> c_int; + pub fn WSACleanup() -> c_int; + pub fn WSAGetLastError() -> c_int; + pub fn WSADuplicateSocketW( + s: SOCKET, + dwProcessId: DWORD, + lpProtocolInfo: LPWSAPROTOCOL_INFO, + ) -> c_int; + pub fn WSASend( + s: SOCKET, + lpBuffers: LPWSABUF, + dwBufferCount: DWORD, + lpNumberOfBytesSent: LPDWORD, + dwFlags: DWORD, + lpOverlapped: LPWSAOVERLAPPED, + lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE, + ) -> c_int; + pub fn WSARecv( + s: SOCKET, + lpBuffers: LPWSABUF, + dwBufferCount: DWORD, + lpNumberOfBytesRecvd: LPDWORD, + lpFlags: LPDWORD, + lpOverlapped: LPWSAOVERLAPPED, + lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE, + ) -> c_int; + pub fn WSASocketW( + af: c_int, + kind: c_int, + protocol: c_int, + lpProtocolInfo: LPWSAPROTOCOL_INFO, + g: GROUP, + dwFlags: DWORD, + ) -> SOCKET; + pub fn ioctlsocket(s: SOCKET, cmd: c_long, argp: *mut c_ulong) -> c_int; + pub fn closesocket(socket: SOCKET) -> c_int; + pub fn recv(socket: SOCKET, buf: *mut c_void, len: c_int, flags: c_int) -> c_int; + pub fn send(socket: SOCKET, buf: *const c_void, len: c_int, flags: c_int) -> c_int; + pub fn recvfrom( + socket: SOCKET, + buf: *mut c_void, + len: c_int, + flags: c_int, + addr: *mut SOCKADDR, + addrlen: *mut c_int, + ) -> c_int; + pub fn sendto( + socket: SOCKET, + buf: *const c_void, + len: c_int, + flags: c_int, + addr: *const SOCKADDR, + addrlen: c_int, + ) -> c_int; + pub fn shutdown(socket: SOCKET, how: c_int) -> c_int; + pub fn accept(socket: SOCKET, address: *mut SOCKADDR, address_len: *mut c_int) -> SOCKET; + pub fn getsockopt( + s: SOCKET, + level: c_int, + optname: c_int, + optval: *mut c_char, + optlen: *mut c_int, + ) -> c_int; + pub fn setsockopt( + s: SOCKET, + level: c_int, + optname: c_int, + optval: *const c_void, + optlen: c_int, + ) -> c_int; + pub fn getsockname(socket: SOCKET, address: *mut SOCKADDR, address_len: *mut c_int) -> c_int; + pub fn getpeername(socket: SOCKET, address: *mut SOCKADDR, address_len: *mut c_int) -> c_int; + pub fn bind(socket: SOCKET, address: *const SOCKADDR, address_len: socklen_t) -> c_int; + pub fn listen(socket: SOCKET, backlog: c_int) -> c_int; + pub fn connect(socket: SOCKET, address: *const SOCKADDR, len: c_int) -> c_int; + pub fn getaddrinfo( + node: *const c_char, + service: *const c_char, + hints: *const ADDRINFOA, + res: *mut *mut ADDRINFOA, + ) -> c_int; + pub fn freeaddrinfo(res: *mut ADDRINFOA); + pub fn select( + nfds: c_int, + readfds: *mut fd_set, + writefds: *mut fd_set, + exceptfds: *mut fd_set, + timeout: *const timeval, + ) -> c_int; +} + // Functions that aren't available on every version of Windows that we support, // but we still use them and just provide some form of a fallback implementation. compat_fn! { diff --git a/library/std/src/sys/windows/mod.rs b/library/std/src/sys/windows/mod.rs index cc60ca375ea..f23e874f249 100644 --- a/library/std/src/sys/windows/mod.rs +++ b/library/std/src/sys/windows/mod.rs @@ -281,17 +281,3 @@ pub fn abort_internal() -> ! { } crate::intrinsics::abort(); } - -cfg_if::cfg_if! { - if #[cfg(target_vendor = "uwp")] { - #[link(name = "ws2_32")] - // For BCryptGenRandom - #[link(name = "bcrypt")] - extern "C" {} - } else { - #[link(name = "advapi32")] - #[link(name = "ws2_32")] - #[link(name = "userenv")] - extern "C" {} - } -} diff --git a/src/bootstrap/bin/rustc.rs b/src/bootstrap/bin/rustc.rs index d462dc4d116..4b98abfb308 100644 --- a/src/bootstrap/bin/rustc.rs +++ b/src/bootstrap/bin/rustc.rs @@ -305,7 +305,7 @@ fn format_rusage_data(_child: Child) -> Option<String> { }; // Mac OS X reports the maxrss in bytes, not kb. let divisor = if env::consts::OS == "macos" { 1024 } else { 1 }; - let maxrss = rusage.ru_maxrss + (divisor - 1) / divisor; + let maxrss = (rusage.ru_maxrss + (divisor - 1)) / divisor; let mut init_str = format!( "user: {USER_SEC}.{USER_USEC:03} \ diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 149a899cef7..bd5b3797ea8 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -991,28 +991,20 @@ class RustBuild(object): ).decode(default_encoding).splitlines()] filtered_submodules = [] submodules_names = [] - llvm_checked_out = os.path.exists(os.path.join(self.rust_root, "src/llvm-project/.git")) - external_llvm_provided = self.get_toml('llvm-config') or self.downloading_llvm() - llvm_needed = not self.get_toml('codegen-backends', 'rust') \ - or "llvm" in self.get_toml('codegen-backends', 'rust') for module in submodules: + # This is handled by native::Llvm in rustbuild, not here if module.endswith("llvm-project"): - # Don't sync the llvm-project submodule if an external LLVM was - # provided, if we are downloading LLVM or if the LLVM backend is - # not being built. Also, if the submodule has been initialized - # already, sync it anyways so that it doesn't mess up contributor - # pull requests. - if external_llvm_provided or not llvm_needed: - if self.get_toml('lld') != 'true' and not llvm_checked_out: - continue + continue check = self.check_submodule(module, slow_submodules) filtered_submodules.append((module, check)) submodules_names.append(module) recorded = subprocess.Popen(["git", "ls-tree", "HEAD"] + submodules_names, cwd=self.rust_root, stdout=subprocess.PIPE) recorded = recorded.communicate()[0].decode(default_encoding).strip().splitlines() + # { filename: hash } recorded_submodules = {} for data in recorded: + # [mode, kind, hash, filename] data = data.split() recorded_submodules[data[3]] = data[2] for module in filtered_submodules: diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 2960dd3df6b..a351290a420 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -472,12 +472,22 @@ impl Build { slice::from_ref(&self.build.triple) } + /// If the LLVM submodule has been initialized already, sync it unconditionally. This avoids + /// contributors checking in a submodule change by accident. + pub fn maybe_update_llvm_submodule(&self) { + if self.in_tree_llvm_info.is_git() { + native::update_llvm_submodule(self); + } + } + /// Executes the entire build, as configured by the flags and configuration. pub fn build(&mut self) { unsafe { job::setup(self); } + self.maybe_update_llvm_submodule(); + if let Subcommand::Format { check, paths } = &self.config.cmd { return format::format(self, *check, &paths); } diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index bde0a96f030..44c281efe22 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -21,7 +21,7 @@ use build_helper::{output, t}; use crate::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::config::TargetSelection; use crate::util::{self, exe}; -use crate::GitRepo; +use crate::{Build, GitRepo}; use build_helper::up_to_date; pub struct Meta { @@ -91,6 +91,85 @@ pub fn prebuilt_llvm_config( Err(Meta { stamp, build_llvm_config, out_dir, root: root.into() }) } +// modified from `check_submodule` and `update_submodule` in bootstrap.py +pub(crate) fn update_llvm_submodule(build: &Build) { + let llvm_project = &Path::new("src").join("llvm-project"); + + fn dir_is_empty(dir: &Path) -> bool { + t!(std::fs::read_dir(dir)).next().is_none() + } + + // NOTE: The check for the empty directory is here because when running x.py + // the first time, the llvm submodule won't be checked out. Check it out + // now so we can build it. + if !build.in_tree_llvm_info.is_git() && !dir_is_empty(&build.config.src.join(llvm_project)) { + return; + } + + // check_submodule + let checked_out = if build.config.fast_submodules { + Some(output( + Command::new("git") + .args(&["rev-parse", "HEAD"]) + .current_dir(build.config.src.join(llvm_project)), + )) + } else { + None + }; + + // update_submodules + let recorded = output( + Command::new("git") + .args(&["ls-tree", "HEAD"]) + .arg(llvm_project) + .current_dir(&build.config.src), + ); + let hash = + recorded.split(' ').nth(2).unwrap_or_else(|| panic!("unexpected output `{}`", recorded)); + + // update_submodule + if let Some(llvm_hash) = checked_out { + if hash == llvm_hash { + // already checked out + return; + } + } + + println!("Updating submodule {}", llvm_project.display()); + build.run( + Command::new("git") + .args(&["submodule", "-q", "sync"]) + .arg(llvm_project) + .current_dir(&build.config.src), + ); + + // Try passing `--progress` to start, then run git again without if that fails. + let update = |progress: bool| { + let mut git = Command::new("git"); + git.args(&["submodule", "update", "--init", "--recursive"]); + if progress { + git.arg("--progress"); + } + git.arg(llvm_project).current_dir(&build.config.src); + git + }; + // NOTE: doesn't use `try_run` because this shouldn't print an error if it fails. + if !update(true).status().map_or(false, |status| status.success()) { + build.run(&mut update(false)); + } + + build.run( + Command::new("git") + .args(&["reset", "-q", "--hard"]) + .current_dir(build.config.src.join(llvm_project)), + ); + build.run( + Command::new("git") + .args(&["clean", "-qdfx"]) + .current_dir(build.config.src.join(llvm_project)), + ); +} + #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct Llvm { pub target: TargetSelection, @@ -128,6 +207,9 @@ impl Step for Llvm { Err(m) => m, }; + if !builder.config.dry_run { + update_llvm_submodule(builder); + } if builder.config.llvm_link_shared && (target.contains("windows") || target.contains("apple-darwin")) { diff --git a/src/build_helper/lib.rs b/src/build_helper/lib.rs index 80f804174ed..b1ec072f3f8 100644 --- a/src/build_helper/lib.rs +++ b/src/build_helper/lib.rs @@ -130,6 +130,7 @@ pub fn make(host: &str) -> PathBuf { } } +#[track_caller] pub fn output(cmd: &mut Command) -> String { let output = match cmd.stderr(Stdio::inherit()).output() { Ok(status) => status, diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index b816b9d7188..5ce75949457 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -10,7 +10,7 @@ path = "lib.rs" [dependencies] arrayvec = { version = "0.7", default-features = false } pulldown-cmark = { version = "0.8", default-features = false } -minifier = "0.0.39" +minifier = "0.0.41" rayon = { version = "0.3.0", package = "rustc-rayon" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index 3bfc9fea62e..35ff57f85a5 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -561,7 +561,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { } WherePredicate::EqPredicate { lhs, rhs } => { match lhs { - Type::QPath { name: left_name, ref self_type, ref trait_ } => { + Type::QPath { name: left_name, ref self_type, ref trait_, .. } => { let ty = &*self_type; match **trait_ { Type::ResolvedPath { diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 6d05ac073cc..a14eefaf571 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -588,6 +588,7 @@ fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean: self_type: box clean::Generic(ref s), trait_: box clean::ResolvedPath { did, .. }, name: ref _name, + .. }, ref bounds, } => !(bounds.is_empty() || *s == kw::SelfUpper && did == trait_did), diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index feeb03b1b67..231f13adeb6 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -418,9 +418,11 @@ impl<'tcx> Clean<Type> for ty::ProjectionTy<'tcx> { GenericBound::TraitBound(t, _) => t.trait_, GenericBound::Outlives(_) => panic!("cleaning a trait got a lifetime"), }; + let self_type = self.self_ty().clean(cx); Type::QPath { name: cx.tcx.associated_item(self.item_def_id).ident.name, - self_type: box self.self_ty().clean(cx), + self_def_id: self_type.def_id(), + self_type: box self_type, trait_: box trait_, } } @@ -1104,7 +1106,7 @@ impl Clean<Item> for ty::AssocItem { .filter_map(|pred| { let (name, self_type, trait_, bounds) = match *pred { WherePredicate::BoundPredicate { - ty: QPath { ref name, ref self_type, ref trait_ }, + ty: QPath { ref name, ref self_type, ref trait_, .. }, ref bounds, } => (name, self_type, trait_, bounds), _ => return None, @@ -1282,16 +1284,15 @@ fn clean_qpath(hir_ty: &hir::Ty<'_>, cx: &mut DocContext<'_>) -> Type { let segments = if p.is_global() { &p.segments[1..] } else { &p.segments }; let trait_segments = &segments[..segments.len() - 1]; + let trait_def = cx.tcx.associated_item(p.res.def_id()).container.id(); let trait_path = self::Path { global: p.is_global(), - res: Res::Def( - DefKind::Trait, - cx.tcx.associated_item(p.res.def_id()).container.id(), - ), + res: Res::Def(DefKind::Trait, trait_def), segments: trait_segments.clean(cx), }; Type::QPath { name: p.segments.last().expect("segments were empty").ident.name, + self_def_id: Some(DefId::local(qself.hir_id.owner.local_def_index)), self_type: box qself.clean(cx), trait_: box resolve_type(cx, trait_path, hir_id), } @@ -1306,6 +1307,7 @@ fn clean_qpath(hir_ty: &hir::Ty<'_>, cx: &mut DocContext<'_>) -> Type { let trait_path = hir::Path { span, res, segments: &[] }.clean(cx); Type::QPath { name: segment.ident.name, + self_def_id: res.opt_def_id(), self_type: box qself.clean(cx), trait_: box resolve_type(cx, trait_path, hir_id), } diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index edd3d77eeb7..de88e249b67 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1519,6 +1519,7 @@ crate enum Type { QPath { name: Symbol, self_type: Box<Type>, + self_def_id: Option<DefId>, trait_: Box<Type>, }, @@ -1665,7 +1666,7 @@ impl Type { crate fn projection(&self) -> Option<(&Type, DefId, Symbol)> { let (self_, trait_, name) = match self { - QPath { self_type, trait_, name } => (self_type, trait_, name), + QPath { self_type, trait_, name, .. } => (self_type, trait_, name), _ => return None, }; let trait_did = match **trait_ { diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 51a011cf197..2c31a502565 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -175,8 +175,9 @@ crate fn strip_type(ty: Type) -> Type { Type::BorrowedRef { lifetime, mutability, type_ } => { Type::BorrowedRef { lifetime, mutability, type_: Box::new(strip_type(*type_)) } } - Type::QPath { name, self_type, trait_ } => Type::QPath { + Type::QPath { name, self_type, trait_, self_def_id } => Type::QPath { name, + self_def_id, self_type: Box::new(strip_type(*self_type)), trait_: Box::new(strip_type(*trait_)), }, diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index fa57c9bda74..3c1d03a78f1 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -18,7 +18,7 @@ use rustc_span::def_id::CRATE_DEF_INDEX; use rustc_target::spec::abi::Abi; use crate::clean::{ - self, utils::find_nearest_parent_module, ExternalCrate, FakeDefId, PrimitiveType, + self, utils::find_nearest_parent_module, ExternalCrate, FakeDefId, GetDefId, PrimitiveType, }; use crate::formats::item_type::ItemType; use crate::html::escape::Escape; @@ -836,10 +836,13 @@ fn fmt_type<'cx>( write!(f, "impl {}", print_generic_bounds(bounds, cx)) } } - clean::QPath { ref name, ref self_type, ref trait_ } => { + clean::QPath { ref name, ref self_type, ref trait_, ref self_def_id } => { let should_show_cast = match *trait_ { box clean::ResolvedPath { ref path, .. } => { - !path.segments.is_empty() && !self_type.is_self_type() + !path.segments.is_empty() + && self_def_id + .zip(trait_.def_id()) + .map_or(!self_type.is_self_type(), |(id, trait_)| id != trait_) } _ => true, }; diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 318e1b44f86..29b10fb8457 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1542,24 +1542,33 @@ fn render_impl( } } let toggled = !impl_items.is_empty() || !default_impl_items.is_empty(); - let open_details = |close_tags: &mut String| { + let open_details = |close_tags: &mut String, is_collapsed: bool| { if toggled { close_tags.insert_str(0, "</details>"); - "<details class=\"rustdoc-toggle implementors-toggle\" open><summary>" + if is_collapsed { + "<details class=\"rustdoc-toggle implementors-toggle\"><summary>" + } else { + "<details class=\"rustdoc-toggle implementors-toggle\" open><summary>" + } } else { "" } }; if render_mode == RenderMode::Normal { + let is_implementing_trait; let id = cx.derive_id(match i.inner_impl().trait_ { Some(ref t) => { + is_implementing_trait = true; if is_on_foreign_type { get_id_for_impl_on_foreign_type(&i.inner_impl().for_, t, cx) } else { format!("impl-{}", small_url_encode(format!("{:#}", t.print(cx)))) } } - None => "impl".to_string(), + None => { + is_implementing_trait = false; + "impl".to_string() + } }); let aliases = if aliases.is_empty() { String::new() @@ -1570,7 +1579,7 @@ fn render_impl( write!( w, "{}<h3 id=\"{}\" class=\"impl\"{}><code class=\"in-band\">", - open_details(&mut close_tags), + open_details(&mut close_tags, is_implementing_trait), id, aliases ); @@ -1597,7 +1606,7 @@ fn render_impl( write!( w, "{}<h3 id=\"{}\" class=\"impl\"{}><code class=\"in-band\">{}</code>", - open_details(&mut close_tags), + open_details(&mut close_tags, is_implementing_trait), id, aliases, i.inner_impl().print(false, cx) diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js index 1ed94ec109c..a96b0c87108 100644 --- a/src/librustdoc/html/static/main.js +++ b/src/librustdoc/html/static/main.js @@ -764,138 +764,6 @@ function hideThemeButtonState() { innerToggle.children[0].innerText = labelForToggleButton(sectionIsCollapsed); } - function collapseDocs(toggle, mode) { - if (!toggle || !toggle.parentNode) { - return; - } - - function adjustToggle(arg) { - return function(e) { - if (hasClass(e, "toggle-label")) { - if (arg) { - e.style.display = "inline-block"; - } else { - e.style.display = "none"; - } - } - if (hasClass(e, "inner")) { - e.innerHTML = labelForToggleButton(arg); - } - }; - } - - function implHider(addOrRemove, fullHide) { - return function(n) { - var shouldHide = - fullHide || - hasClass(n, "method") || - hasClass(n, "associatedconstant"); - if (shouldHide || hasClass(n, "type")) { - if (shouldHide) { - if (addOrRemove) { - addClass(n, "hidden-by-impl-hider"); - } else { - removeClass(n, "hidden-by-impl-hider"); - } - } - var ns = n.nextElementSibling; - while (ns && (hasClass(ns, "docblock") || hasClass(ns, "item-info"))) { - if (addOrRemove) { - addClass(ns, "hidden-by-impl-hider"); - } else { - removeClass(ns, "hidden-by-impl-hider"); - } - ns = ns.nextElementSibling; - } - } - }; - } - - var relatedDoc; - var action = mode; - if (!hasClass(toggle.parentNode, "impl")) { - relatedDoc = toggle.parentNode.nextElementSibling; - if (hasClass(relatedDoc, "item-info")) { - relatedDoc = relatedDoc.nextElementSibling; - } - if (hasClass(relatedDoc, "docblock")) { - if (mode === "toggle") { - if (hasClass(relatedDoc, "hidden-by-usual-hider")) { - action = "show"; - } else { - action = "hide"; - } - } - if (action === "hide") { - addClass(relatedDoc, "hidden-by-usual-hider"); - onEachLazy(toggle.childNodes, adjustToggle(true)); - addClass(toggle.parentNode, "collapsed"); - } else if (action === "show") { - removeClass(relatedDoc, "hidden-by-usual-hider"); - removeClass(toggle.parentNode, "collapsed"); - onEachLazy(toggle.childNodes, adjustToggle(false)); - } - } - } else { - // we are collapsing the impl block(s). - - var parentElem = toggle.parentNode; - relatedDoc = parentElem; - var docblock = relatedDoc.nextElementSibling; - - while (!hasClass(relatedDoc, "impl-items")) { - relatedDoc = relatedDoc.nextElementSibling; - } - - if (!relatedDoc && !hasClass(docblock, "docblock")) { - return; - } - - // Hide all functions, but not associated types/consts. - - if (mode === "toggle") { - if (hasClass(relatedDoc, "fns-now-collapsed") || - hasClass(docblock, "hidden-by-impl-hider")) { - action = "show"; - } else { - action = "hide"; - } - } - - var dontApplyBlockRule = toggle.parentNode.parentNode.id !== "main"; - if (action === "show") { - removeClass(relatedDoc, "fns-now-collapsed"); - // Stability/deprecation/portability information is never hidden. - if (!hasClass(docblock, "item-info")) { - removeClass(docblock, "hidden-by-usual-hider"); - } - onEachLazy(toggle.childNodes, adjustToggle(false, dontApplyBlockRule)); - onEachLazy(relatedDoc.childNodes, implHider(false, dontApplyBlockRule)); - } else if (action === "hide") { - addClass(relatedDoc, "fns-now-collapsed"); - // Stability/deprecation/portability information should be shown even when detailed - // info is hidden. - if (!hasClass(docblock, "item-info")) { - addClass(docblock, "hidden-by-usual-hider"); - } - onEachLazy(toggle.childNodes, adjustToggle(true, dontApplyBlockRule)); - onEachLazy(relatedDoc.childNodes, implHider(true, dontApplyBlockRule)); - } - } - } - - function collapseNonInherent(e) { - // inherent impl ids are like "impl" or impl-<number>'. - // they will never be hidden by default. - var n = e.parentElement; - if (n.id.match(/^impl(?:-\d+)?$/) === null) { - // Automatically minimize all non-inherent impls - if (hasClass(n, "impl")) { - collapseDocs(e, "hide"); - } - } - } - function insertAfter(newNode, referenceNode) { referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); } @@ -908,26 +776,29 @@ function hideThemeButtonState() { var hideMethodDocs = getSettingValue("auto-hide-method-docs") === "true"; var hideImplementors = getSettingValue("auto-collapse-implementors") !== "false"; + var hideImplementations = getSettingValue("auto-hide-trait-implementations") !== "false"; var hideLargeItemContents = getSettingValue("auto-hide-large-items") !== "false"; - var impl_list = document.getElementById("trait-implementations-list"); - if (impl_list !== null) { - onEachLazy(impl_list.getElementsByClassName("rustdoc-toggle"), function(e) { - collapseNonInherent(e); - }); + function openImplementors(id) { + var list = document.getElementById(id); + if (list !== null) { + onEachLazy(list.getElementsByClassName("implementors-toggle"), function(e) { + e.open = true; + }); + } } - var blanket_list = document.getElementById("blanket-implementations-list"); - if (blanket_list !== null) { - onEachLazy(blanket_list.getElementsByClassName("rustdoc-toggle"), function(e) { - collapseNonInherent(e); - }); + if (!hideImplementations) { + openImplementors("trait-implementations-list"); + openImplementors("blanket-implementations-list"); } - onEachLazy(document.getElementsByTagName("details"), function (e) { - var showLargeItem = !hideLargeItemContents && hasClass(e, "type-contents-toggle"); - var showImplementor = !hideImplementors && hasClass(e, "implementors-toggle"); - if (showLargeItem || showImplementor) { + if (!hideImplementors) { + openImplementors("implementors-list"); + } + + onEachLazy(document.getElementsByClassName("rustdoc-toggle"), function (e) { + if (!hideLargeItemContents && hasClass(e, "type-contents-toggle")) { e.open = true; } if (hideMethodDocs && hasClass(e, "method-toggle")) { @@ -936,18 +807,6 @@ function hideThemeButtonState() { }); - var currentType = document.getElementsByClassName("type-decl")[0]; - if (currentType) { - currentType = currentType.getElementsByClassName("rust")[0]; - if (currentType) { - onEachLazy(currentType.classList, function(item) { - if (item !== "main") { - return true; - } - }); - } - } - var pageId = getPageId(); if (pageId !== null) { expandSection(pageId); diff --git a/src/librustdoc/html/static/rustdoc.css b/src/librustdoc/html/static/rustdoc.css index 6d581108836..9c5e3e8bfb2 100644 --- a/src/librustdoc/html/static/rustdoc.css +++ b/src/librustdoc/html/static/rustdoc.css @@ -780,12 +780,12 @@ a { float: left; } -tr.result span.primitive::after { +.result-name span.primitive::after { content: ' (primitive type)'; font-style: italic; } -tr.result span.keyword::after { +.result-name span.keyword::after { content: ' (keyword)'; font-style: italic; } diff --git a/src/librustdoc/html/static/search.js b/src/librustdoc/html/static/search.js index 01cfffc5429..c95136d40d3 100644 --- a/src/librustdoc/html/static/search.js +++ b/src/librustdoc/html/static/search.js @@ -885,12 +885,12 @@ window.initSearch = function(rawSearchIndex) { focusSearchResult(); } - // focus the first search result on the active tab, or the result that + // Focus the first search result on the active tab, or the result that // was focused last time this tab was active. function focusSearchResult() { var target = searchState.focusedByTab[searchState.currentTab] || - document.querySelectorAll(".search-results.active a").item(0) || - document.querySelectorAll("#titles > button").item(searchState.currentTab); + document.querySelectorAll(".search-results.active a").item(0) || + document.querySelectorAll("#titles > button").item(searchState.currentTab); if (target) { target.focus(); } @@ -1076,6 +1076,8 @@ window.initSearch = function(rawSearchIndex) { ret_others[0] + ret_in_args[0] + ret_returned[0] + "</div>"; search.innerHTML = output; + // Reset focused elements. + searchState.focusedByTab = [null, null, null]; searchState.showResults(search); var elems = document.getElementById("titles").childNodes; elems[0].onclick = function() { printTab(0); }; @@ -1365,7 +1367,6 @@ window.initSearch = function(rawSearchIndex) { if (e.which === 38) { // up var previous = document.activeElement.previousElementSibling; if (previous) { - console.log("previousElementSibling", previous); previous.focus(); } else { searchState.focus(); diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 5ac43c73646..c6646ba9ae4 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -396,7 +396,7 @@ impl FromWithTcx<clean::Type> for Type { mutable: mutability == ast::Mutability::Mut, type_: Box::new((*type_).into_tcx(tcx)), }, - QPath { name, self_type, trait_ } => Type::QualifiedPath { + QPath { name, self_type, trait_, .. } => Type::QualifiedPath { name: name.to_string(), self_type: Box::new((*self_type).into_tcx(tcx)), trait_: Box::new((*trait_).into_tcx(tcx)), diff --git a/src/test/rustdoc-gui/search-result-keyword.goml b/src/test/rustdoc-gui/search-result-keyword.goml new file mode 100644 index 00000000000..23552c8ce89 --- /dev/null +++ b/src/test/rustdoc-gui/search-result-keyword.goml @@ -0,0 +1,11 @@ +goto: file://|DOC_PATH|/test_docs/index.html +write: (".search-input", "CookieMonster") +// Waiting for the search results to appear... +wait-for: "#titles" +// Note: The two next assert commands could be merged as one but readability would be +// less good. +// +// Checking that the CSS is displaying " (keyword)"... +assert: (".result-name span.keyword::after", {"content": '" (keyword)"'}) +// ... in italic. +assert: (".result-name span.keyword::after", {"font-style": "italic"}) diff --git a/src/test/rustdoc-gui/src/lib.rs b/src/test/rustdoc-gui/src/lib.rs index eeba3e3f907..7b247a19b8e 100644 --- a/src/test/rustdoc-gui/src/lib.rs +++ b/src/test/rustdoc-gui/src/lib.rs @@ -2,6 +2,7 @@ //! documentation generated so we can test each different features. #![crate_name = "test_docs"] +#![feature(doc_keyword)] use std::fmt; @@ -91,3 +92,6 @@ pub fn check_list_code_block() {} pub enum AnEnum { WithVariants { and: usize, sub: usize, variants: usize }, } + +#[doc(keyword = "CookieMonster")] +pub mod keyword {} diff --git a/src/test/rustdoc/issue-85454.rs b/src/test/rustdoc/issue-85454.rs new file mode 100644 index 00000000000..45664dfc382 --- /dev/null +++ b/src/test/rustdoc/issue-85454.rs @@ -0,0 +1,17 @@ +// @has issue_85454/trait.FromResidual.html +// @has - '//pre[@class="rust trait"]' 'pub trait FromResidual<R = <Self as Try>::Residual> { fn from_residual(residual: R) -> Self; }' +pub trait FromResidual<R = <Self as Try>::Residual> { + fn from_residual(residual: R) -> Self; +} + +pub trait Try: FromResidual { + type Output; + type Residual; + fn from_output(output: Self::Output) -> Self; + fn branch(self) -> ControlFlow<Self::Residual, Self::Output>; +} + +pub enum ControlFlow<B, C = ()> { + Continue(C), + Break(B), +} diff --git a/src/test/rustdoc/manual_impl.rs b/src/test/rustdoc/manual_impl.rs index 776a191ceef..5b448bd1923 100644 --- a/src/test/rustdoc/manual_impl.rs +++ b/src/test/rustdoc/manual_impl.rs @@ -65,7 +65,8 @@ impl T for S2 { } // @has manual_impl/struct.S3.html '//*[@class="trait"]' 'T' -// @has - '//details[@open=""]/div[@class="docblock"]' 'Docs associated with the S3 trait implementation.' +// @has - '//details/div[@class="docblock"]' 'Docs associated with the S3 trait implementation.' +// @!has - '//details[@open=""]/div[@class="docblock"]' 'Docs associated with the S3 trait implementation.' // @has - '//details[@open=""]/div[@class="docblock"]' 'Docs associated with the S3 trait b_method implementation.' // @!has - '//div[@class="impl-items"]/details[@open=""]//div[@class="docblock"]' 'Docs associated with the trait a_method definition.' // @has - '//div[@class="impl-items"]/details//div[@class="docblock"]' 'Docs associated with the trait a_method definition.' diff --git a/src/test/ui/cast/cast-ptr-to-int-const.mir.stderr b/src/test/ui/cast/cast-ptr-to-int-const.mir.stderr new file mode 100644 index 00000000000..dcc9a243f0f --- /dev/null +++ b/src/test/ui/cast/cast-ptr-to-int-const.mir.stderr @@ -0,0 +1,19 @@ +error[E0133]: cast of pointer to int is unsafe and requires unsafe function or block + --> $DIR/cast-ptr-to-int-const.rs:10:9 + | +LL | &Y as *const u32 as usize + | ^^^^^^^^^^^^^^^^^^^^^^^^^ cast of pointer to int + | + = note: casting pointers to integers in constants + +error[E0133]: cast of pointer to int is unsafe and requires unsafe function or block + --> $DIR/cast-ptr-to-int-const.rs:17:5 + | +LL | &0 as *const i32 as usize + | ^^^^^^^^^^^^^^^^^^^^^^^^^ cast of pointer to int + | + = note: casting pointers to integers in constants + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/cast/cast-ptr-to-int-const.rs b/src/test/ui/cast/cast-ptr-to-int-const.rs index aed099a53ea..01ea627679d 100644 --- a/src/test/ui/cast/cast-ptr-to-int-const.rs +++ b/src/test/ui/cast/cast-ptr-to-int-const.rs @@ -1,25 +1,19 @@ -// gate-test-const_raw_ptr_to_usize_cast -// revisions: with_feature without_feature +// revisions: mir thir +// [thir]compile-flags: -Z thir-unsafeck -#![cfg_attr(with_feature, feature(const_raw_ptr_to_usize_cast))] +#![feature(const_raw_ptr_to_usize_cast)] fn main() { - const X: usize = unsafe { - main as usize //[without_feature]~ ERROR casting pointers to integers in constants is unstable - }; const Y: u32 = 0; - const Z: usize = unsafe { - &Y as *const u32 as usize //[without_feature]~ ERROR is unstable - }; // Cast in `const` without `unsafe` block const SAFE: usize = { - &Y as *const u32 as usize //[without_feature]~ ERROR is unstable - //[with_feature]~^ ERROR cast of pointer to int is unsafe and requires unsafe + &Y as *const u32 as usize + //~^ ERROR cast of pointer to int is unsafe and requires unsafe }; } // Cast in `const fn` without `unsafe` block const fn test() -> usize { - &0 as *const i32 as usize //[without_feature]~ ERROR is unstable - //[with_feature]~^ ERROR cast of pointer to int is unsafe and requires unsafe + &0 as *const i32 as usize + //~^ ERROR cast of pointer to int is unsafe and requires unsafe } diff --git a/src/test/ui/cast/cast-ptr-to-int-const.thir.stderr b/src/test/ui/cast/cast-ptr-to-int-const.thir.stderr new file mode 100644 index 00000000000..dcc9a243f0f --- /dev/null +++ b/src/test/ui/cast/cast-ptr-to-int-const.thir.stderr @@ -0,0 +1,19 @@ +error[E0133]: cast of pointer to int is unsafe and requires unsafe function or block + --> $DIR/cast-ptr-to-int-const.rs:10:9 + | +LL | &Y as *const u32 as usize + | ^^^^^^^^^^^^^^^^^^^^^^^^^ cast of pointer to int + | + = note: casting pointers to integers in constants + +error[E0133]: cast of pointer to int is unsafe and requires unsafe function or block + --> $DIR/cast-ptr-to-int-const.rs:17:5 + | +LL | &0 as *const i32 as usize + | ^^^^^^^^^^^^^^^^^^^^^^^^^ cast of pointer to int + | + = note: casting pointers to integers in constants + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/cast/feature-gate-const_raw_ptr_to_usize_cast.rs b/src/test/ui/cast/feature-gate-const_raw_ptr_to_usize_cast.rs new file mode 100644 index 00000000000..03e99eb7527 --- /dev/null +++ b/src/test/ui/cast/feature-gate-const_raw_ptr_to_usize_cast.rs @@ -0,0 +1,13 @@ +fn main() { + const X: usize = unsafe { + main as usize //~ ERROR casting pointers to integers in constants is unstable + }; + const Y: u32 = 0; + const Z: usize = unsafe { + &Y as *const u32 as usize //~ ERROR is unstable + }; +} + +const fn test() -> usize { + &0 as *const i32 as usize //~ ERROR is unstable +} diff --git a/src/test/ui/cast/feature-gate-const_raw_ptr_to_usize_cast.stderr b/src/test/ui/cast/feature-gate-const_raw_ptr_to_usize_cast.stderr new file mode 100644 index 00000000000..4a0b424e181 --- /dev/null +++ b/src/test/ui/cast/feature-gate-const_raw_ptr_to_usize_cast.stderr @@ -0,0 +1,30 @@ +error[E0658]: casting pointers to integers in constants is unstable + --> $DIR/feature-gate-const_raw_ptr_to_usize_cast.rs:3:9 + | +LL | main as usize + | ^^^^^^^^^^^^^ + | + = note: see issue #51910 <https://github.com/rust-lang/rust/issues/51910> for more information + = help: add `#![feature(const_raw_ptr_to_usize_cast)]` to the crate attributes to enable + +error[E0658]: casting pointers to integers in constants is unstable + --> $DIR/feature-gate-const_raw_ptr_to_usize_cast.rs:7:9 + | +LL | &Y as *const u32 as usize + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #51910 <https://github.com/rust-lang/rust/issues/51910> for more information + = help: add `#![feature(const_raw_ptr_to_usize_cast)]` to the crate attributes to enable + +error[E0658]: casting pointers to integers in constant functions is unstable + --> $DIR/feature-gate-const_raw_ptr_to_usize_cast.rs:12:5 + | +LL | &0 as *const i32 as usize + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #51910 <https://github.com/rust-lang/rust/issues/51910> for more information + = help: add `#![feature(const_raw_ptr_to_usize_cast)]` to the crate attributes to enable + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/consts/const_fn_unsize.gated.stderr b/src/test/ui/consts/const_fn_unsize.gated.stderr deleted file mode 100644 index 8a65c274ca9..00000000000 --- a/src/test/ui/consts/const_fn_unsize.gated.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: fatal error triggered by #[rustc_error] - --> $DIR/const_fn_unsize.rs:16:1 - | -LL | fn main() {} - | ^^^^^^^^^ - -error: aborting due to previous error - diff --git a/src/test/ui/consts/const_fn_unsize.rs b/src/test/ui/consts/const_fn_unsize.rs index 0cab3b0a031..01da57320c2 100644 --- a/src/test/ui/consts/const_fn_unsize.rs +++ b/src/test/ui/consts/const_fn_unsize.rs @@ -1,16 +1,21 @@ -// gate-test-const_fn_unsize - -// revisions: stock gated - -#![feature(rustc_attrs)] -#![cfg_attr(gated, feature(const_fn_unsize))] +// run-pass +#![feature(slice_ptr_len)] use std::ptr::NonNull; +#[allow(unused)] const fn test() { let _x = NonNull::<[i32; 0]>::dangling() as NonNull<[i32]>; - //[stock]~^ unsizing cast } -#[rustc_error] -fn main() {} //[gated]~ fatal error triggered by #[rustc_error] +// Regression test for #75118. +pub const fn dangling_slice<T>() -> NonNull<[T]> { + NonNull::<[T; 1]>::dangling() +} + +const C: NonNull<[i32]> = dangling_slice(); + +fn main() { + assert_eq!(C.as_ptr(), NonNull::<[i32; 1]>::dangling().as_ptr() as *mut _); + assert_eq!(C.as_ptr().len(), 1); +} diff --git a/src/test/ui/consts/const_fn_unsize.stock.stderr b/src/test/ui/consts/const_fn_unsize.stock.stderr deleted file mode 100644 index cc746d4f175..00000000000 --- a/src/test/ui/consts/const_fn_unsize.stock.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0658]: unsizing casts to types besides slices are not allowed in const fn - --> $DIR/const_fn_unsize.rs:11:14 - | -LL | let _x = NonNull::<[i32; 0]>::dangling() as NonNull<[i32]>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #64992 <https://github.com/rust-lang/rust/issues/64992> for more information - = help: add `#![feature(const_fn_unsize)]` to the crate attributes to enable - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/consts/min_const_fn/min_const_fn.rs b/src/test/ui/consts/min_const_fn/min_const_fn.rs index e46127c36bf..b7904e6841b 100644 --- a/src/test/ui/consts/min_const_fn/min_const_fn.rs +++ b/src/test/ui/consts/min_const_fn/min_const_fn.rs @@ -133,13 +133,13 @@ const fn no_dyn_trait(_x: &dyn std::fmt::Debug) {} //~^ ERROR trait bounds other than `Sized` const fn no_dyn_trait_ret() -> &'static dyn std::fmt::Debug { &() } //~^ ERROR trait bounds other than `Sized` -//~| ERROR unsizing cast -//~| ERROR unsizing cast const fn no_unsafe() { unsafe {} } const fn really_no_traits_i_mean_it() { (&() as &dyn std::fmt::Debug, ()).1 } -//~^ ERROR unsizing cast +//~^ ERROR trait bounds other than `Sized` +//~| ERROR trait bounds other than `Sized` +//~| ERROR trait bounds other than `Sized` const fn no_fn_ptrs(_x: fn()) {} //~^ ERROR function pointer diff --git a/src/test/ui/consts/min_const_fn/min_const_fn.stderr b/src/test/ui/consts/min_const_fn/min_const_fn.stderr index e6ce7e12520..d31d4121936 100644 --- a/src/test/ui/consts/min_const_fn/min_const_fn.stderr +++ b/src/test/ui/consts/min_const_fn/min_const_fn.stderr @@ -288,32 +288,32 @@ LL | const fn no_dyn_trait_ret() -> &'static dyn std::fmt::Debug { &() } = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information = help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable -error[E0658]: unsizing casts to types besides slices are not allowed in const fn - --> $DIR/min_const_fn.rs:134:63 +error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable + --> $DIR/min_const_fn.rs:139:41 | -LL | const fn no_dyn_trait_ret() -> &'static dyn std::fmt::Debug { &() } - | ^^^ +LL | const fn really_no_traits_i_mean_it() { (&() as &dyn std::fmt::Debug, ()).1 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: see issue #64992 <https://github.com/rust-lang/rust/issues/64992> for more information - = help: add `#![feature(const_fn_unsize)]` to the crate attributes to enable + = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information + = help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable -error[E0658]: unsizing casts to types besides slices are not allowed in const fn - --> $DIR/min_const_fn.rs:134:63 +error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable + --> $DIR/min_const_fn.rs:139:42 | -LL | const fn no_dyn_trait_ret() -> &'static dyn std::fmt::Debug { &() } - | ^^^ +LL | const fn really_no_traits_i_mean_it() { (&() as &dyn std::fmt::Debug, ()).1 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: see issue #64992 <https://github.com/rust-lang/rust/issues/64992> for more information - = help: add `#![feature(const_fn_unsize)]` to the crate attributes to enable + = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information + = help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable -error[E0658]: unsizing casts to types besides slices are not allowed in const fn - --> $DIR/min_const_fn.rs:141:42 +error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable + --> $DIR/min_const_fn.rs:139:42 | LL | const fn really_no_traits_i_mean_it() { (&() as &dyn std::fmt::Debug, ()).1 } - | ^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: see issue #64992 <https://github.com/rust-lang/rust/issues/64992> for more information - = help: add `#![feature(const_fn_unsize)]` to the crate attributes to enable + = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information + = help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable error[E0658]: function pointers cannot appear in constant functions --> $DIR/min_const_fn.rs:144:21 diff --git a/src/test/ui/consts/min_const_fn/min_const_fn_dyn.rs b/src/test/ui/consts/min_const_fn/min_const_fn_dyn.rs index 4a22ef2dffd..6ca1e59b3af 100644 --- a/src/test/ui/consts/min_const_fn/min_const_fn_dyn.rs +++ b/src/test/ui/consts/min_const_fn/min_const_fn_dyn.rs @@ -10,6 +10,6 @@ const fn no_inner_dyn_trait2(x: Hide) { //~^ ERROR trait bounds other than `Sized` } const fn no_inner_dyn_trait_ret() -> Hide { Hide(HasDyn { field: &0 }) } -//~^ ERROR unsizing cast +//~^ ERROR trait bounds other than `Sized` fn main() {} diff --git a/src/test/ui/consts/min_const_fn/min_const_fn_dyn.stderr b/src/test/ui/consts/min_const_fn/min_const_fn_dyn.stderr index cf635d65699..ce844a2f071 100644 --- a/src/test/ui/consts/min_const_fn/min_const_fn_dyn.stderr +++ b/src/test/ui/consts/min_const_fn/min_const_fn_dyn.stderr @@ -7,14 +7,14 @@ LL | x.0.field; = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information = help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable -error[E0658]: unsizing casts to types besides slices are not allowed in const fn +error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable --> $DIR/min_const_fn_dyn.rs:12:66 | LL | const fn no_inner_dyn_trait_ret() -> Hide { Hide(HasDyn { field: &0 }) } | ^^ | - = note: see issue #64992 <https://github.com/rust-lang/rust/issues/64992> for more information - = help: add `#![feature(const_fn_unsize)]` to the crate attributes to enable + = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information + = help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable error: aborting due to 2 previous errors diff --git a/src/test/ui/consts/unsizing-cast-non-null.rs b/src/test/ui/consts/unsizing-cast-non-null.rs deleted file mode 100644 index af6bc2d85fd..00000000000 --- a/src/test/ui/consts/unsizing-cast-non-null.rs +++ /dev/null @@ -1,10 +0,0 @@ -// Regression test for #75118. - -use std::ptr::NonNull; - -pub const fn dangling_slice<T>() -> NonNull<[T]> { - NonNull::<[T; 0]>::dangling() - //~^ ERROR: unsizing casts to types besides slices -} - -fn main() {} diff --git a/src/test/ui/consts/unsizing-cast-non-null.stderr b/src/test/ui/consts/unsizing-cast-non-null.stderr deleted file mode 100644 index 79691cddfb4..00000000000 --- a/src/test/ui/consts/unsizing-cast-non-null.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0658]: unsizing casts to types besides slices are not allowed in const fn - --> $DIR/unsizing-cast-non-null.rs:6:5 - | -LL | NonNull::<[T; 0]>::dangling() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #64992 <https://github.com/rust-lang/rust/issues/64992> for more information - = help: add `#![feature(const_fn_unsize)]` to the crate attributes to enable - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/generator/issue-45729-unsafe-in-generator.stderr b/src/test/ui/generator/issue-45729-unsafe-in-generator.mir.stderr index 0bd3dbf6863..3afbea07931 100644 --- a/src/test/ui/generator/issue-45729-unsafe-in-generator.stderr +++ b/src/test/ui/generator/issue-45729-unsafe-in-generator.mir.stderr @@ -1,5 +1,5 @@ error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block - --> $DIR/issue-45729-unsafe-in-generator.rs:5:9 + --> $DIR/issue-45729-unsafe-in-generator.rs:8:9 | LL | *(1 as *mut u32) = 42; | ^^^^^^^^^^^^^^^^^^^^^ dereference of raw pointer diff --git a/src/test/ui/generator/issue-45729-unsafe-in-generator.rs b/src/test/ui/generator/issue-45729-unsafe-in-generator.rs index 638a1994bb5..379c36d2ca3 100644 --- a/src/test/ui/generator/issue-45729-unsafe-in-generator.rs +++ b/src/test/ui/generator/issue-45729-unsafe-in-generator.rs @@ -1,3 +1,6 @@ +// revisions: mir thir +// [thir]compile-flags: -Z thir-unsafeck + #![feature(generators)] fn main() { diff --git a/src/test/ui/generator/issue-45729-unsafe-in-generator.thir.stderr b/src/test/ui/generator/issue-45729-unsafe-in-generator.thir.stderr new file mode 100644 index 00000000000..a0905f98ca7 --- /dev/null +++ b/src/test/ui/generator/issue-45729-unsafe-in-generator.thir.stderr @@ -0,0 +1,11 @@ +error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block + --> $DIR/issue-45729-unsafe-in-generator.rs:8:9 + | +LL | *(1 as *mut u32) = 42; + | ^^^^^^^^^^^^^^^^ dereference of raw pointer + | + = note: raw pointers may be NULL, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/intrinsics/issue-28575.stderr b/src/test/ui/intrinsics/issue-28575.mir.stderr index 66369decf42..c42498390c7 100644 --- a/src/test/ui/intrinsics/issue-28575.stderr +++ b/src/test/ui/intrinsics/issue-28575.mir.stderr @@ -1,5 +1,5 @@ error[E0133]: use of extern static is unsafe and requires unsafe function or block - --> $DIR/issue-28575.rs:8:5 + --> $DIR/issue-28575.rs:11:5 | LL | FOO() | ^^^ use of extern static diff --git a/src/test/ui/intrinsics/issue-28575.rs b/src/test/ui/intrinsics/issue-28575.rs index 141136d25b2..410f664f89d 100644 --- a/src/test/ui/intrinsics/issue-28575.rs +++ b/src/test/ui/intrinsics/issue-28575.rs @@ -1,3 +1,6 @@ +// revisions: mir thir +// [thir]compile-flags: -Z thir-unsafeck + #![feature(intrinsics)] extern "C" { diff --git a/src/test/ui/intrinsics/issue-28575.thir.stderr b/src/test/ui/intrinsics/issue-28575.thir.stderr new file mode 100644 index 00000000000..c42498390c7 --- /dev/null +++ b/src/test/ui/intrinsics/issue-28575.thir.stderr @@ -0,0 +1,11 @@ +error[E0133]: use of extern static is unsafe and requires unsafe function or block + --> $DIR/issue-28575.rs:11:5 + | +LL | FOO() + | ^^^ use of extern static + | + = note: extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/issues/issue-14227.stderr b/src/test/ui/issues/issue-14227.mir.stderr index f9cdbe452df..8e7a2514dd6 100644 --- a/src/test/ui/issues/issue-14227.stderr +++ b/src/test/ui/issues/issue-14227.mir.stderr @@ -1,5 +1,5 @@ error[E0133]: use of extern static is unsafe and requires unsafe function or block - --> $DIR/issue-14227.rs:4:21 + --> $DIR/issue-14227.rs:7:21 | LL | static CRASH: u32 = symbol; | ^^^^^^ use of extern static diff --git a/src/test/ui/issues/issue-14227.rs b/src/test/ui/issues/issue-14227.rs index a1fde14600a..5f866ec9061 100644 --- a/src/test/ui/issues/issue-14227.rs +++ b/src/test/ui/issues/issue-14227.rs @@ -1,3 +1,6 @@ +// revisions: mir thir +// [thir]compile-flags: -Z thir-unsafeck + extern "C" { pub static symbol: u32; } diff --git a/src/test/ui/issues/issue-14227.thir.stderr b/src/test/ui/issues/issue-14227.thir.stderr new file mode 100644 index 00000000000..8e7a2514dd6 --- /dev/null +++ b/src/test/ui/issues/issue-14227.thir.stderr @@ -0,0 +1,11 @@ +error[E0133]: use of extern static is unsafe and requires unsafe function or block + --> $DIR/issue-14227.rs:7:21 + | +LL | static CRASH: u32 = symbol; + | ^^^^^^ use of extern static + | + = note: extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/issues/issue-16538.stderr b/src/test/ui/issues/issue-16538.mir.stderr index 81a91db3711..d7e8c08bb01 100644 --- a/src/test/ui/issues/issue-16538.stderr +++ b/src/test/ui/issues/issue-16538.mir.stderr @@ -1,11 +1,11 @@ error[E0015]: calls in statics are limited to constant functions, tuple structs and tuple variants - --> $DIR/issue-16538.rs:11:27 + --> $DIR/issue-16538.rs:14:27 | LL | static foo: *const Y::X = Y::foo(Y::x as *const Y::X); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: `*const usize` cannot be shared between threads safely - --> $DIR/issue-16538.rs:11:1 + --> $DIR/issue-16538.rs:14:1 | LL | static foo: *const Y::X = Y::foo(Y::x as *const Y::X); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `*const usize` cannot be shared between threads safely @@ -14,7 +14,7 @@ LL | static foo: *const Y::X = Y::foo(Y::x as *const Y::X); = note: shared static variables must have a type that implements `Sync` error[E0133]: use of extern static is unsafe and requires unsafe function or block - --> $DIR/issue-16538.rs:11:34 + --> $DIR/issue-16538.rs:14:34 | LL | static foo: *const Y::X = Y::foo(Y::x as *const Y::X); | ^^^^ use of extern static diff --git a/src/test/ui/issues/issue-16538.rs b/src/test/ui/issues/issue-16538.rs index 7d6eefa5b1e..1e8ecf015c8 100644 --- a/src/test/ui/issues/issue-16538.rs +++ b/src/test/ui/issues/issue-16538.rs @@ -1,3 +1,6 @@ +// revisions: mir thir +// [thir]compile-flags: -Z thir-unsafeck + mod Y { pub type X = usize; extern "C" { diff --git a/src/test/ui/issues/issue-16538.thir.stderr b/src/test/ui/issues/issue-16538.thir.stderr new file mode 100644 index 00000000000..d7e8c08bb01 --- /dev/null +++ b/src/test/ui/issues/issue-16538.thir.stderr @@ -0,0 +1,27 @@ +error[E0015]: calls in statics are limited to constant functions, tuple structs and tuple variants + --> $DIR/issue-16538.rs:14:27 + | +LL | static foo: *const Y::X = Y::foo(Y::x as *const Y::X); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: `*const usize` cannot be shared between threads safely + --> $DIR/issue-16538.rs:14:1 + | +LL | static foo: *const Y::X = Y::foo(Y::x as *const Y::X); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `*const usize` cannot be shared between threads safely + | + = help: the trait `Sync` is not implemented for `*const usize` + = note: shared static variables must have a type that implements `Sync` + +error[E0133]: use of extern static is unsafe and requires unsafe function or block + --> $DIR/issue-16538.rs:14:34 + | +LL | static foo: *const Y::X = Y::foo(Y::x as *const Y::X); + | ^^^^ use of extern static + | + = note: extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0015, E0133, E0277. +For more information about an error, try `rustc --explain E0015`. diff --git a/src/test/ui/issues/issue-28324.stderr b/src/test/ui/issues/issue-28324.mir.stderr index d7dad992152..aff8bf7927d 100644 --- a/src/test/ui/issues/issue-28324.stderr +++ b/src/test/ui/issues/issue-28324.mir.stderr @@ -1,5 +1,5 @@ error[E0133]: use of extern static is unsafe and requires unsafe function or block - --> $DIR/issue-28324.rs:5:24 + --> $DIR/issue-28324.rs:8:24 | LL | pub static BAZ: u32 = *&error_message_count; | ^^^^^^^^^^^^^^^^^^^^ use of extern static diff --git a/src/test/ui/issues/issue-28324.rs b/src/test/ui/issues/issue-28324.rs index f74726e8166..fbe83e325ed 100644 --- a/src/test/ui/issues/issue-28324.rs +++ b/src/test/ui/issues/issue-28324.rs @@ -1,3 +1,6 @@ +// revisions: mir thir +// [thir]compile-flags: -Z thir-unsafeck + extern "C" { static error_message_count: u32; } diff --git a/src/test/ui/issues/issue-28324.thir.stderr b/src/test/ui/issues/issue-28324.thir.stderr new file mode 100644 index 00000000000..c696c359830 --- /dev/null +++ b/src/test/ui/issues/issue-28324.thir.stderr @@ -0,0 +1,11 @@ +error[E0133]: use of extern static is unsafe and requires unsafe function or block + --> $DIR/issue-28324.rs:8:25 + | +LL | pub static BAZ: u32 = *&error_message_count; + | ^^^^^^^^^^^^^^^^^^^ use of extern static + | + = note: extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/issues/issue-47412.stderr b/src/test/ui/issues/issue-47412.mir.stderr index aebcbf07463..96e50ba6799 100644 --- a/src/test/ui/issues/issue-47412.stderr +++ b/src/test/ui/issues/issue-47412.mir.stderr @@ -1,5 +1,5 @@ error[E0133]: access to union field is unsafe and requires unsafe function or block - --> $DIR/issue-47412.rs:11:11 + --> $DIR/issue-47412.rs:14:11 | LL | match u.void {} | ^^^^^^ access to union field @@ -7,7 +7,7 @@ LL | match u.void {} = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block - --> $DIR/issue-47412.rs:17:11 + --> $DIR/issue-47412.rs:21:11 | LL | match *ptr {} | ^^^^ dereference of raw pointer diff --git a/src/test/ui/issues/issue-47412.rs b/src/test/ui/issues/issue-47412.rs index 2d1ea72280b..d395285eee0 100644 --- a/src/test/ui/issues/issue-47412.rs +++ b/src/test/ui/issues/issue-47412.rs @@ -1,3 +1,6 @@ +// revisions: mir thir +// [thir]compile-flags: -Z thir-unsafeck + #[derive(Copy, Clone)] enum Void {} @@ -9,7 +12,8 @@ fn union_field() { union Union { unit: (), void: Void } let u = Union { unit: () }; match u.void {} - //~^ ERROR access to union field is unsafe + //[mir]~^ ERROR access to union field is unsafe + // FIXME(thir-unsafeck): AccessToUnionField unimplemented } fn raw_ptr_deref() { diff --git a/src/test/ui/issues/issue-47412.thir.stderr b/src/test/ui/issues/issue-47412.thir.stderr new file mode 100644 index 00000000000..2d6004b7911 --- /dev/null +++ b/src/test/ui/issues/issue-47412.thir.stderr @@ -0,0 +1,11 @@ +error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block + --> $DIR/issue-47412.rs:21:11 + | +LL | match *ptr {} + | ^^^^ dereference of raw pointer + | + = note: raw pointers may be NULL, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/check-pass.rs b/src/test/ui/rfcs/rfc-2396-target_feature-11/check-pass.rs index 58a2c271ecf..e0842bfa4cd 100644 --- a/src/test/ui/rfcs/rfc-2396-target_feature-11/check-pass.rs +++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/check-pass.rs @@ -8,6 +8,8 @@ // check-pass // only-x86_64 +// revisions: mir thir +// [thir]compile-flags: -Z thir-unsafeck #![feature(target_feature_11)] diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/closures-inherit-target_feature.rs b/src/test/ui/rfcs/rfc-2396-target_feature-11/closures-inherit-target_feature.rs index af35bc2014b..a59d7c2d784 100644 --- a/src/test/ui/rfcs/rfc-2396-target_feature-11/closures-inherit-target_feature.rs +++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/closures-inherit-target_feature.rs @@ -1,6 +1,8 @@ // Tests #73631: closures inherit `#[target_feature]` annotations // check-pass +// revisions: mir thir +// [thir]compile-flags: -Z thir-unsafeck // only-x86_64 #![feature(target_feature_11)] diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.stderr b/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.mir.stderr index 06cfdde3fb9..cf5815df56e 100644 --- a/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.stderr +++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.mir.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/fn-ptr.rs:9:21 + --> $DIR/fn-ptr.rs:11:21 | LL | #[target_feature(enable = "sse2")] | ---------------------------------- `#[target_feature]` added here diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.rs b/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.rs index 3ecea5c5313..c95d4a08e48 100644 --- a/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.rs +++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.rs @@ -1,3 +1,5 @@ +// revisions: mir thir +// [thir]compile-flags: -Z thir-unsafeck // only-x86_64 #![feature(target_feature_11)] diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.thir.stderr b/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.thir.stderr new file mode 100644 index 00000000000..cf5815df56e --- /dev/null +++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.thir.stderr @@ -0,0 +1,18 @@ +error[E0308]: mismatched types + --> $DIR/fn-ptr.rs:11:21 + | +LL | #[target_feature(enable = "sse2")] + | ---------------------------------- `#[target_feature]` added here +... +LL | let foo: fn() = foo; + | ---- ^^^ cannot coerce functions with `#[target_feature]` to safe function pointers + | | + | expected due to this + | + = note: expected fn pointer `fn()` + found fn item `fn() {foo}` + = note: functions with `#[target_feature]` can only be coerced to `unsafe` function pointers + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr b/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.mir.stderr index b9f748640b5..79273a1dcbf 100644 --- a/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr +++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.mir.stderr @@ -1,5 +1,5 @@ error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:21:5 + --> $DIR/safe-calls.rs:23:5 | LL | sse2(); | ^^^^^^ call to function with `#[target_feature]` @@ -7,7 +7,7 @@ LL | sse2(); = note: can only be called if the required target features are available error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:22:5 + --> $DIR/safe-calls.rs:24:5 | LL | avx_bmi2(); | ^^^^^^^^^^ call to function with `#[target_feature]` @@ -15,7 +15,7 @@ LL | avx_bmi2(); = note: can only be called if the required target features are available error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:23:5 + --> $DIR/safe-calls.rs:25:5 | LL | Quux.avx_bmi2(); | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` @@ -23,7 +23,7 @@ LL | Quux.avx_bmi2(); = note: can only be called if the required target features are available error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:28:5 + --> $DIR/safe-calls.rs:30:5 | LL | avx_bmi2(); | ^^^^^^^^^^ call to function with `#[target_feature]` @@ -31,7 +31,7 @@ LL | avx_bmi2(); = note: can only be called if the required target features are available error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:29:5 + --> $DIR/safe-calls.rs:31:5 | LL | Quux.avx_bmi2(); | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` @@ -39,7 +39,7 @@ LL | Quux.avx_bmi2(); = note: can only be called if the required target features are available error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:34:5 + --> $DIR/safe-calls.rs:36:5 | LL | sse2(); | ^^^^^^ call to function with `#[target_feature]` @@ -47,7 +47,7 @@ LL | sse2(); = note: can only be called if the required target features are available error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:35:5 + --> $DIR/safe-calls.rs:37:5 | LL | avx_bmi2(); | ^^^^^^^^^^ call to function with `#[target_feature]` @@ -55,7 +55,7 @@ LL | avx_bmi2(); = note: can only be called if the required target features are available error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:36:5 + --> $DIR/safe-calls.rs:38:5 | LL | Quux.avx_bmi2(); | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` @@ -63,7 +63,7 @@ LL | Quux.avx_bmi2(); = note: can only be called if the required target features are available error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:42:5 + --> $DIR/safe-calls.rs:44:5 | LL | sse2(); | ^^^^^^ call to function with `#[target_feature]` @@ -71,7 +71,7 @@ LL | sse2(); = note: can only be called if the required target features are available error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:45:18 + --> $DIR/safe-calls.rs:47:18 | LL | const name: () = sse2(); | ^^^^^^ call to function with `#[target_feature]` diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs b/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs index 8da3affc447..de0b89f46ba 100644 --- a/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs +++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs @@ -1,3 +1,5 @@ +// revisions: mir thir +// [thir]compile-flags: -Z thir-unsafeck // only-x86_64 #![feature(target_feature_11)] diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.thir.stderr b/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.thir.stderr new file mode 100644 index 00000000000..79273a1dcbf --- /dev/null +++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.thir.stderr @@ -0,0 +1,83 @@ +error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block + --> $DIR/safe-calls.rs:23:5 + | +LL | sse2(); + | ^^^^^^ call to function with `#[target_feature]` + | + = note: can only be called if the required target features are available + +error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block + --> $DIR/safe-calls.rs:24:5 + | +LL | avx_bmi2(); + | ^^^^^^^^^^ call to function with `#[target_feature]` + | + = note: can only be called if the required target features are available + +error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block + --> $DIR/safe-calls.rs:25:5 + | +LL | Quux.avx_bmi2(); + | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` + | + = note: can only be called if the required target features are available + +error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block + --> $DIR/safe-calls.rs:30:5 + | +LL | avx_bmi2(); + | ^^^^^^^^^^ call to function with `#[target_feature]` + | + = note: can only be called if the required target features are available + +error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block + --> $DIR/safe-calls.rs:31:5 + | +LL | Quux.avx_bmi2(); + | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` + | + = note: can only be called if the required target features are available + +error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block + --> $DIR/safe-calls.rs:36:5 + | +LL | sse2(); + | ^^^^^^ call to function with `#[target_feature]` + | + = note: can only be called if the required target features are available + +error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block + --> $DIR/safe-calls.rs:37:5 + | +LL | avx_bmi2(); + | ^^^^^^^^^^ call to function with `#[target_feature]` + | + = note: can only be called if the required target features are available + +error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block + --> $DIR/safe-calls.rs:38:5 + | +LL | Quux.avx_bmi2(); + | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` + | + = note: can only be called if the required target features are available + +error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block + --> $DIR/safe-calls.rs:44:5 + | +LL | sse2(); + | ^^^^^^ call to function with `#[target_feature]` + | + = note: can only be called if the required target features are available + +error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block + --> $DIR/safe-calls.rs:47:18 + | +LL | const name: () = sse2(); + | ^^^^^^ call to function with `#[target_feature]` + | + = note: can only be called if the required target features are available + +error: aborting due to 10 previous errors + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/safe-extern-statics-mut.stderr b/src/test/ui/safe-extern-statics-mut.mir.stderr index 38803883414..cec5f9d9c9f 100644 --- a/src/test/ui/safe-extern-statics-mut.stderr +++ b/src/test/ui/safe-extern-statics-mut.mir.stderr @@ -1,5 +1,5 @@ error[E0133]: use of mutable static is unsafe and requires unsafe function or block - --> $DIR/safe-extern-statics-mut.rs:11:13 + --> $DIR/safe-extern-statics-mut.rs:13:13 | LL | let b = B; | ^ use of mutable static @@ -7,7 +7,7 @@ LL | let b = B; = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior error[E0133]: use of mutable static is unsafe and requires unsafe function or block - --> $DIR/safe-extern-statics-mut.rs:12:14 + --> $DIR/safe-extern-statics-mut.rs:14:14 | LL | let rb = &B; | ^^ use of mutable static @@ -15,7 +15,7 @@ LL | let rb = &B; = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior error[E0133]: use of mutable static is unsafe and requires unsafe function or block - --> $DIR/safe-extern-statics-mut.rs:13:14 + --> $DIR/safe-extern-statics-mut.rs:15:14 | LL | let xb = XB; | ^^ use of mutable static @@ -23,7 +23,7 @@ LL | let xb = XB; = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior error[E0133]: use of mutable static is unsafe and requires unsafe function or block - --> $DIR/safe-extern-statics-mut.rs:14:15 + --> $DIR/safe-extern-statics-mut.rs:16:15 | LL | let xrb = &XB; | ^^^ use of mutable static diff --git a/src/test/ui/safe-extern-statics-mut.rs b/src/test/ui/safe-extern-statics-mut.rs index 324fa443aa5..389a4589a71 100644 --- a/src/test/ui/safe-extern-statics-mut.rs +++ b/src/test/ui/safe-extern-statics-mut.rs @@ -1,4 +1,6 @@ // aux-build:extern-statics.rs +// revisions: mir thir +// [thir]compile-flags: -Z thir-unsafeck extern crate extern_statics; use extern_statics::*; diff --git a/src/test/ui/safe-extern-statics-mut.thir.stderr b/src/test/ui/safe-extern-statics-mut.thir.stderr new file mode 100644 index 00000000000..8e6d2805a0b --- /dev/null +++ b/src/test/ui/safe-extern-statics-mut.thir.stderr @@ -0,0 +1,35 @@ +error[E0133]: use of mutable static is unsafe and requires unsafe function or block + --> $DIR/safe-extern-statics-mut.rs:13:13 + | +LL | let b = B; + | ^ use of mutable static + | + = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior + +error[E0133]: use of mutable static is unsafe and requires unsafe function or block + --> $DIR/safe-extern-statics-mut.rs:14:15 + | +LL | let rb = &B; + | ^ use of mutable static + | + = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior + +error[E0133]: use of mutable static is unsafe and requires unsafe function or block + --> $DIR/safe-extern-statics-mut.rs:15:14 + | +LL | let xb = XB; + | ^^ use of mutable static + | + = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior + +error[E0133]: use of mutable static is unsafe and requires unsafe function or block + --> $DIR/safe-extern-statics-mut.rs:16:16 + | +LL | let xrb = &XB; + | ^^ use of mutable static + | + = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/safe-extern-statics.stderr b/src/test/ui/safe-extern-statics.mir.stderr index b42572ea3ee..102abd0816f 100644 --- a/src/test/ui/safe-extern-statics.stderr +++ b/src/test/ui/safe-extern-statics.mir.stderr @@ -1,5 +1,5 @@ error[E0133]: use of extern static is unsafe and requires unsafe function or block - --> $DIR/safe-extern-statics.rs:11:13 + --> $DIR/safe-extern-statics.rs:13:13 | LL | let a = A; | ^ use of extern static @@ -7,7 +7,7 @@ LL | let a = A; = note: extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior error[E0133]: use of extern static is unsafe and requires unsafe function or block - --> $DIR/safe-extern-statics.rs:12:14 + --> $DIR/safe-extern-statics.rs:14:14 | LL | let ra = &A; | ^^ use of extern static @@ -15,7 +15,7 @@ LL | let ra = &A; = note: extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior error[E0133]: use of extern static is unsafe and requires unsafe function or block - --> $DIR/safe-extern-statics.rs:13:14 + --> $DIR/safe-extern-statics.rs:15:14 | LL | let xa = XA; | ^^ use of extern static @@ -23,7 +23,7 @@ LL | let xa = XA; = note: extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior error[E0133]: use of extern static is unsafe and requires unsafe function or block - --> $DIR/safe-extern-statics.rs:14:15 + --> $DIR/safe-extern-statics.rs:16:15 | LL | let xra = &XA; | ^^^ use of extern static diff --git a/src/test/ui/safe-extern-statics.rs b/src/test/ui/safe-extern-statics.rs index 6fa4c4aaca5..0aa90c442ea 100644 --- a/src/test/ui/safe-extern-statics.rs +++ b/src/test/ui/safe-extern-statics.rs @@ -1,4 +1,6 @@ // aux-build:extern-statics.rs +// revisions: mir thir +// [thir]compile-flags: -Z thir-unsafeck extern crate extern_statics; use extern_statics::*; diff --git a/src/test/ui/safe-extern-statics.thir.stderr b/src/test/ui/safe-extern-statics.thir.stderr new file mode 100644 index 00000000000..7fd2182c4c6 --- /dev/null +++ b/src/test/ui/safe-extern-statics.thir.stderr @@ -0,0 +1,35 @@ +error[E0133]: use of extern static is unsafe and requires unsafe function or block + --> $DIR/safe-extern-statics.rs:13:13 + | +LL | let a = A; + | ^ use of extern static + | + = note: extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior + +error[E0133]: use of extern static is unsafe and requires unsafe function or block + --> $DIR/safe-extern-statics.rs:14:15 + | +LL | let ra = &A; + | ^ use of extern static + | + = note: extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior + +error[E0133]: use of extern static is unsafe and requires unsafe function or block + --> $DIR/safe-extern-statics.rs:15:14 + | +LL | let xa = XA; + | ^^ use of extern static + | + = note: extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior + +error[E0133]: use of extern static is unsafe and requires unsafe function or block + --> $DIR/safe-extern-statics.rs:16:16 + | +LL | let xra = &XA; + | ^^ use of extern static + | + = note: extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/static/static-mut-foreign-requires-unsafe.stderr b/src/test/ui/static/static-mut-foreign-requires-unsafe.mir.stderr index e7ed0b710b2..a4659bc8712 100644 --- a/src/test/ui/static/static-mut-foreign-requires-unsafe.stderr +++ b/src/test/ui/static/static-mut-foreign-requires-unsafe.mir.stderr @@ -1,5 +1,5 @@ error[E0133]: use of mutable static is unsafe and requires unsafe function or block - --> $DIR/static-mut-foreign-requires-unsafe.rs:6:5 + --> $DIR/static-mut-foreign-requires-unsafe.rs:9:5 | LL | a += 3; | ^^^^^^ use of mutable static @@ -7,7 +7,7 @@ LL | a += 3; = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior error[E0133]: use of mutable static is unsafe and requires unsafe function or block - --> $DIR/static-mut-foreign-requires-unsafe.rs:7:5 + --> $DIR/static-mut-foreign-requires-unsafe.rs:10:5 | LL | a = 4; | ^^^^^ use of mutable static @@ -15,7 +15,7 @@ LL | a = 4; = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior error[E0133]: use of mutable static is unsafe and requires unsafe function or block - --> $DIR/static-mut-foreign-requires-unsafe.rs:8:14 + --> $DIR/static-mut-foreign-requires-unsafe.rs:11:14 | LL | let _b = a; | ^ use of mutable static diff --git a/src/test/ui/static/static-mut-foreign-requires-unsafe.rs b/src/test/ui/static/static-mut-foreign-requires-unsafe.rs index 90aa2537a82..4f96acb3375 100644 --- a/src/test/ui/static/static-mut-foreign-requires-unsafe.rs +++ b/src/test/ui/static/static-mut-foreign-requires-unsafe.rs @@ -1,3 +1,6 @@ +// revisions: mir thir +// [thir]compile-flags: -Z thir-unsafeck + extern "C" { static mut a: i32; } diff --git a/src/test/ui/static/static-mut-foreign-requires-unsafe.thir.stderr b/src/test/ui/static/static-mut-foreign-requires-unsafe.thir.stderr new file mode 100644 index 00000000000..2c62d4d8f3b --- /dev/null +++ b/src/test/ui/static/static-mut-foreign-requires-unsafe.thir.stderr @@ -0,0 +1,27 @@ +error[E0133]: use of mutable static is unsafe and requires unsafe function or block + --> $DIR/static-mut-foreign-requires-unsafe.rs:9:5 + | +LL | a += 3; + | ^ use of mutable static + | + = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior + +error[E0133]: use of mutable static is unsafe and requires unsafe function or block + --> $DIR/static-mut-foreign-requires-unsafe.rs:10:5 + | +LL | a = 4; + | ^ use of mutable static + | + = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior + +error[E0133]: use of mutable static is unsafe and requires unsafe function or block + --> $DIR/static-mut-foreign-requires-unsafe.rs:11:14 + | +LL | let _b = a; + | ^ use of mutable static + | + = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/static/static-mut-requires-unsafe.stderr b/src/test/ui/static/static-mut-requires-unsafe.mir.stderr index 85e468b333c..0d4ce056fc2 100644 --- a/src/test/ui/static/static-mut-requires-unsafe.stderr +++ b/src/test/ui/static/static-mut-requires-unsafe.mir.stderr @@ -1,5 +1,5 @@ error[E0133]: use of mutable static is unsafe and requires unsafe function or block - --> $DIR/static-mut-requires-unsafe.rs:4:5 + --> $DIR/static-mut-requires-unsafe.rs:7:5 | LL | a += 3; | ^^^^^^ use of mutable static @@ -7,7 +7,7 @@ LL | a += 3; = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior error[E0133]: use of mutable static is unsafe and requires unsafe function or block - --> $DIR/static-mut-requires-unsafe.rs:5:5 + --> $DIR/static-mut-requires-unsafe.rs:8:5 | LL | a = 4; | ^^^^^ use of mutable static @@ -15,7 +15,7 @@ LL | a = 4; = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior error[E0133]: use of mutable static is unsafe and requires unsafe function or block - --> $DIR/static-mut-requires-unsafe.rs:6:14 + --> $DIR/static-mut-requires-unsafe.rs:9:14 | LL | let _b = a; | ^ use of mutable static diff --git a/src/test/ui/static/static-mut-requires-unsafe.rs b/src/test/ui/static/static-mut-requires-unsafe.rs index 413b97e431d..ea3ba095007 100644 --- a/src/test/ui/static/static-mut-requires-unsafe.rs +++ b/src/test/ui/static/static-mut-requires-unsafe.rs @@ -1,3 +1,6 @@ +// revisions: mir thir +// [thir]compile-flags: -Z thir-unsafeck + static mut a: isize = 3; fn main() { diff --git a/src/test/ui/static/static-mut-requires-unsafe.thir.stderr b/src/test/ui/static/static-mut-requires-unsafe.thir.stderr new file mode 100644 index 00000000000..1a1cf14271a --- /dev/null +++ b/src/test/ui/static/static-mut-requires-unsafe.thir.stderr @@ -0,0 +1,27 @@ +error[E0133]: use of mutable static is unsafe and requires unsafe function or block + --> $DIR/static-mut-requires-unsafe.rs:7:5 + | +LL | a += 3; + | ^ use of mutable static + | + = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior + +error[E0133]: use of mutable static is unsafe and requires unsafe function or block + --> $DIR/static-mut-requires-unsafe.rs:8:5 + | +LL | a = 4; + | ^ use of mutable static + | + = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior + +error[E0133]: use of mutable static is unsafe and requires unsafe function or block + --> $DIR/static-mut-requires-unsafe.rs:9:14 + | +LL | let _b = a; + | ^ use of mutable static + | + = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/suggestions/mut-borrow-needed-by-trait.rs b/src/test/ui/suggestions/mut-borrow-needed-by-trait.rs index 924bfd82eb8..66e1e77c905 100644 --- a/src/test/ui/suggestions/mut-borrow-needed-by-trait.rs +++ b/src/test/ui/suggestions/mut-borrow-needed-by-trait.rs @@ -17,7 +17,6 @@ fn main() { let fp = BufWriter::new(fp); //~^ ERROR the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied //~| ERROR the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied - //~| ERROR the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied writeln!(fp, "hello world").unwrap(); //~ ERROR the method } diff --git a/src/test/ui/suggestions/mut-borrow-needed-by-trait.stderr b/src/test/ui/suggestions/mut-borrow-needed-by-trait.stderr index a9b06214fe8..71b09d43612 100644 --- a/src/test/ui/suggestions/mut-borrow-needed-by-trait.stderr +++ b/src/test/ui/suggestions/mut-borrow-needed-by-trait.stderr @@ -11,19 +11,6 @@ error[E0277]: the trait bound `&dyn std::io::Write: std::io::Write` is not satis --> $DIR/mut-borrow-needed-by-trait.rs:17:14 | LL | let fp = BufWriter::new(fp); - | ^^^^^^^^^ the trait `std::io::Write` is not implemented for `&dyn std::io::Write` - | - ::: $SRC_DIR/std/src/io/buffered/bufwriter.rs:LL:COL - | -LL | pub struct BufWriter<W: Write> { - | ----- required by this bound in `BufWriter` - | - = note: `std::io::Write` is implemented for `&mut dyn std::io::Write`, but not for `&dyn std::io::Write` - -error[E0277]: the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied - --> $DIR/mut-borrow-needed-by-trait.rs:17:14 - | -LL | let fp = BufWriter::new(fp); | ^^^^^^^^^^^^^^^^^^ the trait `std::io::Write` is not implemented for `&dyn std::io::Write` | ::: $SRC_DIR/std/src/io/buffered/bufwriter.rs:LL:COL @@ -34,7 +21,7 @@ LL | pub struct BufWriter<W: Write> { = note: `std::io::Write` is implemented for `&mut dyn std::io::Write`, but not for `&dyn std::io::Write` error[E0599]: the method `write_fmt` exists for struct `BufWriter<&dyn std::io::Write>`, but its trait bounds were not satisfied - --> $DIR/mut-borrow-needed-by-trait.rs:22:5 + --> $DIR/mut-borrow-needed-by-trait.rs:21:5 | LL | writeln!(fp, "hello world").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ method cannot be called on `BufWriter<&dyn std::io::Write>` due to unsatisfied trait bounds @@ -49,7 +36,7 @@ LL | pub struct BufWriter<W: Write> { which is required by `BufWriter<&dyn std::io::Write>: std::io::Write` = note: this error originates in the macro `writeln` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors Some errors have detailed explanations: E0277, E0599. For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/traits/safety-fn-body.stderr b/src/test/ui/traits/safety-fn-body.mir.stderr index 0aeb186828e..ea7b2048e83 100644 --- a/src/test/ui/traits/safety-fn-body.stderr +++ b/src/test/ui/traits/safety-fn-body.mir.stderr @@ -1,5 +1,5 @@ error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block - --> $DIR/safety-fn-body.rs:11:9 + --> $DIR/safety-fn-body.rs:14:9 | LL | *self += 1; | ^^^^^^^^^^ dereference of raw pointer diff --git a/src/test/ui/traits/safety-fn-body.rs b/src/test/ui/traits/safety-fn-body.rs index df527747305..2cc4fe1b344 100644 --- a/src/test/ui/traits/safety-fn-body.rs +++ b/src/test/ui/traits/safety-fn-body.rs @@ -1,6 +1,9 @@ // Check that an unsafe impl does not imply that unsafe actions are // legal in the methods. +// revisions: mir thir +// [thir]compile-flags: -Z thir-unsafeck + unsafe trait UnsafeTrait : Sized { fn foo(self) { } } diff --git a/src/test/ui/traits/safety-fn-body.thir.stderr b/src/test/ui/traits/safety-fn-body.thir.stderr new file mode 100644 index 00000000000..94a1a2a03cd --- /dev/null +++ b/src/test/ui/traits/safety-fn-body.thir.stderr @@ -0,0 +1,11 @@ +error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block + --> $DIR/safety-fn-body.rs:14:9 + | +LL | *self += 1; + | ^^^^^ dereference of raw pointer + | + = note: raw pointers may be NULL, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/unsafe/issue-45087-unreachable-unsafe.stderr b/src/test/ui/unsafe/issue-45087-unreachable-unsafe.mir.stderr index 88322c3a0a6..33f762ccf63 100644 --- a/src/test/ui/unsafe/issue-45087-unreachable-unsafe.stderr +++ b/src/test/ui/unsafe/issue-45087-unreachable-unsafe.mir.stderr @@ -1,5 +1,5 @@ error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block - --> $DIR/issue-45087-unreachable-unsafe.rs:3:5 + --> $DIR/issue-45087-unreachable-unsafe.rs:6:5 | LL | *(1 as *mut u32) = 42; | ^^^^^^^^^^^^^^^^^^^^^ dereference of raw pointer diff --git a/src/test/ui/unsafe/issue-45087-unreachable-unsafe.rs b/src/test/ui/unsafe/issue-45087-unreachable-unsafe.rs index 5edf7a47e2f..071cea8fbd7 100644 --- a/src/test/ui/unsafe/issue-45087-unreachable-unsafe.rs +++ b/src/test/ui/unsafe/issue-45087-unreachable-unsafe.rs @@ -1,3 +1,6 @@ +// revisions: mir thir +// [thir]compile-flags: -Z thir-unsafeck + fn main() { return; *(1 as *mut u32) = 42; diff --git a/src/test/ui/unsafe/issue-45087-unreachable-unsafe.thir.stderr b/src/test/ui/unsafe/issue-45087-unreachable-unsafe.thir.stderr new file mode 100644 index 00000000000..b89401ce837 --- /dev/null +++ b/src/test/ui/unsafe/issue-45087-unreachable-unsafe.thir.stderr @@ -0,0 +1,11 @@ +error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block + --> $DIR/issue-45087-unreachable-unsafe.rs:6:5 + | +LL | *(1 as *mut u32) = 42; + | ^^^^^^^^^^^^^^^^ dereference of raw pointer + | + = note: raw pointers may be NULL, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/unsafe/ranged_ints.stderr b/src/test/ui/unsafe/ranged_ints.mir.stderr index 4e43df495c0..f9ef7834e1e 100644 --- a/src/test/ui/unsafe/ranged_ints.stderr +++ b/src/test/ui/unsafe/ranged_ints.mir.stderr @@ -1,5 +1,5 @@ error[E0133]: initializing type with `rustc_layout_scalar_valid_range` attr is unsafe and requires unsafe function or block - --> $DIR/ranged_ints.rs:7:14 + --> $DIR/ranged_ints.rs:10:14 | LL | let _x = NonZero(0); | ^^^^^^^^^^ initializing type with `rustc_layout_scalar_valid_range` attr diff --git a/src/test/ui/unsafe/ranged_ints.rs b/src/test/ui/unsafe/ranged_ints.rs index 0fa2da917e9..05efe87ba6e 100644 --- a/src/test/ui/unsafe/ranged_ints.rs +++ b/src/test/ui/unsafe/ranged_ints.rs @@ -1,3 +1,6 @@ +// revisions: mir thir +// [thir]compile-flags: -Z thir-unsafeck + #![feature(rustc_attrs)] #[rustc_layout_scalar_valid_range_start(1)] diff --git a/src/test/ui/unsafe/ranged_ints.thir.stderr b/src/test/ui/unsafe/ranged_ints.thir.stderr new file mode 100644 index 00000000000..f9ef7834e1e --- /dev/null +++ b/src/test/ui/unsafe/ranged_ints.thir.stderr @@ -0,0 +1,11 @@ +error[E0133]: initializing type with `rustc_layout_scalar_valid_range` attr is unsafe and requires unsafe function or block + --> $DIR/ranged_ints.rs:10:14 + | +LL | let _x = NonZero(0); + | ^^^^^^^^^^ initializing type with `rustc_layout_scalar_valid_range` attr + | + = note: initializing a layout restricted type's field with a value outside the valid range is undefined behavior + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/unsafe/ranged_ints_const.stderr b/src/test/ui/unsafe/ranged_ints_const.mir.stderr index 584ad40a92b..33d134c7ce5 100644 --- a/src/test/ui/unsafe/ranged_ints_const.stderr +++ b/src/test/ui/unsafe/ranged_ints_const.mir.stderr @@ -1,5 +1,5 @@ error[E0133]: initializing type with `rustc_layout_scalar_valid_range` attr is unsafe and requires unsafe function or block - --> $DIR/ranged_ints_const.rs:8:34 + --> $DIR/ranged_ints_const.rs:11:34 | LL | const fn foo() -> NonZero<u32> { NonZero(0) } | ^^^^^^^^^^ initializing type with `rustc_layout_scalar_valid_range` attr diff --git a/src/test/ui/unsafe/ranged_ints_const.rs b/src/test/ui/unsafe/ranged_ints_const.rs index 8477772867e..472b0968150 100644 --- a/src/test/ui/unsafe/ranged_ints_const.rs +++ b/src/test/ui/unsafe/ranged_ints_const.rs @@ -1,3 +1,6 @@ +// revisions: mir thir +// [thir]compile-flags: -Z thir-unsafeck + #![feature(rustc_attrs)] #[rustc_layout_scalar_valid_range_start(1)] diff --git a/src/test/ui/unsafe/ranged_ints_const.thir.stderr b/src/test/ui/unsafe/ranged_ints_const.thir.stderr new file mode 100644 index 00000000000..33d134c7ce5 --- /dev/null +++ b/src/test/ui/unsafe/ranged_ints_const.thir.stderr @@ -0,0 +1,11 @@ +error[E0133]: initializing type with `rustc_layout_scalar_valid_range` attr is unsafe and requires unsafe function or block + --> $DIR/ranged_ints_const.rs:11:34 + | +LL | const fn foo() -> NonZero<u32> { NonZero(0) } + | ^^^^^^^^^^ initializing type with `rustc_layout_scalar_valid_range` attr + | + = note: initializing a layout restricted type's field with a value outside the valid range is undefined behavior + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/unsafe/ranged_ints_macro.rs b/src/test/ui/unsafe/ranged_ints_macro.rs index 9192ecfe196..8293d029951 100644 --- a/src/test/ui/unsafe/ranged_ints_macro.rs +++ b/src/test/ui/unsafe/ranged_ints_macro.rs @@ -1,4 +1,7 @@ // build-pass +// revisions: mir thir +// [thir]compile-flags: -Z thir-unsafeck + #![feature(rustc_attrs)] macro_rules! apply { diff --git a/src/test/ui/unsafe/unsafe-fn-assign-deref-ptr.stderr b/src/test/ui/unsafe/unsafe-fn-assign-deref-ptr.mir.stderr index b2a30f81e05..fee645e4118 100644 --- a/src/test/ui/unsafe/unsafe-fn-assign-deref-ptr.stderr +++ b/src/test/ui/unsafe/unsafe-fn-assign-deref-ptr.mir.stderr @@ -1,5 +1,5 @@ error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block - --> $DIR/unsafe-fn-assign-deref-ptr.rs:2:5 + --> $DIR/unsafe-fn-assign-deref-ptr.rs:5:5 | LL | *p = 0; | ^^^^^^ dereference of raw pointer diff --git a/src/test/ui/unsafe/unsafe-fn-assign-deref-ptr.rs b/src/test/ui/unsafe/unsafe-fn-assign-deref-ptr.rs index 91264e790c8..a94e94375ae 100644 --- a/src/test/ui/unsafe/unsafe-fn-assign-deref-ptr.rs +++ b/src/test/ui/unsafe/unsafe-fn-assign-deref-ptr.rs @@ -1,3 +1,6 @@ +// revisions: mir thir +// [thir]compile-flags: -Z thir-unsafeck + fn f(p: *mut u8) { *p = 0; //~ ERROR dereference of raw pointer is unsafe return; diff --git a/src/test/ui/unsafe/unsafe-fn-assign-deref-ptr.thir.stderr b/src/test/ui/unsafe/unsafe-fn-assign-deref-ptr.thir.stderr new file mode 100644 index 00000000000..498d26d30ff --- /dev/null +++ b/src/test/ui/unsafe/unsafe-fn-assign-deref-ptr.thir.stderr @@ -0,0 +1,11 @@ +error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block + --> $DIR/unsafe-fn-assign-deref-ptr.rs:5:5 + | +LL | *p = 0; + | ^^ dereference of raw pointer + | + = note: raw pointers may be NULL, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/unsafe/unsafe-fn-deref-ptr.stderr b/src/test/ui/unsafe/unsafe-fn-deref-ptr.mir.stderr index 98cb7b876f8..a2614992445 100644 --- a/src/test/ui/unsafe/unsafe-fn-deref-ptr.stderr +++ b/src/test/ui/unsafe/unsafe-fn-deref-ptr.mir.stderr @@ -1,5 +1,5 @@ error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block - --> $DIR/unsafe-fn-deref-ptr.rs:2:12 + --> $DIR/unsafe-fn-deref-ptr.rs:5:12 | LL | return *p; | ^^ dereference of raw pointer diff --git a/src/test/ui/unsafe/unsafe-fn-deref-ptr.rs b/src/test/ui/unsafe/unsafe-fn-deref-ptr.rs index 46445aa261d..dc989535bd6 100644 --- a/src/test/ui/unsafe/unsafe-fn-deref-ptr.rs +++ b/src/test/ui/unsafe/unsafe-fn-deref-ptr.rs @@ -1,3 +1,6 @@ +// revisions: mir thir +// [thir]compile-flags: -Z thir-unsafeck + fn f(p: *const u8) -> u8 { return *p; //~ ERROR dereference of raw pointer is unsafe } diff --git a/src/test/ui/unsafe/unsafe-fn-deref-ptr.thir.stderr b/src/test/ui/unsafe/unsafe-fn-deref-ptr.thir.stderr new file mode 100644 index 00000000000..6897e4e691a --- /dev/null +++ b/src/test/ui/unsafe/unsafe-fn-deref-ptr.thir.stderr @@ -0,0 +1,11 @@ +error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block + --> $DIR/unsafe-fn-deref-ptr.rs:5:12 + | +LL | return *p; + | ^^ dereference of raw pointer + | + = note: raw pointers may be NULL, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/unsafe/unsafe-unstable-const-fn.stderr b/src/test/ui/unsafe/unsafe-unstable-const-fn.mir.stderr index 410d8d3fb40..99808495ea6 100644 --- a/src/test/ui/unsafe/unsafe-unstable-const-fn.stderr +++ b/src/test/ui/unsafe/unsafe-unstable-const-fn.mir.stderr @@ -1,5 +1,5 @@ error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block - --> $DIR/unsafe-unstable-const-fn.rs:8:5 + --> $DIR/unsafe-unstable-const-fn.rs:11:5 | LL | *a == b | ^^ dereference of raw pointer diff --git a/src/test/ui/unsafe/unsafe-unstable-const-fn.rs b/src/test/ui/unsafe/unsafe-unstable-const-fn.rs index c7120e05007..0476759ca6d 100644 --- a/src/test/ui/unsafe/unsafe-unstable-const-fn.rs +++ b/src/test/ui/unsafe/unsafe-unstable-const-fn.rs @@ -1,3 +1,6 @@ +// revisions: mir thir +// [thir]compile-flags: -Z thir-unsafeck + #![stable(feature = "foo", since = "1.33.0")] #![feature(staged_api)] #![feature(const_raw_ptr_deref)] diff --git a/src/test/ui/unsafe/unsafe-unstable-const-fn.thir.stderr b/src/test/ui/unsafe/unsafe-unstable-const-fn.thir.stderr new file mode 100644 index 00000000000..49d6a96860b --- /dev/null +++ b/src/test/ui/unsafe/unsafe-unstable-const-fn.thir.stderr @@ -0,0 +1,11 @@ +error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block + --> $DIR/unsafe-unstable-const-fn.rs:11:5 + | +LL | *a == b + | ^^ dereference of raw pointer + | + = note: raw pointers may be NULL, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/wf/wf-static-method.nll.stderr b/src/test/ui/wf/wf-static-method.nll.stderr index d031a68a51d..9c066a7bdb0 100644 --- a/src/test/ui/wf/wf-static-method.nll.stderr +++ b/src/test/ui/wf/wf-static-method.nll.stderr @@ -68,7 +68,7 @@ LL | fn inherent_evil<'a, 'b>(b: &'b u32) -> &'a u32 { | -- -- lifetime `'b` defined here | | | lifetime `'a` defined here -LL | <Evil>::inherent_evil(b) // bug? shouldn't this be an error +LL | <Evil>::inherent_evil(b) | ^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` diff --git a/src/test/ui/wf/wf-static-method.rs b/src/test/ui/wf/wf-static-method.rs index e5a1092175d..6e805d61265 100644 --- a/src/test/ui/wf/wf-static-method.rs +++ b/src/test/ui/wf/wf-static-method.rs @@ -47,7 +47,8 @@ fn indirect_evil<'a, 'b>(b: &'b u32) -> &'a u32 { } fn inherent_evil<'a, 'b>(b: &'b u32) -> &'a u32 { - <Evil>::inherent_evil(b) // bug? shouldn't this be an error + <Evil>::inherent_evil(b) + //~^ ERROR cannot infer an appropriate lifetime } diff --git a/src/test/ui/wf/wf-static-method.stderr b/src/test/ui/wf/wf-static-method.stderr index 0c98a809025..c02a8fe4aaf 100644 --- a/src/test/ui/wf/wf-static-method.stderr +++ b/src/test/ui/wf/wf-static-method.stderr @@ -103,7 +103,34 @@ note: ...so that reference does not outlive borrowed content LL | <IndirectEvil>::static_evil(b) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 5 previous errors +error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'b` due to conflicting requirements + --> $DIR/wf-static-method.rs:50:5 + | +LL | <Evil>::inherent_evil(b) + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: first, the lifetime cannot outlive the lifetime `'b` as defined on the function body at 49:22... + --> $DIR/wf-static-method.rs:49:22 + | +LL | fn inherent_evil<'a, 'b>(b: &'b u32) -> &'a u32 { + | ^^ +note: ...so that reference does not outlive borrowed content + --> $DIR/wf-static-method.rs:50:27 + | +LL | <Evil>::inherent_evil(b) + | ^ +note: but, the lifetime must be valid for the lifetime `'a` as defined on the function body at 49:18... + --> $DIR/wf-static-method.rs:49:18 + | +LL | fn inherent_evil<'a, 'b>(b: &'b u32) -> &'a u32 { + | ^^ +note: ...so that reference does not outlive borrowed content + --> $DIR/wf-static-method.rs:50:5 + | +LL | <Evil>::inherent_evil(b) + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors Some errors have detailed explanations: E0312, E0478, E0495. For more information about an error, try `rustc --explain E0312`. diff --git a/src/tools/clippy/.cargo/config b/src/tools/clippy/.cargo/config index 9b5add4df1c..e95ea224cb6 100644 --- a/src/tools/clippy/.cargo/config +++ b/src/tools/clippy/.cargo/config @@ -2,6 +2,7 @@ uitest = "test --test compile-test" dev = "run --target-dir clippy_dev/target --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --" lintcheck = "run --target-dir lintcheck/target --package lintcheck --bin lintcheck --manifest-path lintcheck/Cargo.toml -- " +collect-metadata = "test --test dogfood --features metadata-collector-lint -- run_metadata_collection_lint --ignored" [build] rustflags = ["-Zunstable-options"] diff --git a/src/tools/clippy/.github/workflows/clippy_bors.yml b/src/tools/clippy/.github/workflows/clippy_bors.yml index ae6f1aa1b30..f27fee87dc1 100644 --- a/src/tools/clippy/.github/workflows/clippy_bors.yml +++ b/src/tools/clippy/.github/workflows/clippy_bors.yml @@ -90,6 +90,11 @@ jobs: - name: Checkout uses: actions/checkout@v2.3.3 + # FIXME: should not be necessary once 1.24.2 is the default version on the windows runner + - name: Update rustup + run: rustup self update + if: runner.os == 'Windows' + - name: Install toolchain run: rustup show active-toolchain diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 204d56e2a98..da5a0712c95 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6,11 +6,195 @@ document. ## Unreleased / In Rust Nightly -[6ed6f1e...master](https://github.com/rust-lang/rust-clippy/compare/6ed6f1e...master) +[7c7683c...master](https://github.com/rust-lang/rust-clippy/compare/7c7683c...master) + +## Rust 1.53 + +Current beta, release 2021-06-17 + +[6ed6f1e...7c7683c](https://github.com/rust-lang/rust-clippy/compare/6ed6f1e...7c7683c) + +### New Lints + +* [`option_filter_map`] + [#6342](https://github.com/rust-lang/rust-clippy/pull/6342) +* [`branches_sharing_code`] + [#6463](https://github.com/rust-lang/rust-clippy/pull/6463) +* [`needless_for_each`] + [#6706](https://github.com/rust-lang/rust-clippy/pull/6706) +* [`if_then_some_else_none`] + [#6859](https://github.com/rust-lang/rust-clippy/pull/6859) +* [`non_octal_unix_permissions`] + [#7001](https://github.com/rust-lang/rust-clippy/pull/7001) +* [`unnecessary_self_imports`] + [#7072](https://github.com/rust-lang/rust-clippy/pull/7072) +* [`bool_assert_comparison`] + [#7083](https://github.com/rust-lang/rust-clippy/pull/7083) +* [`cloned_instead_of_copied`] + [#7098](https://github.com/rust-lang/rust-clippy/pull/7098) +* [`flat_map_option`] + [#7101](https://github.com/rust-lang/rust-clippy/pull/7101) + +### Moves and Deprecations + +* Deprecate [`filter_map`] lint + [#7059](https://github.com/rust-lang/rust-clippy/pull/7059) +* Move [`transmute_ptr_to_ptr`] to `pedantic` + [#7102](https://github.com/rust-lang/rust-clippy/pull/7102) + +### Enhancements + +* [`mem_replace_with_default`]: Also lint on common std constructors + [#6820](https://github.com/rust-lang/rust-clippy/pull/6820) +* [`wrong_self_convention`]: Also lint on `to_*_mut` methods + [#6828](https://github.com/rust-lang/rust-clippy/pull/6828) +* [`wildcard_enum_match_arm`], [`match_wildcard_for_single_variants`]: + [#6863](https://github.com/rust-lang/rust-clippy/pull/6863) + * Attempt to find a common path prefix in suggestion + * Don't lint on `Option` and `Result` + * Consider `Self` prefix +* [`explicit_deref_methods`]: Also lint on chained `deref` calls + [#6865](https://github.com/rust-lang/rust-clippy/pull/6865) +* [`or_fun_call`]: Also lint on `unsafe` blocks + [#6928](https://github.com/rust-lang/rust-clippy/pull/6928) +* [`vec_box`], [`linkedlist`], [`option_option`]: Also lint in `const` and + `static` items [#6938](https://github.com/rust-lang/rust-clippy/pull/6938) +* [`search_is_some`]: Also check for `is_none` + [#6942](https://github.com/rust-lang/rust-clippy/pull/6942) +* [`string_lit_as_bytes`]: Also lint on `into_bytes` + [#6959](https://github.com/rust-lang/rust-clippy/pull/6959) +* [`len_without_is_empty`]: Also lint if function signatures of `len` and + `is_empty` don't match + [#6980](https://github.com/rust-lang/rust-clippy/pull/6980) +* [`redundant_pattern_matching`]: Also lint if the pattern is a `&` pattern + [#6991](https://github.com/rust-lang/rust-clippy/pull/6991) +* [`clone_on_copy`]: Also lint on chained method calls taking `self` by value + [#7000](https://github.com/rust-lang/rust-clippy/pull/7000) +* [`missing_panics_doc`]: Also lint on `assert_eq!` and `assert_ne!` + [#7029](https://github.com/rust-lang/rust-clippy/pull/7029) +* [`needless_return`]: Also lint in `async` functions + [#7067](https://github.com/rust-lang/rust-clippy/pull/7067) +* [`unused_io_amount`]: Also lint on expressions like `_.read().ok()?` + [#7100](https://github.com/rust-lang/rust-clippy/pull/7100) +* [`iter_cloned_collect`]: Also lint on large arrays, since const-generics are + now stable [#7138](https://github.com/rust-lang/rust-clippy/pull/7138) + +### False Positive Fixes + +* [`upper_case_acronyms`]: No longer lints on public items + [#6805](https://github.com/rust-lang/rust-clippy/pull/6805) +* [`suspicious_map`]: No longer lints when side effects may occur inside the + `map` call [#6831](https://github.com/rust-lang/rust-clippy/pull/6831) +* [`manual_map`], [`manual_unwrap_or`]: No longer lints in `const` functions + [#6917](https://github.com/rust-lang/rust-clippy/pull/6917) +* [`wrong_self_convention`]: Now respects `Copy` types + [#6924](https://github.com/rust-lang/rust-clippy/pull/6924) +* [`needless_question_mark`]: No longer lints if the `?` and the `Some(..)` come + from different macro contexts [#6935](https://github.com/rust-lang/rust-clippy/pull/6935) +* [`map_entry`]: Better detect if the entry API can be used + [#6937](https://github.com/rust-lang/rust-clippy/pull/6937) +* [`or_fun_call`]: No longer lints on some `len` function calls + [#6950](https://github.com/rust-lang/rust-clippy/pull/6950) +* [`new_ret_no_self`]: No longer lints when `Self` is returned with different + generic arguments [#6952](https://github.com/rust-lang/rust-clippy/pull/6952) +* [`upper_case_acronyms`]: No longer lints on public items + [#6981](https://github.com/rust-lang/rust-clippy/pull/6981) +* [`explicit_into_iter_loop`]: Only lint when `into_iter` is an implementation + of `IntoIterator` [#6982](https://github.com/rust-lang/rust-clippy/pull/6982) +* [`expl_impl_clone_on_copy`]: Take generic constraints into account before + suggesting to use `derive` instead + [#6993](https://github.com/rust-lang/rust-clippy/pull/6993) +* [`missing_panics_doc`]: No longer lints when only debug-assertions are used + [#6996](https://github.com/rust-lang/rust-clippy/pull/6996) +* [`clone_on_copy`]: Only lint when using the `Clone` trait + [#7000](https://github.com/rust-lang/rust-clippy/pull/7000) +* [`wrong_self_convention`]: No longer lints inside a trait implementation + [#7002](https://github.com/rust-lang/rust-clippy/pull/7002) +* [`redundant_clone`]: No longer lints when the cloned value is modified while + the clone is in use + [#7011](https://github.com/rust-lang/rust-clippy/pull/7011) +* [`same_item_push`]: No longer lints if the `Vec` is used in the loop body + [#7018](https://github.com/rust-lang/rust-clippy/pull/7018) +* [`cargo_common_metadata`]: Remove author requirement + [#7026](https://github.com/rust-lang/rust-clippy/pull/7026) +* [`panic_in_result_fn`]: No longer lints on `debug_assert` family + [#7060](https://github.com/rust-lang/rust-clippy/pull/7060) +* [`panic`]: No longer wrongfully lints on `debug_assert` with message + [#7063](https://github.com/rust-lang/rust-clippy/pull/7063) +* [`wrong_self_convention`]: No longer lints in trait implementations where no + `self` is involved [#7064](https://github.com/rust-lang/rust-clippy/pull/7064) +* [`missing_const_for_fn`]: No longer lints when unstable `const` function is + involved [#7076](https://github.com/rust-lang/rust-clippy/pull/7076) +* [`suspicious_else_formatting`]: Allow Allman style braces + [#7087](https://github.com/rust-lang/rust-clippy/pull/7087) +* [`inconsistent_struct_constructor`]: No longer lints in macros + [#7097](https://github.com/rust-lang/rust-clippy/pull/7097) +* [`single_component_path_imports`]: No longer lints on macro re-exports + [#7120](https://github.com/rust-lang/rust-clippy/pull/7120) + +### Suggestion Fixes/Improvements + +* [`redundant_pattern_matching`]: Add a note when applying this lint would + change the drop order + [#6568](https://github.com/rust-lang/rust-clippy/pull/6568) +* [`write_literal`], [`print_literal`]: Add auto-applicable suggestion + [#6821](https://github.com/rust-lang/rust-clippy/pull/6821) +* [`manual_map`]: Fix suggestion for complex `if let ... else` chains + [#6856](https://github.com/rust-lang/rust-clippy/pull/6856) +* [`inconsistent_struct_constructor`]: Make lint description and message clearer + [#6892](https://github.com/rust-lang/rust-clippy/pull/6892) +* [`map_entry`]: Now suggests `or_insert`, `insert_with` or `match _.entry(_)` + as appropriate [#6937](https://github.com/rust-lang/rust-clippy/pull/6937) +* [`manual_flatten`]: Suggest to insert `copied` if necessary + [#6962](https://github.com/rust-lang/rust-clippy/pull/6962) +* [`redundant_slicing`]: Fix suggestion when a re-borrow might be required or + when the value is from a macro call + [#6975](https://github.com/rust-lang/rust-clippy/pull/6975) +* [`match_wildcard_for_single_variants`]: Fix suggestion for hidden variant + [#6988](https://github.com/rust-lang/rust-clippy/pull/6988) +* [`clone_on_copy`]: Correct suggestion when the cloned value is a macro call + [#7000](https://github.com/rust-lang/rust-clippy/pull/7000) +* [`manual_map`]: Fix suggestion at the end of an if chain + [#7004](https://github.com/rust-lang/rust-clippy/pull/7004) +* Fix needless parenthesis output in multiple lint suggestions + [#7013](https://github.com/rust-lang/rust-clippy/pull/7013) +* [`needless_collect`]: Better explanation in the lint message + [#7020](https://github.com/rust-lang/rust-clippy/pull/7020) +* [`useless_vec`]: Now considers mutability + [#7036](https://github.com/rust-lang/rust-clippy/pull/7036) +* [`useless_format`]: Wrap the content in braces if necessary + [#7092](https://github.com/rust-lang/rust-clippy/pull/7092) +* [`single_match`]: Don't suggest an equality check for types which don't + implement `PartialEq` + [#7093](https://github.com/rust-lang/rust-clippy/pull/7093) +* [`from_over_into`]: Mention type in help message + [#7099](https://github.com/rust-lang/rust-clippy/pull/7099) +* [`manual_unwrap_or`]: Fix invalid code suggestion due to a macro call + [#7136](https://github.com/rust-lang/rust-clippy/pull/7136) + +### ICE Fixes + +* [`macro_use_imports`] + [#7022](https://github.com/rust-lang/rust-clippy/pull/7022) +* [`missing_panics_doc`] + [#7034](https://github.com/rust-lang/rust-clippy/pull/7034) +* [`tabs_in_doc_comments`] + [#7039](https://github.com/rust-lang/rust-clippy/pull/7039) +* [`missing_const_for_fn`] + [#7128](https://github.com/rust-lang/rust-clippy/pull/7128) + +### Others + +* [Clippy's lint + list](https://rust-lang.github.io/rust-clippy/master/index.html) now supports + themes [#7030](https://github.com/rust-lang/rust-clippy/pull/7030) +* Lints that were uplifted to `rustc` now mention the new `rustc` name in the + deprecation warning + [#7056](https://github.com/rust-lang/rust-clippy/pull/7056) ## Rust 1.52 -Current beta, release 2021-05-06 +Current stable, released 2021-05-06 [3e41797...6ed6f1e](https://github.com/rust-lang/rust-clippy/compare/3e41797...6ed6f1e) @@ -99,7 +283,7 @@ Current beta, release 2021-05-06 [#6682](https://github.com/rust-lang/rust-clippy/pull/6682) * [`unit_arg`]: No longer lints on unit arguments when they come from a path expression. [#6601](https://github.com/rust-lang/rust-clippy/pull/6601) -* [`cargo_common_metadata`]: No longer lints if +* [`cargo_common_metadata`]: No longer lints if [`publish = false`](https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field) is defined in the manifest [#6650](https://github.com/rust-lang/rust-clippy/pull/6650) @@ -124,11 +308,11 @@ Current beta, release 2021-05-06 * [`useless_format`]: Improved the documentation example [#6854](https://github.com/rust-lang/rust-clippy/pull/6854) -* Clippy's [`README.md`]: Includes a new subsection on running Clippy as a rustc wrapper +* Clippy's [`README.md`]: Includes a new subsection on running Clippy as a rustc wrapper [#6782](https://github.com/rust-lang/rust-clippy/pull/6782) ### Others -* Running `cargo clippy` after `cargo check` now works as expected +* Running `cargo clippy` after `cargo check` now works as expected (`cargo clippy` and `cargo check` no longer shares the same build cache) [#6687](https://github.com/rust-lang/rust-clippy/pull/6687) * Cargo now re-runs Clippy if arguments after `--` provided to `cargo clippy` are changed. @@ -145,7 +329,7 @@ Current beta, release 2021-05-06 ## Rust 1.51 -Current stable, released 2021-03-25 +Released 2021-03-25 [4911ab1...3e41797](https://github.com/rust-lang/rust-clippy/compare/4911ab1...3e41797) @@ -2365,6 +2549,7 @@ Released 2018-09-13 [`mutex_integer`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_integer [`naive_bytecount`]: https://rust-lang.github.io/rust-clippy/master/index.html#naive_bytecount [`needless_arbitrary_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_arbitrary_self_type +[`needless_bitwise_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bitwise_bool [`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool [`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow [`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference @@ -2538,6 +2723,7 @@ Released 2018-09-13 [`unsound_collection_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsound_collection_transmute [`unstable_as_mut_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_mut_slice [`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice +[`unused_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_async [`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect [`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index f010e609604..848476a9d05 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -49,7 +49,7 @@ rustc-workspace-hack = "1.0.0" rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util" } [features] -deny-warnings = [] +deny-warnings = ["clippy_lints/deny-warnings"] integration = ["tempfile"] internal-lints = ["clippy_lints/internal-lints"] metadata-collector-lint = ["internal-lints", "clippy_lints/metadata-collector-lint"] diff --git a/src/tools/clippy/clippy_dev/src/lib.rs b/src/tools/clippy/clippy_dev/src/lib.rs index 1e5a140e964..69f42aca8b6 100644 --- a/src/tools/clippy/clippy_dev/src/lib.rs +++ b/src/tools/clippy/clippy_dev/src/lib.rs @@ -1,5 +1,7 @@ -#![cfg_attr(feature = "deny-warnings", deny(warnings))] #![feature(once_cell)] +#![cfg_attr(feature = "deny-warnings", deny(warnings))] +// warn on lints, that are included in `rust-lang/rust`s bootstrap +#![warn(rust_2018_idioms, unused_lifetimes)] use itertools::Itertools; use regex::Regex; diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs index f4da783502c..7040c257c83 100644 --- a/src/tools/clippy/clippy_dev/src/main.rs +++ b/src/tools/clippy/clippy_dev/src/main.rs @@ -1,4 +1,6 @@ #![cfg_attr(feature = "deny-warnings", deny(warnings))] +// warn on lints, that are included in `rust-lang/rust`s bootstrap +#![warn(rust_2018_idioms, unused_lifetimes)] use clap::{App, Arg, ArgMatches, SubCommand}; use clippy_dev::{bless, fmt, ide_setup, new_lint, serve, stderr_length_check, update_lints}; diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index 7ceb1da6a6e..48f2972ec58 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -30,7 +30,7 @@ rustc-semver = "1.1.0" url = { version = "2.1.0", features = ["serde"] } [features] -deny-warnings = [] +deny-warnings = ["clippy_utils/deny-warnings"] # build clippy with internal lints enabled, off by default internal-lints = ["clippy_utils/internal-lints"] metadata-collector-lint = ["serde_json", "clippy_utils/metadata-collector-lint"] diff --git a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs index 4688b3d5105..50ffc2e3f19 100644 --- a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs +++ b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs @@ -1,6 +1,13 @@ +/// This struct fakes the `Lint` declaration that is usually created by `declare_lint!`. This +/// enables the simple extraction of the metadata without changing the current deprecation +/// declaration. +pub struct ClippyDeprecatedLint; + macro_rules! declare_deprecated_lint { - (pub $name: ident, $_reason: expr) => { - declare_lint!(pub $name, Allow, "deprecated lint") + { $(#[$attr:meta])* pub $name: ident, $_reason: expr} => { + $(#[$attr])* + #[allow(dead_code)] + pub static $name: ClippyDeprecatedLint = ClippyDeprecatedLint {}; } } diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs index e742cd626ab..840c1eba79d 100644 --- a/src/tools/clippy/clippy_lints/src/derive.rs +++ b/src/tools/clippy/clippy_lints/src/derive.rs @@ -12,7 +12,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{source_map::Span}; +use rustc_span::source_map::Span; declare_clippy_lint! { /// **What it does:** Checks for deriving `Hash` but implementing `PartialEq` @@ -310,15 +310,11 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &T // there's a Copy impl for any instance of the adt. if !is_copy(cx, ty) { if ty_subs.non_erasable_generics().next().is_some() { - let has_copy_impl = cx - .tcx - .all_local_trait_impls(()) - .get(©_id) - .map_or(false, |impls| { - impls - .iter() - .any(|&id| matches!(cx.tcx.type_of(id).kind(), ty::Adt(adt, _) if ty_adt.did == adt.did)) - }); + let has_copy_impl = cx.tcx.all_local_trait_impls(()).get(©_id).map_or(false, |impls| { + impls + .iter() + .any(|&id| matches!(cx.tcx.type_of(id).kind(), ty::Adt(adt, _) if ty_adt.did == adt.did)) + }); if !has_copy_impl { return; } diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs index fb53b55ebd6..e67ec4e06c5 100644 --- a/src/tools/clippy/clippy_lints/src/doc.rs +++ b/src/tools/clippy/clippy_lints/src/doc.rs @@ -383,7 +383,7 @@ pub fn strip_doc_comment_decoration(doc: &str, comment_kind: CommentKind, span: let mut no_stars = String::with_capacity(doc.len()); for line in doc.lines() { let mut chars = line.chars(); - while let Some(c) = chars.next() { + for c in &mut chars { if c.is_whitespace() { no_stars.push(c); } else { 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 e0b687b0205..08f28cd54c5 100644 --- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs @@ -323,7 +323,7 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { cx, SUBOPTIMAL_FLOPS, parent.span, - "square can be computed more efficiently", + "multiply and add expressions can be calculated more efficiently and accurately", "consider using", format!( "{}.mul_add({}, {})", @@ -337,16 +337,6 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { return; } } - - span_lint_and_sugg( - cx, - SUBOPTIMAL_FLOPS, - expr.span, - "square can be computed more efficiently", - "consider using", - format!("{} * {}", Sugg::hir(cx, &args[0], ".."), Sugg::hir(cx, &args[0], "..")), - Applicability::MachineApplicable, - ); } } } diff --git a/src/tools/clippy/clippy_lints/src/implicit_return.rs b/src/tools/clippy/clippy_lints/src/implicit_return.rs index 30174fa2100..260a8f50157 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_return.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_return.rs @@ -147,7 +147,11 @@ fn lint_implicit_returns( visit_break_exprs(block, |break_expr, dest, sub_expr| { if dest.target_id.ok() == Some(expr.hir_id) { if call_site_span.is_none() && break_expr.span.ctxt() == ctxt { - lint_break(cx, break_expr.span, sub_expr.unwrap().span); + // At this point sub_expr can be `None` in async functions which either diverge, or return the + // unit type. + if let Some(sub_expr) = sub_expr { + lint_break(cx, break_expr.span, sub_expr.span); + } } else { // the break expression is from a macro call, add a return to the loop add_return = true; diff --git a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs index d138c3a8acf..3b635071f28 100644 --- a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs @@ -58,7 +58,7 @@ declare_clippy_lint! { /// Foo { x, y }; /// ``` pub INCONSISTENT_STRUCT_CONSTRUCTOR, - style, + pedantic, "the order of the field init shorthand is inconsistent with the order in the struct definition" } diff --git a/src/tools/clippy/clippy_lints/src/inherent_impl.rs b/src/tools/clippy/clippy_lints/src/inherent_impl.rs index 4e0b1ae78df..ee41c4aea2f 100644 --- a/src/tools/clippy/clippy_lints/src/inherent_impl.rs +++ b/src/tools/clippy/clippy_lints/src/inherent_impl.rs @@ -1,12 +1,13 @@ //! lint on inherent implementations -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::in_macro; -use rustc_hir::def_id::DefIdMap; -use rustc_hir::{Crate, Impl, Item, ItemKind}; +use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::{in_macro, is_allowed}; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::{def_id::LocalDefId, Crate, Item, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; +use std::collections::hash_map::Entry; declare_clippy_lint! { /// **What it does:** Checks for multiple inherent implementations of a struct @@ -40,51 +41,96 @@ declare_clippy_lint! { "Multiple inherent impl that could be grouped" } -#[allow(clippy::module_name_repetitions)] -#[derive(Default)] -pub struct MultipleInherentImpl { - impls: DefIdMap<Span>, -} - -impl_lint_pass!(MultipleInherentImpl => [MULTIPLE_INHERENT_IMPL]); +declare_lint_pass!(MultipleInherentImpl => [MULTIPLE_INHERENT_IMPL]); impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { - fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if let ItemKind::Impl(Impl { - ref generics, - of_trait: None, - .. - }) = item.kind + fn check_crate_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx Crate<'_>) { + // Map from a type to it's first impl block. Needed to distinguish generic arguments. + // e.g. `Foo<Bar>` and `Foo<Baz>` + let mut type_map = FxHashMap::default(); + // List of spans to lint. (lint_span, first_span) + let mut lint_spans = Vec::new(); + + for (_, impl_ids) in cx + .tcx + .crate_inherent_impls(()) + .inherent_impls + .iter() + .filter(|(&id, impls)| { + impls.len() > 1 + // Check for `#[allow]` on the type definition + && !is_allowed( + cx, + MULTIPLE_INHERENT_IMPL, + cx.tcx.hir().local_def_id_to_hir_id(id), + ) + }) { - // Remember for each inherent implementation encountered its span and generics - // but filter out implementations that have generic params (type or lifetime) - // or are derived from a macro - if !in_macro(item.span) && generics.params.is_empty() { - self.impls.insert(item.def_id.to_def_id(), item.span); + for impl_id in impl_ids.iter().map(|id| id.expect_local()) { + match type_map.entry(cx.tcx.type_of(impl_id)) { + Entry::Vacant(e) => { + // Store the id for the first impl block of this type. The span is retrieved lazily. + e.insert(IdOrSpan::Id(impl_id)); + }, + Entry::Occupied(mut e) => { + if let Some(span) = get_impl_span(cx, impl_id) { + let first_span = match *e.get() { + IdOrSpan::Span(s) => s, + IdOrSpan::Id(id) => { + if let Some(s) = get_impl_span(cx, id) { + // Remember the span of the first block. + *e.get_mut() = IdOrSpan::Span(s); + s + } else { + // The first impl block isn't considered by the lint. Replace it with the + // current one. + *e.get_mut() = IdOrSpan::Span(span); + continue; + } + }, + }; + lint_spans.push((span, first_span)); + } + }, + } } + + // Switching to the next type definition, no need to keep the current entries around. + type_map.clear(); } - } - fn check_crate_post(&mut self, cx: &LateContext<'tcx>, krate: &'tcx Crate<'_>) { - if !krate.items.is_empty() { - // Retrieve all inherent implementations from the crate, grouped by type - for impls in cx.tcx.crate_inherent_impls(()).inherent_impls.values() { - // Filter out implementations that have generic params (type or lifetime) - let mut impl_spans = impls.iter().filter_map(|impl_def| self.impls.get(impl_def)); - if let Some(initial_span) = impl_spans.next() { - impl_spans.for_each(|additional_span| { - span_lint_and_then( - cx, - MULTIPLE_INHERENT_IMPL, - *additional_span, - "multiple implementations of this structure", - |diag| { - diag.span_note(*initial_span, "first implementation here"); - }, - ) - }) - } - } + // `TyCtxt::crate_inherent_impls` doesn't have a defined order. Sort the lint output first. + lint_spans.sort_by_key(|x| x.0.lo()); + for (span, first_span) in lint_spans { + span_lint_and_note( + cx, + MULTIPLE_INHERENT_IMPL, + span, + "multiple implementations of this structure", + Some(first_span), + "first implementation here", + ); } } } + +/// Gets the span for the given impl block unless it's not being considered by the lint. +fn get_impl_span(cx: &LateContext<'_>, id: LocalDefId) -> Option<Span> { + let id = cx.tcx.hir().local_def_id_to_hir_id(id); + if let Node::Item(&Item { + kind: ItemKind::Impl(ref impl_item), + span, + .. + }) = cx.tcx.hir().get(id) + { + (!in_macro(span) && impl_item.generics.params.is_empty() && !is_allowed(cx, MULTIPLE_INHERENT_IMPL, id)) + .then(|| span) + } else { + None + } +} + +enum IdOrSpan { + Id(LocalDefId), + Span(Span), +} diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 725aa54157e..eb85cca0bd3 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -163,6 +163,8 @@ macro_rules! extract_msrv_attr { mod consts; #[macro_use] mod utils; +#[cfg(feature = "metadata-collector-lint")] +mod deprecated_lints; // begin lints modules, do not remove this comment, it’s used in `update_lints` mod absurd_extreme_comparisons; @@ -289,6 +291,7 @@ mod mut_reference; mod mutable_debug_assertion; mod mutex_atomic; mod needless_arbitrary_self_type; +mod needless_bitwise_bool; mod needless_bool; mod needless_borrow; mod needless_borrowed_ref; @@ -363,6 +366,7 @@ mod unnecessary_sort_by; mod unnecessary_wraps; mod unnested_or_patterns; mod unsafe_removed_from_name; +mod unused_async; mod unused_io_amount; mod unused_self; mod unused_unit; @@ -834,6 +838,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: mutex_atomic::MUTEX_ATOMIC, mutex_atomic::MUTEX_INTEGER, needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE, + needless_bitwise_bool::NEEDLESS_BITWISE_BOOL, needless_bool::BOOL_COMPARISON, needless_bool::NEEDLESS_BOOL, needless_borrow::NEEDLESS_BORROW, @@ -960,6 +965,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: unnecessary_wraps::UNNECESSARY_WRAPS, unnested_or_patterns::UNNESTED_OR_PATTERNS, unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, + unused_async::UNUSED_ASYNC, unused_io_amount::UNUSED_IO_AMOUNT, unused_self::UNUSED_SELF, unused_unit::UNUSED_UNIT, @@ -1008,7 +1014,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: #[cfg(feature = "metadata-collector-lint")] { if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) { - store.register_late_pass(|| box utils::internal_lints::metadata_collector::MetadataCollector::default()); + store.register_late_pass(|| box utils::internal_lints::metadata_collector::MetadataCollector::new()); } } @@ -1019,6 +1025,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let type_complexity_threshold = conf.type_complexity_threshold; store.register_late_pass(move || box types::Types::new(vec_box_size_threshold, type_complexity_threshold)); store.register_late_pass(|| box booleans::NonminimalBool); + store.register_late_pass(|| box needless_bitwise_bool::NeedlessBitwiseBool); store.register_late_pass(|| box eq_op::EqOp); store.register_late_pass(|| box enum_clike::UnportableVariant); store.register_late_pass(|| box float_literal::FloatLiteral); @@ -1155,7 +1162,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box suspicious_operation_groupings::SuspiciousOperationGroupings); store.register_late_pass(|| box suspicious_trait_impl::SuspiciousImpl); store.register_late_pass(|| box map_unit_fn::MapUnit); - store.register_late_pass(|| box inherent_impl::MultipleInherentImpl::default()); + store.register_late_pass(|| box inherent_impl::MultipleInherentImpl); store.register_late_pass(|| box neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd); store.register_late_pass(|| box unwrap::Unwrap); store.register_late_pass(|| box duration_subsec::DurationSubsec); @@ -1272,6 +1279,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box manual_map::ManualMap); store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv)); store.register_early_pass(|| box bool_assert_comparison::BoolAssertComparison); + store.register_late_pass(|| box unused_async::UnusedAsync); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(arithmetic::FLOAT_ARITHMETIC), @@ -1365,6 +1373,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(if_not_else::IF_NOT_ELSE), LintId::of(implicit_hasher::IMPLICIT_HASHER), LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB), + LintId::of(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR), LintId::of(infinite_iter::MAYBE_INFINITE_ITER), LintId::of(invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS), LintId::of(items_after_statements::ITEMS_AFTER_STATEMENTS), @@ -1392,6 +1401,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(misc::USED_UNDERSCORE_BINDING), LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX), LintId::of(mut_mut::MUT_MUT), + LintId::of(needless_bitwise_bool::NEEDLESS_BITWISE_BOOL), LintId::of(needless_continue::NEEDLESS_CONTINUE), LintId::of(needless_for_each::NEEDLESS_FOR_EACH), LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE), @@ -1415,6 +1425,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(unit_types::LET_UNIT_VALUE), LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS), LintId::of(unnested_or_patterns::UNNESTED_OR_PATTERNS), + LintId::of(unused_async::UNUSED_ASYNC), LintId::of(unused_self::UNUSED_SELF), LintId::of(wildcard_imports::ENUM_GLOB_USE), LintId::of(wildcard_imports::WILDCARD_IMPORTS), @@ -1511,7 +1522,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(identity_op::IDENTITY_OP), LintId::of(if_let_mutex::IF_LET_MUTEX), LintId::of(if_let_some_result::IF_LET_SOME_RESULT), - LintId::of(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR), LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING), LintId::of(infinite_iter::INFINITE_ITER), LintId::of(inherent_to_string::INHERENT_TO_STRING), @@ -1764,7 +1774,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(functions::MUST_USE_UNIT), LintId::of(functions::RESULT_UNIT_ERR), LintId::of(if_let_some_result::IF_LET_SOME_RESULT), - LintId::of(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR), LintId::of(inherent_to_string::INHERENT_TO_STRING), LintId::of(len_zero::COMPARISON_TO_EMPTY), LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY), diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs b/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs index 9662a0b22a3..d3406780888 100644 --- a/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs +++ b/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs @@ -1,16 +1,15 @@ use super::NEEDLESS_COLLECT; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::snippet; +use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{is_type_diagnostic_item, match_type}; -use clippy_utils::{is_trait_method, path_to_local_id, paths}; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{is_trait_method, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{Block, Expr, ExprKind, GenericArg, GenericArgs, HirId, Local, Pat, PatKind, QPath, StmtKind, Ty}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; - use rustc_span::symbol::{sym, Ident}; use rustc_span::{MultiSpan, Span}; @@ -28,23 +27,37 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont if let Some(generic_args) = chain_method.args; if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0); if let Some(ty) = cx.typeck_results().node_type_opt(ty.hir_id); - if is_type_diagnostic_item(cx, ty, sym::vec_type) - || is_type_diagnostic_item(cx, ty, sym::vecdeque_type) - || match_type(cx, ty, &paths::BTREEMAP) - || is_type_diagnostic_item(cx, ty, sym::hashmap_type); - if let Some(sugg) = match &*method.ident.name.as_str() { - "len" => Some("count()".to_string()), - "is_empty" => Some("next().is_none()".to_string()), - "contains" => { - let contains_arg = snippet(cx, args[1].span, "??"); - let (arg, pred) = contains_arg - .strip_prefix('&') - .map_or(("&x", &*contains_arg), |s| ("x", s)); - Some(format!("any(|{}| x == {})", arg, pred)) - } - _ => None, - }; then { + let mut applicability = Applicability::MachineApplicable; + let is_empty_sugg = "next().is_none()".to_string(); + let method_name = &*method.ident.name.as_str(); + let sugg = if is_type_diagnostic_item(cx, ty, sym::vec_type) || + is_type_diagnostic_item(cx, ty, sym::vecdeque_type) || + is_type_diagnostic_item(cx, ty, sym::LinkedList) || + is_type_diagnostic_item(cx, ty, sym::BinaryHeap) { + match method_name { + "len" => "count()".to_string(), + "is_empty" => is_empty_sugg, + "contains" => { + let contains_arg = snippet_with_applicability(cx, args[1].span, "??", &mut applicability); + let (arg, pred) = contains_arg + .strip_prefix('&') + .map_or(("&x", &*contains_arg), |s| ("x", s)); + format!("any(|{}| x == {})", arg, pred) + } + _ => return, + } + } + else if is_type_diagnostic_item(cx, ty, sym::BTreeMap) || + is_type_diagnostic_item(cx, ty, sym::hashmap_type) { + match method_name { + "is_empty" => is_empty_sugg, + _ => return, + } + } + else { + return; + }; span_lint_and_sugg( cx, NEEDLESS_COLLECT, @@ -52,7 +65,7 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont NEEDLESS_COLLECT_MSG, "replace with", sugg, - Applicability::MachineApplicable, + applicability, ); } } @@ -86,7 +99,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo if is_type_diagnostic_item(cx, ty, sym::vec_type) || is_type_diagnostic_item(cx, ty, sym::vecdeque_type) || is_type_diagnostic_item(cx, ty, sym::BinaryHeap) || - match_type(cx, ty, &paths::LINKED_LIST); + is_type_diagnostic_item(cx, ty, sym::LinkedList); if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident); if let [iter_call] = &*iter_calls; then { diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs index 82715d9bafa..63560047578 100644 --- a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs @@ -1,170 +1,347 @@ -use super::utils::{LoopNestVisitor, Nesting}; use super::WHILE_LET_ON_ITERATOR; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::implements_trait; -use clippy_utils::usage::mutated_variables; -use clippy_utils::{ - get_enclosing_block, is_refutable, is_trait_method, last_path_segment, path_to_local, path_to_local_id, -}; +use clippy_utils::{get_enclosing_loop, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::{Expr, ExprKind, HirId, MatchSource, Node, PatKind}; +use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}; +use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, MatchSource, Node, PatKind, QPath, UnOp}; use rustc_lint::LateContext; -use rustc_middle::hir::map::Map; -use rustc_span::symbol::sym; +use rustc_span::{symbol::sym, Span, Symbol}; pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Match(match_expr, arms, MatchSource::WhileLetDesugar) = expr.kind { - let pat = &arms[0].pat.kind; - if let (&PatKind::TupleStruct(ref qpath, pat_args, _), &ExprKind::MethodCall(method_path, _, method_args, _)) = - (pat, &match_expr.kind) - { - let iter_expr = &method_args[0]; - - // Don't lint when the iterator is recreated on every iteration - if_chain! { - if let ExprKind::MethodCall(..) | ExprKind::Call(..) = iter_expr.kind; - if let Some(iter_def_id) = cx.tcx.get_diagnostic_item(sym::Iterator); - if implements_trait(cx, cx.typeck_results().expr_ty(iter_expr), iter_def_id, &[]); - then { - return; - } - } + let (scrutinee_expr, iter_expr, some_pat, loop_expr) = if_chain! { + if let ExprKind::Match(scrutinee_expr, [arm, _], MatchSource::WhileLetDesugar) = expr.kind; + // check for `Some(..)` pattern + if let PatKind::TupleStruct(QPath::Resolved(None, pat_path), some_pat, _) = arm.pat.kind; + if let Res::Def(_, pat_did) = pat_path.res; + if match_def_path(cx, pat_did, &paths::OPTION_SOME); + // check for call to `Iterator::next` + if let ExprKind::MethodCall(method_name, _, [iter_expr], _) = scrutinee_expr.kind; + if method_name.ident.name == sym::next; + if is_trait_method(cx, scrutinee_expr, sym::Iterator); + if let Some(iter_expr) = try_parse_iter_expr(cx, iter_expr); + // get the loop containing the match expression + if let Some((_, Node::Expr(loop_expr))) = cx.tcx.hir().parent_iter(expr.hir_id).nth(1); + if !uses_iter(cx, &iter_expr, arm.body); + then { + (scrutinee_expr, iter_expr, some_pat, loop_expr) + } else { + return; + } + }; - let lhs_constructor = last_path_segment(qpath); - if method_path.ident.name == sym::next - && is_trait_method(cx, match_expr, sym::Iterator) - && lhs_constructor.ident.name == sym::Some - && (pat_args.is_empty() - || !is_refutable(cx, pat_args[0]) - && !is_used_inside(cx, iter_expr, arms[0].body) - && !is_iterator_used_after_while_let(cx, iter_expr) - && !is_nested(cx, expr, &method_args[0])) - { - let mut applicability = Applicability::MachineApplicable; - let iterator = snippet_with_applicability(cx, method_args[0].span, "_", &mut applicability); - let loop_var = if pat_args.is_empty() { - "_".to_string() - } else { - snippet_with_applicability(cx, pat_args[0].span, "_", &mut applicability).into_owned() - }; - span_lint_and_sugg( - cx, - WHILE_LET_ON_ITERATOR, - expr.span.with_hi(match_expr.span.hi()), - "this loop could be written as a `for` loop", - "try", - format!("for {} in {}", loop_var, iterator), - applicability, - ); - } + let mut applicability = Applicability::MachineApplicable; + let loop_var = if let Some(some_pat) = some_pat.first() { + if is_refutable(cx, some_pat) { + // Refutable patterns don't work with for loops. + return; } - } -} + snippet_with_applicability(cx, some_pat.span, "..", &mut applicability) + } else { + "_".into() + }; -fn is_used_inside<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, container: &'tcx Expr<'_>) -> bool { - let def_id = match path_to_local(expr) { - Some(id) => id, - None => return false, + // If the iterator is a field or the iterator is accessed after the loop is complete it needs to be + // borrowed mutably. TODO: If the struct can be partially moved from and the struct isn't used + // afterwards a mutable borrow of a field isn't necessary. + let ref_mut = if !iter_expr.fields.is_empty() || needs_mutable_borrow(cx, &iter_expr, loop_expr) { + "&mut " + } else { + "" }; - if let Some(used_mutably) = mutated_variables(container, cx) { - if used_mutably.contains(&def_id) { - return true; + + let iterator = snippet_with_applicability(cx, iter_expr.span, "_", &mut applicability); + span_lint_and_sugg( + cx, + WHILE_LET_ON_ITERATOR, + expr.span.with_hi(scrutinee_expr.span.hi()), + "this loop could be written as a `for` loop", + "try", + format!("for {} in {}{}", loop_var, ref_mut, iterator), + applicability, + ); +} + +#[derive(Debug)] +struct IterExpr { + /// The span of the whole expression, not just the path and fields stored here. + span: Span, + /// The fields used, in order of child to parent. + fields: Vec<Symbol>, + /// The path being used. + path: Res, +} +/// Parses any expression to find out which field of which variable is used. Will return `None` if +/// the expression might have side effects. +fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option<IterExpr> { + let span = e.span; + let mut fields = Vec::new(); + loop { + match e.kind { + ExprKind::Path(ref path) => { + break Some(IterExpr { + span, + fields, + path: cx.qpath_res(path, e.hir_id), + }); + }, + ExprKind::Field(base, name) => { + fields.push(name.name); + e = base; + }, + // Dereferencing a pointer has no side effects and doesn't affect which field is being used. + ExprKind::Unary(UnOp::Deref, base) if cx.typeck_results().expr_ty(base).is_ref() => e = base, + + // Shouldn't have side effects, but there's no way to trace which field is used. So forget which fields have + // already been seen. + ExprKind::Index(base, idx) if !idx.can_have_side_effects() => { + fields.clear(); + e = base; + }, + ExprKind::Unary(UnOp::Deref, base) => { + fields.clear(); + e = base; + }, + + // No effect and doesn't affect which field is being used. + ExprKind::DropTemps(base) | ExprKind::AddrOf(_, _, base) | ExprKind::Type(base, _) => e = base, + _ => break None, } } - false } -fn is_iterator_used_after_while_let<'tcx>(cx: &LateContext<'tcx>, iter_expr: &'tcx Expr<'_>) -> bool { - let def_id = match path_to_local(iter_expr) { - Some(id) => id, - None => return false, - }; - let mut visitor = VarUsedAfterLoopVisitor { - def_id, - iter_expr_id: iter_expr.hir_id, - past_while_let: false, - var_used_after_while_let: false, - }; - if let Some(enclosing_block) = get_enclosing_block(cx, def_id) { - walk_block(&mut visitor, enclosing_block); +fn is_expr_same_field(cx: &LateContext<'_>, mut e: &Expr<'_>, mut fields: &[Symbol], path_res: Res) -> bool { + loop { + match (&e.kind, fields) { + (&ExprKind::Field(base, name), [head_field, tail_fields @ ..]) if name.name == *head_field => { + e = base; + fields = tail_fields; + }, + (ExprKind::Path(path), []) => { + break cx.qpath_res(path, e.hir_id) == path_res; + }, + (&(ExprKind::DropTemps(base) | ExprKind::AddrOf(_, _, base) | ExprKind::Type(base, _)), _) => e = base, + _ => break false, + } } - visitor.var_used_after_while_let } -fn is_nested(cx: &LateContext<'_>, match_expr: &Expr<'_>, iter_expr: &Expr<'_>) -> bool { - if_chain! { - if let Some(loop_block) = get_enclosing_block(cx, match_expr.hir_id); - let parent_node = cx.tcx.hir().get_parent_node(loop_block.hir_id); - if let Some(Node::Expr(loop_expr)) = cx.tcx.hir().find(parent_node); - then { - return is_loop_nested(cx, loop_expr, iter_expr) - } +/// Checks if the given expression is the same field as, is a child of, or is the parent of the +/// given field. Used to check if the expression can be used while the given field is borrowed +/// mutably. e.g. if checking for `x.y`, then `x.y`, `x.y.z`, and `x` will all return true, but +/// `x.z`, and `y` will return false. +fn is_expr_same_child_or_parent_field(cx: &LateContext<'_>, expr: &Expr<'_>, fields: &[Symbol], path_res: Res) -> bool { + match expr.kind { + ExprKind::Field(base, name) => { + if let Some((head_field, tail_fields)) = fields.split_first() { + if name.name == *head_field && is_expr_same_field(cx, base, fields, path_res) { + return true; + } + // Check if the expression is a parent field + let mut fields_iter = tail_fields.iter(); + while let Some(field) = fields_iter.next() { + if *field == name.name && is_expr_same_field(cx, base, fields_iter.as_slice(), path_res) { + return true; + } + } + } + + // Check if the expression is a child field. + let mut e = base; + loop { + match e.kind { + ExprKind::Field(..) if is_expr_same_field(cx, e, fields, path_res) => break true, + ExprKind::Field(base, _) | ExprKind::DropTemps(base) | ExprKind::Type(base, _) => e = base, + ExprKind::Path(ref path) if fields.is_empty() => { + break cx.qpath_res(path, e.hir_id) == path_res; + }, + _ => break false, + } + } + }, + // If the path matches, this is either an exact match, or the expression is a parent of the field. + ExprKind::Path(ref path) => cx.qpath_res(path, expr.hir_id) == path_res, + ExprKind::DropTemps(base) | ExprKind::Type(base, _) | ExprKind::AddrOf(_, _, base) => { + is_expr_same_child_or_parent_field(cx, base, fields, path_res) + }, + _ => false, } - false } -fn is_loop_nested(cx: &LateContext<'_>, loop_expr: &Expr<'_>, iter_expr: &Expr<'_>) -> bool { - let mut id = loop_expr.hir_id; - let iter_id = if let Some(id) = path_to_local(iter_expr) { - id - } else { - return true; +/// Strips off all field and path expressions. This will return true if a field or path has been +/// skipped. Used to skip them after failing to check for equality. +fn skip_fields_and_path(expr: &'tcx Expr<'_>) -> (Option<&'tcx Expr<'tcx>>, bool) { + let mut e = expr; + let e = loop { + match e.kind { + ExprKind::Field(base, _) | ExprKind::DropTemps(base) | ExprKind::Type(base, _) => e = base, + ExprKind::Path(_) => return (None, true), + _ => break e, + } }; - loop { - let parent = cx.tcx.hir().get_parent_node(id); - if parent == id { - return false; + (Some(e), e.hir_id != expr.hir_id) +} + +/// Checks if the given expression uses the iterator. +fn uses_iter(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tcx Expr<'_>) -> bool { + struct V<'a, 'b, 'tcx> { + cx: &'a LateContext<'tcx>, + iter_expr: &'b IterExpr, + uses_iter: bool, + } + impl Visitor<'tcx> for V<'_, '_, 'tcx> { + type Map = ErasedMap<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::None } - match cx.tcx.hir().find(parent) { - Some(Node::Expr(expr)) => { - if let ExprKind::Loop(..) = expr.kind { - return true; - }; - }, - Some(Node::Block(block)) => { - let mut block_visitor = LoopNestVisitor { - hir_id: id, - iterator: iter_id, - nesting: Nesting::Unknown, - }; - walk_block(&mut block_visitor, block); - if block_visitor.nesting == Nesting::RuledOut { - return false; + + fn visit_expr(&mut self, e: &'tcx Expr<'_>) { + if self.uses_iter { + // return + } else if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) { + self.uses_iter = true; + } else if let (e, true) = skip_fields_and_path(e) { + if let Some(e) = e { + self.visit_expr(e); } - }, - Some(Node::Stmt(_)) => (), - _ => { - return false; - }, + } else if let ExprKind::Closure(_, _, id, _, _) = e.kind { + if is_res_used(self.cx, self.iter_expr.path, id) { + self.uses_iter = true; + } + } else { + walk_expr(self, e); + } } - id = parent; } -} -struct VarUsedAfterLoopVisitor { - def_id: HirId, - iter_expr_id: HirId, - past_while_let: bool, - var_used_after_while_let: bool, + let mut v = V { + cx, + iter_expr, + uses_iter: false, + }; + v.visit_expr(container); + v.uses_iter } -impl<'tcx> Visitor<'tcx> for VarUsedAfterLoopVisitor { - type Map = Map<'tcx>; +#[allow(clippy::too_many_lines)] +fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr: &'tcx Expr<'_>) -> bool { + struct AfterLoopVisitor<'a, 'b, 'tcx> { + cx: &'a LateContext<'tcx>, + iter_expr: &'b IterExpr, + loop_id: HirId, + after_loop: bool, + used_iter: bool, + } + impl Visitor<'tcx> for AfterLoopVisitor<'_, '_, 'tcx> { + type Map = ErasedMap<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::None + } - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.past_while_let { - if path_to_local_id(expr, self.def_id) { - self.var_used_after_while_let = true; + fn visit_expr(&mut self, e: &'tcx Expr<'_>) { + if self.used_iter { + return; + } + if self.after_loop { + if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) { + self.used_iter = true; + } else if let (e, true) = skip_fields_and_path(e) { + if let Some(e) = e { + self.visit_expr(e); + } + } else if let ExprKind::Closure(_, _, id, _, _) = e.kind { + self.used_iter = is_res_used(self.cx, self.iter_expr.path, id); + } else { + walk_expr(self, e); + } + } else if self.loop_id == e.hir_id { + self.after_loop = true; + } else { + walk_expr(self, e); + } + } + } + + struct NestedLoopVisitor<'a, 'b, 'tcx> { + cx: &'a LateContext<'tcx>, + iter_expr: &'b IterExpr, + local_id: HirId, + loop_id: HirId, + after_loop: bool, + found_local: bool, + used_after: bool, + } + impl Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> { + type Map = ErasedMap<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::None + } + + fn visit_local(&mut self, l: &'tcx Local<'_>) { + if !self.after_loop { + l.pat.each_binding_or_first(&mut |_, id, _, _| { + if id == self.local_id { + self.found_local = true; + } + }); + } + if let Some(e) = l.init { + self.visit_expr(e); + } + } + + fn visit_expr(&mut self, e: &'tcx Expr<'_>) { + if self.used_after { + return; + } + if self.after_loop { + if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) { + self.used_after = true; + } else if let (e, true) = skip_fields_and_path(e) { + if let Some(e) = e { + self.visit_expr(e); + } + } else if let ExprKind::Closure(_, _, id, _, _) = e.kind { + self.used_after = is_res_used(self.cx, self.iter_expr.path, id); + } else { + walk_expr(self, e); + } + } else if e.hir_id == self.loop_id { + self.after_loop = true; + } else { + walk_expr(self, e); } - } else if self.iter_expr_id == expr.hir_id { - self.past_while_let = true; } - walk_expr(self, expr); } - fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { - NestedVisitorMap::None + + if let Some(e) = get_enclosing_loop(cx.tcx, loop_expr) { + // The iterator expression will be used on the next iteration unless it is declared within the outer + // loop. + let local_id = match iter_expr.path { + Res::Local(id) => id, + _ => return true, + }; + let mut v = NestedLoopVisitor { + cx, + iter_expr, + local_id, + loop_id: loop_expr.hir_id, + after_loop: false, + found_local: false, + used_after: false, + }; + v.visit_expr(e); + v.used_after || !v.found_local + } else { + let mut v = AfterLoopVisitor { + cx, + iter_expr, + loop_id: loop_expr.hir_id, + after_loop: false, + used_iter: false, + }; + v.visit_expr(&cx.tcx.hir().body(cx.enclosing_body.unwrap()).value); + v.used_iter } } diff --git a/src/tools/clippy/clippy_lints/src/macro_use.rs b/src/tools/clippy/clippy_lints/src/macro_use.rs index 314bf11e2d6..914b583186c 100644 --- a/src/tools/clippy/clippy_lints/src/macro_use.rs +++ b/src/tools/clippy/clippy_lints/src/macro_use.rs @@ -47,7 +47,12 @@ pub struct MacroRefData { impl MacroRefData { pub fn new(name: String, callee: Span, cx: &LateContext<'_>) -> Self { - let mut path = cx.sess().source_map().span_to_filename(callee).prefer_local().to_string(); + let mut path = cx + .sess() + .source_map() + .span_to_filename(callee) + .prefer_local() + .to_string(); // std lib paths are <::std::module::file type> // so remove brackets, space and type. @@ -96,7 +101,8 @@ impl MacroUseImports { let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_"); if let Some(callee) = span.source_callee() { if !self.collected.contains(&call_site) { - self.mac_refs.push(MacroRefData::new(name.to_string(), callee.def_site, cx)); + self.mac_refs + .push(MacroRefData::new(name.to_string(), callee.def_site, cx)); self.collected.insert(call_site); } } @@ -174,7 +180,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { .push((*item).to_string()); check_dup.push((*item).to_string()); } - } + }, [root, rest @ ..] => { if rest.iter().all(|item| !check_dup.contains(&(*item).to_string())) { let filtered = rest @@ -198,7 +204,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { .push(rest.join("::")); check_dup.extend(rest.iter().map(ToString::to_string)); } - } + }, } } } diff --git a/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs index 520162559e5..2f579edd6ad 100644 --- a/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs @@ -53,21 +53,6 @@ impl LateLintPass<'_> for ManualUnwrapOr { } } -#[derive(Copy, Clone)] -enum Case { - Option, - Result, -} - -impl Case { - fn unwrap_fn_path(&self) -> &str { - match self { - Case::Option => "Option::unwrap_or", - Case::Result => "Result::unwrap_or", - } - } -} - fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { fn applicable_or_arm<'a>(cx: &LateContext<'_>, arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> { if_chain! { @@ -86,6 +71,7 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk); if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind; if path_to_local_id(unwrap_arm.body, binding_hir_id); + if cx.typeck_results().expr_adjustments(unwrap_arm.body).is_empty(); if !contains_return_break_continue_macro(or_arm.body); then { Some(or_arm) @@ -98,10 +84,10 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if_chain! { if let ExprKind::Match(scrutinee, match_arms, _) = expr.kind; let ty = cx.typeck_results().expr_ty(scrutinee); - if let Some(case) = if is_type_diagnostic_item(cx, ty, sym::option_type) { - Some(Case::Option) + if let Some(ty_name) = if is_type_diagnostic_item(cx, ty, sym::option_type) { + Some("Option") } else if is_type_diagnostic_item(cx, ty, sym::result_type) { - Some(Case::Result) + Some("Result") } else { None }; @@ -124,7 +110,7 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { span_lint_and_sugg( cx, MANUAL_UNWRAP_OR, expr.span, - &format!("this pattern reimplements `{}`", case.unwrap_fn_path()), + &format!("this pattern reimplements `{}::unwrap_or`", ty_name), "replace with", format!( "{}.unwrap_or({})", diff --git a/src/tools/clippy/clippy_lints/src/matches.rs b/src/tools/clippy/clippy_lints/src/matches.rs index a70e8b26087..fcd37687010 100644 --- a/src/tools/clippy/clippy_lints/src/matches.rs +++ b/src/tools/clippy/clippy_lints/src/matches.rs @@ -1478,15 +1478,34 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A ); }, PatKind::Wild => { - span_lint_and_sugg( - cx, - MATCH_SINGLE_BINDING, - expr.span, - "this match could be replaced by its body itself", - "consider using the match body instead", - snippet_body, - Applicability::MachineApplicable, - ); + if ex.can_have_side_effects() { + let indent = " ".repeat(indent_of(cx, expr.span).unwrap_or(0)); + let sugg = format!( + "{};\n{}{}", + snippet_with_applicability(cx, ex.span, "..", &mut applicability), + indent, + snippet_body + ); + span_lint_and_sugg( + cx, + MATCH_SINGLE_BINDING, + expr.span, + "this match could be replaced by its scrutinee and body", + "consider using the scrutinee and body instead", + sugg, + applicability, + ) + } else { + span_lint_and_sugg( + cx, + MATCH_SINGLE_BINDING, + expr.span, + "this match could be replaced by its body itself", + "consider using the match body instead", + snippet_body, + Applicability::MachineApplicable, + ); + } }, _ => (), } diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 0b1b6304def..e0d29682146 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -1838,16 +1838,18 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } - wrong_self_convention::check( - cx, - &name, - item.vis.node.is_pub(), - self_ty, - first_arg_ty, - first_arg.pat.span, - implements_trait, - false - ); + if sig.decl.implicit_self.has_implicit_self() { + wrong_self_convention::check( + cx, + &name, + item.vis.node.is_pub(), + self_ty, + first_arg_ty, + first_arg.pat.span, + implements_trait, + false + ); + } } } @@ -1903,7 +1905,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if_chain! { if let TraitItemKind::Fn(ref sig, _) = item.kind; + if sig.decl.implicit_self.has_implicit_self(); if let Some(first_arg_ty) = sig.decl.inputs.iter().next(); + then { let first_arg_span = first_arg_ty.span; let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty); diff --git a/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs b/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs index 6e2bcb113c2..1773c26c251 100644 --- a/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs +++ b/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs @@ -22,7 +22,7 @@ const CONVENTIONS: [(&[Convention], &[SelfKind]); 9] = [ // Conversion using `to_` can use borrowed (non-Copy types) or owned (Copy types). // Source: https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(false), - Convention::IsTraitItem(false)], &[SelfKind::Ref]), + Convention::IsTraitItem(false), Convention::ImplementsTrait(false)], &[SelfKind::Ref]), (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(true), Convention::IsTraitItem(false), Convention::ImplementsTrait(false)], &[SelfKind::Value]), ]; diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs index 6966d798c53..b5d2549242b 100644 --- a/src/tools/clippy/clippy_lints/src/misc.rs +++ b/src/tools/clippy/clippy_lints/src/misc.rs @@ -660,7 +660,14 @@ fn in_attributes_expansion(expr: &Expr<'_>) -> bool { use rustc_span::hygiene::MacroKind; if expr.span.from_expansion() { let data = expr.span.ctxt().outer_expn_data(); - matches!(data.kind, ExpnKind::Macro { kind: MacroKind::Attr, name: _, proc_macro: _ }) + matches!( + data.kind, + ExpnKind::Macro { + kind: MacroKind::Attr, + name: _, + proc_macro: _ + } + ) } else { false } diff --git a/src/tools/clippy/clippy_lints/src/needless_bitwise_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bitwise_bool.rs new file mode 100644 index 00000000000..b30bfbd4294 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/needless_bitwise_bool.rs @@ -0,0 +1,86 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::in_macro; +use clippy_utils::source::snippet_opt; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** + /// Checks for uses of bitwise and/or operators between booleans, where performance may be improved by using + /// a lazy and. + /// + /// **Why is this bad?** + /// The bitwise operators do not support short-circuiting, so it may hinder code performance. + /// Additionally, boolean logic "masked" as bitwise logic is not caught by lints like `unnecessary_fold` + /// + /// **Known problems:** + /// This lint evaluates only when the right side is determined to have no side effects. At this time, that + /// determination is quite conservative. + /// + /// **Example:** + /// + /// ```rust + /// let (x,y) = (true, false); + /// if x & !y {} // where both x and y are booleans + /// ``` + /// Use instead: + /// ```rust + /// let (x,y) = (true, false); + /// if x && !y {} + /// ``` + pub NEEDLESS_BITWISE_BOOL, + pedantic, + "Boolean expressions that use bitwise rather than lazy operators" +} + +declare_lint_pass!(NeedlessBitwiseBool => [NEEDLESS_BITWISE_BOOL]); + +fn is_bitwise_operation(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let ty = cx.typeck_results().expr_ty(expr); + if_chain! { + if !in_macro(expr.span); + if let (&ExprKind::Binary(ref op, _, right), &ty::Bool) = (&expr.kind, &ty.kind()); + if op.node == BinOpKind::BitAnd || op.node == BinOpKind::BitOr; + if let ExprKind::Call(..) | ExprKind::MethodCall(..) | ExprKind::Binary(..) | ExprKind::Unary(..) = right.kind; + if !right.can_have_side_effects(); + then { + return true; + } + } + false +} + +fn suggession_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> { + if let ExprKind::Binary(ref op, left, right) = expr.kind { + if let (Some(l_snippet), Some(r_snippet)) = (snippet_opt(cx, left.span), snippet_opt(cx, right.span)) { + let op_snippet = match op.node { + BinOpKind::BitAnd => "&&", + _ => "||", + }; + return Some(format!("{} {} {}", l_snippet, op_snippet, r_snippet)); + } + } + None +} + +impl LateLintPass<'_> for NeedlessBitwiseBool { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if is_bitwise_operation(cx, expr) { + span_lint_and_then( + cx, + NEEDLESS_BITWISE_BOOL, + expr.span, + "use of bitwise operator instead of lazy operator between booleans", + |diag| { + if let Some(sugg) = suggession_snippet(cx, expr) { + diag.span_suggestion(expr.span, "try", sugg, Applicability::MachineApplicable); + } + }, + ); + } + } +} 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 d8417c7dc70..c64491c63e2 100644 --- a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs @@ -1,14 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_lang_ctor; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{differing_macro_contexts, is_lang_ctor}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionSome, ResultOk}; use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TyS; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; declare_clippy_lint! { /// **What it does:** @@ -63,12 +62,6 @@ declare_clippy_lint! { declare_lint_pass!(NeedlessQuestionMark => [NEEDLESS_QUESTION_MARK]); -#[derive(Debug)] -enum SomeOkCall<'a> { - SomeCall(&'a Expr<'a>, &'a Expr<'a>), - OkCall(&'a Expr<'a>, &'a Expr<'a>), -} - impl LateLintPass<'_> for NeedlessQuestionMark { /* * The question mark operator is compatible with both Result<T, E> and Option<T>, @@ -90,104 +83,37 @@ impl LateLintPass<'_> for NeedlessQuestionMark { */ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { - let e = match &expr.kind { - ExprKind::Ret(Some(e)) => e, - _ => return, - }; - - if let Some(ok_some_call) = is_some_or_ok_call(cx, e) { - emit_lint(cx, &ok_some_call); + if let ExprKind::Ret(Some(e)) = expr.kind { + check(cx, e); } } fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { - // Function / Closure block - let expr_opt = if let ExprKind::Block(block, _) = &body.value.kind { - block.expr - } else { - // Single line closure - Some(&body.value) - }; - - if_chain! { - if let Some(expr) = expr_opt; - if let Some(ok_some_call) = is_some_or_ok_call(cx, expr); - then { - emit_lint(cx, &ok_some_call); - } - }; + check(cx, body.value.peel_blocks()); } } -fn emit_lint(cx: &LateContext<'_>, expr: &SomeOkCall<'_>) { - let (entire_expr, inner_expr) = match expr { - SomeOkCall::OkCall(outer, inner) | SomeOkCall::SomeCall(outer, inner) => (outer, inner), +fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { + let inner_expr = if_chain! { + if let ExprKind::Call(path, [arg]) = &expr.kind; + if let ExprKind::Path(ref qpath) = &path.kind; + if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk); + if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &arg.kind; + if let ExprKind::Call(called, [inner_expr]) = &inner_expr_with_q.kind; + if let ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, _)) = &called.kind; + if expr.span.ctxt() == inner_expr.span.ctxt(); + let expr_ty = cx.typeck_results().expr_ty(expr); + let inner_ty = cx.typeck_results().expr_ty(inner_expr); + if TyS::same_type(expr_ty, inner_ty); + then { inner_expr } else { return; } }; - span_lint_and_sugg( cx, NEEDLESS_QUESTION_MARK, - entire_expr.span, + expr.span, "question mark operator is useless here", "try", format!("{}", snippet(cx, inner_expr.span, r#""...""#)), Applicability::MachineApplicable, ); } - -fn is_some_or_ok_call<'a>(cx: &'a LateContext<'_>, expr: &'a Expr<'_>) -> Option<SomeOkCall<'a>> { - if_chain! { - // Check outer expression matches CALL_IDENT(ARGUMENT) format - if let ExprKind::Call(path, args) = &expr.kind; - if let ExprKind::Path(ref qpath) = &path.kind; - if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk); - - // Extract inner expression from ARGUMENT - if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &args[0].kind; - if let ExprKind::Call(called, args) = &inner_expr_with_q.kind; - if args.len() == 1; - - if let ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, _)) = &called.kind; - then { - // Extract inner expr type from match argument generated by - // question mark operator - let inner_expr = &args[0]; - - // if the inner expr is inside macro but the outer one is not, do not lint (#6921) - if differing_macro_contexts(expr.span, inner_expr.span) { - return None; - } - - let inner_ty = cx.typeck_results().expr_ty(inner_expr); - let outer_ty = cx.typeck_results().expr_ty(expr); - - // Check if outer and inner type are Option - let outer_is_some = is_type_diagnostic_item(cx, outer_ty, sym::option_type); - let inner_is_some = is_type_diagnostic_item(cx, inner_ty, sym::option_type); - - // Check for Option MSRV - if outer_is_some && inner_is_some { - return Some(SomeOkCall::SomeCall(expr, inner_expr)); - } - - // Check if outer and inner type are Result - let outer_is_result = is_type_diagnostic_item(cx, outer_ty, sym::result_type); - let inner_is_result = is_type_diagnostic_item(cx, inner_ty, sym::result_type); - - // Additional check: if the error type of the Result can be converted - // via the From trait, then don't match - let does_not_call_from = !has_implicit_error_from(cx, expr, inner_expr); - - // Must meet Result MSRV - if outer_is_result && inner_is_result && does_not_call_from { - return Some(SomeOkCall::OkCall(expr, inner_expr)); - } - } - } - - None -} - -fn has_implicit_error_from(cx: &LateContext<'_>, entire_expr: &Expr<'_>, inner_result_expr: &Expr<'_>) -> bool { - return cx.typeck_results().expr_ty(entire_expr) != cx.typeck_results().expr_ty(inner_result_expr); -} diff --git a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs index e527adbb892..b6af4175edf 100644 --- a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::contains_return_break_continue_macro; -use clippy_utils::{eager_or_lazy, get_enclosing_block, in_macro, is_lang_ctor}; +use clippy_utils::{eager_or_lazy, in_macro, is_else_clause, is_lang_ctor}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionSome; @@ -81,7 +81,6 @@ struct OptionIfLetElseOccurence { method_sugg: String, some_expr: String, none_expr: String, - wrap_braces: bool, } /// Extracts the body of a given arm. If the arm contains only an expression, @@ -106,37 +105,6 @@ fn extract_body_from_arm<'a>(arm: &'a Arm<'a>) -> Option<&'a Expr<'a>> { } } -/// If this is the else body of an if/else expression, then we need to wrap -/// it in curly braces. Otherwise, we don't. -fn should_wrap_in_braces(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| { - let mut should_wrap = false; - - if let Some(Expr { - kind: - ExprKind::Match( - _, - arms, - MatchSource::IfLetDesugar { - contains_else_clause: true, - }, - ), - .. - }) = parent.expr - { - should_wrap = expr.hir_id == arms[1].body.hir_id; - } else if let Some(Expr { - kind: ExprKind::If(_, _, Some(else_clause)), - .. - }) = parent.expr - { - should_wrap = expr.hir_id == else_clause.hir_id; - } - - should_wrap - }) -} - fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: bool, as_mut: bool) -> String { format!( "{}{}", @@ -161,6 +129,7 @@ fn detect_option_if_let_else<'tcx>( if_chain! { if !in_macro(expr.span); // Don't lint macros, because it behaves weirdly if let ExprKind::Match(cond_expr, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind; + if !is_else_clause(cx.tcx, expr); if arms.len() == 2; if !is_result_ok(cx, cond_expr); // Don't lint on Result::ok because a different lint does it already if let PatKind::TupleStruct(struct_qpath, &[inner_pat], _) = &arms[0].pat.kind; @@ -168,13 +137,13 @@ fn detect_option_if_let_else<'tcx>( if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind; if !contains_return_break_continue_macro(arms[0].body); if !contains_return_break_continue_macro(arms[1].body); + then { let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" }; let some_body = extract_body_from_arm(&arms[0])?; let none_body = extract_body_from_arm(&arms[1])?; let method_sugg = if eager_or_lazy::is_eagerness_candidate(cx, none_body) { "map_or" } else { "map_or_else" }; let capture_name = id.name.to_ident_string(); - let wrap_braces = should_wrap_in_braces(cx, expr); let (as_ref, as_mut) = match &cond_expr.kind { ExprKind::AddrOf(_, Mutability::Not, _) => (true, false), ExprKind::AddrOf(_, Mutability::Mut, _) => (false, true), @@ -190,7 +159,6 @@ fn detect_option_if_let_else<'tcx>( method_sugg: method_sugg.to_string(), some_expr: format!("|{}{}| {}", capture_mut, capture_name, Sugg::hir(cx, some_body, "..")), none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir(cx, none_body, "..")), - wrap_braces, }) } else { None @@ -208,13 +176,8 @@ impl<'tcx> LateLintPass<'tcx> for OptionIfLetElse { format!("use Option::{} instead of an if let/else", detection.method_sugg).as_str(), "try", format!( - "{}{}.{}({}, {}){}", - if detection.wrap_braces { "{ " } else { "" }, - detection.option, - detection.method_sugg, - detection.none_expr, - detection.some_expr, - if detection.wrap_braces { " }" } else { "" }, + "{}.{}({}, {})", + detection.option, detection.method_sugg, detection.none_expr, detection.some_expr, ), Applicability::MaybeIncorrect, ); diff --git a/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs b/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs index d22f7d9a96b..04542146516 100644 --- a/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs @@ -8,7 +8,12 @@ use super::UNIT_CMP; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if expr.span.from_expansion() { if let Some(callee) = expr.span.source_callee() { - if let ExpnKind::Macro { kind: MacroKind::Bang, name: symbol, proc_macro: _ } = callee.kind { + if let ExpnKind::Macro { + kind: MacroKind::Bang, + name: symbol, + proc_macro: _, + } = callee.kind + { if let ExprKind::Binary(ref cmp, left, _) = expr.kind { let op = cmp.node; if op.is_comparison() && cx.typeck_results().expr_ty(left).is_unit() { diff --git a/src/tools/clippy/clippy_lints/src/unused_async.rs b/src/tools/clippy/clippy_lints/src/unused_async.rs new file mode 100644 index 00000000000..18ee07d3a95 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/unused_async.rs @@ -0,0 +1,92 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, NestedVisitorMap, Visitor}; +use rustc_hir::{Body, Expr, ExprKind, FnDecl, FnHeader, HirId, IsAsync, Item, ItemKind, YieldSource}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for functions that are declared `async` but have no `.await`s inside of them. + /// + /// **Why is this bad?** Async functions with no async code create overhead, both mentally and computationally. + /// Callers of async methods either need to be calling from an async function themselves or run it on an executor, both of which + /// causes runtime overhead and hassle for the caller. + /// + /// **Known problems:** None + /// + /// **Example:** + /// + /// ```rust + /// // Bad + /// async fn get_random_number() -> i64 { + /// 4 // Chosen by fair dice roll. Guaranteed to be random. + /// } + /// let number_future = get_random_number(); + /// + /// // Good + /// fn get_random_number_improved() -> i64 { + /// 4 // Chosen by fair dice roll. Guaranteed to be random. + /// } + /// let number_future = async { get_random_number_improved() }; + /// ``` + pub UNUSED_ASYNC, + pedantic, + "finds async functions with no await statements" +} + +declare_lint_pass!(UnusedAsync => [UNUSED_ASYNC]); + +struct AsyncFnVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + found_await: bool, +} + +impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { + if let ExprKind::Yield(_, YieldSource::Await { .. }) = ex.kind { + self.found_await = true; + } + walk_expr(self, ex); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) + } +} + +impl<'tcx> LateLintPass<'tcx> for UnusedAsync { + fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if let ItemKind::Trait(..) = item.kind { + return; + } + } + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + fn_kind: FnKind<'tcx>, + fn_decl: &'tcx FnDecl<'tcx>, + body: &Body<'tcx>, + span: Span, + hir_id: HirId, + ) { + if let FnKind::ItemFn(_, _, FnHeader { asyncness, .. }, _) = &fn_kind { + if matches!(asyncness, IsAsync::Async) { + let mut visitor = AsyncFnVisitor { cx, found_await: false }; + walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), span, hir_id); + if !visitor.found_await { + span_lint_and_help( + cx, + UNUSED_ASYNC, + span, + "unused `async` for function with no await statements", + None, + "consider removing the `async` from this function", + ); + } + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs index aa4d16633ff..2ad6fa77f48 100644 --- a/src/tools/clippy/clippy_lints/src/use_self.rs +++ b/src/tools/clippy/clippy_lints/src/use_self.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; +use clippy_utils::ty::same_type_and_consts; use clippy_utils::{in_macro, meets_msrv, msrvs}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_hir::def::DefKind; use rustc_hir::{ - def, + self as hir, + def::{self, DefKind}, def_id::LocalDefId, intravisit::{walk_ty, NestedVisitorMap, Visitor}, Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Node, Path, PathSegment, @@ -14,7 +14,7 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; -use rustc_middle::ty::{AssocKind, Ty, TyS}; +use rustc_middle::ty::{AssocKind, Ty}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{BytePos, Span}; @@ -459,7 +459,7 @@ fn in_impl(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>) -> bool { fn should_lint_ty(hir_ty: &hir::Ty<'_>, ty: Ty<'_>, self_ty: Ty<'_>) -> bool { if_chain! { - if TyS::same_type(ty, self_ty); + if same_type_and_consts(ty, self_ty); if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind; then { !matches!(path.res, def::Res::SelfTy(..)) diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs index 7edb280be73..2be99fb761b 100644 --- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs +++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_with_macro_callsite}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts}; use clippy_utils::{get_parent_expr, match_def_path, match_trait_method, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, HirId, MatchSource}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, TyS}; +use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::sym; @@ -67,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { if match_trait_method(cx, e, &paths::INTO) && &*name.ident.as_str() == "into" { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(&args[0]); - if TyS::same_type(a, b) { + if same_type_and_consts(a, b) { let sugg = snippet_with_macro_callsite(cx, args[0].span, "<expr>").to_string(); span_lint_and_sugg( cx, @@ -90,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(&args[0]); - if TyS::same_type(a, b) { + if same_type_and_consts(a, b) { let sugg = snippet(cx, args[0].span, "<expr>").into_owned(); span_lint_and_sugg( cx, @@ -110,7 +110,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { if is_type_diagnostic_item(cx, a, sym::result_type); if let ty::Adt(_, substs) = a.kind(); if let Some(a_type) = substs.types().next(); - if TyS::same_type(a_type, b); + if same_type_and_consts(a_type, b); + then { span_lint_and_help( cx, @@ -137,7 +138,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { if is_type_diagnostic_item(cx, a, sym::result_type); if let ty::Adt(_, substs) = a.kind(); if let Some(a_type) = substs.types().next(); - if TyS::same_type(a_type, b); + if same_type_and_consts(a_type, b); then { let hint = format!("consider removing `{}()`", snippet(cx, path.span, "TryFrom::try_from")); @@ -154,7 +155,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { if_chain! { if match_def_path(cx, def_id, &paths::FROM_FROM); - if TyS::same_type(a, b); + if same_type_and_consts(a, b); then { let sugg = Sugg::hir_with_macro_callsite(cx, &args[0], "<expr>").maybe_par(); diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs index 52c1dc3bdd2..fd2dddb3b96 100644 --- a/src/tools/clippy/clippy_lints/src/utils/conf.rs +++ b/src/tools/clippy/clippy_lints/src/utils/conf.rs @@ -26,13 +26,13 @@ impl TryConf { macro_rules! define_Conf { ($( - #[$doc:meta] + #[doc = $doc:literal] $(#[conf_deprecated($dep:literal)])? ($name:ident: $ty:ty = $default:expr), )*) => { /// Clippy lint configuration pub struct Conf { - $(#[$doc] pub $name: $ty,)* + $(#[doc = $doc] pub $name: $ty,)* } mod defaults { @@ -89,6 +89,34 @@ macro_rules! define_Conf { Ok(TryConf { conf, errors }) } } + + #[cfg(feature = "metadata-collector-lint")] + pub mod metadata { + use crate::utils::internal_lints::metadata_collector::ClippyConfiguration; + + macro_rules! wrap_option { + () => (None); + ($x:literal) => (Some($x)); + } + + pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> { + vec![ + $( + { + let deprecation_reason = wrap_option!($($dep)?); + + ClippyConfiguration::new( + stringify!($name), + stringify!($ty), + format!("{:?}", super::defaults::$name()), + $doc, + deprecation_reason, + ) + }, + )+ + ] + } + } }; } diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs index e85637ca758..e9fa043b20f 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -8,9 +8,6 @@ //! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such //! a simple mistake) -// # NITs -// - TODO xFrednet 2021-02-13: Collect depreciations and maybe renames - use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; use rustc_hir::{ @@ -22,13 +19,14 @@ use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Loc, Span, Symbol}; use serde::{ser::SerializeStruct, Serialize, Serializer}; use std::collections::BinaryHeap; +use std::fmt; use std::fs::{self, OpenOptions}; use std::io::prelude::*; use std::path::Path; use crate::utils::internal_lints::is_lint_ref_type; use clippy_utils::{ - diagnostics::span_lint, last_path_segment, match_function_call, match_path, paths, ty::match_type, + diagnostics::span_lint, last_path_segment, match_def_path, match_function_call, match_path, paths, ty::match_type, ty::walk_ptrs_ty_depth, }; @@ -39,8 +37,52 @@ const BLACK_LISTED_LINTS: [&str; 3] = ["lint_author", "deep_code_inspection", "i /// These groups will be ignored by the lint group matcher. This is useful for collections like /// `clippy::all` const IGNORED_LINT_GROUPS: [&str; 1] = ["clippy::all"]; -/// Lints within this group will be excluded from the collection -const EXCLUDED_LINT_GROUPS: [&str; 1] = ["clippy::internal"]; +/// Lints within this group will be excluded from the collection. These groups +/// have to be defined without the `clippy::` prefix. +const EXCLUDED_LINT_GROUPS: [&str; 1] = ["internal"]; +/// Collected deprecated lint will be assigned to this group in the JSON output +const DEPRECATED_LINT_GROUP_STR: &str = "deprecated"; +/// This is the lint level for deprecated lints that will be displayed in the lint list +const DEPRECATED_LINT_LEVEL: &str = "none"; +/// This array holds Clippy's lint groups with their corresponding default lint level. The +/// lint level for deprecated lints is set in `DEPRECATED_LINT_LEVEL`. +const DEFAULT_LINT_LEVELS: [(&str, &str); 8] = [ + ("correctness", "deny"), + ("restriction", "allow"), + ("style", "warn"), + ("pedantic", "allow"), + ("complexity", "warn"), + ("perf", "warn"), + ("cargo", "allow"), + ("nursery", "allow"), +]; +/// This prefix is in front of the lint groups in the lint store. The prefix will be trimmed +/// to only keep the actual lint group in the output. +const CLIPPY_LINT_GROUP_PREFIX: &str = "clippy::"; + +/// This template will be used to format the configuration section in the lint documentation. +/// The `configurations` parameter will be replaced with one or multiple formatted +/// `ClippyConfiguration` instances. See `CONFIGURATION_VALUE_TEMPLATE` for further customizations +macro_rules! CONFIGURATION_SECTION_TEMPLATE { + () => { + r#" +**Configuration** +This lint has the following configuration variables: + +{configurations} +"# + }; +} +/// This template will be used to format an individual `ClippyConfiguration` instance in the +/// lint documentation. +/// +/// The format function will provide strings for the following parameters: `name`, `ty`, `doc` and +/// `default` +macro_rules! CONFIGURATION_VALUE_TEMPLATE { + () => { + "* {name}: {ty}: {doc} (defaults to `{default}`)\n" + }; +} const LINT_EMISSION_FUNCTIONS: [&[&str]; 7] = [ &["clippy_utils", "diagnostics", "span_lint"], @@ -66,6 +108,7 @@ const SUGGESTION_FUNCTIONS: [&[&str]; 2] = [ &["clippy_utils", "diagnostics", "multispan_sugg"], &["clippy_utils", "diagnostics", "multispan_sugg_with_applicability"], ]; +const DEPRECATED_LINT_TYPE: [&str; 3] = ["clippy_lints", "deprecated_lints", "ClippyDeprecatedLint"]; /// The index of the applicability name of `paths::APPLICABILITY_VALUES` const APPLICABILITY_NAME_INDEX: usize = 2; @@ -102,13 +145,33 @@ declare_clippy_lint! { impl_lint_pass!(MetadataCollector => [INTERNAL_METADATA_COLLECTOR]); #[allow(clippy::module_name_repetitions)] -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone)] pub struct MetadataCollector { /// All collected lints /// /// We use a Heap here to have the lints added in alphabetic order in the export lints: BinaryHeap<LintMetadata>, applicability_info: FxHashMap<String, ApplicabilityInfo>, + config: Vec<ClippyConfiguration>, +} + +impl MetadataCollector { + pub fn new() -> Self { + Self { + lints: BinaryHeap::<LintMetadata>::default(), + applicability_info: FxHashMap::<String, ApplicabilityInfo>::default(), + config: collect_configs(), + } + } + + fn get_lint_configs(&self, lint_name: &str) -> Option<String> { + self.config + .iter() + .filter(|config| config.lints.iter().any(|lint| lint == lint_name)) + .map(ToString::to_string) + .reduce(|acc, x| acc + &x) + .map(|configurations| format!(CONFIGURATION_SECTION_TEMPLATE!(), configurations = configurations)) + } } impl Drop for MetadataCollector { @@ -143,6 +206,7 @@ struct LintMetadata { id: String, id_span: SerializableSpan, group: String, + level: &'static str, docs: String, /// This field is only used in the output and will only be /// mapped shortly before the actual output. @@ -150,11 +214,12 @@ struct LintMetadata { } impl LintMetadata { - fn new(id: String, id_span: SerializableSpan, group: String, docs: String) -> Self { + fn new(id: String, id_span: SerializableSpan, group: String, level: &'static str, docs: String) -> Self { Self { id, id_span, group, + level, docs, applicability: None, } @@ -182,7 +247,7 @@ impl SerializableSpan { let loc: Loc = cx.sess().source_map().lookup_char_pos(span.lo()); Self { - path: format!("{}", loc.file.name), + path: format!("{}", loc.file.name.prefer_remapped()), line: loc.line, } } @@ -214,6 +279,95 @@ impl Serialize for ApplicabilityInfo { } } +// ================================================================== +// Configuration +// ================================================================== +#[derive(Debug, Clone, Default)] +pub struct ClippyConfiguration { + name: String, + config_type: &'static str, + default: String, + lints: Vec<String>, + doc: String, + deprecation_reason: Option<&'static str>, +} + +impl ClippyConfiguration { + pub fn new( + name: &'static str, + config_type: &'static str, + default: String, + doc_comment: &'static str, + deprecation_reason: Option<&'static str>, + ) -> Self { + let (lints, doc) = parse_config_field_doc(doc_comment) + .unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string())); + + Self { + name: to_kebab(name), + lints, + doc, + config_type, + default, + deprecation_reason, + } + } +} + +fn collect_configs() -> Vec<ClippyConfiguration> { + crate::utils::conf::metadata::get_configuration_metadata() +} + +/// This parses the field documentation of the config struct. +/// +/// ```rust, ignore +/// parse_config_field_doc(cx, "Lint: LINT_NAME_1, LINT_NAME_2. Papa penguin, papa penguin") +/// ``` +/// +/// Would yield: +/// ```rust, ignore +/// Some(["lint_name_1", "lint_name_2"], "Papa penguin, papa penguin") +/// ``` +fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec<String>, String)> { + const DOC_START: &str = " Lint: "; + if_chain! { + if doc_comment.starts_with(DOC_START); + if let Some(split_pos) = doc_comment.find('.'); + then { + let mut doc_comment = doc_comment.to_string(); + let documentation = doc_comment.split_off(split_pos); + + doc_comment.make_ascii_lowercase(); + let lints: Vec<String> = doc_comment.split_off(DOC_START.len()).split(", ").map(str::to_string).collect(); + + Some((lints, documentation)) + } else { + None + } + } +} + +/// Transforms a given `snake_case_string` to a tasty `kebab-case-string` +fn to_kebab(config_name: &str) -> String { + config_name.replace('_', "-") +} + +impl fmt::Display for ClippyConfiguration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + CONFIGURATION_VALUE_TEMPLATE!(), + name = self.name, + ty = self.config_type, + doc = self.doc, + default = self.default + ) + } +} + +// ================================================================== +// Lint pass +// ================================================================== impl<'hir> LateLintPass<'hir> for MetadataCollector { /// Collecting lint declarations like: /// ```rust, ignore @@ -225,23 +379,48 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { /// } /// ``` fn check_item(&mut self, cx: &LateContext<'hir>, item: &'hir Item<'_>) { - if_chain! { - // item validation - if let ItemKind::Static(ref ty, Mutability::Not, _) = item.kind; - if is_lint_ref_type(cx, ty); - // blacklist check - let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase(); - if !BLACK_LISTED_LINTS.contains(&lint_name.as_str()); - // metadata extraction - if let Some(group) = get_lint_group_or_lint(cx, &lint_name, item); - if let Some(docs) = extract_attr_docs_or_lint(cx, item); - then { - self.lints.push(LintMetadata::new( - lint_name, - SerializableSpan::from_item(cx, item), - group, - docs, - )); + if let ItemKind::Static(ref ty, Mutability::Not, _) = item.kind { + // Normal lint + if_chain! { + // item validation + if is_lint_ref_type(cx, ty); + // blacklist check + let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase(); + if !BLACK_LISTED_LINTS.contains(&lint_name.as_str()); + // metadata extraction + if let Some((group, level)) = get_lint_group_and_level_or_lint(cx, &lint_name, item); + if let Some(mut docs) = extract_attr_docs_or_lint(cx, item); + then { + if let Some(configuration_section) = self.get_lint_configs(&lint_name) { + docs.push_str(&configuration_section); + } + + self.lints.push(LintMetadata::new( + lint_name, + SerializableSpan::from_item(cx, item), + group, + level, + docs, + )); + } + } + + if_chain! { + if is_deprecated_lint(cx, ty); + // blacklist check + let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase(); + if !BLACK_LISTED_LINTS.contains(&lint_name.as_str()); + // Metadata the little we can get from a deprecated lint + if let Some(docs) = extract_attr_docs_or_lint(cx, item); + then { + self.lints.push(LintMetadata::new( + lint_name, + SerializableSpan::from_item(cx, item), + DEPRECATED_LINT_GROUP_STR.to_string(), + DEPRECATED_LINT_LEVEL, + docs, + )); + } } } } @@ -268,7 +447,7 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { // - src/misc.rs:734:9 // - src/methods/mod.rs:3545:13 // - src/methods/mod.rs:3496:13 - // We are basically unable to resolve the lint name it self. + // We are basically unable to resolve the lint name itself. return; } @@ -318,15 +497,32 @@ fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> { }) } -fn get_lint_group_or_lint(cx: &LateContext<'_>, lint_name: &str, item: &'hir Item<'_>) -> Option<String> { +fn get_lint_group_and_level_or_lint( + cx: &LateContext<'_>, + lint_name: &str, + item: &'hir Item<'_>, +) -> Option<(String, &'static str)> { let result = cx.lint_store.check_lint_name(lint_name, Some(sym::clippy)); if let CheckLintNameResult::Tool(Ok(lint_lst)) = result { - get_lint_group(cx, lint_lst[0]) - .or_else(|| { - lint_collection_error_item(cx, item, "Unable to determine lint group"); + if let Some(group) = get_lint_group(cx, lint_lst[0]) { + if EXCLUDED_LINT_GROUPS.contains(&group.as_str()) { + return None; + } + + if let Some(level) = get_lint_level_from_group(&group) { + Some((group, level)) + } else { + lint_collection_error_item( + cx, + item, + &format!("Unable to determine lint level for found group `{}`", group), + ); None - }) - .filter(|group| !EXCLUDED_LINT_GROUPS.contains(&group.as_str())) + } + } else { + lint_collection_error_item(cx, item, "Unable to determine lint group"); + None + } } else { lint_collection_error_item(cx, item, "Unable to find lint in lint_store"); None @@ -339,14 +535,31 @@ fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option<String> { continue; } - if lints.iter().any(|x| *x == lint_id) { - return Some((*group_name).to_string()); + if lints.iter().any(|group_lint| *group_lint == lint_id) { + let group = group_name.strip_prefix(CLIPPY_LINT_GROUP_PREFIX).unwrap_or(group_name); + return Some((*group).to_string()); } } None } +fn get_lint_level_from_group(lint_group: &str) -> Option<&'static str> { + DEFAULT_LINT_LEVELS + .iter() + .find_map(|(group_name, group_level)| (*group_name == lint_group).then(|| *group_level)) +} + +fn is_deprecated_lint(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { + if let hir::TyKind::Path(ref path) = ty.kind { + if let hir::def::Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, ty.hir_id) { + return match_def_path(cx, def_id, &DEPRECATED_LINT_TYPE); + } + } + + false +} + // ================================================================== // Lint emission // ================================================================== diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index 7e962472c07..d0e79efa70d 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -279,8 +279,15 @@ impl EarlyLintPass for Write { span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprintln!`"); self.lint_println_empty_string(cx, mac); } else if mac.path == sym!(write) { - if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), true) { + if let (Some(fmt_str), dest) = self.check_tts(cx, mac.args.inner_tokens(), true) { if check_newlines(&fmt_str) { + let (nl_span, only_nl) = newline_span(&fmt_str); + let nl_span = match (dest, only_nl) { + // Special case of `write!(buf, "\n")`: Mark everything from the end of + // `buf` for removal so no trailing comma [`writeln!(buf, )`] remains. + (Some(dest_expr), true) => Span::new(dest_expr.span.hi(), nl_span.hi(), nl_span.ctxt()), + _ => nl_span, + }; span_lint_and_then( cx, WRITE_WITH_NEWLINE, @@ -289,10 +296,7 @@ impl EarlyLintPass for Write { |err| { err.multipart_suggestion( "use `writeln!()` instead", - vec![ - (mac.path.span, String::from("writeln")), - (newline_span(&fmt_str), String::new()), - ], + vec![(mac.path.span, String::from("writeln")), (nl_span, String::new())], Applicability::MachineApplicable, ); }, @@ -329,12 +333,13 @@ impl EarlyLintPass for Write { /// Given a format string that ends in a newline and its span, calculates the span of the /// newline, or the format string itself if the format string consists solely of a newline. -fn newline_span(fmtstr: &StrLit) -> Span { +/// Return this and a boolean indicating whether it only consisted of a newline. +fn newline_span(fmtstr: &StrLit) -> (Span, bool) { let sp = fmtstr.span; let contents = &fmtstr.symbol.as_str(); if *contents == r"\n" { - return sp; + return (sp, true); } let newline_sp_hi = sp.hi() @@ -351,7 +356,7 @@ fn newline_span(fmtstr: &StrLit) -> Span { panic!("expected format string to contain a newline"); }; - sp.with_lo(newline_sp_hi - newline_sp_len).with_hi(newline_sp_hi) + (sp.with_lo(newline_sp_hi - newline_sp_len).with_hi(newline_sp_hi), false) } /// Stores a list of replacement spans for each argument, but only if all the replacements used an @@ -613,7 +618,7 @@ impl Write { |err| { err.multipart_suggestion( &format!("use `{}!` instead", suggested), - vec![(mac.path.span, suggested), (newline_span(&fmt_str), String::new())], + vec![(mac.path.span, suggested), (newline_span(&fmt_str).0, String::new())], Applicability::MachineApplicable, ); }, diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml index 0a1d4e11142..93ed3b18400 100644 --- a/src/tools/clippy/clippy_utils/Cargo.toml +++ b/src/tools/clippy/clippy_utils/Cargo.toml @@ -14,6 +14,7 @@ unicode-normalization = "0.1" rustc-semver="1.1.0" [features] +deny-warnings = [] internal-lints = [] metadata-collector-lint = [] diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 9ac9500b4eb..d6dcd4abbd9 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -4,7 +4,14 @@ #![cfg_attr(bootstrap, feature(or_patterns))] #![feature(rustc_private)] #![recursion_limit = "512"] +#![cfg_attr(feature = "deny-warnings", deny(warnings))] #![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)] +// warn on the same lints as `clippy_lints` +#![warn(trivial_casts, trivial_numeric_casts)] +// warn on lints, that are included in `rust-lang/rust`s bootstrap +#![warn(rust_2018_idioms, unused_lifetimes)] +// warn on rustc internal lints +#![warn(rustc::internal)] // FIXME: switch to something more ergonomic here, once available. // (Currently there is no way to opt into sysroot crates without `extern crate`.) @@ -855,6 +862,24 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio }) } +/// Gets the loop enclosing the given expression, if any. +pub fn get_enclosing_loop(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + let map = tcx.hir(); + for (_, node) in map.parent_iter(expr.hir_id) { + match node { + Node::Expr( + e @ Expr { + kind: ExprKind::Loop(..), + .. + }, + ) => return Some(e), + Node::Expr(_) | Node::Stmt(_) | Node::Block(_) | Node::Local(_) | Node::Arm(_) => (), + _ => break, + } + } + None +} + /// Gets the parent node if it's an impl block. pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> { let map = tcx.hir(); @@ -947,7 +972,12 @@ pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> { let data = span.ctxt().outer_expn_data(); let new_span = data.call_site; - if let ExpnKind::Macro { kind: MacroKind::Bang, name: mac_name, proc_macro: _ } = data.kind { + if let ExpnKind::Macro { + kind: MacroKind::Bang, + name: mac_name, + proc_macro: _, + } = data.kind + { if mac_name.as_str() == name { return Some(new_span); } @@ -975,7 +1005,12 @@ pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> { let data = span.ctxt().outer_expn_data(); let new_span = data.call_site; - if let ExpnKind::Macro { kind: MacroKind::Bang, name: mac_name, proc_macro: _ } = data.kind { + if let ExpnKind::Macro { + kind: MacroKind::Bang, + name: mac_name, + proc_macro: _, + } = data.kind + { if mac_name.as_str() == name { return Some(new_span); } diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index 0633a19391f..0c950661757 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -289,7 +289,7 @@ fn has_enclosing_paren(sugg: impl AsRef<str>) -> bool { let mut chars = sugg.as_ref().chars(); if let Some('(') = chars.next() { let mut depth = 1; - while let Some(c) = chars.next() { + for c in &mut chars { if c == '(' { depth += 1; } else if c == ')' { diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index 64a80f2554f..c36e215f184 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -2,9 +2,8 @@ #![allow(clippy::module_name_repetitions)] -use std::collections::HashMap; - use rustc_ast::ast::Mutability; +use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::{TyKind, Unsafety}; @@ -184,14 +183,14 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { /// Checks if `Ty` is normalizable. This function is useful /// to avoid crashes on `layout_of`. pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool { - is_normalizable_helper(cx, param_env, ty, &mut HashMap::new()) + is_normalizable_helper(cx, param_env, ty, &mut FxHashMap::default()) } fn is_normalizable_helper<'tcx>( cx: &LateContext<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, - cache: &mut HashMap<Ty<'tcx>, bool>, + cache: &mut FxHashMap<Ty<'tcx>, bool>, ) -> bool { if let Some(&cached_result) = cache.get(ty) { return cached_result; @@ -322,3 +321,27 @@ pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) { } inner(ty, 0) } + +/// Returns `true` if types `a` and `b` are same types having same `Const` generic args, +/// otherwise returns `false` +pub fn same_type_and_consts(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { + match (&a.kind(), &b.kind()) { + (&ty::Adt(did_a, substs_a), &ty::Adt(did_b, substs_b)) => { + if did_a != did_b { + return false; + } + + substs_a + .iter() + .zip(substs_b.iter()) + .all(|(arg_a, arg_b)| match (arg_a.unpack(), arg_b.unpack()) { + (GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => inner_a == inner_b, + (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => { + same_type_and_consts(type_a, type_b) + }, + _ => true, + }) + }, + _ => a == b, + } +} diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index d431bdf34ee..ecdc666b5f6 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -1,7 +1,7 @@ use crate::path_to_local_id; use rustc_hir as hir; use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visitor}; -use rustc_hir::{Arm, Block, Body, Destination, Expr, ExprKind, HirId, Stmt}; +use rustc_hir::{def::Res, Arm, Block, Body, BodyId, Destination, Expr, ExprKind, HirId, Stmt}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; @@ -218,6 +218,7 @@ impl<'tcx> Visitable<'tcx> for &'tcx Arm<'tcx> { } } +/// Calls the given function for each break expression. pub fn visit_break_exprs<'tcx>( node: impl Visitable<'tcx>, f: impl FnMut(&'tcx Expr<'tcx>, Destination, Option<&'tcx Expr<'tcx>>), @@ -239,3 +240,36 @@ pub fn visit_break_exprs<'tcx>( node.visit(&mut V(f)); } + +/// Checks if the given resolved path is used in the given body. +pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool { + struct V<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + res: Res, + found: bool, + } + impl Visitor<'tcx> for V<'_, 'tcx> { + type Map = Map<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) + } + + fn visit_expr(&mut self, e: &'tcx Expr<'_>) { + if self.found { + return; + } + + if let ExprKind::Path(p) = &e.kind { + if self.cx.qpath_res(p, e.hir_id) == self.res { + self.found = true; + } + } else { + walk_expr(self, e) + } + } + } + + let mut v = V { cx, res, found: false }; + v.visit_expr(&cx.tcx.hir().body(body).value); + v.found +} diff --git a/src/tools/clippy/doc/basics.md b/src/tools/clippy/doc/basics.md index 5226875cc21..e2e307ce4f6 100644 --- a/src/tools/clippy/doc/basics.md +++ b/src/tools/clippy/doc/basics.md @@ -28,6 +28,8 @@ git clone git@github.com:<your-username>/rust-clippy If you've already cloned Clippy in the past, update it to the latest version: ```bash +# If the upstream remote has not been added yet +git remote add upstream https://github.com/rust-lang/rust-clippy # upstream has to be the remote of the rust-lang/rust-clippy repo git fetch upstream # make sure that you are on the master branch diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index 593162f09a7..cb8cb0978f6 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-05-06" +channel = "nightly-2021-05-20" components = ["llvm-tools-preview", "rustc-dev", "rust-src"] diff --git a/src/tools/clippy/tests/dogfood.rs b/src/tools/clippy/tests/dogfood.rs index 6524fd4706c..5d9f128753f 100644 --- a/src/tools/clippy/tests/dogfood.rs +++ b/src/tools/clippy/tests/dogfood.rs @@ -22,14 +22,12 @@ fn dogfood_clippy() { return; } let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let enable_metadata_collection = std::env::var("ENABLE_METADATA_COLLECTION").unwrap_or_else(|_| "0".to_string()); let mut command = Command::new(&*CLIPPY_PATH); command .current_dir(root_dir) .env("CLIPPY_DOGFOOD", "1") .env("CARGO_INCREMENTAL", "0") - .env("ENABLE_METADATA_COLLECTION", &enable_metadata_collection) .arg("clippy") .arg("--all-targets") .arg("--all-features") @@ -157,10 +155,9 @@ fn dogfood_subprojects() { if cargo::is_rustc_test_suite() { return; } - let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); // NOTE: `path_dep` crate is omitted on purpose here - for d in &[ + for project in &[ "clippy_workspace_tests", "clippy_workspace_tests/src", "clippy_workspace_tests/subcrate", @@ -170,34 +167,49 @@ fn dogfood_subprojects() { "clippy_utils", "rustc_tools_util", ] { - let mut command = Command::new(&*CLIPPY_PATH); - command - .current_dir(root_dir.join(d)) - .env("CLIPPY_DOGFOOD", "1") - .env("CARGO_INCREMENTAL", "0") - .arg("clippy") - .arg("--all-targets") - .arg("--all-features") - .arg("--") - .args(&["-D", "clippy::all"]) - .args(&["-D", "clippy::pedantic"]) - .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir + run_clippy_for_project(project); + } + + // NOTE: Since tests run in parallel we can't run cargo commands on the same workspace at the + // same time, so we test this immediately after the dogfood for workspaces. + test_no_deps_ignores_path_deps_in_workspaces(); +} - // internal lints only exist if we build with the internal-lints feature - if cfg!(feature = "internal-lints") { - command.args(&["-D", "clippy::internal"]); - } +#[test] +#[ignore] +#[cfg(feature = "metadata-collector-lint")] +fn run_metadata_collection_lint() { + std::env::set_var("ENABLE_METADATA_COLLECTION", "1"); + run_clippy_for_project("clippy_lints"); +} - let output = command.output().unwrap(); +fn run_clippy_for_project(project: &str) { + let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - println!("status: {}", output.status); - println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); - println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + let mut command = Command::new(&*CLIPPY_PATH); - assert!(output.status.success()); + command + .current_dir(root_dir.join(project)) + .env("CLIPPY_DOGFOOD", "1") + .env("CARGO_INCREMENTAL", "0") + .arg("clippy") + .arg("--all-targets") + .arg("--all-features") + .arg("--") + .args(&["-D", "clippy::all"]) + .args(&["-D", "clippy::pedantic"]) + .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir + + // internal lints only exist if we build with the internal-lints feature + if cfg!(feature = "internal-lints") { + command.args(&["-D", "clippy::internal"]); } - // NOTE: Since tests run in parallel we can't run cargo commands on the same workspace at the - // same time, so we test this immediately after the dogfood for workspaces. - test_no_deps_ignores_path_deps_in_workspaces(); + let output = command.output().unwrap(); + + println!("status: {}", output.status); + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + + assert!(output.status.success()); } diff --git a/src/tools/clippy/tests/ui/crashes/ice-7231.rs b/src/tools/clippy/tests/ui/crashes/ice-7231.rs new file mode 100644 index 00000000000..5595d8d1d62 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-7231.rs @@ -0,0 +1,10 @@ +// edition:2018 +#![allow(clippy::never_loop)] + +async fn f() { + loop { + break; + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/floating_point_powi.fixed b/src/tools/clippy/tests/ui/floating_point_powi.fixed index 56762400593..85f7c531e39 100644 --- a/src/tools/clippy/tests/ui/floating_point_powi.fixed +++ b/src/tools/clippy/tests/ui/floating_point_powi.fixed @@ -4,8 +4,6 @@ fn main() { let one = 1; let x = 3f32; - let _ = x * x; - let _ = x * x; let y = 4f32; let _ = x.mul_add(x, y); @@ -13,7 +11,10 @@ fn main() { let _ = x.mul_add(x, y).sqrt(); let _ = y.mul_add(y, x).sqrt(); // Cases where the lint shouldn't be applied + let _ = x.powi(2); + let _ = x.powi(1 + 1); let _ = x.powi(3); + let _ = x.powi(4) + y; let _ = x.powi(one + 1); let _ = (x.powi(2) + y.powi(2)).sqrt(); } diff --git a/src/tools/clippy/tests/ui/floating_point_powi.rs b/src/tools/clippy/tests/ui/floating_point_powi.rs index 1f800e4628d..ece61d1bec4 100644 --- a/src/tools/clippy/tests/ui/floating_point_powi.rs +++ b/src/tools/clippy/tests/ui/floating_point_powi.rs @@ -4,8 +4,6 @@ fn main() { let one = 1; let x = 3f32; - let _ = x.powi(2); - let _ = x.powi(1 + 1); let y = 4f32; let _ = x.powi(2) + y; @@ -13,7 +11,10 @@ fn main() { let _ = (x.powi(2) + y).sqrt(); let _ = (x + y.powi(2)).sqrt(); // Cases where the lint shouldn't be applied + let _ = x.powi(2); + let _ = x.powi(1 + 1); let _ = x.powi(3); + let _ = x.powi(4) + y; let _ = x.powi(one + 1); let _ = (x.powi(2) + y.powi(2)).sqrt(); } diff --git a/src/tools/clippy/tests/ui/floating_point_powi.stderr b/src/tools/clippy/tests/ui/floating_point_powi.stderr index d5a5f1bcca1..37d840988bb 100644 --- a/src/tools/clippy/tests/ui/floating_point_powi.stderr +++ b/src/tools/clippy/tests/ui/floating_point_powi.stderr @@ -1,40 +1,28 @@ -error: square can be computed more efficiently - --> $DIR/floating_point_powi.rs:7:13 - | -LL | let _ = x.powi(2); - | ^^^^^^^^^ help: consider using: `x * x` - | - = note: `-D clippy::suboptimal-flops` implied by `-D warnings` - -error: square can be computed more efficiently - --> $DIR/floating_point_powi.rs:8:13 - | -LL | let _ = x.powi(1 + 1); - | ^^^^^^^^^^^^^ help: consider using: `x * x` - -error: square can be computed more efficiently - --> $DIR/floating_point_powi.rs:11:13 +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:9:13 | LL | let _ = x.powi(2) + y; | ^^^^^^^^^^^^^ help: consider using: `x.mul_add(x, y)` + | + = note: `-D clippy::suboptimal-flops` implied by `-D warnings` -error: square can be computed more efficiently - --> $DIR/floating_point_powi.rs:12:13 +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:10:13 | LL | let _ = x + y.powi(2); | ^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)` -error: square can be computed more efficiently - --> $DIR/floating_point_powi.rs:13:13 +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:11:13 | LL | let _ = (x.powi(2) + y).sqrt(); | ^^^^^^^^^^^^^^^ help: consider using: `x.mul_add(x, y)` -error: square can be computed more efficiently - --> $DIR/floating_point_powi.rs:14:13 +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:12:13 | LL | let _ = (x + y.powi(2)).sqrt(); | ^^^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)` -error: aborting due to 6 previous errors +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/impl.rs b/src/tools/clippy/tests/ui/impl.rs index 1c46e3a5337..39443775015 100644 --- a/src/tools/clippy/tests/ui/impl.rs +++ b/src/tools/clippy/tests/ui/impl.rs @@ -33,4 +33,35 @@ impl fmt::Debug for MyStruct { } } +// issue #5772 +struct WithArgs<T>(T); +impl WithArgs<u32> { + fn f1() {} +} +impl WithArgs<u64> { + fn f2() {} +} +impl WithArgs<u64> { + fn f3() {} +} + +// Ok, the struct is allowed to have multiple impls. +#[allow(clippy::multiple_inherent_impl)] +struct Allowed; +impl Allowed {} +impl Allowed {} +impl Allowed {} + +struct AllowedImpl; +#[allow(clippy::multiple_inherent_impl)] +impl AllowedImpl {} +// Ok, the first block is skipped by this lint. +impl AllowedImpl {} + +struct OneAllowedImpl; +impl OneAllowedImpl {} +#[allow(clippy::multiple_inherent_impl)] +impl OneAllowedImpl {} +impl OneAllowedImpl {} // Lint, only one of the three blocks is allowed. + fn main() {} diff --git a/src/tools/clippy/tests/ui/impl.stderr b/src/tools/clippy/tests/ui/impl.stderr index aab688cc2d8..8703ecac93e 100644 --- a/src/tools/clippy/tests/ui/impl.stderr +++ b/src/tools/clippy/tests/ui/impl.stderr @@ -31,5 +31,33 @@ LL | | fn first() {} LL | | } | |_^ -error: aborting due to 2 previous errors +error: multiple implementations of this structure + --> $DIR/impl.rs:44:1 + | +LL | / impl WithArgs<u64> { +LL | | fn f3() {} +LL | | } + | |_^ + | +note: first implementation here + --> $DIR/impl.rs:41:1 + | +LL | / impl WithArgs<u64> { +LL | | fn f2() {} +LL | | } + | |_^ + +error: multiple implementations of this structure + --> $DIR/impl.rs:65:1 + | +LL | impl OneAllowedImpl {} // Lint, only one of the three blocks is allowed. + | ^^^^^^^^^^^^^^^^^^^^^^ + | +note: first implementation here + --> $DIR/impl.rs:62:1 + | +LL | impl OneAllowedImpl {} + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or.fixed b/src/tools/clippy/tests/ui/manual_unwrap_or.fixed index e7a29596b73..3717f962745 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or.fixed +++ b/src/tools/clippy/tests/ui/manual_unwrap_or.fixed @@ -163,4 +163,19 @@ mod issue6965 { } } +use std::rc::Rc; +fn format_name(name: Option<&Rc<str>>) -> &str { + match name { + None => "<anon>", + Some(name) => name, + } +} + +fn implicit_deref_ref() { + let _: &str = match Some(&"bye") { + None => "hi", + Some(s) => s, + }; +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or.rs b/src/tools/clippy/tests/ui/manual_unwrap_or.rs index 66006b6c616..989adde1f5b 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or.rs +++ b/src/tools/clippy/tests/ui/manual_unwrap_or.rs @@ -205,4 +205,19 @@ mod issue6965 { } } +use std::rc::Rc; +fn format_name(name: Option<&Rc<str>>) -> &str { + match name { + None => "<anon>", + Some(name) => name, + } +} + +fn implicit_deref_ref() { + let _: &str = match Some(&"bye") { + None => "hi", + Some(s) => s, + }; +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/match_single_binding.fixed b/src/tools/clippy/tests/ui/match_single_binding.fixed index 526e94b10bd..30bf6402253 100644 --- a/src/tools/clippy/tests/ui/match_single_binding.fixed +++ b/src/tools/clippy/tests/ui/match_single_binding.fixed @@ -94,10 +94,7 @@ fn main() { 0 => println!("Disabled branch"), _ => println!("Enabled branch"), } - // Lint - let x = 1; - let y = 1; - println!("Single branch"); + // Ok let x = 1; let y = 1; diff --git a/src/tools/clippy/tests/ui/match_single_binding.rs b/src/tools/clippy/tests/ui/match_single_binding.rs index 6a2ca7c5e93..d8bb80d8b96 100644 --- a/src/tools/clippy/tests/ui/match_single_binding.rs +++ b/src/tools/clippy/tests/ui/match_single_binding.rs @@ -106,15 +106,7 @@ fn main() { 0 => println!("Disabled branch"), _ => println!("Enabled branch"), } - // Lint - let x = 1; - let y = 1; - match match y { - 0 => 1, - _ => 2, - } { - _ => println!("Single branch"), - } + // Ok let x = 1; let y = 1; diff --git a/src/tools/clippy/tests/ui/match_single_binding.stderr b/src/tools/clippy/tests/ui/match_single_binding.stderr index cbbf5d29c02..795c8c3e24d 100644 --- a/src/tools/clippy/tests/ui/match_single_binding.stderr +++ b/src/tools/clippy/tests/ui/match_single_binding.stderr @@ -167,16 +167,5 @@ LL | unwrapped LL | }) | -error: this match could be replaced by its body itself - --> $DIR/match_single_binding.rs:112:5 - | -LL | / match match y { -LL | | 0 => 1, -LL | | _ => 2, -LL | | } { -LL | | _ => println!("Single branch"), -LL | | } - | |_____^ help: consider using the match body instead: `println!("Single branch");` - -error: aborting due to 12 previous errors +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/match_single_binding2.fixed b/src/tools/clippy/tests/ui/match_single_binding2.fixed index e73a85b73d7..a91fcc2125d 100644 --- a/src/tools/clippy/tests/ui/match_single_binding2.fixed +++ b/src/tools/clippy/tests/ui/match_single_binding2.fixed @@ -34,4 +34,20 @@ fn main() { }, None => println!("nothing"), } + + fn side_effects() {} + + // Lint (scrutinee has side effects) + // issue #7094 + side_effects(); + println!("Side effects"); + + // Lint (scrutinee has side effects) + // issue #7094 + let x = 1; + match x { + 0 => 1, + _ => 2, + }; + println!("Single branch"); } diff --git a/src/tools/clippy/tests/ui/match_single_binding2.rs b/src/tools/clippy/tests/ui/match_single_binding2.rs index 7362cb390e5..476386ebabe 100644 --- a/src/tools/clippy/tests/ui/match_single_binding2.rs +++ b/src/tools/clippy/tests/ui/match_single_binding2.rs @@ -34,4 +34,22 @@ fn main() { }, None => println!("nothing"), } + + fn side_effects() {} + + // Lint (scrutinee has side effects) + // issue #7094 + match side_effects() { + _ => println!("Side effects"), + } + + // Lint (scrutinee has side effects) + // issue #7094 + let x = 1; + match match x { + 0 => 1, + _ => 2, + } { + _ => println!("Single branch"), + } } diff --git a/src/tools/clippy/tests/ui/match_single_binding2.stderr b/src/tools/clippy/tests/ui/match_single_binding2.stderr index bc18d191aa3..4372f55af87 100644 --- a/src/tools/clippy/tests/ui/match_single_binding2.stderr +++ b/src/tools/clippy/tests/ui/match_single_binding2.stderr @@ -30,5 +30,39 @@ LL | let (a, b) = get_tup(); LL | println!("a {:?} and b {:?}", a, b); | -error: aborting due to 2 previous errors +error: this match could be replaced by its scrutinee and body + --> $DIR/match_single_binding2.rs:42:5 + | +LL | / match side_effects() { +LL | | _ => println!("Side effects"), +LL | | } + | |_____^ + | +help: consider using the scrutinee and body instead + | +LL | side_effects(); +LL | println!("Side effects"); + | + +error: this match could be replaced by its scrutinee and body + --> $DIR/match_single_binding2.rs:49:5 + | +LL | / match match x { +LL | | 0 => 1, +LL | | _ => 2, +LL | | } { +LL | | _ => println!("Single branch"), +LL | | } + | |_____^ + | +help: consider using the scrutinee and body instead + | +LL | match x { +LL | 0 => 1, +LL | _ => 2, +LL | }; +LL | println!("Single branch"); + | + +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/needless_bitwise_bool.fixed b/src/tools/clippy/tests/ui/needless_bitwise_bool.fixed new file mode 100644 index 00000000000..5e1ea663a10 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_bitwise_bool.fixed @@ -0,0 +1,40 @@ +// run-rustfix + +#![warn(clippy::needless_bitwise_bool)] + +fn returns_bool() -> bool { + true +} + +const fn const_returns_bool() -> bool { + false +} + +fn main() { + let (x, y) = (false, true); + if x & y { + println!("true") + } + if returns_bool() & x { + println!("true") + } + if !returns_bool() & returns_bool() { + println!("true") + } + if y && !x { + println!("true") + } + + // BELOW: lints we hope to catch as `Expr::can_have_side_effects` improves. + if y & !const_returns_bool() { + println!("true") // This is a const function, in an UnOp + } + + if y & "abcD".is_empty() { + println!("true") // This is a const method call + } + + if y & (0 < 1) { + println!("true") // This is a BinOp with no side effects + } +} diff --git a/src/tools/clippy/tests/ui/needless_bitwise_bool.rs b/src/tools/clippy/tests/ui/needless_bitwise_bool.rs new file mode 100644 index 00000000000..f3075fba0a2 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_bitwise_bool.rs @@ -0,0 +1,40 @@ +// run-rustfix + +#![warn(clippy::needless_bitwise_bool)] + +fn returns_bool() -> bool { + true +} + +const fn const_returns_bool() -> bool { + false +} + +fn main() { + let (x, y) = (false, true); + if x & y { + println!("true") + } + if returns_bool() & x { + println!("true") + } + if !returns_bool() & returns_bool() { + println!("true") + } + if y & !x { + println!("true") + } + + // BELOW: lints we hope to catch as `Expr::can_have_side_effects` improves. + if y & !const_returns_bool() { + println!("true") // This is a const function, in an UnOp + } + + if y & "abcD".is_empty() { + println!("true") // This is a const method call + } + + if y & (0 < 1) { + println!("true") // This is a BinOp with no side effects + } +} diff --git a/src/tools/clippy/tests/ui/needless_bitwise_bool.stderr b/src/tools/clippy/tests/ui/needless_bitwise_bool.stderr new file mode 100644 index 00000000000..63c88ef63f5 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_bitwise_bool.stderr @@ -0,0 +1,10 @@ +error: use of bitwise operator instead of lazy operator between booleans + --> $DIR/needless_bitwise_bool.rs:24:8 + | +LL | if y & !x { + | ^^^^^^ help: try: `y && !x` + | + = note: `-D clippy::needless-bitwise-bool` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/needless_collect.fixed b/src/tools/clippy/tests/ui/needless_collect.fixed index af6c7bf15ea..6ecbbcb6249 100644 --- a/src/tools/clippy/tests/ui/needless_collect.fixed +++ b/src/tools/clippy/tests/ui/needless_collect.fixed @@ -2,7 +2,7 @@ #![allow(unused, clippy::suspicious_map, clippy::iter_count)] -use std::collections::{BTreeSet, HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList}; #[warn(clippy::needless_collect)] #[allow(unused_variables, clippy::iter_cloned_collect, clippy::iter_next_slice)] @@ -13,9 +13,24 @@ fn main() { // Empty } sample.iter().cloned().any(|x| x == 1); - sample.iter().map(|x| (x, x)).count(); + // #7164 HashMap's and BTreeMap's `len` usage should not be linted + sample.iter().map(|x| (x, x)).collect::<HashMap<_, _>>().len(); + sample.iter().map(|x| (x, x)).collect::<BTreeMap<_, _>>().len(); + + sample.iter().map(|x| (x, x)).next().is_none(); + sample.iter().map(|x| (x, x)).next().is_none(); + // Notice the `HashSet`--this should not be linted sample.iter().collect::<HashSet<_>>().len(); // Neither should this sample.iter().collect::<BTreeSet<_>>().len(); + + sample.iter().count(); + sample.iter().next().is_none(); + sample.iter().cloned().any(|x| x == 1); + sample.iter().any(|x| x == &1); + + // `BinaryHeap` doesn't have `contains` method + sample.iter().count(); + sample.iter().next().is_none(); } diff --git a/src/tools/clippy/tests/ui/needless_collect.rs b/src/tools/clippy/tests/ui/needless_collect.rs index 6ae14f370b1..8dc69bcf5b3 100644 --- a/src/tools/clippy/tests/ui/needless_collect.rs +++ b/src/tools/clippy/tests/ui/needless_collect.rs @@ -2,7 +2,7 @@ #![allow(unused, clippy::suspicious_map, clippy::iter_count)] -use std::collections::{BTreeSet, HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList}; #[warn(clippy::needless_collect)] #[allow(unused_variables, clippy::iter_cloned_collect, clippy::iter_next_slice)] @@ -13,9 +13,24 @@ fn main() { // Empty } sample.iter().cloned().collect::<Vec<_>>().contains(&1); + // #7164 HashMap's and BTreeMap's `len` usage should not be linted sample.iter().map(|x| (x, x)).collect::<HashMap<_, _>>().len(); + sample.iter().map(|x| (x, x)).collect::<BTreeMap<_, _>>().len(); + + sample.iter().map(|x| (x, x)).collect::<HashMap<_, _>>().is_empty(); + sample.iter().map(|x| (x, x)).collect::<BTreeMap<_, _>>().is_empty(); + // Notice the `HashSet`--this should not be linted sample.iter().collect::<HashSet<_>>().len(); // Neither should this sample.iter().collect::<BTreeSet<_>>().len(); + + sample.iter().collect::<LinkedList<_>>().len(); + sample.iter().collect::<LinkedList<_>>().is_empty(); + sample.iter().cloned().collect::<LinkedList<_>>().contains(&1); + sample.iter().collect::<LinkedList<_>>().contains(&&1); + + // `BinaryHeap` doesn't have `contains` method + sample.iter().collect::<BinaryHeap<_>>().len(); + sample.iter().collect::<BinaryHeap<_>>().is_empty(); } diff --git a/src/tools/clippy/tests/ui/needless_collect.stderr b/src/tools/clippy/tests/ui/needless_collect.stderr index 2a9539d5975..039091627a8 100644 --- a/src/tools/clippy/tests/ui/needless_collect.stderr +++ b/src/tools/clippy/tests/ui/needless_collect.stderr @@ -19,10 +19,52 @@ LL | sample.iter().cloned().collect::<Vec<_>>().contains(&1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == 1)` error: avoid using `collect()` when not needed - --> $DIR/needless_collect.rs:16:35 + --> $DIR/needless_collect.rs:20:35 | -LL | sample.iter().map(|x| (x, x)).collect::<HashMap<_, _>>().len(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()` +LL | sample.iter().map(|x| (x, x)).collect::<HashMap<_, _>>().is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` -error: aborting due to 4 previous errors +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:21:35 + | +LL | sample.iter().map(|x| (x, x)).collect::<BTreeMap<_, _>>().is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:28:19 + | +LL | sample.iter().collect::<LinkedList<_>>().len(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:29:19 + | +LL | sample.iter().collect::<LinkedList<_>>().is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:30:28 + | +LL | sample.iter().cloned().collect::<LinkedList<_>>().contains(&1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == 1)` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:31:19 + | +LL | sample.iter().collect::<LinkedList<_>>().contains(&&1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == &1)` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:34:19 + | +LL | sample.iter().collect::<BinaryHeap<_>>().len(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:35:19 + | +LL | sample.iter().collect::<BinaryHeap<_>>().is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` + +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/needless_question_mark.fixed b/src/tools/clippy/tests/ui/needless_question_mark.fixed index 52ddd9d2dc8..f1fc81aa12b 100644 --- a/src/tools/clippy/tests/ui/needless_question_mark.fixed +++ b/src/tools/clippy/tests/ui/needless_question_mark.fixed @@ -94,6 +94,11 @@ where Ok(x?) } +// not quite needless +fn deref_ref(s: Option<&String>) -> Option<&str> { + Some(s?) +} + fn main() {} // #6921 if a macro wraps an expr in Some( ) and the ? is in the macro use, diff --git a/src/tools/clippy/tests/ui/needless_question_mark.rs b/src/tools/clippy/tests/ui/needless_question_mark.rs index 1ea4ba0d83f..44a0c5f61b5 100644 --- a/src/tools/clippy/tests/ui/needless_question_mark.rs +++ b/src/tools/clippy/tests/ui/needless_question_mark.rs @@ -94,6 +94,11 @@ where Ok(x?) } +// not quite needless +fn deref_ref(s: Option<&String>) -> Option<&str> { + Some(s?) +} + fn main() {} // #6921 if a macro wraps an expr in Some( ) and the ? is in the macro use, diff --git a/src/tools/clippy/tests/ui/needless_question_mark.stderr b/src/tools/clippy/tests/ui/needless_question_mark.stderr index f1f05d1af3a..02bf50d077a 100644 --- a/src/tools/clippy/tests/ui/needless_question_mark.stderr +++ b/src/tools/clippy/tests/ui/needless_question_mark.stderr @@ -67,7 +67,7 @@ LL | return Ok(t.magic?); | ^^^^^^^^^^^^ help: try: `t.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:115:27 + --> $DIR/needless_question_mark.rs:120:27 | LL | || -> Option<_> { Some(Some($expr)?) }() | ^^^^^^^^^^^^^^^^^^ help: try: `Some($expr)` diff --git a/src/tools/clippy/tests/ui/option_if_let_else.fixed b/src/tools/clippy/tests/ui/option_if_let_else.fixed index 47e7460fa7a..769ccc14bc1 100644 --- a/src/tools/clippy/tests/ui/option_if_let_else.fixed +++ b/src/tools/clippy/tests/ui/option_if_let_else.fixed @@ -10,7 +10,11 @@ fn bad1(string: Option<&str>) -> (bool, &str) { fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> { if string.is_none() { None - } else { string.map_or(Some((false, "")), |x| Some((true, x))) } + } else if let Some(x) = string { + Some((true, x)) + } else { + Some((false, "")) + } } fn unop_bad(string: &Option<&str>, mut num: Option<i32>) { diff --git a/src/tools/clippy/tests/ui/option_if_let_else.stderr b/src/tools/clippy/tests/ui/option_if_let_else.stderr index 7aab068800a..4ebb068d22e 100644 --- a/src/tools/clippy/tests/ui/option_if_let_else.stderr +++ b/src/tools/clippy/tests/ui/option_if_let_else.stderr @@ -11,17 +11,6 @@ LL | | } = note: `-D clippy::option-if-let-else` implied by `-D warnings` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:17:12 - | -LL | } else if let Some(x) = string { - | ____________^ -LL | | Some((true, x)) -LL | | } else { -LL | | Some((false, "")) -LL | | } - | |_____^ help: try: `{ string.map_or(Some((false, "")), |x| Some((true, x))) }` - -error: use Option::map_or instead of an if let/else --> $DIR/option_if_let_else.rs:25:13 | LL | let _ = if let Some(s) = *string { s.len() } else { 0 }; @@ -159,5 +148,5 @@ error: use Option::map_or instead of an if let/else LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` -error: aborting due to 12 previous errors +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/unused_async.rs b/src/tools/clippy/tests/ui/unused_async.rs new file mode 100644 index 00000000000..4f4203f5fdb --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_async.rs @@ -0,0 +1,15 @@ +// edition:2018 +#![warn(clippy::unused_async)] + +async fn foo() -> i32 { + 4 +} + +async fn bar() -> i32 { + foo().await +} + +fn main() { + foo(); + bar(); +} diff --git a/src/tools/clippy/tests/ui/unused_async.stderr b/src/tools/clippy/tests/ui/unused_async.stderr new file mode 100644 index 00000000000..8b834d205b1 --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_async.stderr @@ -0,0 +1,13 @@ +error: unused `async` for function with no await statements + --> $DIR/unused_async.rs:4:1 + | +LL | / async fn foo() -> i32 { +LL | | 4 +LL | | } + | |_^ + | + = note: `-D clippy::unused-async` implied by `-D warnings` + = help: consider removing the `async` from this function + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/use_self.fixed b/src/tools/clippy/tests/ui/use_self.fixed index 1282befdfb3..631da6fe066 100644 --- a/src/tools/clippy/tests/ui/use_self.fixed +++ b/src/tools/clippy/tests/ui/use_self.fixed @@ -462,3 +462,33 @@ mod issue6818 { a: i32, } } + +mod issue7206 { + struct MyStruct<const C: char>; + impl From<MyStruct<'a'>> for MyStruct<'b'> { + fn from(_s: MyStruct<'a'>) -> Self { + Self + } + } + + // keep linting non-`Const` generic args + struct S<'a> { + inner: &'a str, + } + + struct S2<T> { + inner: T, + } + + impl<T> S2<T> { + fn new() -> Self { + unimplemented!(); + } + } + + impl<'a> S2<S<'a>> { + fn new_again() -> Self { + Self::new() + } + } +} diff --git a/src/tools/clippy/tests/ui/use_self.rs b/src/tools/clippy/tests/ui/use_self.rs index 7aaac7b2414..7a10d755faa 100644 --- a/src/tools/clippy/tests/ui/use_self.rs +++ b/src/tools/clippy/tests/ui/use_self.rs @@ -462,3 +462,33 @@ mod issue6818 { a: i32, } } + +mod issue7206 { + struct MyStruct<const C: char>; + impl From<MyStruct<'a'>> for MyStruct<'b'> { + fn from(_s: MyStruct<'a'>) -> Self { + Self + } + } + + // keep linting non-`Const` generic args + struct S<'a> { + inner: &'a str, + } + + struct S2<T> { + inner: T, + } + + impl<T> S2<T> { + fn new() -> Self { + unimplemented!(); + } + } + + impl<'a> S2<S<'a>> { + fn new_again() -> Self { + S2::new() + } + } +} diff --git a/src/tools/clippy/tests/ui/use_self.stderr b/src/tools/clippy/tests/ui/use_self.stderr index a32a9b9157d..cf6222c9b45 100644 --- a/src/tools/clippy/tests/ui/use_self.stderr +++ b/src/tools/clippy/tests/ui/use_self.stderr @@ -162,5 +162,11 @@ error: unnecessary structure name repetition LL | A::new::<submod::B>(submod::B {}) | ^ help: use the applicable keyword: `Self` -error: aborting due to 27 previous errors +error: unnecessary structure name repetition + --> $DIR/use_self.rs:491:13 + | +LL | S2::new() + | ^^ help: use the applicable keyword: `Self` + +error: aborting due to 28 previous errors diff --git a/src/tools/clippy/tests/ui/useless_conversion.fixed b/src/tools/clippy/tests/ui/useless_conversion.fixed index 03977de9455..76aa82068d6 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.fixed +++ b/src/tools/clippy/tests/ui/useless_conversion.fixed @@ -70,4 +70,23 @@ fn main() { let a: i32 = 1; let b: i32 = 1; let _ = (a + b) * 3; + + // see #7205 + let s: Foo<'a'> = Foo; + let _: Foo<'b'> = s.into(); + let s2: Foo<'a'> = Foo; + let _: Foo<'a'> = s2; + let s3: Foo<'a'> = Foo; + let _ = s3; + let s4: Foo<'a'> = Foo; + let _ = vec![s4, s4, s4].into_iter(); +} + +#[derive(Copy, Clone)] +struct Foo<const C: char>; + +impl From<Foo<'a'>> for Foo<'b'> { + fn from(_s: Foo<'a'>) -> Self { + Foo + } } diff --git a/src/tools/clippy/tests/ui/useless_conversion.rs b/src/tools/clippy/tests/ui/useless_conversion.rs index f6e094c1661..ccee7abb404 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.rs +++ b/src/tools/clippy/tests/ui/useless_conversion.rs @@ -70,4 +70,23 @@ fn main() { let a: i32 = 1; let b: i32 = 1; let _ = i32::from(a + b) * 3; + + // see #7205 + let s: Foo<'a'> = Foo; + let _: Foo<'b'> = s.into(); + let s2: Foo<'a'> = Foo; + let _: Foo<'a'> = s2.into(); + let s3: Foo<'a'> = Foo; + let _ = Foo::<'a'>::from(s3); + let s4: Foo<'a'> = Foo; + let _ = vec![s4, s4, s4].into_iter().into_iter(); +} + +#[derive(Copy, Clone)] +struct Foo<const C: char>; + +impl From<Foo<'a'>> for Foo<'b'> { + fn from(_s: Foo<'a'>) -> Self { + Foo + } } diff --git a/src/tools/clippy/tests/ui/useless_conversion.stderr b/src/tools/clippy/tests/ui/useless_conversion.stderr index 26a33595031..e6760f700f3 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.stderr +++ b/src/tools/clippy/tests/ui/useless_conversion.stderr @@ -70,5 +70,23 @@ error: useless conversion to the same type: `i32` LL | let _ = i32::from(a + b) * 3; | ^^^^^^^^^^^^^^^^ help: consider removing `i32::from()`: `(a + b)` -error: aborting due to 11 previous errors +error: useless conversion to the same type: `Foo<'a'>` + --> $DIR/useless_conversion.rs:78:23 + | +LL | let _: Foo<'a'> = s2.into(); + | ^^^^^^^^^ help: consider removing `.into()`: `s2` + +error: useless conversion to the same type: `Foo<'a'>` + --> $DIR/useless_conversion.rs:80:13 + | +LL | let _ = Foo::<'a'>::from(s3); + | ^^^^^^^^^^^^^^^^^^^^ help: consider removing `Foo::<'a'>::from()`: `s3` + +error: useless conversion to the same type: `std::vec::IntoIter<Foo<'a'>>` + --> $DIR/useless_conversion.rs:82:13 + | +LL | let _ = vec![s4, s4, s4].into_iter().into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![s4, s4, s4].into_iter()` + +error: aborting due to 14 previous errors diff --git a/src/tools/clippy/tests/ui/while_let_on_iterator.fixed b/src/tools/clippy/tests/ui/while_let_on_iterator.fixed index 749393db124..c3e2cf0c4a4 100644 --- a/src/tools/clippy/tests/ui/while_let_on_iterator.fixed +++ b/src/tools/clippy/tests/ui/while_let_on_iterator.fixed @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::while_let_on_iterator)] -#![allow(clippy::never_loop, unreachable_code, unused_mut)] +#![allow(clippy::never_loop, unreachable_code, unused_mut, dead_code)] fn base() { let mut iter = 1..20; @@ -41,13 +41,6 @@ fn base() { // or this let mut iter = 1u32..20; while let Some(_) = iter.next() { - break; - } - println!("Remaining iter {:?}", iter); - - // or this - let mut iter = 1u32..20; - while let Some(_) = iter.next() { iter = 1..20; } } @@ -135,18 +128,6 @@ fn refutable2() { fn nested_loops() { let a = [42, 1337]; - let mut y = a.iter(); - loop { - // x is reused, so don't lint here - while let Some(_) = y.next() {} - } - - let mut y = a.iter(); - for _ in 0..2 { - while let Some(_) = y.next() { - // y is reused, don't lint - } - } loop { let mut y = a.iter(); @@ -167,10 +148,8 @@ fn issue1121() { } fn issue2965() { - // This should not cause an ICE and suggest: - // - // for _ in values.iter() {} - // + // This should not cause an ICE + use std::collections::HashSet; let mut values = HashSet::new(); values.insert(1); @@ -205,13 +184,145 @@ fn issue1654() { } } +fn issue6491() { + // Used in outer loop, needs &mut + let mut it = 1..40; + while let Some(n) = it.next() { + for m in &mut it { + if m % 10 == 0 { + break; + } + println!("doing something with m: {}", m); + } + println!("n still is {}", n); + } + + // This is fine, inner loop uses a new iterator. + let mut it = 1..40; + for n in it { + let mut it = 1..40; + for m in it { + if m % 10 == 0 { + break; + } + println!("doing something with m: {}", m); + } + + // Weird binding shouldn't change anything. + let (mut it, _) = (1..40, 0); + for m in it { + if m % 10 == 0 { + break; + } + println!("doing something with m: {}", m); + } + + // Used after the loop, needs &mut. + let mut it = 1..40; + for m in &mut it { + if m % 10 == 0 { + break; + } + println!("doing something with m: {}", m); + } + println!("next item {}", it.next().unwrap()); + + println!("n still is {}", n); + } +} + +fn issue6231() { + // Closure in the outer loop, needs &mut + let mut it = 1..40; + let mut opt = Some(0); + while let Some(n) = opt.take().or_else(|| it.next()) { + for m in &mut it { + if n % 10 == 0 { + break; + } + println!("doing something with m: {}", m); + } + println!("n still is {}", n); + } +} + +fn issue1924() { + struct S<T>(T); + impl<T: Iterator<Item = u32>> S<T> { + fn f(&mut self) -> Option<u32> { + // Used as a field. + for i in &mut self.0 { + if !(3..=7).contains(&i) { + return Some(i); + } + } + None + } + + fn f2(&mut self) -> Option<u32> { + // Don't lint, self borrowed inside the loop + while let Some(i) = self.0.next() { + if i == 1 { + return self.f(); + } + } + None + } + } + impl<T: Iterator<Item = u32>> S<(S<T>, Option<u32>)> { + fn f3(&mut self) -> Option<u32> { + // Don't lint, self borrowed inside the loop + while let Some(i) = self.0.0.0.next() { + if i == 1 { + return self.0.0.f(); + } + } + while let Some(i) = self.0.0.0.next() { + if i == 1 { + return self.f3(); + } + } + // This one is fine, a different field is borrowed + for i in &mut self.0.0.0 { + if i == 1 { + return self.0.1.take(); + } else { + self.0.1 = Some(i); + } + } + None + } + } + + struct S2<T>(T, u32); + impl<T: Iterator<Item = u32>> Iterator for S2<T> { + type Item = u32; + fn next(&mut self) -> Option<u32> { + self.0.next() + } + } + + // Don't lint, field of the iterator is accessed in the loop + let mut it = S2(1..40, 0); + while let Some(n) = it.next() { + if n == it.1 { + break; + } + } + + // Needs &mut, field of the iterator is accessed after the loop + let mut it = S2(1..40, 0); + for n in &mut it { + if n == 0 { + break; + } + } + println!("iterator field {}", it.1); +} + fn main() { - base(); - refutable(); - refutable2(); - nested_loops(); - issue1121(); - issue2965(); - issue3670(); - issue1654(); + let mut it = 0..20; + for _ in it { + println!("test"); + } } diff --git a/src/tools/clippy/tests/ui/while_let_on_iterator.rs b/src/tools/clippy/tests/ui/while_let_on_iterator.rs index 30e3b82a7cc..1717006a449 100644 --- a/src/tools/clippy/tests/ui/while_let_on_iterator.rs +++ b/src/tools/clippy/tests/ui/while_let_on_iterator.rs @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::while_let_on_iterator)] -#![allow(clippy::never_loop, unreachable_code, unused_mut)] +#![allow(clippy::never_loop, unreachable_code, unused_mut, dead_code)] fn base() { let mut iter = 1..20; @@ -41,13 +41,6 @@ fn base() { // or this let mut iter = 1u32..20; while let Some(_) = iter.next() { - break; - } - println!("Remaining iter {:?}", iter); - - // or this - let mut iter = 1u32..20; - while let Some(_) = iter.next() { iter = 1..20; } } @@ -135,18 +128,6 @@ fn refutable2() { fn nested_loops() { let a = [42, 1337]; - let mut y = a.iter(); - loop { - // x is reused, so don't lint here - while let Some(_) = y.next() {} - } - - let mut y = a.iter(); - for _ in 0..2 { - while let Some(_) = y.next() { - // y is reused, don't lint - } - } loop { let mut y = a.iter(); @@ -167,10 +148,8 @@ fn issue1121() { } fn issue2965() { - // This should not cause an ICE and suggest: - // - // for _ in values.iter() {} - // + // This should not cause an ICE + use std::collections::HashSet; let mut values = HashSet::new(); values.insert(1); @@ -205,13 +184,145 @@ fn issue1654() { } } +fn issue6491() { + // Used in outer loop, needs &mut + let mut it = 1..40; + while let Some(n) = it.next() { + while let Some(m) = it.next() { + if m % 10 == 0 { + break; + } + println!("doing something with m: {}", m); + } + println!("n still is {}", n); + } + + // This is fine, inner loop uses a new iterator. + let mut it = 1..40; + while let Some(n) = it.next() { + let mut it = 1..40; + while let Some(m) = it.next() { + if m % 10 == 0 { + break; + } + println!("doing something with m: {}", m); + } + + // Weird binding shouldn't change anything. + let (mut it, _) = (1..40, 0); + while let Some(m) = it.next() { + if m % 10 == 0 { + break; + } + println!("doing something with m: {}", m); + } + + // Used after the loop, needs &mut. + let mut it = 1..40; + while let Some(m) = it.next() { + if m % 10 == 0 { + break; + } + println!("doing something with m: {}", m); + } + println!("next item {}", it.next().unwrap()); + + println!("n still is {}", n); + } +} + +fn issue6231() { + // Closure in the outer loop, needs &mut + let mut it = 1..40; + let mut opt = Some(0); + while let Some(n) = opt.take().or_else(|| it.next()) { + while let Some(m) = it.next() { + if n % 10 == 0 { + break; + } + println!("doing something with m: {}", m); + } + println!("n still is {}", n); + } +} + +fn issue1924() { + struct S<T>(T); + impl<T: Iterator<Item = u32>> S<T> { + fn f(&mut self) -> Option<u32> { + // Used as a field. + while let Some(i) = self.0.next() { + if i < 3 || i > 7 { + return Some(i); + } + } + None + } + + fn f2(&mut self) -> Option<u32> { + // Don't lint, self borrowed inside the loop + while let Some(i) = self.0.next() { + if i == 1 { + return self.f(); + } + } + None + } + } + impl<T: Iterator<Item = u32>> S<(S<T>, Option<u32>)> { + fn f3(&mut self) -> Option<u32> { + // Don't lint, self borrowed inside the loop + while let Some(i) = self.0.0.0.next() { + if i == 1 { + return self.0.0.f(); + } + } + while let Some(i) = self.0.0.0.next() { + if i == 1 { + return self.f3(); + } + } + // This one is fine, a different field is borrowed + while let Some(i) = self.0.0.0.next() { + if i == 1 { + return self.0.1.take(); + } else { + self.0.1 = Some(i); + } + } + None + } + } + + struct S2<T>(T, u32); + impl<T: Iterator<Item = u32>> Iterator for S2<T> { + type Item = u32; + fn next(&mut self) -> Option<u32> { + self.0.next() + } + } + + // Don't lint, field of the iterator is accessed in the loop + let mut it = S2(1..40, 0); + while let Some(n) = it.next() { + if n == it.1 { + break; + } + } + + // Needs &mut, field of the iterator is accessed after the loop + let mut it = S2(1..40, 0); + while let Some(n) = it.next() { + if n == 0 { + break; + } + } + println!("iterator field {}", it.1); +} + fn main() { - base(); - refutable(); - refutable2(); - nested_loops(); - issue1121(); - issue2965(); - issue3670(); - issue1654(); + let mut it = 0..20; + while let Some(..) = it.next() { + println!("test"); + } } diff --git a/src/tools/clippy/tests/ui/while_let_on_iterator.stderr b/src/tools/clippy/tests/ui/while_let_on_iterator.stderr index 6554977c798..eff559bef7e 100644 --- a/src/tools/clippy/tests/ui/while_let_on_iterator.stderr +++ b/src/tools/clippy/tests/ui/while_let_on_iterator.stderr @@ -19,28 +19,96 @@ LL | while let Some(_) = iter.next() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in iter` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:101:9 + --> $DIR/while_let_on_iterator.rs:94:9 | LL | while let Some([..]) = it.next() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [..] in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:108:9 + --> $DIR/while_let_on_iterator.rs:101:9 | LL | while let Some([_x]) = it.next() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [_x] in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:121:9 + --> $DIR/while_let_on_iterator.rs:114:9 | LL | while let Some(x @ [_]) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x @ [_] in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:153:9 + --> $DIR/while_let_on_iterator.rs:134:9 | LL | while let Some(_) = y.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in y` -error: aborting due to 7 previous errors +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:191:9 + | +LL | while let Some(m) = it.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in &mut it` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:202:5 + | +LL | while let Some(n) = it.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in it` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:204:9 + | +LL | while let Some(m) = it.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:213:9 + | +LL | while let Some(m) = it.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:222:9 + | +LL | while let Some(m) = it.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in &mut it` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:239:9 + | +LL | while let Some(m) = it.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in &mut it` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:254:13 + | +LL | while let Some(i) = self.0.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in &mut self.0` + +error: manual `!RangeInclusive::contains` implementation + --> $DIR/while_let_on_iterator.rs:255:20 + | +LL | if i < 3 || i > 7 { + | ^^^^^^^^^^^^^^ help: use: `!(3..=7).contains(&i)` + | + = note: `-D clippy::manual-range-contains` implied by `-D warnings` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:286:13 + | +LL | while let Some(i) = self.0.0.0.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in &mut self.0.0.0` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:315:5 + | +LL | while let Some(n) = it.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in &mut it` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:325:5 + | +LL | while let Some(..) = it.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it` + +error: aborting due to 18 previous errors diff --git a/src/tools/clippy/tests/ui/write_with_newline.stderr b/src/tools/clippy/tests/ui/write_with_newline.stderr index a14e86122ee..cecc2ea9406 100644 --- a/src/tools/clippy/tests/ui/write_with_newline.stderr +++ b/src/tools/clippy/tests/ui/write_with_newline.stderr @@ -51,8 +51,8 @@ LL | write!(&mut v, "/n"); | help: use `writeln!()` instead | -LL | writeln!(&mut v, ); - | ^^^^^^^ -- +LL | writeln!(&mut v); + | ^^^^^^^ -- error: using `write!()` with a format string that ends in a single newline --> $DIR/write_with_newline.rs:36:5 diff --git a/src/tools/clippy/tests/ui/wrong_self_convention2.rs b/src/tools/clippy/tests/ui/wrong_self_convention2.rs index ae3a740d405..3a72174d03d 100644 --- a/src/tools/clippy/tests/ui/wrong_self_convention2.rs +++ b/src/tools/clippy/tests/ui/wrong_self_convention2.rs @@ -23,7 +23,7 @@ mod issue6983 { } struct FooNoCopy; - // trigger lint + // don't trigger impl ToU64 for FooNoCopy { fn to_u64(self) -> u64 { 2 @@ -42,3 +42,30 @@ mod issue7032 { } } } + +mod issue7179 { + pub struct S(i32); + + impl S { + // don't trigger (`s` is not `self`) + pub fn from_be(s: Self) -> Self { + S(i32::from_be(s.0)) + } + + // lint + pub fn from_be_self(self) -> Self { + S(i32::from_be(self.0)) + } + } + + trait T { + // don't trigger (`s` is not `self`) + fn from_be(s: Self) -> Self; + // lint + fn from_be_self(self) -> Self; + } + + trait Foo: Sized { + fn as_byte_slice(slice: &[Self]) -> &[u8]; + } +} diff --git a/src/tools/clippy/tests/ui/wrong_self_convention2.stderr b/src/tools/clippy/tests/ui/wrong_self_convention2.stderr index 0ca1a390974..d2d74ce099e 100644 --- a/src/tools/clippy/tests/ui/wrong_self_convention2.stderr +++ b/src/tools/clippy/tests/ui/wrong_self_convention2.stderr @@ -1,11 +1,19 @@ -error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference - --> $DIR/wrong_self_convention2.rs:28:19 +error: methods called `from_*` usually take no `self` + --> $DIR/wrong_self_convention2.rs:56:29 | -LL | fn to_u64(self) -> u64 { - | ^^^^ +LL | pub fn from_be_self(self) -> Self { + | ^^^^ | = note: `-D clippy::wrong-self-convention` implied by `-D warnings` = help: consider choosing a less ambiguous name -error: aborting due to previous error +error: methods called `from_*` usually take no `self` + --> $DIR/wrong_self_convention2.rs:65:25 + | +LL | fn from_be_self(self) -> Self; + | ^^^^ + | + = help: consider choosing a less ambiguous name + +error: aborting due to 2 previous errors diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 7b42de0ec43..3f983884460 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -7,8 +7,8 @@ use std::path::Path; const ENTRY_LIMIT: usize = 1000; // FIXME: The following limits should be reduced eventually. -const ROOT_ENTRY_LIMIT: usize = 1370; -const ISSUES_ENTRY_LIMIT: usize = 2555; +const ROOT_ENTRY_LIMIT: usize = 1371; +const ISSUES_ENTRY_LIMIT: usize = 2558; fn check_entries(path: &Path, bad: &mut bool) { let dirs = walkdir::WalkDir::new(&path.join("test/ui")) |
