diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 6c2a871e520f9..544ef38c12c6d 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -214,11 +214,7 @@ fn handle_native(name: &str) -> &str { } pub fn target_cpu(sess: &Session) -> &str { - let name = match sess.opts.cg.target_cpu { - Some(ref s) => &**s, - None => &*sess.target.cpu, - }; - + let name = sess.opts.cg.target_cpu.as_ref().unwrap_or(&sess.target.cpu); handle_native(name) } @@ -254,8 +250,6 @@ pub fn handle_native_features(sess: &Session) -> Vec { } pub fn tune_cpu(sess: &Session) -> Option<&str> { - match sess.opts.debugging_opts.tune_cpu { - Some(ref s) => Some(handle_native(&**s)), - None => None, - } + let name = sess.opts.debugging_opts.tune_cpu.as_ref()?; + Some(handle_native(name)) } diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 4ede9d67b741f..a81eb747a335a 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -5,6 +5,7 @@ use rustc_ast as ast; use rustc_ast::NodeId; use rustc_macros::HashStable_Generic; use rustc_span::hygiene::MacroKind; +use rustc_span::Symbol; use std::array::IntoIter; use std::fmt::Debug; @@ -34,7 +35,7 @@ pub enum CtorKind { #[derive(HashStable_Generic)] pub enum NonMacroAttrKind { /// Single-segment attribute defined by the language (`#[inline]`) - Builtin, + Builtin(Symbol), /// Multi-segment custom attribute living in a "tool module" (`#[rustfmt::skip]`). Tool, /// Single-segment custom attribute registered by a derive macro (`#[serde(default)]`). @@ -371,7 +372,7 @@ impl CtorKind { impl NonMacroAttrKind { pub fn descr(self) -> &'static str { match self { - NonMacroAttrKind::Builtin => "built-in attribute", + NonMacroAttrKind::Builtin(..) => "built-in attribute", NonMacroAttrKind::Tool => "tool attribute", NonMacroAttrKind::DeriveHelper | NonMacroAttrKind::DeriveHelperCompat => { "derive helper attribute" @@ -393,7 +394,7 @@ impl NonMacroAttrKind { NonMacroAttrKind::Tool | NonMacroAttrKind::DeriveHelper | NonMacroAttrKind::DeriveHelperCompat => true, - NonMacroAttrKind::Builtin | NonMacroAttrKind::Registered => false, + NonMacroAttrKind::Builtin(..) | NonMacroAttrKind::Registered => false, } } } diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index 8273c2d291d09..d4fecb80f4daf 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -1,9 +1,10 @@ use smallvec::smallvec; use crate::traits::{Obligation, ObligationCause, PredicateObligation}; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_middle::ty::outlives::Component; use rustc_middle::ty::{self, ToPredicate, TyCtxt, WithConstness}; +use rustc_span::symbol::Symbol; pub fn anonymize_predicate<'tcx>( tcx: TyCtxt<'tcx>, @@ -287,6 +288,37 @@ pub fn transitive_bounds<'tcx>( elaborate_trait_refs(tcx, bounds).filter_to_traits() } +/// A specialized variant of `elaborate_trait_refs` that only elaborates trait references that may +/// define the given associated type `assoc_name`. It uses the +/// `super_predicates_that_define_assoc_type` query to avoid enumerating super-predicates that +/// aren't related to `assoc_item`. This is used when resolving types like `Self::Item` or +/// `T::Item` and helps to avoid cycle errors (see e.g. #35237). +pub fn transitive_bounds_that_define_assoc_type<'tcx>( + tcx: TyCtxt<'tcx>, + bounds: impl Iterator>, + assoc_name: Symbol, +) -> FxIndexSet> { + let mut stack: Vec<_> = bounds.collect(); + let mut trait_refs = FxIndexSet::default(); + + while let Some(trait_ref) = stack.pop() { + if trait_refs.insert(trait_ref) { + let super_predicates = + tcx.super_predicates_that_define_assoc_type((trait_ref.def_id(), Some(assoc_name))); + for (super_predicate, _) in super_predicates.predicates { + let bound_predicate = super_predicate.bound_atom(); + let subst_predicate = super_predicate + .subst_supertrait(tcx, &bound_predicate.rebind(trait_ref.skip_binder())); + if let Some(binder) = subst_predicate.to_opt_poly_trait_ref() { + stack.push(binder.value); + } + } + } + } + + trait_refs +} + /////////////////////////////////////////////////////////////////////////// // Other /////////////////////////////////////////////////////////////////////////// diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 9770e67f2d25a..b9a9b8c62d935 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -456,12 +456,23 @@ rustc_queries! { /// full predicates are available (note that supertraits have /// additional acyclicity requirements). query super_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { - desc { |tcx| "computing the supertraits of `{}`", tcx.def_path_str(key) } + desc { |tcx| "computing the super predicates of `{}`", tcx.def_path_str(key) } + } + + /// The `Option` is the name of an associated type. If it is `None`, then this query + /// returns the full set of predicates. If `Some`, then the query returns only the + /// subset of super-predicates that reference traits that define the given associated type. + /// This is used to avoid cycles in resolving types like `T::Item`. + query super_predicates_that_define_assoc_type(key: (DefId, Option)) -> ty::GenericPredicates<'tcx> { + desc { |tcx| "computing the super traits of `{}`{}", + tcx.def_path_str(key.0), + if let Some(assoc_name) = key.1 { format!(" with associated type name `{}`", assoc_name) } else { "".to_string() }, + } } /// To avoid cycles within the predicates of a single item we compute /// per-type-parameter predicates for resolving `T::AssocTy`. - query type_param_predicates(key: (DefId, LocalDefId)) -> ty::GenericPredicates<'tcx> { + query type_param_predicates(key: (DefId, LocalDefId, rustc_span::symbol::Symbol)) -> ty::GenericPredicates<'tcx> { desc { |tcx| "computing the bounds for type parameter `{}`", { let id = tcx.hir().local_def_id_to_hir_id(key.1); tcx.hir().ty_param_name(id) @@ -1316,7 +1327,7 @@ rustc_queries! { desc { "looking up a named region" } } query is_late_bound_map(_: LocalDefId) -> - Option<&'tcx FxHashSet> { + Option<(LocalDefId, &'tcx FxHashSet)> { desc { "testing if a region is late bound" } } query object_lifetime_defaults_map(_: LocalDefId) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index a355242f5ce67..a176333070482 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -3,6 +3,7 @@ use crate::arena::Arena; use crate::dep_graph::DepGraph; use crate::hir::exports::ExportMap; +use crate::hir::place::Place as HirPlace; use crate::ich::{NodeIdHashingMode, StableHashingContext}; use crate::infer::canonical::{Canonical, CanonicalVarInfo, CanonicalVarInfos}; use crate::lint::{struct_lint_level, LintDiagnosticBuilder, LintLevelSource}; @@ -379,7 +380,7 @@ pub struct TypeckResults<'tcx> { /// Records the reasons that we picked the kind of each closure; /// not all closures are present in the map. - closure_kind_origins: ItemLocalMap<(Span, Symbol)>, + closure_kind_origins: ItemLocalMap<(Span, HirPlace<'tcx>)>, /// For each fn, records the "liberated" types of its arguments /// and return type. Liberated means that all bound regions @@ -642,11 +643,13 @@ impl<'tcx> TypeckResults<'tcx> { self.upvar_capture_map[&upvar_id] } - pub fn closure_kind_origins(&self) -> LocalTableInContext<'_, (Span, Symbol)> { + pub fn closure_kind_origins(&self) -> LocalTableInContext<'_, (Span, HirPlace<'tcx>)> { LocalTableInContext { hir_owner: self.hir_owner, data: &self.closure_kind_origins } } - pub fn closure_kind_origins_mut(&mut self) -> LocalTableInContextMut<'_, (Span, Symbol)> { + pub fn closure_kind_origins_mut( + &mut self, + ) -> LocalTableInContextMut<'_, (Span, HirPlace<'tcx>)> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.closure_kind_origins } } @@ -2056,6 +2059,43 @@ impl<'tcx> TyCtxt<'tcx> { self.mk_fn_ptr(sig.map_bound(|sig| ty::FnSig { unsafety: hir::Unsafety::Unsafe, ..sig })) } + /// Given the def_id of a Trait `trait_def_id` and the name of an associated item `assoc_name` + /// returns true if the `trait_def_id` defines an associated item of name `assoc_name`. + pub fn trait_may_define_assoc_type(self, trait_def_id: DefId, assoc_name: Symbol) -> bool { + self.super_traits_of(trait_def_id).any(|trait_did| { + self.associated_items(trait_did) + .find_by_name_and_kind_unhygienic(assoc_name, ty::AssocKind::Type) + .next() + .is_some() + }) + } + + /// Computes the def-ids of the transitive super-traits of `trait_def_id`. This (intentionally) + /// does not compute the full elaborated super-predicates but just the set of def-ids. It is used + /// to identify which traits may define a given associated type to help avoid cycle errors. + /// Returns a `DefId` iterator. + fn super_traits_of(self, trait_def_id: DefId) -> impl Iterator + 'tcx { + let mut set = FxHashSet::default(); + let mut stack = vec![trait_def_id]; + + set.insert(trait_def_id); + + iter::from_fn(move || -> Option { + let trait_did = stack.pop()?; + let generic_predicates = self.super_predicates_of(trait_did); + + for (predicate, _) in generic_predicates.predicates { + if let ty::PredicateAtom::Trait(data, _) = predicate.skip_binders() { + if set.insert(data.def_id()) { + stack.push(data.def_id()); + } + } + } + + Some(trait_did) + }) + } + /// Given a closure signature, returns an equivalent fn signature. Detuples /// and so forth -- so e.g., if we have a sig with `Fn<(u32, i32)>` then /// you would get a `fn(u32, i32)`. @@ -2578,7 +2618,8 @@ impl<'tcx> TyCtxt<'tcx> { } pub fn is_late_bound(self, id: HirId) -> bool { - self.is_late_bound_map(id.owner).map_or(false, |set| set.contains(&id.local_id)) + self.is_late_bound_map(id.owner) + .map_or(false, |(owner, set)| owner == id.owner && set.contains(&id.local_id)) } pub fn object_lifetime_defaults(self, id: HirId) -> Option<&'tcx [ObjectLifetimeDefault]> { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 1399fc76e02d6..236275a3fb969 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -17,7 +17,9 @@ pub use self::IntVarValue::*; pub use self::Variance::*; use crate::hir::exports::ExportMap; -use crate::hir::place::Place as HirPlace; +use crate::hir::place::{ + Place as HirPlace, PlaceBase as HirPlaceBase, ProjectionKind as HirProjectionKind, +}; use crate::ich::StableHashingContext; use crate::middle::cstore::CrateStoreDyn; use crate::middle::resolve_lifetime::ObjectLifetimeDefault; @@ -318,6 +320,16 @@ impl<'tcx> AssociatedItems<'tcx> { .find(|item| tcx.hygienic_eq(ident, item.ident, parent_def_id)) } + /// Returns the associated item with the given name and `AssocKind`, if one exists, ignoring + /// hygiene. + pub fn find_by_name_and_kind_unhygienic( + &self, + name: Symbol, + kind: AssocKind, + ) -> impl '_ + Iterator { + self.filter_by_name_unhygienic(name).filter(move |item| item.kind == kind) + } + /// Returns the associated item with the given name in the given `Namespace`, if one exists. pub fn find_by_name_and_namespace( &self, @@ -734,6 +746,43 @@ pub struct CapturedPlace<'tcx> { pub info: CaptureInfo<'tcx>, } +pub fn place_to_string_for_capture(tcx: TyCtxt<'tcx>, place: &HirPlace<'tcx>) -> String { + let name = match place.base { + HirPlaceBase::Upvar(upvar_id) => tcx.hir().name(upvar_id.var_path.hir_id).to_string(), + _ => bug!("Capture_information should only contain upvars"), + }; + let mut curr_string = name; + + for (i, proj) in place.projections.iter().enumerate() { + match proj.kind { + HirProjectionKind::Deref => { + curr_string = format!("*{}", curr_string); + } + HirProjectionKind::Field(idx, variant) => match place.ty_before_projection(i).kind() { + ty::Adt(def, ..) => { + curr_string = format!( + "{}.{}", + curr_string, + def.variants[variant].fields[idx as usize].ident.name.as_str() + ); + } + ty::Tuple(_) => { + curr_string = format!("{}.{}", curr_string, idx); + } + _ => { + bug!( + "Field projection applied to a type other than Adt or Tuple: {:?}.", + place.ty_before_projection(i).kind() + ) + } + }, + proj => bug!("{:?} unexpected because it isn't captured", proj), + } + } + + curr_string.to_string() +} + /// Part of `MinCaptureInformationMap`; describes the capture kind (&, &mut, move) /// for a particular capture as well as identifying the part of the source code /// that triggered this capture to occur. diff --git a/compiler/rustc_middle/src/ty/query/keys.rs b/compiler/rustc_middle/src/ty/query/keys.rs index a005990264cf1..6fe8423897f54 100644 --- a/compiler/rustc_middle/src/ty/query/keys.rs +++ b/compiler/rustc_middle/src/ty/query/keys.rs @@ -149,6 +149,28 @@ impl Key for (LocalDefId, DefId) { } } +impl Key for (DefId, Option) { + type CacheSelector = DefaultCacheSelector; + + fn query_crate(&self) -> CrateNum { + self.0.krate + } + fn default_span(&self, tcx: TyCtxt<'_>) -> Span { + tcx.def_span(self.0) + } +} + +impl Key for (DefId, LocalDefId, Symbol) { + type CacheSelector = DefaultCacheSelector; + + fn query_crate(&self) -> CrateNum { + self.0.krate + } + fn default_span(&self, tcx: TyCtxt<'_>) -> Span { + self.1.default_span(tcx) + } +} + impl Key for (CrateNum, DefId) { type CacheSelector = DefaultCacheSelector; diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs index a5fb8a1cbe8b7..8f41bfae2fdf5 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs @@ -1616,20 +1616,17 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> { let tcx = self.infcx.tcx; - match place.projection { - [] => StorageDeadOrDrop::LocalStorageDead, - [proj_base @ .., elem] => { + match place.last_projection() { + None => StorageDeadOrDrop::LocalStorageDead, + Some((place_base, elem)) => { // FIXME(spastorino) make this iterate - let base_access = self.classify_drop_access_kind(PlaceRef { - local: place.local, - projection: proj_base, - }); + let base_access = self.classify_drop_access_kind(place_base); match elem { ProjectionElem::Deref => match base_access { StorageDeadOrDrop::LocalStorageDead | StorageDeadOrDrop::BoxedStorageDead => { assert!( - Place::ty_from(place.local, proj_base, self.body, tcx).ty.is_box(), + place_base.ty(self.body, tcx).ty.is_box(), "Drop of value behind a reference or raw pointer" ); StorageDeadOrDrop::BoxedStorageDead @@ -1637,7 +1634,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { StorageDeadOrDrop::Destructor(_) => base_access, }, ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => { - let base_ty = Place::ty_from(place.local, proj_base, self.body, tcx).ty; + let base_ty = place_base.ty(self.body, tcx).ty; match base_ty.kind() { ty::Adt(def, _) if def.has_dtor(tcx) => { // Report the outermost adt with a destructor @@ -1652,7 +1649,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { _ => base_access, } } - ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } | ProjectionElem::Index(_) => base_access, diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs index 4ebc1cdca6059..6d98bf554f1cf 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs @@ -103,7 +103,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let did = did.expect_local(); let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did); - if let Some((span, name)) = + if let Some((span, hir_place)) = self.infcx.tcx.typeck(did).closure_kind_origins().get(hir_id) { diag.span_note( @@ -111,7 +111,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { &format!( "closure cannot be invoked more than once because it moves the \ variable `{}` out of its environment", - name, + ty::place_to_string_for_capture(self.infcx.tcx, hir_place) ), ); return; @@ -127,7 +127,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let did = did.expect_local(); let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did); - if let Some((span, name)) = + if let Some((span, hir_place)) = self.infcx.tcx.typeck(did).closure_kind_origins().get(hir_id) { diag.span_note( @@ -135,7 +135,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { &format!( "closure cannot be moved more than once as it is not `Copy` due to \ moving the variable `{}` out of its environment", - name + ty::place_to_string_for_capture(self.infcx.tcx, hir_place) ), ); } @@ -338,8 +338,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { self.describe_field(PlaceRef { local, projection: proj_base }, field) } ProjectionElem::Downcast(_, variant_index) => { - let base_ty = - Place::ty_from(place.local, place.projection, self.body, self.infcx.tcx).ty; + let base_ty = place.ty(self.body, self.infcx.tcx).ty; self.describe_field_from_ty(&base_ty, field, Some(*variant_index)) } ProjectionElem::Field(_, field_type) => { @@ -473,7 +472,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // If we didn't find an overloaded deref or index, then assume it's a // built in deref and check the type of the base. - let base_ty = Place::ty_from(deref_base.local, deref_base.projection, self.body, tcx).ty; + let base_ty = deref_base.ty(self.body, tcx).ty; if base_ty.is_unsafe_ptr() { BorrowedContentSource::DerefRawPointer } else if base_ty.is_mutable_ptr() { diff --git a/compiler/rustc_mir/src/borrow_check/path_utils.rs b/compiler/rustc_mir/src/borrow_check/path_utils.rs index 934729553a73b..fa3ae2367e08e 100644 --- a/compiler/rustc_mir/src/borrow_check/path_utils.rs +++ b/compiler/rustc_mir/src/borrow_check/path_utils.rs @@ -147,27 +147,25 @@ pub(crate) fn is_upvar_field_projection( place_ref: PlaceRef<'tcx>, body: &Body<'tcx>, ) -> Option { - let mut place_projection = place_ref.projection; + let mut place_ref = place_ref; let mut by_ref = false; - if let [proj_base @ .., ProjectionElem::Deref] = place_projection { - place_projection = proj_base; + if let Some((place_base, ProjectionElem::Deref)) = place_ref.last_projection() { + place_ref = place_base; by_ref = true; } - match place_projection { - [base @ .., ProjectionElem::Field(field, _ty)] => { - let base_ty = Place::ty_from(place_ref.local, base, body, tcx).ty; - + match place_ref.last_projection() { + Some((place_base, ProjectionElem::Field(field, _ty))) => { + let base_ty = place_base.ty(body, tcx).ty; if (base_ty.is_closure() || base_ty.is_generator()) && (!by_ref || upvars[field.index()].by_ref) { - Some(*field) + Some(field) } else { None } } - _ => None, } } diff --git a/compiler/rustc_mir/src/borrow_check/type_check/input_output.rs b/compiler/rustc_mir/src/borrow_check/type_check/input_output.rs index b7d22fab3dde6..157959b115947 100644 --- a/compiler/rustc_mir/src/borrow_check/type_check/input_output.rs +++ b/compiler/rustc_mir/src/borrow_check/type_check/input_output.rs @@ -39,10 +39,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { user_provided_sig = None; } else { let typeck_results = self.tcx().typeck(mir_def_id); - user_provided_sig = match typeck_results.user_provided_sigs.get(&mir_def_id.to_def_id()) - { - None => None, - Some(user_provided_poly_sig) => { + user_provided_sig = typeck_results.user_provided_sigs.get(&mir_def_id.to_def_id()).map( + |user_provided_poly_sig| { // Instantiate the canonicalized variables from // user-provided signature (e.g., the `_` in the code // above) with fresh variables. @@ -54,18 +52,16 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // Replace the bound items in the fn sig with fresh // variables, so that they represent the view from // "inside" the closure. - Some( - self.infcx - .replace_bound_vars_with_fresh_vars( - body.span, - LateBoundRegionConversionTime::FnCall, - poly_sig, - ) - .0, - ) - } - } - }; + self.infcx + .replace_bound_vars_with_fresh_vars( + body.span, + LateBoundRegionConversionTime::FnCall, + poly_sig, + ) + .0 + }, + ); + } debug!( "equate_inputs_and_outputs: normalized_input_tys = {:?}, local_decls = {:?}", diff --git a/compiler/rustc_mir/src/borrow_check/universal_regions.rs b/compiler/rustc_mir/src/borrow_check/universal_regions.rs index c1a0d9856b7ea..02d951b70e28d 100644 --- a/compiler/rustc_mir/src/borrow_check/universal_regions.rs +++ b/compiler/rustc_mir/src/borrow_check/universal_regions.rs @@ -788,13 +788,13 @@ fn for_each_late_bound_region_defined_on<'tcx>( fn_def_id: DefId, mut f: impl FnMut(ty::Region<'tcx>), ) { - if let Some(late_bounds) = tcx.is_late_bound_map(fn_def_id.expect_local()) { - for late_bound in late_bounds.iter() { - let hir_id = HirId { owner: fn_def_id.expect_local(), local_id: *late_bound }; + if let Some((owner, late_bounds)) = tcx.is_late_bound_map(fn_def_id.expect_local()) { + for &late_bound in late_bounds.iter() { + let hir_id = HirId { owner, local_id: late_bound }; let name = tcx.hir().name(hir_id); let region_def_id = tcx.hir().local_def_id(hir_id); let liberated_region = tcx.mk_region(ty::ReFree(ty::FreeRegion { - scope: fn_def_id, + scope: owner.to_def_id(), bound_region: ty::BoundRegionKind::BrNamed(region_def_id.to_def_id(), name), })); f(liberated_region); diff --git a/compiler/rustc_mir/src/dataflow/move_paths/builder.rs b/compiler/rustc_mir/src/dataflow/move_paths/builder.rs index ab7fadac91e39..ee78ff00c9b2b 100644 --- a/compiler/rustc_mir/src/dataflow/move_paths/builder.rs +++ b/compiler/rustc_mir/src/dataflow/move_paths/builder.rs @@ -518,14 +518,10 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { // Check if we are assigning into a field of a union, if so, lookup the place // of the union so it is marked as initialized again. - if let [proj_base @ .., ProjectionElem::Field(_, _)] = place.projection { - if let ty::Adt(def, _) = - Place::ty_from(place.local, proj_base, self.builder.body, self.builder.tcx) - .ty - .kind() - { + if let Some((place_base, ProjectionElem::Field(_, _))) = place.last_projection() { + if let ty::Adt(def, _) = place_base.ty(self.builder.body, self.builder.tcx).ty.kind() { if def.is_union() { - place = PlaceRef { local: place.local, projection: proj_base } + place = place_base; } } } diff --git a/compiler/rustc_mir/src/transform/check_consts/qualifs.rs b/compiler/rustc_mir/src/transform/check_consts/qualifs.rs index c66d3ed76df90..0ce1980f10a11 100644 --- a/compiler/rustc_mir/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_mir/src/transform/check_consts/qualifs.rs @@ -174,14 +174,10 @@ where Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => { // Special-case reborrows to be more like a copy of the reference. - if let &[ref proj_base @ .., ProjectionElem::Deref] = place.projection.as_ref() { - let base_ty = Place::ty_from(place.local, proj_base, cx.body, cx.tcx).ty; + if let Some((place_base, ProjectionElem::Deref)) = place.as_ref().last_projection() { + let base_ty = place_base.ty(cx.body, cx.tcx).ty; if let ty::Ref(..) = base_ty.kind() { - return in_place::( - cx, - in_local, - PlaceRef { local: place.local, projection: proj_base }, - ); + return in_place::(cx, in_local, place_base); } } @@ -209,9 +205,9 @@ where Q: Qualif, F: FnMut(Local) -> bool, { - let mut projection = place.projection; - while let &[ref proj_base @ .., proj_elem] = projection { - match proj_elem { + let mut place = place; + while let Some((place_base, elem)) = place.last_projection() { + match elem { ProjectionElem::Index(index) if in_local(index) => return true, ProjectionElem::Deref @@ -222,16 +218,16 @@ where | ProjectionElem::Index(_) => {} } - let base_ty = Place::ty_from(place.local, proj_base, cx.body, cx.tcx); - let proj_ty = base_ty.projection_ty(cx.tcx, proj_elem).ty; + let base_ty = place_base.ty(cx.body, cx.tcx); + let proj_ty = base_ty.projection_ty(cx.tcx, elem).ty; if !Q::in_any_value_of_ty(cx, proj_ty) { return false; } - projection = proj_base; + place = place_base; } - assert!(projection.is_empty()); + assert!(place.projection.is_empty()); in_local(place.local) } diff --git a/compiler/rustc_mir/src/transform/check_consts/validation.rs b/compiler/rustc_mir/src/transform/check_consts/validation.rs index d1c07d1051d2f..36150ec58fd61 100644 --- a/compiler/rustc_mir/src/transform/check_consts/validation.rs +++ b/compiler/rustc_mir/src/transform/check_consts/validation.rs @@ -1007,27 +1007,26 @@ fn place_as_reborrow( body: &Body<'tcx>, place: Place<'tcx>, ) -> Option<&'a [PlaceElem<'tcx>]> { - place.projection.split_last().and_then(|(outermost, inner)| { - if outermost != &ProjectionElem::Deref { - return None; - } - - // A borrow of a `static` also looks like `&(*_1)` in the MIR, but `_1` is a `const` - // that points to the allocation for the static. Don't treat these as reborrows. - if body.local_decls[place.local].is_ref_to_static() { - return None; - } - - // Ensure the type being derefed is a reference and not a raw pointer. - // - // This is sufficient to prevent an access to a `static mut` from being marked as a - // reborrow, even if the check above were to disappear. - let inner_ty = Place::ty_from(place.local, inner, body, tcx).ty; - match inner_ty.kind() { - ty::Ref(..) => Some(inner), - _ => None, + match place.as_ref().last_projection() { + Some((place_base, ProjectionElem::Deref)) => { + // A borrow of a `static` also looks like `&(*_1)` in the MIR, but `_1` is a `const` + // that points to the allocation for the static. Don't treat these as reborrows. + if body.local_decls[place_base.local].is_ref_to_static() { + None + } else { + // Ensure the type being derefed is a reference and not a raw pointer. + // + // This is sufficient to prevent an access to a `static mut` from being marked as a + // reborrow, even if the check above were to disappear. + let inner_ty = place_base.ty(body, tcx).ty; + match inner_ty.kind() { + ty::Ref(..) => Some(place_base.projection), + _ => None, + } + } } - }) + _ => None, + } } fn is_int_bool_or_char(ty: Ty<'_>) -> bool { diff --git a/compiler/rustc_mir/src/transform/check_unsafety.rs b/compiler/rustc_mir/src/transform/check_unsafety.rs index 0732d2568ad1d..bac47d0b547e9 100644 --- a/compiler/rustc_mir/src/transform/check_unsafety.rs +++ b/compiler/rustc_mir/src/transform/check_unsafety.rs @@ -399,17 +399,13 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { place: Place<'tcx>, is_mut_use: bool, ) { - let mut cursor = place.projection.as_ref(); - while let &[ref proj_base @ .., elem] = cursor { - cursor = proj_base; - + for (place_base, elem) in place.iter_projections().rev() { match elem { // Modifications behind a dereference don't affect the value of // the pointer. ProjectionElem::Deref => return, ProjectionElem::Field(..) => { - let ty = - Place::ty_from(place.local, proj_base, &self.body.local_decls, self.tcx).ty; + let ty = place_base.ty(&self.body.local_decls, self.tcx).ty; if let ty::Adt(def, _) = ty.kind() { if self.tcx.layout_scalar_valid_range(def.did) != (Bound::Unbounded, Bound::Unbounded) diff --git a/compiler/rustc_mir/src/transform/instcombine.rs b/compiler/rustc_mir/src/transform/instcombine.rs index 990ca313c5ddc..b0c70372b161a 100644 --- a/compiler/rustc_mir/src/transform/instcombine.rs +++ b/compiler/rustc_mir/src/transform/instcombine.rs @@ -277,11 +277,9 @@ impl OptimizationFinder<'b, 'tcx> { impl Visitor<'tcx> for OptimizationFinder<'b, 'tcx> { fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { if let Rvalue::Ref(_, _, place) = rvalue { - if let PlaceRef { local, projection: &[ref proj_base @ .., ProjectionElem::Deref] } = - place.as_ref() - { + if let Some((place_base, ProjectionElem::Deref)) = place.as_ref().last_projection() { // The dereferenced place must have type `&_`. - let ty = Place::ty_from(local, proj_base, self.body, self.tcx).ty; + let ty = place_base.ty(self.body, self.tcx).ty; if let ty::Ref(_, _, Mutability::Not) = ty.kind() { self.optimizations.and_stars.insert(location); } diff --git a/compiler/rustc_mir/src/transform/promote_consts.rs b/compiler/rustc_mir/src/transform/promote_consts.rs index ea92e23e9bffb..cac5abb1059a8 100644 --- a/compiler/rustc_mir/src/transform/promote_consts.rs +++ b/compiler/rustc_mir/src/transform/promote_consts.rs @@ -445,43 +445,50 @@ impl<'tcx> Validator<'_, 'tcx> { } fn validate_place(&self, place: PlaceRef<'tcx>) -> Result<(), Unpromotable> { - match place { - PlaceRef { local, projection: [] } => self.validate_local(local), - PlaceRef { local, projection: [proj_base @ .., elem] } => { + match place.last_projection() { + None => self.validate_local(place.local), + Some((place_base, elem)) => { // Validate topmost projection, then recurse. - match *elem { + match elem { ProjectionElem::Deref => { let mut promotable = false; - // This is a special treatment for cases like *&STATIC where STATIC is a - // global static variable. - // This pattern is generated only when global static variables are directly - // accessed and is qualified for promotion safely. - if let TempState::Defined { location, .. } = self.temps[local] { - let def_stmt = - self.body[location.block].statements.get(location.statement_index); - if let Some(Statement { - kind: - StatementKind::Assign(box (_, Rvalue::Use(Operand::Constant(c)))), - .. - }) = def_stmt + // The `is_empty` predicate is introduced to exclude the case + // where the projection operations are [ .field, * ]. + // The reason is because promotion will be illegal if field + // accesses precede the dereferencing. + // Discussion can be found at + // https://github.com/rust-lang/rust/pull/74945#discussion_r463063247 + // There may be opportunity for generalization, but this needs to be + // accounted for. + if place_base.projection.is_empty() { + // This is a special treatment for cases like *&STATIC where STATIC is a + // global static variable. + // This pattern is generated only when global static variables are directly + // accessed and is qualified for promotion safely. + if let TempState::Defined { location, .. } = + self.temps[place_base.local] { - if let Some(did) = c.check_static_ptr(self.tcx) { - // Evaluating a promoted may not read statics except if it got - // promoted from a static (this is a CTFE check). So we - // can only promote static accesses inside statics. - if let Some(hir::ConstContext::Static(..)) = self.const_kind { - // The `is_empty` predicate is introduced to exclude the case - // where the projection operations are [ .field, * ]. - // The reason is because promotion will be illegal if field - // accesses precede the dereferencing. - // Discussion can be found at - // https://github.com/rust-lang/rust/pull/74945#discussion_r463063247 - // There may be opportunity for generalization, but this needs to be - // accounted for. - if proj_base.is_empty() - && !self.tcx.is_thread_local_static(did) + let def_stmt = self.body[location.block] + .statements + .get(location.statement_index); + if let Some(Statement { + kind: + StatementKind::Assign(box ( + _, + Rvalue::Use(Operand::Constant(c)), + )), + .. + }) = def_stmt + { + if let Some(did) = c.check_static_ptr(self.tcx) { + // Evaluating a promoted may not read statics except if it got + // promoted from a static (this is a CTFE check). So we + // can only promote static accesses inside statics. + if let Some(hir::ConstContext::Static(..)) = self.const_kind { - promotable = true; + if !self.tcx.is_thread_local_static(did) { + promotable = true; + } } } } @@ -502,8 +509,7 @@ impl<'tcx> Validator<'_, 'tcx> { } ProjectionElem::Field(..) => { - let base_ty = - Place::ty_from(place.local, proj_base, self.body, self.tcx).ty; + let base_ty = place_base.ty(self.body, self.tcx).ty; if let Some(def) = base_ty.ty_adt_def() { // No promotion of union field accesses. if def.is_union() { @@ -513,7 +519,7 @@ impl<'tcx> Validator<'_, 'tcx> { } } - self.validate_place(PlaceRef { local: place.local, projection: proj_base }) + self.validate_place(place_base) } } } @@ -660,13 +666,11 @@ impl<'tcx> Validator<'_, 'tcx> { Rvalue::AddressOf(_, place) => { // We accept `&raw *`, i.e., raw reborrows -- creating a raw pointer is // no problem, only using it is. - if let [proj_base @ .., ProjectionElem::Deref] = place.projection.as_ref() { - let base_ty = Place::ty_from(place.local, proj_base, self.body, self.tcx).ty; + if let Some((place_base, ProjectionElem::Deref)) = place.as_ref().last_projection() + { + let base_ty = place_base.ty(self.body, self.tcx).ty; if let ty::Ref(..) = base_ty.kind() { - return self.validate_place(PlaceRef { - local: place.local, - projection: proj_base, - }); + return self.validate_place(place_base); } } return Err(Unpromotable); @@ -675,12 +679,12 @@ impl<'tcx> Validator<'_, 'tcx> { Rvalue::Ref(_, kind, place) => { // Special-case reborrows to be more like a copy of the reference. let mut place_simplified = place.as_ref(); - if let [proj_base @ .., ProjectionElem::Deref] = &place_simplified.projection { - let base_ty = - Place::ty_from(place_simplified.local, proj_base, self.body, self.tcx).ty; + if let Some((place_base, ProjectionElem::Deref)) = + place_simplified.last_projection() + { + let base_ty = place_base.ty(self.body, self.tcx).ty; if let ty::Ref(..) = base_ty.kind() { - place_simplified = - PlaceRef { local: place_simplified.local, projection: proj_base }; + place_simplified = place_base; } } diff --git a/compiler/rustc_mir/src/util/alignment.rs b/compiler/rustc_mir/src/util/alignment.rs index a0728a6a63015..f567c9cfaab86 100644 --- a/compiler/rustc_mir/src/util/alignment.rs +++ b/compiler/rustc_mir/src/util/alignment.rs @@ -38,15 +38,12 @@ fn is_within_packed<'tcx, L>(tcx: TyCtxt<'tcx>, local_decls: &L, place: Place<'t where L: HasLocalDecls<'tcx>, { - let mut cursor = place.projection.as_ref(); - while let &[ref proj_base @ .., elem] = cursor { - cursor = proj_base; - + for (place_base, elem) in place.iter_projections().rev() { match elem { // encountered a Deref, which is ABI-aligned ProjectionElem::Deref => break, ProjectionElem::Field(..) => { - let ty = Place::ty_from(place.local, proj_base, local_decls, tcx).ty; + let ty = place_base.ty(local_decls, tcx).ty; match ty.kind() { ty::Adt(def, _) if def.repr.packed() => return true, _ => {} diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index f7b16bd991bfd..f150f7a41ae80 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -736,7 +736,7 @@ fn find_skips_from_snippet( fn find_skips(snippet: &str, is_raw: bool) -> Vec { let mut eat_ws = false; - let mut s = snippet.chars().enumerate().peekable(); + let mut s = snippet.char_indices().peekable(); let mut skips = vec![]; while let Some((pos, c)) = s.next() { match (c, s.peek()) { diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index e96fc185b7e03..4ab14c158d337 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -115,7 +115,7 @@ impl<'a> Resolver<'a> { self.get_module(parent_id) } - crate fn get_module(&mut self, def_id: DefId) -> Module<'a> { + pub fn get_module(&mut self, def_id: DefId) -> Module<'a> { // If this is a local module, it will be in `module_map`, no need to recalculate it. if let Some(def_id) = def_id.as_local() { return self.module_map[&def_id]; diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 4f8047ac2dff0..69fb6870d2c67 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -683,7 +683,7 @@ impl<'a> Resolver<'a> { )); } Scope::BuiltinAttrs => { - let res = Res::NonMacroAttr(NonMacroAttrKind::Builtin); + let res = Res::NonMacroAttr(NonMacroAttrKind::Builtin(kw::Empty)); if filter_fn(res) { suggestions.extend( BUILTIN_ATTRIBUTES diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 9de35a8006123..97e556f5a338d 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -14,7 +14,6 @@ use crate::{ResolutionError, Resolver, Segment, UseError}; use rustc_ast::ptr::P; use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor}; use rustc_ast::*; -use rustc_ast::{unwrap_or, walk_list}; use rustc_ast_lowering::ResolverAstLowering; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::DiagnosticId; @@ -1911,7 +1910,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // it needs to be added to the trait map. if ns == ValueNS { let item_name = path.last().unwrap().ident; - let traits = self.get_traits_containing_item(item_name, ns); + let traits = self.traits_in_scope(item_name, ns); self.r.trait_map.insert(id, traits); } @@ -2371,12 +2370,12 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // field, we need to add any trait methods we find that match // the field name so that we can do some nice error reporting // later on in typeck. - let traits = self.get_traits_containing_item(ident, ValueNS); + let traits = self.traits_in_scope(ident, ValueNS); self.r.trait_map.insert(expr.id, traits); } ExprKind::MethodCall(ref segment, ..) => { debug!("(recording candidate traits for expr) recording traits for {}", expr.id); - let traits = self.get_traits_containing_item(segment.ident, ValueNS); + let traits = self.traits_in_scope(segment.ident, ValueNS); self.r.trait_map.insert(expr.id, traits); } _ => { @@ -2385,64 +2384,13 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } } - fn get_traits_containing_item( - &mut self, - mut ident: Ident, - ns: Namespace, - ) -> Vec { - debug!("(getting traits containing item) looking for '{}'", ident.name); - - let mut found_traits = Vec::new(); - // Look for the current trait. - if let Some((module, _)) = self.current_trait_ref { - if self - .r - .resolve_ident_in_module( - ModuleOrUniformRoot::Module(module), - ident, - ns, - &self.parent_scope, - false, - module.span, - ) - .is_ok() - { - let def_id = module.def_id().unwrap(); - found_traits.push(TraitCandidate { def_id, import_ids: smallvec![] }); - } - } - - ident.span = ident.span.normalize_to_macros_2_0(); - let mut search_module = self.parent_scope.module; - loop { - self.r.get_traits_in_module_containing_item( - ident, - ns, - search_module, - &mut found_traits, - &self.parent_scope, - ); - let mut span_data = ident.span.data(); - search_module = unwrap_or!( - self.r.hygienic_lexical_parent(search_module, &mut span_data.ctxt), - break - ); - ident.span = span_data.span(); - } - - if let Some(prelude) = self.r.prelude { - if !search_module.no_implicit_prelude { - self.r.get_traits_in_module_containing_item( - ident, - ns, - prelude, - &mut found_traits, - &self.parent_scope, - ); - } - } - - found_traits + fn traits_in_scope(&mut self, ident: Ident, ns: Namespace) -> Vec { + self.r.traits_in_scope( + self.current_trait_ref.as_ref().map(|(module, _)| *module), + &self.parent_scope, + ident.span.ctxt(), + Some((ident.name, ns)), + ) } } diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index aab5c3cf8f59d..1be06a446875b 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -11,7 +11,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE}; +use rustc_hir::def_id::{CrateNum, DefIdMap, LOCAL_CRATE}; +use rustc_hir::hir_id::ItemLocalId; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::{GenericArg, GenericParam, LifetimeName, Node, ParamName, QPath}; use rustc_hir::{GenericParamKind, HirIdMap, HirIdSet, LifetimeParamKind}; @@ -20,6 +21,7 @@ use rustc_middle::middle::resolve_lifetime::*; use rustc_middle::ty::{self, DefIdTree, GenericParamDefKind, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::lint; +use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; use std::borrow::Cow; @@ -284,7 +286,7 @@ pub fn provide(providers: &mut ty::query::Providers) { resolve_lifetimes, named_region_map: |tcx, id| tcx.resolve_lifetimes(LOCAL_CRATE).defs.get(&id), - is_late_bound_map: |tcx, id| tcx.resolve_lifetimes(LOCAL_CRATE).late_bound.get(&id), + is_late_bound_map, object_lifetime_defaults_map: |tcx, id| { tcx.resolve_lifetimes(LOCAL_CRATE).object_lifetime_defaults.get(&id) }, @@ -320,6 +322,32 @@ fn resolve_lifetimes(tcx: TyCtxt<'_>, for_krate: CrateNum) -> ResolveLifetimes { rl } +fn is_late_bound_map<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, +) -> Option<(LocalDefId, &'tcx FxHashSet)> { + match tcx.def_kind(def_id) { + DefKind::AnonConst => { + let mut def_id = tcx + .parent(def_id.to_def_id()) + .unwrap_or_else(|| bug!("anon const or closure without a parent")); + // We search for the next outer anon const or fn here + // while skipping closures. + // + // Note that for `AnonConst` we still just recurse until we + // find a function body, but who cares :shrug: + while tcx.is_closure(def_id) { + def_id = tcx + .parent(def_id) + .unwrap_or_else(|| bug!("anon const or closure without a parent")); + } + + tcx.is_late_bound_map(def_id.expect_local()) + } + _ => tcx.resolve_lifetimes(LOCAL_CRATE).late_bound.get(&def_id).map(|lt| (def_id, lt)), + } +} + fn krate(tcx: TyCtxt<'_>) -> NamedRegionMap { let krate = tcx.hir().krate(); let mut map = NamedRegionMap { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index c5b8f7d647ca7..af5341623758a 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -44,9 +44,9 @@ use rustc_index::vec::IndexVec; use rustc_metadata::creader::{CStore, CrateLoader}; use rustc_middle::hir::exports::ExportMap; use rustc_middle::middle::cstore::{CrateStore, MetadataLoaderDyn}; +use rustc_middle::span_bug; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, DefIdTree, ResolverOutputs}; -use rustc_middle::{bug, span_bug}; use rustc_session::lint; use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer}; use rustc_session::Session; @@ -1477,49 +1477,76 @@ impl<'a> Resolver<'a> { self.crate_loader.postprocess(krate); } - fn get_traits_in_module_containing_item( + pub fn traits_in_scope( + &mut self, + current_trait: Option>, + parent_scope: &ParentScope<'a>, + ctxt: SyntaxContext, + assoc_item: Option<(Symbol, Namespace)>, + ) -> Vec { + let mut found_traits = Vec::new(); + + if let Some(module) = current_trait { + if self.trait_may_have_item(Some(module), assoc_item) { + let def_id = module.def_id().unwrap(); + found_traits.push(TraitCandidate { def_id, import_ids: smallvec![] }); + } + } + + self.visit_scopes(ScopeSet::All(TypeNS, false), parent_scope, ctxt, |this, scope, _, _| { + match scope { + Scope::Module(module) => { + this.traits_in_module(module, assoc_item, &mut found_traits); + } + Scope::StdLibPrelude => { + if let Some(module) = this.prelude { + this.traits_in_module(module, assoc_item, &mut found_traits); + } + } + Scope::ExternPrelude | Scope::ToolPrelude | Scope::BuiltinTypes => {} + _ => unreachable!(), + } + None::<()> + }); + + found_traits + } + + fn traits_in_module( &mut self, - ident: Ident, - ns: Namespace, module: Module<'a>, + assoc_item: Option<(Symbol, Namespace)>, found_traits: &mut Vec, - parent_scope: &ParentScope<'a>, ) { - assert!(ns == TypeNS || ns == ValueNS); module.ensure_traits(self); let traits = module.traits.borrow(); + for (trait_name, trait_binding) in traits.as_ref().unwrap().iter() { + if self.trait_may_have_item(trait_binding.module(), assoc_item) { + let def_id = trait_binding.res().def_id(); + let import_ids = self.find_transitive_imports(&trait_binding.kind, *trait_name); + found_traits.push(TraitCandidate { def_id, import_ids }); + } + } + } - for &(trait_name, binding) in traits.as_ref().unwrap().iter() { - // Traits have pseudo-modules that can be used to search for the given ident. - if let Some(module) = binding.module() { - let mut ident = ident; - if ident.span.glob_adjust(module.expansion, binding.span).is_none() { - continue; - } - if self - .resolve_ident_in_module_unadjusted( - ModuleOrUniformRoot::Module(module), - ident, - ns, - parent_scope, - false, - module.span, - ) - .is_ok() - { - let import_ids = self.find_transitive_imports(&binding.kind, trait_name); - let trait_def_id = module.def_id().unwrap(); - found_traits.push(TraitCandidate { def_id: trait_def_id, import_ids }); - } - } else if let Res::Def(DefKind::TraitAlias, _) = binding.res() { - // For now, just treat all trait aliases as possible candidates, since we don't - // know if the ident is somewhere in the transitive bounds. - let import_ids = self.find_transitive_imports(&binding.kind, trait_name); - let trait_def_id = binding.res().def_id(); - found_traits.push(TraitCandidate { def_id: trait_def_id, import_ids }); - } else { - bug!("candidate is not trait or trait alias?") + // List of traits in scope is pruned on best effort basis. We reject traits not having an + // associated item with the given name and namespace (if specified). This is a conservative + // optimization, proper hygienic type-based resolution of associated items is done in typeck. + // We don't reject trait aliases (`trait_module == None`) because we don't have access to their + // associated items. + fn trait_may_have_item( + &mut self, + trait_module: Option>, + assoc_item: Option<(Symbol, Namespace)>, + ) -> bool { + match (trait_module, assoc_item) { + (Some(trait_module), Some((name, ns))) => { + self.resolutions(trait_module).borrow().iter().any(|resolution| { + let (&BindingKey { ident: assoc_ident, ns: assoc_ns, .. }, _) = resolution; + assoc_ns == ns && assoc_ident.name == name + }) } + _ => true, } } @@ -3227,34 +3254,6 @@ impl<'a> Resolver<'a> { }) } - /// This is equivalent to `get_traits_in_module_containing_item`, but without filtering by the associated item. - /// - /// This is used by rustdoc for intra-doc links. - pub fn traits_in_scope(&mut self, module_id: DefId) -> Vec { - let module = self.get_module(module_id); - module.ensure_traits(self); - let traits = module.traits.borrow(); - let to_candidate = - |this: &mut Self, &(trait_name, binding): &(Ident, &NameBinding<'_>)| TraitCandidate { - def_id: binding.res().def_id(), - import_ids: this.find_transitive_imports(&binding.kind, trait_name), - }; - - let mut candidates: Vec<_> = - traits.as_ref().unwrap().iter().map(|x| to_candidate(self, x)).collect(); - - if let Some(prelude) = self.prelude { - if !module.no_implicit_prelude { - prelude.ensure_traits(self); - candidates.extend( - prelude.traits.borrow().as_ref().unwrap().iter().map(|x| to_candidate(self, x)), - ); - } - } - - candidates - } - /// Rustdoc uses this to resolve things in a recoverable way. `ResolutionError<'a>` /// isn't something that can be returned because it can't be made to live that long, /// and also it's a private type. Fortunately rustdoc doesn't need to know the error, diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 5c74094ecd3ee..5d6120cd31076 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -757,7 +757,11 @@ impl<'a> Resolver<'a> { } Scope::BuiltinAttrs => { if is_builtin_attr_name(ident.name) { - ok(Res::NonMacroAttr(NonMacroAttrKind::Builtin), DUMMY_SP, this.arenas) + ok( + Res::NonMacroAttr(NonMacroAttrKind::Builtin(ident.name)), + DUMMY_SP, + this.arenas, + ) } else { Err(Determinacy::Determined) } @@ -810,13 +814,15 @@ impl<'a> Resolver<'a> { // Found another solution, if the first one was "weak", report an error. let (res, innermost_res) = (binding.res(), innermost_binding.res()); if res != innermost_res { - let builtin = Res::NonMacroAttr(NonMacroAttrKind::Builtin); + let is_builtin = |res| { + matches!(res, Res::NonMacroAttr(NonMacroAttrKind::Builtin(..))) + }; let derive_helper_compat = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelperCompat); let ambiguity_error_kind = if is_import { Some(AmbiguityKind::Import) - } else if innermost_res == builtin || res == builtin { + } else if is_builtin(innermost_res) || is_builtin(res) { Some(AmbiguityKind::BuiltinAttr) } else if innermost_res == derive_helper_compat || res == derive_helper_compat diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 795cf2e19decc..e78ccb3521914 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -589,23 +589,23 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { if let Some(typeck_results) = self.in_progress_typeck_results { let typeck_results = typeck_results.borrow(); match (found_kind, typeck_results.closure_kind_origins().get(hir_id)) { - (ty::ClosureKind::FnOnce, Some((span, name))) => { + (ty::ClosureKind::FnOnce, Some((span, place))) => { err.span_label( *span, format!( "closure is `FnOnce` because it moves the \ variable `{}` out of its environment", - name + ty::place_to_string_for_capture(tcx, place) ), ); } - (ty::ClosureKind::FnMut, Some((span, name))) => { + (ty::ClosureKind::FnMut, Some((span, place))) => { err.span_label( *span, format!( "closure is `FnMut` because it mutates the \ variable `{}` here", - name + ty::place_to_string_for_capture(tcx, place) ), ); } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 9c894e99a389a..d218cc282073d 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -65,7 +65,8 @@ pub use self::util::{ get_vtable_index_of_object_method, impl_item_is_final, predicate_for_trait_def, upcast_choices, }; pub use self::util::{ - supertrait_def_ids, supertraits, transitive_bounds, SupertraitDefIds, Supertraits, + supertrait_def_ids, supertraits, transitive_bounds, transitive_bounds_that_define_assoc_type, + SupertraitDefIds, Supertraits, }; pub use self::chalk_fulfill::FulfillmentContext as ChalkFulfillmentContext; diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 9a2f69da32e81..e39c0c5a25d3c 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -49,9 +49,10 @@ pub trait AstConv<'tcx> { fn default_constness_for_trait_bounds(&self) -> Constness; - /// Returns predicates in scope of the form `X: Foo`, where `X` is - /// a type parameter `X` with the given id `def_id`. This is a - /// subset of the full set of predicates. + /// Returns predicates in scope of the form `X: Foo`, where `X` + /// is a type parameter `X` with the given id `def_id` and T + /// matches `assoc_name`. This is a subset of the full set of + /// predicates. /// /// This is used for one specific purpose: resolving "short-hand" /// associated type references like `T::Item`. In principle, we @@ -60,7 +61,12 @@ pub trait AstConv<'tcx> { /// but this can lead to cycle errors. The problem is that we have /// to do this resolution *in order to create the predicates in /// the first place*. Hence, we have this "special pass". - fn get_type_parameter_bounds(&self, span: Span, def_id: DefId) -> ty::GenericPredicates<'tcx>; + fn get_type_parameter_bounds( + &self, + span: Span, + def_id: DefId, + assoc_name: Symbol, + ) -> ty::GenericPredicates<'tcx>; /// Returns the lifetime to use when a lifetime is omitted (and not elided). fn re_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) @@ -783,7 +789,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } // Returns `true` if a bounds list includes `?Sized`. - pub fn is_unsized(&self, ast_bounds: &[hir::GenericBound<'_>], span: Span) -> bool { + pub fn is_unsized(&self, ast_bounds: &[&hir::GenericBound<'_>], span: Span) -> bool { let tcx = self.tcx(); // Try to find an unbound in bounds. @@ -841,7 +847,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { fn add_bounds( &self, param_ty: Ty<'tcx>, - ast_bounds: &[hir::GenericBound<'_>], + ast_bounds: &[&hir::GenericBound<'_>], bounds: &mut Bounds<'tcx>, ) { let constness = self.default_constness_for_trait_bounds(); @@ -856,7 +862,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { hir::GenericBound::Trait(_, hir::TraitBoundModifier::Maybe) => {} hir::GenericBound::LangItemTrait(lang_item, span, hir_id, args) => self .instantiate_lang_item_trait_ref( - lang_item, span, hir_id, args, param_ty, bounds, + *lang_item, *span, *hir_id, args, param_ty, bounds, ), hir::GenericBound::Outlives(ref l) => bounds .region_bounds @@ -887,6 +893,42 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ast_bounds: &[hir::GenericBound<'_>], sized_by_default: SizedByDefault, span: Span, + ) -> Bounds<'tcx> { + let ast_bounds: Vec<_> = ast_bounds.iter().collect(); + self.compute_bounds_inner(param_ty, &ast_bounds, sized_by_default, span) + } + + /// Convert the bounds in `ast_bounds` that refer to traits which define an associated type + /// named `assoc_name` into ty::Bounds. Ignore the rest. + pub fn compute_bounds_that_match_assoc_type( + &self, + param_ty: Ty<'tcx>, + ast_bounds: &[hir::GenericBound<'_>], + sized_by_default: SizedByDefault, + span: Span, + assoc_name: Symbol, + ) -> Bounds<'tcx> { + let mut result = Vec::new(); + + for ast_bound in ast_bounds { + if let Some(trait_ref) = ast_bound.trait_ref() { + if let Some(trait_did) = trait_ref.trait_def_id() { + if self.tcx().trait_may_define_assoc_type(trait_did, assoc_name) { + result.push(ast_bound); + } + } + } + } + + self.compute_bounds_inner(param_ty, &result, sized_by_default, span) + } + + fn compute_bounds_inner( + &self, + param_ty: Ty<'tcx>, + ast_bounds: &[&hir::GenericBound<'_>], + sized_by_default: SizedByDefault, + span: Span, ) -> Bounds<'tcx> { let mut bounds = Bounds::default(); @@ -1056,7 +1098,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // Calling `skip_binder` is okay, because `add_bounds` expects the `param_ty` // parameter to have a skipped binder. let param_ty = tcx.mk_projection(assoc_ty.def_id, candidate.skip_binder().substs); - self.add_bounds(param_ty, ast_bounds, bounds); + let ast_bounds: Vec<_> = ast_bounds.iter().collect(); + self.add_bounds(param_ty, &ast_bounds, bounds); } } Ok(()) @@ -1371,8 +1414,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ty_param_def_id, assoc_name, span, ); - let predicates = - &self.get_type_parameter_bounds(span, ty_param_def_id.to_def_id()).predicates; + let predicates = &self + .get_type_parameter_bounds(span, ty_param_def_id.to_def_id(), assoc_name.name) + .predicates; debug!("find_bound_for_assoc_item: predicates={:#?}", predicates); @@ -1380,12 +1424,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let param_name = tcx.hir().ty_param_name(param_hir_id); self.one_bound_for_assoc_type( || { - traits::transitive_bounds( + traits::transitive_bounds_that_define_assoc_type( tcx, predicates.iter().filter_map(|(p, _)| { p.to_opt_poly_trait_ref().map(|trait_ref| trait_ref.value) }), + assoc_name.name, ) + .into_iter() }, || param_name.to_string(), assoc_name, diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs index 0036edda36da5..d37d6bc4f2df6 100644 --- a/compiler/rustc_typeck/src/check/compare_method.rs +++ b/compiler/rustc_typeck/src/check/compare_method.rs @@ -365,12 +365,10 @@ fn check_region_bounds_on_impl_item<'tcx>( let item_kind = assoc_item_kind_str(impl_m); let def_span = tcx.sess.source_map().guess_head_span(span); let span = tcx.hir().get_generics(impl_m.def_id).map_or(def_span, |g| g.span); - let generics_span = if let Some(sp) = tcx.hir().span_if_local(trait_m.def_id) { + let generics_span = tcx.hir().span_if_local(trait_m.def_id).map(|sp| { let def_sp = tcx.sess.source_map().guess_head_span(sp); - Some(tcx.hir().get_generics(trait_m.def_id).map_or(def_sp, |g| g.span)) - } else { - None - }; + tcx.hir().get_generics(trait_m.def_id).map_or(def_sp, |g| g.span) + }); tcx.sess.emit_err(LifetimesOrBoundsMismatchOnTrait { span, diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs b/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs index f635e0b6f931c..6f175f16fecbc 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs @@ -20,6 +20,7 @@ use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::{self, Const, Ty, TyCtxt}; use rustc_session::Session; +use rustc_span::symbol::Symbol; use rustc_span::{self, Span}; use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode}; @@ -183,7 +184,12 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { } } - fn get_type_parameter_bounds(&self, _: Span, def_id: DefId) -> ty::GenericPredicates<'tcx> { + fn get_type_parameter_bounds( + &self, + _: Span, + def_id: DefId, + _: Symbol, + ) -> ty::GenericPredicates<'tcx> { let tcx = self.tcx; let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); let item_id = tcx.hir().ty_param_owner(hir_id); diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index e8cbefd44ee6d..feda35daa617d 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -176,7 +176,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.demand_eqtype(span, inferred_kind.to_ty(self.tcx), closure_kind_ty); // If we have an origin, store it. - if let Some(origin) = delegate.current_origin { + if let Some(origin) = delegate.current_origin.clone() { + let origin = if self.tcx.features().capture_disjoint_fields { + origin + } else { + // FIXME(project-rfc-2229#26): Once rust-lang#80092 is merged, we should restrict the + // precision of origin as well. Otherwise, this will cause issues when project-rfc-2229#26 + // is fixed as we might see Index projections in the origin, which we can't print because + // we don't store enough information. + (origin.0, Place { projections: vec![], ..origin.1 }) + }; + self.typeck_results .borrow_mut() .closure_kind_origins_mut() @@ -563,7 +573,7 @@ struct InferBorrowKind<'a, 'tcx> { // If we modified `current_closure_kind`, this field contains a `Some()` with the // variable access that caused us to do so. - current_origin: Option<(Span, Symbol)>, + current_origin: Option<(Span, Place<'tcx>)>, /// For each Place that is captured by the closure, we track the minimal kind of /// access we need (ref, ref mut, move, etc) and the expression that resulted in such access. @@ -628,7 +638,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { upvar_id.closure_expr_id, ty::ClosureKind::FnOnce, usage_span, - var_name(tcx, upvar_id.var_path.hir_id), + place_with_id.place.clone(), ); let capture_info = ty::CaptureInfo { @@ -720,7 +730,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { upvar_id.closure_expr_id, ty::ClosureKind::FnMut, tcx.hir().span(diag_expr_id), - var_name(tcx, upvar_id.var_path.hir_id), + place_with_id.place.clone(), ); } } @@ -765,11 +775,11 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { closure_id: LocalDefId, new_kind: ty::ClosureKind, upvar_span: Span, - var_name: Symbol, + place: Place<'tcx>, ) { debug!( - "adjust_closure_kind(closure_id={:?}, new_kind={:?}, upvar_span={:?}, var_name={})", - closure_id, new_kind, upvar_span, var_name + "adjust_closure_kind(closure_id={:?}, new_kind={:?}, upvar_span={:?}, place={:?})", + closure_id, new_kind, upvar_span, place ); // Is this the closure whose kind is currently being inferred? @@ -797,7 +807,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { | (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { // new kind is stronger than the old kind self.current_closure_kind = new_kind; - self.current_origin = Some((upvar_span, var_name)); + self.current_origin = Some((upvar_span, place)); } } } diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_typeck/src/check/writeback.rs index 7c9cfe69fc94b..6edce62f76bc9 100644 --- a/compiler/rustc_typeck/src/check/writeback.rs +++ b/compiler/rustc_typeck/src/check/writeback.rs @@ -384,9 +384,11 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner); let common_hir_owner = fcx_typeck_results.hir_owner; - for (&id, &origin) in fcx_typeck_results.closure_kind_origins().iter() { - let hir_id = hir::HirId { owner: common_hir_owner, local_id: id }; - self.typeck_results.closure_kind_origins_mut().insert(hir_id, origin); + for (id, origin) in fcx_typeck_results.closure_kind_origins().iter() { + let hir_id = hir::HirId { owner: common_hir_owner, local_id: *id }; + let place_span = origin.0; + let place = self.resolve(origin.1.clone(), &place_span); + self.typeck_results.closure_kind_origins_mut().insert(hir_id, (place_span, place)); } } diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index a6677328f8f88..3e08ba6562703 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -1,3 +1,4 @@ +// ignore-tidy-filelength //! "Collection" is the process of determining the type and other external //! details of each item in Rust. Collection is specifically concerned //! with *inter-procedural* things -- for example, for a function @@ -79,6 +80,7 @@ pub fn provide(providers: &mut Providers) { projection_ty_from_predicates, explicit_predicates_of, super_predicates_of, + super_predicates_that_define_assoc_type, trait_explicit_predicates_and_bounds, type_param_predicates, trait_def, @@ -310,8 +312,17 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> { } } - fn get_type_parameter_bounds(&self, span: Span, def_id: DefId) -> ty::GenericPredicates<'tcx> { - self.tcx.at(span).type_param_predicates((self.item_def_id, def_id.expect_local())) + fn get_type_parameter_bounds( + &self, + span: Span, + def_id: DefId, + assoc_name: Symbol, + ) -> ty::GenericPredicates<'tcx> { + self.tcx.at(span).type_param_predicates(( + self.item_def_id, + def_id.expect_local(), + assoc_name, + )) } fn re_infer(&self, _: Option<&ty::GenericParamDef>, _: Span) -> Option> { @@ -492,7 +503,7 @@ fn get_new_lifetime_name<'tcx>( /// `X: Foo` where `X` is the type parameter `def_id`. fn type_param_predicates( tcx: TyCtxt<'_>, - (item_def_id, def_id): (DefId, LocalDefId), + (item_def_id, def_id, assoc_name): (DefId, LocalDefId, Symbol), ) -> ty::GenericPredicates<'_> { use rustc_hir::*; @@ -517,7 +528,7 @@ fn type_param_predicates( let mut result = parent .map(|parent| { let icx = ItemCtxt::new(tcx, parent); - icx.get_type_parameter_bounds(DUMMY_SP, def_id.to_def_id()) + icx.get_type_parameter_bounds(DUMMY_SP, def_id.to_def_id(), assoc_name) }) .unwrap_or_default(); let mut extend = None; @@ -560,12 +571,18 @@ fn type_param_predicates( let icx = ItemCtxt::new(tcx, item_def_id); let extra_predicates = extend.into_iter().chain( - icx.type_parameter_bounds_in_generics(ast_generics, param_id, ty, OnlySelfBounds(true)) - .into_iter() - .filter(|(predicate, _)| match predicate.skip_binders() { - ty::PredicateAtom::Trait(data, _) => data.self_ty().is_param(index), - _ => false, - }), + icx.type_parameter_bounds_in_generics( + ast_generics, + param_id, + ty, + OnlySelfBounds(true), + Some(assoc_name), + ) + .into_iter() + .filter(|(predicate, _)| match predicate.skip_binders() { + ty::PredicateAtom::Trait(data, _) => data.self_ty().is_param(index), + _ => false, + }), ); result.predicates = tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(extra_predicates)); @@ -583,6 +600,7 @@ impl ItemCtxt<'tcx> { param_id: hir::HirId, ty: Ty<'tcx>, only_self_bounds: OnlySelfBounds, + assoc_name: Option, ) -> Vec<(ty::Predicate<'tcx>, Span)> { let constness = self.default_constness_for_trait_bounds(); let from_ty_params = ast_generics @@ -593,6 +611,10 @@ impl ItemCtxt<'tcx> { _ => None, }) .flat_map(|bounds| bounds.iter()) + .filter(|b| match assoc_name { + Some(assoc_name) => self.bound_defines_assoc_item(b, assoc_name), + None => true, + }) .flat_map(|b| predicates_from_bound(self, ty, b, constness)); let from_where_clauses = ast_generics @@ -611,12 +633,34 @@ impl ItemCtxt<'tcx> { } else { None }; - bp.bounds.iter().filter_map(move |b| bt.map(|bt| (bt, b))) + bp.bounds + .iter() + .filter(|b| match assoc_name { + Some(assoc_name) => self.bound_defines_assoc_item(b, assoc_name), + None => true, + }) + .filter_map(move |b| bt.map(|bt| (bt, b))) }) .flat_map(|(bt, b)| predicates_from_bound(self, bt, b, constness)); from_ty_params.chain(from_where_clauses).collect() } + + fn bound_defines_assoc_item(&self, b: &hir::GenericBound<'_>, assoc_name: Symbol) -> bool { + debug!("bound_defines_assoc_item(b={:?}, assoc_name={:?})", b, assoc_name); + + match b { + hir::GenericBound::Trait(poly_trait_ref, _) => { + let trait_ref = &poly_trait_ref.trait_ref; + if let Some(trait_did) = trait_ref.trait_def_id() { + self.tcx.trait_may_define_assoc_type(trait_did, assoc_name) + } else { + false + } + } + _ => false, + } + } } /// Tests whether this is the AST for a reference to the type @@ -985,54 +1029,90 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::AdtDef { /// the transitive super-predicates are converted. fn super_predicates_of(tcx: TyCtxt<'_>, trait_def_id: DefId) -> ty::GenericPredicates<'_> { debug!("super_predicates(trait_def_id={:?})", trait_def_id); - let trait_hir_id = tcx.hir().local_def_id_to_hir_id(trait_def_id.expect_local()); + tcx.super_predicates_that_define_assoc_type((trait_def_id, None)) +} - let item = match tcx.hir().get(trait_hir_id) { - Node::Item(item) => item, - _ => bug!("trait_node_id {} is not an item", trait_hir_id), - }; +/// Ensures that the super-predicates of the trait with a `DefId` +/// of `trait_def_id` are converted and stored. This also ensures that +/// the transitive super-predicates are converted. +fn super_predicates_that_define_assoc_type( + tcx: TyCtxt<'_>, + (trait_def_id, assoc_name): (DefId, Option), +) -> ty::GenericPredicates<'_> { + debug!( + "super_predicates_that_define_assoc_type(trait_def_id={:?}, assoc_name={:?})", + trait_def_id, assoc_name + ); + if trait_def_id.is_local() { + debug!("super_predicates_that_define_assoc_type: local trait_def_id={:?}", trait_def_id); + let trait_hir_id = tcx.hir().local_def_id_to_hir_id(trait_def_id.expect_local()); - let (generics, bounds) = match item.kind { - hir::ItemKind::Trait(.., ref generics, ref supertraits, _) => (generics, supertraits), - hir::ItemKind::TraitAlias(ref generics, ref supertraits) => (generics, supertraits), - _ => span_bug!(item.span, "super_predicates invoked on non-trait"), - }; + let item = match tcx.hir().get(trait_hir_id) { + Node::Item(item) => item, + _ => bug!("trait_node_id {} is not an item", trait_hir_id), + }; - let icx = ItemCtxt::new(tcx, trait_def_id); - - // Convert the bounds that follow the colon, e.g., `Bar + Zed` in `trait Foo: Bar + Zed`. - let self_param_ty = tcx.types.self_param; - let superbounds1 = - AstConv::compute_bounds(&icx, self_param_ty, bounds, SizedByDefault::No, item.span); - - let superbounds1 = superbounds1.predicates(tcx, self_param_ty); - - // Convert any explicit superbounds in the where-clause, - // e.g., `trait Foo where Self: Bar`. - // In the case of trait aliases, however, we include all bounds in the where-clause, - // so e.g., `trait Foo = where u32: PartialEq` would include `u32: PartialEq` - // as one of its "superpredicates". - let is_trait_alias = tcx.is_trait_alias(trait_def_id); - let superbounds2 = icx.type_parameter_bounds_in_generics( - generics, - item.hir_id, - self_param_ty, - OnlySelfBounds(!is_trait_alias), - ); + let (generics, bounds) = match item.kind { + hir::ItemKind::Trait(.., ref generics, ref supertraits, _) => (generics, supertraits), + hir::ItemKind::TraitAlias(ref generics, ref supertraits) => (generics, supertraits), + _ => span_bug!(item.span, "super_predicates invoked on non-trait"), + }; + + let icx = ItemCtxt::new(tcx, trait_def_id); + + // Convert the bounds that follow the colon, e.g., `Bar + Zed` in `trait Foo: Bar + Zed`. + let self_param_ty = tcx.types.self_param; + let superbounds1 = if let Some(assoc_name) = assoc_name { + AstConv::compute_bounds_that_match_assoc_type( + &icx, + self_param_ty, + &bounds, + SizedByDefault::No, + item.span, + assoc_name, + ) + } else { + AstConv::compute_bounds(&icx, self_param_ty, &bounds, SizedByDefault::No, item.span) + }; - // Combine the two lists to form the complete set of superbounds: - let superbounds = &*tcx.arena.alloc_from_iter(superbounds1.into_iter().chain(superbounds2)); + let superbounds1 = superbounds1.predicates(tcx, self_param_ty); + + // Convert any explicit superbounds in the where-clause, + // e.g., `trait Foo where Self: Bar`. + // In the case of trait aliases, however, we include all bounds in the where-clause, + // so e.g., `trait Foo = where u32: PartialEq` would include `u32: PartialEq` + // as one of its "superpredicates". + let is_trait_alias = tcx.is_trait_alias(trait_def_id); + let superbounds2 = icx.type_parameter_bounds_in_generics( + generics, + item.hir_id, + self_param_ty, + OnlySelfBounds(!is_trait_alias), + assoc_name, + ); - // Now require that immediate supertraits are converted, - // which will, in turn, reach indirect supertraits. - for &(pred, span) in superbounds { - debug!("superbound: {:?}", pred); - if let ty::PredicateAtom::Trait(bound, _) = pred.skip_binders() { - tcx.at(span).super_predicates_of(bound.def_id()); + // Combine the two lists to form the complete set of superbounds: + let superbounds = &*tcx.arena.alloc_from_iter(superbounds1.into_iter().chain(superbounds2)); + + // Now require that immediate supertraits are converted, + // which will, in turn, reach indirect supertraits. + if assoc_name.is_none() { + // FIXME: move this into the `super_predicates_of` query + for &(pred, span) in superbounds { + debug!("superbound: {:?}", pred); + if let ty::PredicateAtom::Trait(bound, _) = pred.skip_binders() { + tcx.at(span).super_predicates_of(bound.def_id()); + } + } } - } - ty::GenericPredicates { parent: None, predicates: superbounds } + ty::GenericPredicates { parent: None, predicates: superbounds } + } else { + // if `assoc_name` is None, then the query should've been redirected to an + // external provider + assert!(assoc_name.is_some()); + tcx.super_predicates_of(trait_def_id) + } } fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> ty::TraitDef { diff --git a/compiler/rustc_typeck/src/collect/item_bounds.rs b/compiler/rustc_typeck/src/collect/item_bounds.rs index e596dd1a396c9..dd1da7d9cff2d 100644 --- a/compiler/rustc_typeck/src/collect/item_bounds.rs +++ b/compiler/rustc_typeck/src/collect/item_bounds.rs @@ -28,7 +28,7 @@ fn associated_type_bounds<'tcx>( let bounds = AstConv::compute_bounds( &ItemCtxt::new(tcx, assoc_item_def_id), item_ty, - bounds, + &bounds, SizedByDefault::Yes, span, ); @@ -68,7 +68,7 @@ fn opaque_type_bounds<'tcx>( let bounds = AstConv::compute_bounds( &ItemCtxt::new(tcx, opaque_def_id), item_ty, - bounds, + &bounds, SizedByDefault::Yes, span, ) diff --git a/config.toml.example b/config.toml.example index 9e08ce9b27e0c..55b20adabd045 100644 --- a/config.toml.example +++ b/config.toml.example @@ -35,9 +35,11 @@ changelog-seen = 2 # Unless you're developing for a target where Rust CI doesn't build a compiler # toolchain or changing LLVM locally, you probably want to set this to true. # -# It's currently false by default due to being newly added; please file bugs if -# enabling this did not work for you on x86_64-unknown-linux-gnu. -# Other target triples are currently not supported; see #77084. +# This is false by default so that distributions don't unexpectedly download +# LLVM from the internet. +# +# All tier 1 targets are currently supported; set this to `"if-supported"` if +# you are not sure whether you're on a tier 1 target. # # We also currently only support this when building LLVM for the build triple. # diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs index e7d66a9a6a83e..6f705414a9b9d 100644 --- a/library/alloc/src/collections/btree/node.rs +++ b/library/alloc/src/collections/btree/node.rs @@ -179,7 +179,7 @@ impl NodeRef { /// Removes the internal root node, using its first child as the new root node. /// As it is intended only to be called when the root node has only one child, - /// no cleanup is done on any of the other children. + /// no cleanup is done on any of the keys, values and other children. /// This decreases the height by 1 and is the opposite of `push_internal_level`. /// /// Requires exclusive access to the `Root` object but not to the root node; @@ -220,7 +220,7 @@ impl NodeRef { /// - When this is `Owned`, the `NodeRef` acts roughly like `Box`, /// but does not have a destructor, and must be cleaned up manually. /// Since any `NodeRef` allows navigating through the tree, `BorrowType` -/// effectively applies to the entire tree, not just the node itself. +/// effectively applies to the entire tree, not just to the node itself. /// - `K` and `V`: These are the types of keys and values stored in the nodes. /// - `Type`: This can be `Leaf`, `Internal`, or `LeafOrInternal`. When this is /// `Leaf`, the `NodeRef` points to a leaf node, when this is `Internal` the @@ -420,7 +420,7 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { impl NodeRef { /// Similar to `ascend`, gets a reference to a node's parent node, but also - /// deallocate the current node in the process. This is unsafe because the + /// deallocates the current node in the process. This is unsafe because the /// current node will still be accessible despite being deallocated. pub unsafe fn deallocate_and_ascend( self, @@ -656,7 +656,10 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { /// Removes a key-value pair from the end of the node and returns the pair. /// Also removes the edge that was to the right of that pair and, if the node /// is internal, returns the orphaned subtree that this edge owned. - fn pop(&mut self) -> (K, V, Option>) { + /// + /// # Safety + /// The node must not be empty. + unsafe fn pop(&mut self) -> (K, V, Option>) { debug_assert!(self.len() > 0); let idx = self.len() - 1; diff --git a/library/alloc/src/collections/btree/search.rs b/library/alloc/src/collections/btree/search.rs index ed7f95fe632fa..efe94ef175ce7 100644 --- a/library/alloc/src/collections/btree/search.rs +++ b/library/alloc/src/collections/btree/search.rs @@ -12,8 +12,7 @@ pub enum SearchResult { /// Looks up a given key in a (sub)tree headed by the given node, recursively. /// Returns a `Found` with the handle of the matching KV, if any. Otherwise, -/// returns a `GoDown` with the handle of the possible leaf edge where the key -/// belongs. +/// returns a `GoDown` with the handle of the leaf edge where the key belongs. /// /// The result is meaningful only if the tree is ordered by key, like the tree /// in a `BTreeMap` is. diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 6d2d7bbbef92c..5350c9eefe753 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -465,8 +465,21 @@ def download_stage0(self): def downloading_llvm(self): opt = self.get_toml('download-ci-llvm', 'llvm') + # This is currently all tier 1 targets (since others may not have CI + # artifacts) + # https://doc.rust-lang.org/rustc/platform-support.html#tier-1 + supported_platforms = [ + "aarch64-unknown-linux-gnu", + "i686-pc-windows-gnu", + "i686-pc-windows-msvc", + "i686-unknown-linux-gnu", + "x86_64-unknown-linux-gnu", + "x86_64-apple-darwin", + "x86_64-pc-windows-gnu", + "x86_64-pc-windows-msvc", + ] return opt == "true" \ - or (opt == "if-available" and self.build == "x86_64-unknown-linux-gnu") + or (opt == "if-available" and self.build in supported_platforms) def _download_stage0_helper(self, filename, pattern, tarball_suffix, date=None): if date is None: diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs index 72a979338a549..c19bb536ce83c 100644 --- a/src/bootstrap/check.rs +++ b/src/bootstrap/check.rs @@ -73,7 +73,7 @@ impl Step for Std { fn run(self, builder: &Builder<'_>) { let target = self.target; - let compiler = builder.compiler(0, builder.config.build); + let compiler = builder.compiler(builder.top_stage, builder.config.build); let mut cargo = builder.cargo( compiler, @@ -84,7 +84,10 @@ impl Step for Std { ); std_cargo(builder, target, compiler.stage, &mut cargo); - builder.info(&format!("Checking std artifacts ({} -> {})", &compiler.host, target)); + builder.info(&format!( + "Checking stage{} std artifacts ({} -> {})", + builder.top_stage, &compiler.host, target + )); run_cargo( builder, cargo, @@ -94,9 +97,13 @@ impl Step for Std { true, ); - let libdir = builder.sysroot_libdir(compiler, target); - let hostdir = builder.sysroot_libdir(compiler, compiler.host); - add_to_sysroot(&builder, &libdir, &hostdir, &libstd_stamp(builder, compiler, target)); + // We skip populating the sysroot in non-zero stage because that'll lead + // to rlib/rmeta conflicts if std gets built during this session. + if compiler.stage == 0 { + let libdir = builder.sysroot_libdir(compiler, target); + let hostdir = builder.sysroot_libdir(compiler, compiler.host); + add_to_sysroot(&builder, &libdir, &hostdir, &libstd_stamp(builder, compiler, target)); + } // Then run cargo again, once we've put the rmeta files for the library // crates into the sysroot. This is needed because e.g., core's tests @@ -124,8 +131,8 @@ impl Step for Std { } builder.info(&format!( - "Checking std test/bench/example targets ({} -> {})", - &compiler.host, target + "Checking stage{} std test/bench/example targets ({} -> {})", + builder.top_stage, &compiler.host, target )); run_cargo( builder, @@ -163,10 +170,20 @@ impl Step for Rustc { /// the `compiler` targeting the `target` architecture. The artifacts /// created will also be linked into the sysroot directory. fn run(self, builder: &Builder<'_>) { - let compiler = builder.compiler(0, builder.config.build); + let compiler = builder.compiler(builder.top_stage, builder.config.build); let target = self.target; - builder.ensure(Std { target }); + if compiler.stage != 0 { + // If we're not in stage 0, then we won't have a std from the beta + // compiler around. That means we need to make sure there's one in + // the sysroot for the compiler to find. Otherwise, we're going to + // fail when building crates that need to generate code (e.g., build + // scripts and their dependencies). + builder.ensure(crate::compile::Std { target: compiler.host, compiler }); + builder.ensure(crate::compile::Std { target, compiler }); + } else { + builder.ensure(Std { target }); + } let mut cargo = builder.cargo( compiler, @@ -187,7 +204,10 @@ impl Step for Rustc { cargo.arg("-p").arg(krate.name); } - builder.info(&format!("Checking compiler artifacts ({} -> {})", &compiler.host, target)); + builder.info(&format!( + "Checking stage{} compiler artifacts ({} -> {})", + builder.top_stage, &compiler.host, target + )); run_cargo( builder, cargo, @@ -225,7 +245,7 @@ impl Step for CodegenBackend { } fn run(self, builder: &Builder<'_>) { - let compiler = builder.compiler(0, builder.config.build); + let compiler = builder.compiler(builder.top_stage, builder.config.build); let target = self.target; let backend = self.backend; @@ -244,8 +264,8 @@ impl Step for CodegenBackend { rustc_cargo_env(builder, &mut cargo, target); builder.info(&format!( - "Checking {} artifacts ({} -> {})", - backend, &compiler.host.triple, target.triple + "Checking stage{} {} artifacts ({} -> {})", + builder.top_stage, backend, &compiler.host.triple, target.triple )); run_cargo( @@ -280,7 +300,7 @@ macro_rules! tool_check_step { } fn run(self, builder: &Builder<'_>) { - let compiler = builder.compiler(0, builder.config.build); + let compiler = builder.compiler(builder.top_stage, builder.config.build); let target = self.target; builder.ensure(Rustc { target }); @@ -301,7 +321,8 @@ macro_rules! tool_check_step { } builder.info(&format!( - "Checking {} artifacts ({} -> {})", + "Checking stage{} {} artifacts ({} -> {})", + builder.top_stage, stringify!($name).to_lowercase(), &compiler.host.triple, target.triple diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index f4d89a89c1467..ec1308ab82b51 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -377,6 +377,7 @@ struct Build { configure_args: Option>, local_rebuild: Option, print_step_timings: Option, + check_stage: Option, doc_stage: Option, build_stage: Option, test_stage: Option, @@ -676,6 +677,7 @@ impl Config { // See https://github.com/rust-lang/compiler-team/issues/326 config.stage = match config.cmd { + Subcommand::Check { .. } => flags.stage.or(build.check_stage).unwrap_or(0), Subcommand::Doc { .. } => flags.stage.or(build.doc_stage).unwrap_or(0), Subcommand::Build { .. } => flags.stage.or(build.build_stage).unwrap_or(1), Subcommand::Test { .. } => flags.stage.or(build.test_stage).unwrap_or(1), @@ -685,7 +687,6 @@ impl Config { // These are all bootstrap tools, which don't depend on the compiler. // The stage we pass shouldn't matter, but use 0 just in case. Subcommand::Clean { .. } - | Subcommand::Check { .. } | Subcommand::Clippy { .. } | Subcommand::Fix { .. } | Subcommand::Run { .. } @@ -816,8 +817,10 @@ impl Config { check_ci_llvm!(llvm.allow_old_toolchain); check_ci_llvm!(llvm.polly); - // CI-built LLVM is shared - config.llvm_link_shared = true; + // CI-built LLVM can be either dynamic or static. + let ci_llvm = config.out.join(&*config.build.triple).join("ci-llvm"); + let link_type = t!(std::fs::read_to_string(ci_llvm.join("link-type.txt"))); + config.llvm_link_shared = link_type == "dynamic"; } if config.llvm_thin_lto { diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index e2c2e19b0bc17..af9c0fb04bc9d 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -1800,19 +1800,11 @@ fn add_env(builder: &Builder<'_>, cmd: &mut Command, target: TargetSelection) { } } -/// Maybe add libLLVM.so to the given destination lib-dir. It will only have -/// been built if LLVM tools are linked dynamically. +/// Maybe add LLVM object files to the given destination lib-dir. Allows either static or dynamic linking. /// -/// Note: This function does not yet support Windows, but we also don't support -/// linking LLVM tools dynamically on Windows yet. -fn maybe_install_llvm(builder: &Builder<'_>, target: TargetSelection, dst_libdir: &Path) { - if !builder.config.llvm_link_shared { - // We do not need to copy LLVM files into the sysroot if it is not - // dynamically linked; it is already included into librustc_llvm - // statically. - return; - } +/// Returns whether the files were actually copied. +fn maybe_install_llvm(builder: &Builder<'_>, target: TargetSelection, dst_libdir: &Path) -> bool { if let Some(config) = builder.config.target_config.get(&target) { if config.llvm_config.is_some() && !builder.config.llvm_from_ci { // If the LLVM was externally provided, then we don't currently copy @@ -1828,7 +1820,7 @@ fn maybe_install_llvm(builder: &Builder<'_>, target: TargetSelection, dst_libdir // // If the LLVM is coming from ourselves (just from CI) though, we // still want to install it, as it otherwise won't be available. - return; + return false; } } @@ -1837,31 +1829,48 @@ fn maybe_install_llvm(builder: &Builder<'_>, target: TargetSelection, dst_libdir // clear why this is the case, though. llvm-config will emit the versioned // paths and we don't want those in the sysroot (as we're expecting // unversioned paths). - if target.contains("apple-darwin") { + if target.contains("apple-darwin") && builder.config.llvm_link_shared { let src_libdir = builder.llvm_out(target).join("lib"); let llvm_dylib_path = src_libdir.join("libLLVM.dylib"); if llvm_dylib_path.exists() { builder.install(&llvm_dylib_path, dst_libdir, 0o644); } + !builder.config.dry_run } else if let Ok(llvm_config) = crate::native::prebuilt_llvm_config(builder, target) { - let files = output(Command::new(llvm_config).arg("--libfiles")); - for file in files.lines() { + let mut cmd = Command::new(llvm_config); + cmd.arg("--libfiles"); + builder.verbose(&format!("running {:?}", cmd)); + let files = output(&mut cmd); + for file in files.trim_end().split(' ') { builder.install(Path::new(file), dst_libdir, 0o644); } + !builder.config.dry_run + } else { + false } } /// Maybe add libLLVM.so to the target lib-dir for linking. pub fn maybe_install_llvm_target(builder: &Builder<'_>, target: TargetSelection, sysroot: &Path) { let dst_libdir = sysroot.join("lib/rustlib").join(&*target.triple).join("lib"); - maybe_install_llvm(builder, target, &dst_libdir); + // We do not need to copy LLVM files into the sysroot if it is not + // dynamically linked; it is already included into librustc_llvm + // statically. + if builder.config.llvm_link_shared { + maybe_install_llvm(builder, target, &dst_libdir); + } } /// Maybe add libLLVM.so to the runtime lib-dir for rustc itself. pub fn maybe_install_llvm_runtime(builder: &Builder<'_>, target: TargetSelection, sysroot: &Path) { let dst_libdir = sysroot.join(builder.sysroot_libdir_relative(Compiler { stage: 1, host: target })); - maybe_install_llvm(builder, target, &dst_libdir); + // We do not need to copy LLVM files into the sysroot if it is not + // dynamically linked; it is already included into librustc_llvm + // statically. + if builder.config.llvm_link_shared { + maybe_install_llvm(builder, target, &dst_libdir); + } } #[derive(Clone, Debug, Eq, Hash, PartialEq)] @@ -1973,7 +1982,10 @@ impl Step for RustDev { // `$ORIGIN/../lib` can find it. It may also be used as a dependency // of `rustc-dev` to support the inherited `-lLLVM` when using the // compiler libraries. - maybe_install_llvm(builder, target, &tarball.image_dir().join("lib")); + let dst_libdir = tarball.image_dir().join("lib"); + maybe_install_llvm(builder, target, &dst_libdir); + let link_type = if builder.config.llvm_link_shared { "dynamic" } else { "static" }; + t!(std::fs::write(tarball.image_dir().join("link-type.txt"), link_type), dst_libdir); Some(tarball.generate()) } diff --git a/src/bootstrap/download-ci-llvm-stamp b/src/bootstrap/download-ci-llvm-stamp index b29ecd65401df..fb5b058cb4d74 100644 --- a/src/bootstrap/download-ci-llvm-stamp +++ b/src/bootstrap/download-ci-llvm-stamp @@ -1,4 +1,4 @@ Change this file to make users of the `download-ci-llvm` configuration download a new version of LLVM from CI, even if the LLVM submodule hasn’t changed. -Last change is for: https://github.com/rust-lang/rust/pull/80087 +Last change is for: https://github.com/rust-lang/rust/pull/80932 diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index d6a45f1c17076..55062e11e029a 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -614,14 +614,10 @@ Arguments: }; if let Subcommand::Check { .. } = &cmd { - if matches.opt_str("stage").is_some() { - println!("--stage not supported for x.py check, always treated as stage 0"); - process::exit(1); - } if matches.opt_str("keep-stage").is_some() || matches.opt_str("keep-stage-std").is_some() { - println!("--keep-stage not supported for x.py check, only one stage available"); + println!("--keep-stage not yet supported for x.py check"); process::exit(1); } } diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index 6412df3fd904e..609ac8b366952 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -171,7 +171,6 @@ impl Step for Llvm { .define("LLVM_TARGETS_TO_BUILD", llvm_targets) .define("LLVM_EXPERIMENTAL_TARGETS_TO_BUILD", llvm_exp_targets) .define("LLVM_INCLUDE_EXAMPLES", "OFF") - .define("LLVM_INCLUDE_TESTS", "OFF") .define("LLVM_INCLUDE_DOCS", "OFF") .define("LLVM_INCLUDE_BENCHMARKS", "OFF") .define("LLVM_ENABLE_TERMINFO", "OFF") diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 708d7710058d5..280984088a94e 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -19,7 +19,7 @@ use rustc_session::lint::{ builtin::{BROKEN_INTRA_DOC_LINKS, PRIVATE_INTRA_DOC_LINKS}, Lint, }; -use rustc_span::hygiene::MacroKind; +use rustc_span::hygiene::{MacroKind, SyntaxContext}; use rustc_span::symbol::Ident; use rustc_span::symbol::Symbol; use rustc_span::DUMMY_SP; @@ -770,7 +770,12 @@ fn traits_implemented_by(cx: &DocContext<'_>, type_: DefId, module: DefId) -> Fx let mut cache = cx.module_trait_cache.borrow_mut(); let in_scope_traits = cache.entry(module).or_insert_with(|| { cx.enter_resolver(|resolver| { - resolver.traits_in_scope(module).into_iter().map(|candidate| candidate.def_id).collect() + let parent_scope = &ParentScope::module(resolver.get_module(module), resolver); + resolver + .traits_in_scope(None, parent_scope, SyntaxContext::root(), None) + .into_iter() + .map(|candidate| candidate.def_id) + .collect() }) }); diff --git a/src/test/incremental/cyclic-trait-hierarchy.rs b/src/test/incremental/cyclic-trait-hierarchy.rs index 03bb5eea765cc..b502e7207d561 100644 --- a/src/test/incremental/cyclic-trait-hierarchy.rs +++ b/src/test/incremental/cyclic-trait-hierarchy.rs @@ -3,11 +3,11 @@ // revisions: rpass1 cfail2 #[cfg(rpass1)] -pub trait T2 { } +pub trait T2 {} #[cfg(cfail2)] -pub trait T2: T1 { } -//[cfail2]~^ ERROR cycle detected when computing the supertraits of `T2` +pub trait T2: T1 {} +//[cfail2]~^ ERROR cycle detected when computing the super predicates of `T2` -pub trait T1: T2 { } +pub trait T1: T2 {} -fn main() { } +fn main() {} diff --git a/src/test/ui/associated-type-bounds/ambiguous-associated-type2.rs b/src/test/ui/associated-type-bounds/ambiguous-associated-type2.rs new file mode 100644 index 0000000000000..1b6d6d0ff599f --- /dev/null +++ b/src/test/ui/associated-type-bounds/ambiguous-associated-type2.rs @@ -0,0 +1,12 @@ +// ignore-tidy-linelength + +trait Foo { + type Item; +} +trait Bar { + type Item; +} +trait Baz: Foo + Bar {} +//~^ ERROR cycle detected when computing the super traits of `Baz` with associated type name `Item` [E0391] + +fn main() {} diff --git a/src/test/ui/associated-type-bounds/ambiguous-associated-type2.stderr b/src/test/ui/associated-type-bounds/ambiguous-associated-type2.stderr new file mode 100644 index 0000000000000..bda1debeac0d6 --- /dev/null +++ b/src/test/ui/associated-type-bounds/ambiguous-associated-type2.stderr @@ -0,0 +1,16 @@ +error[E0391]: cycle detected when computing the super traits of `Baz` with associated type name `Item` + --> $DIR/ambiguous-associated-type2.rs:9:1 + | +LL | trait Baz: Foo + Bar {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: ...which again requires computing the super traits of `Baz` with associated type name `Item`, completing the cycle +note: cycle used when computing the super traits of `Baz` + --> $DIR/ambiguous-associated-type2.rs:9:1 + | +LL | trait Baz: Foo + Bar {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0391`. diff --git a/src/test/ui/associated-type-bounds/associated-item-through-where-clause.rs b/src/test/ui/associated-type-bounds/associated-item-through-where-clause.rs new file mode 100644 index 0000000000000..3eb50ab554735 --- /dev/null +++ b/src/test/ui/associated-type-bounds/associated-item-through-where-clause.rs @@ -0,0 +1,21 @@ +// check-pass + +trait Foo { + type Item; +} + +trait Bar +where + Self: Foo, +{ +} + +#[allow(dead_code)] +fn foo(_m: M) +where + M: Bar, + M::Item: Send, +{ +} + +fn main() {} diff --git a/src/test/ui/associated-type-bounds/handle-predicates-that-can-define-assoc-type.rs b/src/test/ui/associated-type-bounds/handle-predicates-that-can-define-assoc-type.rs new file mode 100644 index 0000000000000..b1e54ec04493b --- /dev/null +++ b/src/test/ui/associated-type-bounds/handle-predicates-that-can-define-assoc-type.rs @@ -0,0 +1,10 @@ +// check-pass + +trait Foo {} +trait Bar { + type A; + type B; +} +trait Baz: Bar + Foo {} + +fn main() {} diff --git a/src/test/ui/associated-type-bounds/missing-trait-bound-for-assoc-fails.rs b/src/test/ui/associated-type-bounds/missing-trait-bound-for-assoc-fails.rs new file mode 100644 index 0000000000000..07d0f8f8769e5 --- /dev/null +++ b/src/test/ui/associated-type-bounds/missing-trait-bound-for-assoc-fails.rs @@ -0,0 +1,10 @@ +#[allow(dead_code)] +fn foo(_m: M) +where + M::Item: Temp, + //~^ ERROR cannot find trait `Temp` in this scope [E0405] + //~| ERROR associated type `Item` not found for `M` [E0220] +{ +} + +fn main() {} diff --git a/src/test/ui/associated-type-bounds/missing-trait-bound-for-assoc-fails.stderr b/src/test/ui/associated-type-bounds/missing-trait-bound-for-assoc-fails.stderr new file mode 100644 index 0000000000000..bc2807b03961c --- /dev/null +++ b/src/test/ui/associated-type-bounds/missing-trait-bound-for-assoc-fails.stderr @@ -0,0 +1,16 @@ +error[E0405]: cannot find trait `Temp` in this scope + --> $DIR/missing-trait-bound-for-assoc-fails.rs:4:14 + | +LL | M::Item: Temp, + | ^^^^ not found in this scope + +error[E0220]: associated type `Item` not found for `M` + --> $DIR/missing-trait-bound-for-assoc-fails.rs:4:8 + | +LL | M::Item: Temp, + | ^^^^ associated type `Item` not found + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0220, E0405. +For more information about an error, try `rustc --explain E0220`. diff --git a/src/test/ui/associated-type-bounds/super-trait-referencing-self.rs b/src/test/ui/associated-type-bounds/super-trait-referencing-self.rs new file mode 100644 index 0000000000000..c82ec01f4d61d --- /dev/null +++ b/src/test/ui/associated-type-bounds/super-trait-referencing-self.rs @@ -0,0 +1,12 @@ +// check-pass +trait Foo { + type Bar; +} +trait Qux: Foo + AsRef {} +trait Foo2 {} + +trait Qux2: Foo2 + AsRef { + type Bar; +} + +fn main() {} diff --git a/src/test/ui/associated-type-bounds/super-trait-referencing.rs b/src/test/ui/associated-type-bounds/super-trait-referencing.rs new file mode 100644 index 0000000000000..2e97535157fd2 --- /dev/null +++ b/src/test/ui/associated-type-bounds/super-trait-referencing.rs @@ -0,0 +1,19 @@ +// check-pass + +// The goal of this test is to ensure that T: Bar +// in the where clause does not cycle + +trait Foo { + type Item; +} + +trait Bar {} + +fn baz() +where + T: Foo, + T: Bar, +{ +} + +fn main() {} diff --git a/src/test/ui/associated-type-bounds/super-trait-where-referencing-self.rs b/src/test/ui/associated-type-bounds/super-trait-where-referencing-self.rs new file mode 100644 index 0000000000000..72a6be9ffc388 --- /dev/null +++ b/src/test/ui/associated-type-bounds/super-trait-where-referencing-self.rs @@ -0,0 +1,27 @@ +// check-pass + +// Test that we do not get a cycle due to +// resolving `Self::Bar` in the where clauses +// on a trait definition (in particular, in +// a where clause that is defining a superpredicate). + +trait Foo { + type Bar; +} +trait Qux +where + Self: Foo, + Self: AsRef, +{ +} +trait Foo2 {} + +trait Qux2 +where + Self: Foo2, + Self: AsRef, +{ + type Bar; +} + +fn main() {} diff --git a/src/test/ui/associated-type-bounds/traits-assoc-type-macros.rs b/src/test/ui/associated-type-bounds/traits-assoc-type-macros.rs new file mode 100644 index 0000000000000..ad5c6aed97c2c --- /dev/null +++ b/src/test/ui/associated-type-bounds/traits-assoc-type-macros.rs @@ -0,0 +1,43 @@ +// check-pass +// compile-flags:-Cincremental=tmp/traits-assoc-type-macros + +// This test case makes sure that we can compile with incremental compilation +// enabled when there are macros, traits, inheritance and associated types involved. + +trait Deserializer { + type Error; +} + +trait Deserialize { + fn deserialize(_: D) -> D::Error + where + D: Deserializer; +} + +macro_rules! impl_deserialize { + ($name:ident) => { + impl Deserialize for $name { + fn deserialize(_: D) -> D::Error + where + D: Deserializer, + { + loop {} + } + } + }; +} + +macro_rules! formats { + { + $($name:ident,)* + } => { + $( + pub struct $name; + + impl_deserialize!($name); + )* + } +} +formats! { Foo, Bar, } + +fn main() {} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.rs new file mode 100644 index 0000000000000..2916d8c794f22 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.rs @@ -0,0 +1,31 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| `#[warn(incomplete_features)]` on by default +//~| see issue #53488 + +// Check that precise paths are being reported back in the error message. + + +enum MultiVariant { + Point(i32, i32), + Meta(i32) +} + +fn main() { + let mut point = MultiVariant::Point(10, -10,); + + let mut meta = MultiVariant::Meta(1); + + let c = || { + if let MultiVariant::Point(ref mut x, _) = point { + *x += 1; + } + + if let MultiVariant::Meta(ref mut v) = meta { + *v += 1; + } + }; + + let a = c; + let b = c; //~ ERROR use of moved value: `c` [E0382] +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.stderr new file mode 100644 index 0000000000000..de0bfe3bd769f --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.stderr @@ -0,0 +1,26 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/closure-origin-multi-variant-diagnostics.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error[E0382]: use of moved value: `c` + --> $DIR/closure-origin-multi-variant-diagnostics.rs:30:13 + | +LL | let a = c; + | - value moved here +LL | let b = c; + | ^ value used here after move + | +note: closure cannot be moved more than once as it is not `Copy` due to moving the variable `point.0` out of its environment + --> $DIR/closure-origin-multi-variant-diagnostics.rs:20:52 + | +LL | if let MultiVariant::Point(ref mut x, _) = point { + | ^^^^^ + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.rs new file mode 100644 index 0000000000000..8486f03f2eb8e --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.rs @@ -0,0 +1,26 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| `#[warn(incomplete_features)]` on by default +//~| see issue #53488 + +// Check that precise paths are being reported back in the error message. + +enum SingleVariant { + Point(i32, i32), +} + +fn main() { + let mut point = SingleVariant::Point(10, -10); + + let c = || { + // FIXME(project-rfc-2229#24): Change this to be a destructure pattern + // once this is fixed, to remove the warning. + if let SingleVariant::Point(ref mut x, _) = point { + //~^ WARNING: irrefutable if-let pattern + *x += 1; + } + }; + + let b = c; + let a = c; //~ ERROR use of moved value: `c` [E0382] +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.stderr new file mode 100644 index 0000000000000..ad66f6d7ffcaa --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.stderr @@ -0,0 +1,37 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/closure-origin-single-variant-diagnostics.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +warning: irrefutable if-let pattern + --> $DIR/closure-origin-single-variant-diagnostics.rs:18:9 + | +LL | / if let SingleVariant::Point(ref mut x, _) = point { +LL | | +LL | | *x += 1; +LL | | } + | |_________^ + | + = note: `#[warn(irrefutable_let_patterns)]` on by default + +error[E0382]: use of moved value: `c` + --> $DIR/closure-origin-single-variant-diagnostics.rs:25:13 + | +LL | let b = c; + | - value moved here +LL | let a = c; + | ^ value used here after move + | +note: closure cannot be moved more than once as it is not `Copy` due to moving the variable `point.0` out of its environment + --> $DIR/closure-origin-single-variant-diagnostics.rs:18:53 + | +LL | if let SingleVariant::Point(ref mut x, _) = point { + | ^^^^^ + +error: aborting due to previous error; 2 warnings emitted + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.rs new file mode 100644 index 0000000000000..103890f1f3537 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.rs @@ -0,0 +1,25 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| `#[warn(incomplete_features)]` on by default +//~| see issue #53488 + +// Check that precise paths are being reported back in the error message. + +struct Y { + y: X +} + +struct X { + a: u32, + b: u32, +} + +fn main() { + let mut x = Y { y: X { a: 5, b: 0 } }; + let hello = || { + x.y.a += 1; + }; + + let b = hello; + let c = hello; //~ ERROR use of moved value: `hello` [E0382] +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.stderr new file mode 100644 index 0000000000000..474d77b7cd208 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.stderr @@ -0,0 +1,26 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/closure-origin-struct-diagnostics.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error[E0382]: use of moved value: `hello` + --> $DIR/closure-origin-struct-diagnostics.rs:24:13 + | +LL | let b = hello; + | ----- value moved here +LL | let c = hello; + | ^^^^^ value used here after move + | +note: closure cannot be moved more than once as it is not `Copy` due to moving the variable `x.y.a` out of its environment + --> $DIR/closure-origin-struct-diagnostics.rs:20:9 + | +LL | x.y.a += 1; + | ^^^^^ + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.rs new file mode 100644 index 0000000000000..6b078d2329c2f --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.rs @@ -0,0 +1,16 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| `#[warn(incomplete_features)]` on by default +//~| see issue #53488 + +// Check that precise paths are being reported back in the error message. + +fn main() { + let mut x = (5, 0); + let hello = || { + x.0 += 1; + }; + + let b = hello; + let c = hello; //~ ERROR use of moved value: `hello` [E0382] +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.stderr new file mode 100644 index 0000000000000..716728e96ecbb --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.stderr @@ -0,0 +1,26 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/closure-origin-tuple-diagnostics-1.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error[E0382]: use of moved value: `hello` + --> $DIR/closure-origin-tuple-diagnostics-1.rs:15:13 + | +LL | let b = hello; + | ----- value moved here +LL | let c = hello; + | ^^^^^ value used here after move + | +note: closure cannot be moved more than once as it is not `Copy` due to moving the variable `x.0` out of its environment + --> $DIR/closure-origin-tuple-diagnostics-1.rs:11:9 + | +LL | x.0 += 1; + | ^^^ + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics.rs new file mode 100644 index 0000000000000..0638db607690f --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics.rs @@ -0,0 +1,15 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| `#[warn(incomplete_features)]` on by default +//~| see issue #53488 +struct S(String, String); + +fn expect_fn(_f: F) {} + +fn main() { + let s = S(format!("s"), format!("s")); + let c = || { //~ ERROR expected a closure that implements the `Fn` + let s = s.1; + }; + expect_fn(c); +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics.stderr new file mode 100644 index 0000000000000..77eb2a94ffb4d --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics.stderr @@ -0,0 +1,23 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/closure-origin-tuple-diagnostics.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce` + --> $DIR/closure-origin-tuple-diagnostics.rs:11:13 + | +LL | let c = || { + | ^^ this closure implements `FnOnce`, not `Fn` +LL | let s = s.1; + | --- closure is `FnOnce` because it moves the variable `s.1` out of its environment +LL | }; +LL | expect_fn(c); + | --------- the requirement to implement `Fn` derives from here + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0525`. diff --git a/src/test/ui/const-generics/late-bound-vars/in_closure.rs b/src/test/ui/const-generics/late-bound-vars/in_closure.rs new file mode 100644 index 0000000000000..0aaeaffb4cb9d --- /dev/null +++ b/src/test/ui/const-generics/late-bound-vars/in_closure.rs @@ -0,0 +1,18 @@ +// run-pass +#![feature(const_generics)] +#![allow(incomplete_features)] + +const fn inner<'a>() -> usize where &'a (): Sized { + 3 +} + +fn test<'a>() { + let _ = || { + let _: [u8; inner::<'a>()]; + let _ = [0; inner::<'a>()]; + }; +} + +fn main() { + test(); +} diff --git a/src/test/ui/const-generics/late-bound-vars/simple.rs b/src/test/ui/const-generics/late-bound-vars/simple.rs new file mode 100644 index 0000000000000..2c411a3bdc5f3 --- /dev/null +++ b/src/test/ui/const-generics/late-bound-vars/simple.rs @@ -0,0 +1,16 @@ +// run-pass +#![feature(const_generics)] +#![allow(incomplete_features)] + +const fn inner<'a>() -> usize where &'a (): Sized { + 3 +} + +fn test<'a>() { + let _: [u8; inner::<'a>()]; + let _ = [0; inner::<'a>()]; +} + +fn main() { + test(); +} diff --git a/src/test/ui/cycle-projection-based-on-where-clause.rs b/src/test/ui/cycle-projection-based-on-where-clause.rs deleted file mode 100644 index d3609acfdff63..0000000000000 --- a/src/test/ui/cycle-projection-based-on-where-clause.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Example cycle where a bound on `T` uses a shorthand for `T`. This -// creates a cycle because we have to know the bounds on `T` to figure -// out what trait defines `Item`, but we can't know the bounds on `T` -// without knowing how to handle `T::Item`. -// -// Note that in the future cases like this could perhaps become legal, -// if we got more fine-grained about our cycle detection or changed -// how we handle `T::Item` resolution. - -use std::ops::Add; - -// Preamble. -trait Trait { type Item; } - -struct A - where T : Trait, - T : Add - //~^ ERROR cycle detected -{ - data: T -} - -fn main() { -} diff --git a/src/test/ui/cycle-projection-based-on-where-clause.stderr b/src/test/ui/cycle-projection-based-on-where-clause.stderr deleted file mode 100644 index 2c337cc6bf903..0000000000000 --- a/src/test/ui/cycle-projection-based-on-where-clause.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error[E0391]: cycle detected when computing the bounds for type parameter `T` - --> $DIR/cycle-projection-based-on-where-clause.rs:17:19 - | -LL | T : Add - | ^^^^^^^ - | - = note: ...which again requires computing the bounds for type parameter `T`, completing the cycle -note: cycle used when computing explicit predicates of `A` - --> $DIR/cycle-projection-based-on-where-clause.rs:17:19 - | -LL | T : Add - | ^^^^^^^ - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0391`. diff --git a/src/test/ui/cycle-trait/cycle-trait-supertrait-direct.stderr b/src/test/ui/cycle-trait/cycle-trait-supertrait-direct.stderr index 8aa3ac8abf52c..ee54b2fd151d7 100644 --- a/src/test/ui/cycle-trait/cycle-trait-supertrait-direct.stderr +++ b/src/test/ui/cycle-trait/cycle-trait-supertrait-direct.stderr @@ -1,10 +1,15 @@ -error[E0391]: cycle detected when computing the supertraits of `Chromosome` +error[E0391]: cycle detected when computing the super predicates of `Chromosome` + --> $DIR/cycle-trait-supertrait-direct.rs:3:1 + | +LL | trait Chromosome: Chromosome { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: ...which requires computing the super traits of `Chromosome`... --> $DIR/cycle-trait-supertrait-direct.rs:3:19 | LL | trait Chromosome: Chromosome { | ^^^^^^^^^^ - | - = note: ...which again requires computing the supertraits of `Chromosome`, completing the cycle + = note: ...which again requires computing the super predicates of `Chromosome`, completing the cycle note: cycle used when collecting item types in top-level module --> $DIR/cycle-trait-supertrait-direct.rs:3:1 | diff --git a/src/test/ui/cycle-trait/cycle-trait-supertrait-indirect.stderr b/src/test/ui/cycle-trait/cycle-trait-supertrait-indirect.stderr index 9740f43a4ba97..0a2284e0efbca 100644 --- a/src/test/ui/cycle-trait/cycle-trait-supertrait-indirect.stderr +++ b/src/test/ui/cycle-trait/cycle-trait-supertrait-indirect.stderr @@ -1,16 +1,26 @@ -error[E0391]: cycle detected when computing the supertraits of `B` +error[E0391]: cycle detected when computing the super predicates of `B` + --> $DIR/cycle-trait-supertrait-indirect.rs:7:1 + | +LL | trait B: C { + | ^^^^^^^^^^ + | +note: ...which requires computing the super traits of `B`... --> $DIR/cycle-trait-supertrait-indirect.rs:7:10 | LL | trait B: C { | ^ +note: ...which requires computing the super predicates of `C`... + --> $DIR/cycle-trait-supertrait-indirect.rs:11:1 | -note: ...which requires computing the supertraits of `C`... +LL | trait C: B { } + | ^^^^^^^^^^ +note: ...which requires computing the super traits of `C`... --> $DIR/cycle-trait-supertrait-indirect.rs:11:10 | LL | trait C: B { } | ^ - = note: ...which again requires computing the supertraits of `B`, completing the cycle -note: cycle used when computing the supertraits of `A` + = note: ...which again requires computing the super predicates of `B`, completing the cycle +note: cycle used when computing the super traits of `A` --> $DIR/cycle-trait-supertrait-indirect.rs:4:10 | LL | trait A: B { diff --git a/src/test/ui/hygiene/traits-in-scope.rs b/src/test/ui/hygiene/traits-in-scope.rs new file mode 100644 index 0000000000000..548bb226b713c --- /dev/null +++ b/src/test/ui/hygiene/traits-in-scope.rs @@ -0,0 +1,53 @@ +// Macros with def-site hygiene still bring traits into scope. +// It is not clear whether this is desirable behavior or not. +// It is also not clear how to prevent it if it is not desirable. + +// check-pass + +#![feature(decl_macro)] +#![feature(trait_alias)] + +mod traits { + pub trait Trait1 { + fn simple_import(&self) {} + } + pub trait Trait2 { + fn renamed_import(&self) {} + } + pub trait Trait3 { + fn underscore_import(&self) {} + } + pub trait Trait4 { + fn trait_alias(&self) {} + } + + impl Trait1 for () {} + impl Trait2 for () {} + impl Trait3 for () {} + impl Trait4 for () {} +} + +macro m1() { + use traits::Trait1; +} +macro m2() { + use traits::Trait2 as Alias; +} +macro m3() { + use traits::Trait3 as _; +} +macro m4() { + trait Alias = traits::Trait4; +} + +fn main() { + m1!(); + m2!(); + m3!(); + m4!(); + + ().simple_import(); + ().renamed_import(); + ().underscore_import(); + ().trait_alias(); +} diff --git a/src/test/ui/issues/issue-12511.stderr b/src/test/ui/issues/issue-12511.stderr index 37e38ff60ae4b..5f2b98c5237db 100644 --- a/src/test/ui/issues/issue-12511.stderr +++ b/src/test/ui/issues/issue-12511.stderr @@ -1,15 +1,25 @@ -error[E0391]: cycle detected when computing the supertraits of `T1` +error[E0391]: cycle detected when computing the super predicates of `T1` + --> $DIR/issue-12511.rs:1:1 + | +LL | trait T1 : T2 { + | ^^^^^^^^^^^^^ + | +note: ...which requires computing the super traits of `T1`... --> $DIR/issue-12511.rs:1:12 | LL | trait T1 : T2 { | ^^ +note: ...which requires computing the super predicates of `T2`... + --> $DIR/issue-12511.rs:5:1 | -note: ...which requires computing the supertraits of `T2`... +LL | trait T2 : T1 { + | ^^^^^^^^^^^^^ +note: ...which requires computing the super traits of `T2`... --> $DIR/issue-12511.rs:5:12 | LL | trait T2 : T1 { | ^^ - = note: ...which again requires computing the supertraits of `T1`, completing the cycle + = note: ...which again requires computing the super predicates of `T1`, completing the cycle note: cycle used when collecting item types in top-level module --> $DIR/issue-12511.rs:1:1 | diff --git a/src/test/ui/issues/issue-20772.stderr b/src/test/ui/issues/issue-20772.stderr index d64636310a368..4aecc7eab4628 100644 --- a/src/test/ui/issues/issue-20772.stderr +++ b/src/test/ui/issues/issue-20772.stderr @@ -1,4 +1,4 @@ -error[E0391]: cycle detected when computing the supertraits of `T` +error[E0391]: cycle detected when computing the super traits of `T` with associated type name `Item` --> $DIR/issue-20772.rs:1:1 | LL | / trait T : Iterator @@ -6,8 +6,8 @@ LL | | LL | | {} | |__^ | - = note: ...which again requires computing the supertraits of `T`, completing the cycle -note: cycle used when collecting item types in top-level module + = note: ...which again requires computing the super traits of `T` with associated type name `Item`, completing the cycle +note: cycle used when computing the super traits of `T` --> $DIR/issue-20772.rs:1:1 | LL | / trait T : Iterator diff --git a/src/test/ui/issues/issue-20825.stderr b/src/test/ui/issues/issue-20825.stderr index 5f9709d1c649b..ccbe06d9c0d56 100644 --- a/src/test/ui/issues/issue-20825.stderr +++ b/src/test/ui/issues/issue-20825.stderr @@ -1,11 +1,11 @@ -error[E0391]: cycle detected when computing the supertraits of `Processor` +error[E0391]: cycle detected when computing the super traits of `Processor` with associated type name `Input` --> $DIR/issue-20825.rs:5:1 | LL | pub trait Processor: Subscriber { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: ...which again requires computing the supertraits of `Processor`, completing the cycle -note: cycle used when collecting item types in top-level module + = note: ...which again requires computing the super traits of `Processor` with associated type name `Input`, completing the cycle +note: cycle used when computing the super traits of `Processor` --> $DIR/issue-20825.rs:5:1 | LL | pub trait Processor: Subscriber { diff --git a/src/test/ui/issues/issue-22673.rs b/src/test/ui/issues/issue-22673.rs index ba8057b684de8..4b9b4d6b23da4 100644 --- a/src/test/ui/issues/issue-22673.rs +++ b/src/test/ui/issues/issue-22673.rs @@ -1,5 +1,6 @@ -trait Expr : PartialEq { - //~^ ERROR: cycle detected +// check-pass + +trait Expr: PartialEq { type Item; } diff --git a/src/test/ui/issues/issue-22673.stderr b/src/test/ui/issues/issue-22673.stderr deleted file mode 100644 index 9e7e4b218b1c6..0000000000000 --- a/src/test/ui/issues/issue-22673.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error[E0391]: cycle detected when computing the supertraits of `Expr` - --> $DIR/issue-22673.rs:1:1 - | -LL | trait Expr : PartialEq { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: ...which again requires computing the supertraits of `Expr`, completing the cycle -note: cycle used when collecting item types in top-level module - --> $DIR/issue-22673.rs:1:1 - | -LL | trait Expr : PartialEq { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0391`. diff --git a/src/test/ui/issues/issue-81006.rs b/src/test/ui/issues/issue-81006.rs new file mode 100644 index 0000000000000..602eb59742879 --- /dev/null +++ b/src/test/ui/issues/issue-81006.rs @@ -0,0 +1,10 @@ +// check-fail + +// First format below would cause a panic, second would generate error with incorrect span + +fn main() { + let _ = format!("→{}→\n"); + //~^ ERROR 1 positional argument in format string, but no arguments were given + let _ = format!("→{} \n"); + //~^ ERROR 1 positional argument in format string, but no arguments were given +} diff --git a/src/test/ui/issues/issue-81006.stderr b/src/test/ui/issues/issue-81006.stderr new file mode 100644 index 0000000000000..14a8cbe0155f9 --- /dev/null +++ b/src/test/ui/issues/issue-81006.stderr @@ -0,0 +1,14 @@ +error: 1 positional argument in format string, but no arguments were given + --> $DIR/issue-81006.rs:6:23 + | +LL | let _ = format!("→{}→\n"); + | ^^ + +error: 1 positional argument in format string, but no arguments were given + --> $DIR/issue-81006.rs:8:23 + | +LL | let _ = format!("→{} \n"); + | ^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/proc-macro/ambiguous-builtin-attrs.rs b/src/test/ui/proc-macro/ambiguous-builtin-attrs.rs index 142efb3c6cdcf..5f45f6892d2e3 100644 --- a/src/test/ui/proc-macro/ambiguous-builtin-attrs.rs +++ b/src/test/ui/proc-macro/ambiguous-builtin-attrs.rs @@ -1,5 +1,5 @@ +// edition:2018 // aux-build:builtin-attrs.rs - #![feature(decl_macro)] //~ ERROR `feature` is ambiguous extern crate builtin_attrs; @@ -31,3 +31,7 @@ fn main() { Bench; NonExistent; //~ ERROR cannot find value `NonExistent` in this scope } + +use deny as allow; +#[allow(unused)] //~ ERROR `allow` is ambiguous (built-in attribute vs any other name) +fn builtin_renamed() {} diff --git a/src/test/ui/proc-macro/ambiguous-builtin-attrs.stderr b/src/test/ui/proc-macro/ambiguous-builtin-attrs.stderr index 276ee1cfd3562..dfd60dc92cc35 100644 --- a/src/test/ui/proc-macro/ambiguous-builtin-attrs.stderr +++ b/src/test/ui/proc-macro/ambiguous-builtin-attrs.stderr @@ -60,6 +60,20 @@ LL | use builtin_attrs::*; | ^^^^^^^^^^^^^^^^ = help: use `crate::repr` to refer to this attribute macro unambiguously +error[E0659]: `allow` is ambiguous (built-in attribute vs any other name) + --> $DIR/ambiguous-builtin-attrs.rs:36:3 + | +LL | #[allow(unused)] + | ^^^^^ ambiguous name + | + = note: `allow` could refer to a built-in attribute +note: `allow` could also refer to the built-in attribute imported here + --> $DIR/ambiguous-builtin-attrs.rs:35:5 + | +LL | use deny as allow; + | ^^^^^^^^^^^^^ + = help: use `crate::allow` to refer to this built-in attribute unambiguously + error[E0659]: `feature` is ambiguous (built-in attribute vs any other name) --> $DIR/ambiguous-builtin-attrs.rs:3:4 | @@ -80,7 +94,7 @@ error[E0517]: attribute should be applied to a struct, enum, or union LL | fn non_macro_expanded_location<#[repr(C)] T>() { | ^ - not a struct, enum, or union -error: aborting due to 7 previous errors +error: aborting due to 8 previous errors Some errors have detailed explanations: E0425, E0517, E0659. For more information about an error, try `rustc --explain E0425`. diff --git a/src/test/ui/underscore-imports/hygiene.rs b/src/test/ui/underscore-imports/hygiene.rs index a254f6eaa5980..c4db65245386f 100644 --- a/src/test/ui/underscore-imports/hygiene.rs +++ b/src/test/ui/underscore-imports/hygiene.rs @@ -1,5 +1,6 @@ -// Make sure that underscore imports have the same hygiene considerations as -// other imports. +// Make sure that underscore imports have the same hygiene considerations as other imports. + +// check-pass #![feature(decl_macro)] @@ -7,7 +8,6 @@ mod x { pub use std::ops::Deref as _; } - macro glob_import() { pub use crate::x::*; } @@ -35,6 +35,6 @@ fn main() { use crate::z::*; glob_import!(); underscore_import!(); - (&()).deref(); //~ ERROR no method named `deref` - (&mut ()).deref_mut(); //~ ERROR no method named `deref_mut` + (&()).deref(); + (&mut ()).deref_mut(); } diff --git a/src/test/ui/underscore-imports/hygiene.stderr b/src/test/ui/underscore-imports/hygiene.stderr deleted file mode 100644 index 2983613786038..0000000000000 --- a/src/test/ui/underscore-imports/hygiene.stderr +++ /dev/null @@ -1,27 +0,0 @@ -error[E0599]: no method named `deref` found for reference `&()` in the current scope - --> $DIR/hygiene.rs:38:11 - | -LL | (&()).deref(); - | ^^^^^ method not found in `&()` - | - = help: items from traits can only be used if the trait is in scope -help: the following trait is implemented but not in scope; perhaps add a `use` for it: - | -LL | use std::ops::Deref; - | - -error[E0599]: no method named `deref_mut` found for mutable reference `&mut ()` in the current scope - --> $DIR/hygiene.rs:39:15 - | -LL | (&mut ()).deref_mut(); - | ^^^^^^^^^ method not found in `&mut ()` - | - = help: items from traits can only be used if the trait is in scope -help: the following trait is implemented but not in scope; perhaps add a `use` for it: - | -LL | use std::ops::DerefMut; - | - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0599`. diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index de8da99cdee12..64864c2e2780d 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6,11 +6,138 @@ document. ## Unreleased / In Rust Nightly -[b20d4c1...master](https://github.com/rust-lang/rust-clippy/compare/b20d4c1...master) +[4911ab1...master](https://github.com/rust-lang/rust-clippy/compare/4911ab1...master) + +## Rust 1.50 + +Current beta, release 2021-02-11 + +[b20d4c1...4911ab1](https://github.com/rust-lang/rust-clippy/compare/b20d4c1...4911ab1) + +### New Lints + +* [`suspicious_operation_groupings`] [#6086](https://github.com/rust-lang/rust-clippy/pull/6086) +* [`size_of_in_element_count`] [#6394](https://github.com/rust-lang/rust-clippy/pull/6394) +* [`unnecessary_wraps`] [#6070](https://github.com/rust-lang/rust-clippy/pull/6070) +* [`let_underscore_drop`] [#6305](https://github.com/rust-lang/rust-clippy/pull/6305) +* [`collapsible_match`] [#6402](https://github.com/rust-lang/rust-clippy/pull/6402) +* [`redundant_else`] [#6330](https://github.com/rust-lang/rust-clippy/pull/6330) +* [`zero_sized_map_values`] [#6218](https://github.com/rust-lang/rust-clippy/pull/6218) +* [`print_stderr`] [#6367](https://github.com/rust-lang/rust-clippy/pull/6367) +* [`string_from_utf8_as_bytes`] [#6134](https://github.com/rust-lang/rust-clippy/pull/6134) + +### Moves and Deprecations + +* Previously deprecated [`str_to_string`] and [`string_to_string`] have been un-deprecated + as `restriction` lints [#6333](https://github.com/rust-lang/rust-clippy/pull/6333) +* Deprecate [`panic_params`] lint. This is now available in rustc as `panic_fmt` + [#6351](https://github.com/rust-lang/rust-clippy/pull/6351) +* Move [`map_err_ignore`] to `restriction` + [#6416](https://github.com/rust-lang/rust-clippy/pull/6416) +* Move [`await_holding_refcell_ref`] to `pedantic` + [#6354](https://github.com/rust-lang/rust-clippy/pull/6354) +* Move [`await_holding_lock`] to `pedantic` + [#6354](https://github.com/rust-lang/rust-clippy/pull/6354) + +### Enhancements + +* Add the `unreadable-literal-lint-fractions` configuration to disable + the `unreadable_literal` lint for fractions + [#6421](https://github.com/rust-lang/rust-clippy/pull/6421) +* [`clone_on_copy`]: Now shows the type in the lint message + [#6443](https://github.com/rust-lang/rust-clippy/pull/6443) +* [`redundant_pattern_matching`]: Now also lints on `std::task::Poll` + [#6339](https://github.com/rust-lang/rust-clippy/pull/6339) +* [`redundant_pattern_matching`]: Additionally also lints on `std::net::IpAddr` + [#6377](https://github.com/rust-lang/rust-clippy/pull/6377) +* [`search_is_some`]: Now suggests `contains` instead of `find(foo).is_some()` + [#6119](https://github.com/rust-lang/rust-clippy/pull/6119) +* [`clone_double_ref`]: Now prints the reference type in the lint message + [#6442](https://github.com/rust-lang/rust-clippy/pull/6442) +* [`modulo_one`]: Now also lints on -1. + [#6360](https://github.com/rust-lang/rust-clippy/pull/6360) +* [`empty_loop`]: Now lints no_std crates, too + [#6205](https://github.com/rust-lang/rust-clippy/pull/6205) +* [`or_fun_call`]: Now also lints when indexing `HashMap` or `BTreeMap` + [#6267](https://github.com/rust-lang/rust-clippy/pull/6267) +* [`wrong_self_convention`]: Now also lints in trait definitions + [#6316](https://github.com/rust-lang/rust-clippy/pull/6316) +* [`needless_borrow`]: Print the type in the lint message + [#6449](https://github.com/rust-lang/rust-clippy/pull/6449) + +[msrv_readme]: https://github.com/rust-lang/rust-clippy#specifying-the-minimum-supported-rust-version + +### False Positive Fixes + +* [`manual_range_contains`]: No longer lints in `const fn` + [#6382](https://github.com/rust-lang/rust-clippy/pull/6382) +* [`unnecessary_lazy_evaluations`]: No longer lints if closure argument is used + [#6370](https://github.com/rust-lang/rust-clippy/pull/6370) +* [`match_single_binding`]: Now ignores cases with `#[cfg()]` macros + [#6435](https://github.com/rust-lang/rust-clippy/pull/6435) +* [`match_like_matches_macro`]: No longer lints on arms with attributes + [#6290](https://github.com/rust-lang/rust-clippy/pull/6290) +* [`map_clone`]: No longer lints with deref and clone + [#6269](https://github.com/rust-lang/rust-clippy/pull/6269) +* [`map_clone`]: No longer lints in the case of &mut + [#6301](https://github.com/rust-lang/rust-clippy/pull/6301) +* [`needless_update`]: Now ignores `non_exhaustive` structs + [#6464](https://github.com/rust-lang/rust-clippy/pull/6464) +* [`needless_collect`]: No longer lints when a collect is needed multiple times + [#6313](https://github.com/rust-lang/rust-clippy/pull/6313) +* [`unnecessary_cast`] No longer lints cfg-dependent types + [#6369](https://github.com/rust-lang/rust-clippy/pull/6369) +* [`declare_interior_mutable_const`] and [`borrow_interior_mutable_const`]: + Both now ignore enums with frozen variants + [#6110](https://github.com/rust-lang/rust-clippy/pull/6110) + + +### Suggestion Fixes/Improvements + +* [`vec_box`]: Provide correct type scope suggestion + [#6271](https://github.com/rust-lang/rust-clippy/pull/6271) +* [`manual_range_contains`]: Give correct suggestion when using floats + [#6320](https://github.com/rust-lang/rust-clippy/pull/6320) +* [`unnecessary_lazy_evaluations`]: Don't always mark suggestion as MachineApplicable + [#6272](https://github.com/rust-lang/rust-clippy/pull/6272) +* [`manual_async_fn`]: Improve suggestion formatting + [#6294](https://github.com/rust-lang/rust-clippy/pull/6294) +* [`unnecessary_cast`]: Fix incorrectly formatted float literal suggestion + [#6362](https://github.com/rust-lang/rust-clippy/pull/6362) + +### ICE Fixes + +* Fix a crash in [`from_iter_instead_of_collect`] + [#6304](https://github.com/rust-lang/rust-clippy/pull/6304) +* Fix a silent crash when parsing doc comments in [`needless_doctest_main`] + [#6458](https://github.com/rust-lang/rust-clippy/pull/6458) + +### Documentation Improvements + +* The lint website search has been improved ([#6477](https://github.com/rust-lang/rust-clippy/pull/6477)): + * Searching for lints with dashes and spaces is possible now. For example + `missing-errors-doc` and `missing errors doc` are now valid aliases for lint names + * Improved fuzzy search in lint descriptions +* Various README improvements + [#6287](https://github.com/rust-lang/rust-clippy/pull/6287) +* Add known problems to [`comparison_chain`] documentation + [#6390](https://github.com/rust-lang/rust-clippy/pull/6390) +* Fix example used in [`cargo_common_metadata`] + [#6293](https://github.com/rust-lang/rust-clippy/pull/6293) +* Improve [`map_clone`] documentation + [#6340](https://github.com/rust-lang/rust-clippy/pull/6340) + +### Others + +* You can now tell Clippy about the MSRV your project supports. Please refer to + the specific README section to learn more about MSRV support [here][msrv_readme] + [#6201](https://github.com/rust-lang/rust-clippy/pull/6201) +* Add `--no-deps` option to avoid running on path dependencies in workspaces + [#6188](https://github.com/rust-lang/rust-clippy/pull/6188) ## Rust 1.49 -Current beta, release 2020-12-31 +Current stable, released 2020-12-31 [e636b88...b20d4c1](https://github.com/rust-lang/rust-clippy/compare/e636b88...b20d4c1) @@ -116,7 +243,7 @@ Current beta, release 2020-12-31 ## Rust 1.48 -Current stable, released 2020-11-19 +Released 2020-11-19 [09bd400...e636b88](https://github.com/rust-lang/rust-clippy/compare/09bd400...e636b88) @@ -1769,6 +1896,7 @@ Released 2018-09-13 [`cmp_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_null [`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned [`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity +[`collapsible_else_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_else_if [`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if [`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain @@ -1973,6 +2101,7 @@ Released 2018-09-13 [`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main [`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes [`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value +[`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark [`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop [`needless_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return [`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update @@ -2012,6 +2141,7 @@ Released 2018-09-13 [`print_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_with_newline [`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string [`ptr_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg +[`ptr_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr [`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq [`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast [`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names @@ -2152,6 +2282,7 @@ Released 2018-09-13 [`useless_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_transmute [`useless_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec [`vec_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_box +[`vec_init_then_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_init_then_push [`vec_resize_to_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_resize_to_zero [`verbose_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask [`verbose_file_reads`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_file_reads diff --git a/src/tools/clippy/clippy_dev/src/bless.rs b/src/tools/clippy/clippy_dev/src/bless.rs index 645098e4cfcd2..b877806946cfe 100644 --- a/src/tools/clippy/clippy_dev/src/bless.rs +++ b/src/tools/clippy/clippy_dev/src/bless.rs @@ -5,7 +5,7 @@ use std::env; use std::ffi::OsStr; use std::fs; use std::lazy::SyncLazy; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use walkdir::WalkDir; use crate::clippy_project_root; @@ -16,27 +16,41 @@ pub static CARGO_TARGET_DIR: SyncLazy = SyncLazy::new(|| match env::var None => env::current_dir().unwrap().join("target"), }); -pub fn bless() { - let test_dirs = [ +static CLIPPY_BUILD_TIME: SyncLazy> = SyncLazy::new(|| { + let profile = env::var("PROFILE").unwrap_or_else(|_| "debug".to_string()); + let mut path = PathBuf::from(&**CARGO_TARGET_DIR); + path.push(profile); + path.push("cargo-clippy"); + fs::metadata(path).ok()?.modified().ok() +}); + +pub fn bless(ignore_timestamp: bool) { + let test_suite_dirs = [ clippy_project_root().join("tests").join("ui"), + clippy_project_root().join("tests").join("ui-internal"), clippy_project_root().join("tests").join("ui-toml"), clippy_project_root().join("tests").join("ui-cargo"), ]; - for test_dir in &test_dirs { - WalkDir::new(test_dir) + for test_suite_dir in &test_suite_dirs { + WalkDir::new(test_suite_dir) .into_iter() .filter_map(Result::ok) .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) .for_each(|f| { - update_reference_file(f.path().with_extension("stdout")); - update_reference_file(f.path().with_extension("stderr")); - update_reference_file(f.path().with_extension("fixed")); + let test_name = f.path().strip_prefix(test_suite_dir).unwrap(); + for &ext in &["stdout", "stderr", "fixed"] { + update_reference_file( + f.path().with_extension(ext), + test_name.with_extension(ext), + ignore_timestamp, + ); + } }); } } -fn update_reference_file(reference_file_path: PathBuf) { - let test_output_path = build_dir().join(PathBuf::from(reference_file_path.file_name().unwrap())); +fn update_reference_file(reference_file_path: PathBuf, test_name: PathBuf, ignore_timestamp: bool) { + let test_output_path = build_dir().join(test_name); let relative_reference_file_path = reference_file_path.strip_prefix(clippy_project_root()).unwrap(); // If compiletest did not write any changes during the test run, @@ -45,6 +59,11 @@ fn update_reference_file(reference_file_path: PathBuf) { return; } + // If the test output was not updated since the last clippy build, it may be outdated + if !ignore_timestamp && !updated_since_clippy_build(&test_output_path).unwrap_or(true) { + return; + } + let test_output_file = fs::read(&test_output_path).expect("Unable to read test output file"); let reference_file = fs::read(&reference_file_path).unwrap_or_default(); @@ -64,6 +83,12 @@ fn update_reference_file(reference_file_path: PathBuf) { } } +fn updated_since_clippy_build(path: &Path) -> Option { + let clippy_build_time = (*CLIPPY_BUILD_TIME)?; + let modified = fs::metadata(path).ok()?.modified().ok()?; + Some(modified >= clippy_build_time) +} + fn build_dir() -> PathBuf { let profile = env::var("PROFILE").unwrap_or_else(|_| "debug".to_string()); let mut path = PathBuf::new(); diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs index 4fdae38e3ab7a..2ea56c42fafd6 100644 --- a/src/tools/clippy/clippy_dev/src/main.rs +++ b/src/tools/clippy/clippy_dev/src/main.rs @@ -7,8 +7,8 @@ fn main() { let matches = get_clap_config(); match matches.subcommand() { - ("bless", Some(_)) => { - bless::bless(); + ("bless", Some(matches)) => { + bless::bless(matches.is_present("ignore-timestamp")); }, ("fmt", Some(matches)) => { fmt::run(matches.is_present("check"), matches.is_present("verbose")); @@ -47,7 +47,15 @@ fn main() { fn get_clap_config<'a>() -> ArgMatches<'a> { App::new("Clippy developer tooling") - .subcommand(SubCommand::with_name("bless").about("bless the test output changes")) + .subcommand( + SubCommand::with_name("bless") + .about("bless the test output changes") + .arg( + Arg::with_name("ignore-timestamp") + .long("ignore-timestamp") + .help("Include files updated before clippy was built"), + ), + ) .subcommand( SubCommand::with_name("fmt") .about("Run rustfmt on all projects and tests") diff --git a/src/tools/clippy/clippy_lints/src/attrs.rs b/src/tools/clippy/clippy_lints/src/attrs.rs index 3edbe723922f8..9a00fc535fc5c 100644 --- a/src/tools/clippy/clippy_lints/src/attrs.rs +++ b/src/tools/clippy/clippy_lints/src/attrs.rs @@ -399,7 +399,7 @@ fn extract_clippy_lint(lint: &NestedMetaItem) -> Option { if let Some(meta_item) = lint.meta_item(); if meta_item.path.segments.len() > 1; if let tool_name = meta_item.path.segments[0].ident; - if tool_name.as_str() == "clippy"; + if tool_name.name == sym::clippy; let lint_name = meta_item.path.segments.last().unwrap().ident.name; then { return Some(lint_name.as_str()); diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs index 42bff564de03d..93ccc76d0c9cd 100644 --- a/src/tools/clippy/clippy_lints/src/collapsible_if.rs +++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs @@ -23,9 +23,7 @@ use rustc_errors::Applicability; declare_clippy_lint! { /// **What it does:** Checks for nested `if` statements which can be collapsed - /// by `&&`-combining their conditions and for `else { if ... }` expressions - /// that - /// can be collapsed to `else if ...`. + /// by `&&`-combining their conditions. /// /// **Why is this bad?** Each `if`-statement adds one level of nesting, which /// makes code look more complex than it really is. @@ -40,7 +38,31 @@ declare_clippy_lint! { /// } /// } /// - /// // or + /// ``` + /// + /// Should be written: + /// + /// ```rust.ignore + /// if x && y { + /// … + /// } + /// ``` + pub COLLAPSIBLE_IF, + style, + "nested `if`s that can be collapsed (e.g., `if x { if y { ... } }`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for collapsible `else { if ... }` expressions + /// that can be collapsed to `else if ...`. + /// + /// **Why is this bad?** Each `if`-statement adds one level of nesting, which + /// makes code look more complex than it really is. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore /// /// if x { /// … @@ -54,24 +76,18 @@ declare_clippy_lint! { /// Should be written: /// /// ```rust.ignore - /// if x && y { - /// … - /// } - /// - /// // or - /// /// if x { /// … /// } else if y { /// … /// } /// ``` - pub COLLAPSIBLE_IF, + pub COLLAPSIBLE_ELSE_IF, style, - "`if`s that can be collapsed (e.g., `if x { if y { ... } }` and `else { if x { ... } }`)" + "nested `else`-`if` expressions that can be collapsed (e.g., `else { if x { ... } }`)" } -declare_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF]); +declare_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]); impl EarlyLintPass for CollapsibleIf { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { @@ -112,7 +128,7 @@ fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, - COLLAPSIBLE_IF, + COLLAPSIBLE_ELSE_IF, block.span, "this `else { if .. }` block can be collapsed", "collapse nested if block", diff --git a/src/tools/clippy/clippy_lints/src/comparison_chain.rs b/src/tools/clippy/clippy_lints/src/comparison_chain.rs index ae1143b2c50ce..90d31dece1311 100644 --- a/src/tools/clippy/clippy_lints/src/comparison_chain.rs +++ b/src/tools/clippy/clippy_lints/src/comparison_chain.rs @@ -13,7 +13,7 @@ declare_clippy_lint! { /// repetitive /// /// **Known problems:** The match statement may be slower due to the compiler - /// not inlining the call to cmp. See issue #5354 + /// not inlining the call to cmp. See issue [#5354](https://github.com/rust-lang/rust-clippy/issues/5354) /// /// **Example:** /// ```rust,ignore diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs index 6f48ffeb0e9c9..944aaafb46de5 100644 --- a/src/tools/clippy/clippy_lints/src/copies.rs +++ b/src/tools/clippy/clippy_lints/src/copies.rs @@ -112,7 +112,8 @@ impl<'tcx> LateLintPass<'tcx> for CopyAndPaste { if let Some(&Expr { kind: ExprKind::If(_, _, Some(ref else_expr)), .. - }) = get_parent_expr(cx, expr) { + }) = get_parent_expr(cx, expr) + { if else_expr.hir_id == expr.hir_id { return; } diff --git a/src/tools/clippy/clippy_lints/src/copy_iterator.rs b/src/tools/clippy/clippy_lints/src/copy_iterator.rs index a7aa2cb35c1c1..48899b3389937 100644 --- a/src/tools/clippy/clippy_lints/src/copy_iterator.rs +++ b/src/tools/clippy/clippy_lints/src/copy_iterator.rs @@ -1,5 +1,5 @@ use crate::utils::{is_copy, match_path, paths, span_lint_and_note}; -use rustc_hir::{Item, ItemKind, Impl}; +use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs index b0d7c7b3baab1..f7224811e6e79 100644 --- a/src/tools/clippy/clippy_lints/src/default.rs +++ b/src/tools/clippy/clippy_lints/src/default.rs @@ -1,4 +1,6 @@ -use crate::utils::{any_parent_is_automatically_derived, contains_name, match_def_path, paths, qpath_res, snippet}; +use crate::utils::{ + any_parent_is_automatically_derived, contains_name, match_def_path, paths, qpath_res, snippet_with_macro_callsite, +}; use crate::utils::{span_lint_and_note, span_lint_and_sugg}; use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; @@ -6,6 +8,7 @@ use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::{Ident, Symbol}; @@ -118,6 +121,8 @@ impl LateLintPass<'_> for Default { // only take `let ...` statements if let StmtKind::Local(local) = stmt.kind; if let Some(expr) = local.init; + if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id); + if !in_external_macro(cx.tcx.sess, expr.span); // only take bindings to identifiers if let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind; // only when assigning `... = Default::default()` @@ -187,7 +192,7 @@ impl LateLintPass<'_> for Default { .into_iter() .map(|(field, rhs)| { // extract and store the assigned value for help message - let value_snippet = snippet(cx, rhs.span, ".."); + let value_snippet = snippet_with_macro_callsite(cx, rhs.span, ".."); format!("{}: {}", field, value_snippet) }) .collect::>() diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs index b55f59f021dff..b1e363663bb27 100644 --- a/src/tools/clippy/clippy_lints/src/derive.rs +++ b/src/tools/clippy/clippy_lints/src/derive.rs @@ -7,7 +7,7 @@ use if_chain::if_chain; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::{ - BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Item, ItemKind, Impl, TraitRef, UnsafeSource, Unsafety, + BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, TraitRef, UnsafeSource, Unsafety, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; diff --git a/src/tools/clippy/clippy_lints/src/empty_enum.rs b/src/tools/clippy/clippy_lints/src/empty_enum.rs index a249117d182fa..853b3afdc3ae2 100644 --- a/src/tools/clippy/clippy_lints/src/empty_enum.rs +++ b/src/tools/clippy/clippy_lints/src/empty_enum.rs @@ -8,8 +8,12 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** Checks for `enum`s with no variants. /// + /// As of this writing, the `never_type` is still a + /// nightly-only experimental API. Therefore, this lint is only triggered + /// if the `never_type` is enabled. + /// /// **Why is this bad?** If you want to introduce a type which - /// can't be instantiated, you should use `!` (the never type), + /// can't be instantiated, you should use `!` (the primitive type "never"), /// or a wrapper around it, because `!` has more extensive /// compiler support (type inference, etc...) and wrappers /// around it are the conventional way to define an uninhabited type. @@ -40,6 +44,11 @@ declare_lint_pass!(EmptyEnum => [EMPTY_ENUM]); impl<'tcx> LateLintPass<'tcx> for EmptyEnum { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + // Only suggest the `never_type` if the feature is enabled + if !cx.tcx.features().never_type { + return; + } + let did = cx.tcx.hir().local_def_id(item.hir_id); if let ItemKind::Enum(..) = item.kind { let ty = cx.tcx.type_of(did); diff --git a/src/tools/clippy/clippy_lints/src/escape.rs b/src/tools/clippy/clippy_lints/src/escape.rs index 550876978129e..40e93da8dffb4 100644 --- a/src/tools/clippy/clippy_lints/src/escape.rs +++ b/src/tools/clippy/clippy_lints/src/escape.rs @@ -1,15 +1,16 @@ use rustc_hir::intravisit; -use rustc_hir::{self, Body, FnDecl, HirId, HirIdSet, ItemKind, Impl, Node}; +use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, TraitRef, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; +use rustc_span::symbol::kw; use rustc_target::abi::LayoutOf; use rustc_target::spec::abi::Abi; use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; -use crate::utils::span_lint; +use crate::utils::{contains_ty, span_lint}; #[derive(Copy, Clone)] pub struct BoxedLocal { @@ -51,6 +52,7 @@ fn is_non_trait_box(ty: Ty<'_>) -> bool { struct EscapeDelegate<'a, 'tcx> { cx: &'a LateContext<'tcx>, set: HirIdSet, + trait_self_ty: Option>, too_large_for_stack: u64, } @@ -72,19 +74,34 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { } } - // If the method is an impl for a trait, don't warn. let parent_id = cx.tcx.hir().get_parent_item(hir_id); let parent_node = cx.tcx.hir().find(parent_id); + let mut trait_self_ty = None; if let Some(Node::Item(item)) = parent_node { + // If the method is an impl for a trait, don't warn. if let ItemKind::Impl(Impl { of_trait: Some(_), .. }) = item.kind { return; } + + // find `self` ty for this trait if relevant + if let ItemKind::Trait(_, _, _, _, items) = item.kind { + for trait_item in items { + if trait_item.id.hir_id == hir_id { + // be sure we have `self` parameter in this function + if let AssocItemKind::Fn { has_self: true } = trait_item.kind { + trait_self_ty = + Some(TraitRef::identity(cx.tcx, trait_item.id.hir_id.owner.to_def_id()).self_ty()); + } + } + } + } } let mut v = EscapeDelegate { cx, set: HirIdSet::default(), + trait_self_ty, too_large_for_stack: self.too_large_for_stack, }; @@ -153,10 +170,17 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { return; } + // skip if there is a `self` parameter binding to a type + // that contains `Self` (i.e.: `self: Box`), see #4804 + if let Some(trait_self_ty) = self.trait_self_ty { + if map.name(cmt.hir_id) == kw::SelfLower && contains_ty(cmt.place.ty(), trait_self_ty) { + return; + } + } + if is_non_trait_box(cmt.place.ty()) && !self.is_large_box(cmt.place.ty()) { self.set.insert(cmt.hir_id); } - return; } } } diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index 53df3abbf5437..1a722d39f730b 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -22,7 +22,7 @@ declare_clippy_lint! { /// **Known problems:** If creating the closure inside the closure has a side- /// effect then moving the closure creation out will change when that side- /// effect runs. - /// See rust-lang/rust-clippy#1439 for more details. + /// See [#1439](https://github.com/rust-lang/rust-clippy/issues/1439) for more details. /// /// **Example:** /// ```rust,ignore @@ -45,8 +45,9 @@ declare_clippy_lint! { /// /// **Why is this bad?** It's unnecessary to create the closure. /// - /// **Known problems:** rust-lang/rust-clippy#3071, rust-lang/rust-clippy#4002, - /// rust-lang/rust-clippy#3942 + /// **Known problems:** [#3071](https://github.com/rust-lang/rust-clippy/issues/3071), + /// [#3942](https://github.com/rust-lang/rust-clippy/issues/3942), + /// [#4002](https://github.com/rust-lang/rust-clippy/issues/4002) /// /// /// **Example:** diff --git a/src/tools/clippy/clippy_lints/src/from_over_into.rs b/src/tools/clippy/clippy_lints/src/from_over_into.rs index 1e7e5f53cc2a3..b010abda24d10 100644 --- a/src/tools/clippy/clippy_lints/src/from_over_into.rs +++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs @@ -70,7 +70,7 @@ impl LateLintPass<'_> for FromOverInto { span_lint_and_help( cx, FROM_OVER_INTO, - item.span, + cx.tcx.sess.source_map().guess_head_span(item.span), "an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true", None, "consider to implement `From` instead", diff --git a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs index 2e55094d90c6f..58511c6d57c68 100644 --- a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs +++ b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs @@ -145,7 +145,7 @@ impl<'tcx, 'l> ArmVisitor<'tcx, 'l> { fn is_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { if_chain! { if let ExprKind::MethodCall(path, _span, args, _) = &expr.kind; - if path.ident.to_string() == "lock"; + if path.ident.as_str() == "lock"; let ty = cx.typeck_results().expr_ty(&args[0]); if is_type_diagnostic_item(cx, ty, sym!(mutex_type)); then { diff --git a/src/tools/clippy/clippy_lints/src/inherent_impl.rs b/src/tools/clippy/clippy_lints/src/inherent_impl.rs index e287aecb044f5..ea26c84cde16a 100644 --- a/src/tools/clippy/clippy_lints/src/inherent_impl.rs +++ b/src/tools/clippy/clippy_lints/src/inherent_impl.rs @@ -2,7 +2,7 @@ use crate::utils::{in_macro, span_lint_and_then}; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::{def_id, Crate, Item, ItemKind, Impl}; +use rustc_hir::{def_id, Crate, Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index 5474b30bdec80..e95caf6a35f90 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -3,7 +3,7 @@ use rustc_ast::ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; -use rustc_hir::{AssocItemKind, BinOpKind, Expr, ExprKind, ImplItemRef, Item, ItemKind, Impl, TraitItemRef}; +use rustc_hir::{AssocItemKind, BinOpKind, Expr, ExprKind, Impl, ImplItemRef, Item, ItemKind, TraitItemRef}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 35b057d7b6a41..f12994c7a605e 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -271,6 +271,7 @@ mod needless_borrow; mod needless_borrowed_ref; mod needless_continue; mod needless_pass_by_value; +mod needless_question_mark; mod needless_update; mod neg_cmp_op_on_partial_ord; mod neg_multiply; @@ -341,6 +342,7 @@ mod unwrap_in_result; mod use_self; mod useless_conversion; mod vec; +mod vec_init_then_push; mod vec_resize_to_zero; mod verbose_file_reads; mod wildcard_dependencies; @@ -524,6 +526,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &utils::internal_lints::OUTER_EXPN_EXPN_DATA, #[cfg(feature = "internal-lints")] &utils::internal_lints::PRODUCE_ICE, + #[cfg(feature = "internal-lints")] + &utils::internal_lints::UNNECESSARY_SYMBOL_STR, &approx_const::APPROX_CONSTANT, &arithmetic::FLOAT_ARITHMETIC, &arithmetic::INTEGER_ARITHMETIC, @@ -556,6 +560,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &cargo_common_metadata::CARGO_COMMON_METADATA, &checked_conversions::CHECKED_CONVERSIONS, &cognitive_complexity::COGNITIVE_COMPLEXITY, + &collapsible_if::COLLAPSIBLE_ELSE_IF, &collapsible_if::COLLAPSIBLE_IF, &collapsible_match::COLLAPSIBLE_MATCH, &comparison_chain::COMPARISON_CHAIN, @@ -799,6 +804,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, &needless_continue::NEEDLESS_CONTINUE, &needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, + &needless_question_mark::NEEDLESS_QUESTION_MARK, &needless_update::NEEDLESS_UPDATE, &neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD, &neg_multiply::NEG_MULTIPLY, @@ -908,6 +914,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &types::LET_UNIT_VALUE, &types::LINKEDLIST, &types::OPTION_OPTION, + &types::PTR_AS_PTR, &types::RC_BUFFER, &types::REDUNDANT_ALLOCATION, &types::TYPE_COMPLEXITY, @@ -935,6 +942,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &use_self::USE_SELF, &useless_conversion::USELESS_CONVERSION, &vec::USELESS_VEC, + &vec_init_then_push::VEC_INIT_THEN_PUSH, &vec_resize_to_zero::VEC_RESIZE_TO_ZERO, &verbose_file_reads::VERBOSE_FILE_READS, &wildcard_dependencies::WILDCARD_DEPENDENCIES, @@ -1019,6 +1027,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box from_over_into::FromOverInto::new(msrv)); store.register_late_pass(move || box use_self::UseSelf::new(msrv)); store.register_late_pass(move || box missing_const_for_fn::MissingConstForFn::new(msrv)); + store.register_late_pass(move || box needless_question_mark::NeedlessQuestionMark::new(msrv)); store.register_late_pass(|| box size_of_in_element_count::SizeOfInElementCount); store.register_late_pass(|| box map_clone::MapClone); @@ -1215,6 +1224,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box strings::StrToString); store.register_late_pass(|| box strings::StringToString); store.register_late_pass(|| box zero_sized_map_values::ZeroSizedMapValues); + store.register_late_pass(|| box vec_init_then_push::VecInitThenPush::default()); + store.register_late_pass(move || box types::PtrAsPtr::new(msrv)); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1341,6 +1352,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::LET_UNIT_VALUE), LintId::of(&types::LINKEDLIST), LintId::of(&types::OPTION_OPTION), + LintId::of(&types::PTR_AS_PTR), LintId::of(&unicode::NON_ASCII_LITERAL), LintId::of(&unicode::UNICODE_NOT_NFC), LintId::of(&unnested_or_patterns::UNNESTED_OR_PATTERNS), @@ -1362,6 +1374,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM), LintId::of(&utils::internal_lints::OUTER_EXPN_EXPN_DATA), LintId::of(&utils::internal_lints::PRODUCE_ICE), + LintId::of(&utils::internal_lints::UNNECESSARY_SYMBOL_STR), ]); store.register_group(true, "clippy::all", Some("clippy"), vec![ @@ -1384,6 +1397,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&booleans::LOGIC_BUG), LintId::of(&booleans::NONMINIMAL_BOOL), LintId::of(&bytecount::NAIVE_BYTECOUNT), + LintId::of(&collapsible_if::COLLAPSIBLE_ELSE_IF), LintId::of(&collapsible_if::COLLAPSIBLE_IF), LintId::of(&collapsible_match::COLLAPSIBLE_MATCH), LintId::of(&comparison_chain::COMPARISON_CHAIN), @@ -1545,6 +1559,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&needless_bool::BOOL_COMPARISON), LintId::of(&needless_bool::NEEDLESS_BOOL), LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), + LintId::of(&needless_question_mark::NEEDLESS_QUESTION_MARK), LintId::of(&needless_update::NEEDLESS_UPDATE), LintId::of(&neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), LintId::of(&neg_multiply::NEG_MULTIPLY), @@ -1636,6 +1651,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unwrap::UNNECESSARY_UNWRAP), LintId::of(&useless_conversion::USELESS_CONVERSION), LintId::of(&vec::USELESS_VEC), + LintId::of(&vec_init_then_push::VEC_INIT_THEN_PUSH), LintId::of(&vec_resize_to_zero::VEC_RESIZE_TO_ZERO), LintId::of(&write::PRINTLN_EMPTY_STRING), LintId::of(&write::PRINT_LITERAL), @@ -1653,6 +1669,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS), LintId::of(&blacklisted_name::BLACKLISTED_NAME), LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), + LintId::of(&collapsible_if::COLLAPSIBLE_ELSE_IF), LintId::of(&collapsible_if::COLLAPSIBLE_IF), LintId::of(&collapsible_match::COLLAPSIBLE_MATCH), LintId::of(&comparison_chain::COMPARISON_CHAIN), @@ -1803,6 +1820,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&needless_bool::BOOL_COMPARISON), LintId::of(&needless_bool::NEEDLESS_BOOL), LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), + LintId::of(&needless_question_mark::NEEDLESS_QUESTION_MARK), LintId::of(&needless_update::NEEDLESS_UPDATE), LintId::of(&neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), LintId::of(&no_effect::NO_EFFECT), @@ -1935,6 +1953,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::BOX_VEC), LintId::of(&types::REDUNDANT_ALLOCATION), LintId::of(&vec::USELESS_VEC), + LintId::of(&vec_init_then_push::VEC_INIT_THEN_PUSH), ]); store.register_group(true, "clippy::cargo", Some("clippy_cargo"), vec![ diff --git a/src/tools/clippy/clippy_lints/src/loops.rs b/src/tools/clippy/clippy_lints/src/loops.rs index 281964ee5e8f3..1c5ab2874b048 100644 --- a/src/tools/clippy/clippy_lints/src/loops.rs +++ b/src/tools/clippy/clippy_lints/src/loops.rs @@ -218,7 +218,7 @@ declare_clippy_lint! { /// **Why is this bad?** The `while let` loop is usually shorter and more /// readable. /// - /// **Known problems:** Sometimes the wrong binding is displayed (#383). + /// **Known problems:** Sometimes the wrong binding is displayed ([#383](https://github.com/rust-lang/rust-clippy/issues/383)). /// /// **Example:** /// ```rust,no_run diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs index 29439e52c48e1..89f5b2ff31137 100644 --- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs +++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs @@ -9,7 +9,7 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; +use rustc_span::{sym, Span}; declare_clippy_lint! { /// **What it does:** It checks for manual implementations of `async` functions. @@ -137,7 +137,7 @@ fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'t if let Some(args) = segment.args; if args.bindings.len() == 1; let binding = &args.bindings[0]; - if binding.ident.as_str() == "Output"; + if binding.ident.name == sym::Output; if let TypeBindingKind::Equality{ty: output} = binding.kind; then { return Some(output) diff --git a/src/tools/clippy/clippy_lints/src/map_clone.rs b/src/tools/clippy/clippy_lints/src/map_clone.rs index 220240acb7aa9..1818836d5d5e8 100644 --- a/src/tools/clippy/clippy_lints/src/map_clone.rs +++ b/src/tools/clippy/clippy_lints/src/map_clone.rs @@ -53,7 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { if_chain! { if let hir::ExprKind::MethodCall(ref method, _, ref args, _) = e.kind; if args.len() == 2; - if method.ident.as_str() == "map"; + if method.ident.name == sym::map; let ty = cx.typeck_results().expr_ty(&args[0]); if is_type_diagnostic_item(cx, ty, sym::option_type) || match_trait_method(cx, e, &paths::ITERATOR); if let hir::ExprKind::Closure(_, _, body_id, _, _) = args[1].kind; diff --git a/src/tools/clippy/clippy_lints/src/map_identity.rs b/src/tools/clippy/clippy_lints/src/map_identity.rs index 6b782385a38d2..9f9c108a85a05 100644 --- a/src/tools/clippy/clippy_lints/src/map_identity.rs +++ b/src/tools/clippy/clippy_lints/src/map_identity.rs @@ -63,7 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for MapIdentity { fn get_map_argument<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a [Expr<'a>]> { if_chain! { if let ExprKind::MethodCall(ref method, _, ref args, _) = expr.kind; - if args.len() == 2 && method.ident.as_str() == "map"; + if args.len() == 2 && method.ident.name == sym::map; let caller_ty = cx.typeck_results().expr_ty(&args[0]); if match_trait_method(cx, expr, &paths::ITERATOR) || is_type_diagnostic_item(cx, caller_ty, sym::result_type) diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 6e8102790a594..79aec928d298b 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -3096,7 +3096,7 @@ fn lint_flat_map_identity<'tcx>( if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = body.value.kind; if path.segments.len() == 1; - if path.segments[0].ident.as_str() == binding_ident.as_str(); + if path.segments[0].ident.name == binding_ident.name; then { apply_lint("called `flat_map(|x| x)` on an `Iterator`"); diff --git a/src/tools/clippy/clippy_lints/src/minmax.rs b/src/tools/clippy/clippy_lints/src/minmax.rs index 004dd50a31be8..8d0c3b8e0fe89 100644 --- a/src/tools/clippy/clippy_lints/src/minmax.rs +++ b/src/tools/clippy/clippy_lints/src/minmax.rs @@ -89,9 +89,9 @@ fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Cons if let [obj, _] = args; if cx.typeck_results().expr_ty(obj).is_floating_point() || match_trait_method(cx, expr, &paths::ORD); then { - if path.ident.as_str() == sym!(max).as_str() { + if path.ident.name == sym!(max) { fetch_const(cx, args, MinMax::Max) - } else if path.ident.as_str() == sym!(min).as_str() { + } else if path.ident.name == sym!(min) { fetch_const(cx, args, MinMax::Min) } else { None diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs index 27f1074a0dd8a..0e49eaab43685 100644 --- a/src/tools/clippy/clippy_lints/src/missing_doc.rs +++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs @@ -63,7 +63,7 @@ impl MissingDoc { if let Some(meta) = list.get(0); if let Some(name) = meta.ident(); then { - name.as_str() == "include" + name.name == sym::include } else { false } diff --git a/src/tools/clippy/clippy_lints/src/needless_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bool.rs index 6b9a37b525201..d795f12645794 100644 --- a/src/tools/clippy/clippy_lints/src/needless_bool.rs +++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs @@ -3,9 +3,7 @@ //! This lint is **warn** by default use crate::utils::sugg::Sugg; -use crate::utils::{ - is_expn_of, parent_node_is_if_expr, snippet_with_applicability, span_lint, span_lint_and_sugg, -}; +use crate::utils::{is_expn_of, parent_node_is_if_expr, snippet_with_applicability, span_lint, span_lint_and_sugg}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp}; diff --git a/src/tools/clippy/clippy_lints/src/needless_borrow.rs b/src/tools/clippy/clippy_lints/src/needless_borrow.rs index bff53eb8ccada..f1c06692e30d9 100644 --- a/src/tools/clippy/clippy_lints/src/needless_borrow.rs +++ b/src/tools/clippy/clippy_lints/src/needless_borrow.rs @@ -2,7 +2,7 @@ //! //! This lint is **warn** by default -use crate::utils::{snippet_opt, span_lint_and_then}; +use crate::utils::{is_automatically_derived, snippet_opt, span_lint_and_then}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BindingAnnotation, BorrowKind, Expr, ExprKind, HirId, Item, Mutability, Pat, PatKind}; @@ -10,7 +10,6 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for address of operations (`&`) that are going to @@ -116,7 +115,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { } fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if item.attrs.iter().any(|a| a.has_name(sym::automatically_derived)) { + if is_automatically_derived(item.attrs) { debug_assert!(self.derived_item.is_none()); self.derived_item = Some(item.hir_id); } diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs index a435f86bfd8d5..c8f89f8046c85 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs @@ -8,11 +8,12 @@ use rustc_ast::ast::Attribute; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir::intravisit::FnKind; -use rustc_hir::{BindingAnnotation, Body, FnDecl, GenericArg, HirId, ItemKind, Impl, Node, PatKind, QPath, TyKind}; +use rustc_hir::{BindingAnnotation, Body, FnDecl, GenericArg, HirId, Impl, ItemKind, Node, PatKind, QPath, TyKind}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, TypeFoldable}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::kw; use rustc_span::{sym, Span}; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; @@ -153,7 +154,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { // Ignore `self`s. if idx == 0 { if let PatKind::Binding(.., ident, _) = arg.pat.kind { - if ident.as_str() == "self" { + if ident.name == kw::SelfLower { continue; } } diff --git a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs new file mode 100644 index 0000000000000..9e9b79ee1cf08 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs @@ -0,0 +1,232 @@ +use rustc_errors::Applicability; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; +use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::ty::DefIdTree; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::sym; + +use crate::utils; +use if_chain::if_chain; + +declare_clippy_lint! { + /// **What it does:** + /// Suggests alternatives for useless applications of `?` in terminating expressions + /// + /// **Why is this bad?** There's no reason to use `?` to short-circuit when execution of the body will end there anyway. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// struct TO { + /// magic: Option, + /// } + /// + /// fn f(to: TO) -> Option { + /// Some(to.magic?) + /// } + /// + /// struct TR { + /// magic: Result, + /// } + /// + /// fn g(tr: Result) -> Result { + /// tr.and_then(|t| Ok(t.magic?)) + /// } + /// + /// ``` + /// Use instead: + /// ```rust + /// struct TO { + /// magic: Option, + /// } + /// + /// fn f(to: TO) -> Option { + /// to.magic + /// } + /// + /// struct TR { + /// magic: Result, + /// } + /// + /// fn g(tr: Result) -> Result { + /// tr.and_then(|t| t.magic) + /// } + /// ``` + pub NEEDLESS_QUESTION_MARK, + complexity, + "Suggest `value.inner_option` instead of `Some(value.inner_option?)`. The same goes for `Result`." +} + +const NEEDLESS_QUESTION_MARK_RESULT_MSRV: RustcVersion = RustcVersion::new(1, 13, 0); +const NEEDLESS_QUESTION_MARK_OPTION_MSRV: RustcVersion = RustcVersion::new(1, 22, 0); + +pub struct NeedlessQuestionMark { + msrv: Option, +} + +impl NeedlessQuestionMark { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_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 and Option, + * from Rust 1.13 and 1.22 respectively. + */ + + /* + * What do we match: + * Expressions that look like this: + * Some(option?), Ok(result?) + * + * Where do we match: + * Last expression of a body + * Return statement + * A body's value (single line closure) + * + * What do we not match: + * Implicit calls to `from(..)` on the error value + */ + + 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(self, cx, e) { + emit_lint(cx, &ok_some_call); + } + } + + 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(self, cx, expr); + then { + emit_lint(cx, &ok_some_call); + } + }; + } + + extract_msrv_attr!(LateContext); +} + +fn emit_lint(cx: &LateContext<'_>, expr: &SomeOkCall<'_>) { + let (entire_expr, inner_expr) = match expr { + SomeOkCall::OkCall(outer, inner) | SomeOkCall::SomeCall(outer, inner) => (outer, inner), + }; + + utils::span_lint_and_sugg( + cx, + NEEDLESS_QUESTION_MARK, + entire_expr.span, + "Question mark operator is useless here", + "try", + format!("{}", utils::snippet(cx, inner_expr.span, r#""...""#)), + Applicability::MachineApplicable, + ); +} + +fn is_some_or_ok_call<'a>( + nqml: &NeedlessQuestionMark, + cx: &'a LateContext<'_>, + expr: &'a Expr<'_>, +) -> Option> { + if_chain! { + // Check outer expression matches CALL_IDENT(ARGUMENT) format + if let ExprKind::Call(path, args) = &expr.kind; + if let ExprKind::Path(QPath::Resolved(None, path)) = &path.kind; + if is_some_ctor(cx, path.res) || is_ok_ctor(cx, path.res); + + // 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::TryIntoResult, _)) = &called.kind; + then { + // Extract inner expr type from match argument generated by + // question mark operator + let inner_expr = &args[0]; + + 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 = utils::is_type_diagnostic_item(cx, outer_ty, sym::option_type); + let inner_is_some = utils::is_type_diagnostic_item(cx, inner_ty, sym::option_type); + + // Check for Option MSRV + let meets_option_msrv = utils::meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_OPTION_MSRV); + if outer_is_some && inner_is_some && meets_option_msrv { + return Some(SomeOkCall::SomeCall(expr, inner_expr)); + } + + // Check if outer and inner type are Result + let outer_is_result = utils::is_type_diagnostic_item(cx, outer_ty, sym::result_type); + let inner_is_result = utils::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 + let meets_result_msrv = utils::meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_RESULT_MSRV); + if outer_is_result && inner_is_result && does_not_call_from && meets_result_msrv { + 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); +} + +fn is_ok_ctor(cx: &LateContext<'_>, res: Res) -> bool { + if let Some(ok_id) = cx.tcx.lang_items().result_ok_variant() { + if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { + if let Some(variant_id) = cx.tcx.parent(id) { + return variant_id == ok_id; + } + } + } + false +} + +fn is_some_ctor(cx: &LateContext<'_>, res: Res) -> bool { + if let Some(some_id) = cx.tcx.lang_items().option_some_variant() { + if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { + if let Some(variant_id) = cx.tcx.parent(id) { + return variant_id == some_id; + } + } + } + false +} 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 391f893ef35ff..7bdf975ffd446 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 @@ -66,7 +66,7 @@ declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]); /// Returns true iff the given expression is the result of calling `Result::ok` fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool { if let ExprKind::MethodCall(ref path, _, &[ref receiver], _) = &expr.kind { - path.ident.name.to_ident_string() == "ok" + path.ident.name.as_str() == "ok" && is_type_diagnostic_item(cx, &cx.typeck_results().expr_ty(&receiver), sym::result_type) } else { false @@ -110,7 +110,7 @@ fn extract_body_from_arm<'a>(arm: &'a Arm<'a>) -> Option<&'a Expr<'a>> { fn should_wrap_in_braces(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { utils::get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| { let mut should_wrap = false; - + if let Some(Expr { kind: ExprKind::Match( @@ -124,7 +124,11 @@ fn should_wrap_in_braces(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { }) = 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 { + } else if let Some(Expr { + kind: ExprKind::If(_, _, Some(else_clause)), + .. + }) = parent.expr + { should_wrap = expr.hir_id == else_clause.hir_id; } diff --git a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs index 04b6e5d58478b..ed314937ce8be 100644 --- a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs +++ b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs @@ -1,6 +1,6 @@ use crate::utils::{is_automatically_derived, span_lint_hir}; use if_chain::if_chain; -use rustc_hir::{Item, ItemKind, Impl}; +use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; diff --git a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs index 7814065e31a1a..d96a9b025f089 100644 --- a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs +++ b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs @@ -6,7 +6,7 @@ use rustc_ast::attr; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; -use rustc_hir::{BindingAnnotation, Body, FnDecl, HirId, ItemKind, MutTy, Mutability, Node, PatKind, Impl}; +use rustc_hir::{BindingAnnotation, Body, FnDecl, HirId, Impl, ItemKind, MutTy, Mutability, Node, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -63,7 +63,7 @@ declare_clippy_lint! { /// /// **Why is this bad?** Arguments passed by value might result in an unnecessary /// shallow copy, taking up more space in the stack and requiring a call to - /// `memcpy`, which which can be expensive. + /// `memcpy`, which can be expensive. /// /// **Example:** /// diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs index b832add009f86..c6329a1381c90 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -8,8 +8,8 @@ use crate::utils::{ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{ - BinOpKind, BodyId, Expr, ExprKind, FnDecl, FnRetTy, GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Impl, - Lifetime, MutTy, Mutability, Node, PathSegment, QPath, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, + BinOpKind, BodyId, Expr, ExprKind, FnDecl, FnRetTy, GenericArg, HirId, Impl, ImplItem, ImplItemKind, Item, + ItemKind, Lifetime, MutTy, Mutability, Node, PathSegment, QPath, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; diff --git a/src/tools/clippy/clippy_lints/src/ref_option_ref.rs b/src/tools/clippy/clippy_lints/src/ref_option_ref.rs index 803ebada54b79..8cd6692ce03a0 100644 --- a/src/tools/clippy/clippy_lints/src/ref_option_ref.rs +++ b/src/tools/clippy/clippy_lints/src/ref_option_ref.rs @@ -13,7 +13,7 @@ declare_clippy_lint! { /// **Why is this bad?** Since `&` is Copy, it's useless to have a /// reference on `Option<&T>`. /// - /// **Known problems:** It may be irrevelent to use this lint on + /// **Known problems:** It may be irrelevant to use this lint on /// public API code as it will make a breaking change to apply it. /// /// **Example:** diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs index 35827e027aab8..63548d8fdb438 100644 --- a/src/tools/clippy/clippy_lints/src/returns.rs +++ b/src/tools/clippy/clippy_lints/src/returns.rs @@ -202,7 +202,7 @@ fn check_final_expr<'tcx>( check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block); } }, - | MatchSource::IfLetDesugar { + MatchSource::IfLetDesugar { contains_else_clause: true, } => { if let ExprKind::Block(ref ifblock, _) = arms[0].body.kind { @@ -217,6 +217,9 @@ fn check_final_expr<'tcx>( } fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option, replacement: RetReplacement) { + if ret_span.from_expansion() { + return; + } match inner_span { Some(inner_span) => { if in_external_macro(cx.tcx.sess, inner_span) || inner_span.from_expansion() { diff --git a/src/tools/clippy/clippy_lints/src/serde_api.rs b/src/tools/clippy/clippy_lints/src/serde_api.rs index ca4fd9f35597f..44e739725c820 100644 --- a/src/tools/clippy/clippy_lints/src/serde_api.rs +++ b/src/tools/clippy/clippy_lints/src/serde_api.rs @@ -1,5 +1,5 @@ use crate::utils::{get_trait_def_id, paths, span_lint}; -use rustc_hir::{Item, ItemKind, Impl}; +use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/src/tools/clippy/clippy_lints/src/shadow.rs b/src/tools/clippy/clippy_lints/src/shadow.rs index f2f3dfa09a7d4..24da056770c9d 100644 --- a/src/tools/clippy/clippy_lints/src/shadow.rs +++ b/src/tools/clippy/clippy_lints/src/shadow.rs @@ -396,5 +396,5 @@ fn is_self_shadow(name: Symbol, expr: &Expr<'_>) -> bool { } fn path_eq_name(name: Symbol, path: &Path<'_>) -> bool { - !path.is_global() && path.segments.len() == 1 && path.segments[0].ident.as_str() == name.as_str() + !path.is_global() && path.segments.len() == 1 && path.segments[0].ident.name == name } diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs index 386987eb181ea..699fd51ccc194 100644 --- a/src/tools/clippy/clippy_lints/src/swap.rs +++ b/src/tools/clippy/clippy_lints/src/swap.rs @@ -91,7 +91,7 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) { if let ExprKind::Path(QPath::Resolved(None, ref rhs2)) = rhs2.kind; if rhs2.segments.len() == 1; - if ident.as_str() == rhs2.segments[0].ident.as_str(); + if ident.name == rhs2.segments[0].ident.name; if eq_expr_value(cx, tmp_init, lhs1); if eq_expr_value(cx, rhs1, lhs2); then { diff --git a/src/tools/clippy/clippy_lints/src/to_string_in_display.rs b/src/tools/clippy/clippy_lints/src/to_string_in_display.rs index 675eaf4277a43..c53727ba16004 100644 --- a/src/tools/clippy/clippy_lints/src/to_string_in_display.rs +++ b/src/tools/clippy/clippy_lints/src/to_string_in_display.rs @@ -1,7 +1,7 @@ use crate::utils::{match_def_path, match_trait_method, paths, qpath_res, span_lint}; use if_chain::if_chain; use rustc_hir::def::Res; -use rustc_hir::{Expr, ExprKind, HirId, ImplItem, ImplItemKind, Item, ItemKind, Impl}; +use rustc_hir::{Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; diff --git a/src/tools/clippy/clippy_lints/src/types.rs b/src/tools/clippy/clippy_lints/src/types.rs index 2696c5e781abc..3b5a83d2a0bec 100644 --- a/src/tools/clippy/clippy_lints/src/types.rs +++ b/src/tools/clippy/clippy_lints/src/types.rs @@ -8,7 +8,6 @@ use if_chain::if_chain; use rustc_ast::{FloatTy, IntTy, LitFloatType, LitIntType, LitKind, UintTy}; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; -use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::{ BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericBounds, GenericParamKind, HirId, @@ -19,7 +18,8 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::TypeFoldable; -use rustc_middle::ty::{self, InferTy, Ty, TyCtxt, TyS, TypeckResults}; +use rustc_middle::ty::{self, InferTy, Ty, TyCtxt, TyS, TypeAndMut, TypeckResults}; +use rustc_semver::RustcVersion; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::Span; @@ -30,11 +30,13 @@ use rustc_typeck::hir_ty_to_ty; use crate::consts::{constant, Constant}; use crate::utils::paths; +use crate::utils::sugg::Sugg; use crate::utils::{ - clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_type_diagnostic_item, - last_path_segment, match_def_path, match_path, method_chain_args, multispan_sugg, numeric_literal::NumericLiteral, - qpath_res, reindent_multiline, sext, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, - span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext, + clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_hir_ty_cfg_dependant, + is_type_diagnostic_item, last_path_segment, match_def_path, match_path, meets_msrv, method_chain_args, + multispan_sugg, numeric_literal::NumericLiteral, qpath_res, reindent_multiline, sext, snippet, snippet_opt, + snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, + span_lint_and_then, unsext, }; declare_clippy_lint! { @@ -73,7 +75,7 @@ declare_clippy_lint! { /// **Why is this bad?** `Vec` already keeps its contents in a separate area on /// the heap. So if you `Box` its contents, you just add another level of indirection. /// - /// **Known problems:** Vec> makes sense if T is a large type (see #3530, + /// **Known problems:** Vec> makes sense if T is a large type (see [#3530](https://github.com/rust-lang/rust-clippy/issues/3530), /// 1st comment). /// /// **Example:** @@ -1279,8 +1281,8 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for casts from a less-strictly-aligned pointer to a - /// more-strictly-aligned pointer + /// **What it does:** Checks for casts, using `as` or `pointer::cast`, + /// from a less-strictly-aligned pointer to a more-strictly-aligned pointer /// /// **Why is this bad?** Dereferencing the resulting pointer may be undefined /// behavior. @@ -1293,6 +1295,9 @@ declare_clippy_lint! { /// ```rust /// let _ = (&1u8 as *const u8) as *const u16; /// let _ = (&mut 1u8 as *mut u8) as *mut u16; + /// + /// (&1u8 as *const u8).cast::(); + /// (&mut 1u8 as *mut u8).cast::(); /// ``` pub CAST_PTR_ALIGNMENT, pedantic, @@ -1634,12 +1639,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts { return; } if let ExprKind::Cast(ref ex, cast_to) = expr.kind { - if let TyKind::Path(QPath::Resolved(_, path)) = cast_to.kind { - if let Res::Def(_, def_id) = path.res { - if cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr) { - return; - } - } + if is_hir_ty_cfg_dependant(cx, cast_to) { + return; } let (cast_from, cast_to) = (cx.typeck_results().expr_ty(ex), cx.typeck_results().expr_ty(expr)); lint_fn_to_numeric_cast(cx, expr, ex, cast_from, cast_to); @@ -1689,6 +1690,19 @@ impl<'tcx> LateLintPass<'tcx> for Casts { } lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); + } else if let ExprKind::MethodCall(method_path, _, args, _) = expr.kind { + if_chain! { + if method_path.ident.name == sym!(cast); + if let Some(generic_args) = method_path.args; + if let [GenericArg::Type(cast_to)] = generic_args.args; + // There probably is no obvious reason to do this, just to be consistent with `as` cases. + if !is_hir_ty_cfg_dependant(cx, cast_to); + then { + let (cast_from, cast_to) = + (cx.typeck_results().expr_ty(&args[0]), cx.typeck_results().expr_ty(expr)); + lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); + } + } } } } @@ -2873,3 +2887,93 @@ impl<'tcx> LateLintPass<'tcx> for RefToMut { } } } + +const PTR_AS_PTR_MSRV: RustcVersion = RustcVersion::new(1, 38, 0); + +declare_clippy_lint! { + /// **What it does:** + /// Checks for `as` casts between raw pointers without changing its mutability, + /// namely `*const T` to `*const U` and `*mut T` to `*mut U`. + /// + /// **Why is this bad?** + /// Though `as` casts between raw pointers is not terrible, `pointer::cast` is safer because + /// it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let ptr: *const u32 = &42_u32; + /// let mut_ptr: *mut u32 = &mut 42_u32; + /// let _ = ptr as *const i32; + /// let _ = mut_ptr as *mut i32; + /// ``` + /// Use instead: + /// ```rust + /// let ptr: *const u32 = &42_u32; + /// let mut_ptr: *mut u32 = &mut 42_u32; + /// let _ = ptr.cast::(); + /// let _ = mut_ptr.cast::(); + /// ``` + pub PTR_AS_PTR, + pedantic, + "casting using `as` from and to raw pointers that doesn't change its mutability, where `pointer::cast` could take the place of `as`" +} + +pub struct PtrAsPtr { + msrv: Option, +} + +impl PtrAsPtr { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(PtrAsPtr => [PTR_AS_PTR]); + +impl<'tcx> LateLintPass<'tcx> for PtrAsPtr { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if !meets_msrv(self.msrv.as_ref(), &PTR_AS_PTR_MSRV) { + return; + } + + if expr.span.from_expansion() { + return; + } + + if_chain! { + if let ExprKind::Cast(cast_expr, cast_to_hir_ty) = expr.kind; + let (cast_from, cast_to) = (cx.typeck_results().expr_ty(cast_expr), cx.typeck_results().expr_ty(expr)); + if let ty::RawPtr(TypeAndMut { mutbl: from_mutbl, .. }) = cast_from.kind(); + if let ty::RawPtr(TypeAndMut { ty: to_pointee_ty, mutbl: to_mutbl }) = cast_to.kind(); + if matches!((from_mutbl, to_mutbl), + (Mutability::Not, Mutability::Not) | (Mutability::Mut, Mutability::Mut)); + // The `U` in `pointer::cast` have to be `Sized` + // as explained here: https://github.com/rust-lang/rust/issues/60602. + if to_pointee_ty.is_sized(cx.tcx.at(expr.span), cx.param_env); + then { + let mut applicability = Applicability::MachineApplicable; + let cast_expr_sugg = Sugg::hir_with_applicability(cx, cast_expr, "_", &mut applicability); + let turbofish = match &cast_to_hir_ty.kind { + TyKind::Infer => Cow::Borrowed(""), + TyKind::Ptr(mut_ty) if matches!(mut_ty.ty.kind, TyKind::Infer) => Cow::Borrowed(""), + _ => Cow::Owned(format!("::<{}>", to_pointee_ty)), + }; + span_lint_and_sugg( + cx, + PTR_AS_PTR, + expr.span, + "`as` casting between raw pointers without changing its mutability", + "try `pointer::cast`, a safer alternative", + format!("{}.cast{}()", cast_expr_sugg.maybe_par(), turbofish), + applicability, + ); + } + } + } + + extract_msrv_attr!(LateContext); +} diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs b/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs index 0bccfc156788a..9b45d38afd42f 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs @@ -183,7 +183,7 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { Param { pat: Pat { kind: PatKind::Binding(_, _, right_ident, _), .. }, .. } ] = &closure_body.params; if let ExprKind::MethodCall(method_path, _, [ref left_expr, ref right_expr], _) = &closure_body.value.kind; - if method_path.ident.name.to_ident_string() == "cmp"; + if method_path.ident.name == sym::cmp; then { let (closure_body, closure_arg, reverse) = if mirrored_exprs( &cx, diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs index 07cd752184bbc..8ac5dd696b762 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs @@ -5,7 +5,7 @@ use crate::utils::{ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, ExprKind, FnDecl, HirId, ItemKind, Impl, Node}; +use rustc_hir::{Body, ExprKind, FnDecl, HirId, Impl, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/src/tools/clippy/clippy_lints/src/unused_self.rs b/src/tools/clippy/clippy_lints/src/unused_self.rs index a617179431107..5349c4f7eb8a7 100644 --- a/src/tools/clippy/clippy_lints/src/unused_self.rs +++ b/src/tools/clippy/clippy_lints/src/unused_self.rs @@ -1,7 +1,7 @@ use if_chain::if_chain; use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_path, NestedVisitorMap, Visitor}; -use rustc_hir::{HirId, ImplItem, ImplItemKind, ItemKind, Impl, Path}; +use rustc_hir::{HirId, Impl, ImplItem, ImplItemKind, ItemKind, Path}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs index 6a87f53436980..b82909eaea604 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap.rs @@ -1,6 +1,5 @@ use crate::utils::{ - differing_macro_contexts, is_type_diagnostic_item, span_lint_and_then, - usage::is_potentially_mutated, + differing_macro_contexts, is_type_diagnostic_item, span_lint_and_then, usage::is_potentially_mutated, }; use if_chain::if_chain; use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, NestedVisitorMap, Visitor}; diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs index b82ea66190fcf..72d1ca7392913 100644 --- a/src/tools/clippy/clippy_lints/src/use_self.rs +++ b/src/tools/clippy/clippy_lints/src/use_self.rs @@ -28,8 +28,8 @@ declare_clippy_lint! { /// feels inconsistent. /// /// **Known problems:** - /// - False positive when using associated types (#2843) - /// - False positives in some situations when using generics (#3410) + /// - False positive when using associated types ([#2843](https://github.com/rust-lang/rust-clippy/issues/2843)) + /// - False positives in some situations when using generics ([#3410](https://github.com/rust-lang/rust-clippy/issues/3410)) /// /// **Example:** /// ```rust diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs index efa9c3fab4ab8..c533485398605 100644 --- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs +++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs @@ -80,10 +80,10 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { ); } } - if match_trait_method(cx, e, &paths::INTO_ITERATOR) && &*name.ident.as_str() == "into_iter" { + if match_trait_method(cx, e, &paths::INTO_ITERATOR) && name.ident.name == sym::into_iter { if let Some(parent_expr) = get_parent_expr(cx, e) { if let ExprKind::MethodCall(ref parent_name, ..) = parent_expr.kind { - if &*parent_name.ident.as_str() != "into_iter" { + if parent_name.ident.name != sym::into_iter { return; } } diff --git a/src/tools/clippy/clippy_lints/src/utils/attrs.rs b/src/tools/clippy/clippy_lints/src/utils/attrs.rs index 24052a243af82..8d28421d70d70 100644 --- a/src/tools/clippy/clippy_lints/src/utils/attrs.rs +++ b/src/tools/clippy/clippy_lints/src/utils/attrs.rs @@ -1,6 +1,7 @@ use rustc_ast::ast; use rustc_errors::Applicability; use rustc_session::Session; +use rustc_span::sym; use std::str::FromStr; /// Deprecation status of attributes known by Clippy. @@ -64,11 +65,11 @@ pub fn get_attr<'a>( return false; }; let attr_segments = &attr.path.segments; - if attr_segments.len() == 2 && attr_segments[0].ident.to_string() == "clippy" { + if attr_segments.len() == 2 && attr_segments[0].ident.name == sym::clippy { BUILTIN_ATTRIBUTES .iter() - .find_map(|(builtin_name, deprecation_status)| { - if *builtin_name == attr_segments[1].ident.to_string() { + .find_map(|&(builtin_name, ref deprecation_status)| { + if attr_segments[1].ident.name.as_str() == builtin_name { Some(deprecation_status) } else { None @@ -99,7 +100,7 @@ pub fn get_attr<'a>( }, DeprecationStatus::None => { diag.cancel(); - attr_segments[1].ident.to_string() == name + attr_segments[1].ident.name.as_str() == name }, } }, diff --git a/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs b/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs index 062df68c0a482..10120a8805db2 100644 --- a/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs +++ b/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs @@ -86,7 +86,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { lb == rb && l_mut == r_mut && self.eq_expr(le, re) }, (&ExprKind::Continue(li), &ExprKind::Continue(ri)) => { - both(&li.label, &ri.label, |l, r| l.ident.as_str() == r.ident.as_str()) + both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name) }, (&ExprKind::Assign(ref ll, ref lr, _), &ExprKind::Assign(ref rl, ref rr, _)) => { self.allow_side_effects && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) @@ -102,7 +102,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { }) }, (&ExprKind::Break(li, ref le), &ExprKind::Break(ri, ref re)) => { - both(&li.label, &ri.label, |l, r| l.ident.as_str() == r.ident.as_str()) + both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name) && both(le, re, |l, r| self.eq_expr(l, r)) }, (&ExprKind::Box(ref l), &ExprKind::Box(ref r)) => self.eq_expr(l, r), @@ -124,7 +124,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { }, (&ExprKind::Lit(ref l), &ExprKind::Lit(ref r)) => l.node == r.node, (&ExprKind::Loop(ref lb, ref ll, ref lls), &ExprKind::Loop(ref rb, ref rl, ref rls)) => { - lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.as_str() == r.ident.as_str()) + lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.name == r.ident.name) }, (&ExprKind::Match(ref le, ref la, ref ls), &ExprKind::Match(ref re, ref ra, ref rs)) => { ls == rs @@ -191,7 +191,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { pub fn eq_fieldpat(&mut self, left: &FieldPat<'_>, right: &FieldPat<'_>) -> bool { let (FieldPat { ident: li, pat: lp, .. }, FieldPat { ident: ri, pat: rp, .. }) = (&left, &right); - li.name.as_str() == ri.name.as_str() && self.eq_pat(lp, rp) + li.name == ri.name && self.eq_pat(lp, rp) } /// Checks whether two patterns are the same. @@ -205,7 +205,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs }, (&PatKind::Binding(ref lb, .., ref li, ref lp), &PatKind::Binding(ref rb, .., ref ri, ref rp)) => { - lb == rb && li.name.as_str() == ri.name.as_str() && both(lp, rp, |l, r| self.eq_pat(l, r)) + lb == rb && li.name == ri.name && both(lp, rp, |l, r| self.eq_pat(l, r)) }, (&PatKind::Path(ref l), &PatKind::Path(ref r)) => self.eq_qpath(l, r), (&PatKind::Lit(ref l), &PatKind::Lit(ref r)) => self.eq_expr(l, r), @@ -266,8 +266,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool { // The == of idents doesn't work with different contexts, // we have to be explicit about hygiene - left.ident.as_str() == right.ident.as_str() - && both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r)) + left.ident.name == right.ident.name && both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r)) } pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs index 407f06f489420..7aa17520ba79f 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs @@ -10,9 +10,12 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefId; use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; -use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind}; +use rustc_hir::{ + BinOpKind, Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind, UnOp, +}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::mir::interpret::ConstValue; @@ -272,6 +275,28 @@ declare_clippy_lint! { "interning a symbol that is pre-interned and defined as a constant" } +declare_clippy_lint! { + /// **What it does:** Checks for unnecessary conversion from Symbol to a string. + /// + /// **Why is this bad?** It's faster use symbols directly intead of strings. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// Bad: + /// ```rust,ignore + /// symbol.as_str() == "clippy"; + /// ``` + /// + /// Good: + /// ```rust,ignore + /// symbol == sym::clippy; + /// ``` + pub UNNECESSARY_SYMBOL_STR, + internal, + "unnecessary conversion between Symbol and string" +} + declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); impl EarlyLintPass for ClippyLintsInternal { @@ -868,11 +893,11 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths { #[derive(Default)] pub struct InterningDefinedSymbol { - // Maps the symbol value to the constant name. - symbol_map: FxHashMap, + // Maps the symbol value to the constant DefId. + symbol_map: FxHashMap, } -impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL]); +impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL, UNNECESSARY_SYMBOL_STR]); impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) { @@ -880,16 +905,18 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { return; } - if let Some(Res::Def(_, def_id)) = path_to_res(cx, &paths::SYM_MODULE) { - for item in cx.tcx.item_children(def_id).iter() { - if_chain! { - if let Res::Def(DefKind::Const, item_def_id) = item.res; - let ty = cx.tcx.type_of(item_def_id); - if match_type(cx, ty, &paths::SYMBOL); - if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id); - if let Ok(value) = value.to_u32(); - then { - self.symbol_map.insert(value, item.ident.to_string()); + for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { + if let Some(Res::Def(_, def_id)) = path_to_res(cx, module) { + for item in cx.tcx.item_children(def_id).iter() { + if_chain! { + if let Res::Def(DefKind::Const, item_def_id) = item.res; + let ty = cx.tcx.type_of(item_def_id); + if match_type(cx, ty, &paths::SYMBOL); + if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id); + if let Ok(value) = value.to_u32(); + then { + self.symbol_map.insert(value, item_def_id); + } } } } @@ -903,7 +930,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN); if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg); let value = Symbol::intern(&arg).as_u32(); - if let Some(symbol_const) = self.symbol_map.get(&value); + if let Some(&def_id) = self.symbol_map.get(&value); then { span_lint_and_sugg( cx, @@ -911,10 +938,135 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { is_expn_of(expr.span, "sym").unwrap_or(expr.span), "interning a defined symbol", "try", - format!("rustc_span::symbol::sym::{}", symbol_const), + cx.tcx.def_path_str(def_id), Applicability::MachineApplicable, ); } } + if let ExprKind::Binary(op, left, right) = expr.kind { + if matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) { + let data = [ + (left, self.symbol_str_expr(left, cx)), + (right, self.symbol_str_expr(right, cx)), + ]; + match data { + // both operands are a symbol string + [(_, Some(left)), (_, Some(right))] => { + span_lint_and_sugg( + cx, + UNNECESSARY_SYMBOL_STR, + expr.span, + "unnecessary `Symbol` to string conversion", + "try", + format!( + "{} {} {}", + left.as_symbol_snippet(cx), + op.node.as_str(), + right.as_symbol_snippet(cx), + ), + Applicability::MachineApplicable, + ); + }, + // one of the operands is a symbol string + [(expr, Some(symbol)), _] | [_, (expr, Some(symbol))] => { + // creating an owned string for comparison + if matches!(symbol, SymbolStrExpr::Expr { is_to_owned: true, .. }) { + span_lint_and_sugg( + cx, + UNNECESSARY_SYMBOL_STR, + expr.span, + "unnecessary string allocation", + "try", + format!("{}.as_str()", symbol.as_symbol_snippet(cx)), + Applicability::MachineApplicable, + ); + } + }, + // nothing found + [(_, None), (_, None)] => {}, + } + } + } + } +} + +impl InterningDefinedSymbol { + fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option> { + static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR, &paths::TO_STRING_METHOD]; + static SYMBOL_STR_PATHS: &[&[&str]] = &[ + &paths::SYMBOL_AS_STR, + &paths::SYMBOL_TO_IDENT_STRING, + &paths::TO_STRING_METHOD, + ]; + // SymbolStr might be de-referenced: `&*symbol.as_str()` + let call = if_chain! { + if let ExprKind::AddrOf(_, _, e) = expr.kind; + if let ExprKind::Unary(UnOp::UnDeref, e) = e.kind; + then { e } else { expr } + }; + if_chain! { + // is a method call + if let ExprKind::MethodCall(_, _, [item], _) = call.kind; + if let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id); + let ty = cx.typeck_results().expr_ty(item); + // ...on either an Ident or a Symbol + if let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) { + Some(false) + } else if match_type(cx, ty, &paths::IDENT) { + Some(true) + } else { + None + }; + // ...which converts it to a string + let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS }; + if let Some(path) = paths.iter().find(|path| match_def_path(cx, did, path)); + then { + let is_to_owned = path.last().unwrap().ends_with("string"); + return Some(SymbolStrExpr::Expr { + item, + is_ident, + is_to_owned, + }); + } + } + // is a string constant + if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) { + let value = Symbol::intern(&s).as_u32(); + // ...which matches a symbol constant + if let Some(&def_id) = self.symbol_map.get(&value) { + return Some(SymbolStrExpr::Const(def_id)); + } + } + None + } +} + +enum SymbolStrExpr<'tcx> { + /// a string constant with a corresponding symbol constant + Const(DefId), + /// a "symbol to string" expression like `symbol.as_str()` + Expr { + /// part that evaluates to `Symbol` or `Ident` + item: &'tcx Expr<'tcx>, + is_ident: bool, + /// whether an owned `String` is created like `to_ident_string()` + is_to_owned: bool, + }, +} + +impl<'tcx> SymbolStrExpr<'tcx> { + /// Returns a snippet that evaluates to a `Symbol` and is const if possible + fn as_symbol_snippet(&self, cx: &LateContext<'_>) -> Cow<'tcx, str> { + match *self { + Self::Const(def_id) => cx.tcx.def_path_str(def_id).into(), + Self::Expr { item, is_ident, .. } => { + let mut snip = snippet(cx, item.span.source_callsite(), ".."); + if is_ident { + // get `Ident.name` + snip.to_mut().push_str(".name"); + } + snip + }, + } } } diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs index 3e39a47f1963d..548c4f7510ad5 100644 --- a/src/tools/clippy/clippy_lints/src/utils/mod.rs +++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs @@ -1,5 +1,5 @@ #[macro_use] -pub mod sym; +pub mod sym_helper; #[allow(clippy::module_name_repetitions)] pub mod ast_utils; @@ -56,8 +56,8 @@ use rustc_semver::RustcVersion; use rustc_session::Session; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; -use rustc_span::sym as rustc_sym; -use rustc_span::symbol::{self, kw, Symbol}; +use rustc_span::sym; +use rustc_span::symbol::{kw, Symbol}; use rustc_span::{BytePos, Pos, Span, DUMMY_SP}; use rustc_target::abi::Integer; use rustc_trait_selection::traits::query::normalize::AtExt; @@ -1121,7 +1121,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { /// Checks for the `#[automatically_derived]` attribute all `#[derive]`d /// implementations have. pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool { - attrs.iter().any(|attr| attr.has_name(rustc_sym::automatically_derived)) + attrs.iter().any(|attr| attr.has_name(sym::automatically_derived)) } /// Remove blocks around an expression. @@ -1434,12 +1434,13 @@ pub fn parent_node_is_if_expr(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool { let map = cx.tcx.hir(); let parent_id = map.get_parent_node(expr.hir_id); let parent_node = map.get(parent_id); - if let Node::Expr(Expr { kind: ExprKind::If(_, _, _), .. }) = parent_node { - true - } - else { - false - } + matches!( + parent_node, + Node::Expr(Expr { + kind: ExprKind::If(_, _, _), + .. + }) + ) } // Finds the attribute with the given name, if any @@ -1514,7 +1515,7 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { pub fn is_no_std_crate(krate: &Crate<'_>) -> bool { krate.item.attrs.iter().any(|attr| { if let ast::AttrKind::Normal(ref attr, _) = attr.kind { - attr.path == symbol::sym::no_std + attr.path == sym::no_std } else { false } @@ -1686,6 +1687,18 @@ macro_rules! unwrap_cargo_metadata { }}; } +pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { + if_chain! { + if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind; + if let Res::Def(_, def_id) = path.res; + then { + cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr) + } else { + false + } + } +} + #[cfg(test)] mod test { use super::{reindent_multiline, without_block_comments}; diff --git a/src/tools/clippy/clippy_lints/src/utils/paths.rs b/src/tools/clippy/clippy_lints/src/utils/paths.rs index 2080a49a11cd6..c0b203b5388dc 100644 --- a/src/tools/clippy/clippy_lints/src/utils/paths.rs +++ b/src/tools/clippy/clippy_lints/src/utils/paths.rs @@ -54,6 +54,10 @@ pub const HASH: [&str; 3] = ["core", "hash", "Hash"]; pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"]; pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"]; pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"]; +#[cfg(feature = "internal-lints")] +pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"]; +#[cfg(feature = "internal-lints")] +pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"]; pub const INDEX: [&str; 3] = ["core", "ops", "Index"]; pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"]; pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"]; @@ -65,6 +69,8 @@ pub const IPADDR_V4: [&str; 4] = ["std", "net", "IpAddr", "V4"]; pub const IPADDR_V6: [&str; 4] = ["std", "net", "IpAddr", "V6"]; pub const ITERATOR: [&str; 5] = ["core", "iter", "traits", "iterator", "Iterator"]; #[cfg(feature = "internal-lints")] +pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; +#[cfg(feature = "internal-lints")] pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"]; pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"]; #[cfg(feature = "internal-lints")] @@ -148,8 +154,12 @@ pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "", "starts_wit #[cfg(feature = "internal-lints")] pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"]; #[cfg(feature = "internal-lints")] +pub const SYMBOL_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Symbol", "as_str"]; +#[cfg(feature = "internal-lints")] pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern"]; #[cfg(feature = "internal-lints")] +pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"]; +#[cfg(feature = "internal-lints")] pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"]; #[cfg(feature = "internal-lints")] pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; diff --git a/src/tools/clippy/clippy_lints/src/utils/sym.rs b/src/tools/clippy/clippy_lints/src/utils/sym_helper.rs similarity index 68% rename from src/tools/clippy/clippy_lints/src/utils/sym.rs rename to src/tools/clippy/clippy_lints/src/utils/sym_helper.rs index 273288c3d52c5..f47dc80ebade8 100644 --- a/src/tools/clippy/clippy_lints/src/utils/sym.rs +++ b/src/tools/clippy/clippy_lints/src/utils/sym_helper.rs @@ -1,4 +1,5 @@ #[macro_export] +/// Convenience wrapper around rustc's `Symbol::intern` macro_rules! sym { ($tt:tt) => { rustc_span::symbol::Symbol::intern(stringify!($tt)) diff --git a/src/tools/clippy/clippy_lints/src/utils/visitors.rs b/src/tools/clippy/clippy_lints/src/utils/visitors.rs index b769a18802b6b..ebf69df31ca41 100644 --- a/src/tools/clippy/clippy_lints/src/utils/visitors.rs +++ b/src/tools/clippy/clippy_lints/src/utils/visitors.rs @@ -107,7 +107,7 @@ where if let Some(el) = else_opt { self.visit_expr(el); } - } + }, hir::ExprKind::Match(cond, arms, _) => { self.inside_stmt(true).visit_expr(cond); for arm in arms { diff --git a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs new file mode 100644 index 0000000000000..e632a7e57ee87 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs @@ -0,0 +1,187 @@ +use crate::utils::{is_type_diagnostic_item, match_def_path, paths, snippet, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, Local, PatKind, QPath, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{symbol::sym, Span, Symbol}; +use std::convert::TryInto; + +declare_clippy_lint! { + /// **What it does:** Checks for calls to `push` immediately after creating a new `Vec`. + /// + /// **Why is this bad?** The `vec![]` macro is both more performant and easier to read than + /// multiple `push` calls. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let mut v = Vec::new(); + /// v.push(0); + /// ``` + /// Use instead: + /// ```rust + /// let v = vec![0]; + /// ``` + pub VEC_INIT_THEN_PUSH, + perf, + "`push` immediately after `Vec` creation" +} + +impl_lint_pass!(VecInitThenPush => [VEC_INIT_THEN_PUSH]); + +#[derive(Default)] +pub struct VecInitThenPush { + searcher: Option, +} + +#[derive(Clone, Copy)] +enum VecInitKind { + New, + WithCapacity(u64), +} +struct VecPushSearcher { + init: VecInitKind, + name: Symbol, + lhs_is_local: bool, + lhs_span: Span, + err_span: Span, + found: u64, +} +impl VecPushSearcher { + fn display_err(&self, cx: &LateContext<'_>) { + match self.init { + _ if self.found == 0 => return, + VecInitKind::WithCapacity(x) if x > self.found => return, + _ => (), + }; + + let mut s = if self.lhs_is_local { + String::from("let ") + } else { + String::new() + }; + s.push_str(&snippet(cx, self.lhs_span, "..")); + s.push_str(" = vec![..];"); + + span_lint_and_sugg( + cx, + VEC_INIT_THEN_PUSH, + self.err_span, + "calls to `push` immediately after creation", + "consider using the `vec![]` macro", + s, + Applicability::HasPlaceholders, + ); + } +} + +impl LateLintPass<'_> for VecInitThenPush { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { + self.searcher = None; + if_chain! { + if !in_external_macro(cx.sess(), local.span); + if let Some(init) = local.init; + if let PatKind::Binding(BindingAnnotation::Mutable, _, ident, None) = local.pat.kind; + if let Some(init_kind) = get_vec_init_kind(cx, init); + then { + self.searcher = Some(VecPushSearcher { + init: init_kind, + name: ident.name, + lhs_is_local: true, + lhs_span: local.ty.map_or(local.pat.span, |t| local.pat.span.to(t.span)), + err_span: local.span, + found: 0, + }); + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if self.searcher.is_none() { + if_chain! { + if !in_external_macro(cx.sess(), expr.span); + if let ExprKind::Assign(left, right, _) = expr.kind; + if let ExprKind::Path(QPath::Resolved(_, path)) = left.kind; + if let Some(name) = path.segments.get(0); + if let Some(init_kind) = get_vec_init_kind(cx, right); + then { + self.searcher = Some(VecPushSearcher { + init: init_kind, + name: name.ident.name, + lhs_is_local: false, + lhs_span: left.span, + err_span: expr.span, + found: 0, + }); + } + } + } + } + + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + if let Some(searcher) = self.searcher.take() { + if_chain! { + if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind; + if let ExprKind::MethodCall(path, _, [self_arg, _], _) = expr.kind; + if path.ident.name.as_str() == "push"; + if let ExprKind::Path(QPath::Resolved(_, self_path)) = self_arg.kind; + if let [self_name] = self_path.segments; + if self_name.ident.name == searcher.name; + then { + self.searcher = Some(VecPushSearcher { + found: searcher.found + 1, + err_span: searcher.err_span.to(stmt.span), + .. searcher + }); + } else { + searcher.display_err(cx); + } + } + } + } + + fn check_block_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx Block<'tcx>) { + if let Some(searcher) = self.searcher.take() { + searcher.display_err(cx); + } + } +} + +fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option { + if let ExprKind::Call(func, args) = expr.kind { + match func.kind { + ExprKind::Path(QPath::TypeRelative(ty, name)) + if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::vec_type) => + { + if name.ident.name == sym::new { + return Some(VecInitKind::New); + } else if name.ident.name.as_str() == "with_capacity" { + return args.get(0).and_then(|arg| { + if_chain! { + if let ExprKind::Lit(lit) = &arg.kind; + if let LitKind::Int(num, _) = lit.node; + then { + Some(VecInitKind::WithCapacity(num.try_into().ok()?)) + } else { + None + } + } + }); + } + } + ExprKind::Path(QPath::Resolved(_, path)) + if match_def_path(cx, path.res.opt_def_id()?, &paths::DEFAULT_TRAIT_METHOD) + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::vec_type) => + { + return Some(VecInitKind::New); + } + _ => (), + } + } + None +} diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs index 5683a71efea4e..10005a7fc81ed 100644 --- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs +++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs @@ -7,7 +7,8 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::BytePos; +use rustc_span::symbol::kw; +use rustc_span::{sym, BytePos}; declare_clippy_lint! { /// **What it does:** Checks for `use Enum::*`. @@ -198,12 +199,12 @@ impl WildcardImports { // Allow "...prelude::..::*" imports. // Many crates have a prelude, and it is imported as a glob by design. fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool { - segments.iter().any(|ps| ps.ident.as_str() == "prelude") + segments.iter().any(|ps| ps.ident.name == sym::prelude) } // Allow "super::*" imports in tests. fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool { - segments.len() == 1 && segments[0].ident.as_str() == "super" + segments.len() == 1 && segments[0].ident.name == kw::Super } fn is_test_module_or_function(item: &Item<'_>) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index 337f7a229b906..af324f831dfa2 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -10,7 +10,8 @@ use rustc_lexer::unescape::{self, EscapeError}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_parse::parser; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{sym, BytePos, Span, Symbol}; +use rustc_span::symbol::kw; +use rustc_span::{sym, BytePos, Span}; declare_clippy_lint! { /// **What it does:** This lint warns when you use `println!("")` to @@ -301,7 +302,7 @@ impl EarlyLintPass for Write { } } else if mac.path == sym!(writeln) { if let (Some(fmt_str), expr) = self.check_tts(cx, mac.args.inner_tokens(), true) { - if fmt_str.symbol == Symbol::intern("") { + if fmt_str.symbol == kw::Empty { let mut applicability = Applicability::MachineApplicable; // FIXME: remove this `#[allow(...)]` once the issue #5822 gets fixed #[allow(clippy::option_if_let_else)] @@ -484,7 +485,7 @@ impl Write { fn lint_println_empty_string(&self, cx: &EarlyContext<'_>, mac: &MacCall) { if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { - if fmt_str.symbol == Symbol::intern("") { + if fmt_str.symbol == kw::Empty { let name = mac.path.segments[0].ident.name; span_lint_and_sugg( cx, diff --git a/src/tools/clippy/doc/adding_lints.md b/src/tools/clippy/doc/adding_lints.md index 60dfdb76650a1..1a7a30c61be5b 100644 --- a/src/tools/clippy/doc/adding_lints.md +++ b/src/tools/clippy/doc/adding_lints.md @@ -147,10 +147,14 @@ add `// edition:2018` at the top of the test file (note that it's space-sensitiv Manually testing against an example file can be useful if you have added some `println!`s and the test suite output becomes unreadable. To try Clippy with -your local modifications, run `env CLIPPY_TESTS=true cargo run --bin -clippy-driver -- -L ./target/debug input.rs` from the working copy root. +your local modifications, run -With tests in place, let's have a look at implementing our lint now. +``` +env __CLIPPY_INTERNAL_TESTS=true cargo run --bin clippy-driver -- -L ./target/debug input.rs +``` + +from the working copy root. With tests in place, let's have a look at +implementing our lint now. ## Lint declaration diff --git a/src/tools/clippy/doc/roadmap-2021.md b/src/tools/clippy/doc/roadmap-2021.md new file mode 100644 index 0000000000000..fe8b080f56f2b --- /dev/null +++ b/src/tools/clippy/doc/roadmap-2021.md @@ -0,0 +1,235 @@ +# Roadmap 2021 + +# Summary + +This Roadmap lays out the plans for Clippy in 2021: + +- Improving usability and reliability +- Improving experience of contributors and maintainers +- Develop and specify processes + +Members of the Clippy team will be assigned tasks from one or more of these +topics. The team member is then responsible to complete the assigned tasks. This +can either be done by implementing them or by providing mentorship to interested +contributors. + +# Motivation + +With the ongoing growth of the Rust language and with that of the whole +ecosystem, also Clippy gets more and more users and contributors. This is good +for the project, but also brings challenges along. Some of these challenges are: + +- More issues about reliability or usability are popping up +- Traffic is hard to handle for a small team +- Bigger projects don't get completed due to the lack of processes and/or time + of the team members + +Additionally, according to the [Rust Roadmap 2021], clear processes should be +defined by every team and unified across teams. This Roadmap is the first step +towards this. + +[Rust Roadmap 2021]: https://github.com/rust-lang/rfcs/pull/3037 + +# Explanation + +This section will explain the things that should be done in 2021. It is +important to note, that this document focuses on the "What?", not the "How?". +The later will be addressed in follow-up tracking issue, with an assigned team +member. + +The following is split up in two major sections. The first section covers the +user facing plans, the second section the internal plans. + +## User Facing + +Clippy should be as pleasant to use and configure as possible. This section +covers plans that should be implemented to improve the situation of Clippy in +this regard. + +### Usability + +In the following, plans to improve the usability are covered. + +#### No Output After `cargo check` + +Currently when `cargo clippy` is run after `cargo check`, it does not produce +any output. This is especially problematic since `rust-analyzer` is on the rise +and it uses `cargo check` for checking code. A fix is already implemented, but +it still has to be pushed over the finish line. This also includes the +stabilization of the `cargo clippy --fix` command or the support of multi-span +suggestions in `rustfix`. + +- [#4612](https://github.com/rust-lang/rust-clippy/issues/4612) + +#### `lints.toml` Configuration + +This is something that comes up every now and then: a reusable configuration +file, where lint levels can be defined. Discussions about this often lead to +nothing specific or to "we need an RFC for this". And this is exactly what needs +to be done. Get together with the cargo team and write an RFC and implement such +a configuration file somehow and somewhere. + +- [#3164](https://github.com/rust-lang/rust-clippy/issues/3164) +- [cargo#5034](https://github.com/rust-lang/cargo/issues/5034) +- [IRLO](https://internals.rust-lang.org/t/proposal-cargo-lint-configuration/9135/8) + +#### Lint Groups + +There are more and more issues about managing lints in Clippy popping up. Lints +are hard to implement with a guarantee of no/few false positives (FPs). One way +to address this might be to introduce more lint groups to give users the ability +to better manage lints, or improve the process of classifying lints, so that +disabling lints due to FPs becomes rare. It is important to note, that Clippy +lints are less conservative than `rustc` lints, which won't change in the +future. + +- [#5537](https://github.com/rust-lang/rust-clippy/issues/5537) +- [#6366](https://github.com/rust-lang/rust-clippy/issues/6366) + +### Reliability + +In the following, plans to improve the reliability are covered. + +#### False Positive Rate + +In the worst case, new lints are only available in nightly for 2 weeks, before +hitting beta and ultimately stable. This and the fact that fewer people use +nightly Rust nowadays makes it more probable that a lint with many FPs hits +stable. This leads to annoyed users, that will disable these new lints in the +best case and to more annoyed users, that will stop using Clippy in the worst. +A process should be developed and implemented to prevent this from happening. + +- [#6429](https://github.com/rust-lang/rust-clippy/issues/6429) + +## Internal + +(The end of) 2020 has shown, that Clippy has to think about the available +resources, especially regarding management and maintenance of the project. This +section address issues affecting team members and contributors. + +### Management + +In 2020 Clippy achieved over 1000 open issues with regularly between 25-35 open +PRs. This is simultaneously a win and a loss. More issues and PRs means more +people are interested in Clippy and in contributing to it. On the other hand, it +means for team members more work and for contributors longer wait times for +reviews. The following will describe plans how to improve the situation for both +team members and contributors. + +#### Clear Expectations for Team Members + +According to the [Rust Roadmap 2021], a document specifying what it means to be +a member of the team should be produced. This should not put more pressure on +the team members, but rather help them and interested folks to know what the +expectations are. With this it should also be easier to recruit new team members +and may encourage people to get in touch, if they're interested to join. + +#### Scaling up the Team + +More people means less work for each individual. Together with the document +about expectations for team members, a document defining the process of how to +join the team should be produced. This can also increase the stability of the +team, in case of current members dropping out (temporarily). There can also be +different roles in the team, like people triaging vs. people reviewing. + +#### Regular Meetings + +Other teams have regular meetings. Clippy is big enough that it might be worth +to also do them. Especially if more people join the team, this can be important +for sync-ups. Besides the asynchronous communication, that works well for +working on separate lints, a meeting adds a synchronous alternative at a known +time. This is especially helpful if there are bigger things that need to be +discussed (like the projects in this roadmap). For starters bi-weekly meetings +before Rust syncs might make sense. + +#### Triaging + +To get a handle on the influx of open issues, a process for triaging issues and +PRs should be developed. Officially, Clippy follows the Rust triage process, but +currently no one enforces it. This can be improved by sharing triage teams +across projects or by implementing dashboards / tools which simplify triaging. + +### Development + +Improving the developer and contributor experience is something the Clippy team +works on regularly. Though, some things might need special attention and +planing. These topics are listed in the following. + +#### Process for New and Existing Lints + +As already mentioned above, classifying new lints gets quite hard, because the +probability of a buggy lint getting into stable is quite high. A process should +be implemented on how to classify lints. In addition, a test system should be +developed to find out which lints are currently problematic in real world code +to fix or disable them. + +- [#6429 (comment)](https://github.com/rust-lang/rust-clippy/issues/6429#issuecomment-741056379) +- [#6429 (comment)](https://github.com/rust-lang/rust-clippy/issues/6429#issuecomment-741153345) + +#### Processes + +Related to the point before, a process for suggesting and discussing major +changes should be implemented. It's also not clearly defined when a lint should +be enabled or disabled by default. This can also be improved by the test system +mentioned above. + +#### Dev-Tools + +There's already `cargo dev` which makes Clippy development easier and more +pleasant. This can still be expanded, so that it covers more areas of the +development process. + +- [#5394](https://github.com/rust-lang/rust-clippy/issues/5394) + +#### Contributor Guide + +Similar to a Clippy Book, which describes how to use Clippy, a book about how to +contribute to Clippy might be helpful for new and existing contributors. There's +already the `doc` directory in the Clippy repo, this can be turned into a +`mdbook`. + +#### `rustc` integration + +Recently Clippy was integrated with `git subtree` into the `rust-lang/rust` +repository. This made syncing between the two repositories easier. A +`#[non_exhaustive]` list of things that still can be improved is: + +1. Use the same `rustfmt` version and configuration as `rustc`. +2. Make `cargo dev` work in the Rust repo, just as it works in the Clippy repo. + E.g. `cargo dev bless` or `cargo dev update_lints`. And even add more things + to it that might be useful for the Rust repo, e.g. `cargo dev deprecate`. +3. Easier sync process. The `subtree` situation is not ideal. + +## Prioritization + +The most pressing issues for users of Clippy are of course the user facing +issues. So there should be a priority on those issues, but without losing track +of the internal issues listed in this document. + +Getting the FP rate of warn/deny-by-default lints under control should have the +highest priority. Other user facing issues should also get a high priority, but +shouldn't be in the way of addressing internal issues. + +To better manage the upcoming projects, the basic internal processes, like +meetings, tracking issues and documentation, should be established as soon as +possible. They might even be necessary to properly manage the projects, +regarding the user facing issues. + +# Prior Art + +## Rust Roadmap + +Rust's roadmap process was established by [RFC 1728] in 2016. Since then every +year a roadmap was published, that defined the bigger plans for the coming +years. This years roadmap can be found [here][Rust Roadmap 2021]. + +[RFC 1728]: https://rust-lang.github.io/rfcs/1728-north-star.html + +# Drawbacks + +## Big Roadmap + +This roadmap is pretty big and not all items listed in this document might be +addressed during 2021. Because this is the first roadmap for Clippy, having open +tasks at the end of 2021 is fine, but they should be revisited in the 2022 +roadmap. diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index c579beeae89be..72935072f8cdd 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-01-02" +channel = "nightly-2021-01-15" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index e490ee54c0be0..f5f6c09ed8e94 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -298,7 +298,7 @@ pub fn main() { // - IF Clippy is run on the main crate, not on deps (`!cap_lints_allow`) THEN // - IF `--no-deps` is not set (`!no_deps`) OR // - IF `--no-deps` is set and Clippy is run on the specified primary package - let clippy_tests_set = env::var("CLIPPY_TESTS").map_or(false, |val| val == "true"); + let clippy_tests_set = env::var("__CLIPPY_INTERNAL_TESTS").map_or(false, |val| val == "true"); let cap_lints_allow = arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_some(); let in_primary_package = env::var("CARGO_PRIMARY_PACKAGE").is_ok(); diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs index ec3af94b9ca91..ea800336ef550 100644 --- a/src/tools/clippy/tests/compile-test.rs +++ b/src/tools/clippy/tests/compile-test.rs @@ -254,7 +254,7 @@ fn run_ui_cargo(config: &mut compiletest::Config) { fn prepare_env() { set_var("CLIPPY_DISABLE_DOCS_LINKS", "true"); - set_var("CLIPPY_TESTS", "true"); + set_var("__CLIPPY_INTERNAL_TESTS", "true"); //set_var("RUST_BACKTRACE", "0"); } diff --git a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed index c6b84d2ef650b..9ab845a573aca 100644 --- a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed +++ b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed @@ -14,13 +14,16 @@ macro_rules! sym { fn main() { // Direct use of Symbol::intern - let _ = rustc_span::symbol::sym::f32; + let _ = rustc_span::sym::f32; // Using a sym macro - let _ = rustc_span::symbol::sym::f32; + let _ = rustc_span::sym::f32; // Correct suggestion when symbol isn't stringified constant name - let _ = rustc_span::symbol::sym::proc_dash_macro; + let _ = rustc_span::sym::proc_dash_macro; + + // interning a keyword + let _ = rustc_span::symbol::kw::SelfLower; // Interning a symbol that is not defined let _ = Symbol::intern("xyz123"); diff --git a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs index 9ec82d4ad0bae..a58e182971d73 100644 --- a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs +++ b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs @@ -22,6 +22,9 @@ fn main() { // Correct suggestion when symbol isn't stringified constant name let _ = Symbol::intern("proc-macro"); + // interning a keyword + let _ = Symbol::intern("self"); + // Interning a symbol that is not defined let _ = Symbol::intern("xyz123"); let _ = sym!(xyz123); diff --git a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr index 74b906c8a5797..50c1c268eb132 100644 --- a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr +++ b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr @@ -2,7 +2,7 @@ error: interning a defined symbol --> $DIR/interning_defined_symbol.rs:17:13 | LL | let _ = Symbol::intern("f32"); - | ^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::sym::f32` + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::f32` | note: the lint level is defined here --> $DIR/interning_defined_symbol.rs:2:9 @@ -15,13 +15,19 @@ error: interning a defined symbol --> $DIR/interning_defined_symbol.rs:20:13 | LL | let _ = sym!(f32); - | ^^^^^^^^^ help: try: `rustc_span::symbol::sym::f32` + | ^^^^^^^^^ help: try: `rustc_span::sym::f32` error: interning a defined symbol --> $DIR/interning_defined_symbol.rs:23:13 | LL | let _ = Symbol::intern("proc-macro"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::sym::proc_dash_macro` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::proc_dash_macro` -error: aborting due to 3 previous errors +error: interning a defined symbol + --> $DIR/interning_defined_symbol.rs:26:13 + | +LL | let _ = Symbol::intern("self"); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::kw::SelfLower` + +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed new file mode 100644 index 0000000000000..2ec0efe4c10a5 --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed @@ -0,0 +1,16 @@ +// run-rustfix +#![feature(rustc_private)] +#![deny(clippy::internal)] +#![allow(clippy::unnecessary_operation, unused_must_use)] + +extern crate rustc_span; + +use rustc_span::symbol::{Ident, Symbol}; + +fn main() { + Symbol::intern("foo") == rustc_span::sym::clippy; + Symbol::intern("foo") == rustc_span::symbol::kw::SelfLower; + Symbol::intern("foo") != rustc_span::symbol::kw::SelfUpper; + Ident::invalid().name == rustc_span::sym::clippy; + rustc_span::sym::clippy == Ident::invalid().name; +} diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.rs b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.rs new file mode 100644 index 0000000000000..87e1b3a2ee76a --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.rs @@ -0,0 +1,16 @@ +// run-rustfix +#![feature(rustc_private)] +#![deny(clippy::internal)] +#![allow(clippy::unnecessary_operation, unused_must_use)] + +extern crate rustc_span; + +use rustc_span::symbol::{Ident, Symbol}; + +fn main() { + Symbol::intern("foo").as_str() == "clippy"; + Symbol::intern("foo").to_string() == "self"; + Symbol::intern("foo").to_ident_string() != "Self"; + &*Ident::invalid().as_str() == "clippy"; + "clippy" == Ident::invalid().to_string(); +} diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr new file mode 100644 index 0000000000000..b1284b7c8ffd0 --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr @@ -0,0 +1,39 @@ +error: unnecessary `Symbol` to string conversion + --> $DIR/unnecessary_symbol_str.rs:11:5 + | +LL | Symbol::intern("foo").as_str() == "clippy"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::sym::clippy` + | +note: the lint level is defined here + --> $DIR/unnecessary_symbol_str.rs:3:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::unnecessary_symbol_str)]` implied by `#[deny(clippy::internal)]` + +error: unnecessary `Symbol` to string conversion + --> $DIR/unnecessary_symbol_str.rs:12:5 + | +LL | Symbol::intern("foo").to_string() == "self"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::symbol::kw::SelfLower` + +error: unnecessary `Symbol` to string conversion + --> $DIR/unnecessary_symbol_str.rs:13:5 + | +LL | Symbol::intern("foo").to_ident_string() != "Self"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") != rustc_span::symbol::kw::SelfUpper` + +error: unnecessary `Symbol` to string conversion + --> $DIR/unnecessary_symbol_str.rs:14:5 + | +LL | &*Ident::invalid().as_str() == "clippy"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Ident::invalid().name == rustc_span::sym::clippy` + +error: unnecessary `Symbol` to string conversion + --> $DIR/unnecessary_symbol_str.rs:15:5 + | +LL | "clippy" == Ident::invalid().to_string(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::clippy == Ident::invalid().name` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs index 1832482346820..d6ecd8568ce78 100644 --- a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs +++ b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs @@ -94,3 +94,19 @@ macro_rules! large_enum_variant { } }; } + +#[macro_export] +macro_rules! field_reassign_with_default { + () => { + #[derive(Default)] + struct A { + pub i: i32, + pub j: i64, + } + fn lint() { + let mut a: A = Default::default(); + a.i = 42; + a; + } + }; +} diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs index 7c4e4a145512f..24891682d368d 100644 --- a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs +++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs @@ -4,6 +4,7 @@ #![crate_type = "proc-macro"] #![feature(repr128, proc_macro_quote)] #![allow(incomplete_features)] +#![allow(clippy::field_reassign_with_default)] #![allow(clippy::eq_op)] extern crate proc_macro; @@ -23,3 +24,20 @@ pub fn derive(_: TokenStream) -> TokenStream { }; output } + +#[proc_macro_derive(FieldReassignWithDefault)] +pub fn derive_foo(_input: TokenStream) -> TokenStream { + quote! { + #[derive(Default)] + struct A { + pub i: i32, + pub j: i64, + } + #[automatically_derived] + fn lint() { + let mut a: A = Default::default(); + a.i = 42; + a; + } + } +} diff --git a/src/tools/clippy/tests/ui/cast_alignment.rs b/src/tools/clippy/tests/ui/cast_alignment.rs index 4c08935639f1f..d011e84b115a7 100644 --- a/src/tools/clippy/tests/ui/cast_alignment.rs +++ b/src/tools/clippy/tests/ui/cast_alignment.rs @@ -12,6 +12,10 @@ fn main() { (&1u8 as *const u8) as *const u16; (&mut 1u8 as *mut u8) as *mut u16; + // cast to more-strictly-aligned type, but with the `pointer::cast` function. + (&1u8 as *const u8).cast::(); + (&mut 1u8 as *mut u8).cast::(); + /* These should be ok */ // not a pointer type diff --git a/src/tools/clippy/tests/ui/cast_alignment.stderr b/src/tools/clippy/tests/ui/cast_alignment.stderr index 79219f86155a4..7998b787b91fb 100644 --- a/src/tools/clippy/tests/ui/cast_alignment.stderr +++ b/src/tools/clippy/tests/ui/cast_alignment.stderr @@ -12,5 +12,17 @@ error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 LL | (&mut 1u8 as *mut u8) as *mut u16; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: casting from `*const u8` to a more-strictly-aligned pointer (`*const u16`) (1 < 2 bytes) + --> $DIR/cast_alignment.rs:16:5 + | +LL | (&1u8 as *const u8).cast::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 < 2 bytes) + --> $DIR/cast_alignment.rs:17:5 + | +LL | (&mut 1u8 as *mut u8).cast::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/clone_on_copy.fixed b/src/tools/clippy/tests/ui/clone_on_copy.fixed index 1f0ca101757ec..d924625132eb0 100644 --- a/src/tools/clippy/tests/ui/clone_on_copy.fixed +++ b/src/tools/clippy/tests/ui/clone_on_copy.fixed @@ -5,7 +5,8 @@ clippy::redundant_clone, clippy::deref_addrof, clippy::no_effect, - clippy::unnecessary_operation + clippy::unnecessary_operation, + clippy::vec_init_then_push )] use std::cell::RefCell; diff --git a/src/tools/clippy/tests/ui/clone_on_copy.rs b/src/tools/clippy/tests/ui/clone_on_copy.rs index ca39a654b4fce..97f4946724458 100644 --- a/src/tools/clippy/tests/ui/clone_on_copy.rs +++ b/src/tools/clippy/tests/ui/clone_on_copy.rs @@ -5,7 +5,8 @@ clippy::redundant_clone, clippy::deref_addrof, clippy::no_effect, - clippy::unnecessary_operation + clippy::unnecessary_operation, + clippy::vec_init_then_push )] use std::cell::RefCell; diff --git a/src/tools/clippy/tests/ui/clone_on_copy.stderr b/src/tools/clippy/tests/ui/clone_on_copy.stderr index 14a700886a7bc..7a706884fb0e7 100644 --- a/src/tools/clippy/tests/ui/clone_on_copy.stderr +++ b/src/tools/clippy/tests/ui/clone_on_copy.stderr @@ -1,5 +1,5 @@ error: using `clone` on type `i32` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:22:5 + --> $DIR/clone_on_copy.rs:23:5 | LL | 42.clone(); | ^^^^^^^^^^ help: try removing the `clone` call: `42` @@ -7,25 +7,25 @@ LL | 42.clone(); = note: `-D clippy::clone-on-copy` implied by `-D warnings` error: using `clone` on type `i32` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:26:5 + --> $DIR/clone_on_copy.rs:27:5 | LL | (&42).clone(); | ^^^^^^^^^^^^^ help: try dereferencing it: `*(&42)` error: using `clone` on type `i32` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:29:5 + --> $DIR/clone_on_copy.rs:30:5 | LL | rc.borrow().clone(); | ^^^^^^^^^^^^^^^^^^^ help: try dereferencing it: `*rc.borrow()` error: using `clone` on type `char` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:35:14 + --> $DIR/clone_on_copy.rs:36:14 | LL | is_ascii('z'.clone()); | ^^^^^^^^^^^ help: try removing the `clone` call: `'z'` error: using `clone` on type `i32` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:39:14 + --> $DIR/clone_on_copy.rs:40:14 | LL | vec.push(42.clone()); | ^^^^^^^^^^ help: try removing the `clone` call: `42` diff --git a/src/tools/clippy/tests/ui/collapsible_else_if.fixed b/src/tools/clippy/tests/ui/collapsible_else_if.fixed index ce2a1c28c8a80..fa4bc30e933a2 100644 --- a/src/tools/clippy/tests/ui/collapsible_else_if.fixed +++ b/src/tools/clippy/tests/ui/collapsible_else_if.fixed @@ -3,6 +3,8 @@ #[rustfmt::skip] #[warn(clippy::collapsible_if)] +#[warn(clippy::collapsible_else_if)] + fn main() { let x = "hello"; let y = "world"; diff --git a/src/tools/clippy/tests/ui/collapsible_else_if.rs b/src/tools/clippy/tests/ui/collapsible_else_if.rs index 99c40b8d38eb9..bf6c1d1f894d7 100644 --- a/src/tools/clippy/tests/ui/collapsible_else_if.rs +++ b/src/tools/clippy/tests/ui/collapsible_else_if.rs @@ -3,6 +3,8 @@ #[rustfmt::skip] #[warn(clippy::collapsible_if)] +#[warn(clippy::collapsible_else_if)] + fn main() { let x = "hello"; let y = "world"; diff --git a/src/tools/clippy/tests/ui/collapsible_else_if.stderr b/src/tools/clippy/tests/ui/collapsible_else_if.stderr index 3d1c458879e58..ee3e11ae565d4 100644 --- a/src/tools/clippy/tests/ui/collapsible_else_if.stderr +++ b/src/tools/clippy/tests/ui/collapsible_else_if.stderr @@ -1,5 +1,5 @@ error: this `else { if .. }` block can be collapsed - --> $DIR/collapsible_else_if.rs:12:12 + --> $DIR/collapsible_else_if.rs:14:12 | LL | } else { | ____________^ @@ -9,7 +9,7 @@ LL | | } LL | | } | |_____^ | - = note: `-D clippy::collapsible-if` implied by `-D warnings` + = note: `-D clippy::collapsible-else-if` implied by `-D warnings` help: collapse nested if block | LL | } else if y == "world" { @@ -18,7 +18,7 @@ LL | } | error: this `else { if .. }` block can be collapsed - --> $DIR/collapsible_else_if.rs:20:12 + --> $DIR/collapsible_else_if.rs:22:12 | LL | } else { | ____________^ @@ -36,7 +36,7 @@ LL | } | error: this `else { if .. }` block can be collapsed - --> $DIR/collapsible_else_if.rs:28:12 + --> $DIR/collapsible_else_if.rs:30:12 | LL | } else { | ____________^ @@ -59,7 +59,7 @@ LL | } | error: this `else { if .. }` block can be collapsed - --> $DIR/collapsible_else_if.rs:39:12 + --> $DIR/collapsible_else_if.rs:41:12 | LL | } else { | ____________^ @@ -82,7 +82,7 @@ LL | } | error: this `else { if .. }` block can be collapsed - --> $DIR/collapsible_else_if.rs:50:12 + --> $DIR/collapsible_else_if.rs:52:12 | LL | } else { | ____________^ @@ -105,7 +105,7 @@ LL | } | error: this `else { if .. }` block can be collapsed - --> $DIR/collapsible_else_if.rs:61:12 + --> $DIR/collapsible_else_if.rs:63:12 | LL | } else { | ____________^ @@ -128,7 +128,7 @@ LL | } | error: this `else { if .. }` block can be collapsed - --> $DIR/collapsible_else_if.rs:72:12 + --> $DIR/collapsible_else_if.rs:74:12 | LL | } else { | ____________^ diff --git a/src/tools/clippy/tests/ui/empty_enum.rs b/src/tools/clippy/tests/ui/empty_enum.rs index 12428f29625c0..a2e5c13c45282 100644 --- a/src/tools/clippy/tests/ui/empty_enum.rs +++ b/src/tools/clippy/tests/ui/empty_enum.rs @@ -1,6 +1,7 @@ #![allow(dead_code)] #![warn(clippy::empty_enum)] - +// Enable never type to test empty enum lint +#![feature(never_type)] enum Empty {} fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_enum.stderr b/src/tools/clippy/tests/ui/empty_enum.stderr index 466dfbe7cee7a..7125e5f602b75 100644 --- a/src/tools/clippy/tests/ui/empty_enum.stderr +++ b/src/tools/clippy/tests/ui/empty_enum.stderr @@ -1,5 +1,5 @@ error: enum with no variants - --> $DIR/empty_enum.rs:4:1 + --> $DIR/empty_enum.rs:5:1 | LL | enum Empty {} | ^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/empty_enum_without_never_type.rs b/src/tools/clippy/tests/ui/empty_enum_without_never_type.rs new file mode 100644 index 0000000000000..386677352e29b --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_enum_without_never_type.rs @@ -0,0 +1,7 @@ +#![allow(dead_code)] +#![warn(clippy::empty_enum)] + +// `never_type` is not enabled; this test has no stderr file +enum Empty {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/escape_analysis.rs b/src/tools/clippy/tests/ui/escape_analysis.rs index 07004489610d0..d26f48fc68f85 100644 --- a/src/tools/clippy/tests/ui/escape_analysis.rs +++ b/src/tools/clippy/tests/ui/escape_analysis.rs @@ -182,3 +182,23 @@ pub extern "C" fn do_not_warn_me(_c_pointer: Box) -> () {} #[rustfmt::skip] // Forces rustfmt to not add ABI pub extern fn do_not_warn_me_no_abi(_c_pointer: Box) -> () {} + +// Issue #4804 - default implementation in trait +mod issue4804 { + trait DefaultTraitImplTest { + // don't warn on `self` + fn default_impl(self: Box) -> u32 { + 5 + } + + // warn on `x: Box` + fn default_impl_x(self: Box, x: Box) -> u32 { + 4 + } + } + + trait WarnTrait { + // warn on `x: Box` + fn foo(x: Box) {} + } +} diff --git a/src/tools/clippy/tests/ui/escape_analysis.stderr b/src/tools/clippy/tests/ui/escape_analysis.stderr index c86a769a3da4b..4a82b4419f997 100644 --- a/src/tools/clippy/tests/ui/escape_analysis.stderr +++ b/src/tools/clippy/tests/ui/escape_analysis.stderr @@ -12,5 +12,17 @@ error: local variable doesn't need to be boxed here LL | pub fn new(_needs_name: Box>) -> () {} | ^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: local variable doesn't need to be boxed here + --> $DIR/escape_analysis.rs:195:44 + | +LL | fn default_impl_x(self: Box, x: Box) -> u32 { + | ^ + +error: local variable doesn't need to be boxed here + --> $DIR/escape_analysis.rs:202:16 + | +LL | fn foo(x: Box) {} + | ^ + +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/field_reassign_with_default.rs b/src/tools/clippy/tests/ui/field_reassign_with_default.rs index 3e0921022b417..9fc208f5332a5 100644 --- a/src/tools/clippy/tests/ui/field_reassign_with_default.rs +++ b/src/tools/clippy/tests/ui/field_reassign_with_default.rs @@ -1,5 +1,18 @@ +// aux-build:proc_macro_derive.rs +// aux-build:macro_rules.rs + #![warn(clippy::field_reassign_with_default)] +#[macro_use] +extern crate proc_macro_derive; +#[macro_use] +extern crate macro_rules; + +// Don't lint on derives that derive `Default` +// See https://github.com/rust-lang/rust-clippy/issues/6545 +#[derive(FieldReassignWithDefault)] +struct DerivedStruct; + #[derive(Default)] struct A { i: i32, @@ -11,6 +24,11 @@ struct B { j: i64, } +#[derive(Default)] +struct C { + i: Vec, + j: i64, +} /// Implements .next() that returns a different number each time. struct SideEffect(i32); @@ -111,6 +129,13 @@ fn main() { // don't lint - some private fields let mut x = m::F::default(); x.a = 1; + + // don't expand macros in the suggestion (#6522) + let mut a: C = C::default(); + a.i = vec![1]; + + // Don't lint in external macros + field_reassign_with_default!(); } mod m { diff --git a/src/tools/clippy/tests/ui/field_reassign_with_default.stderr b/src/tools/clippy/tests/ui/field_reassign_with_default.stderr index 9a2bc778c3ff7..2f0f28f7bb724 100644 --- a/src/tools/clippy/tests/ui/field_reassign_with_default.stderr +++ b/src/tools/clippy/tests/ui/field_reassign_with_default.stderr @@ -1,75 +1,87 @@ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:30:5 + --> $DIR/field_reassign_with_default.rs:48:5 | LL | a.i = 42; | ^^^^^^^^^ | = note: `-D clippy::field-reassign-with-default` implied by `-D warnings` -note: consider initializing the variable with `A { i: 42, ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:29:5 +note: consider initializing the variable with `main::A { i: 42, ..Default::default() }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:47:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:70:5 + --> $DIR/field_reassign_with_default.rs:88:5 | LL | a.j = 43; | ^^^^^^^^^ | -note: consider initializing the variable with `A { j: 43, i: 42 }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:69:5 +note: consider initializing the variable with `main::A { j: 43, i: 42 }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:87:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:75:5 + --> $DIR/field_reassign_with_default.rs:93:5 | LL | a.i = 42; | ^^^^^^^^^ | -note: consider initializing the variable with `A { i: 42, j: 44 }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:74:5 +note: consider initializing the variable with `main::A { i: 42, j: 44 }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:92:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:81:5 + --> $DIR/field_reassign_with_default.rs:99:5 | LL | a.i = 42; | ^^^^^^^^^ | -note: consider initializing the variable with `A { i: 42, ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:80:5 +note: consider initializing the variable with `main::A { i: 42, ..Default::default() }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:98:5 | LL | let mut a = A::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:91:5 + --> $DIR/field_reassign_with_default.rs:109:5 | LL | a.i = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: consider initializing the variable with `A { i: Default::default(), ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:90:5 +note: consider initializing the variable with `main::A { i: Default::default(), ..Default::default() }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:108:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:95:5 + --> $DIR/field_reassign_with_default.rs:113:5 | LL | a.i = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: consider initializing the variable with `A { i: Default::default(), j: 45 }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:94:5 +note: consider initializing the variable with `main::A { i: Default::default(), j: 45 }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:112:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 6 previous errors +error: field assignment outside of initializer for an instance created with Default::default() + --> $DIR/field_reassign_with_default.rs:135:5 + | +LL | a.i = vec![1]; + | ^^^^^^^^^^^^^^ + | +note: consider initializing the variable with `C { i: vec![1], ..Default::default() }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:134:5 + | +LL | let mut a: C = C::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/from_over_into.stderr b/src/tools/clippy/tests/ui/from_over_into.stderr index 18f56f854329e..b101d2704fbda 100644 --- a/src/tools/clippy/tests/ui/from_over_into.stderr +++ b/src/tools/clippy/tests/ui/from_over_into.stderr @@ -1,12 +1,8 @@ error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true --> $DIR/from_over_into.rs:6:1 | -LL | / impl Into for String { -LL | | fn into(self) -> StringWrapper { -LL | | StringWrapper(self) -LL | | } -LL | | } - | |_^ +LL | impl Into for String { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::from-over-into` implied by `-D warnings` = help: consider to implement `From` instead diff --git a/src/tools/clippy/tests/ui/if_same_then_else2.rs b/src/tools/clippy/tests/ui/if_same_then_else2.rs index 8d54f75b5d19f..e83ce47e56308 100644 --- a/src/tools/clippy/tests/ui/if_same_then_else2.rs +++ b/src/tools/clippy/tests/ui/if_same_then_else2.rs @@ -1,6 +1,7 @@ #![warn(clippy::if_same_then_else)] #![allow( clippy::blacklisted_name, + clippy::collapsible_else_if, clippy::collapsible_if, clippy::ifs_same_cond, clippy::needless_return, diff --git a/src/tools/clippy/tests/ui/if_same_then_else2.stderr b/src/tools/clippy/tests/ui/if_same_then_else2.stderr index da2be6c8aa5ac..f98e30fa376fe 100644 --- a/src/tools/clippy/tests/ui/if_same_then_else2.stderr +++ b/src/tools/clippy/tests/ui/if_same_then_else2.stderr @@ -1,5 +1,5 @@ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:20:12 + --> $DIR/if_same_then_else2.rs:21:12 | LL | } else { | ____________^ @@ -13,7 +13,7 @@ LL | | } | = note: `-D clippy::if-same-then-else` implied by `-D warnings` note: same as this - --> $DIR/if_same_then_else2.rs:11:13 + --> $DIR/if_same_then_else2.rs:12:13 | LL | if true { | _____________^ @@ -26,7 +26,7 @@ LL | | } else { | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:34:12 + --> $DIR/if_same_then_else2.rs:35:12 | LL | } else { | ____________^ @@ -36,7 +36,7 @@ LL | | } | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:32:13 + --> $DIR/if_same_then_else2.rs:33:13 | LL | if true { | _____________^ @@ -45,7 +45,7 @@ LL | | } else { | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:41:12 + --> $DIR/if_same_then_else2.rs:42:12 | LL | } else { | ____________^ @@ -55,7 +55,7 @@ LL | | } | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:39:13 + --> $DIR/if_same_then_else2.rs:40:13 | LL | if true { | _____________^ @@ -64,7 +64,7 @@ LL | | } else { | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:91:12 + --> $DIR/if_same_then_else2.rs:92:12 | LL | } else { | ____________^ @@ -74,7 +74,7 @@ LL | | }; | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:89:21 + --> $DIR/if_same_then_else2.rs:90:21 | LL | let _ = if true { | _____________________^ @@ -83,7 +83,7 @@ LL | | } else { | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:98:12 + --> $DIR/if_same_then_else2.rs:99:12 | LL | } else { | ____________^ @@ -93,7 +93,7 @@ LL | | } | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:96:13 + --> $DIR/if_same_then_else2.rs:97:13 | LL | if true { | _____________^ @@ -102,7 +102,7 @@ LL | | } else { | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:123:12 + --> $DIR/if_same_then_else2.rs:124:12 | LL | } else { | ____________^ @@ -112,7 +112,7 @@ LL | | } | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:120:20 + --> $DIR/if_same_then_else2.rs:121:20 | LL | } else if true { | ____________________^ diff --git a/src/tools/clippy/tests/ui/needless_question_mark.fixed b/src/tools/clippy/tests/ui/needless_question_mark.fixed new file mode 100644 index 0000000000000..70218f3f041d8 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_question_mark.fixed @@ -0,0 +1,163 @@ +// run-rustfix + +#![warn(clippy::needless_question_mark)] +#![allow(clippy::needless_return, clippy::unnecessary_unwrap, dead_code, unused_must_use)] +#![feature(custom_inner_attributes)] + +struct TO { + magic: Option, +} + +struct TR { + magic: Result, +} + +fn simple_option_bad1(to: TO) -> Option { + // return as a statement + return to.magic; +} + +// formatting will add a semi-colon, which would make +// this identical to the test case above +#[rustfmt::skip] +fn simple_option_bad2(to: TO) -> Option { + // return as an expression + return to.magic +} + +fn simple_option_bad3(to: TO) -> Option { + // block value "return" + to.magic +} + +fn simple_option_bad4(to: Option) -> Option { + // single line closure + to.and_then(|t| t.magic) +} + +// formatting this will remove the block brackets, making +// this test identical to the one above +#[rustfmt::skip] +fn simple_option_bad5(to: Option) -> Option { + // closure with body + to.and_then(|t| { + t.magic + }) +} + +fn simple_result_bad1(tr: TR) -> Result { + return tr.magic; +} + +// formatting will add a semi-colon, which would make +// this identical to the test case above +#[rustfmt::skip] +fn simple_result_bad2(tr: TR) -> Result { + return tr.magic +} + +fn simple_result_bad3(tr: TR) -> Result { + tr.magic +} + +fn simple_result_bad4(tr: Result) -> Result { + tr.and_then(|t| t.magic) +} + +// formatting this will remove the block brackets, making +// this test identical to the one above +#[rustfmt::skip] +fn simple_result_bad5(tr: Result) -> Result { + tr.and_then(|t| { + t.magic + }) +} + +fn also_bad(tr: Result) -> Result { + if tr.is_ok() { + let t = tr.unwrap(); + return t.magic; + } + Err(false) +} + +fn false_positive_test(x: Result<(), U>) -> Result<(), T> +where + T: From, +{ + Ok(x?) +} + +fn main() {} + +mod question_mark_none { + #![clippy::msrv = "1.12.0"] + fn needless_question_mark_option() -> Option { + struct TO { + magic: Option, + } + let to = TO { magic: None }; + Some(to.magic?) // should not be triggered + } + + fn needless_question_mark_result() -> Result { + struct TO { + magic: Result, + } + let to = TO { magic: Ok(1_usize) }; + Ok(to.magic?) // should not be triggered + } + + fn main() { + needless_question_mark_option(); + needless_question_mark_result(); + } +} + +mod question_mark_result { + #![clippy::msrv = "1.21.0"] + fn needless_question_mark_option() -> Option { + struct TO { + magic: Option, + } + let to = TO { magic: None }; + Some(to.magic?) // should not be triggered + } + + fn needless_question_mark_result() -> Result { + struct TO { + magic: Result, + } + let to = TO { magic: Ok(1_usize) }; + to.magic // should be triggered + } + + fn main() { + needless_question_mark_option(); + needless_question_mark_result(); + } +} + +mod question_mark_both { + #![clippy::msrv = "1.22.0"] + fn needless_question_mark_option() -> Option { + struct TO { + magic: Option, + } + let to = TO { magic: None }; + to.magic // should be triggered + } + + fn needless_question_mark_result() -> Result { + struct TO { + magic: Result, + } + let to = TO { magic: Ok(1_usize) }; + to.magic // should be triggered + } + + fn main() { + needless_question_mark_option(); + needless_question_mark_result(); + } +} diff --git a/src/tools/clippy/tests/ui/needless_question_mark.rs b/src/tools/clippy/tests/ui/needless_question_mark.rs new file mode 100644 index 0000000000000..60ac2c8d72eac --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_question_mark.rs @@ -0,0 +1,163 @@ +// run-rustfix + +#![warn(clippy::needless_question_mark)] +#![allow(clippy::needless_return, clippy::unnecessary_unwrap, dead_code, unused_must_use)] +#![feature(custom_inner_attributes)] + +struct TO { + magic: Option, +} + +struct TR { + magic: Result, +} + +fn simple_option_bad1(to: TO) -> Option { + // return as a statement + return Some(to.magic?); +} + +// formatting will add a semi-colon, which would make +// this identical to the test case above +#[rustfmt::skip] +fn simple_option_bad2(to: TO) -> Option { + // return as an expression + return Some(to.magic?) +} + +fn simple_option_bad3(to: TO) -> Option { + // block value "return" + Some(to.magic?) +} + +fn simple_option_bad4(to: Option) -> Option { + // single line closure + to.and_then(|t| Some(t.magic?)) +} + +// formatting this will remove the block brackets, making +// this test identical to the one above +#[rustfmt::skip] +fn simple_option_bad5(to: Option) -> Option { + // closure with body + to.and_then(|t| { + Some(t.magic?) + }) +} + +fn simple_result_bad1(tr: TR) -> Result { + return Ok(tr.magic?); +} + +// formatting will add a semi-colon, which would make +// this identical to the test case above +#[rustfmt::skip] +fn simple_result_bad2(tr: TR) -> Result { + return Ok(tr.magic?) +} + +fn simple_result_bad3(tr: TR) -> Result { + Ok(tr.magic?) +} + +fn simple_result_bad4(tr: Result) -> Result { + tr.and_then(|t| Ok(t.magic?)) +} + +// formatting this will remove the block brackets, making +// this test identical to the one above +#[rustfmt::skip] +fn simple_result_bad5(tr: Result) -> Result { + tr.and_then(|t| { + Ok(t.magic?) + }) +} + +fn also_bad(tr: Result) -> Result { + if tr.is_ok() { + let t = tr.unwrap(); + return Ok(t.magic?); + } + Err(false) +} + +fn false_positive_test(x: Result<(), U>) -> Result<(), T> +where + T: From, +{ + Ok(x?) +} + +fn main() {} + +mod question_mark_none { + #![clippy::msrv = "1.12.0"] + fn needless_question_mark_option() -> Option { + struct TO { + magic: Option, + } + let to = TO { magic: None }; + Some(to.magic?) // should not be triggered + } + + fn needless_question_mark_result() -> Result { + struct TO { + magic: Result, + } + let to = TO { magic: Ok(1_usize) }; + Ok(to.magic?) // should not be triggered + } + + fn main() { + needless_question_mark_option(); + needless_question_mark_result(); + } +} + +mod question_mark_result { + #![clippy::msrv = "1.21.0"] + fn needless_question_mark_option() -> Option { + struct TO { + magic: Option, + } + let to = TO { magic: None }; + Some(to.magic?) // should not be triggered + } + + fn needless_question_mark_result() -> Result { + struct TO { + magic: Result, + } + let to = TO { magic: Ok(1_usize) }; + Ok(to.magic?) // should be triggered + } + + fn main() { + needless_question_mark_option(); + needless_question_mark_result(); + } +} + +mod question_mark_both { + #![clippy::msrv = "1.22.0"] + fn needless_question_mark_option() -> Option { + struct TO { + magic: Option, + } + let to = TO { magic: None }; + Some(to.magic?) // should be triggered + } + + fn needless_question_mark_result() -> Result { + struct TO { + magic: Result, + } + let to = TO { magic: Ok(1_usize) }; + Ok(to.magic?) // should be triggered + } + + fn main() { + needless_question_mark_option(); + needless_question_mark_result(); + } +} diff --git a/src/tools/clippy/tests/ui/needless_question_mark.stderr b/src/tools/clippy/tests/ui/needless_question_mark.stderr new file mode 100644 index 0000000000000..b4eb21882ece9 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_question_mark.stderr @@ -0,0 +1,88 @@ +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:17:12 + | +LL | return Some(to.magic?); + | ^^^^^^^^^^^^^^^ help: try: `to.magic` + | + = note: `-D clippy::needless-question-mark` implied by `-D warnings` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:25:12 + | +LL | return Some(to.magic?) + | ^^^^^^^^^^^^^^^ help: try: `to.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:30:5 + | +LL | Some(to.magic?) + | ^^^^^^^^^^^^^^^ help: try: `to.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:35:21 + | +LL | to.and_then(|t| Some(t.magic?)) + | ^^^^^^^^^^^^^^ help: try: `t.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:44:9 + | +LL | Some(t.magic?) + | ^^^^^^^^^^^^^^ help: try: `t.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:49:12 + | +LL | return Ok(tr.magic?); + | ^^^^^^^^^^^^^ help: try: `tr.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:56:12 + | +LL | return Ok(tr.magic?) + | ^^^^^^^^^^^^^ help: try: `tr.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:60:5 + | +LL | Ok(tr.magic?) + | ^^^^^^^^^^^^^ help: try: `tr.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:64:21 + | +LL | tr.and_then(|t| Ok(t.magic?)) + | ^^^^^^^^^^^^ help: try: `t.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:72:9 + | +LL | Ok(t.magic?) + | ^^^^^^^^^^^^ help: try: `t.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:79:16 + | +LL | return Ok(t.magic?); + | ^^^^^^^^^^^^ help: try: `t.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:132:9 + | +LL | Ok(to.magic?) // should be triggered + | ^^^^^^^^^^^^^ help: try: `to.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:148:9 + | +LL | Some(to.magic?) // should be triggered + | ^^^^^^^^^^^^^^^ help: try: `to.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:156:9 + | +LL | Ok(to.magic?) // should be triggered + | ^^^^^^^^^^^^^ help: try: `to.magic` + +error: aborting due to 14 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_return.fixed b/src/tools/clippy/tests/ui/needless_return.fixed index d849e093da7bb..86bfc5b4bb283 100644 --- a/src/tools/clippy/tests/ui/needless_return.fixed +++ b/src/tools/clippy/tests/ui/needless_return.fixed @@ -86,6 +86,21 @@ fn borrows_but_not_last(value: bool) -> String { } } +macro_rules! needed_return { + ($e:expr) => { + if $e > 3 { + return; + } + }; +} + +fn test_return_in_macro() { + // This will return and the macro below won't be executed. Removing the `return` from the macro + // will change semantics. + needed_return!(10); + needed_return!(0); +} + fn main() { let _ = test_end_of_fn(); let _ = test_no_semicolon(); diff --git a/src/tools/clippy/tests/ui/needless_return.rs b/src/tools/clippy/tests/ui/needless_return.rs index 29f2bd1852af0..51061370dfe74 100644 --- a/src/tools/clippy/tests/ui/needless_return.rs +++ b/src/tools/clippy/tests/ui/needless_return.rs @@ -86,6 +86,21 @@ fn borrows_but_not_last(value: bool) -> String { } } +macro_rules! needed_return { + ($e:expr) => { + if $e > 3 { + return; + } + }; +} + +fn test_return_in_macro() { + // This will return and the macro below won't be executed. Removing the `return` from the macro + // will change semantics. + needed_return!(10); + needed_return!(0); +} + fn main() { let _ = test_end_of_fn(); let _ = test_no_semicolon(); diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.fixed b/src/tools/clippy/tests/ui/ptr_as_ptr.fixed new file mode 100644 index 0000000000000..8346a9454f4ee --- /dev/null +++ b/src/tools/clippy/tests/ui/ptr_as_ptr.fixed @@ -0,0 +1,50 @@ +// run-rustfix + +#![warn(clippy::ptr_as_ptr)] +#![feature(custom_inner_attributes)] + +fn main() { + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + let _ = ptr.cast::(); + let _ = mut_ptr.cast::(); + + // Make sure the lint can handle the difference in their operator precedences. + unsafe { + let ptr_ptr: *const *const u32 = &ptr; + let _ = (*ptr_ptr).cast::(); + } + + // Changes in mutability. Do not lint this. + let _ = ptr as *mut i32; + let _ = mut_ptr as *const i32; + + // `pointer::cast` cannot perform unsized coercions unlike `as`. Do not lint this. + let ptr_of_array: *const [u32; 4] = &[1, 2, 3, 4]; + let _ = ptr_of_array as *const [u32]; + let _ = ptr_of_array as *const dyn std::fmt::Debug; + + // Ensure the lint doesn't produce unnecessary turbofish for inferred types. + let _: *const i32 = ptr.cast(); + let _: *mut i32 = mut_ptr.cast(); +} + +fn _msrv_1_37() { + #![clippy::msrv = "1.37"] + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + // `pointer::cast` was stabilized in 1.38. Do not lint this + let _ = ptr as *const i32; + let _ = mut_ptr as *mut i32; +} + +fn _msrv_1_38() { + #![clippy::msrv = "1.38"] + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + let _ = ptr.cast::(); + let _ = mut_ptr.cast::(); +} diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.rs b/src/tools/clippy/tests/ui/ptr_as_ptr.rs new file mode 100644 index 0000000000000..b68d4bc0aaca1 --- /dev/null +++ b/src/tools/clippy/tests/ui/ptr_as_ptr.rs @@ -0,0 +1,50 @@ +// run-rustfix + +#![warn(clippy::ptr_as_ptr)] +#![feature(custom_inner_attributes)] + +fn main() { + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + let _ = ptr as *const i32; + let _ = mut_ptr as *mut i32; + + // Make sure the lint can handle the difference in their operator precedences. + unsafe { + let ptr_ptr: *const *const u32 = &ptr; + let _ = *ptr_ptr as *const i32; + } + + // Changes in mutability. Do not lint this. + let _ = ptr as *mut i32; + let _ = mut_ptr as *const i32; + + // `pointer::cast` cannot perform unsized coercions unlike `as`. Do not lint this. + let ptr_of_array: *const [u32; 4] = &[1, 2, 3, 4]; + let _ = ptr_of_array as *const [u32]; + let _ = ptr_of_array as *const dyn std::fmt::Debug; + + // Ensure the lint doesn't produce unnecessary turbofish for inferred types. + let _: *const i32 = ptr as *const _; + let _: *mut i32 = mut_ptr as _; +} + +fn _msrv_1_37() { + #![clippy::msrv = "1.37"] + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + // `pointer::cast` was stabilized in 1.38. Do not lint this + let _ = ptr as *const i32; + let _ = mut_ptr as *mut i32; +} + +fn _msrv_1_38() { + #![clippy::msrv = "1.38"] + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + let _ = ptr as *const i32; + let _ = mut_ptr as *mut i32; +} diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.stderr b/src/tools/clippy/tests/ui/ptr_as_ptr.stderr new file mode 100644 index 0000000000000..854906dc111df --- /dev/null +++ b/src/tools/clippy/tests/ui/ptr_as_ptr.stderr @@ -0,0 +1,46 @@ +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:10:13 + | +LL | let _ = ptr as *const i32; + | ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::()` + | + = note: `-D clippy::ptr-as-ptr` implied by `-D warnings` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:11:13 + | +LL | let _ = mut_ptr as *mut i32; + | ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:16:17 + | +LL | let _ = *ptr_ptr as *const i32; + | ^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `(*ptr_ptr).cast::()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:29:25 + | +LL | let _: *const i32 = ptr as *const _; + | ^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:30:23 + | +LL | let _: *mut i32 = mut_ptr as _; + | ^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:48:13 + | +LL | let _ = ptr as *const i32; + | ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:49:13 + | +LL | let _ = mut_ptr as *mut i32; + | ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::()` + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/try_err.fixed b/src/tools/clippy/tests/ui/try_err.fixed index 652b611208b73..5b96bb59c5f18 100644 --- a/src/tools/clippy/tests/ui/try_err.fixed +++ b/src/tools/clippy/tests/ui/try_err.fixed @@ -2,7 +2,7 @@ // aux-build:macro_rules.rs #![deny(clippy::try_err)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::unnecessary_wraps, clippy::needless_question_mark)] #[macro_use] extern crate macro_rules; diff --git a/src/tools/clippy/tests/ui/try_err.rs b/src/tools/clippy/tests/ui/try_err.rs index 6bd479657b70b..f220d697d2cd7 100644 --- a/src/tools/clippy/tests/ui/try_err.rs +++ b/src/tools/clippy/tests/ui/try_err.rs @@ -2,7 +2,7 @@ // aux-build:macro_rules.rs #![deny(clippy::try_err)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::unnecessary_wraps, clippy::needless_question_mark)] #[macro_use] extern crate macro_rules; diff --git a/src/tools/clippy/tests/ui/unit_arg.rs b/src/tools/clippy/tests/ui/unit_arg.rs index 9ad16d365094e..b6a7bc5a1cc95 100644 --- a/src/tools/clippy/tests/ui/unit_arg.rs +++ b/src/tools/clippy/tests/ui/unit_arg.rs @@ -5,7 +5,8 @@ unused_variables, clippy::unused_unit, clippy::unnecessary_wraps, - clippy::or_fun_call + clippy::or_fun_call, + clippy::needless_question_mark )] use std::fmt::Debug; diff --git a/src/tools/clippy/tests/ui/unit_arg.stderr b/src/tools/clippy/tests/ui/unit_arg.stderr index c3a839a9bf812..094cff8c98591 100644 --- a/src/tools/clippy/tests/ui/unit_arg.stderr +++ b/src/tools/clippy/tests/ui/unit_arg.stderr @@ -1,5 +1,5 @@ error: passing a unit value to a function - --> $DIR/unit_arg.rs:30:5 + --> $DIR/unit_arg.rs:31:5 | LL | / foo({ LL | | 1; @@ -20,7 +20,7 @@ LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:33:5 + --> $DIR/unit_arg.rs:34:5 | LL | foo(foo(1)); | ^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:34:5 + --> $DIR/unit_arg.rs:35:5 | LL | / foo({ LL | | foo(1); @@ -54,7 +54,7 @@ LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:39:5 + --> $DIR/unit_arg.rs:40:5 | LL | / b.bar({ LL | | 1; @@ -74,7 +74,7 @@ LL | b.bar(()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:42:5 + --> $DIR/unit_arg.rs:43:5 | LL | taking_multiple_units(foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -87,7 +87,7 @@ LL | taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:43:5 + --> $DIR/unit_arg.rs:44:5 | LL | / taking_multiple_units(foo(0), { LL | | foo(1); @@ -110,7 +110,7 @@ LL | taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:47:5 + --> $DIR/unit_arg.rs:48:5 | LL | / taking_multiple_units( LL | | { @@ -140,7 +140,7 @@ LL | foo(2); ... error: passing a unit value to a function - --> $DIR/unit_arg.rs:58:13 + --> $DIR/unit_arg.rs:59:13 | LL | None.or(Some(foo(2))); | ^^^^^^^^^^^^ @@ -154,7 +154,7 @@ LL | }); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:61:5 + --> $DIR/unit_arg.rs:62:5 | LL | foo(foo(())) | ^^^^^^^^^^^^ @@ -166,7 +166,7 @@ LL | foo(()) | error: passing a unit value to a function - --> $DIR/unit_arg.rs:94:5 + --> $DIR/unit_arg.rs:95:5 | LL | Some(foo(1)) | ^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/vec_init_then_push.rs b/src/tools/clippy/tests/ui/vec_init_then_push.rs new file mode 100644 index 0000000000000..642ce5040096e --- /dev/null +++ b/src/tools/clippy/tests/ui/vec_init_then_push.rs @@ -0,0 +1,21 @@ +#![allow(unused_variables)] +#![warn(clippy::vec_init_then_push)] + +fn main() { + let mut def_err: Vec = Default::default(); + def_err.push(0); + + let mut new_err = Vec::::new(); + new_err.push(1); + + let mut cap_err = Vec::with_capacity(2); + cap_err.push(0); + cap_err.push(1); + cap_err.push(2); + + let mut cap_ok = Vec::with_capacity(10); + cap_ok.push(0); + + new_err = Vec::new(); + new_err.push(0); +} diff --git a/src/tools/clippy/tests/ui/vec_init_then_push.stderr b/src/tools/clippy/tests/ui/vec_init_then_push.stderr new file mode 100644 index 0000000000000..819ed47d09919 --- /dev/null +++ b/src/tools/clippy/tests/ui/vec_init_then_push.stderr @@ -0,0 +1,34 @@ +error: calls to `push` immediately after creation + --> $DIR/vec_init_then_push.rs:5:5 + | +LL | / let mut def_err: Vec = Default::default(); +LL | | def_err.push(0); + | |____________________^ help: consider using the `vec![]` macro: `let mut def_err: Vec = vec![..];` + | + = note: `-D clippy::vec-init-then-push` implied by `-D warnings` + +error: calls to `push` immediately after creation + --> $DIR/vec_init_then_push.rs:8:5 + | +LL | / let mut new_err = Vec::::new(); +LL | | new_err.push(1); + | |____________________^ help: consider using the `vec![]` macro: `let mut new_err = vec![..];` + +error: calls to `push` immediately after creation + --> $DIR/vec_init_then_push.rs:11:5 + | +LL | / let mut cap_err = Vec::with_capacity(2); +LL | | cap_err.push(0); +LL | | cap_err.push(1); +LL | | cap_err.push(2); + | |____________________^ help: consider using the `vec![]` macro: `let mut cap_err = vec![..];` + +error: calls to `push` immediately after creation + --> $DIR/vec_init_then_push.rs:19:5 + | +LL | / new_err = Vec::new(); +LL | | new_err.push(0); + | |____________________^ help: consider using the `vec![]` macro: `new_err = vec![..];` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/wrong_self_convention.rs b/src/tools/clippy/tests/ui/wrong_self_convention.rs index 5282eba74fd18..6cfc0fcb4cae4 100644 --- a/src/tools/clippy/tests/ui/wrong_self_convention.rs +++ b/src/tools/clippy/tests/ui/wrong_self_convention.rs @@ -94,7 +94,8 @@ mod issue6307 { trait T: Sized { fn as_i32(self) {} fn as_u32(&self) {} - fn into_i32(&self) {} + fn into_i32(self) {} + fn into_i32_ref(&self) {} fn into_u32(self) {} fn is_i32(self) {} fn is_u32(&self) {} @@ -117,7 +118,32 @@ mod issue6307 { trait U { fn as_i32(self); fn as_u32(&self); - fn into_i32(&self); + fn into_i32(self); + fn into_i32_ref(&self); + fn into_u32(self); + fn is_i32(self); + fn is_u32(&self); + fn to_i32(self); + fn to_u32(&self); + fn from_i32(self); + // check whether the lint can be allowed at the function level + #[allow(clippy::wrong_self_convention)] + fn from_cake(self); + + // test for false positives + fn as_(self); + fn into_(&self); + fn is_(self); + fn to_(self); + fn from_(self); + fn to_mut(&mut self); + } + + trait C: Copy { + fn as_i32(self); + fn as_u32(&self); + fn into_i32(self); + fn into_i32_ref(&self); fn into_u32(self); fn is_i32(self); fn is_u32(&self); diff --git a/src/tools/clippy/tests/ui/wrong_self_convention.stderr b/src/tools/clippy/tests/ui/wrong_self_convention.stderr index 86467eb0fc737..32bd9075bd5e1 100644 --- a/src/tools/clippy/tests/ui/wrong_self_convention.stderr +++ b/src/tools/clippy/tests/ui/wrong_self_convention.stderr @@ -79,58 +79,70 @@ LL | fn as_i32(self) {} | ^^^^ error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:97:21 + --> $DIR/wrong_self_convention.rs:98:25 | -LL | fn into_i32(&self) {} - | ^^^^^ +LL | fn into_i32_ref(&self) {} + | ^^^^^ error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:99:19 + --> $DIR/wrong_self_convention.rs:100:19 | LL | fn is_i32(self) {} | ^^^^ error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:101:19 + --> $DIR/wrong_self_convention.rs:102:19 | LL | fn to_i32(self) {} | ^^^^ error: methods called `from_*` usually take no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:103:21 + --> $DIR/wrong_self_convention.rs:104:21 | LL | fn from_i32(self) {} | ^^^^ error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:118:19 + --> $DIR/wrong_self_convention.rs:119:19 | LL | fn as_i32(self); | ^^^^ error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:120:21 + --> $DIR/wrong_self_convention.rs:122:25 | -LL | fn into_i32(&self); - | ^^^^^ +LL | fn into_i32_ref(&self); + | ^^^^^ error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:122:19 + --> $DIR/wrong_self_convention.rs:124:19 | LL | fn is_i32(self); | ^^^^ error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:124:19 + --> $DIR/wrong_self_convention.rs:126:19 | LL | fn to_i32(self); | ^^^^ error: methods called `from_*` usually take no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:126:21 + --> $DIR/wrong_self_convention.rs:128:21 + | +LL | fn from_i32(self); + | ^^^^ + +error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:146:25 + | +LL | fn into_i32_ref(&self); + | ^^^^^ + +error: methods called `from_*` usually take no self; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:152:21 | LL | fn from_i32(self); | ^^^^ -error: aborting due to 22 previous errors +error: aborting due to 24 previous errors diff --git a/src/tools/rust-analyzer b/src/tools/rust-analyzer index 0d03fe6ef57d3..60c501fa19dc4 160000 --- a/src/tools/rust-analyzer +++ b/src/tools/rust-analyzer @@ -1 +1 @@ -Subproject commit 0d03fe6ef57d3956e92382e0e1f1a916015191cb +Subproject commit 60c501fa19dc446ca8122db17bca9e0dcd5233a2