diff options
438 files changed, 6609 insertions, 3232 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 521f8ef0f5a..4cf0e5fba53 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -95,8 +95,6 @@ jobs: path-type: inherit install: > make - dos2unix - diffutils - name: disable git crlf conversion run: git config --global core.autocrlf false diff --git a/Cargo.lock b/Cargo.lock index 10ca6b0b4bd..a8f6eccb3d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -628,7 +628,7 @@ checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "clippy" -version = "0.1.80" +version = "0.1.81" dependencies = [ "anstream", "clippy_config", @@ -655,7 +655,7 @@ dependencies = [ [[package]] name = "clippy_config" -version = "0.1.80" +version = "0.1.81" dependencies = [ "rustc-semver", "serde", @@ -678,7 +678,7 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.80" +version = "0.1.81" dependencies = [ "arrayvec", "cargo_metadata 0.18.1", @@ -703,7 +703,7 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.80" +version = "0.1.81" dependencies = [ "arrayvec", "clippy_config", @@ -1023,7 +1023,7 @@ dependencies = [ [[package]] name = "declare_clippy_lint" -version = "0.1.80" +version = "0.1.81" dependencies = [ "itertools 0.12.1", "quote", @@ -3452,7 +3452,7 @@ dependencies = [ [[package]] name = "run_make_support" -version = "0.1.0" +version = "0.2.0" dependencies = [ "gimli 0.28.1", "object 0.34.0", diff --git a/compiler/rustc_borrowck/src/constraints/mod.rs b/compiler/rustc_borrowck/src/constraints/mod.rs index 97408fa20d7..b54e05b2b34 100644 --- a/compiler/rustc_borrowck/src/constraints/mod.rs +++ b/compiler/rustc_borrowck/src/constraints/mod.rs @@ -1,4 +1,4 @@ -use rustc_data_structures::graph::scc::Sccs; +use crate::type_check::Locations; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::{RegionVid, TyCtxt, VarianceDiagInfo}; @@ -6,8 +6,6 @@ use rustc_span::Span; use std::fmt; use std::ops::Index; -use crate::type_check::Locations; - pub(crate) mod graph; /// A set of NLL region constraints. These include "outlives" @@ -45,18 +43,6 @@ impl<'tcx> OutlivesConstraintSet<'tcx> { graph::ConstraintGraph::new(graph::Reverse, self, num_region_vars) } - /// Computes cycles (SCCs) in the graph of regions. In particular, - /// find all regions R1, R2 such that R1: R2 and R2: R1 and group - /// them into an SCC, and find the relationships between SCCs. - pub(crate) fn compute_sccs( - &self, - constraint_graph: &graph::NormalConstraintGraph, - static_region: RegionVid, - ) -> Sccs<RegionVid, ConstraintSccIndex> { - let region_graph = &constraint_graph.region_graph(self, static_region); - Sccs::new(region_graph) - } - pub(crate) fn outlives( &self, ) -> &IndexSlice<OutlivesConstraintIndex, OutlivesConstraint<'tcx>> { diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 0e3140ca98b..40b58500598 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -4,10 +4,10 @@ use std::rc::Rc; use rustc_data_structures::binary_search_util; use rustc_data_structures::frozen::Frozen; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; -use rustc_data_structures::graph::scc::Sccs; +use rustc_data_structures::graph::scc::{self, Sccs}; use rustc_errors::Diag; use rustc_hir::def_id::CRATE_DEF_ID; -use rustc_index::{IndexSlice, IndexVec}; +use rustc_index::IndexVec; use rustc_infer::infer::outlives::test_type_match; use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound, VerifyIfEq}; use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin}; @@ -19,7 +19,7 @@ use rustc_middle::mir::{ }; use rustc_middle::traits::ObligationCause; use rustc_middle::traits::ObligationCauseCode; -use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable, UniverseIndex}; use rustc_mir_dataflow::points::DenseLocationMap; use rustc_span::Span; @@ -46,6 +46,97 @@ mod reverse_sccs; pub mod values; +pub type ConstraintSccs = Sccs<RegionVid, ConstraintSccIndex, RegionTracker>; + +/// An annotation for region graph SCCs that tracks +/// the values of its elements. +#[derive(Copy, Debug, Clone)] +pub struct RegionTracker { + /// The largest universe of a placeholder reached from this SCC. + /// This includes placeholders within this SCC. + max_placeholder_universe_reached: UniverseIndex, + + /// The smallest universe index reachable form the nodes of this SCC. + min_reachable_universe: UniverseIndex, + + /// The representative Region Variable Id for this SCC. We prefer + /// placeholders over existentially quantified variables, otherwise + /// it's the one with the smallest Region Variable ID. + representative: RegionVid, + + /// Is the current representative a placeholder? + representative_is_placeholder: bool, + + /// Is the current representative existentially quantified? + representative_is_existential: bool, +} + +impl scc::Annotation for RegionTracker { + fn merge_scc(mut self, mut other: Self) -> Self { + // Prefer any placeholder over any existential + if other.representative_is_placeholder && self.representative_is_existential { + other.merge_min_max_seen(&self); + return other; + } + + if self.representative_is_placeholder && other.representative_is_existential + || (self.representative <= other.representative) + { + self.merge_min_max_seen(&other); + return self; + } + other.merge_min_max_seen(&self); + other + } + + fn merge_reached(mut self, other: Self) -> Self { + // No update to in-component values, only add seen values. + self.merge_min_max_seen(&other); + self + } +} + +impl RegionTracker { + fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self { + let (representative_is_placeholder, representative_is_existential) = match definition.origin + { + rustc_infer::infer::NllRegionVariableOrigin::FreeRegion => (false, false), + rustc_infer::infer::NllRegionVariableOrigin::Placeholder(_) => (true, false), + rustc_infer::infer::NllRegionVariableOrigin::Existential { .. } => (false, true), + }; + + let placeholder_universe = + if representative_is_placeholder { definition.universe } else { UniverseIndex::ROOT }; + + Self { + max_placeholder_universe_reached: placeholder_universe, + min_reachable_universe: definition.universe, + representative: rvid, + representative_is_placeholder, + representative_is_existential, + } + } + fn universe(self) -> UniverseIndex { + self.min_reachable_universe + } + + fn merge_min_max_seen(&mut self, other: &Self) { + self.max_placeholder_universe_reached = std::cmp::max( + self.max_placeholder_universe_reached, + other.max_placeholder_universe_reached, + ); + + self.min_reachable_universe = + std::cmp::min(self.min_reachable_universe, other.min_reachable_universe); + } + + /// Returns `true` if during the annotated SCC reaches a placeholder + /// with a universe larger than the smallest reachable one, `false` otherwise. + pub fn has_incompatible_universes(&self) -> bool { + self.universe().cannot_name(self.max_placeholder_universe_reached) + } +} + pub struct RegionInferenceContext<'tcx> { pub var_infos: VarInfos, @@ -72,7 +163,7 @@ pub struct RegionInferenceContext<'tcx> { /// The SCC computed from `constraints` and the constraint /// graph. We have an edge from SCC A to SCC B if `A: B`. Used to /// compute the values of each region. - constraint_sccs: Rc<Sccs<RegionVid, ConstraintSccIndex>>, + constraint_sccs: Rc<ConstraintSccs>, /// Reverse of the SCC constraint graph -- i.e., an edge `A -> B` exists if /// `B: A`. This is used to compute the universal regions that are required @@ -91,22 +182,6 @@ pub struct RegionInferenceContext<'tcx> { /// Map universe indexes to information on why we created it. universe_causes: FxIndexMap<ty::UniverseIndex, UniverseInfo<'tcx>>, - /// Contains the minimum universe of any variable within the same - /// SCC. We will ensure that no SCC contains values that are not - /// visible from this index. - scc_universes: IndexVec<ConstraintSccIndex, ty::UniverseIndex>, - - /// Contains the "representative" region of each SCC. - /// It is defined as the one with the minimal RegionVid, favoring - /// free regions, then placeholders, then existential regions. - /// - /// It is a hacky way to manage checking regions for equality, - /// since we can 'canonicalize' each region to the representative - /// of its SCC and be sure that -- if they have the same repr -- - /// they *must* be equal (though not having the same repr does not - /// mean they are unequal). - scc_representatives: IndexVec<ConstraintSccIndex, ty::RegionVid>, - /// The final inferred values of the region variables; we compute /// one value per SCC. To get the value for any given *region*, /// you first find which scc it is a part of. @@ -151,7 +226,7 @@ pub(crate) struct AppliedMemberConstraint { } #[derive(Debug)] -pub(crate) struct RegionDefinition<'tcx> { +pub struct RegionDefinition<'tcx> { /// What kind of variable is this -- a free region? existential /// variable? etc. (See the `NllRegionVariableOrigin` for more /// info.) @@ -250,7 +325,7 @@ pub enum ExtraConstraintInfo { } #[instrument(skip(infcx, sccs), level = "debug")] -fn sccs_info<'tcx>(infcx: &BorrowckInferCtxt<'tcx>, sccs: Rc<Sccs<RegionVid, ConstraintSccIndex>>) { +fn sccs_info<'tcx>(infcx: &BorrowckInferCtxt<'tcx>, sccs: &ConstraintSccs) { use crate::renumber::RegionCtxt; let var_to_origin = infcx.reg_var_to_origin.borrow(); @@ -264,7 +339,7 @@ fn sccs_info<'tcx>(infcx: &BorrowckInferCtxt<'tcx>, sccs: Rc<Sccs<RegionVid, Con } debug!("{}", reg_vars_to_origins_str); - let num_components = sccs.scc_data().ranges().len(); + let num_components = sccs.num_sccs(); let mut components = vec![FxIndexSet::default(); num_components]; for (reg_var_idx, scc_idx) in sccs.scc_indices().iter().enumerate() { @@ -301,10 +376,11 @@ fn sccs_info<'tcx>(infcx: &BorrowckInferCtxt<'tcx>, sccs: Rc<Sccs<RegionVid, Con let mut scc_node_to_edges = FxIndexMap::default(); for (scc_idx, repr) in components_representatives.iter() { - let edges_range = sccs.scc_data().ranges()[*scc_idx].clone(); - let edges = &sccs.scc_data().all_successors()[edges_range]; - let edge_representatives = - edges.iter().map(|scc_idx| components_representatives[scc_idx]).collect::<Vec<_>>(); + let edge_representatives = sccs + .successors(*scc_idx) + .iter() + .map(|scc_idx| components_representatives[scc_idx]) + .collect::<Vec<_>>(); scc_node_to_edges.insert((scc_idx, repr), edge_representatives); } @@ -320,7 +396,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// The `outlives_constraints` and `type_tests` are an initial set /// of constraints produced by the MIR type check. pub(crate) fn new( - _infcx: &BorrowckInferCtxt<'tcx>, + infcx: &BorrowckInferCtxt<'tcx>, var_infos: VarInfos, universal_regions: Rc<UniversalRegions<'tcx>>, placeholder_indices: Rc<PlaceholderIndices>, @@ -343,13 +419,20 @@ impl<'tcx> RegionInferenceContext<'tcx> { .map(|info| RegionDefinition::new(info.universe, info.origin)) .collect(); + let fr_static = universal_regions.fr_static; let constraints = Frozen::freeze(outlives_constraints); let constraint_graph = Frozen::freeze(constraints.graph(definitions.len())); - let fr_static = universal_regions.fr_static; - let constraint_sccs = Rc::new(constraints.compute_sccs(&constraint_graph, fr_static)); + let constraint_sccs = { + let constraint_graph = constraints.graph(definitions.len()); + let region_graph = &constraint_graph.region_graph(&constraints, fr_static); + let sccs = ConstraintSccs::new_with_annotation(®ion_graph, |r| { + RegionTracker::new(r, &definitions[r]) + }); + Rc::new(sccs) + }; if cfg!(debug_assertions) { - sccs_info(_infcx, constraint_sccs.clone()); + sccs_info(infcx, &constraint_sccs); } let mut scc_values = @@ -360,10 +443,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { scc_values.merge_liveness(scc, region, &liveness_constraints); } - let scc_universes = Self::compute_scc_universes(&constraint_sccs, &definitions); - - let scc_representatives = Self::compute_scc_representatives(&constraint_sccs, &definitions); - let member_constraints = Rc::new(member_constraints_in.into_mapped(|r| constraint_sccs.scc(r))); @@ -378,8 +457,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { member_constraints, member_constraints_applied: Vec::new(), universe_causes, - scc_universes, - scc_representatives, scc_values, type_tests, universal_regions, @@ -391,123 +468,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { result } - /// Each SCC is the combination of many region variables which - /// have been equated. Therefore, we can associate a universe with - /// each SCC which is minimum of all the universes of its - /// constituent regions -- this is because whatever value the SCC - /// takes on must be a value that each of the regions within the - /// SCC could have as well. This implies that the SCC must have - /// the minimum, or narrowest, universe. - fn compute_scc_universes( - constraint_sccs: &Sccs<RegionVid, ConstraintSccIndex>, - definitions: &IndexSlice<RegionVid, RegionDefinition<'tcx>>, - ) -> IndexVec<ConstraintSccIndex, ty::UniverseIndex> { - let num_sccs = constraint_sccs.num_sccs(); - let mut scc_universes = IndexVec::from_elem_n(ty::UniverseIndex::MAX, num_sccs); - - debug!("compute_scc_universes()"); - - // For each region R in universe U, ensure that the universe for the SCC - // that contains R is "no bigger" than U. This effectively sets the universe - // for each SCC to be the minimum of the regions within. - for (region_vid, region_definition) in definitions.iter_enumerated() { - let scc = constraint_sccs.scc(region_vid); - let scc_universe = &mut scc_universes[scc]; - let scc_min = std::cmp::min(region_definition.universe, *scc_universe); - if scc_min != *scc_universe { - *scc_universe = scc_min; - debug!( - "compute_scc_universes: lowered universe of {scc:?} to {scc_min:?} \ - because it contains {region_vid:?} in {region_universe:?}", - scc = scc, - scc_min = scc_min, - region_vid = region_vid, - region_universe = region_definition.universe, - ); - } - } - - // Walk each SCC `A` and `B` such that `A: B` - // and ensure that universe(A) can see universe(B). - // - // This serves to enforce the 'empty/placeholder' hierarchy - // (described in more detail on `RegionKind`): - // - // ``` - // static -----+ - // | | - // empty(U0) placeholder(U1) - // | / - // empty(U1) - // ``` - // - // In particular, imagine we have variables R0 in U0 and R1 - // created in U1, and constraints like this; - // - // ``` - // R1: !1 // R1 outlives the placeholder in U1 - // R1: R0 // R1 outlives R0 - // ``` - // - // Here, we wish for R1 to be `'static`, because it - // cannot outlive `placeholder(U1)` and `empty(U0)` any other way. - // - // Thanks to this loop, what happens is that the `R1: R0` - // constraint lowers the universe of `R1` to `U0`, which in turn - // means that the `R1: !1` constraint will (later) cause - // `R1` to become `'static`. - for scc_a in constraint_sccs.all_sccs() { - for &scc_b in constraint_sccs.successors(scc_a) { - let scc_universe_a = scc_universes[scc_a]; - let scc_universe_b = scc_universes[scc_b]; - let scc_universe_min = std::cmp::min(scc_universe_a, scc_universe_b); - if scc_universe_a != scc_universe_min { - scc_universes[scc_a] = scc_universe_min; - - debug!( - "compute_scc_universes: lowered universe of {scc_a:?} to {scc_universe_min:?} \ - because {scc_a:?}: {scc_b:?} and {scc_b:?} is in universe {scc_universe_b:?}", - scc_a = scc_a, - scc_b = scc_b, - scc_universe_min = scc_universe_min, - scc_universe_b = scc_universe_b - ); - } - } - } - - debug!("compute_scc_universes: scc_universe = {:#?}", scc_universes); - - scc_universes - } - - /// For each SCC, we compute a unique `RegionVid`. See the - /// `scc_representatives` field of `RegionInferenceContext` for - /// more details. - fn compute_scc_representatives( - constraints_scc: &Sccs<RegionVid, ConstraintSccIndex>, - definitions: &IndexSlice<RegionVid, RegionDefinition<'tcx>>, - ) -> IndexVec<ConstraintSccIndex, ty::RegionVid> { - let num_sccs = constraints_scc.num_sccs(); - let mut scc_representatives = IndexVec::from_elem_n(RegionVid::MAX, num_sccs); - - // Iterate over all RegionVids *in-order* and pick the least RegionVid as the - // representative of its SCC. This naturally prefers free regions over others. - for (vid, def) in definitions.iter_enumerated() { - let repr = &mut scc_representatives[constraints_scc.scc(vid)]; - if *repr == ty::RegionVid::MAX { - *repr = vid; - } else if matches!(def.origin, NllRegionVariableOrigin::Placeholder(_)) - && matches!(definitions[*repr].origin, NllRegionVariableOrigin::Existential { .. }) - { - // Pick placeholders over existentials even if they have a greater RegionVid. - *repr = vid; - } - } - - scc_representatives - } - /// Initializes the region variables for each universally /// quantified region (lifetime parameter). The first N variables /// always correspond to the regions appearing in the function @@ -528,12 +488,45 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// and (b) any universally quantified regions that it outlives, /// which in this case is just itself. R1 (`'b`) in contrast also /// outlives `'a` and hence contains R0 and R1. + /// + /// This bit of logic also handles invalid universe relations + /// for higher-kinded types. + /// + /// We Walk each SCC `A` and `B` such that `A: B` + /// and ensure that universe(A) can see universe(B). + /// + /// This serves to enforce the 'empty/placeholder' hierarchy + /// (described in more detail on `RegionKind`): + /// + /// ```ignore (illustrative) + /// static -----+ + /// | | + /// empty(U0) placeholder(U1) + /// | / + /// empty(U1) + /// ``` + /// + /// In particular, imagine we have variables R0 in U0 and R1 + /// created in U1, and constraints like this; + /// + /// ```ignore (illustrative) + /// R1: !1 // R1 outlives the placeholder in U1 + /// R1: R0 // R1 outlives R0 + /// ``` + /// + /// Here, we wish for R1 to be `'static`, because it + /// cannot outlive `placeholder(U1)` and `empty(U0)` any other way. + /// + /// Thanks to this loop, what happens is that the `R1: R0` + /// constraint has lowered the universe of `R1` to `U0`, which in turn + /// means that the `R1: !1` constraint here will cause + /// `R1` to become `'static`. fn init_free_and_bound_regions(&mut self) { // Update the names (if any) // This iterator has unstable order but we collect it all into an IndexVec for (external_name, variable) in self.universal_regions.named_universal_regions() { debug!( - "init_universal_regions: region {:?} has external name {:?}", + "init_free_and_bound_regions: region {:?} has external name {:?}", variable, external_name ); self.definitions[variable].external_name = Some(external_name); @@ -559,7 +552,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // its universe `ui` and its extensions. So we // can't just add it into `scc` unless the // universe of the scc can name this region. - let scc_universe = self.scc_universes[scc]; + let scc_universe = self.scc_universe(scc); if scc_universe.can_name(placeholder.universe) { self.scc_values.add_element(scc, placeholder); } else { @@ -640,8 +633,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// Returns access to the value of `r` for debugging purposes. pub(crate) fn region_universe(&self, r: RegionVid) -> ty::UniverseIndex { - let scc = self.constraint_sccs.scc(r); - self.scc_universes[scc] + self.scc_universe(self.constraint_sccs.scc(r)) } /// Once region solving has completed, this function will return the member constraints that @@ -737,8 +729,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // SCC. For each SCC, we visit its successors and compute // their values, then we union all those values to get our // own. - let constraint_sccs = self.constraint_sccs.clone(); - for scc in constraint_sccs.all_sccs() { + for scc in self.constraint_sccs.all_sccs() { self.compute_value_for_scc(scc); } @@ -817,20 +808,15 @@ impl<'tcx> RegionInferenceContext<'tcx> { // if one exists. for c_r in &mut choice_regions { let scc = self.constraint_sccs.scc(*c_r); - *c_r = self.scc_representatives[scc]; + *c_r = self.scc_representative(scc); } // If the member region lives in a higher universe, we currently choose // the most conservative option by leaving it unchanged. - if self.scc_universes[scc] != ty::UniverseIndex::ROOT { + + if !self.constraint_sccs().annotation(scc).universe().is_root() { return; } - debug_assert!( - self.scc_values.placeholders_contained_in(scc).next().is_none(), - "scc {:?} in a member constraint has placeholder value: {:?}", - scc, - self.scc_values.region_value_str(scc), - ); // The existing value for `scc` is a lower-bound. This will // consist of some set `{P} + {LB}` of points `{P}` and @@ -900,12 +886,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// in `scc_a`. Used during constraint propagation, and only once /// the value of `scc_b` has been computed. fn universe_compatible(&self, scc_b: ConstraintSccIndex, scc_a: ConstraintSccIndex) -> bool { - let universe_a = self.scc_universes[scc_a]; + let universe_a = self.constraint_sccs().annotation(scc_a).universe(); + let universe_b = self.constraint_sccs().annotation(scc_b).universe(); // Quick check: if scc_b's declared universe is a subset of // scc_a's declared universe (typically, both are ROOT), then // it cannot contain any problematic universe elements. - if universe_a.can_name(self.scc_universes[scc_b]) { + if universe_a.can_name(universe_b) { return true; } @@ -1033,7 +1020,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { debug!( "lower_bound = {:?} r_scc={:?} universe={:?}", - lower_bound, r_scc, self.scc_universes[r_scc] + lower_bound, + r_scc, + self.constraint_sccs.annotation(r_scc).universe() ); // If the type test requires that `T: 'a` where `'a` is a @@ -1321,7 +1310,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { tcx.fold_regions(value, |r, _db| { let vid = self.to_region_vid(r); let scc = self.constraint_sccs.scc(vid); - let repr = self.scc_representatives[scc]; + let repr = self.scc_representative(scc); ty::Region::new_var(tcx, repr) }) } @@ -1547,6 +1536,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } + /// The minimum universe of any variable reachable from this + /// SCC, inside or outside of it. + fn scc_universe(&self, scc: ConstraintSccIndex) -> UniverseIndex { + self.constraint_sccs().annotation(scc).universe() + } /// Checks the final value for the free region `fr` to see if it /// grew too large. In particular, examine what `end(X)` points /// wound up in `fr`'s final value; for each `end(X)` where `X != @@ -1566,8 +1560,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Because this free region must be in the ROOT universe, we // know it cannot contain any bound universes. - assert!(self.scc_universes[longer_fr_scc].is_root()); - debug_assert!(self.scc_values.placeholders_contained_in(longer_fr_scc).next().is_none()); + assert!(self.scc_universe(longer_fr_scc).is_root()); // Only check all of the relations for the main representative of each // SCC, otherwise just check that we outlive said representative. This @@ -1575,7 +1568,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // closures. // Note that the representative will be a universal region if there is // one in this SCC, so we will always check the representative here. - let representative = self.scc_representatives[longer_fr_scc]; + let representative = self.scc_representative(longer_fr_scc); if representative != longer_fr { if let RegionRelationCheckResult::Error = self.check_universal_region_relation( longer_fr, @@ -1796,16 +1789,14 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// `true` if `r1` cannot name that placeholder in its /// value; otherwise, returns `false`. pub(crate) fn cannot_name_placeholder(&self, r1: RegionVid, r2: RegionVid) -> bool { - debug!("cannot_name_value_of(r1={:?}, r2={:?})", r1, r2); - match self.definitions[r2].origin { NllRegionVariableOrigin::Placeholder(placeholder) => { - let universe1 = self.definitions[r1].universe; + let r1_universe = self.definitions[r1].universe; debug!( - "cannot_name_value_of: universe1={:?} placeholder={:?}", - universe1, placeholder + "cannot_name_value_of: universe1={r1_universe:?} placeholder={:?}", + placeholder ); - universe1.cannot_name(placeholder.universe) + r1_universe.cannot_name(placeholder.universe) } NllRegionVariableOrigin::FreeRegion | NllRegionVariableOrigin::Existential { .. } => { @@ -1835,6 +1826,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// /// Returns: a series of constraints as well as the region `R` /// that passed the target test. + #[instrument(skip(self, target_test), ret)] pub(crate) fn find_constraint_paths_between_regions( &self, from_region: RegionVid, @@ -1932,7 +1924,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { #[instrument(skip(self), level = "trace", ret)] pub(crate) fn find_sub_region_live_at(&self, fr1: RegionVid, location: Location) -> RegionVid { trace!(scc = ?self.constraint_sccs.scc(fr1)); - trace!(universe = ?self.scc_universes[self.constraint_sccs.scc(fr1)]); + trace!(universe = ?self.region_universe(fr1)); self.find_constraint_paths_between_regions(fr1, |r| { // First look for some `r` such that `fr1: r` and `r` is live at `location` trace!(?r, liveness_constraints=?self.liveness_constraints.pretty_print_live_points(r)); @@ -2252,8 +2244,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// This can be used to quickly under-approximate the regions which are equal to each other /// and their relative orderings. // This is `pub` because it's used by unstable external borrowck data users, see `consumers.rs`. - pub fn constraint_sccs(&self) -> &Sccs<RegionVid, ConstraintSccIndex> { - self.constraint_sccs.as_ref() + pub fn constraint_sccs(&self) -> &ConstraintSccs { + &self.constraint_sccs } /// Access to the region graph, built from the outlives constraints. @@ -2282,6 +2274,18 @@ impl<'tcx> RegionInferenceContext<'tcx> { let point = self.liveness_constraints.point_from_location(location); self.liveness_constraints.is_loan_live_at(loan_idx, point) } + + /// Returns the representative `RegionVid` for a given SCC. + /// See `RegionTracker` for how a region variable ID is chosen. + /// + /// It is a hacky way to manage checking regions for equality, + /// since we can 'canonicalize' each region to the representative + /// of its SCC and be sure that -- if they have the same repr -- + /// they *must* be equal (though not having the same repr does not + /// mean they are unequal). + fn scc_representative(&self, scc: ConstraintSccIndex) -> RegionVid { + self.constraint_sccs.annotation(scc).representative + } } impl<'tcx> RegionDefinition<'tcx> { diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 06adb686ed4..51c3648d730 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -85,7 +85,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Use the SCC representative instead of directly using `region`. // See [rustc-dev-guide chapter] § "Strict lifetime equality". let scc = self.constraint_sccs.scc(region.as_var()); - let vid = self.scc_representatives[scc]; + let vid = self.scc_representative(scc); let named = match self.definitions[vid].origin { // Iterate over all universal regions in a consistent order and find the // *first* equal region. This makes sure that equal lifetimes will have @@ -213,7 +213,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { let scc = self.constraint_sccs.scc(vid); // Special handling of higher-ranked regions. - if !self.scc_universes[scc].is_root() { + if !self.scc_universe(scc).is_root() { match self.scc_values.placeholders_contained_in(scc).enumerate().last() { // If the region contains a single placeholder then they're equal. Some((0, placeholder)) => { diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index 923b9ddf9af..b17dc7f3ddd 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -1,7 +1,6 @@ use std::mem; use rustc_errors::{DiagArgName, DiagArgValue, DiagMessage, Diagnostic, IntoDiagArg}; -use rustc_hir::CRATE_HIR_ID; use rustc_middle::mir::interpret::{Provenance, ReportedErrorInfo}; use rustc_middle::mir::AssertKind; use rustc_middle::query::TyCtxtAt; @@ -9,7 +8,7 @@ use rustc_middle::ty::TyCtxt; use rustc_middle::ty::{layout::LayoutError, ConstInt}; use rustc_span::{Span, Symbol}; -use super::CompileTimeInterpreter; +use super::CompileTimeMachine; use crate::errors::{self, FrameNote, ReportErrorExt}; use crate::interpret::{err_inval, err_machine_stop}; use crate::interpret::{ErrorHandled, Frame, InterpError, InterpErrorInfo, MachineStopType}; @@ -156,11 +155,11 @@ where } } -/// Emit a lint from a const-eval situation. +/// Emit a lint from a const-eval situation, with a backtrace. // Even if this is unused, please don't remove it -- chances are we will need to emit a lint during const-eval again in the future! pub(super) fn lint<'tcx, L>( tcx: TyCtxtAt<'tcx>, - machine: &CompileTimeInterpreter<'tcx>, + machine: &CompileTimeMachine<'tcx>, lint: &'static rustc_session::lint::Lint, decorator: impl FnOnce(Vec<errors::FrameNote>) -> L, ) where @@ -168,12 +167,5 @@ pub(super) fn lint<'tcx, L>( { let (span, frames) = get_span_and_frames(tcx, &machine.stack); - tcx.emit_node_span_lint( - lint, - // We use the root frame for this so the crate that defines the const defines whether the - // lint is emitted. - machine.stack.first().and_then(|frame| frame.lint_root()).unwrap_or(CRATE_HIR_ID), - span, - decorator(frames), - ); + tcx.emit_node_span_lint(lint, machine.best_lint_scope(*tcx), span, decorator(frames)); } diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 36f468d3308..4b8145eb485 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -17,7 +17,7 @@ use rustc_span::def_id::LocalDefId; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::{self, Abi}; -use super::{CanAccessMutGlobal, CompileTimeEvalContext, CompileTimeInterpreter}; +use super::{CanAccessMutGlobal, CompileTimeInterpCx, CompileTimeMachine}; use crate::const_eval::CheckAlignment; use crate::errors::ConstEvalError; use crate::errors::{self, DanglingPtrInFinal}; @@ -32,7 +32,7 @@ use crate::CTRL_C_RECEIVED; // Returns a pointer to where the result lives #[instrument(level = "trace", skip(ecx, body))] fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>( - ecx: &mut CompileTimeEvalContext<'tcx>, + ecx: &mut CompileTimeInterpCx<'tcx>, cid: GlobalId<'tcx>, body: &'tcx mir::Body<'tcx>, ) -> InterpResult<'tcx, R> { @@ -114,7 +114,7 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>( let err_diag = errors::MutablePtrInFinal { span: ecx.tcx.span, kind: intern_kind }; ecx.tcx.emit_node_span_lint( lint::builtin::CONST_EVAL_MUTABLE_PTR_IN_FINAL_VALUE, - ecx.best_lint_scope(), + ecx.machine.best_lint_scope(*ecx.tcx), err_diag.span, err_diag, ) @@ -139,13 +139,13 @@ pub(crate) fn mk_eval_cx_to_read_const_val<'tcx>( root_span: Span, param_env: ty::ParamEnv<'tcx>, can_access_mut_global: CanAccessMutGlobal, -) -> CompileTimeEvalContext<'tcx> { +) -> CompileTimeInterpCx<'tcx> { debug!("mk_eval_cx: {:?}", param_env); InterpCx::new( tcx, root_span, param_env, - CompileTimeInterpreter::new(can_access_mut_global, CheckAlignment::No), + CompileTimeMachine::new(can_access_mut_global, CheckAlignment::No), ) } @@ -156,7 +156,7 @@ pub fn mk_eval_cx_for_const_val<'tcx>( param_env: ty::ParamEnv<'tcx>, val: mir::ConstValue<'tcx>, ty: Ty<'tcx>, -) -> Option<(CompileTimeEvalContext<'tcx>, OpTy<'tcx>)> { +) -> Option<(CompileTimeInterpCx<'tcx>, OpTy<'tcx>)> { let ecx = mk_eval_cx_to_read_const_val(tcx.tcx, tcx.span, param_env, CanAccessMutGlobal::No); let op = ecx.const_val_to_op(val, ty, None).ok()?; Some((ecx, op)) @@ -170,7 +170,7 @@ pub fn mk_eval_cx_for_const_val<'tcx>( /// encounter an `Indirect` they cannot handle. #[instrument(skip(ecx), level = "debug")] pub(super) fn op_to_const<'tcx>( - ecx: &CompileTimeEvalContext<'tcx>, + ecx: &CompileTimeInterpCx<'tcx>, op: &OpTy<'tcx>, for_diagnostics: bool, ) -> ConstValue<'tcx> { @@ -328,14 +328,14 @@ pub trait InterpretationResult<'tcx> { /// evaluation query. fn make_result( mplace: MPlaceTy<'tcx>, - ecx: &mut InterpCx<'tcx, CompileTimeInterpreter<'tcx>>, + ecx: &mut InterpCx<'tcx, CompileTimeMachine<'tcx>>, ) -> Self; } impl<'tcx> InterpretationResult<'tcx> for ConstAlloc<'tcx> { fn make_result( mplace: MPlaceTy<'tcx>, - _ecx: &mut InterpCx<'tcx, CompileTimeInterpreter<'tcx>>, + _ecx: &mut InterpCx<'tcx, CompileTimeMachine<'tcx>>, ) -> Self { ConstAlloc { alloc_id: mplace.ptr().provenance.unwrap().alloc_id(), ty: mplace.layout.ty } } @@ -383,7 +383,7 @@ fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>( // they do not have to behave "as if" they were evaluated at runtime. // For consts however we want to ensure they behave "as if" they were evaluated at runtime, // so we have to reject reading mutable global memory. - CompileTimeInterpreter::new(CanAccessMutGlobal::from(is_static), CheckAlignment::Error), + CompileTimeMachine::new(CanAccessMutGlobal::from(is_static), CheckAlignment::Error), ); let res = ecx.load_mir(cid.instance.def, cid.promoted); res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, body)).map_err(|error| { @@ -417,7 +417,7 @@ fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>( #[inline(always)] fn const_validate_mplace<'tcx>( - ecx: &InterpCx<'tcx, CompileTimeInterpreter<'tcx>>, + ecx: &InterpCx<'tcx, CompileTimeMachine<'tcx>>, mplace: &MPlaceTy<'tcx>, cid: GlobalId<'tcx>, ) -> Result<(), ErrorHandled> { @@ -447,7 +447,7 @@ fn const_validate_mplace<'tcx>( #[inline(always)] fn report_validation_error<'tcx>( - ecx: &InterpCx<'tcx, CompileTimeInterpreter<'tcx>>, + ecx: &InterpCx<'tcx, CompileTimeMachine<'tcx>>, error: InterpErrorInfo<'tcx>, alloc_id: AllocId, ) -> ErrorHandled { diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 79a161d3f03..4f6df857142 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -9,12 +9,13 @@ use rustc_data_structures::fx::IndexEntry; use rustc_hir::def_id::DefId; use rustc_hir::def_id::LocalDefId; use rustc_hir::LangItem; +use rustc_hir::{self as hir, CRATE_HIR_ID}; use rustc_middle::bug; use rustc_middle::mir; use rustc_middle::mir::AssertMessage; use rustc_middle::query::TyCtxtAt; -use rustc_middle::ty; use rustc_middle::ty::layout::{FnAbiOf, TyAndLayout}; +use rustc_middle::ty::{self, TyCtxt}; use rustc_session::lint::builtin::WRITES_THROUGH_IMMUTABLE_POINTER; use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; @@ -44,7 +45,7 @@ const TINY_LINT_TERMINATOR_LIMIT: usize = 20; const PROGRESS_INDICATOR_START: usize = 4_000_000; /// Extra machine state for CTFE, and the Machine instance -pub struct CompileTimeInterpreter<'tcx> { +pub struct CompileTimeMachine<'tcx> { /// The number of terminators that have been evaluated. /// /// This is used to produce lints informing the user that the compiler is not stuck. @@ -89,12 +90,12 @@ impl From<bool> for CanAccessMutGlobal { } } -impl<'tcx> CompileTimeInterpreter<'tcx> { +impl<'tcx> CompileTimeMachine<'tcx> { pub(crate) fn new( can_access_mut_global: CanAccessMutGlobal, check_alignment: CheckAlignment, ) -> Self { - CompileTimeInterpreter { + CompileTimeMachine { num_evaluated_steps: 0, stack: Vec::new(), can_access_mut_global, @@ -163,7 +164,7 @@ impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxIndexMap<K, V> { } } -pub(crate) type CompileTimeEvalContext<'tcx> = InterpCx<'tcx, CompileTimeInterpreter<'tcx>>; +pub(crate) type CompileTimeInterpCx<'tcx> = InterpCx<'tcx, CompileTimeMachine<'tcx>>; #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum MemoryKind { @@ -195,7 +196,7 @@ impl interpret::MayLeak for ! { } } -impl<'tcx> CompileTimeEvalContext<'tcx> { +impl<'tcx> CompileTimeInterpCx<'tcx> { fn location_triple_for_span(&self, span: Span) -> (Symbol, u32, u32) { let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span); let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo()); @@ -369,7 +370,16 @@ impl<'tcx> CompileTimeEvalContext<'tcx> { } } -impl<'tcx> interpret::Machine<'tcx> for CompileTimeInterpreter<'tcx> { +impl<'tcx> CompileTimeMachine<'tcx> { + #[inline(always)] + /// Find the first stack frame that is within the current crate, if any. + /// Otherwise, return the crate's HirId + pub fn best_lint_scope(&self, tcx: TyCtxt<'tcx>) -> hir::HirId { + self.stack.iter().find_map(|frame| frame.lint_root(tcx)).unwrap_or(CRATE_HIR_ID) + } +} + +impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { compile_time_machine!(<'tcx>); type MemoryKind = MemoryKind; @@ -600,7 +610,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeInterpreter<'tcx> { // By default, we stop after a million steps, but the user can disable this lint // to be able to run until the heat death of the universe or power loss, whichever // comes first. - let hir_id = ecx.best_lint_scope(); + let hir_id = ecx.machine.best_lint_scope(*ecx.tcx); let is_error = ecx .tcx .lint_level_at_node( diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 66993476bef..2e8ad445cf5 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -9,7 +9,7 @@ use rustc_target::abi::{Abi, VariantIdx}; use tracing::{debug, instrument, trace}; use super::eval_queries::{mk_eval_cx_to_read_const_val, op_to_const}; -use super::machine::CompileTimeEvalContext; +use super::machine::CompileTimeInterpCx; use super::{ValTreeCreationError, ValTreeCreationResult, VALTREE_MAX_NODES}; use crate::const_eval::CanAccessMutGlobal; use crate::errors::MaxNumNodesInConstErr; @@ -21,7 +21,7 @@ use crate::interpret::{ #[instrument(skip(ecx), level = "debug")] fn branches<'tcx>( - ecx: &CompileTimeEvalContext<'tcx>, + ecx: &CompileTimeInterpCx<'tcx>, place: &MPlaceTy<'tcx>, n: usize, variant: Option<VariantIdx>, @@ -59,7 +59,7 @@ fn branches<'tcx>( #[instrument(skip(ecx), level = "debug")] fn slice_branches<'tcx>( - ecx: &CompileTimeEvalContext<'tcx>, + ecx: &CompileTimeInterpCx<'tcx>, place: &MPlaceTy<'tcx>, num_nodes: &mut usize, ) -> ValTreeCreationResult<'tcx> { @@ -77,7 +77,7 @@ fn slice_branches<'tcx>( #[instrument(skip(ecx), level = "debug")] fn const_to_valtree_inner<'tcx>( - ecx: &CompileTimeEvalContext<'tcx>, + ecx: &CompileTimeInterpCx<'tcx>, place: &MPlaceTy<'tcx>, num_nodes: &mut usize, ) -> ValTreeCreationResult<'tcx> { @@ -219,7 +219,7 @@ fn reconstruct_place_meta<'tcx>( #[instrument(skip(ecx), level = "debug", ret)] fn create_valtree_place<'tcx>( - ecx: &mut CompileTimeEvalContext<'tcx>, + ecx: &mut CompileTimeInterpCx<'tcx>, layout: TyAndLayout<'tcx>, valtree: ty::ValTree<'tcx>, ) -> MPlaceTy<'tcx> { @@ -364,7 +364,7 @@ pub fn valtree_to_const_value<'tcx>( /// Put a valtree into memory and return a reference to that. fn valtree_to_ref<'tcx>( - ecx: &mut CompileTimeEvalContext<'tcx>, + ecx: &mut CompileTimeInterpCx<'tcx>, valtree: ty::ValTree<'tcx>, pointee_ty: Ty<'tcx>, ) -> Immediate { @@ -380,7 +380,7 @@ fn valtree_to_ref<'tcx>( #[instrument(skip(ecx), level = "debug")] fn valtree_into_mplace<'tcx>( - ecx: &mut CompileTimeEvalContext<'tcx>, + ecx: &mut CompileTimeInterpCx<'tcx>, place: &MPlaceTy<'tcx>, valtree: ty::ValTree<'tcx>, ) { @@ -457,6 +457,6 @@ fn valtree_into_mplace<'tcx>( } } -fn dump_place<'tcx>(ecx: &CompileTimeEvalContext<'tcx>, place: &MPlaceTy<'tcx>) { +fn dump_place<'tcx>(ecx: &CompileTimeInterpCx<'tcx>, place: &MPlaceTy<'tcx>) { trace!("{:?}", ecx.dump_place(&PlaceTy::from(place.clone()))); } diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs index 0dbee8c1d94..a50b50d231d 100644 --- a/compiler/rustc_const_eval/src/interpret/discriminant.rs +++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs @@ -241,10 +241,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { variant_index: VariantIdx, ) -> InterpResult<'tcx, Option<(ScalarInt, usize)>> { match self.layout_of(ty)?.variants { - abi::Variants::Single { index } => { - assert_eq!(index, variant_index); - Ok(None) - } + abi::Variants::Single { .. } => Ok(None), abi::Variants::Multiple { tag_encoding: TagEncoding::Direct, diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index e28cc05cc2a..4d93038a81e 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -4,7 +4,6 @@ use std::{fmt, mem}; use either::{Either, Left, Right}; use tracing::{debug, info, info_span, instrument, trace}; -use hir::CRATE_HIR_ID; use rustc_errors::DiagCtxt; use rustc_hir::{self as hir, def_id::DefId, definitions::DefPathData}; use rustc_index::IndexVec; @@ -250,7 +249,7 @@ impl<'tcx, Prov: Provenance> Frame<'tcx, Prov> { impl<'tcx, Prov: Provenance, Extra> Frame<'tcx, Prov, Extra> { /// Get the current location within the Frame. /// - /// If this is `Left`, we are not currently executing any particular statement in + /// If this is `Right`, we are not currently executing any particular statement in /// this frame (can happen e.g. during frame initialization, and during unwinding on /// frames without cleanup code). /// @@ -271,13 +270,18 @@ impl<'tcx, Prov: Provenance, Extra> Frame<'tcx, Prov, Extra> { } } - pub fn lint_root(&self) -> Option<hir::HirId> { - self.current_source_info().and_then(|source_info| { - match &self.body.source_scopes[source_info.scope].local_data { + pub fn lint_root(&self, tcx: TyCtxt<'tcx>) -> Option<hir::HirId> { + // We first try to get a HirId via the current source scope, + // and fall back to `body.source`. + self.current_source_info() + .and_then(|source_info| match &self.body.source_scopes[source_info.scope].local_data { mir::ClearCrossCrate::Set(data) => Some(data.lint_root), mir::ClearCrossCrate::Clear => None, - } - }) + }) + .or_else(|| { + let def_id = self.body.source.def_id().as_local(); + def_id.map(|def_id| tcx.local_def_id_to_hir_id(def_id)) + }) } /// Returns the address of the buffer where the locals are stored. This is used by `Place` as a @@ -500,6 +504,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } + /// Returns the span of the currently executed statement/terminator. + /// This is the span typically used for error reporting. #[inline(always)] pub fn cur_span(&self) -> Span { // This deliberately does *not* honor `requires_caller_location` since it is used for much @@ -507,16 +513,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.stack().last().map_or(self.tcx.span, |f| f.current_span()) } - #[inline(always)] - /// Find the first stack frame that is within the current crate, if any, otherwise return the crate's HirId - pub fn best_lint_scope(&self) -> hir::HirId { - self.stack() - .iter() - .find_map(|frame| frame.body.source.def_id().as_local()) - .map_or(CRATE_HIR_ID, |def_id| self.tcx.local_def_id_to_hir_id(def_id)) - } - - #[inline(always)] pub(crate) fn stack(&self) -> &[Frame<'tcx, M::Provenance, M::FrameExtra>] { M::stack(self) } @@ -632,7 +628,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } /// Walks up the callstack from the intrinsic's callsite, searching for the first callsite in a - /// frame which is not `#[track_caller]`. This is the fancy version of `cur_span`. + /// frame which is not `#[track_caller]`. This matches the `caller_location` intrinsic, + /// and is primarily intended for the panic machinery. pub(crate) fn find_closest_untracked_caller_location(&self) -> Span { for frame in self.stack().iter().rev() { debug!("find_closest_untracked_caller_location: checking frame {:?}", frame.instance); @@ -1057,7 +1054,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => false, - ty::Tuple(tys) => tys.last().iter().all(|ty| is_very_trivially_sized(**ty)), + ty::Tuple(tys) => tys.last().is_none_or(|ty| is_very_trivially_sized(*ty)), ty::Pat(ty, ..) => is_very_trivially_sized(*ty), diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index 3066e0933d9..8b0a2afa4d6 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -45,7 +45,7 @@ pub trait HasStaticRootDefId { fn static_def_id(&self) -> Option<LocalDefId>; } -impl HasStaticRootDefId for const_eval::CompileTimeInterpreter<'_> { +impl HasStaticRootDefId for const_eval::CompileTimeMachine<'_> { fn static_def_id(&self) -> Option<LocalDefId> { Some(self.static_root_ids?.1) } diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 5461e9c6ad3..9a26ac04b85 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -446,7 +446,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let (alloc_size, _alloc_align, ret_val) = alloc_size(alloc_id, offset, prov)?; // Test bounds. // It is sufficient to check this for the end pointer. Also check for overflow! - if offset.checked_add(size, &self.tcx).map_or(true, |end| end > alloc_size) { + if offset.checked_add(size, &self.tcx).is_none_or(|end| end > alloc_size) { throw_ub!(PointerOutOfBounds { alloc_id, alloc_size, diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index 09e1a59dfa1..efa01b54342 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -300,7 +300,7 @@ where ) -> InterpResult<'tcx, P> { let len = base.len(self)?; // also asserts that we have a type where this makes sense let actual_to = if from_end { - if from.checked_add(to).map_or(true, |to| to > len) { + if from.checked_add(to).is_none_or(|to| to > len) { // This can only be reached in ConstProp and non-rustc-MIR. throw_ub!(BoundsCheckFailed { len: len, index: from.saturating_add(to) }); } diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index 10fd6399b9a..f6537ed6ea9 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -1,4 +1,4 @@ -use crate::const_eval::{CompileTimeEvalContext, CompileTimeInterpreter, InterpretationResult}; +use crate::const_eval::{CompileTimeInterpCx, CompileTimeMachine, InterpretationResult}; use rustc_hir::def_id::LocalDefId; use rustc_middle::mir; use rustc_middle::mir::interpret::{Allocation, InterpResult, Pointer}; @@ -84,7 +84,7 @@ where impl<'tcx> InterpretationResult<'tcx> for mir::interpret::ConstAllocation<'tcx> { fn make_result( mplace: MPlaceTy<'tcx>, - ecx: &mut InterpCx<'tcx, CompileTimeInterpreter<'tcx>>, + ecx: &mut InterpCx<'tcx, CompileTimeMachine<'tcx>>, ) -> Self { let alloc_id = mplace.ptr().provenance.unwrap().alloc_id(); let alloc = ecx.memory.alloc_map.swap_remove(&alloc_id).unwrap().1; @@ -93,7 +93,7 @@ impl<'tcx> InterpretationResult<'tcx> for mir::interpret::ConstAllocation<'tcx> } pub(crate) fn create_static_alloc<'tcx>( - ecx: &mut CompileTimeEvalContext<'tcx>, + ecx: &mut CompileTimeInterpCx<'tcx>, static_def_id: LocalDefId, layout: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, MPlaceTy<'tcx>> { diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 45ea3ec08f8..50a4d0612cc 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -6,6 +6,7 @@ #![feature(box_patterns)] #![feature(decl_macro)] #![feature(if_let_guard)] +#![feature(is_none_or)] #![feature(let_chains)] #![feature(never_type)] #![feature(rustdoc_internals)] diff --git a/compiler/rustc_const_eval/src/util/caller_location.rs b/compiler/rustc_const_eval/src/util/caller_location.rs index 62c5f8734a2..3b07bee2d9c 100644 --- a/compiler/rustc_const_eval/src/util/caller_location.rs +++ b/compiler/rustc_const_eval/src/util/caller_location.rs @@ -7,12 +7,12 @@ use rustc_middle::ty::{self, Mutability}; use rustc_span::symbol::Symbol; use tracing::trace; -use crate::const_eval::{mk_eval_cx_to_read_const_val, CanAccessMutGlobal, CompileTimeEvalContext}; +use crate::const_eval::{mk_eval_cx_to_read_const_val, CanAccessMutGlobal, CompileTimeInterpCx}; use crate::interpret::*; /// Allocate a `const core::panic::Location` with the provided filename and line/column numbers. fn alloc_caller_location<'tcx>( - ecx: &mut CompileTimeEvalContext<'tcx>, + ecx: &mut CompileTimeInterpCx<'tcx>, filename: Symbol, line: u32, col: u32, diff --git a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs index 68fb122a765..daf57285ebe 100644 --- a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs +++ b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs @@ -3,7 +3,7 @@ use rustc_middle::ty::layout::{LayoutCx, LayoutError, LayoutOf, TyAndLayout, Val use rustc_middle::ty::{ParamEnv, ParamEnvAnd, Ty, TyCtxt}; use rustc_target::abi::{Abi, FieldsShape, Scalar, Variants}; -use crate::const_eval::{CanAccessMutGlobal, CheckAlignment, CompileTimeInterpreter}; +use crate::const_eval::{CanAccessMutGlobal, CheckAlignment, CompileTimeMachine}; use crate::interpret::{InterpCx, MemoryKind, OpTy}; /// Determines if this type permits "raw" initialization by just transmuting some memory into an @@ -45,7 +45,7 @@ fn might_permit_raw_init_strict<'tcx>( tcx: TyCtxt<'tcx>, kind: ValidityRequirement, ) -> Result<bool, &'tcx LayoutError<'tcx>> { - let machine = CompileTimeInterpreter::new(CanAccessMutGlobal::No, CheckAlignment::Error); + let machine = CompileTimeMachine::new(CanAccessMutGlobal::No, CheckAlignment::Error); let mut cx = InterpCx::new(tcx, rustc_span::DUMMY_SP, ParamEnv::reveal_all(), machine); diff --git a/compiler/rustc_data_structures/src/graph/scc/mod.rs b/compiler/rustc_data_structures/src/graph/scc/mod.rs index 7f36e4ca16d..8b96b36a851 100644 --- a/compiler/rustc_data_structures/src/graph/scc/mod.rs +++ b/compiler/rustc_data_structures/src/graph/scc/mod.rs @@ -4,52 +4,119 @@ //! node in the graph. This uses [Tarjan's algorithm]( //! https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm) //! that completes in *O*(*n*) time. +//! Optionally, also annotate the SCC nodes with some commutative data. +//! Typical examples would include: minimum element in SCC, maximum element +//! reachable from it, etc. use crate::fx::FxHashSet; use crate::graph::vec_graph::VecGraph; use crate::graph::{DirectedGraph, NumEdges, Successors}; use rustc_index::{Idx, IndexSlice, IndexVec}; +use std::fmt::Debug; use std::ops::Range; use tracing::{debug, instrument}; #[cfg(test)] mod tests; +/// An annotation for an SCC. This can be a representative, +/// the max/min element of the SCC, or all of the above. +/// +/// Concretely, the both merge operations must commute, e.g. where `merge` +/// is `merge_scc` and `merge_reached`: `a.merge(b) == b.merge(a)` +/// +/// In general, what you want is probably always min/max according +/// to some ordering, potentially with side constraints (min x such +/// that P holds). +pub trait Annotation: Debug + Copy { + /// Merge two existing annotations into one during + /// path compression.o + fn merge_scc(self, other: Self) -> Self; + + /// Merge a successor into this annotation. + fn merge_reached(self, other: Self) -> Self; + + fn update_scc(&mut self, other: Self) { + *self = self.merge_scc(other) + } + + fn update_reachable(&mut self, other: Self) { + *self = self.merge_reached(other) + } +} + +/// The empty annotation, which does nothing. +impl Annotation for () { + fn merge_reached(self, _other: Self) -> Self { + () + } + fn merge_scc(self, _other: Self) -> Self { + () + } +} + /// Strongly connected components (SCC) of a graph. The type `N` is /// the index type for the graph nodes and `S` is the index type for /// the SCCs. We can map from each node to the SCC that it /// participates in, and we also have the successors of each SCC. -pub struct Sccs<N: Idx, S: Idx> { +pub struct Sccs<N: Idx, S: Idx, A: Annotation = ()> { /// For each node, what is the SCC index of the SCC to which it /// belongs. scc_indices: IndexVec<N, S>, - /// Data about each SCC. - scc_data: SccData<S>, + /// Data about all the SCCs. + scc_data: SccData<S, A>, } -pub struct SccData<S: Idx> { - /// For each SCC, the range of `all_successors` where its +/// Information about an invidividual SCC node. +struct SccDetails<A: Annotation> { + /// For this SCC, the range of `all_successors` where its /// successors can be found. - ranges: IndexVec<S, Range<usize>>, + range: Range<usize>, + + /// User-specified metadata about the SCC. + annotation: A, +} + +// The name of this struct should discourage you from making it public and leaking +// its representation. This message was left here by one who came before you, +// who learnt the hard way that making even small changes in representation +// is difficult when it's publicly inspectable. +// +// Obey the law of Demeter! +struct SccData<S: Idx, A: Annotation> { + /// Maps SCC indices to their metadata, including + /// offsets into `all_successors`. + scc_details: IndexVec<S, SccDetails<A>>, /// Contains the successors for all the Sccs, concatenated. The /// range of indices corresponding to a given SCC is found in its - /// SccData. + /// `scc_details.range`. all_successors: Vec<S>, } -impl<N: Idx, S: Idx + Ord> Sccs<N, S> { +impl<N: Idx, S: Idx + Ord> Sccs<N, S, ()> { + /// Compute SCCs without annotations. pub fn new(graph: &impl Successors<Node = N>) -> Self { - SccsConstruction::construct(graph) + Self::new_with_annotation(graph, |_| ()) } +} - pub fn scc_indices(&self) -> &IndexSlice<N, S> { - &self.scc_indices +impl<N: Idx, S: Idx + Ord, A: Annotation> Sccs<N, S, A> { + /// Compute SCCs and annotate them with a user-supplied annotation + pub fn new_with_annotation<F: Fn(N) -> A>( + graph: &impl Successors<Node = N>, + to_annotation: F, + ) -> Self { + SccsConstruction::construct(graph, to_annotation) } - pub fn scc_data(&self) -> &SccData<S> { - &self.scc_data + pub fn annotation(&self, scc: S) -> A { + self.scc_data.annotation(scc) + } + + pub fn scc_indices(&self) -> &IndexSlice<N, S> { + &self.scc_indices } /// Returns the number of SCCs in the graph. @@ -90,7 +157,7 @@ impl<N: Idx, S: Idx + Ord> Sccs<N, S> { } } -impl<N: Idx, S: Idx + Ord> DirectedGraph for Sccs<N, S> { +impl<N: Idx, S: Idx + Ord, A: Annotation> DirectedGraph for Sccs<N, S, A> { type Node = S; fn num_nodes(&self) -> usize { @@ -98,43 +165,33 @@ impl<N: Idx, S: Idx + Ord> DirectedGraph for Sccs<N, S> { } } -impl<N: Idx, S: Idx + Ord> NumEdges for Sccs<N, S> { +impl<N: Idx, S: Idx + Ord, A: Annotation> NumEdges for Sccs<N, S, A> { fn num_edges(&self) -> usize { self.scc_data.all_successors.len() } } -impl<N: Idx, S: Idx + Ord> Successors for Sccs<N, S> { +impl<N: Idx, S: Idx + Ord, A: Annotation> Successors for Sccs<N, S, A> { fn successors(&self, node: S) -> impl Iterator<Item = Self::Node> { self.successors(node).iter().cloned() } } -impl<S: Idx> SccData<S> { +impl<S: Idx, A: Annotation> SccData<S, A> { /// Number of SCCs, fn len(&self) -> usize { - self.ranges.len() - } - - pub fn ranges(&self) -> &IndexSlice<S, Range<usize>> { - &self.ranges - } - - pub fn all_successors(&self) -> &Vec<S> { - &self.all_successors + self.scc_details.len() } /// Returns the successors of the given SCC. fn successors(&self, scc: S) -> &[S] { - // Annoyingly, `range` does not implement `Copy`, so we have - // to do `range.start..range.end`: - let range = &self.ranges[scc]; - &self.all_successors[range.start..range.end] + &self.all_successors[self.scc_details[scc].range.clone()] } /// Creates a new SCC with `successors` as its successors and + /// the maximum weight of its internal nodes `scc_max_weight` and /// returns the resulting index. - fn create_scc(&mut self, successors: impl IntoIterator<Item = S>) -> S { + fn create_scc(&mut self, successors: impl IntoIterator<Item = S>, annotation: A) -> S { // Store the successors on `scc_successors_vec`, remembering // the range of indices. let all_successors_start = self.all_successors.len(); @@ -142,22 +199,35 @@ impl<S: Idx> SccData<S> { let all_successors_end = self.all_successors.len(); debug!( - "create_scc({:?}) successors={:?}", - self.ranges.len(), + "create_scc({:?}) successors={:?}, annotation={:?}", + self.len(), &self.all_successors[all_successors_start..all_successors_end], + annotation ); - self.ranges.push(all_successors_start..all_successors_end) + let range = all_successors_start..all_successors_end; + let metadata = SccDetails { range, annotation }; + self.scc_details.push(metadata) + } + + fn annotation(&self, scc: S) -> A { + self.scc_details[scc].annotation } } -struct SccsConstruction<'c, G: DirectedGraph + Successors, S: Idx> { +struct SccsConstruction<'c, G, S, A, F> +where + G: DirectedGraph + Successors, + S: Idx, + A: Annotation, + F: Fn(G::Node) -> A, +{ graph: &'c G, /// The state of each node; used during walk to record the stack /// and after walk to record what cycle each node ended up being /// in. - node_states: IndexVec<G::Node, NodeState<G::Node, S>>, + node_states: IndexVec<G::Node, NodeState<G::Node, S, A>>, /// The stack of nodes that we are visiting as part of the DFS. node_stack: Vec<G::Node>, @@ -174,26 +244,34 @@ struct SccsConstruction<'c, G: DirectedGraph + Successors, S: Idx> { /// around between successors to amortize memory allocation costs. duplicate_set: FxHashSet<S>, - scc_data: SccData<S>, + scc_data: SccData<S, A>, + + /// A function that constructs an initial SCC annotation + /// out of a single node. + to_annotation: F, } #[derive(Copy, Clone, Debug)] -enum NodeState<N, S> { +enum NodeState<N, S, A> { /// This node has not yet been visited as part of the DFS. /// /// After SCC construction is complete, this state ought to be /// impossible. NotVisited, - /// This node is currently being walk as part of our DFS. It is on - /// the stack at the depth `depth`. + /// This node is currently being walked as part of our DFS. It is on + /// the stack at the depth `depth` and its current annotation is + /// `annotation`. /// /// After SCC construction is complete, this state ought to be /// impossible. - BeingVisited { depth: usize }, + BeingVisited { depth: usize, annotation: A }, - /// Indicates that this node is a member of the given cycle. - InCycle { scc_index: S }, + /// Indicates that this node is a member of the given cycle where + /// the merged annotation is `annotation`. + /// Note that an SCC can have several cycles, so its final annotation + /// is the merged value of all its member annotations. + InCycle { scc_index: S, annotation: A }, /// Indicates that this node is a member of whatever cycle /// `parent` is a member of. This state is transient: whenever we @@ -203,16 +281,27 @@ enum NodeState<N, S> { InCycleWith { parent: N }, } +/// The state of walking a given node. #[derive(Copy, Clone, Debug)] -enum WalkReturn<S> { - Cycle { min_depth: usize }, - Complete { scc_index: S }, +enum WalkReturn<S, A> { + /// The walk found a cycle, but the entire component is not known to have + /// been fully walked yet. We only know the minimum depth of this + /// component in a minimum spanning tree of the graph. This component + /// is tentatively represented by the state of the first node of this + /// cycle we met, which is at `min_depth`. + Cycle { min_depth: usize, annotation: A }, + /// The SCC and everything reachable from it have been fully walked. + /// At this point we know what is inside the SCC as we have visited every + /// node reachable from it. The SCC can now be fully represented by its ID. + Complete { scc_index: S, annotation: A }, } -impl<'c, G, S> SccsConstruction<'c, G, S> +impl<'c, G, S, A, F> SccsConstruction<'c, G, S, A, F> where G: DirectedGraph + Successors, S: Idx, + F: Fn(G::Node) -> A, + A: Annotation, { /// Identifies SCCs in the graph `G` and computes the resulting /// DAG. This uses a variant of [Tarjan's @@ -225,8 +314,10 @@ where /// D' (i.e., D' < D), we know that N, N', and all nodes in /// between them on the stack are part of an SCC. /// + /// Additionally, we keep track of a current annotation of the SCC. + /// /// [wikipedia]: https://bit.ly/2EZIx84 - fn construct(graph: &'c G) -> Sccs<G::Node, S> { + fn construct(graph: &'c G, to_annotation: F) -> Sccs<G::Node, S, A> { let num_nodes = graph.num_nodes(); let mut this = Self { @@ -234,15 +325,16 @@ where node_states: IndexVec::from_elem_n(NodeState::NotVisited, num_nodes), node_stack: Vec::with_capacity(num_nodes), successors_stack: Vec::new(), - scc_data: SccData { ranges: IndexVec::new(), all_successors: Vec::new() }, + scc_data: SccData { scc_details: IndexVec::new(), all_successors: Vec::new() }, duplicate_set: FxHashSet::default(), + to_annotation, }; let scc_indices = (0..num_nodes) .map(G::Node::new) .map(|node| match this.start_walk_from(node) { - WalkReturn::Complete { scc_index } => scc_index, - WalkReturn::Cycle { min_depth } => { + WalkReturn::Complete { scc_index, .. } => scc_index, + WalkReturn::Cycle { min_depth, .. } => { panic!("`start_walk_node({node:?})` returned cycle with depth {min_depth:?}") } }) @@ -251,12 +343,8 @@ where Sccs { scc_indices, scc_data: this.scc_data } } - fn start_walk_from(&mut self, node: G::Node) -> WalkReturn<S> { - if let Some(result) = self.inspect_node(node) { - result - } else { - self.walk_unvisited_node(node) - } + fn start_walk_from(&mut self, node: G::Node) -> WalkReturn<S, A> { + self.inspect_node(node).unwrap_or_else(|| self.walk_unvisited_node(node)) } /// Inspect a node during the DFS. We first examine its current @@ -271,11 +359,15 @@ where /// Otherwise, we are looking at a node that has already been /// completely visited. We therefore return `WalkReturn::Complete` /// with its associated SCC index. - fn inspect_node(&mut self, node: G::Node) -> Option<WalkReturn<S>> { + fn inspect_node(&mut self, node: G::Node) -> Option<WalkReturn<S, A>> { Some(match self.find_state(node) { - NodeState::InCycle { scc_index } => WalkReturn::Complete { scc_index }, + NodeState::InCycle { scc_index, annotation } => { + WalkReturn::Complete { scc_index, annotation } + } - NodeState::BeingVisited { depth: min_depth } => WalkReturn::Cycle { min_depth }, + NodeState::BeingVisited { depth: min_depth, annotation } => { + WalkReturn::Cycle { min_depth, annotation } + } NodeState::NotVisited => return None, @@ -290,7 +382,7 @@ where /// of `r2` (and updates `r` to reflect current result). This is /// basically the "find" part of a standard union-find algorithm /// (with path compression). - fn find_state(&mut self, mut node: G::Node) -> NodeState<G::Node, S> { + fn find_state(&mut self, mut node: G::Node) -> NodeState<G::Node, S, A> { // To avoid recursion we temporarily reuse the `parent` of each // InCycleWith link to encode a downwards link while compressing // the path. After we have found the root or deepest node being @@ -306,24 +398,40 @@ where // found the initial self-loop. let mut previous_node = node; - // Ultimately assigned by the parent when following + // Ultimately propagated to all the transitive parents when following // `InCycleWith` upwards. - let node_state = loop { - debug!("find_state(r = {:?} in state {:?})", node, self.node_states[node]); - match self.node_states[node] { - NodeState::InCycle { scc_index } => break NodeState::InCycle { scc_index }, - NodeState::BeingVisited { depth } => break NodeState::BeingVisited { depth }, - NodeState::NotVisited => break NodeState::NotVisited, - NodeState::InCycleWith { parent } => { - // We test this, to be extremely sure that we never - // ever break our termination condition for the - // reverse iteration loop. - assert!(node != parent, "Node can not be in cycle with itself"); - // Store the previous node as an inverted list link - self.node_states[node] = NodeState::InCycleWith { parent: previous_node }; - // Update to parent node. - previous_node = node; - node = parent; + // This loop performs the downward link encoding mentioned above. Details below! + // Note that there are two different states being assigned: the root state, and + // a potentially derived version of the root state for non-root nodes in the chain. + let (root_state, assigned_state) = { + loop { + debug!("find_state(r = {node:?} in state {:?})", self.node_states[node]); + match self.node_states[node] { + // This must have been the first and only state since it is unexplored*; + // no update needed! * Unless there is a bug :') + s @ NodeState::NotVisited => return s, + // We are in a completely discovered SCC; every node on our path is in that SCC: + s @ NodeState::InCycle { .. } => break (s, s), + // The Interesting Third Base Case: we are a path back to a root node + // still being explored. Now we need that node to keep its state and + // every other node to be recorded as being in whatever component that + // ends up in. + s @ NodeState::BeingVisited { depth, .. } => { + break (s, NodeState::InCycleWith { parent: self.node_stack[depth] }); + } + // We are not at the head of a path; keep compressing it! + NodeState::InCycleWith { parent } => { + // We test this, to be extremely sure that we never + // ever break our termination condition for the + // reverse iteration loop. + assert!(node != parent, "Node can not be in cycle with itself"); + + // Store the previous node as an inverted list link + self.node_states[node] = NodeState::InCycleWith { parent: previous_node }; + // Update to parent node. + previous_node = node; + node = parent; + } } } }; @@ -365,10 +473,14 @@ where // Move backwards until we found the node where we started. We // will know when we hit the state where previous_node == node. loop { - // Back at the beginning, we can return. + // Back at the beginning, we can return. Note that we return the root state. + // This is becuse for components being explored, we would otherwise get a + // `node_state[n] = InCycleWith{ parent: n }` and that's wrong. if previous_node == node { - return node_state; + return root_state; } + debug!("Compressing {node:?} down to {previous_node:?} with state {assigned_state:?}"); + // Update to previous node in the link. match self.node_states[previous_node] { NodeState::InCycleWith { parent: previous } => { @@ -376,34 +488,14 @@ where previous_node = previous; } // Only InCycleWith nodes were added to the reverse linked list. - other => panic!("Invalid previous link while compressing cycle: {other:?}"), + other => unreachable!("Invalid previous link while compressing cycle: {other:?}"), } - debug!("find_state: parent_state = {:?}", node_state); - - // Update the node state from the parent state. The assigned - // state is actually a loop invariant but it will only be - // evaluated if there is at least one backlink to follow. - // Fully trusting llvm here to find this loop optimization. - match node_state { - // Path compression, make current node point to the same root. - NodeState::InCycle { .. } => { - self.node_states[node] = node_state; - } - // Still visiting nodes, compress to cycle to the node - // at that depth. - NodeState::BeingVisited { depth } => { - self.node_states[node] = - NodeState::InCycleWith { parent: self.node_stack[depth] }; - } - // These are never allowed as parent nodes. InCycleWith - // should have been followed to a real parent and - // NotVisited can not be part of a cycle since it should - // have instead gotten explored. - NodeState::NotVisited | NodeState::InCycleWith { .. } => { - panic!("invalid parent state: {node_state:?}") - } - } + // Update the node state to the (potentially derived) state. + // If the root is still being explored, this is + // `InCycleWith{ parent: <root node>}`, otherwise + // `assigned_state == root_state`. + self.node_states[node] = assigned_state; } } @@ -413,30 +505,36 @@ where /// caller decide avoids mutual recursion between the two methods and allows /// us to maintain an allocated stack for nodes on the path between calls. #[instrument(skip(self, initial), level = "debug")] - fn walk_unvisited_node(&mut self, initial: G::Node) -> WalkReturn<S> { - struct VisitingNodeFrame<G: DirectedGraph, Successors> { + fn walk_unvisited_node(&mut self, initial: G::Node) -> WalkReturn<S, A> { + debug!("Walk unvisited node: {initial:?}"); + struct VisitingNodeFrame<G: DirectedGraph, Successors, A> { node: G::Node, - iter: Option<Successors>, + successors: Option<Successors>, depth: usize, min_depth: usize, successors_len: usize, min_cycle_root: G::Node, successor_node: G::Node, + /// The annotation for the SCC starting in `node`. It may or may + /// not contain other nodes. + current_component_annotation: A, } // Move the stack to a local variable. We want to utilize the existing allocation and // mutably borrow it without borrowing self at the same time. let mut successors_stack = core::mem::take(&mut self.successors_stack); + debug_assert_eq!(successors_stack.len(), 0); - let mut stack: Vec<VisitingNodeFrame<G, _>> = vec![VisitingNodeFrame { + let mut stack: Vec<VisitingNodeFrame<G, _, _>> = vec![VisitingNodeFrame { node: initial, depth: 0, min_depth: 0, - iter: None, + successors: None, successors_len: 0, min_cycle_root: initial, successor_node: initial, + current_component_annotation: (self.to_annotation)(initial), }]; let mut return_value = None; @@ -445,18 +543,26 @@ where let VisitingNodeFrame { node, depth, - iter, + successors, successors_len, min_depth, min_cycle_root, successor_node, + current_component_annotation, } = frame; - let node = *node; let depth = *depth; - let successors = match iter { - Some(iter) => iter, + // node is definitely in the current component, add it to the annotation. + if node != initial { + current_component_annotation.update_scc((self.to_annotation)(node)); + } + debug!( + "Visiting {node:?} at depth {depth:?}, annotation: {current_component_annotation:?}" + ); + + let successors = match successors { + Some(successors) => successors, None => { // This None marks that we still have the initialize this node's frame. debug!(?depth, ?node); @@ -464,7 +570,10 @@ where debug_assert!(matches!(self.node_states[node], NodeState::NotVisited)); // Push `node` onto the stack. - self.node_states[node] = NodeState::BeingVisited { depth }; + self.node_states[node] = NodeState::BeingVisited { + depth, + annotation: *current_component_annotation, + }; self.node_stack.push(node); // Walk each successor of the node, looking to see if any of @@ -472,11 +581,11 @@ where // so, that means they can also reach us. *successors_len = successors_stack.len(); // Set and return a reference, this is currently empty. - iter.get_or_insert(self.graph.successors(node)) + successors.get_or_insert(self.graph.successors(node)) } }; - // Now that iter is initialized, this is a constant for this frame. + // Now that the successors iterator is initialized, this is a constant for this frame. let successors_len = *successors_len; // Construct iterators for the nodes and walk results. There are two cases: @@ -489,10 +598,17 @@ where debug!(?node, ?successor_node); (successor_node, self.inspect_node(successor_node)) }); - for (successor_node, walk) in returned_walk.chain(successor_walk) { match walk { - Some(WalkReturn::Cycle { min_depth: successor_min_depth }) => { + // The starting node `node` leads to a cycle whose earliest node, + // `successor_node`, is at `min_depth`. There may be more cycles. + Some(WalkReturn::Cycle { + min_depth: successor_min_depth, + annotation: successor_annotation, + }) => { + debug!( + "Cycle found from {node:?}, minimum depth: {successor_min_depth:?}, annotation: {successor_annotation:?}" + ); // Track the minimum depth we can reach. assert!(successor_min_depth <= depth); if successor_min_depth < *min_depth { @@ -500,41 +616,56 @@ where *min_depth = successor_min_depth; *min_cycle_root = successor_node; } + current_component_annotation.update_scc(successor_annotation); } - - Some(WalkReturn::Complete { scc_index: successor_scc_index }) => { + // The starting node `node` is succeeded by a fully identified SCC + // which is now added to the set under `scc_index`. + Some(WalkReturn::Complete { + scc_index: successor_scc_index, + annotation: successor_annotation, + }) => { + debug!( + "Complete; {node:?} is root of complete-visited SCC idx {successor_scc_index:?} with annotation {successor_annotation:?}" + ); // Push the completed SCC indices onto // the `successors_stack` for later. debug!(?node, ?successor_scc_index); successors_stack.push(successor_scc_index); + current_component_annotation.update_reachable(successor_annotation); } - + // `node` has no more (direct) successors; search recursively. None => { let depth = depth + 1; + debug!("Recursing down into {successor_node:?} at depth {depth:?}"); debug!(?depth, ?successor_node); // Remember which node the return value will come from. frame.successor_node = successor_node; - // Start a new stack frame the step into it. + // Start a new stack frame, then step into it. stack.push(VisitingNodeFrame { node: successor_node, depth, - iter: None, + successors: None, successors_len: 0, min_depth: depth, min_cycle_root: successor_node, successor_node, + current_component_annotation: (self.to_annotation)(successor_node), }); continue 'recurse; } } } + debug!("Finished walk from {node:?} with annotation: {current_component_annotation:?}"); + // Completed walk, remove `node` from the stack. let r = self.node_stack.pop(); debug_assert_eq!(r, Some(node)); // Remove the frame, it's done. let frame = stack.pop().unwrap(); + let current_component_annotation = frame.current_component_annotation; + debug_assert_eq!(frame.node, node); // If `min_depth == depth`, then we are the root of the // cycle: we can't reach anyone further down the stack. @@ -543,6 +674,8 @@ where // We return one frame at a time so there can't be another return value. debug_assert!(return_value.is_none()); return_value = Some(if frame.min_depth == depth { + // We are at the head of the component. + // Note that successor stack may have duplicates, so we // want to remove those: let deduplicated_successors = { @@ -552,15 +685,25 @@ where .drain(successors_len..) .filter(move |&i| duplicate_set.insert(i)) }; - let scc_index = self.scc_data.create_scc(deduplicated_successors); - self.node_states[node] = NodeState::InCycle { scc_index }; - WalkReturn::Complete { scc_index } + + debug!("Creating SCC rooted in {node:?} with successor {:?}", frame.successor_node); + + let scc_index = + self.scc_data.create_scc(deduplicated_successors, current_component_annotation); + + self.node_states[node] = + NodeState::InCycle { scc_index, annotation: current_component_annotation }; + + WalkReturn::Complete { scc_index, annotation: current_component_annotation } } else { // We are not the head of the cycle. Return back to our // caller. They will take ownership of the // `self.successors` data that we pushed. self.node_states[node] = NodeState::InCycleWith { parent: frame.min_cycle_root }; - WalkReturn::Cycle { min_depth: frame.min_depth } + WalkReturn::Cycle { + min_depth: frame.min_depth, + annotation: current_component_annotation, + } }); } diff --git a/compiler/rustc_data_structures/src/graph/scc/tests.rs b/compiler/rustc_data_structures/src/graph/scc/tests.rs index 513df666d0d..373f87bfdbc 100644 --- a/compiler/rustc_data_structures/src/graph/scc/tests.rs +++ b/compiler/rustc_data_structures/src/graph/scc/tests.rs @@ -3,10 +3,53 @@ extern crate test; use super::*; use crate::graph::tests::TestGraph; +#[derive(Copy, Clone, Debug)] +struct MaxReached(usize); +type UsizeSccs = Sccs<usize, usize, ()>; +type MaxReachedSccs = Sccs<usize, usize, MaxReached>; + +impl Annotation for MaxReached { + fn merge_scc(self, other: Self) -> Self { + Self(std::cmp::max(other.0, self.0)) + } + + fn merge_reached(self, other: Self) -> Self { + self.merge_scc(other) + } +} + +impl PartialEq<usize> for MaxReached { + fn eq(&self, other: &usize) -> bool { + &self.0 == other + } +} + +impl MaxReached { + fn from_usize(nr: usize) -> Self { + Self(nr) + } +} + +#[derive(Copy, Clone, Debug)] +struct MinMaxIn { + min: usize, + max: usize, +} + +impl Annotation for MinMaxIn { + fn merge_scc(self, other: Self) -> Self { + Self { min: std::cmp::min(self.min, other.min), max: std::cmp::max(self.max, other.max) } + } + + fn merge_reached(self, _other: Self) -> Self { + self + } +} + #[test] fn diamond() { let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (2, 3)]); - let sccs: Sccs<_, usize> = Sccs::new(&graph); + let sccs: UsizeSccs = Sccs::new(&graph); assert_eq!(sccs.num_sccs(), 4); assert_eq!(sccs.num_sccs(), 4); } @@ -34,7 +77,7 @@ fn test_big_scc() { +-- 2 <--+ */ let graph = TestGraph::new(0, &[(0, 1), (1, 2), (1, 3), (2, 0), (3, 2)]); - let sccs: Sccs<_, usize> = Sccs::new(&graph); + let sccs: UsizeSccs = Sccs::new(&graph); assert_eq!(sccs.num_sccs(), 1); } @@ -50,7 +93,7 @@ fn test_three_sccs() { +-- 2 <--+ */ let graph = TestGraph::new(0, &[(0, 1), (1, 2), (2, 1), (3, 2)]); - let sccs: Sccs<_, usize> = Sccs::new(&graph); + let sccs: UsizeSccs = Sccs::new(&graph); assert_eq!(sccs.num_sccs(), 3); assert_eq!(sccs.scc(0), 1); assert_eq!(sccs.scc(1), 0); @@ -106,7 +149,7 @@ fn test_find_state_2() { // 2 InCycleWith { 1 } // 3 InCycleWith { 0 } - let sccs: Sccs<_, usize> = Sccs::new(&graph); + let sccs: UsizeSccs = Sccs::new(&graph); assert_eq!(sccs.num_sccs(), 1); assert_eq!(sccs.scc(0), 0); assert_eq!(sccs.scc(1), 0); @@ -130,7 +173,7 @@ fn test_find_state_3() { */ let graph = TestGraph::new(0, &[(0, 1), (0, 4), (1, 2), (1, 3), (2, 1), (3, 0), (4, 2), (5, 2)]); - let sccs: Sccs<_, usize> = Sccs::new(&graph); + let sccs: UsizeSccs = Sccs::new(&graph); assert_eq!(sccs.num_sccs(), 2); assert_eq!(sccs.scc(0), 0); assert_eq!(sccs.scc(1), 0); @@ -165,7 +208,7 @@ fn test_deep_linear() { nodes.push((i - 1, i)); } let graph = TestGraph::new(0, nodes.as_slice()); - let sccs: Sccs<_, usize> = Sccs::new(&graph); + let sccs: UsizeSccs = Sccs::new(&graph); assert_eq!(sccs.num_sccs(), NR_NODES); assert_eq!(sccs.scc(0), NR_NODES - 1); assert_eq!(sccs.scc(NR_NODES - 1), 0); @@ -210,7 +253,164 @@ fn bench_sccc(b: &mut test::Bencher) { graph[21] = (7, 4); let graph = TestGraph::new(0, &graph[..]); b.iter(|| { - let sccs: Sccs<_, usize> = Sccs::new(&graph); + let sccs: UsizeSccs = Sccs::new(&graph); assert_eq!(sccs.num_sccs(), 3); }); } + +#[test] +fn test_max_self_loop() { + let graph = TestGraph::new(0, &[(0, 0)]); + let sccs: MaxReachedSccs = + Sccs::new_with_annotation(&graph, |n| if n == 0 { MaxReached(17) } else { MaxReached(0) }); + assert_eq!(sccs.annotation(0), 17); +} + +#[test] +fn test_max_branch() { + let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (2, 4)]); + let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, MaxReached::from_usize); + assert_eq!(sccs.annotation(sccs.scc(0)), 4); + assert_eq!(sccs.annotation(sccs.scc(1)), 3); + assert_eq!(sccs.annotation(sccs.scc(2)), 4); +} +#[test] +fn test_single_cycle_max() { + let graph = TestGraph::new(0, &[(0, 2), (2, 3), (2, 4), (4, 1), (1, 2)]); + let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, MaxReached::from_usize); + assert_eq!(sccs.annotation(sccs.scc(2)), 4); + assert_eq!(sccs.annotation(sccs.scc(0)), 4); +} + +#[test] +fn test_simple_cycle_max() { + let graph = TestGraph::new(0, &[(0, 1), (1, 2), (2, 0)]); + let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, MaxReached::from_usize); + assert_eq!(sccs.num_sccs(), 1); +} + +#[test] +fn test_double_cycle_max() { + let graph = + TestGraph::new(0, &[(0, 1), (1, 2), (1, 4), (2, 3), (2, 4), (3, 5), (4, 1), (5, 4)]); + let sccs: MaxReachedSccs = + Sccs::new_with_annotation(&graph, |n| if n == 5 { MaxReached(2) } else { MaxReached(1) }); + + assert_eq!(sccs.annotation(sccs.scc(0)).0, 2); +} + +#[test] +fn test_bug_minimised() { + let graph = TestGraph::new(0, &[(0, 3), (0, 1), (3, 2), (2, 3), (1, 4), (4, 5), (5, 4)]); + let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |n| match n { + 3 => MaxReached(1), + _ => MaxReached(0), + }); + assert_eq!(sccs.annotation(sccs.scc(2)), 1); + assert_eq!(sccs.annotation(sccs.scc(1)), 0); + assert_eq!(sccs.annotation(sccs.scc(4)), 0); +} + +#[test] +fn test_bug_max_leak_minimised() { + let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (3, 0), (3, 4), (4, 3)]); + let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |w| match w { + 4 => MaxReached(1), + _ => MaxReached(0), + }); + + assert_eq!(sccs.annotation(sccs.scc(2)), 0); + assert_eq!(sccs.annotation(sccs.scc(3)), 1); + assert_eq!(sccs.annotation(sccs.scc(0)), 1); +} + +#[test] +fn test_bug_max_leak() { + let graph = TestGraph::new( + 8, + &[ + (0, 0), + (0, 18), + (0, 19), + (0, 1), + (0, 2), + (0, 7), + (0, 8), + (0, 23), + (18, 0), + (18, 12), + (19, 0), + (19, 25), + (12, 18), + (12, 3), + (12, 5), + (3, 12), + (3, 21), + (3, 22), + (5, 13), + (21, 3), + (22, 3), + (13, 5), + (13, 4), + (4, 13), + (4, 0), + (2, 11), + (7, 6), + (6, 20), + (20, 6), + (8, 17), + (17, 9), + (9, 16), + (16, 26), + (26, 15), + (15, 10), + (10, 14), + (14, 27), + (23, 24), + ], + ); + let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |w| match w { + 22 => MaxReached(1), + 24 => MaxReached(2), + 27 => MaxReached(2), + _ => MaxReached(0), + }); + + assert_eq!(sccs.annotation(sccs.scc(2)), 0); + assert_eq!(sccs.annotation(sccs.scc(7)), 0); + assert_eq!(sccs.annotation(sccs.scc(8)), 2); + assert_eq!(sccs.annotation(sccs.scc(23)), 2); + assert_eq!(sccs.annotation(sccs.scc(3)), 2); + assert_eq!(sccs.annotation(sccs.scc(0)), 2); +} + +#[test] +fn test_bug_max_zero_stick_shape() { + let graph = TestGraph::new(0, &[(0, 1), (1, 2), (2, 3), (3, 2), (3, 4)]); + + let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |w| match w { + 4 => MaxReached(1), + _ => MaxReached(0), + }); + + assert_eq!(sccs.annotation(sccs.scc(0)), 1); + assert_eq!(sccs.annotation(sccs.scc(1)), 1); + assert_eq!(sccs.annotation(sccs.scc(2)), 1); + assert_eq!(sccs.annotation(sccs.scc(3)), 1); + assert_eq!(sccs.annotation(sccs.scc(4)), 1); +} + +#[test] +fn test_min_max_in() { + let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (3, 0), (3, 4), (4, 3), (3, 5)]); + let sccs: Sccs<usize, usize, MinMaxIn> = + Sccs::new_with_annotation(&graph, |w| MinMaxIn { min: w, max: w }); + + assert_eq!(sccs.annotation(sccs.scc(2)).min, 2); + assert_eq!(sccs.annotation(sccs.scc(2)).max, 2); + assert_eq!(sccs.annotation(sccs.scc(0)).min, 0); + assert_eq!(sccs.annotation(sccs.scc(0)).max, 4); + assert_eq!(sccs.annotation(sccs.scc(3)).min, 0); + assert_eq!(sccs.annotation(sccs.scc(3)).max, 4); + assert_eq!(sccs.annotation(sccs.scc(5)).min, 5); +} diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 042894beec2..458f1f14cbb 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1633,6 +1633,13 @@ pub struct ConstBlock { } /// An expression. +/// +/// For more details, see the [rust lang reference]. +/// Note that the reference does not document nightly-only features. +/// There may be also slight differences in the names and representation of AST nodes between +/// the compiler and the reference. +/// +/// [rust lang reference]: https://doc.rust-lang.org/reference/expressions.html #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct Expr<'hir> { pub hir_id: HirId, @@ -3147,6 +3154,13 @@ impl ItemId { /// An item /// /// The name might be a dummy name in case of anonymous items +/// +/// For more details, see the [rust lang reference]. +/// Note that the reference does not document nightly-only features. +/// There may be also slight differences in the names and representation of AST nodes between +/// the compiler and the reference. +/// +/// [rust lang reference]: https://doc.rust-lang.org/reference/items.html #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct Item<'hir> { pub ident: Ident, diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index c3ccba487ed..69461957f80 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -244,7 +244,9 @@ language_item_table! { AsyncIterator, sym::async_iterator, async_iterator_trait, Target::Trait, GenericRequirement::Exact(0); CoroutineState, sym::coroutine_state, coroutine_state, Target::Enum, GenericRequirement::None; - Coroutine, sym::coroutine, coroutine_trait, Target::Trait, GenericRequirement::Minimum(1); + Coroutine, sym::coroutine, coroutine_trait, Target::Trait, GenericRequirement::Exact(1); + CoroutineReturn, sym::coroutine_return, coroutine_return, Target::AssocTy, GenericRequirement::Exact(1); + CoroutineYield, sym::coroutine_yield, coroutine_yield, Target::AssocTy, GenericRequirement::Exact(1); CoroutineResume, sym::coroutine_resume, coroutine_resume, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; Unpin, sym::unpin, unpin_trait, Target::Trait, GenericRequirement::None; diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index 6f499947d5c..3ab319a037b 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -44,6 +44,9 @@ hir_typeck_convert_using_method = try using `{$sugg}` to convert `{$found}` to ` hir_typeck_ctor_is_private = tuple struct constructor `{$def}` is private +hir_typeck_dependency_on_unit_never_type_fallback = this function depends on never type fallback being `()` + .help = specify the types explicitly + hir_typeck_deref_is_empty = this expression `Deref`s to `{$deref_ty}` which implements `is_empty` hir_typeck_expected_default_return_type = expected `()` because of default return type diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index a599e8d05fd..281af80bff5 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -234,7 +234,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ret_ty = ret_coercion.borrow().expected_ty(); let ret_ty = self.infcx.shallow_resolve(ret_ty); self.can_coerce(arm_ty, ret_ty) - && prior_arm.map_or(true, |(_, ty, _)| self.can_coerce(ty, ret_ty)) + && prior_arm.is_none_or(|(_, ty, _)| self.can_coerce(ty, ret_ty)) // The match arms need to unify for the case of `impl Trait`. && !matches!(ret_ty.kind(), ty::Alias(ty::Opaque, ..)) } diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index e54a07786cd..fcd22b74676 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -913,7 +913,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { if self .tcx .upvars_mentioned(closure_def_id_a.expect_local()) - .map_or(true, |u| u.is_empty()) => + .is_none_or(|u| u.is_empty()) => { // We coerce the closure, which has fn type // `extern "rust-call" fn((arg0,arg1,...)) -> _` diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 3f12f252654..6dd34024fd1 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -183,6 +183,11 @@ pub enum NeverTypeFallbackFlowingIntoUnsafe { Deref, } +#[derive(LintDiagnostic)] +#[help] +#[diag(hir_typeck_dependency_on_unit_never_type_fallback)] +pub struct DependencyOnUnitNeverTypeFallback {} + #[derive(Subdiagnostic)] #[multipart_suggestion( hir_typeck_add_missing_parentheses_in_range, diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 3321f029c8d..7fd08159385 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1557,7 +1557,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the length is 0, we don't create any elements, so we don't copy any. If the length is 1, we // don't copy that one element, we move it. Only check for Copy if the length is larger. - if count.try_eval_target_usize(tcx, self.param_env).map_or(true, |len| len > 1) { + if count.try_eval_target_usize(tcx, self.param_env).is_none_or(|len| len > 1) { let lang_item = self.tcx.require_lang_item(LangItem::Copy, None); let code = traits::ObligationCauseCode::RepeatElementCopy { is_constable, diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index 5ba67c52ef2..b7937f458b3 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -14,6 +14,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable}; use rustc_session::lint; use rustc_span::DUMMY_SP; use rustc_span::{def_id::LocalDefId, Span}; +use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt}; #[derive(Copy, Clone)] pub enum DivergingFallbackBehavior { @@ -344,6 +345,9 @@ impl<'tcx> FnCtxt<'_, 'tcx> { // `!`. let mut diverging_fallback = UnordMap::with_capacity(diverging_vids.len()); let unsafe_infer_vars = OnceCell::new(); + + self.lint_obligations_broken_by_never_type_fallback_change(behavior, &diverging_vids); + for &diverging_vid in &diverging_vids { let diverging_ty = Ty::new_var(self.tcx, diverging_vid); let root_vid = self.root_var(diverging_vid); @@ -468,6 +472,52 @@ impl<'tcx> FnCtxt<'_, 'tcx> { } } + fn lint_obligations_broken_by_never_type_fallback_change( + &self, + behavior: DivergingFallbackBehavior, + diverging_vids: &[ty::TyVid], + ) { + let DivergingFallbackBehavior::ToUnit = behavior else { return }; + + // Fallback happens if and only if there are diverging variables + if diverging_vids.is_empty() { + return; + } + + // Returns errors which happen if fallback is set to `fallback` + let remaining_errors_if_fallback_to = |fallback| { + self.probe(|_| { + let obligations = self.fulfillment_cx.borrow().pending_obligations(); + let ocx = ObligationCtxt::new(&self.infcx); + ocx.register_obligations(obligations.iter().cloned()); + + for &diverging_vid in diverging_vids { + let diverging_ty = Ty::new_var(self.tcx, diverging_vid); + + ocx.eq(&ObligationCause::dummy(), self.param_env, diverging_ty, fallback) + .expect("expected diverging var to be unconstrained"); + } + + ocx.select_where_possible() + }) + }; + + // If we have no errors with `fallback = ()`, but *do* have errors with `fallback = !`, + // then this code will be broken by the never type fallback change.qba + let unit_errors = remaining_errors_if_fallback_to(self.tcx.types.unit); + if unit_errors.is_empty() + && let never_errors = remaining_errors_if_fallback_to(self.tcx.types.never) + && !never_errors.is_empty() + { + self.tcx.emit_node_span_lint( + lint::builtin::DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK, + self.tcx.local_def_id_to_hir_id(self.body_id), + self.tcx.def_span(self.body_id), + errors::DependencyOnUnitNeverTypeFallback {}, + ) + } + } + /// Returns a graph whose nodes are (unresolved) inference variables and where /// an edge `?A -> ?B` indicates that the variable `?A` is coerced to `?B`. fn create_coercion_graph(&self) -> VecGraph<ty::TyVid, true> { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index e20a6ef7c13..172d874d4ee 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -2240,7 +2240,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for (idx, (generic_param, param)) in params_with_generics.iter().enumerate().filter(|(idx, _)| { check_for_matched_generics - || expected_idx.map_or(true, |expected_idx| expected_idx == *idx) + || expected_idx.is_none_or(|expected_idx| expected_idx == *idx) }) { let Some(generic_param) = generic_param else { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 9ab89f3444c..8a88e5a6ff4 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -440,7 +440,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; // Given `Result<_, E>`, check our expected ty is `Result<_, &E>` for // `as_ref` and `as_deref` compatibility. - let error_tys_equate_as_ref = error_tys.map_or(true, |(found, expected)| { + let error_tys_equate_as_ref = error_tys.is_none_or(|(found, expected)| { self.can_eq( self.param_env, Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, found), @@ -492,7 +492,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && Some(adt.did()) == self.tcx.lang_items().string() && peeled.is_str() // `Result::map`, conversely, does not take ref of the error type. - && error_tys.map_or(true, |(found, expected)| { + && error_tys.is_none_or(|(found, expected)| { self.can_eq(self.param_env, found, expected) }) { diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index b0fd6de3496..a87ee7b4554 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -4,6 +4,7 @@ #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(if_let_guard)] +#![feature(is_none_or)] #![feature(let_chains)] #![feature(never_type)] #![feature(try_blocks)] diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index 01bd732a4cd..5e9c3c64e39 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -225,6 +225,50 @@ impl<'a, 'tcx> At<'a, 'tcx> { } } + /// Used in the new solver since we don't care about tracking an `ObligationCause`. + pub fn relate_no_trace<T>( + self, + expected: T, + variance: ty::Variance, + actual: T, + ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution> + where + T: Relate<TyCtxt<'tcx>>, + { + let mut fields = CombineFields::new( + self.infcx, + TypeTrace::dummy(self.cause), + self.param_env, + DefineOpaqueTypes::Yes, + ); + fields.sub().relate_with_variance( + variance, + ty::VarianceDiagInfo::default(), + expected, + actual, + )?; + Ok(fields.goals) + } + + /// Used in the new solver since we don't care about tracking an `ObligationCause`. + pub fn eq_structurally_relating_aliases_no_trace<T>( + self, + expected: T, + actual: T, + ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution> + where + T: Relate<TyCtxt<'tcx>>, + { + let mut fields = CombineFields::new( + self.infcx, + TypeTrace::dummy(self.cause), + self.param_env, + DefineOpaqueTypes::Yes, + ); + fields.equate(StructurallyRelateAliases::Yes).relate(expected, actual)?; + Ok(fields.goals) + } + /// Computes the least-upper-bound, or mutual supertype, of two /// values. The order of the arguments doesn't matter, but since /// this can result in an error (e.g., if asked to compute LUB of @@ -303,7 +347,7 @@ impl<'tcx> ToTrace<'tcx> for Ty<'tcx> { ) -> TypeTrace<'tcx> { TypeTrace { cause: cause.clone(), - values: Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())), + values: ValuePairs::Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())), } } } @@ -315,7 +359,10 @@ impl<'tcx> ToTrace<'tcx> for ty::Region<'tcx> { a: Self, b: Self, ) -> TypeTrace<'tcx> { - TypeTrace { cause: cause.clone(), values: Regions(ExpectedFound::new(a_is_expected, a, b)) } + TypeTrace { + cause: cause.clone(), + values: ValuePairs::Regions(ExpectedFound::new(a_is_expected, a, b)), + } } } @@ -328,7 +375,7 @@ impl<'tcx> ToTrace<'tcx> for Const<'tcx> { ) -> TypeTrace<'tcx> { TypeTrace { cause: cause.clone(), - values: Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())), + values: ValuePairs::Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())), } } } @@ -344,13 +391,13 @@ impl<'tcx> ToTrace<'tcx> for ty::GenericArg<'tcx> { cause: cause.clone(), values: match (a.unpack(), b.unpack()) { (GenericArgKind::Lifetime(a), GenericArgKind::Lifetime(b)) => { - Regions(ExpectedFound::new(a_is_expected, a, b)) + ValuePairs::Regions(ExpectedFound::new(a_is_expected, a, b)) } (GenericArgKind::Type(a), GenericArgKind::Type(b)) => { - Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())) + ValuePairs::Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())) } (GenericArgKind::Const(a), GenericArgKind::Const(b)) => { - Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())) + ValuePairs::Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())) } ( @@ -379,7 +426,10 @@ impl<'tcx> ToTrace<'tcx> for ty::Term<'tcx> { a: Self, b: Self, ) -> TypeTrace<'tcx> { - TypeTrace { cause: cause.clone(), values: Terms(ExpectedFound::new(a_is_expected, a, b)) } + TypeTrace { + cause: cause.clone(), + values: ValuePairs::Terms(ExpectedFound::new(a_is_expected, a, b)), + } } } @@ -392,7 +442,7 @@ impl<'tcx> ToTrace<'tcx> for ty::TraitRef<'tcx> { ) -> TypeTrace<'tcx> { TypeTrace { cause: cause.clone(), - values: TraitRefs(ExpectedFound::new(a_is_expected, a, b)), + values: ValuePairs::TraitRefs(ExpectedFound::new(a_is_expected, a, b)), } } } @@ -406,7 +456,7 @@ impl<'tcx> ToTrace<'tcx> for ty::AliasTy<'tcx> { ) -> TypeTrace<'tcx> { TypeTrace { cause: cause.clone(), - values: Aliases(ExpectedFound::new(a_is_expected, a.into(), b.into())), + values: ValuePairs::Aliases(ExpectedFound::new(a_is_expected, a.into(), b.into())), } } } @@ -418,7 +468,10 @@ impl<'tcx> ToTrace<'tcx> for ty::AliasTerm<'tcx> { a: Self, b: Self, ) -> TypeTrace<'tcx> { - TypeTrace { cause: cause.clone(), values: Aliases(ExpectedFound::new(a_is_expected, a, b)) } + TypeTrace { + cause: cause.clone(), + values: ValuePairs::Aliases(ExpectedFound::new(a_is_expected, a, b)), + } } } @@ -431,7 +484,7 @@ impl<'tcx> ToTrace<'tcx> for ty::FnSig<'tcx> { ) -> TypeTrace<'tcx> { TypeTrace { cause: cause.clone(), - values: PolySigs(ExpectedFound::new( + values: ValuePairs::PolySigs(ExpectedFound::new( a_is_expected, ty::Binder::dummy(a), ty::Binder::dummy(b), @@ -449,7 +502,7 @@ impl<'tcx> ToTrace<'tcx> for ty::PolyFnSig<'tcx> { ) -> TypeTrace<'tcx> { TypeTrace { cause: cause.clone(), - values: PolySigs(ExpectedFound::new(a_is_expected, a, b)), + values: ValuePairs::PolySigs(ExpectedFound::new(a_is_expected, a, b)), } } } @@ -463,7 +516,7 @@ impl<'tcx> ToTrace<'tcx> for ty::PolyExistentialTraitRef<'tcx> { ) -> TypeTrace<'tcx> { TypeTrace { cause: cause.clone(), - values: ExistentialTraitRef(ExpectedFound::new(a_is_expected, a, b)), + values: ValuePairs::ExistentialTraitRef(ExpectedFound::new(a_is_expected, a, b)), } } } @@ -477,7 +530,7 @@ impl<'tcx> ToTrace<'tcx> for ty::PolyExistentialProjection<'tcx> { ) -> TypeTrace<'tcx> { TypeTrace { cause: cause.clone(), - values: ExistentialProjection(ExpectedFound::new(a_is_expected, a, b)), + values: ValuePairs::ExistentialProjection(ExpectedFound::new(a_is_expected, a, b)), } } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index ed483c6cbeb..e15866f3f0f 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -64,11 +64,11 @@ use rustc_errors::{ codes::*, pluralize, struct_span_code_err, Applicability, Diag, DiagCtxt, DiagStyledString, ErrorGuaranteed, IntoDiagArg, StringPart, }; -use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; +use rustc_hir::{self as hir, ParamName}; use rustc_macros::extension; use rustc_middle::bug; use rustc_middle::dep_graph::DepContext; @@ -1707,6 +1707,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ValuePairs::ExistentialProjection(_) => { (false, Mismatch::Fixed("existential projection")) } + ValuePairs::Dummy => { + bug!("do not expect to report a type error from a ValuePairs::Dummy") + } }; let Some(vals) = self.values_str(values) else { // Derived error. Cancel the emitter. @@ -2250,12 +2253,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { values: ValuePairs<'tcx>, ) -> Option<(DiagStyledString, DiagStyledString, Option<PathBuf>)> { match values { - infer::Regions(exp_found) => self.expected_found_str(exp_found), - infer::Terms(exp_found) => self.expected_found_str_term(exp_found), - infer::Aliases(exp_found) => self.expected_found_str(exp_found), - infer::ExistentialTraitRef(exp_found) => self.expected_found_str(exp_found), - infer::ExistentialProjection(exp_found) => self.expected_found_str(exp_found), - infer::TraitRefs(exp_found) => { + ValuePairs::Regions(exp_found) => self.expected_found_str(exp_found), + ValuePairs::Terms(exp_found) => self.expected_found_str_term(exp_found), + ValuePairs::Aliases(exp_found) => self.expected_found_str(exp_found), + ValuePairs::ExistentialTraitRef(exp_found) => self.expected_found_str(exp_found), + ValuePairs::ExistentialProjection(exp_found) => self.expected_found_str(exp_found), + ValuePairs::TraitRefs(exp_found) => { let pretty_exp_found = ty::error::ExpectedFound { expected: exp_found.expected.print_trait_sugared(), found: exp_found.found.print_trait_sugared(), @@ -2267,7 +2270,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ret => ret, } } - infer::PolySigs(exp_found) => { + ValuePairs::PolySigs(exp_found) => { let exp_found = self.resolve_vars_if_possible(exp_found); if exp_found.references_error() { return None; @@ -2275,6 +2278,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let (exp, fnd) = self.cmp_fn_sig(&exp_found.expected, &exp_found.found); Some((exp, fnd, None)) } + ValuePairs::Dummy => { + bug!("do not expect to report a type error from a ValuePairs::Dummy") + } } } @@ -2423,7 +2429,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let (type_scope, type_param_sugg_span) = match bound_kind { GenericKind::Param(param) => { let generics = self.tcx.generics_of(generic_param_scope); - let def_id = generics.type_param(param, self.tcx).def_id.expect_local(); + let type_param = generics.type_param(param, self.tcx); + let def_id = type_param.def_id.expect_local(); let scope = self.tcx.local_def_id_to_hir_id(def_id).owner.def_id; // Get the `hir::Param` to verify whether it already has any bounds. // We do this to avoid suggesting code that ends up as `T: 'a'b`, @@ -2433,7 +2440,18 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { Some((span, open_paren_sp)) => Some((span, true, open_paren_sp)), // If `param` corresponds to `Self`, no usable suggestion span. None if generics.has_self && param.index == 0 => None, - None => Some((self.tcx.def_span(def_id).shrink_to_hi(), false, None)), + None => { + let span = if let Some(param) = + hir_generics.params.iter().find(|param| param.def_id == def_id) + && let ParamName::Plain(ident) = param.name + { + ident.span.shrink_to_hi() + } else { + let span = self.tcx.def_span(def_id); + span.shrink_to_hi() + }; + Some((span, false, None)) + } }; (scope, sugg_span) } diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs index effb4090692..bc59b5e033b 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs @@ -546,7 +546,7 @@ impl<T> Trait<T> for X { for pred in hir_generics.bounds_for_param(def_id) { if self.constrain_generic_bound_associated_type_structured_suggestion( diag, - &trait_ref, + trait_ref, pred.bounds, assoc, assoc_args, @@ -715,7 +715,7 @@ fn foo(&self) -> Self::T { String::new() } self.constrain_generic_bound_associated_type_structured_suggestion( diag, - &trait_ref, + trait_ref, opaque_hir_ty.bounds, assoc, assoc_args, @@ -869,7 +869,7 @@ fn foo(&self) -> Self::T { String::new() } fn constrain_generic_bound_associated_type_structured_suggestion( &self, diag: &mut Diag<'_>, - trait_ref: &ty::TraitRef<'tcx>, + trait_ref: ty::TraitRef<'tcx>, bounds: hir::GenericBounds<'_>, assoc: ty::AssocItem, assoc_args: &[ty::GenericArg<'tcx>], diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 32b50053b50..dcf415e720f 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -9,9 +9,8 @@ pub use rustc_middle::ty::IntVarValue; pub use BoundRegionConversionTime::*; pub use RegionVariableOrigin::*; pub use SubregionOrigin::*; -pub use ValuePairs::*; -use crate::infer::relate::RelateResult; +use crate::infer::relate::{Relate, RelateResult}; use crate::traits::{self, ObligationCause, ObligationInspector, PredicateObligation, TraitEngine}; use error_reporting::TypeErrCtxt; use free_regions::RegionRelations; @@ -35,6 +34,7 @@ use rustc_middle::infer::unify_key::{ConstVidKey, EffectVidKey}; use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult}; use rustc_middle::mir::ConstraintCategory; use rustc_middle::traits::select; +use rustc_middle::traits::solve::{Goal, NoSolution}; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::BoundVarReplacerDelegate; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; @@ -44,7 +44,7 @@ use rustc_middle::ty::{ConstVid, EffectVid, FloatVid, IntVid, TyVid}; use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgs, GenericArgsRef}; use rustc_middle::{bug, span_bug}; use rustc_span::symbol::Symbol; -use rustc_span::Span; +use rustc_span::{Span, DUMMY_SP}; use snapshot::undo_log::InferCtxtUndoLogs; use std::cell::{Cell, RefCell}; use std::fmt; @@ -352,27 +352,27 @@ impl<'tcx> ty::InferCtxtLike for InferCtxt<'tcx> { } } - fn universe_of_ct(&self, ct: ConstVid) -> Option<ty::UniverseIndex> { - // Same issue as with `universe_of_ty` - match self.probe_const_var(ct) { + fn universe_of_lt(&self, lt: ty::RegionVid) -> Option<ty::UniverseIndex> { + match self.inner.borrow_mut().unwrap_region_constraints().probe_value(lt) { Err(universe) => Some(universe), Ok(_) => None, } } - fn universe_of_lt(&self, lt: ty::RegionVid) -> Option<ty::UniverseIndex> { - match self.inner.borrow_mut().unwrap_region_constraints().probe_value(lt) { + fn universe_of_ct(&self, ct: ConstVid) -> Option<ty::UniverseIndex> { + // Same issue as with `universe_of_ty` + match self.probe_const_var(ct) { Err(universe) => Some(universe), Ok(_) => None, } } - fn opportunistic_resolve_lt_var(&self, vid: ty::RegionVid) -> ty::Region<'tcx> { - self.inner.borrow_mut().unwrap_region_constraints().opportunistic_resolve_var(self.tcx, vid) + fn root_ty_var(&self, var: TyVid) -> TyVid { + self.root_var(var) } - fn defining_opaque_types(&self) -> &'tcx ty::List<LocalDefId> { - self.defining_opaque_types + fn root_const_var(&self, var: ConstVid) -> ConstVid { + self.root_const_var(var) } fn opportunistic_resolve_ty_var(&self, vid: TyVid) -> Ty<'tcx> { @@ -405,6 +405,72 @@ impl<'tcx> ty::InferCtxtLike for InferCtxt<'tcx> { } } } + + fn opportunistic_resolve_lt_var(&self, vid: ty::RegionVid) -> ty::Region<'tcx> { + self.inner.borrow_mut().unwrap_region_constraints().opportunistic_resolve_var(self.tcx, vid) + } + + fn defining_opaque_types(&self) -> &'tcx ty::List<LocalDefId> { + self.defining_opaque_types + } + + fn next_ty_infer(&self) -> Ty<'tcx> { + self.next_ty_var(DUMMY_SP) + } + + fn next_const_infer(&self) -> ty::Const<'tcx> { + self.next_const_var(DUMMY_SP) + } + + fn fresh_args_for_item(&self, def_id: DefId) -> ty::GenericArgsRef<'tcx> { + self.fresh_args_for_item(DUMMY_SP, def_id) + } + + fn instantiate_binder_with_infer<T: TypeFoldable<Self::Interner> + Copy>( + &self, + value: ty::Binder<'tcx, T>, + ) -> T { + self.instantiate_binder_with_fresh_vars( + DUMMY_SP, + BoundRegionConversionTime::HigherRankedType, + value, + ) + } + + fn enter_forall<T: TypeFoldable<TyCtxt<'tcx>> + Copy, U>( + &self, + value: ty::Binder<'tcx, T>, + f: impl FnOnce(T) -> U, + ) -> U { + self.enter_forall(value, f) + } + + fn relate<T: Relate<TyCtxt<'tcx>>>( + &self, + param_env: ty::ParamEnv<'tcx>, + lhs: T, + variance: ty::Variance, + rhs: T, + ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution> { + self.at(&ObligationCause::dummy(), param_env).relate_no_trace(lhs, variance, rhs) + } + + fn eq_structurally_relating_aliases<T: Relate<TyCtxt<'tcx>>>( + &self, + param_env: ty::ParamEnv<'tcx>, + lhs: T, + rhs: T, + ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution> { + self.at(&ObligationCause::dummy(), param_env) + .eq_structurally_relating_aliases_no_trace(lhs, rhs) + } + + fn resolve_vars_if_possible<T>(&self, value: T) -> T + where + T: TypeFoldable<TyCtxt<'tcx>>, + { + self.resolve_vars_if_possible(value) + } } /// See the `error_reporting` module for more details. @@ -417,6 +483,7 @@ pub enum ValuePairs<'tcx> { PolySigs(ExpectedFound<ty::PolyFnSig<'tcx>>), ExistentialTraitRef(ExpectedFound<ty::PolyExistentialTraitRef<'tcx>>), ExistentialProjection(ExpectedFound<ty::PolyExistentialProjection<'tcx>>), + Dummy, } impl<'tcx> ValuePairs<'tcx> { @@ -1812,7 +1879,7 @@ impl<'tcx> TypeTrace<'tcx> { ) -> TypeTrace<'tcx> { TypeTrace { cause: cause.clone(), - values: Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())), + values: ValuePairs::Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())), } } @@ -1824,7 +1891,7 @@ impl<'tcx> TypeTrace<'tcx> { ) -> TypeTrace<'tcx> { TypeTrace { cause: cause.clone(), - values: TraitRefs(ExpectedFound::new(a_is_expected, a, b)), + values: ValuePairs::TraitRefs(ExpectedFound::new(a_is_expected, a, b)), } } @@ -1836,9 +1903,13 @@ impl<'tcx> TypeTrace<'tcx> { ) -> TypeTrace<'tcx> { TypeTrace { cause: cause.clone(), - values: Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())), + values: ValuePairs::Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())), } } + + fn dummy(cause: &ObligationCause<'tcx>) -> TypeTrace<'tcx> { + TypeTrace { cause: cause.clone(), values: ValuePairs::Dummy } + } } impl<'tcx> SubregionOrigin<'tcx> { diff --git a/compiler/rustc_infer/src/infer/relate/mod.rs b/compiler/rustc_infer/src/infer/relate/mod.rs index e7b50479b85..41cc945492d 100644 --- a/compiler/rustc_infer/src/infer/relate/mod.rs +++ b/compiler/rustc_infer/src/infer/relate/mod.rs @@ -4,11 +4,9 @@ pub use rustc_middle::ty::relate::*; -pub use self::_match::MatchAgainstFreshVars; pub use self::combine::CombineFields; pub use self::combine::PredicateEmittingRelation; -pub mod _match; pub(super) mod combine; mod generalize; mod glb; diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index cc12a4bf091..ab4148faaab 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -285,8 +285,7 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> { let obligations = predicates.predicates.iter().enumerate().map(|(index, &(clause, span))| { elaboratable.child_with_derived_cause( - clause - .instantiate_supertrait(tcx, &bound_clause.rebind(data.trait_ref)), + clause.instantiate_supertrait(tcx, bound_clause.rebind(data.trait_ref)), span, bound_clause.rebind(data), index, diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 726bd0de129..a12c76037e7 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -34,6 +34,7 @@ declare_lint_pass! { CONST_EVALUATABLE_UNCHECKED, CONST_ITEM_MUTATION, DEAD_CODE, + DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK, DEPRECATED, DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME, DEPRECATED_IN_FUTURE, @@ -3257,7 +3258,11 @@ declare_lint! { /// See the [Checking Conditional Configurations][check-cfg] section for more /// details. /// + /// See the [Cargo Specifics][unexpected_cfgs_lint_config] section for configuring this lint in + /// `Cargo.toml`. + /// /// [check-cfg]: https://doc.rust-lang.org/nightly/rustc/check-cfg.html + /// [unexpected_cfgs_lint_config]: https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html#check-cfg-in-lintsrust-table pub UNEXPECTED_CFGS, Warn, "detects unexpected names and values in `#[cfg]` conditions", @@ -4196,6 +4201,59 @@ declare_lint! { } declare_lint! { + /// The `dependency_on_unit_never_type_fallback` lint detects cases where code compiles with + /// [never type fallback] being [`()`], but will stop compiling with fallback being [`!`]. + /// + /// [never type fallback]: https://doc.rust-lang.org/nightly/core/primitive.never.html#never-type-fallback + /// [`!`]: https://doc.rust-lang.org/core/primitive.never.html + /// [`()`]: https://doc.rust-lang.org/core/primitive.unit.html + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(dependency_on_unit_never_type_fallback)] + /// fn main() { + /// if true { + /// // return has type `!` which, is some cases, causes never type fallback + /// return + /// } else { + /// // the type produced by this call is not specified explicitly, + /// // so it will be inferred from the previous branch + /// Default::default() + /// }; + /// // depending on the fallback, this may compile (because `()` implements `Default`), + /// // or it may not (because `!` does not implement `Default`) + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Due to historic reasons never type fallback was `()`, meaning that `!` got spontaneously + /// coerced to `()`. There are plans to change that, but they may make the code such as above + /// not compile. Instead of depending on the fallback, you should specify the type explicitly: + /// ``` + /// if true { + /// return + /// } else { + /// // type is explicitly specified, fallback can't hurt us no more + /// <() as Default>::default() + /// }; + /// ``` + /// + /// See [Tracking Issue for making `!` fall back to `!`](https://github.com/rust-lang/rust/issues/123748). + pub DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK, + Warn, + "never type fallback affecting unsafe function calls", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps, + reference: "issue #123748 <https://github.com/rust-lang/rust/issues/123748>", + }; + report_in_external_macro +} + +declare_lint! { /// The `byte_slice_in_packed_struct_with_derive` lint detects cases where a byte slice field /// (`[u8]`) or string slice field (`str`) is used in a `packed` struct that derives one or /// more built-in traits. diff --git a/compiler/rustc_middle/src/traits/util.rs b/compiler/rustc_middle/src/traits/util.rs index 707e076921b..adbb6cf2ddc 100644 --- a/compiler/rustc_middle/src/traits/util.rs +++ b/compiler/rustc_middle/src/traits/util.rs @@ -37,7 +37,7 @@ impl<'tcx> Elaborator<'tcx> { let super_predicates = self.tcx.super_predicates_of(trait_ref.def_id()).predicates.iter().filter_map( |&(pred, _)| { - let clause = pred.instantiate_supertrait(self.tcx, &trait_ref); + let clause = pred.instantiate_supertrait(self.tcx, trait_ref); self.visited.insert(clause).then_some(clause) }, ); diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 886dbd317af..a89ebe46c37 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -204,6 +204,23 @@ impl<'tcx> rustc_type_ir::inherent::AdtDef<TyCtxt<'tcx>> for AdtDef<'tcx> { fn def_id(self) -> DefId { self.did() } + + fn is_phantom_data(self) -> bool { + self.is_phantom_data() + } + + fn all_field_tys( + self, + tcx: TyCtxt<'tcx>, + ) -> ty::EarlyBinder<'tcx, impl Iterator<Item = Ty<'tcx>>> { + ty::EarlyBinder::bind( + self.all_fields().map(move |field| tcx.type_of(field.did).skip_binder()), + ) + } + + fn sized_constraint(self, tcx: TyCtxt<'tcx>) -> Option<ty::EarlyBinder<'tcx, Ty<'tcx>>> { + self.sized_constraint(tcx) + } } #[derive(Copy, Clone, Debug, Eq, PartialEq, HashStable, TyEncodable, TyDecodable)] diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 65d744239a6..3651c990c97 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -70,9 +70,9 @@ use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::{FieldIdx, Layout, LayoutS, TargetDataLayout, VariantIdx}; use rustc_target::spec::abi; use rustc_type_ir::fold::TypeFoldable; +use rustc_type_ir::lang_items::TraitSolverLangItem; use rustc_type_ir::TyKind::*; -use rustc_type_ir::WithCachedTypeInfo; -use rustc_type_ir::{CollectAndApply, Interner, TypeFlags}; +use rustc_type_ir::{CollectAndApply, Interner, TypeFlags, WithCachedTypeInfo}; use tracing::{debug, instrument}; use std::assert_matches::assert_matches; @@ -154,7 +154,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type VariancesOf = &'tcx [ty::Variance]; - fn variances_of(self, def_id: Self::DefId) -> Self::VariancesOf { + fn variances_of(self, def_id: DefId) -> Self::VariancesOf { self.variances_of(def_id) } @@ -198,7 +198,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { fn trait_ref_and_own_args_for_alias( self, - def_id: Self::DefId, + def_id: DefId, args: Self::GenericArgs, ) -> (rustc_type_ir::TraitRef<Self>, Self::GenericArgsSlice) { assert_matches!(self.def_kind(def_id), DefKind::AssocTy | DefKind::AssocConst); @@ -246,7 +246,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.mk_type_list_from_iter(args) } - fn parent(self, def_id: Self::DefId) -> Self::DefId { + fn parent(self, def_id: DefId) -> DefId { self.parent(def_id) } @@ -259,15 +259,85 @@ impl<'tcx> Interner for TyCtxt<'tcx> { fn features(self) -> Self::Features { self.features() } + + fn bound_coroutine_hidden_types( + self, + def_id: DefId, + ) -> impl IntoIterator<Item = ty::EarlyBinder<'tcx, ty::Binder<'tcx, Ty<'tcx>>>> { + self.bound_coroutine_hidden_types(def_id) + } + + fn fn_sig(self, def_id: DefId) -> ty::EarlyBinder<'tcx, ty::PolyFnSig<'tcx>> { + self.fn_sig(def_id) + } + + fn coroutine_movability(self, def_id: DefId) -> rustc_ast::Movability { + self.coroutine_movability(def_id) + } + + fn coroutine_for_closure(self, def_id: DefId) -> DefId { + self.coroutine_for_closure(def_id) + } + + fn generics_require_sized_self(self, def_id: DefId) -> bool { + self.generics_require_sized_self(def_id) + } + + fn item_bounds( + self, + def_id: DefId, + ) -> ty::EarlyBinder<'tcx, impl IntoIterator<Item = ty::Clause<'tcx>>> { + self.item_bounds(def_id).map_bound(IntoIterator::into_iter) + } + + fn super_predicates_of( + self, + def_id: DefId, + ) -> ty::EarlyBinder<'tcx, impl IntoIterator<Item = ty::Clause<'tcx>>> { + ty::EarlyBinder::bind( + self.super_predicates_of(def_id).instantiate_identity(self).predicates.into_iter(), + ) + } + + fn has_target_features(self, def_id: DefId) -> bool { + !self.codegen_fn_attrs(def_id).target_features.is_empty() + } + + fn require_lang_item(self, lang_item: TraitSolverLangItem) -> DefId { + self.require_lang_item( + match lang_item { + TraitSolverLangItem::Future => hir::LangItem::Future, + TraitSolverLangItem::FutureOutput => hir::LangItem::FutureOutput, + TraitSolverLangItem::AsyncFnKindHelper => hir::LangItem::AsyncFnKindHelper, + TraitSolverLangItem::AsyncFnKindUpvars => hir::LangItem::AsyncFnKindUpvars, + }, + None, + ) + } + + fn associated_type_def_ids(self, def_id: DefId) -> impl IntoIterator<Item = DefId> { + self.associated_items(def_id) + .in_definition_order() + .filter(|assoc_item| matches!(assoc_item.kind, ty::AssocKind::Type)) + .map(|assoc_item| assoc_item.def_id) + } } impl<'tcx> rustc_type_ir::inherent::Abi<TyCtxt<'tcx>> for abi::Abi { + fn rust() -> Self { + abi::Abi::Rust + } + fn is_rust(self) -> bool { matches!(self, abi::Abi::Rust) } } impl<'tcx> rustc_type_ir::inherent::Safety<TyCtxt<'tcx>> for hir::Safety { + fn safe() -> Self { + hir::Safety::Safe + } + fn is_safe(self) -> bool { matches!(self, hir::Safety::Safe) } @@ -281,6 +351,10 @@ impl<'tcx> rustc_type_ir::inherent::Features<TyCtxt<'tcx>> for &'tcx rustc_featu fn generic_const_exprs(self) -> bool { self.generic_const_exprs } + + fn coroutine_clone(self) -> bool { + self.coroutine_clone + } } type InternedSet<'tcx, T> = ShardedHashMap<InternedInSet<'tcx, T>, ()>; diff --git a/compiler/rustc_middle/src/ty/generic_args.rs b/compiler/rustc_middle/src/ty/generic_args.rs index 7fff3d01324..54c88e48614 100644 --- a/compiler/rustc_middle/src/ty/generic_args.rs +++ b/compiler/rustc_middle/src/ty/generic_args.rs @@ -41,6 +41,8 @@ pub struct GenericArg<'tcx> { marker: PhantomData<(Ty<'tcx>, ty::Region<'tcx>, ty::Const<'tcx>)>, } +impl<'tcx> rustc_type_ir::inherent::GenericArg<TyCtxt<'tcx>> for GenericArg<'tcx> {} + impl<'tcx> rustc_type_ir::inherent::GenericArgs<TyCtxt<'tcx>> for ty::GenericArgsRef<'tcx> { fn type_at(self, i: usize) -> Ty<'tcx> { self.type_at(i) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 83f8de6b6f9..c322c87bce4 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -488,6 +488,8 @@ pub struct Term<'tcx> { marker: PhantomData<(Ty<'tcx>, Const<'tcx>)>, } +impl<'tcx> rustc_type_ir::inherent::Term<TyCtxt<'tcx>> for Term<'tcx> {} + impl<'tcx> rustc_type_ir::inherent::IntoKind for Term<'tcx> { type Kind = TermKind<'tcx>; diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index c730f5117c5..ae36f2624ca 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -49,6 +49,18 @@ impl<'tcx> rustc_type_ir::inherent::Predicate<TyCtxt<'tcx>> for Predicate<'tcx> fn is_coinductive(self, interner: TyCtxt<'tcx>) -> bool { self.is_coinductive(interner) } + + fn allow_normalization(self) -> bool { + self.allow_normalization() + } +} + +impl<'tcx> rustc_type_ir::inherent::IntoKind for Predicate<'tcx> { + type Kind = ty::Binder<'tcx, ty::PredicateKind<'tcx>>; + + fn kind(self) -> Self::Kind { + self.kind() + } } impl<'tcx> rustc_type_ir::visit::Flags for Predicate<'tcx> { @@ -120,6 +132,7 @@ impl<'tcx> Predicate<'tcx> { /// unsoundly accept some programs. See #91068. #[inline] pub fn allow_normalization(self) -> bool { + // Keep this in sync with the one in `rustc_type_ir::inherent`! match self.kind().skip_binder() { PredicateKind::Clause(ClauseKind::WellFormed(_)) | PredicateKind::AliasRelate(..) @@ -313,7 +326,7 @@ impl<'tcx> Clause<'tcx> { pub fn instantiate_supertrait( self, tcx: TyCtxt<'tcx>, - trait_ref: &ty::PolyTraitRef<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, ) -> Clause<'tcx> { // The interaction between HRTB and supertraits is not entirely // obvious. Let me walk you (and myself) through an example. diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index ba9ed0d5b70..8308e537e5e 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -786,6 +786,10 @@ impl<'tcx> rustc_type_ir::inherent::Ty<TyCtxt<'tcx>> for Ty<'tcx> { tcx.types.bool } + fn new_u8(tcx: TyCtxt<'tcx>) -> Self { + tcx.types.u8 + } + fn new_infer(tcx: TyCtxt<'tcx>, infer: ty::InferTy) -> Self { Ty::new_infer(tcx, infer) } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 121a3b99a39..0f8f28e3462 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -330,8 +330,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { let is_sized = !self.feature_unsized_locals || self.local_decls[local].ty.is_sized(self.tcx, self.param_env); if is_sized { - self.rev_locals.ensure_contains_elem(value, SmallVec::new); - self.rev_locals[value].push(local); + self.rev_locals.ensure_contains_elem(value, SmallVec::new).push(local); } } diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index a72fbdb9021..6a74ddc5508 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -898,7 +898,7 @@ fn create_and_seed_worklist( match tcx.def_kind(id) { DefKind::Impl { .. } => false, DefKind::AssocConst | DefKind::AssocFn => !matches!(tcx.associated_item(id).container, AssocItemContainer::ImplContainer), - DefKind::Struct => struct_all_fields_are_public(tcx, id.to_def_id()), + DefKind::Struct => struct_all_fields_are_public(tcx, id.to_def_id()) || has_allow_dead_code_or_lang_attr(tcx, id).is_some(), _ => true }) .map(|id| (id, ComesFromAllowExpect::No)) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index d1541527cf5..8d470c6c61e 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -2783,33 +2783,65 @@ fn show_candidates( // by iterating through a hash map, so make sure they are ordered: for path_strings in [&mut accessible_path_strings, &mut inaccessible_path_strings] { path_strings.sort_by(|a, b| a.0.cmp(&b.0)); + path_strings.dedup_by(|a, b| a.0 == b.0); let core_path_strings = path_strings.extract_if(|p| p.0.starts_with("core::")).collect::<Vec<_>>(); - path_strings.extend(core_path_strings); - path_strings.dedup_by(|a, b| a.0 == b.0); + let std_path_strings = + path_strings.extract_if(|p| p.0.starts_with("std::")).collect::<Vec<_>>(); + let foreign_crate_path_strings = + path_strings.extract_if(|p| !p.0.starts_with("crate::")).collect::<Vec<_>>(); + + // We list the `crate` local paths first. + // Then we list the `std`/`core` paths. + if std_path_strings.len() == core_path_strings.len() { + // Do not list `core::` paths if we are already listing the `std::` ones. + path_strings.extend(std_path_strings); + } else { + path_strings.extend(std_path_strings); + path_strings.extend(core_path_strings); + } + // List all paths from foreign crates last. + path_strings.extend(foreign_crate_path_strings); } - accessible_path_strings.sort(); if !accessible_path_strings.is_empty() { - let (determiner, kind, name, through) = + let (determiner, kind, s, name, through) = if let [(name, descr, _, _, via_import)] = &accessible_path_strings[..] { ( "this", *descr, + "", format!(" `{name}`"), if *via_import { " through its public re-export" } else { "" }, ) } else { - ("one of these", "items", String::new(), "") + // Get the unique item kinds and if there's only one, we use the right kind name + // instead of the more generic "items". + let mut kinds = accessible_path_strings + .iter() + .map(|(_, descr, _, _, _)| *descr) + .collect::<FxHashSet<&str>>() + .into_iter(); + let kind = if let Some(kind) = kinds.next() + && let None = kinds.next() + { + kind + } else { + "item" + }; + let s = if kind.ends_with('s') { "es" } else { "s" }; + + ("one of these", kind, s, String::new(), "") }; let instead = if let Instead::Yes = instead { " instead" } else { "" }; let mut msg = if let DiagMode::Pattern = mode { format!( - "if you meant to match on {kind}{instead}{name}, use the full path in the pattern", + "if you meant to match on {kind}{s}{instead}{name}, use the full path in the \ + pattern", ) } else { - format!("consider importing {determiner} {kind}{through}{instead}") + format!("consider importing {determiner} {kind}{s}{through}{instead}") }; for note in accessible_path_strings.iter().flat_map(|cand| cand.3.as_ref()) { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index e245dfb9f5d..c89323b7f16 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -635,7 +635,9 @@ symbols! { coroutine, coroutine_clone, coroutine_resume, + coroutine_return, coroutine_state, + coroutine_yield, coroutines, cosf128, cosf16, diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs index 4d7e2fc2cef..59a8de99b8f 100644 --- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs +++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs @@ -48,6 +48,12 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { rhs }; + // Add a `make_canonical_response` probe step so that we treat this as + // a candidate, even if `try_evaluate_added_goals` bails due to an error. + // It's `Certainty::AMBIGUOUS` because this candidate is not "finished", + // since equating the normalized terms will lead to additional constraints. + self.inspect.make_canonical_response(Certainty::AMBIGUOUS); + // Apply the constraints. self.try_evaluate_added_goals()?; let lhs = self.resolve_vars_if_possible(lhs); diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs index 98f98d9992d..d6074617caf 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs @@ -1,14 +1,14 @@ //! Code which is used by built-in goals that match "structurally", such a auto //! traits, `Copy`/`Clone`. + +use rustc_ast_ir::{Movability, Mutability}; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::LangItem; -use rustc_hir::{def_id::DefId, Movability, Mutability}; -use rustc_infer::infer::InferCtxt; -use rustc_infer::traits::query::NoSolution; -use rustc_macros::{TypeFoldable, TypeVisitable}; -use rustc_middle::bug; -use rustc_middle::traits::solve::Goal; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, Upcast}; +use rustc_next_trait_solver::solve::{Goal, NoSolution}; +use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; +use rustc_type_ir::inherent::*; +use rustc_type_ir::lang_items::TraitSolverLangItem; +use rustc_type_ir::{self as ty, InferCtxtLike, Interner, Upcast}; +use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; use crate::solve::EvalCtxt; @@ -17,12 +17,16 @@ use crate::solve::EvalCtxt; // For types with an "existential" binder, i.e. coroutine witnesses, we also // instantiate the binder with placeholders eagerly. #[instrument(level = "trace", skip(ecx), ret)] -pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>( - ecx: &EvalCtxt<'_, InferCtxt<'tcx>>, - ty: Ty<'tcx>, -) -> Result<Vec<ty::Binder<'tcx, Ty<'tcx>>>, NoSolution> { +pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<Infcx, I>( + ecx: &EvalCtxt<'_, Infcx>, + ty: I::Ty, +) -> Result<Vec<ty::Binder<I, I::Ty>>, NoSolution> +where + Infcx: InferCtxtLike<Interner = I>, + I: Interner, +{ let tcx = ecx.interner(); - match *ty.kind() { + match ty.kind() { ty::Uint(_) | ty::Int(_) | ty::Bool @@ -34,7 +38,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>( | ty::Char => Ok(vec![]), // Treat `str` like it's defined as `struct str([u8]);` - ty::Str => Ok(vec![ty::Binder::dummy(Ty::new_slice(tcx, tcx.types.u8))]), + ty::Str => Ok(vec![ty::Binder::dummy(Ty::new_slice(tcx, Ty::new_u8(tcx)))]), ty::Dynamic(..) | ty::Param(..) @@ -43,7 +47,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>( | ty::Placeholder(..) | ty::Bound(..) | ty::Infer(_) => { - bug!("unexpected type `{ty}`") + panic!("unexpected type `{ty:?}`") } ty::RawPtr(element_ty, _) | ty::Ref(_, element_ty, _) => { @@ -56,7 +60,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>( ty::Tuple(tys) => { // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet - Ok(tys.iter().map(ty::Binder::dummy).collect()) + Ok(tys.into_iter().map(ty::Binder::dummy).collect()) } ty::Closure(_, args) => Ok(vec![ty::Binder::dummy(args.as_closure().tupled_upvars_ty())]), @@ -76,31 +80,38 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>( ty::CoroutineWitness(def_id, args) => Ok(ecx .interner() .bound_coroutine_hidden_types(def_id) - .map(|bty| bty.instantiate(tcx, args)) + .into_iter() + .map(|bty| bty.instantiate(tcx, &args)) .collect()), // For `PhantomData<T>`, we pass `T`. ty::Adt(def, args) if def.is_phantom_data() => Ok(vec![ty::Binder::dummy(args.type_at(0))]), - ty::Adt(def, args) => { - Ok(def.all_fields().map(|f| ty::Binder::dummy(f.ty(tcx, args))).collect()) - } + ty::Adt(def, args) => Ok(def + .all_field_tys(tcx) + .iter_instantiated(tcx, &args) + .map(ty::Binder::dummy) + .collect()), ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => { // We can resolve the `impl Trait` to its concrete type, // which enforces a DAG between the functions requiring // the auto trait bounds in question. - Ok(vec![ty::Binder::dummy(tcx.type_of(def_id).instantiate(tcx, args))]) + Ok(vec![ty::Binder::dummy(tcx.type_of(def_id).instantiate(tcx, &args))]) } } } #[instrument(level = "trace", skip(ecx), ret)] -pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>( - ecx: &EvalCtxt<'_, InferCtxt<'tcx>>, - ty: Ty<'tcx>, -) -> Result<Vec<ty::Binder<'tcx, Ty<'tcx>>>, NoSolution> { - match *ty.kind() { +pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<Infcx, I>( + ecx: &EvalCtxt<'_, Infcx>, + ty: I::Ty, +) -> Result<Vec<ty::Binder<I, I::Ty>>, NoSolution> +where + Infcx: InferCtxtLike<Interner = I>, + I: Interner, +{ + match ty.kind() { // impl Sized for u*, i*, bool, f*, FnDef, FnPtr, *(const/mut) T, char, &mut? T, [T; N], dyn* Trait, ! // impl Sized for Coroutine, CoroutineWitness, Closure, CoroutineClosure ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) @@ -133,7 +144,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>( ty::Bound(..) | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { - bug!("unexpected type `{ty}`") + panic!("unexpected type `{ty:?}`") } // impl Sized for () @@ -151,7 +162,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>( // if the ADT is sized for all possible args. ty::Adt(def, args) => { if let Some(sized_crit) = def.sized_constraint(ecx.interner()) { - Ok(vec![ty::Binder::dummy(sized_crit.instantiate(ecx.interner(), args))]) + Ok(vec![ty::Binder::dummy(sized_crit.instantiate(ecx.interner(), &args))]) } else { Ok(vec![]) } @@ -160,11 +171,15 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>( } #[instrument(level = "trace", skip(ecx), ret)] -pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>( - ecx: &EvalCtxt<'_, InferCtxt<'tcx>>, - ty: Ty<'tcx>, -) -> Result<Vec<ty::Binder<'tcx, Ty<'tcx>>>, NoSolution> { - match *ty.kind() { +pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<Infcx, I>( + ecx: &EvalCtxt<'_, Infcx>, + ty: I::Ty, +) -> Result<Vec<ty::Binder<I, I::Ty>>, NoSolution> +where + Infcx: InferCtxtLike<Interner = I>, + I: Interner, +{ + match ty.kind() { // impl Copy/Clone for FnDef, FnPtr ty::FnDef(..) | ty::FnPtr(_) | ty::Error(_) => Ok(vec![]), @@ -196,11 +211,11 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>( ty::Bound(..) | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { - bug!("unexpected type `{ty}`") + panic!("unexpected type `{ty:?}`") } // impl Copy/Clone for (T1, T2, .., Tn) where T1: Copy/Clone, T2: Copy/Clone, .. Tn: Copy/Clone - ty::Tuple(tys) => Ok(tys.iter().map(ty::Binder::dummy).collect()), + ty::Tuple(tys) => Ok(tys.into_iter().map(ty::Binder::dummy).collect()), // impl Copy/Clone for Closure where Self::TupledUpvars: Copy/Clone ty::Closure(_, args) => Ok(vec![ty::Binder::dummy(args.as_closure().tupled_upvars_ty())]), @@ -212,7 +227,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>( ty::Coroutine(def_id, args) => match ecx.interner().coroutine_movability(def_id) { Movability::Static => Err(NoSolution), Movability::Movable => { - if ecx.interner().features().coroutine_clone { + if ecx.interner().features().coroutine_clone() { let coroutine = args.as_coroutine(); Ok(vec![ ty::Binder::dummy(coroutine.tupled_upvars_ty()), @@ -228,27 +243,26 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>( ty::CoroutineWitness(def_id, args) => Ok(ecx .interner() .bound_coroutine_hidden_types(def_id) - .map(|bty| bty.instantiate(ecx.interner(), args)) + .into_iter() + .map(|bty| bty.instantiate(ecx.interner(), &args)) .collect()), } } // Returns a binder of the tupled inputs types and output type from a builtin callable type. -pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>( - tcx: TyCtxt<'tcx>, - self_ty: Ty<'tcx>, +pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<I: Interner>( + tcx: I, + self_ty: I::Ty, goal_kind: ty::ClosureKind, -) -> Result<Option<ty::Binder<'tcx, (Ty<'tcx>, Ty<'tcx>)>>, NoSolution> { - match *self_ty.kind() { +) -> Result<Option<ty::Binder<I, (I::Ty, I::Ty)>>, NoSolution> { + match self_ty.kind() { // keep this in sync with assemble_fn_pointer_candidates until the old solver is removed. ty::FnDef(def_id, args) => { let sig = tcx.fn_sig(def_id); - if sig.skip_binder().is_fn_trait_compatible() - && tcx.codegen_fn_attrs(def_id).target_features.is_empty() - { + if sig.skip_binder().is_fn_trait_compatible() && !tcx.has_target_features(def_id) { Ok(Some( - sig.instantiate(tcx, args) - .map_bound(|sig| (Ty::new_tup(tcx, sig.inputs()), sig.output())), + sig.instantiate(tcx, &args) + .map_bound(|sig| (Ty::new_tup(tcx, &sig.inputs()), sig.output())), )) } else { Err(NoSolution) @@ -257,7 +271,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>( // keep this in sync with assemble_fn_pointer_candidates until the old solver is removed. ty::FnPtr(sig) => { if sig.is_fn_trait_compatible() { - Ok(Some(sig.map_bound(|sig| (Ty::new_tup(tcx, sig.inputs()), sig.output())))) + Ok(Some(sig.map_bound(|sig| (Ty::new_tup(tcx, &sig.inputs()), sig.output())))) } else { Err(NoSolution) } @@ -311,7 +325,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>( tcx, goal_kind, // No captures by ref, so this doesn't matter. - tcx.lifetimes.re_static, + Region::new_static(tcx), def_id, args, sig, @@ -326,7 +340,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>( coroutine_closure_to_ambiguous_coroutine( tcx, goal_kind, // No captures by ref, so this doesn't matter. - tcx.lifetimes.re_static, + Region::new_static(tcx), def_id, args, sig, @@ -362,22 +376,24 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>( ty::Bound(..) | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { - bug!("unexpected type `{self_ty}`") + panic!("unexpected type `{self_ty:?}`") } } } /// Relevant types for an async callable, including its inputs, output, /// and the return type you get from awaiting the output. -#[derive(Copy, Clone, Debug, TypeVisitable, TypeFoldable)] -pub(in crate::solve) struct AsyncCallableRelevantTypes<'tcx> { - pub tupled_inputs_ty: Ty<'tcx>, +#[derive(derivative::Derivative)] +#[derivative(Clone(bound = ""), Copy(bound = ""), Debug(bound = ""))] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +pub(in crate::solve) struct AsyncCallableRelevantTypes<I: Interner> { + pub tupled_inputs_ty: I::Ty, /// Type returned by calling the closure /// i.e. `f()`. - pub output_coroutine_ty: Ty<'tcx>, + pub output_coroutine_ty: I::Ty, /// Type returned by `await`ing the output /// i.e. `f().await`. - pub coroutine_return_ty: Ty<'tcx>, + pub coroutine_return_ty: I::Ty, } // Returns a binder of the tupled inputs types, output type, and coroutine type @@ -385,16 +401,13 @@ pub(in crate::solve) struct AsyncCallableRelevantTypes<'tcx> { // the coroutine-closure, emit an additional trait predicate for `AsyncFnKindHelper` // which enforces the closure is actually callable with the given trait. When we // know the kind already, we can short-circuit this check. -pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tcx>( - tcx: TyCtxt<'tcx>, - self_ty: Ty<'tcx>, +pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<I: Interner>( + tcx: I, + self_ty: I::Ty, goal_kind: ty::ClosureKind, - env_region: ty::Region<'tcx>, -) -> Result< - (ty::Binder<'tcx, AsyncCallableRelevantTypes<'tcx>>, Vec<ty::Predicate<'tcx>>), - NoSolution, -> { - match *self_ty.kind() { + env_region: I::Region, +) -> Result<(ty::Binder<I, AsyncCallableRelevantTypes<I>>, Vec<I::Predicate>), NoSolution> { + match self_ty.kind() { ty::CoroutineClosure(def_id, args) => { let args = args.as_coroutine_closure(); let kind_ty = args.kind_ty(); @@ -421,7 +434,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc nested.push( ty::TraitRef::new( tcx, - tcx.require_lang_item(LangItem::AsyncFnKindHelper, None), + tcx.require_lang_item(TraitSolverLangItem::AsyncFnKindHelper), [kind_ty, Ty::from_closure_kind(tcx, goal_kind)], ) .upcast(tcx), @@ -445,7 +458,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc ty::FnDef(..) | ty::FnPtr(..) => { let bound_sig = self_ty.fn_sig(tcx); let sig = bound_sig.skip_binder(); - let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None); + let future_trait_def_id = tcx.require_lang_item(TraitSolverLangItem::Future); // `FnDef` and `FnPtr` only implement `AsyncFn*` when their // return type implements `Future`. let nested = vec![ @@ -453,11 +466,11 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc .rebind(ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()])) .upcast(tcx), ]; - let future_output_def_id = tcx.require_lang_item(LangItem::FutureOutput, None); + let future_output_def_id = tcx.require_lang_item(TraitSolverLangItem::FutureOutput); let future_output_ty = Ty::new_projection(tcx, future_output_def_id, [sig.output()]); Ok(( bound_sig.rebind(AsyncCallableRelevantTypes { - tupled_inputs_ty: Ty::new_tup(tcx, sig.inputs()), + tupled_inputs_ty: Ty::new_tup(tcx, &sig.inputs()), output_coroutine_ty: sig.output(), coroutine_return_ty: future_output_ty, }), @@ -468,7 +481,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc let args = args.as_closure(); let bound_sig = args.sig(); let sig = bound_sig.skip_binder(); - let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None); + let future_trait_def_id = tcx.require_lang_item(TraitSolverLangItem::Future); // `Closure`s only implement `AsyncFn*` when their return type // implements `Future`. let mut nested = vec![ @@ -486,7 +499,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc } } else { let async_fn_kind_trait_def_id = - tcx.require_lang_item(LangItem::AsyncFnKindHelper, None); + tcx.require_lang_item(TraitSolverLangItem::AsyncFnKindHelper); // When we don't know the closure kind (and therefore also the closure's upvars, // which are computed at the same time), we must delay the computation of the // generator's upvars. We do this using the `AsyncFnKindHelper`, which as a trait @@ -504,7 +517,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc ); } - let future_output_def_id = tcx.require_lang_item(LangItem::FutureOutput, None); + let future_output_def_id = tcx.require_lang_item(TraitSolverLangItem::FutureOutput); let future_output_ty = Ty::new_projection(tcx, future_output_def_id, [sig.output()]); Ok(( bound_sig.rebind(AsyncCallableRelevantTypes { @@ -542,21 +555,21 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc ty::Bound(..) | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { - bug!("unexpected type `{self_ty}`") + panic!("unexpected type `{self_ty:?}`") } } } /// Given a coroutine-closure, project to its returned coroutine when we are *certain* /// that the closure's kind is compatible with the goal. -fn coroutine_closure_to_certain_coroutine<'tcx>( - tcx: TyCtxt<'tcx>, +fn coroutine_closure_to_certain_coroutine<I: Interner>( + tcx: I, goal_kind: ty::ClosureKind, - goal_region: ty::Region<'tcx>, - def_id: DefId, - args: ty::CoroutineClosureArgs<TyCtxt<'tcx>>, - sig: ty::CoroutineClosureSignature<TyCtxt<'tcx>>, -) -> Ty<'tcx> { + goal_region: I::Region, + def_id: I::DefId, + args: ty::CoroutineClosureArgs<I>, + sig: ty::CoroutineClosureSignature<I>, +) -> I::Ty { sig.to_coroutine_given_kind_and_upvars( tcx, args.parent_args(), @@ -573,20 +586,20 @@ fn coroutine_closure_to_certain_coroutine<'tcx>( /// yet what the closure's upvars are. /// /// Note that we do not also push a `AsyncFnKindHelper` goal here. -fn coroutine_closure_to_ambiguous_coroutine<'tcx>( - tcx: TyCtxt<'tcx>, +fn coroutine_closure_to_ambiguous_coroutine<I: Interner>( + tcx: I, goal_kind: ty::ClosureKind, - goal_region: ty::Region<'tcx>, - def_id: DefId, - args: ty::CoroutineClosureArgs<TyCtxt<'tcx>>, - sig: ty::CoroutineClosureSignature<TyCtxt<'tcx>>, -) -> Ty<'tcx> { - let upvars_projection_def_id = tcx.require_lang_item(LangItem::AsyncFnKindUpvars, None); + goal_region: I::Region, + def_id: I::DefId, + args: ty::CoroutineClosureArgs<I>, + sig: ty::CoroutineClosureSignature<I>, +) -> I::Ty { + let upvars_projection_def_id = tcx.require_lang_item(TraitSolverLangItem::AsyncFnKindUpvars); let tupled_upvars_ty = Ty::new_projection( tcx, upvars_projection_def_id, [ - ty::GenericArg::from(args.kind_ty()), + I::GenericArg::from(args.kind_ty()), Ty::from_closure_kind(tcx, goal_kind).into(), goal_region.into(), sig.tupled_inputs_ty.into(), @@ -643,41 +656,44 @@ fn coroutine_closure_to_ambiguous_coroutine<'tcx>( // This is unsound in general and once that is fixed, we don't need to // normalize eagerly here. See https://github.com/lcnr/solver-woes/issues/9 // for more details. -pub(in crate::solve) fn predicates_for_object_candidate<'tcx>( - ecx: &EvalCtxt<'_, InferCtxt<'tcx>>, - param_env: ty::ParamEnv<'tcx>, - trait_ref: ty::TraitRef<'tcx>, - object_bound: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, -) -> Vec<Goal<'tcx, ty::Predicate<'tcx>>> { +pub(in crate::solve) fn predicates_for_object_candidate<Infcx, I>( + ecx: &EvalCtxt<'_, Infcx>, + param_env: I::ParamEnv, + trait_ref: ty::TraitRef<I>, + object_bounds: I::BoundExistentialPredicates, +) -> Vec<Goal<I, I::Predicate>> +where + Infcx: InferCtxtLike<Interner = I>, + I: Interner, +{ let tcx = ecx.interner(); let mut requirements = vec![]; - requirements.extend( - tcx.super_predicates_of(trait_ref.def_id).instantiate(tcx, trait_ref.args).predicates, - ); - for item in tcx.associated_items(trait_ref.def_id).in_definition_order() { - // FIXME(associated_const_equality): Also add associated consts to - // the requirements here. - if item.kind == ty::AssocKind::Type { - // associated types that require `Self: Sized` do not show up in the built-in - // implementation of `Trait for dyn Trait`, and can be dropped here. - if tcx.generics_require_sized_self(item.def_id) { - continue; - } - - requirements - .extend(tcx.item_bounds(item.def_id).iter_instantiated(tcx, trait_ref.args)); + requirements + .extend(tcx.super_predicates_of(trait_ref.def_id).iter_instantiated(tcx, &trait_ref.args)); + + // FIXME(associated_const_equality): Also add associated consts to + // the requirements here. + for associated_type_def_id in tcx.associated_type_def_ids(trait_ref.def_id) { + // associated types that require `Self: Sized` do not show up in the built-in + // implementation of `Trait for dyn Trait`, and can be dropped here. + if tcx.generics_require_sized_self(associated_type_def_id) { + continue; } + + requirements.extend( + tcx.item_bounds(associated_type_def_id).iter_instantiated(tcx, &trait_ref.args), + ); } let mut replace_projection_with = FxHashMap::default(); - for bound in object_bound { + for bound in object_bounds { if let ty::ExistentialPredicate::Projection(proj) = bound.skip_binder() { let proj = proj.with_self_ty(tcx, trait_ref.self_ty()); let old_ty = replace_projection_with.insert(proj.def_id(), bound.rebind(proj)); assert_eq!( old_ty, None, - "{} has two generic parameters: {} and {}", + "{:?} has two generic parameters: {:?} and {:?}", proj.projection_term, proj.term, old_ty.unwrap() @@ -696,20 +712,22 @@ pub(in crate::solve) fn predicates_for_object_candidate<'tcx>( .collect() } -struct ReplaceProjectionWith<'a, 'tcx> { - ecx: &'a EvalCtxt<'a, InferCtxt<'tcx>>, - param_env: ty::ParamEnv<'tcx>, - mapping: FxHashMap<DefId, ty::PolyProjectionPredicate<'tcx>>, - nested: Vec<Goal<'tcx, ty::Predicate<'tcx>>>, +struct ReplaceProjectionWith<'a, Infcx: InferCtxtLike<Interner = I>, I: Interner> { + ecx: &'a EvalCtxt<'a, Infcx>, + param_env: I::ParamEnv, + mapping: FxHashMap<I::DefId, ty::Binder<I, ty::ProjectionPredicate<I>>>, + nested: Vec<Goal<I, I::Predicate>>, } -impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceProjectionWith<'_, 'tcx> { - fn interner(&self) -> TyCtxt<'tcx> { +impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeFolder<I> + for ReplaceProjectionWith<'_, Infcx, I> +{ + fn interner(&self) -> I { self.ecx.interner() } - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if let ty::Alias(ty::Projection, alias_ty) = *ty.kind() + fn fold_ty(&mut self, ty: I::Ty) -> I::Ty { + if let ty::Alias(ty::Projection, alias_ty) = ty.kind() && let Some(replacement) = self.mapping.get(&alias_ty.def_id) { // We may have a case where our object type's projection bound is higher-ranked, diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs index 43013a01069..860c580374d 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs @@ -1,9 +1,6 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::def_id::DefId; -use rustc_infer::infer::at::ToTrace; -use rustc_infer::infer::{ - BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk, TyCtxtInferExt, -}; +use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::solve::{MaybeCause, NestedNormalizationGoals}; use rustc_infer::traits::ObligationCause; @@ -15,11 +12,13 @@ use rustc_middle::traits::solve::{ use rustc_middle::ty::AliasRelationDirection; use rustc_middle::ty::TypeFolder; use rustc_middle::ty::{ - self, InferCtxtLike, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, - TypeVisitable, TypeVisitableExt, TypeVisitor, + self, InferCtxtLike, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, }; use rustc_span::DUMMY_SP; use rustc_type_ir::fold::TypeSuperFoldable; +use rustc_type_ir::inherent::*; +use rustc_type_ir::relate::Relate; +use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}; use rustc_type_ir::{self as ir, CanonicalVarValues, Interner}; use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; use std::ops::ControlFlow; @@ -456,28 +455,6 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> { } } - #[instrument(level = "trace", skip(self))] - pub(super) fn add_normalizes_to_goal(&mut self, mut goal: Goal<'tcx, ty::NormalizesTo<'tcx>>) { - goal.predicate = goal - .predicate - .fold_with(&mut ReplaceAliasWithInfer { ecx: self, param_env: goal.param_env }); - self.inspect.add_normalizes_to_goal(self.infcx, self.max_input_universe, goal); - self.nested_goals.normalizes_to_goals.push(goal); - } - - #[instrument(level = "debug", skip(self))] - pub(super) fn add_goal( - &mut self, - source: GoalSource, - mut goal: Goal<'tcx, ty::Predicate<'tcx>>, - ) { - goal.predicate = goal - .predicate - .fold_with(&mut ReplaceAliasWithInfer { ecx: self, param_env: goal.param_env }); - self.inspect.add_goal(self.infcx, self.max_input_universe, source, goal); - self.nested_goals.goals.push((source, goal)); - } - // Recursively evaluates all the goals added to this `EvalCtxt` to completion, returning // the certainty of all the goals. #[instrument(level = "trace", skip(self))] @@ -600,27 +577,61 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> { pub(super) fn interner(&self) -> I { self.infcx.interner() } -} -impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { - pub(super) fn next_ty_infer(&mut self) -> Ty<'tcx> { - let ty = self.infcx.next_ty_var(DUMMY_SP); + #[instrument(level = "trace", skip(self))] + pub(super) fn add_normalizes_to_goal( + &mut self, + mut goal: ir::solve::Goal<I, ir::NormalizesTo<I>>, + ) { + goal.predicate = goal + .predicate + .fold_with(&mut ReplaceAliasWithInfer { ecx: self, param_env: goal.param_env }); + self.inspect.add_normalizes_to_goal(self.infcx, self.max_input_universe, goal); + self.nested_goals.normalizes_to_goals.push(goal); + } + + #[instrument(level = "debug", skip(self))] + pub(super) fn add_goal( + &mut self, + source: GoalSource, + mut goal: ir::solve::Goal<I, I::Predicate>, + ) { + goal.predicate = goal + .predicate + .fold_with(&mut ReplaceAliasWithInfer { ecx: self, param_env: goal.param_env }); + self.inspect.add_goal(self.infcx, self.max_input_universe, source, goal); + self.nested_goals.goals.push((source, goal)); + } + + #[instrument(level = "trace", skip(self, goals))] + pub(super) fn add_goals( + &mut self, + source: GoalSource, + goals: impl IntoIterator<Item = ir::solve::Goal<I, I::Predicate>>, + ) { + for goal in goals { + self.add_goal(source, goal); + } + } + + pub(super) fn next_ty_infer(&mut self) -> I::Ty { + let ty = self.infcx.next_ty_infer(); self.inspect.add_var_value(ty); ty } - pub(super) fn next_const_infer(&mut self) -> ty::Const<'tcx> { - let ct = self.infcx.next_const_var(DUMMY_SP); + pub(super) fn next_const_infer(&mut self) -> I::Const { + let ct = self.infcx.next_const_infer(); self.inspect.add_var_value(ct); ct } /// Returns a ty infer or a const infer depending on whether `kind` is a `Ty` or `Const`. /// If `kind` is an integer inference variable this will still return a ty infer var. - pub(super) fn next_term_infer_of_kind(&mut self, kind: ty::Term<'tcx>) -> ty::Term<'tcx> { - match kind.unpack() { - ty::TermKind::Ty(_) => self.next_ty_infer().into(), - ty::TermKind::Const(_) => self.next_const_infer().into(), + pub(super) fn next_term_infer_of_kind(&mut self, kind: I::Term) -> I::Term { + match kind.kind() { + ir::TermKind::Ty(_) => self.next_ty_infer().into(), + ir::TermKind::Const(_) => self.next_const_infer().into(), } } @@ -631,18 +642,18 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { #[instrument(level = "trace", skip(self), ret)] pub(super) fn term_is_fully_unconstrained( &self, - goal: Goal<'tcx, ty::NormalizesTo<'tcx>>, + goal: ir::solve::Goal<I, ir::NormalizesTo<I>>, ) -> bool { - let universe_of_term = match goal.predicate.term.unpack() { - ty::TermKind::Ty(ty) => { - if let &ty::Infer(ty::TyVar(vid)) = ty.kind() { + let universe_of_term = match goal.predicate.term.kind() { + ir::TermKind::Ty(ty) => { + if let ir::Infer(ir::TyVar(vid)) = ty.kind() { self.infcx.universe_of_ty(vid).unwrap() } else { return false; } } - ty::TermKind::Const(ct) => { - if let ty::ConstKind::Infer(ty::InferConst::Var(vid)) = ct.kind() { + ir::TermKind::Const(ct) => { + if let ir::ConstKind::Infer(ir::InferConst::Var(vid)) = ct.kind() { self.infcx.universe_of_ct(vid).unwrap() } else { return false; @@ -650,14 +661,14 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { } }; - struct ContainsTermOrNotNameable<'a, 'tcx> { - term: ty::Term<'tcx>, - universe_of_term: ty::UniverseIndex, - infcx: &'a InferCtxt<'tcx>, + struct ContainsTermOrNotNameable<'a, Infcx: InferCtxtLike<Interner = I>, I: Interner> { + term: I::Term, + universe_of_term: ir::UniverseIndex, + infcx: &'a Infcx, } - impl<'a, 'tcx> ContainsTermOrNotNameable<'a, 'tcx> { - fn check_nameable(&self, universe: ty::UniverseIndex) -> ControlFlow<()> { + impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> ContainsTermOrNotNameable<'_, Infcx, I> { + fn check_nameable(&self, universe: ir::UniverseIndex) -> ControlFlow<()> { if self.universe_of_term.can_name(universe) { ControlFlow::Continue(()) } else { @@ -666,21 +677,23 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { } } - impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ContainsTermOrNotNameable<'_, 'tcx> { + impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeVisitor<I> + for ContainsTermOrNotNameable<'_, Infcx, I> + { type Result = ControlFlow<()>; - fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { - match *t.kind() { - ty::Infer(ty::TyVar(vid)) => { - if let ty::TermKind::Ty(term) = self.term.unpack() - && let Some(term_vid) = term.ty_vid() - && self.infcx.root_var(vid) == self.infcx.root_var(term_vid) + fn visit_ty(&mut self, t: I::Ty) -> Self::Result { + match t.kind() { + ir::Infer(ir::TyVar(vid)) => { + if let ir::TermKind::Ty(term) = self.term.kind() + && let ir::Infer(ir::TyVar(term_vid)) = term.kind() + && self.infcx.root_ty_var(vid) == self.infcx.root_ty_var(term_vid) { ControlFlow::Break(()) } else { self.check_nameable(self.infcx.universe_of_ty(vid).unwrap()) } } - ty::Placeholder(p) => self.check_nameable(p.universe), + ir::Placeholder(p) => self.check_nameable(p.universe()), _ => { if t.has_non_region_infer() || t.has_placeholders() { t.super_visit_with(self) @@ -691,11 +704,11 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { } } - fn visit_const(&mut self, c: ty::Const<'tcx>) -> Self::Result { + fn visit_const(&mut self, c: I::Const) -> Self::Result { match c.kind() { - ty::ConstKind::Infer(ty::InferConst::Var(vid)) => { - if let ty::TermKind::Const(term) = self.term.unpack() - && let ty::ConstKind::Infer(ty::InferConst::Var(term_vid)) = term.kind() + ir::ConstKind::Infer(ir::InferConst::Var(vid)) => { + if let ir::TermKind::Const(term) = self.term.kind() + && let ir::ConstKind::Infer(ir::InferConst::Var(term_vid)) = term.kind() && self.infcx.root_const_var(vid) == self.infcx.root_const_var(term_vid) { ControlFlow::Break(()) @@ -703,7 +716,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { self.check_nameable(self.infcx.universe_of_ct(vid).unwrap()) } } - ty::ConstKind::Placeholder(p) => self.check_nameable(p.universe), + ir::ConstKind::Placeholder(p) => self.check_nameable(p.universe()), _ => { if c.has_non_region_infer() || c.has_placeholders() { c.super_visit_with(self) @@ -725,23 +738,13 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { } #[instrument(level = "trace", skip(self, param_env), ret)] - pub(super) fn eq<T: ToTrace<'tcx>>( + pub(super) fn eq<T: Relate<I>>( &mut self, - param_env: ty::ParamEnv<'tcx>, + param_env: I::ParamEnv, lhs: T, rhs: T, ) -> Result<(), NoSolution> { - self.infcx - .at(&ObligationCause::dummy(), param_env) - // New solver ignores DefineOpaqueTypes, so choose Yes for consistency - .eq(DefineOpaqueTypes::Yes, lhs, rhs) - .map(|InferOk { value: (), obligations }| { - self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into())); - }) - .map_err(|e| { - trace!(?e, "failed to equate"); - NoSolution - }) + self.relate(param_env, lhs, ir::Variance::Invariant, rhs) } /// This should be used when relating a rigid alias with another type. @@ -752,10 +755,10 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { #[instrument(level = "trace", skip(self, param_env), ret)] pub(super) fn relate_rigid_alias_non_alias( &mut self, - param_env: ty::ParamEnv<'tcx>, - alias: ty::AliasTerm<'tcx>, - variance: ty::Variance, - term: ty::Term<'tcx>, + param_env: I::ParamEnv, + alias: ir::AliasTerm<I>, + variance: ir::Variance, + term: I::Term, ) -> Result<(), NoSolution> { // NOTE: this check is purely an optimization, the structural eq would // always fail if the term is not an inference variable. @@ -770,12 +773,10 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { // Alternatively we could modify `Equate` for this case by adding another // variant to `StructurallyRelateAliases`. let identity_args = self.fresh_args_for_item(alias.def_id); - let rigid_ctor = ty::AliasTerm::new(tcx, alias.def_id, identity_args); + let rigid_ctor = ir::AliasTerm::new(tcx, alias.def_id, identity_args); let ctor_term = rigid_ctor.to_term(tcx); - let InferOk { value: (), obligations } = self - .infcx - .at(&ObligationCause::dummy(), param_env) - .eq_structurally_relating_aliases(term, ctor_term)?; + let obligations = + self.infcx.eq_structurally_relating_aliases(param_env, term, ctor_term)?; debug_assert!(obligations.is_empty()); self.relate(param_env, alias, variance, rigid_ctor) } else { @@ -787,58 +788,38 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { /// unconstrained "return value" or when we're sure that all aliases in /// the types are rigid. #[instrument(level = "trace", skip(self, param_env), ret)] - pub(super) fn eq_structurally_relating_aliases<T: ToTrace<'tcx>>( + pub(super) fn eq_structurally_relating_aliases<T: Relate<I>>( &mut self, - param_env: ty::ParamEnv<'tcx>, + param_env: I::ParamEnv, lhs: T, rhs: T, ) -> Result<(), NoSolution> { - let cause = ObligationCause::dummy(); - let InferOk { value: (), obligations } = - self.infcx.at(&cause, param_env).eq_structurally_relating_aliases(lhs, rhs)?; - assert!(obligations.is_empty()); + let result = self.infcx.eq_structurally_relating_aliases(param_env, lhs, rhs)?; + assert_eq!(result, vec![]); Ok(()) } #[instrument(level = "trace", skip(self, param_env), ret)] - pub(super) fn sub<T: ToTrace<'tcx>>( + pub(super) fn sub<T: Relate<I>>( &mut self, - param_env: ty::ParamEnv<'tcx>, + param_env: I::ParamEnv, sub: T, sup: T, ) -> Result<(), NoSolution> { - self.infcx - .at(&ObligationCause::dummy(), param_env) - // New solver ignores DefineOpaqueTypes, so choose Yes for consistency - .sub(DefineOpaqueTypes::Yes, sub, sup) - .map(|InferOk { value: (), obligations }| { - self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into())); - }) - .map_err(|e| { - trace!(?e, "failed to subtype"); - NoSolution - }) + self.relate(param_env, sub, ir::Variance::Covariant, sup) } #[instrument(level = "trace", skip(self, param_env), ret)] - pub(super) fn relate<T: ToTrace<'tcx>>( + pub(super) fn relate<T: Relate<I>>( &mut self, - param_env: ty::ParamEnv<'tcx>, + param_env: I::ParamEnv, lhs: T, - variance: ty::Variance, + variance: ir::Variance, rhs: T, ) -> Result<(), NoSolution> { - self.infcx - .at(&ObligationCause::dummy(), param_env) - // New solver ignores DefineOpaqueTypes, so choose Yes for consistency - .relate(DefineOpaqueTypes::Yes, lhs, variance, rhs) - .map(|InferOk { value: (), obligations }| { - self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into())); - }) - .map_err(|e| { - trace!(?e, "failed to relate"); - NoSolution - }) + let goals = self.infcx.relate(param_env, lhs, variance, rhs)?; + self.add_goals(GoalSource::Misc, goals); + Ok(()) } /// Equates two values returning the nested goals without adding them @@ -847,58 +828,47 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { /// If possible, try using `eq` instead which automatically handles nested /// goals correctly. #[instrument(level = "trace", skip(self, param_env), ret)] - pub(super) fn eq_and_get_goals<T: ToTrace<'tcx>>( + pub(super) fn eq_and_get_goals<T: Relate<I>>( &self, - param_env: ty::ParamEnv<'tcx>, + param_env: I::ParamEnv, lhs: T, rhs: T, - ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution> { - self.infcx - .at(&ObligationCause::dummy(), param_env) - // New solver ignores DefineOpaqueTypes, so choose Yes for consistency - .eq(DefineOpaqueTypes::Yes, lhs, rhs) - .map(|InferOk { value: (), obligations }| { - obligations.into_iter().map(|o| o.into()).collect() - }) - .map_err(|e| { - trace!(?e, "failed to equate"); - NoSolution - }) + ) -> Result<Vec<ir::solve::Goal<I, I::Predicate>>, NoSolution> { + self.infcx.relate(param_env, lhs, ir::Variance::Invariant, rhs) } - pub(super) fn instantiate_binder_with_infer<T: TypeFoldable<TyCtxt<'tcx>> + Copy>( + pub(super) fn instantiate_binder_with_infer<T: TypeFoldable<I> + Copy>( &self, - value: ty::Binder<'tcx, T>, + value: ir::Binder<I, T>, ) -> T { - self.infcx.instantiate_binder_with_fresh_vars( - DUMMY_SP, - BoundRegionConversionTime::HigherRankedType, - value, - ) + self.infcx.instantiate_binder_with_infer(value) } - pub(super) fn enter_forall<T: TypeFoldable<TyCtxt<'tcx>> + Copy, U>( + pub(super) fn enter_forall<T: TypeFoldable<I> + Copy, U>( &self, - value: ty::Binder<'tcx, T>, + value: ir::Binder<I, T>, f: impl FnOnce(T) -> U, ) -> U { self.infcx.enter_forall(value, f) } + pub(super) fn resolve_vars_if_possible<T>(&self, value: T) -> T where - T: TypeFoldable<TyCtxt<'tcx>>, + T: TypeFoldable<I>, { self.infcx.resolve_vars_if_possible(value) } - pub(super) fn fresh_args_for_item(&mut self, def_id: DefId) -> ty::GenericArgsRef<'tcx> { - let args = self.infcx.fresh_args_for_item(DUMMY_SP, def_id); + pub(super) fn fresh_args_for_item(&mut self, def_id: I::DefId) -> I::GenericArgs { + let args = self.infcx.fresh_args_for_item(def_id); for arg in args { self.inspect.add_var_value(arg); } args } +} +impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { pub(super) fn register_ty_outlives(&self, ty: Ty<'tcx>, lt: ty::Region<'tcx>) { self.infcx.register_region_obligation_with_cause(ty, lt, &ObligationCause::dummy()); } @@ -1096,28 +1066,36 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { /// /// This is a performance optimization to more eagerly detect cycles during trait /// solving. See tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.rs. -struct ReplaceAliasWithInfer<'me, 'a, 'tcx> { - ecx: &'me mut EvalCtxt<'a, InferCtxt<'tcx>>, - param_env: ty::ParamEnv<'tcx>, +struct ReplaceAliasWithInfer<'me, 'a, Infcx, I> +where + Infcx: InferCtxtLike<Interner = I>, + I: Interner, +{ + ecx: &'me mut EvalCtxt<'a, Infcx>, + param_env: I::ParamEnv, } -impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceAliasWithInfer<'_, '_, 'tcx> { - fn interner(&self) -> TyCtxt<'tcx> { +impl<Infcx, I> TypeFolder<I> for ReplaceAliasWithInfer<'_, '_, Infcx, I> +where + Infcx: InferCtxtLike<Interner = I>, + I: Interner, +{ + fn interner(&self) -> I { self.ecx.interner() } - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - match *ty.kind() { - ty::Alias(..) if !ty.has_escaping_bound_vars() => { + fn fold_ty(&mut self, ty: I::Ty) -> I::Ty { + match ty.kind() { + ir::Alias(..) if !ty.has_escaping_bound_vars() => { let infer_ty = self.ecx.next_ty_infer(); - let normalizes_to = ty::PredicateKind::AliasRelate( + let normalizes_to = ir::PredicateKind::AliasRelate( ty.into(), infer_ty.into(), AliasRelationDirection::Equate, ); self.ecx.add_goal( GoalSource::Misc, - Goal::new(self.interner(), self.param_env, normalizes_to), + ir::solve::Goal::new(self.interner(), self.param_env, normalizes_to), ); infer_ty } @@ -1125,18 +1103,18 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceAliasWithInfer<'_, '_, 'tcx> { } } - fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + fn fold_const(&mut self, ct: I::Const) -> I::Const { match ct.kind() { - ty::ConstKind::Unevaluated(..) if !ct.has_escaping_bound_vars() => { + ir::ConstKind::Unevaluated(..) if !ct.has_escaping_bound_vars() => { let infer_ct = self.ecx.next_const_infer(); - let normalizes_to = ty::PredicateKind::AliasRelate( + let normalizes_to = ir::PredicateKind::AliasRelate( ct.into(), infer_ct.into(), AliasRelationDirection::Equate, ); self.ecx.add_goal( GoalSource::Misc, - Goal::new(self.interner(), self.param_env, normalizes_to), + ir::solve::Goal::new(self.interner(), self.param_env, normalizes_to), ); infer_ct } @@ -1144,7 +1122,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceAliasWithInfer<'_, '_, 'tcx> { } } - fn fold_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { + fn fold_predicate(&mut self, predicate: I::Predicate) -> I::Predicate { if predicate.allow_normalization() { predicate.super_fold_with(self) } else { predicate } } } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index dc13941e5d7..3c3d5dfe79c 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -460,9 +460,10 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { polarity: ty::PredicatePolarity::Positive, })) } - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => { - ChildMode::WellFormedObligation - } + ty::PredicateKind::Clause( + ty::ClauseKind::WellFormed(_) | ty::ClauseKind::Projection(..), + ) + | ty::PredicateKind::AliasRelate(..) => ChildMode::PassThrough, _ => { return ControlFlow::Break(self.obligation.clone()); } @@ -496,7 +497,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { (_, GoalSource::InstantiateHigherRanked) => { obligation = self.obligation.clone(); } - (ChildMode::WellFormedObligation, _) => { + (ChildMode::PassThrough, _) => { obligation = make_obligation(self.obligation.cause.clone()); } } @@ -527,7 +528,7 @@ enum ChildMode<'tcx> { // Skip trying to derive an `ObligationCause` from this obligation, and // report *all* sub-obligations as if they came directly from the parent // obligation. - WellFormedObligation, + PassThrough, } fn derive_cause<'tcx>( diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index 19c95dad48c..b9c98b6a2e9 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -15,7 +15,7 @@ use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk}; use rustc_macros::extension; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::{inspect, QueryResult}; -use rustc_middle::traits::solve::{Certainty, Goal}; +use rustc_middle::traits::solve::{Certainty, Goal, MaybeCause}; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{TyCtxt, TypeFoldable}; use rustc_middle::{bug, ty}; @@ -291,7 +291,10 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { steps.push(step) } inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty: c } => { - assert_eq!(shallow_certainty.replace(c), None); + assert!(matches!( + shallow_certainty.replace(c), + None | Some(Certainty::Maybe(MaybeCause::Ambiguity)) + )); } inspect::ProbeStep::NestedProbe(ref probe) => { match probe.kind { diff --git a/compiler/rustc_trait_selection/src/solve/inspect/build.rs b/compiler/rustc_trait_selection/src/solve/inspect/build.rs index 84c04900ae4..35750598bc7 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/build.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/build.rs @@ -34,10 +34,11 @@ use rustc_type_ir::{self as ty, InferCtxtLike, Interner}; /// trees. At the end of trait solving `ProofTreeBuilder::finalize` /// is called to recursively convert the whole structure to a /// finished proof tree. -pub(in crate::solve) struct ProofTreeBuilder< +pub(in crate::solve) struct ProofTreeBuilder<Infcx, I = <Infcx as InferCtxtLike>::Interner> +where Infcx: InferCtxtLike<Interner = I>, - I: Interner = <Infcx as InferCtxtLike>::Interner, -> { + I: Interner, +{ _infcx: PhantomData<Infcx>, state: Option<Box<DebugSolver<I>>>, } diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index c47b0194964..fdcf4ff11e4 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -235,17 +235,6 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> { } impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { - #[instrument(level = "trace", skip(self, goals))] - fn add_goals( - &mut self, - source: GoalSource, - goals: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>, - ) { - for goal in goals { - self.add_goal(source, goal); - } - } - /// Try to merge multiple possible ways to prove a goal, if that is not possible returns `None`. /// /// In this case we tend to flounder and return ambiguity by calling `[EvalCtxt::flounder]`. diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs index 787f08a084e..50253d81528 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs @@ -17,7 +17,7 @@ use rustc_middle::ty::NormalizesTo; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{TypeVisitableExt, Upcast}; use rustc_middle::{bug, span_bug}; -use rustc_span::{sym, ErrorGuaranteed, DUMMY_SP}; +use rustc_span::{ErrorGuaranteed, DUMMY_SP}; mod anon_const; mod inherent; @@ -719,13 +719,16 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { let coroutine = args.as_coroutine(); - let name = tcx.associated_item(goal.predicate.def_id()).name; - let term = if name == sym::Return { + let lang_items = tcx.lang_items(); + let term = if Some(goal.predicate.def_id()) == lang_items.coroutine_return() { coroutine.return_ty().into() - } else if name == sym::Yield { + } else if Some(goal.predicate.def_id()) == lang_items.coroutine_yield() { coroutine.yield_ty().into() } else { - bug!("unexpected associated item `<{self_ty} as Coroutine>::{name}`") + bug!( + "unexpected associated item `<{self_ty} as Coroutine>::{}`", + tcx.item_name(goal.predicate.def_id()) + ) }; Self::probe_and_consider_implied_clause( diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 087f7fbea00..4604c132835 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -3597,7 +3597,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { &self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>, - trait_ref: &ty::PolyTraitRef<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, ) { let rhs_span = match obligation.cause.code() { ObligationCauseCode::BinOp { rhs_span: Some(span), rhs_is_lit, .. } if *rhs_is_lit => { @@ -4592,7 +4592,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { &self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>, - trait_ref: ty::PolyTraitRef<'tcx>, + trait_pred: ty::PolyTraitPredicate<'tcx>, ) { if ObligationCauseCode::QuestionMark != *obligation.cause.code().peel_derives() { return; @@ -4602,10 +4602,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if let hir::Node::Item(item) = node && let hir::ItemKind::Fn(sig, _, body_id) = item.kind && let hir::FnRetTy::DefaultReturn(ret_span) = sig.decl.output - && self.tcx.is_diagnostic_item(sym::FromResidual, trait_ref.def_id()) - && let ty::Tuple(l) = trait_ref.skip_binder().args.type_at(0).kind() - && l.len() == 0 - && let ty::Adt(def, _) = trait_ref.skip_binder().args.type_at(1).kind() + && self.tcx.is_diagnostic_item(sym::FromResidual, trait_pred.def_id()) + && trait_pred.skip_binder().trait_ref.args.type_at(0).is_unit() + && let ty::Adt(def, _) = trait_pred.skip_binder().trait_ref.args.type_at(1).kind() && self.tcx.is_diagnostic_item(sym::Result, def.did()) { let body = self.tcx.hir().body(body_id); @@ -4863,14 +4862,13 @@ impl<'a, 'hir> hir::intravisit::Visitor<'hir> for ReplaceImplTraitVisitor<'a> { pub(super) fn get_explanation_based_on_obligation<'tcx>( tcx: TyCtxt<'tcx>, obligation: &PredicateObligation<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, - trait_predicate: &ty::PolyTraitPredicate<'tcx>, + trait_predicate: ty::PolyTraitPredicate<'tcx>, pre_message: String, ) -> String { if let ObligationCauseCode::MainFunctionType = obligation.cause.code() { "consider using `()`, or a `Result`".to_owned() } else { - let ty_desc = match trait_ref.skip_binder().self_ty().kind() { + let ty_desc = match trait_predicate.self_ty().skip_binder().kind() { ty::FnDef(_, _) => Some("fn item"), ty::Closure(_, _) => Some("closure"), _ => None, @@ -4895,7 +4893,7 @@ pub(super) fn get_explanation_based_on_obligation<'tcx>( format!( "{pre_message}the trait `{}` is not implemented for{desc} `{}`{post}", trait_predicate.print_modifiers_and_trait_path(), - tcx.short_ty_string(trait_ref.skip_binder().self_ty(), &mut None), + tcx.short_ty_string(trait_predicate.self_ty().skip_binder(), &mut None), ) } else { // "the trait bound `T: !Send` is not satisfied" reads better than "`!Send` is diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index 6b6438a7887..aef9d482bec 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -412,8 +412,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let bound_predicate = obligation.predicate.kind(); match bound_predicate.skip_binder() { ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_predicate)) => { - let trait_predicate = bound_predicate.rebind(trait_predicate); - let trait_predicate = self.resolve_vars_if_possible(trait_predicate); + let leaf_trait_predicate = + self.resolve_vars_if_possible(bound_predicate.rebind(trait_predicate)); // Let's use the root obligation as the main message, when we care about the // most general case ("X doesn't implement Pattern<'_>") over the case that @@ -424,7 +424,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let (main_trait_predicate, o) = if let ty::PredicateKind::Clause( ty::ClauseKind::Trait(root_pred) ) = root_obligation.predicate.kind().skip_binder() - && !trait_predicate.self_ty().skip_binder().has_escaping_bound_vars() + && !leaf_trait_predicate.self_ty().skip_binder().has_escaping_bound_vars() && !root_pred.self_ty().has_escaping_bound_vars() // The type of the leaf predicate is (roughly) the same as the type // from the root predicate, as a proxy for "we care about the root" @@ -434,20 +434,20 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // `T: Trait` && `&&T: OtherTrait`, we want `OtherTrait` self.can_eq( obligation.param_env, - trait_predicate.self_ty().skip_binder(), + leaf_trait_predicate.self_ty().skip_binder(), root_pred.self_ty().peel_refs(), ) // `&str: Iterator` && `&str: IntoIterator`, we want `IntoIterator` || self.can_eq( obligation.param_env, - trait_predicate.self_ty().skip_binder(), + leaf_trait_predicate.self_ty().skip_binder(), root_pred.self_ty(), ) ) // The leaf trait and the root trait are different, so as to avoid // talking about `&mut T: Trait` and instead remain talking about // `T: Trait` instead - && trait_predicate.def_id() != root_pred.def_id() + && leaf_trait_predicate.def_id() != root_pred.def_id() // The root trait is not `Unsize`, as to avoid talking about it in // `tests/ui/coercion/coerce-issue-49593-box-never.rs`. && Some(root_pred.def_id()) != self.tcx.lang_items().unsize_trait() @@ -459,13 +459,14 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { root_obligation, ) } else { - (trait_predicate, &obligation) + (leaf_trait_predicate, &obligation) }; - let trait_ref = main_trait_predicate.to_poly_trait_ref(); + let main_trait_ref = main_trait_predicate.to_poly_trait_ref(); + let leaf_trait_ref = leaf_trait_predicate.to_poly_trait_ref(); if let Some(guar) = self.emit_specialized_closure_kind_error( &obligation, - trait_ref, + leaf_trait_ref, ) { return guar; } @@ -473,7 +474,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // FIXME(effects) let predicate_is_const = false; - if let Err(guar) = trait_predicate.error_reported() + if let Err(guar) = leaf_trait_predicate.error_reported() { return guar; } @@ -507,16 +508,17 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { notes, parent_label, append_const_msg, - } = self.on_unimplemented_note(trait_ref, o, &mut long_ty_file); + } = self.on_unimplemented_note(main_trait_ref, o, &mut long_ty_file); + let have_alt_message = message.is_some() || label.is_some(); - let is_try_conversion = self.is_try_conversion(span, trait_ref.def_id()); + let is_try_conversion = self.is_try_conversion(span, main_trait_ref.def_id()); let is_unsize = - Some(trait_ref.def_id()) == self.tcx.lang_items().unsize_trait(); + Some(leaf_trait_ref.def_id()) == self.tcx.lang_items().unsize_trait(); let (message, notes, append_const_msg) = if is_try_conversion { ( Some(format!( "`?` couldn't convert the error to `{}`", - trait_ref.skip_binder().self_ty(), + main_trait_ref.skip_binder().self_ty(), )), vec![ "the question mark operation (`?`) implicitly performs a \ @@ -530,20 +532,20 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }; let err_msg = self.get_standard_error_message( - &main_trait_predicate, + main_trait_predicate, message, predicate_is_const, append_const_msg, post_message, ); - let (err_msg, safe_transmute_explanation) = if Some(trait_ref.def_id()) + let (err_msg, safe_transmute_explanation) = if Some(main_trait_ref.def_id()) == self.tcx.lang_items().transmute_trait() { // Recompute the safe transmute reason and use that for the error reporting match self.get_safe_transmute_error_and_reason( obligation.clone(), - trait_ref, + main_trait_ref, span, ) { GetSafeTransmuteErrorAndReason::Silent => { @@ -571,7 +573,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } let mut suggested = false; if is_try_conversion { - suggested = self.try_conversion_context(&obligation, trait_ref.skip_binder(), &mut err); + suggested = self.try_conversion_context(&obligation, main_trait_ref.skip_binder(), &mut err); } if is_try_conversion && let Some(ret_span) = self.return_type_span(&obligation) { @@ -579,19 +581,19 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ret_span, format!( "expected `{}` because of this", - trait_ref.skip_binder().self_ty() + main_trait_ref.skip_binder().self_ty() ), ); } - if Some(trait_ref.def_id()) == tcx.lang_items().tuple_trait() { + if Some(leaf_trait_ref.def_id()) == tcx.lang_items().tuple_trait() { self.add_tuple_trait_message( obligation.cause.code().peel_derives(), &mut err, ); } - if Some(trait_ref.def_id()) == tcx.lang_items().drop_trait() + if Some(leaf_trait_ref.def_id()) == tcx.lang_items().drop_trait() && predicate_is_const { err.note("`~const Drop` was renamed to `~const Destruct`"); @@ -601,24 +603,25 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let explanation = get_explanation_based_on_obligation( self.tcx, &obligation, - trait_ref, - &trait_predicate, + leaf_trait_predicate, pre_message, ); self.check_for_binding_assigned_block_without_tail_expression( &obligation, &mut err, - trait_predicate, + leaf_trait_predicate, ); - self.suggest_add_result_as_return_type(&obligation, + self.suggest_add_result_as_return_type( + &obligation, &mut err, - trait_ref); + leaf_trait_predicate, + ); if self.suggest_add_reference_to_arg( &obligation, &mut err, - trait_predicate, + leaf_trait_predicate, have_alt_message, ) { self.note_obligation_cause(&mut err, &obligation); @@ -630,7 +633,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // If it has a custom `#[rustc_on_unimplemented]` // error message, let's display it as the label! err.span_label(span, s); - if !matches!(trait_ref.skip_binder().self_ty().kind(), ty::Param(_)) { + if !matches!(leaf_trait_ref.skip_binder().self_ty().kind(), ty::Param(_)) { // When the self type is a type param We don't need to "the trait // `std::marker::Sized` is not implemented for `T`" as we will point // at the type param with a label to suggest constraining it. @@ -645,7 +648,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if let ObligationCauseCode::Coercion { source, target } = *obligation.cause.code().peel_derives() { - if Some(trait_ref.def_id()) == self.tcx.lang_items().sized_trait() { + if Some(leaf_trait_ref.def_id()) == self.tcx.lang_items().sized_trait() { self.suggest_borrowing_for_object_cast( &mut err, root_obligation, @@ -657,7 +660,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let UnsatisfiedConst(unsatisfied_const) = self .maybe_add_note_for_unsatisfied_const( - &trait_predicate, + leaf_trait_predicate, &mut err, span, ); @@ -674,15 +677,15 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { err.span_label(tcx.def_span(body), s); } - self.suggest_floating_point_literal(&obligation, &mut err, &trait_ref); - self.suggest_dereferencing_index(&obligation, &mut err, trait_predicate); - suggested |= self.suggest_dereferences(&obligation, &mut err, trait_predicate); - suggested |= self.suggest_fn_call(&obligation, &mut err, trait_predicate); - let impl_candidates = self.find_similar_impl_candidates(trait_predicate); + self.suggest_floating_point_literal(&obligation, &mut err, leaf_trait_ref); + self.suggest_dereferencing_index(&obligation, &mut err, leaf_trait_predicate); + suggested |= self.suggest_dereferences(&obligation, &mut err, leaf_trait_predicate); + suggested |= self.suggest_fn_call(&obligation, &mut err, leaf_trait_predicate); + let impl_candidates = self.find_similar_impl_candidates(leaf_trait_predicate); suggested = if let &[cand] = &impl_candidates[..] { let cand = cand.trait_ref; if let (ty::FnPtr(_), ty::FnDef(..)) = - (cand.self_ty().kind(), trait_ref.self_ty().skip_binder().kind()) + (cand.self_ty().kind(), main_trait_ref.self_ty().skip_binder().kind()) { err.span_suggestion( span.shrink_to_hi(), @@ -702,31 +705,31 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { false } || suggested; suggested |= - self.suggest_remove_reference(&obligation, &mut err, trait_predicate); + self.suggest_remove_reference(&obligation, &mut err, leaf_trait_predicate); suggested |= self.suggest_semicolon_removal( &obligation, &mut err, span, - trait_predicate, + leaf_trait_predicate, ); - self.note_version_mismatch(&mut err, &trait_ref); + self.note_version_mismatch(&mut err, leaf_trait_ref); self.suggest_remove_await(&obligation, &mut err); - self.suggest_derive(&obligation, &mut err, trait_predicate); + self.suggest_derive(&obligation, &mut err, leaf_trait_predicate); - if Some(trait_ref.def_id()) == tcx.lang_items().try_trait() { + if Some(leaf_trait_ref.def_id()) == tcx.lang_items().try_trait() { self.suggest_await_before_try( &mut err, &obligation, - trait_predicate, + leaf_trait_predicate, span, ); } - if self.suggest_add_clone_to_arg(&obligation, &mut err, trait_predicate) { + if self.suggest_add_clone_to_arg(&obligation, &mut err, leaf_trait_predicate) { return err.emit(); } - if self.suggest_impl_trait(&mut err, &obligation, trait_predicate) { + if self.suggest_impl_trait(&mut err, &obligation, leaf_trait_predicate) { return err.emit(); } @@ -741,9 +744,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); } - let is_fn_trait = tcx.is_fn_trait(trait_ref.def_id()); + let is_fn_trait = tcx.is_fn_trait(leaf_trait_ref.def_id()); let is_target_feature_fn = if let ty::FnDef(def_id, _) = - *trait_ref.skip_binder().self_ty().kind() + *leaf_trait_ref.skip_binder().self_ty().kind() { !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty() } else { @@ -757,8 +760,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { self.try_to_add_help_message( &obligation, - trait_ref, - &trait_predicate, + leaf_trait_predicate, &mut err, span, is_fn_trait, @@ -769,17 +771,17 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // Changing mutability doesn't make a difference to whether we have // an `Unsize` impl (Fixes ICE in #71036) if !is_unsize { - self.suggest_change_mut(&obligation, &mut err, trait_predicate); + self.suggest_change_mut(&obligation, &mut err, leaf_trait_predicate); } // If this error is due to `!: Trait` not implemented but `(): Trait` is // implemented, and fallback has occurred, then it could be due to a // variable that used to fallback to `()` now falling back to `!`. Issue a // note informing about the change in behaviour. - if trait_predicate.skip_binder().self_ty().is_never() + if leaf_trait_predicate.skip_binder().self_ty().is_never() && self.fallback_has_occurred { - let predicate = trait_predicate.map_bound(|trait_pred| { + let predicate = leaf_trait_predicate.map_bound(|trait_pred| { trait_pred.with_self_ty(self.tcx, tcx.types.unit) }); let unit_obligation = obligation.with(tcx, predicate); @@ -794,8 +796,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } - self.explain_hrtb_projection(&mut err, trait_predicate, obligation.param_env, &obligation.cause); - self.suggest_desugaring_async_fn_in_trait(&mut err, trait_ref); + self.explain_hrtb_projection(&mut err, leaf_trait_predicate, obligation.param_env, &obligation.cause); + self.suggest_desugaring_async_fn_in_trait(&mut err, main_trait_ref); // Return early if the trait is Debug or Display and the invocation // originates within a standard library macro, because the output @@ -813,15 +815,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if in_std_macro && matches!( - self.tcx.get_diagnostic_name(trait_ref.def_id()), + self.tcx.get_diagnostic_name(leaf_trait_ref.def_id()), Some(sym::Debug | sym::Display) ) { return err.emit(); } - - err } @@ -2236,11 +2236,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { /// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait /// with the same path as `trait_ref`, a help message about /// a probable version mismatch is added to `err` - fn note_version_mismatch( - &self, - err: &mut Diag<'_>, - trait_ref: &ty::PolyTraitRef<'tcx>, - ) -> bool { + fn note_version_mismatch(&self, err: &mut Diag<'_>, trait_ref: ty::PolyTraitRef<'tcx>) -> bool { let get_trait_impls = |trait_def_id| { let mut trait_impls = vec![]; self.tcx.for_each_relevant_impl( @@ -2705,6 +2701,22 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ), ); } + + ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term }) + if term.is_infer() => + { + if let Some(e) = self.tainted_by_errors() { + return e; + } + struct_span_code_err!( + self.dcx(), + span, + E0284, + "type annotations needed: cannot normalize `{alias}`", + ) + .with_span_label(span, format!("cannot normalize `{alias}`")) + } + _ => { if let Some(e) = self.tainted_by_errors() { return e; @@ -3044,7 +3056,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn get_standard_error_message( &self, - trait_predicate: &ty::PolyTraitPredicate<'tcx>, + trait_predicate: ty::PolyTraitPredicate<'tcx>, message: Option<String>, predicate_is_const: bool, append_const_msg: Option<AppendConstMessage>, @@ -3215,8 +3227,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn try_to_add_help_message( &self, obligation: &PredicateObligation<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, - trait_predicate: &ty::PolyTraitPredicate<'tcx>, + trait_predicate: ty::PolyTraitPredicate<'tcx>, err: &mut Diag<'_>, span: Span, is_fn_trait: bool, @@ -3233,16 +3244,22 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }; // Try to report a help message + let trait_def_id = trait_predicate.def_id(); if is_fn_trait && let Ok((implemented_kind, params)) = self.type_implements_fn_trait( obligation.param_env, - trait_ref.self_ty(), + trait_predicate.self_ty(), trait_predicate.skip_binder().polarity, ) { - self.add_help_message_for_fn_trait(trait_ref, err, implemented_kind, params); - } else if !trait_ref.has_non_region_infer() - && self.predicate_can_apply(obligation.param_env, *trait_predicate) + self.add_help_message_for_fn_trait( + trait_predicate.to_poly_trait_ref(), + err, + implemented_kind, + params, + ); + } else if !trait_predicate.has_non_region_infer() + && self.predicate_can_apply(obligation.param_env, trait_predicate) { // If a where-clause may be useful, remind the // user that they can add it. @@ -3253,25 +3270,25 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // which is somewhat confusing. self.suggest_restricting_param_bound( err, - *trait_predicate, + trait_predicate, None, obligation.cause.body_id, ); - } else if trait_ref.def_id().is_local() - && self.tcx.trait_impls_of(trait_ref.def_id()).is_empty() - && !self.tcx.trait_is_auto(trait_ref.def_id()) - && !self.tcx.trait_is_alias(trait_ref.def_id()) + } else if trait_def_id.is_local() + && self.tcx.trait_impls_of(trait_def_id).is_empty() + && !self.tcx.trait_is_auto(trait_def_id) + && !self.tcx.trait_is_alias(trait_def_id) { err.span_help( - self.tcx.def_span(trait_ref.def_id()), + self.tcx.def_span(trait_def_id), crate::fluent_generated::trait_selection_trait_has_no_impls, ); } else if !suggested && !unsatisfied_const { // Can't show anything else useful, try to find similar impls. - let impl_candidates = self.find_similar_impl_candidates(*trait_predicate); + let impl_candidates = self.find_similar_impl_candidates(trait_predicate); if !self.report_similar_impl_candidates( &impl_candidates, - trait_ref, + trait_predicate.to_poly_trait_ref(), body_def_id, err, true, @@ -3279,7 +3296,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ) { self.report_similar_impl_candidates_for_root_obligation( obligation, - *trait_predicate, + trait_predicate, body_def_id, err, ); @@ -3288,7 +3305,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { self.suggest_convert_to_slice( err, obligation, - trait_ref, + trait_predicate.to_poly_trait_ref(), impl_candidates.as_slice(), span, ); @@ -3353,7 +3370,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn maybe_add_note_for_unsatisfied_const( &self, - _trait_predicate: &ty::PolyTraitPredicate<'tcx>, + _trait_predicate: ty::PolyTraitPredicate<'tcx>, _err: &mut Diag<'_>, _span: Span, ) -> UnsatisfiedConst { diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 08355ef55c4..fc5a2875b67 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -194,7 +194,7 @@ fn predicates_reference_self( predicates .predicates .iter() - .map(|&(predicate, sp)| (predicate.instantiate_supertrait(tcx, &trait_ref), sp)) + .map(|&(predicate, sp)| (predicate.instantiate_supertrait(tcx, trait_ref), sp)) .filter_map(|predicate| predicate_references_self(tcx, predicate)) .collect() } diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 2c9cb79664b..32409e13842 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1373,15 +1373,16 @@ fn confirm_coroutine_candidate<'cx, 'tcx>( coroutine_sig, ); - let name = tcx.associated_item(obligation.predicate.def_id).name; - let ty = if name == sym::Return { + let lang_items = tcx.lang_items(); + let ty = if Some(obligation.predicate.def_id) == lang_items.coroutine_return() { return_ty - } else if name == sym::Yield { + } else if Some(obligation.predicate.def_id) == lang_items.coroutine_yield() { yield_ty } else { span_bug!( tcx.def_span(obligation.predicate.def_id), - "unexpected associated type: `Coroutine::{name}`" + "unexpected associated type: `Coroutine::{}`", + tcx.item_name(obligation.predicate.def_id), ); }; diff --git a/compiler/rustc_infer/src/infer/relate/_match.rs b/compiler/rustc_trait_selection/src/traits/select/_match.rs index 30a066a265a..50d8e96aaf9 100644 --- a/compiler/rustc_infer/src/infer/relate/_match.rs +++ b/compiler/rustc_trait_selection/src/traits/select/_match.rs @@ -1,10 +1,10 @@ +use rustc_infer::infer::relate::{ + self, structurally_relate_tys, Relate, RelateResult, TypeRelation, +}; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{self, InferConst, Ty, TyCtxt}; use tracing::{debug, instrument}; -use super::{structurally_relate_tys, Relate, RelateResult, TypeRelation}; -use crate::infer::relate; - /// A type "A" *matches* "B" if the fresh types in B could be /// instantiated with values so as to make it equal to A. Matching is /// intended to be used only on freshened types, and it basically diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 4306a803524..212ef2e4b2b 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -32,7 +32,6 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{Diag, EmissionGuarantee}; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_infer::infer::relate::MatchAgainstFreshVars; use rustc_infer::infer::relate::TypeRelation; use rustc_infer::infer::BoundRegionConversionTime; use rustc_infer::infer::BoundRegionConversionTime::HigherRankedType; @@ -60,6 +59,7 @@ use std::ops::ControlFlow; pub use rustc_middle::traits::select::*; use rustc_middle::ty::print::with_no_trimmed_paths; +mod _match; mod candidate_assembly; mod confirmation; @@ -1866,7 +1866,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { // the param_env so that it can be given the lowest priority. See // #50825 for the motivation for this. let is_global = - |cand: &ty::PolyTraitPredicate<'tcx>| cand.is_global() && !cand.has_bound_vars(); + |cand: ty::PolyTraitPredicate<'tcx>| cand.is_global() && !cand.has_bound_vars(); // (*) Prefer `BuiltinCandidate { has_nested: false }`, `PointeeCandidate`, // `DiscriminantKindCandidate`, `ConstDestructCandidate` @@ -1909,7 +1909,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } ( - ParamCandidate(ref other_cand), + ParamCandidate(other_cand), ImplCandidate(..) | AutoImplCandidate | ClosureCandidate { .. } @@ -1934,12 +1934,12 @@ impl<'tcx> SelectionContext<'_, 'tcx> { // // Global bounds from the where clause should be ignored // here (see issue #50825). - DropVictim::drop_if(!is_global(other_cand)) + DropVictim::drop_if(!is_global(*other_cand)) } - (ObjectCandidate(_) | ProjectionCandidate(_), ParamCandidate(ref victim_cand)) => { + (ObjectCandidate(_) | ProjectionCandidate(_), ParamCandidate(victim_cand)) => { // Prefer these to a global where-clause bound // (see issue #50825). - if is_global(victim_cand) { DropVictim::Yes } else { DropVictim::No } + if is_global(*victim_cand) { DropVictim::Yes } else { DropVictim::No } } ( ImplCandidate(_) @@ -1957,12 +1957,12 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { has_nested: true } | TraitAliasCandidate, - ParamCandidate(ref victim_cand), + ParamCandidate(victim_cand), ) => { // Prefer these to a global where-clause bound // (see issue #50825). DropVictim::drop_if( - is_global(victim_cand) && other.evaluation.must_apply_modulo_regions(), + is_global(*victim_cand) && other.evaluation.must_apply_modulo_regions(), ) } @@ -2719,7 +2719,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { previous: ty::PolyTraitPredicate<'tcx>, current: ty::PolyTraitPredicate<'tcx>, ) -> bool { - let mut matcher = MatchAgainstFreshVars::new(self.tcx()); + let mut matcher = _match::MatchAgainstFreshVars::new(self.tcx()); matcher.relate(previous, current).is_ok() } diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index 960c27b636e..ce7245d93a4 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -132,7 +132,7 @@ impl<'tcx> TraitAliasExpander<'tcx> { debug!(?predicates); let items = predicates.predicates.iter().rev().filter_map(|(pred, span)| { - pred.instantiate_supertrait(tcx, &trait_ref) + pred.instantiate_supertrait(tcx, trait_ref) .as_trait_clause() .map(|trait_ref| item.clone_and_push(trait_ref.map_bound(|t| t.trait_ref), *span)) }); diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index c93ec43944a..9bd4a9aab0a 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -125,7 +125,7 @@ fn prepare_vtable_segments_inner<'tcx, T>( .predicates .into_iter() .filter_map(move |(pred, _)| { - pred.instantiate_supertrait(tcx, &inner_most_trait_ref).as_trait_clause() + pred.instantiate_supertrait(tcx, inner_most_trait_ref).as_trait_clause() }); // Find an unvisited supertrait diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index eae1a9dfaa2..241381f5875 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -331,30 +331,63 @@ pub(crate) mod rustc { assert!(def.is_enum()); let layout = ty_and_layout.layout; - if let Variants::Multiple { tag_field, .. } = layout.variants() { - // For enums (but not coroutines), the tag field is - // currently always the first field of the layout. - assert_eq!(*tag_field, 0); - } + // Computes the variant of a given index. + let layout_of_variant = |index| { + let tag = cx.tcx.tag_for_variant((ty_and_layout.ty, index)); + let variant_def = Def::Variant(def.variant(index)); + let variant_ty_and_layout = ty_and_layout.for_variant(&cx, index); + Self::from_variant(variant_def, tag, variant_ty_and_layout, layout.size, cx) + }; - let variants = def.discriminants(cx.tcx()).try_fold( - Self::uninhabited(), - |variants, (idx, ref discriminant)| { - let tag = cx.tcx.tag_for_variant((ty_and_layout.ty, idx)); - let variant_def = Def::Variant(def.variant(idx)); - let variant_ty_and_layout = ty_and_layout.for_variant(&cx, idx); - let variant = Self::from_variant( - variant_def, - tag, - variant_ty_and_layout, - layout.size, - cx, + // We consider three kinds of enums, each demanding a different + // treatment of their layout computation: + // 1. enums that are uninhabited + // 2. enums for which all but one variant is uninhabited + // 3. enums with multiple inhabited variants + match layout.variants() { + _ if layout.abi.is_uninhabited() => { + // Uninhabited enums are usually (always?) zero-sized. In + // the (unlikely?) event that an uninhabited enum is + // non-zero-sized, this assert will trigger an ICE, and this + // code should be modified such that a `layout.size` amount + // of uninhabited bytes is returned instead. + // + // Uninhabited enums are currently implemented such that + // their layout is described with `Variants::Single`, even + // though they don't necessarily have a 'single' variant to + // defer to. That said, we don't bother specifically + // matching on `Variants::Single` in this arm because the + // behavioral principles here remain true even if, for + // whatever reason, the compiler describes an uninhabited + // enum with `Variants::Multiple`. + assert_eq!(layout.size, Size::ZERO); + Ok(Self::uninhabited()) + } + Variants::Single { index } => { + // `Variants::Single` on non-uninhabited enums denotes that + // the enum delegates its layout to the variant at `index`. + layout_of_variant(*index) + } + Variants::Multiple { tag_field, .. } => { + // `Variants::Multiple` denotes an enum with multiple + // inhabited variants. The layout of such an enum is the + // disjunction of the layouts of its tagged variants. + + // For enums (but not coroutines), the tag field is + // currently always the first field of the layout. + assert_eq!(*tag_field, 0); + + let variants = def.discriminants(cx.tcx()).try_fold( + Self::uninhabited(), + |variants, (idx, ref discriminant)| { + let variant = layout_of_variant(idx)?; + Result::<Self, Err>::Ok(variants.or(variant)) + }, )?; - Result::<Self, Err>::Ok(variants.or(variant)) - }, - )?; - return Ok(Self::def(Def::Adt(def)).then(variants)); + return Ok(Self::def(Def::Adt(def)).then(variants)); + } + } } /// Constructs a `Tree` from a 'variant-like' layout. diff --git a/compiler/rustc_type_ir/src/infcx.rs b/compiler/rustc_type_ir/src/infcx.rs index 24e10722448..92a717a0d9e 100644 --- a/compiler/rustc_type_ir/src/infcx.rs +++ b/compiler/rustc_type_ir/src/infcx.rs @@ -1,23 +1,75 @@ -use crate::{ConstVid, EffectVid, FloatVid, IntVid, Interner, RegionVid, TyVid, UniverseIndex}; +use crate::fold::TypeFoldable; +use crate::relate::Relate; +use crate::solve::{Goal, NoSolution}; +use crate::{self as ty, Interner}; -pub trait InferCtxtLike { +pub trait InferCtxtLike: Sized { type Interner: Interner; fn interner(&self) -> Self::Interner; - fn universe_of_ty(&self, ty: TyVid) -> Option<UniverseIndex>; - fn universe_of_lt(&self, lt: RegionVid) -> Option<UniverseIndex>; - fn universe_of_ct(&self, ct: ConstVid) -> Option<UniverseIndex>; + fn universe_of_ty(&self, ty: ty::TyVid) -> Option<ty::UniverseIndex>; + fn universe_of_lt(&self, lt: ty::RegionVid) -> Option<ty::UniverseIndex>; + fn universe_of_ct(&self, ct: ty::ConstVid) -> Option<ty::UniverseIndex>; - fn opportunistic_resolve_ty_var(&self, vid: TyVid) -> <Self::Interner as Interner>::Ty; - fn opportunistic_resolve_int_var(&self, vid: IntVid) -> <Self::Interner as Interner>::Ty; - fn opportunistic_resolve_float_var(&self, vid: FloatVid) -> <Self::Interner as Interner>::Ty; - fn opportunistic_resolve_ct_var(&self, vid: ConstVid) -> <Self::Interner as Interner>::Const; + fn root_ty_var(&self, var: ty::TyVid) -> ty::TyVid; + fn root_const_var(&self, var: ty::ConstVid) -> ty::ConstVid; + + fn opportunistic_resolve_ty_var(&self, vid: ty::TyVid) -> <Self::Interner as Interner>::Ty; + fn opportunistic_resolve_int_var(&self, vid: ty::IntVid) -> <Self::Interner as Interner>::Ty; + fn opportunistic_resolve_float_var( + &self, + vid: ty::FloatVid, + ) -> <Self::Interner as Interner>::Ty; + fn opportunistic_resolve_ct_var( + &self, + vid: ty::ConstVid, + ) -> <Self::Interner as Interner>::Const; fn opportunistic_resolve_effect_var( &self, - vid: EffectVid, + vid: ty::EffectVid, ) -> <Self::Interner as Interner>::Const; - fn opportunistic_resolve_lt_var(&self, vid: RegionVid) -> <Self::Interner as Interner>::Region; + fn opportunistic_resolve_lt_var( + &self, + vid: ty::RegionVid, + ) -> <Self::Interner as Interner>::Region; fn defining_opaque_types(&self) -> <Self::Interner as Interner>::DefiningOpaqueTypes; + + fn next_ty_infer(&self) -> <Self::Interner as Interner>::Ty; + fn next_const_infer(&self) -> <Self::Interner as Interner>::Const; + fn fresh_args_for_item( + &self, + def_id: <Self::Interner as Interner>::DefId, + ) -> <Self::Interner as Interner>::GenericArgs; + + fn instantiate_binder_with_infer<T: TypeFoldable<Self::Interner> + Copy>( + &self, + value: ty::Binder<Self::Interner, T>, + ) -> T; + + fn enter_forall<T: TypeFoldable<Self::Interner> + Copy, U>( + &self, + value: ty::Binder<Self::Interner, T>, + f: impl FnOnce(T) -> U, + ) -> U; + + fn relate<T: Relate<Self::Interner>>( + &self, + param_env: <Self::Interner as Interner>::ParamEnv, + lhs: T, + variance: ty::Variance, + rhs: T, + ) -> Result<Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>, NoSolution>; + + fn eq_structurally_relating_aliases<T: Relate<Self::Interner>>( + &self, + param_env: <Self::Interner as Interner>::ParamEnv, + lhs: T, + rhs: T, + ) -> Result<Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>, NoSolution>; + + fn resolve_vars_if_possible<T>(&self, value: T) -> T + where + T: TypeFoldable<Self::Interner>; } diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 2fc765f1c8f..6b84592978a 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -29,6 +29,8 @@ pub trait Ty<I: Interner<Ty = Self>>: { fn new_bool(interner: I) -> Self; + fn new_u8(interner: I) -> Self; + fn new_infer(interner: I, var: ty::InferTy) -> Self; fn new_var(interner: I, var: ty::TyVid) -> Self; @@ -39,6 +41,18 @@ pub trait Ty<I: Interner<Ty = Self>>: fn new_alias(interner: I, kind: ty::AliasTyKind, alias_ty: ty::AliasTy<I>) -> Self; + fn new_projection( + interner: I, + def_id: I::DefId, + args: impl IntoIterator<Item: Into<I::GenericArg>>, + ) -> Self { + Ty::new_alias( + interner, + ty::AliasTyKind::Projection, + ty::AliasTy::new(interner, def_id, args), + ) + } + fn new_error(interner: I, guar: I::ErrorGuaranteed) -> Self; fn new_adt(interner: I, adt_def: I::AdtDef, args: I::GenericArgs) -> Self; @@ -75,6 +89,12 @@ pub trait Ty<I: Interner<Ty = Self>>: It: Iterator<Item = T>, T: CollectAndApply<Self, Self>; + fn new_fn_def(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self; + + fn new_fn_ptr(interner: I, sig: ty::Binder<I, ty::FnSig<I>>) -> Self; + + fn new_pat(interner: I, ty: Self, pat: I::Pat) -> Self; + fn tuple_fields(self) -> I::Tys; fn to_opt_closure_kind(self) -> Option<ty::ClosureKind>; @@ -83,11 +103,29 @@ pub trait Ty<I: Interner<Ty = Self>>: fn from_coroutine_closure_kind(interner: I, kind: ty::ClosureKind) -> Self; - fn new_fn_def(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self; - - fn new_fn_ptr(interner: I, sig: ty::Binder<I, ty::FnSig<I>>) -> Self; - - fn new_pat(interner: I, ty: Self, pat: I::Pat) -> Self; + fn is_ty_var(self) -> bool { + matches!(self.kind(), ty::Infer(ty::TyVar(_))) + } + + fn fn_sig(self, interner: I) -> ty::Binder<I, ty::FnSig<I>> { + match self.kind() { + ty::FnPtr(sig) => sig, + ty::FnDef(def_id, args) => interner.fn_sig(def_id).instantiate(interner, &args), + ty::Error(_) => { + // ignore errors (#54954) + ty::Binder::dummy(ty::FnSig { + inputs_and_output: Default::default(), + c_variadic: false, + safety: I::Safety::safe(), + abi: I::Abi::rust(), + }) + } + ty::Closure(..) => panic!( + "to get the signature of a closure, use `args.as_closure().sig()` not `fn_sig()`", + ), + _ => panic!("Ty::fn_sig() called on non-fn type: {:?}", self), + } + } } pub trait Tys<I: Interner<Tys = Self>>: @@ -103,12 +141,16 @@ pub trait Tys<I: Interner<Tys = Self>>: fn split_inputs_and_output(self) -> (I::FnInputTys, I::Ty); } -pub trait Abi<I: Interner<Abi = Self>>: Copy + Debug + Hash + Eq + TypeVisitable<I> { +pub trait Abi<I: Interner<Abi = Self>>: Copy + Debug + Hash + Eq + Relate<I> { + fn rust() -> Self; + /// Whether this ABI is `extern "Rust"`. fn is_rust(self) -> bool; } -pub trait Safety<I: Interner<Safety = Self>>: Copy + Debug + Hash + Eq + TypeVisitable<I> { +pub trait Safety<I: Interner<Safety = Self>>: Copy + Debug + Hash + Eq + Relate<I> { + fn safe() -> Self; + fn is_safe(self) -> bool; fn prefix_str(self) -> &'static str; @@ -122,7 +164,6 @@ pub trait Region<I: Interner<Region = Self>>: + Into<I::GenericArg> + IntoKind<Kind = ty::RegionKind<I>> + Flags - + TypeVisitable<I> + Relate<I> { fn new_bound(interner: I, debruijn: ty::DebruijnIndex, var: I::BoundRegion) -> Self; @@ -158,12 +199,57 @@ pub trait Const<I: Interner<Const = Self>>: fn new_unevaluated(interner: I, uv: ty::UnevaluatedConst<I>) -> Self; fn new_expr(interner: I, expr: I::ExprConst) -> Self; + + fn is_ct_var(self) -> bool { + matches!(self.kind(), ty::ConstKind::Infer(ty::InferConst::Var(_))) + } } pub trait GenericsOf<I: Interner<GenericsOf = Self>> { fn count(&self) -> usize; } +pub trait GenericArg<I: Interner<GenericArg = Self>>: + Copy + + Debug + + Hash + + Eq + + IntoKind<Kind = ty::GenericArgKind<I>> + + TypeVisitable<I> + + Relate<I> + + From<I::Ty> + + From<I::Region> + + From<I::Const> +{ +} + +pub trait Term<I: Interner<Term = Self>>: + Copy + Debug + Hash + Eq + IntoKind<Kind = ty::TermKind<I>> + TypeFoldable<I> + Relate<I> +{ + fn as_type(&self) -> Option<I::Ty> { + if let ty::TermKind::Ty(ty) = self.kind() { Some(ty) } else { None } + } + + fn expect_type(&self) -> I::Ty { + self.as_type().expect("expected a type, but found a const") + } + + fn as_const(&self) -> Option<I::Const> { + if let ty::TermKind::Const(c) = self.kind() { Some(c) } else { None } + } + + fn expect_const(&self) -> I::Const { + self.as_const().expect("expected a const, but found a type") + } + + fn is_infer(self) -> bool { + match self.kind() { + ty::TermKind::Ty(ty) => ty.is_ty_var(), + ty::TermKind::Const(ct) => ct.is_ct_var(), + } + } +} + pub trait GenericArgs<I: Interner<GenericArgs = Self>>: Copy + Debug @@ -172,7 +258,6 @@ pub trait GenericArgs<I: Interner<GenericArgs = Self>>: + IntoIterator<Item = I::GenericArg> + Deref<Target: Deref<Target = [I::GenericArg]>> + Default - + TypeFoldable<I> + Relate<I> { fn type_at(self, i: usize) -> I::Ty; @@ -188,6 +273,16 @@ pub trait GenericArgs<I: Interner<GenericArgs = Self>>: fn split_closure_args(self) -> ty::ClosureArgsParts<I>; fn split_coroutine_closure_args(self) -> ty::CoroutineClosureArgsParts<I>; fn split_coroutine_args(self) -> ty::CoroutineArgsParts<I>; + + fn as_closure(self) -> ty::ClosureArgs<I> { + ty::ClosureArgs { args: self } + } + fn as_coroutine_closure(self) -> ty::CoroutineClosureArgs<I> { + ty::CoroutineClosureArgs { args: self } + } + fn as_coroutine(self) -> ty::CoroutineArgs<I> { + ty::CoroutineArgs { args: self } + } } pub trait Predicate<I: Interner<Predicate = Self>>: @@ -198,9 +293,20 @@ pub trait Predicate<I: Interner<Predicate = Self>>: + TypeSuperVisitable<I> + TypeSuperFoldable<I> + Flags + + UpcastFrom<I, ty::PredicateKind<I>> + + UpcastFrom<I, ty::Binder<I, ty::PredicateKind<I>>> + + UpcastFrom<I, ty::ClauseKind<I>> + + UpcastFrom<I, ty::Binder<I, ty::ClauseKind<I>>> + + UpcastFrom<I, I::Clause> + UpcastFrom<I, ty::NormalizesTo<I>> + + UpcastFrom<I, ty::TraitRef<I>> + + UpcastFrom<I, ty::Binder<I, ty::TraitRef<I>>> + + IntoKind<Kind = ty::Binder<I, ty::PredicateKind<I>>> { fn is_coinductive(self, interner: I) -> bool; + + // FIXME: Eventually uplift the impl out of rustc and make this defaulted. + fn allow_normalization(self) -> bool; } pub trait Clause<I: Interner<Clause = Self>>: @@ -208,6 +314,7 @@ pub trait Clause<I: Interner<Clause = Self>>: + Debug + Hash + Eq + + TypeFoldable<I> // FIXME: Remove these, uplift the `Upcast` impls. + UpcastFrom<I, ty::Binder<I, ty::TraitRef<I>>> + UpcastFrom<I, ty::Binder<I, ty::ProjectionPredicate<I>>> @@ -242,8 +349,17 @@ pub trait ParamLike { pub trait AdtDef<I: Interner>: Copy + Debug + Hash + Eq { fn def_id(self) -> I::DefId; + + fn is_phantom_data(self) -> bool; + + // FIXME: perhaps use `all_fields` and expose `FieldDef`. + fn all_field_tys(self, interner: I) -> ty::EarlyBinder<I, impl Iterator<Item = I::Ty>>; + + fn sized_constraint(self, interner: I) -> Option<ty::EarlyBinder<I, I::Ty>>; } pub trait Features<I: Interner>: Copy { fn generic_const_exprs(self) -> bool; + + fn coroutine_clone(self) -> bool; } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index b7f412ecb8e..a2b71e1fc25 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -1,3 +1,4 @@ +use rustc_ast_ir::Movability; use smallvec::SmallVec; use std::fmt::Debug; use std::hash::Hash; @@ -6,6 +7,7 @@ use std::ops::Deref; use crate::fold::TypeFoldable; use crate::inherent::*; use crate::ir_print::IrPrint; +use crate::lang_items::TraitSolverLangItem; use crate::relate::Relate; use crate::solve::inspect::CanonicalGoalEvaluationStep; use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable}; @@ -31,20 +33,8 @@ pub trait Interner: type GenericArgs: GenericArgs<Self>; type GenericArgsSlice: Copy + Debug + Hash + Eq + Deref<Target = [Self::GenericArg]>; - type GenericArg: Copy - + Debug - + Hash - + Eq - + IntoKind<Kind = ty::GenericArgKind<Self>> - + TypeVisitable<Self> - + Relate<Self>; - type Term: Copy - + Debug - + Hash - + Eq - + IntoKind<Kind = ty::TermKind<Self>> - + TypeFoldable<Self> - + Relate<Self>; + type GenericArg: GenericArg<Self>; + type Term: Term<Self>; type BoundVarKinds: Copy + Debug @@ -74,11 +64,16 @@ pub trait Interner: // Things stored inside of tys type ErrorGuaranteed: Copy + Debug + Hash + Eq; - type BoundExistentialPredicates: Copy + Debug + Hash + Eq + Relate<Self>; + type BoundExistentialPredicates: Copy + + Debug + + Hash + + Eq + + Relate<Self> + + IntoIterator<Item = ty::Binder<Self, ty::ExistentialPredicate<Self>>>; type AllocId: Copy + Debug + Hash + Eq; type Pat: Copy + Debug + Hash + Eq + Debug + Relate<Self>; - type Safety: Safety<Self> + TypeFoldable<Self> + Relate<Self>; - type Abi: Abi<Self> + TypeFoldable<Self> + Relate<Self>; + type Safety: Safety<Self>; + type Abi: Abi<Self>; // Kinds of consts type Const: Const<Self>; @@ -153,6 +148,38 @@ pub trait Interner: type Features: Features<Self>; fn features(self) -> Self::Features; + + fn bound_coroutine_hidden_types( + self, + def_id: Self::DefId, + ) -> impl IntoIterator<Item = ty::EarlyBinder<Self, ty::Binder<Self, Self::Ty>>>; + + fn fn_sig( + self, + def_id: Self::DefId, + ) -> ty::EarlyBinder<Self, ty::Binder<Self, ty::FnSig<Self>>>; + + fn coroutine_movability(self, def_id: Self::DefId) -> Movability; + + fn coroutine_for_closure(self, def_id: Self::DefId) -> Self::DefId; + + fn generics_require_sized_self(self, def_id: Self::DefId) -> bool; + + fn item_bounds( + self, + def_id: Self::DefId, + ) -> ty::EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>>; + + fn super_predicates_of( + self, + def_id: Self::DefId, + ) -> ty::EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>>; + + fn has_target_features(self, def_id: Self::DefId) -> bool; + + fn require_lang_item(self, lang_item: TraitSolverLangItem) -> Self::DefId; + + fn associated_type_def_ids(self, def_id: Self::DefId) -> impl IntoIterator<Item = Self::DefId>; } /// Imagine you have a function `F: FnOnce(&[T]) -> R`, plus an iterator `iter` diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs new file mode 100644 index 00000000000..9a3b324fcd7 --- /dev/null +++ b/compiler/rustc_type_ir/src/lang_items.rs @@ -0,0 +1,8 @@ +/// Lang items used by the new trait solver. This can be mapped to whatever internal +/// representation of `LangItem`s used in the underlying compiler implementation. +pub enum TraitSolverLangItem { + Future, + FutureOutput, + AsyncFnKindHelper, + AsyncFnKindUpvars, +} diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index a76e278cc05..7f0c3df381d 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -32,6 +32,7 @@ pub mod error; pub mod fold; pub mod inherent; pub mod ir_print; +pub mod lang_items; pub mod lift; pub mod relate; pub mod solve; diff --git a/compiler/stable_mir/src/crate_def.rs b/compiler/stable_mir/src/crate_def.rs index 70ca9e6825e..67752a5e629 100644 --- a/compiler/stable_mir/src/crate_def.rs +++ b/compiler/stable_mir/src/crate_def.rs @@ -1,7 +1,7 @@ //! Module that define a common trait for things that represent a crate definition, //! such as, a function, a trait, an enum, and any other definitions. -use crate::ty::Span; +use crate::ty::{GenericArgs, Span, Ty}; use crate::{with, Crate, Symbol}; /// A unique identification number for each item accessible for the current compilation unit. @@ -52,6 +52,23 @@ pub trait CrateDef { } } +/// A trait that can be used to retrieve a definition's type. +/// +/// Note that not every CrateDef has a type `Ty`. They should not implement this trait. +pub trait CrateDefType: CrateDef { + /// Returns the type of this crate item. + fn ty(&self) -> Ty { + with(|cx| cx.def_ty(self.def_id())) + } + + /// Retrieve the type of this definition by instantiating and normalizing it with `args`. + /// + /// This will panic if instantiation fails. + fn ty_with_args(&self, args: &GenericArgs) -> Ty { + with(|cx| cx.def_ty_with_args(self.def_id(), args)) + } +} + macro_rules! crate_def { ( $(#[$attr:meta])* $vis:vis $name:ident $(;)? @@ -67,3 +84,21 @@ macro_rules! crate_def { } }; } + +macro_rules! crate_def_with_ty { + ( $(#[$attr:meta])* + $vis:vis $name:ident $(;)? + ) => { + $(#[$attr])* + #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] + $vis struct $name(pub DefId); + + impl CrateDef for $name { + fn def_id(&self) -> DefId { + self.0 + } + } + + impl CrateDefType for $name {} + }; +} diff --git a/compiler/stable_mir/src/lib.rs b/compiler/stable_mir/src/lib.rs index d9f988935ab..8385856ae53 100644 --- a/compiler/stable_mir/src/lib.rs +++ b/compiler/stable_mir/src/lib.rs @@ -22,8 +22,7 @@ use std::fmt::Debug; use std::io; use crate::compiler_interface::with; -pub use crate::crate_def::CrateDef; -pub use crate::crate_def::DefId; +pub use crate::crate_def::{CrateDef, CrateDefType, DefId}; pub use crate::error::*; use crate::mir::Body; use crate::mir::Mutability; @@ -115,12 +114,15 @@ pub enum CtorKind { pub type Filename = String; -crate_def! { +crate_def_with_ty! { /// Holds information about an item in a crate. pub CrateItem; } impl CrateItem { + /// This will return the body of an item. + /// + /// This will panic if no body is available. pub fn body(&self) -> mir::Body { with(|cx| cx.mir_body(self.0)) } diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs index bcbe87f7303..76f94e6beba 100644 --- a/compiler/stable_mir/src/ty.rs +++ b/compiler/stable_mir/src/ty.rs @@ -3,9 +3,10 @@ use super::{ with, DefId, Error, Symbol, }; use crate::abi::Layout; +use crate::crate_def::{CrateDef, CrateDefType}; use crate::mir::alloc::{read_target_int, read_target_uint, AllocId}; +use crate::mir::mono::StaticDef; use crate::target::MachineInfo; -use crate::{crate_def::CrateDef, mir::mono::StaticDef}; use crate::{Filename, Opaque}; use std::fmt::{self, Debug, Display, Formatter}; use std::ops::Range; @@ -122,7 +123,7 @@ impl TyConst { } /// Creates an interned usize constant. - fn try_from_target_usize(val: u64) -> Result<Self, Error> { + pub fn try_from_target_usize(val: u64) -> Result<Self, Error> { with(|cx| cx.try_new_ty_const_uint(val.into(), UintTy::Usize)) } @@ -504,6 +505,15 @@ impl TyKind { pub fn discriminant_ty(&self) -> Option<Ty> { self.rigid().map(|ty| with(|cx| cx.rigid_ty_discriminant_ty(ty))) } + + /// Deconstruct a function type if this is one. + pub fn fn_def(&self) -> Option<(FnDef, &GenericArgs)> { + if let TyKind::RigidTy(RigidTy::FnDef(def, args)) = self { + Some((*def, args)) + } else { + None + } + } } pub struct TypeAndMut { @@ -629,7 +639,7 @@ impl ForeignModule { } } -crate_def! { +crate_def_with_ty! { /// Hold information about a ForeignItem in a crate. pub ForeignDef; } @@ -647,7 +657,7 @@ pub enum ForeignItemKind { Type(Ty), } -crate_def! { +crate_def_with_ty! { /// Hold information about a function definition in a crate. pub FnDef; } @@ -668,9 +678,15 @@ impl FnDef { pub fn is_intrinsic(&self) -> bool { self.as_intrinsic().is_some() } + + /// Get the function signature for this function definition. + pub fn fn_sig(&self) -> PolyFnSig { + let kind = self.ty().kind(); + kind.fn_sig().unwrap() + } } -crate_def! { +crate_def_with_ty! { pub IntrinsicDef; } @@ -710,7 +726,7 @@ crate_def! { pub BrNamedDef; } -crate_def! { +crate_def_with_ty! { pub AdtDef; } @@ -866,7 +882,7 @@ crate_def! { pub GenericDef; } -crate_def! { +crate_def_with_ty! { pub ConstDef; } diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index cd3534ecb12..6b5054a9f06 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -3043,8 +3043,7 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) { unsafe { ub_checks::assert_unsafe_precondition!( check_language_ub, - "ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \ - and the specified memory ranges do not overlap", + "ptr::copy requires that both pointer arguments are aligned and non-null", ( src: *const () = src as *const (), dst: *mut () = dst as *mut (), diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index cee99e28b5a..733d414d444 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -2080,8 +2080,7 @@ pub trait Iterator { fn try_collect<B>(&mut self) -> ChangeOutputType<Self::Item, B> where Self: Sized, - <Self as Iterator>::Item: Try, - <<Self as Iterator>::Item as Try>::Residual: Residual<B>, + Self::Item: Try<Residual: Residual<B>>, B: FromIterator<<Self::Item as Try>::Output>, { try_process(ByRefSized(self), |i| i.collect()) @@ -2689,12 +2688,13 @@ pub trait Iterator { #[inline] #[unstable(feature = "iterator_try_reduce", reason = "new API", issue = "87053")] #[rustc_do_not_const_check] - fn try_reduce<F, R>(&mut self, f: F) -> ChangeOutputType<R, Option<R::Output>> + fn try_reduce<R>( + &mut self, + f: impl FnMut(Self::Item, Self::Item) -> R, + ) -> ChangeOutputType<R, Option<R::Output>> where Self: Sized, - F: FnMut(Self::Item, Self::Item) -> R, - R: Try<Output = Self::Item>, - R::Residual: Residual<Option<Self::Item>>, + R: Try<Output = Self::Item, Residual: Residual<Option<Self::Item>>>, { let first = match self.next() { Some(i) => i, @@ -2956,12 +2956,13 @@ pub trait Iterator { #[inline] #[unstable(feature = "try_find", reason = "new API", issue = "63178")] #[rustc_do_not_const_check] - fn try_find<F, R>(&mut self, f: F) -> ChangeOutputType<R, Option<Self::Item>> + fn try_find<R>( + &mut self, + f: impl FnMut(&Self::Item) -> R, + ) -> ChangeOutputType<R, Option<Self::Item>> where Self: Sized, - F: FnMut(&Self::Item) -> R, - R: Try<Output = bool>, - R::Residual: Residual<Option<Self::Item>>, + R: Try<Output = bool, Residual: Residual<Option<Self::Item>>>, { #[inline] fn check<I, V, R>( diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 863f0d61df3..5956a08593a 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -1059,7 +1059,7 @@ macro_rules! nonzero_integer_signedness_dependent_methods { unsafe { Self::new_unchecked(self.get().unchecked_add(other)) } } - /// Returns the smallest power of two greater than or equal to n. + /// Returns the smallest power of two greater than or equal to `self`. /// Checks for overflow and returns [`None`] /// if the next power of two is greater than the type’s maximum value. /// As a consequence, the result cannot wrap to zero. diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 1491c27372b..cdbd695008e 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -2830,7 +2830,7 @@ macro_rules! uint_impl { /// /// When return value overflows (i.e., `self > (1 << (N-1))` for type /// `uN`), it panics in debug mode and the return value is wrapped to 0 in - /// release mode (the only situation in which method can return 0). + /// release mode (the only situation in which this method can return 0). /// /// # Examples /// @@ -2851,7 +2851,7 @@ macro_rules! uint_impl { self.one_less_than_next_power_of_two() + 1 } - /// Returns the smallest power of two greater than or equal to `n`. If + /// Returns the smallest power of two greater than or equal to `self`. If /// the next power of two is greater than the type's maximum value, /// `None` is returned, otherwise the power of two is wrapped in `Some`. /// diff --git a/library/core/src/ops/coroutine.rs b/library/core/src/ops/coroutine.rs index 6a6c5db1ab1..753f14c6b85 100644 --- a/library/core/src/ops/coroutine.rs +++ b/library/core/src/ops/coroutine.rs @@ -76,6 +76,7 @@ pub trait Coroutine<R = ()> { /// values which are allowed to be returned each time a coroutine yields. /// For example an iterator-as-a-coroutine would likely have this type as /// `T`, the type being iterated over. + #[cfg_attr(not(bootstrap), lang = "coroutine_yield")] type Yield; /// The type of value this coroutine returns. @@ -84,6 +85,7 @@ pub trait Coroutine<R = ()> { /// `return` statement or implicitly as the last expression of a coroutine /// literal. For example futures would use this as `Result<T, E>` as it /// represents a completed future. + #[cfg_attr(not(bootstrap), lang = "coroutine_return")] type Return; /// Resumes the execution of this coroutine. diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 8ac2a8bb17f..4d6ba2fa3b3 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -654,6 +654,32 @@ impl<T> Option<T> { !self.is_some() } + /// Returns `true` if the option is a [`None`] or the value inside of it matches a predicate. + /// + /// # Examples + /// + /// ``` + /// #![feature(is_none_or)] + /// + /// let x: Option<u32> = Some(2); + /// assert_eq!(x.is_none_or(|x| x > 1), true); + /// + /// let x: Option<u32> = Some(0); + /// assert_eq!(x.is_none_or(|x| x > 1), false); + /// + /// let x: Option<u32> = None; + /// assert_eq!(x.is_none_or(|x| x > 1), true); + /// ``` + #[must_use] + #[inline] + #[unstable(feature = "is_none_or", issue = "126383")] + pub fn is_none_or(self, f: impl FnOnce(T) -> bool) -> bool { + match self { + None => true, + Some(x) => f(x), + } + } + ///////////////////////////////////////////////////////////////////////// // Adapter for working with references ///////////////////////////////////////////////////////////////////////// diff --git a/library/std/src/process.rs b/library/std/src/process.rs index c926c89f7a9..f351dab78dc 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -629,6 +629,25 @@ impl Command { /// .spawn() /// .expect("sh command failed to start"); /// ``` + /// + /// # Caveats + /// + /// [`Command::new`] is only intended to accept the path of the program. If you pass a program + /// path along with arguments like `Command::new("ls -l").spawn()`, it will try to search for + /// `ls -l` literally. The arguments need to be passed separately, such as via [`arg`] or + /// [`args`]. + /// + /// ```no_run + /// use std::process::Command; + /// + /// Command::new("ls") + /// .arg("-l") // arg passed separately + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` + /// + /// [`arg`]: Self::arg + /// [`args`]: Self::args #[stable(feature = "process", since = "1.0.0")] pub fn new<S: AsRef<OsStr>>(program: S) -> Command { Command { inner: imp::Command::new(program.as_ref()) } diff --git a/library/std/src/sys/pal/unix/fs.rs b/library/std/src/sys/pal/unix/fs.rs index 56a0f8e39c4..035c92bc84b 100644 --- a/library/std/src/sys/pal/unix/fs.rs +++ b/library/std/src/sys/pal/unix/fs.rs @@ -22,16 +22,12 @@ use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; #[cfg(any(all(target_os = "linux", target_env = "gnu"), target_vendor = "apple"))] use crate::sys::weak::syscall; -#[cfg(any(target_os = "android", target_os = "macos", target_os = "solaris"))] +#[cfg(target_os = "android")] use crate::sys::weak::weak; use libc::{c_int, mode_t}; -#[cfg(any( - target_os = "solaris", - all(target_os = "linux", target_env = "gnu"), - target_vendor = "apple", -))] +#[cfg(any(all(target_os = "linux", target_env = "gnu"), target_vendor = "apple"))] use libc::c_char; #[cfg(any( all(target_os = "linux", not(target_env = "musl")), @@ -1753,19 +1749,6 @@ pub fn link(original: &Path, link: &Path) -> io::Result<()> { // Android has `linkat` on newer versions, but we happen to know `link` // always has the correct behavior, so it's here as well. cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?; - } else if #[cfg(any(target_os = "macos", target_os = "solaris"))] { - // MacOS (<=10.9) and Solaris 10 lack support for linkat while newer - // versions have it. We want to use linkat if it is available, so we use weak! - // to check. `linkat` is preferable to `link` because it gives us a flag to - // specify how symlinks should be handled. We pass 0 as the flags argument, - // meaning it shouldn't follow symlinks. - weak!(fn linkat(c_int, *const c_char, c_int, *const c_char, c_int) -> c_int); - - if let Some(f) = linkat.get() { - cvt(unsafe { f(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?; - } else { - cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?; - }; } else { // Where we can, use `linkat` instead of `link`; see the comment above // this one for details on why. diff --git a/src/bootstrap/src/core/build_steps/format.rs b/src/bootstrap/src/core/build_steps/format.rs index 2deb9168df2..b96e26dbb3a 100644 --- a/src/bootstrap/src/core/build_steps/format.rs +++ b/src/bootstrap/src/core/build_steps/format.rs @@ -93,7 +93,7 @@ fn get_modified_rs_files(build: &Builder<'_>) -> Result<Option<Vec<String>>, Str return Ok(None); } - get_git_modified_files(&build.config.git_config(), Some(&build.config.src), &vec!["rs"]) + get_git_modified_files(&build.config.git_config(), Some(&build.config.src), &["rs"]) } #[derive(serde_derive::Deserialize)] diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs index 354eb2b6003..9268b335db7 100644 --- a/src/bootstrap/src/core/build_steps/run.rs +++ b/src/bootstrap/src/core/build_steps/run.rs @@ -149,12 +149,14 @@ impl Step for Miri { &[], ); miri.add_rustc_lib_path(builder); - // Forward arguments. miri.arg("--").arg("--target").arg(target.rustc_target_arg()); - miri.args(builder.config.args()); // miri tests need to know about the stage sysroot - miri.env("MIRI_SYSROOT", &miri_sysroot); + miri.arg("--sysroot").arg(miri_sysroot); + + // Forward arguments. This may contain further arguments to the program + // after another --, so this must be at the end. + miri.args(builder.config.args()); let mut miri = Command::from(miri); builder.run(&mut miri); diff --git a/src/doc/rustc/src/check-cfg.md b/src/doc/rustc/src/check-cfg.md index 8e39adaa438..dfc4871b924 100644 --- a/src/doc/rustc/src/check-cfg.md +++ b/src/doc/rustc/src/check-cfg.md @@ -11,8 +11,8 @@ development process. In order to accomplish that goal, `rustc` accepts the `--check-cfg` flag, which specifies whether to check conditions and how to check them. -> **Note:** No implicit expectation is added when using `--cfg`. Users are expected to -pass all expected names and values using the _check cfg specification_. +> **Note:** For interacting with this through Cargo, +see [Cargo Specifics](check-cfg/cargo-specifics.md) page. [^reachable]: `rustc` promises to at least check reachable `#[cfg]`, and while non-reachable `#[cfg]` are not currently checked, they may well be checked in the future without it being a @@ -23,6 +23,9 @@ breaking change. To specify expected names and values, the _check cfg specification_ provides the `cfg(...)` option which enables specifying for an expected config name and it's expected values. +> **Note:** No implicit expectation is added when using `--cfg`. Users are expected to +pass all expected names and values using the _check cfg specification_. + It has this basic form: ```bash diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index 9f33e431274..b4d47cba7c5 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -495,7 +495,7 @@ impl Builder { Some(p) => p, None => return false, }; - pkg.target.get(&c.target).is_some() + pkg.target.contains_key(&c.target) }; extensions.retain(&has_component); components.retain(&has_component); diff --git a/src/tools/build_helper/src/git.rs b/src/tools/build_helper/src/git.rs index a3c857b0268..b4522de6897 100644 --- a/src/tools/build_helper/src/git.rs +++ b/src/tools/build_helper/src/git.rs @@ -21,7 +21,7 @@ fn output_result(cmd: &mut Command) -> Result<String, String> { String::from_utf8(output.stderr).map_err(|err| format!("{err:?}"))? )); } - Ok(String::from_utf8(output.stdout).map_err(|err| format!("{err:?}"))?) + String::from_utf8(output.stdout).map_err(|err| format!("{err:?}")) } /// Finds the remote for rust-lang/rust. @@ -64,18 +64,14 @@ pub fn rev_exists(rev: &str, git_dir: Option<&Path>) -> Result<bool, String> { match output.status.code() { Some(0) => Ok(true), Some(128) => Ok(false), - None => { - return Err(format!( - "git didn't exit properly: {}", - String::from_utf8(output.stderr).map_err(|err| format!("{err:?}"))? - )); - } - Some(code) => { - return Err(format!( - "git command exited with status code: {code}: {}", - String::from_utf8(output.stderr).map_err(|err| format!("{err:?}"))? - )); - } + None => Err(format!( + "git didn't exit properly: {}", + String::from_utf8(output.stderr).map_err(|err| format!("{err:?}"))? + )), + Some(code) => Err(format!( + "git command exited with status code: {code}: {}", + String::from_utf8(output.stderr).map_err(|err| format!("{err:?}"))? + )), } } @@ -96,7 +92,7 @@ pub fn updated_master_branch( } } - Err(format!("Cannot find any suitable upstream master branch")) + Err("Cannot find any suitable upstream master branch".to_owned()) } pub fn get_git_merge_base( @@ -118,7 +114,7 @@ pub fn get_git_merge_base( pub fn get_git_modified_files( config: &GitConfig<'_>, git_dir: Option<&Path>, - extensions: &Vec<&str>, + extensions: &[&str], ) -> Result<Option<Vec<String>>, String> { let merge_base = get_git_merge_base(config, git_dir)?; diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index d5115f70f66..d7bcd7a1968 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6,11 +6,69 @@ document. ## Unreleased / Beta / In Rust Nightly -[93f0a9a9...master](https://github.com/rust-lang/rust-clippy/compare/93f0a9a9...master) +[ca3b3937...master](https://github.com/rust-lang/rust-clippy/compare/ca3b3937...master) + +## Rust 1.79 + +Current stable, released 2024-06-13 + +[View all 102 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-03-08T11%3A13%3A58Z..2024-04-18T15%3A50%3A50Z+base%3Amaster) + +### New Lints + +* Added [`legacy_numeric_constants`] to `style` + [#12312](https://github.com/rust-lang/rust-clippy/pull/12312) +* Added [`missing_transmute_annotations`] to `suspicious` + [#12239](https://github.com/rust-lang/rust-clippy/pull/12239) +* Added [`integer_division_remainder_used`] to `restriction` + [#12451](https://github.com/rust-lang/rust-clippy/pull/12451) +* Added [`duplicated_attributes`] to `suspicious` + [#12378](https://github.com/rust-lang/rust-clippy/pull/12378) +* Added [`manual_unwrap_or_default`] to `suspicious` + [#12440](https://github.com/rust-lang/rust-clippy/pull/12440) +* Added [`zero_repeat_side_effects`] to `suspicious` + [#12449](https://github.com/rust-lang/rust-clippy/pull/12449) +* Added [`const_is_empty`] to `suspicious` + [#12310](https://github.com/rust-lang/rust-clippy/pull/12310) + +### Moves and Deprecations + +* Moved [`box_default`] to `style` (From `perf`) + [#12601](https://github.com/rust-lang/rust-clippy/pull/12601) +* Moved [`manual_clamp`] to `complexity` (From `nursery` now warn-by-default) + [#12543](https://github.com/rust-lang/rust-clippy/pull/12543) +* Moved [`readonly_write_lock`] to `perf` (From `nursery` now warn-by-default) + [#12479](https://github.com/rust-lang/rust-clippy/pull/12479) + +### Enhancements + +* [`module_name_repetitions`]: Added the [`allowed-prefixes`] configuration to allow common prefixes. + [#12573](https://github.com/rust-lang/rust-clippy/pull/12573) +* [`cast_sign_loss`], [`cast_possible_truncation`], [`cast_lossless`]: Are now allowed in macros + [#12631](https://github.com/rust-lang/rust-clippy/pull/12631) +* [`manual_clamp`]: Now only lints on constant min and max values + [#12543](https://github.com/rust-lang/rust-clippy/pull/12543) +* [`assigning_clones`]: Now considers the [`msrv`] configuration + [#12511](https://github.com/rust-lang/rust-clippy/pull/12511) +* [`needless_return`], [`useless_let_if_seq`], [`mut_mut`], [`read_zero_byte_vec`], [`unused_io_amount`], + [`unused_peekable`]: Now respects `#[allow]` attributes on the affected statement instead + [#12446](https://github.com/rust-lang/rust-clippy/pull/12446) + +### False Positive Fixes + +* [`cast_lossless`]: No longer lints when casting to `u128` + [#12496](https://github.com/rust-lang/rust-clippy/pull/12496) +* [`std_instead_of_core`] No longer lints on modules that are only in `std` + [#12447](https://github.com/rust-lang/rust-clippy/pull/12447) + +### ICE Fixes + +* [`needless_return`]: No longer crashes on non-ascii characters + [#12493](https://github.com/rust-lang/rust-clippy/pull/12493) ## Rust 1.78 -Current stable, released 2024-05-02 +Released 2024-05-02 [View all 112 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-01-26T05%3A46%3A23Z..2024-03-07T16%3A25%3A52Z+base%3Amaster) @@ -5474,6 +5532,7 @@ Released 2018-09-13 [`manual_next_back`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_next_back [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive [`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or +[`manual_pattern_char_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_pattern_char_comparison [`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains [`manual_range_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_patterns [`manual_rem_euclid`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid @@ -5567,6 +5626,7 @@ Released 2018-09-13 [`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow [`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference [`needless_borrows_for_generic_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrows_for_generic_args +[`needless_character_iteration`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_character_iteration [`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect [`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue [`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main @@ -5576,6 +5636,7 @@ Released 2018-09-13 [`needless_late_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_late_init [`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes [`needless_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match +[`needless_maybe_sized`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_maybe_sized [`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref [`needless_option_take`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_take [`needless_parens_on_range_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_parens_on_range_literals diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index b48f3ab3919..43788499055 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.80" +version = "0.1.81" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/src/tools/clippy/README.md b/src/tools/clippy/README.md index fa18447090c..ec76a6dfb08 100644 --- a/src/tools/clippy/README.md +++ b/src/tools/clippy/README.md @@ -172,7 +172,7 @@ You can add options to your code to `allow`/`warn`/`deny` Clippy lints: Note: `allow` means to suppress the lint for your code. With `warn` the lint will only emit a warning, while with `deny` the lint will emit an error, when -triggering for your code. An error causes clippy to exit with an error code, so +triggering for your code. An error causes Clippy to exit with an error code, so is useful in scripts like CI/CD. If you do not want to include your lint levels in your code, you can globally @@ -238,7 +238,7 @@ define the `CLIPPY_DISABLE_DOCS_LINKS` environment variable. ### Specifying the minimum supported Rust version Projects that intend to support old versions of Rust can disable lints pertaining to newer features by -specifying the minimum supported Rust version (MSRV) in the clippy configuration file. +specifying the minimum supported Rust version (MSRV) in the Clippy configuration file. ```toml msrv = "1.30.0" diff --git a/src/tools/clippy/book/src/configuration.md b/src/tools/clippy/book/src/configuration.md index ea549e4df4a..b1305443189 100644 --- a/src/tools/clippy/book/src/configuration.md +++ b/src/tools/clippy/book/src/configuration.md @@ -99,7 +99,7 @@ For more details and options, refer to the Cargo documentation. ### Specifying the minimum supported Rust version Projects that intend to support old versions of Rust can disable lints pertaining to newer features by specifying the -minimum supported Rust version (MSRV) in the clippy configuration file. +minimum supported Rust version (MSRV) in the Clippy configuration file. ```toml msrv = "1.30.0" diff --git a/src/tools/clippy/book/src/development/basics.md b/src/tools/clippy/book/src/development/basics.md index f4c109ff119..166b6aab9fb 100644 --- a/src/tools/clippy/book/src/development/basics.md +++ b/src/tools/clippy/book/src/development/basics.md @@ -107,7 +107,7 @@ More about [intellij] command usage and reasons. ## lintcheck -`cargo lintcheck` will build and run clippy on a fixed set of crates and +`cargo lintcheck` will build and run Clippy on a fixed set of crates and generate a log of the results. You can `git diff` the updated log against its previous version and see what impact your lint made on a small set of crates. If you add a new lint, please audit the resulting warnings and make sure there diff --git a/src/tools/clippy/book/src/development/defining_lints.md b/src/tools/clippy/book/src/development/defining_lints.md index 806ed0845f0..ceabb255e2d 100644 --- a/src/tools/clippy/book/src/development/defining_lints.md +++ b/src/tools/clippy/book/src/development/defining_lints.md @@ -163,11 +163,11 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // example code where clippy issues a warning + /// // example code where Clippy issues a warning /// ``` /// Use instead: /// ```rust - /// // example code which does not raise clippy warning + /// // example code which does not raise Clippy warning /// ``` #[clippy::version = "1.70.0"] // <- In which version was this implemented, keep it up to date! pub LINT_NAME, // <- The lint name IN_ALL_CAPS diff --git a/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md b/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md index 285488cec55..92fbf733a8f 100644 --- a/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md +++ b/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md @@ -428,7 +428,7 @@ selection of possible matches is produced by the pattern syntax. In the second stage, the named subpattern references can be used to do additional tests like asserting that a node hasn't been created as part of a macro expansion. -## Implementing clippy lints using patterns +## Implementing Clippy lints using patterns As a "real-world" example, I re-implemented the `collapsible_if` lint using patterns. The code can be found @@ -572,7 +572,7 @@ The pattern syntax and the *PatternTree* are independent of specific syntax tree implementations (rust ast / hir, syn, ...). When looking at the different pattern examples in the previous sections, it can be seen that the patterns don't contain any information specific to a certain syntax tree implementation. -In contrast, clippy lints currently match against ast / hir syntax tree nodes +In contrast, Clippy lints currently match against ast / hir syntax tree nodes and therefore directly depend on their implementation. The connection between the *PatternTree* and specific syntax tree @@ -690,7 +690,7 @@ change, only the `IsMatch` trait implementations need to be adapted and existing lints can remain unchanged. This also means that if the `IsMatch` trait implementations were integrated into the compiler, updating the `IsMatch` implementations would be required for the compiler to compile successfully. This -could reduce the number of times clippy breaks because of changes in the +could reduce the number of times Clippy breaks because of changes in the compiler. Another advantage of the pattern's independence is that converting an `EarlyLintPass` lint into a `LatePassLint` wouldn't require rewriting the whole pattern matching code. In fact, the pattern might work just fine without any @@ -777,7 +777,7 @@ complexity to solve a relatively minor problem. The issue of users not knowing about the *PatternTree* structure could be solved by a tool that, given a rust program, generates a pattern that matches only this -program (similar to the clippy author lint). +program (similar to the Clippy author lint). For some simple cases (like the first example above), it might be possible to successfully mix Rust and pattern syntax. This space could be further explored @@ -789,7 +789,7 @@ The pattern syntax is heavily inspired by regular expressions (repetitions, alternatives, sequences, ...). From what I've seen until now, other linters also implement lints that directly -work on syntax tree data structures, just like clippy does currently. I would +work on syntax tree data structures, just like Clippy does currently. I would therefore consider the pattern syntax to be *new*, but please correct me if I'm wrong. @@ -982,5 +982,5 @@ pattern!{ } ``` -In the future, clippy could use this system to also provide lints for custom +In the future, Clippy could use this system to also provide lints for custom syntaxes like those found in macros. diff --git a/src/tools/clippy/clippy_config/Cargo.toml b/src/tools/clippy/clippy_config/Cargo.toml index 7f7dc9d6cfb..be0b048ac0c 100644 --- a/src/tools/clippy/clippy_config/Cargo.toml +++ b/src/tools/clippy/clippy_config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_config" -version = "0.1.80" +version = "0.1.81" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/src/tools/clippy/clippy_dummy/PUBLISH.md b/src/tools/clippy/clippy_dummy/PUBLISH.md index 8e420ec959a..f0021f1594f 100644 --- a/src/tools/clippy/clippy_dummy/PUBLISH.md +++ b/src/tools/clippy/clippy_dummy/PUBLISH.md @@ -1,5 +1,5 @@ This is a dummy crate to publish to crates.io. It primarily exists to ensure -that folks trying to install clippy from crates.io get redirected to the +that folks trying to install Clippy from crates.io get redirected to the `rustup` technique. Before publishing, be sure to rename `clippy_dummy` to `clippy` in `Cargo.toml`, diff --git a/src/tools/clippy/clippy_dummy/crates-readme.md b/src/tools/clippy/clippy_dummy/crates-readme.md index 0decae8b910..a8ec0a1c36c 100644 --- a/src/tools/clippy/clippy_dummy/crates-readme.md +++ b/src/tools/clippy/clippy_dummy/crates-readme.md @@ -1,9 +1,9 @@ -Installing clippy via crates.io is deprecated. Please use the following: +Installing Clippy via crates.io is deprecated. Please use the following: ```terminal rustup component add clippy ``` -on a Rust version 1.29 or later. You may need to run `rustup self update` if it complains about a missing clippy binary. +on a Rust version 1.29 or later. You may need to run `rustup self update` if it complains about a missing Clippy binary. See [the homepage](https://github.com/rust-lang/rust-clippy/#clippy) for more information diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index 5e3a119337c..5708ffba08f 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.80" +version = "0.1.81" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/src/tools/clippy/clippy_lints/src/attrs/maybe_misused_cfg.rs b/src/tools/clippy/clippy_lints/src/attrs/maybe_misused_cfg.rs deleted file mode 100644 index e6b2e835be8..00000000000 --- a/src/tools/clippy/clippy_lints/src/attrs/maybe_misused_cfg.rs +++ /dev/null @@ -1,51 +0,0 @@ -use super::{Attribute, MAYBE_MISUSED_CFG}; -use clippy_utils::diagnostics::span_lint_and_sugg; -use rustc_ast::{MetaItemKind, NestedMetaItem}; -use rustc_errors::Applicability; -use rustc_lint::EarlyContext; -use rustc_span::sym; - -pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute) { - if attr.has_name(sym::cfg) - && let Some(items) = attr.meta_item_list() - { - check_nested_misused_cfg(cx, &items); - } -} - -fn check_nested_misused_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) { - for item in items { - if let NestedMetaItem::MetaItem(meta) = item { - if let Some(ident) = meta.ident() - && ident.name.as_str() == "features" - && let Some(val) = meta.value_str() - { - span_lint_and_sugg( - cx, - MAYBE_MISUSED_CFG, - meta.span, - "'feature' may be misspelled as 'features'", - "did you mean", - format!("feature = \"{val}\""), - Applicability::MaybeIncorrect, - ); - } - if let MetaItemKind::List(list) = &meta.kind { - check_nested_misused_cfg(cx, list); - // If this is not a list, then we check for `cfg(test)`. - } else if let Some(ident) = meta.ident() - && matches!(ident.name.as_str(), "tests" | "Test") - { - span_lint_and_sugg( - cx, - MAYBE_MISUSED_CFG, - meta.span, - format!("'test' may be misspelled as '{}'", ident.name.as_str()), - "did you mean", - "test".to_string(), - Applicability::MaybeIncorrect, - ); - } - } - } -} diff --git a/src/tools/clippy/clippy_lints/src/attrs/mismatched_target_os.rs b/src/tools/clippy/clippy_lints/src/attrs/mismatched_target_os.rs deleted file mode 100644 index b1cc0a763c5..00000000000 --- a/src/tools/clippy/clippy_lints/src/attrs/mismatched_target_os.rs +++ /dev/null @@ -1,90 +0,0 @@ -use super::{Attribute, MISMATCHED_TARGET_OS}; -use clippy_utils::diagnostics::span_lint_and_then; -use rustc_ast::{MetaItemKind, NestedMetaItem}; -use rustc_errors::Applicability; -use rustc_lint::EarlyContext; -use rustc_span::{sym, Span}; - -static UNIX_SYSTEMS: &[&str] = &[ - "android", - "dragonfly", - "emscripten", - "freebsd", - "fuchsia", - "haiku", - "illumos", - "ios", - "l4re", - "linux", - "macos", - "netbsd", - "openbsd", - "redox", - "solaris", - "vxworks", -]; - -// NOTE: windows is excluded from the list because it's also a valid target family. -static NON_UNIX_SYSTEMS: &[&str] = &["hermit", "none", "wasi"]; - -pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute) { - fn find_os(name: &str) -> Option<&'static str> { - UNIX_SYSTEMS - .iter() - .chain(NON_UNIX_SYSTEMS.iter()) - .find(|&&os| os == name) - .copied() - } - - fn is_unix(name: &str) -> bool { - UNIX_SYSTEMS.iter().any(|&os| os == name) - } - - fn find_mismatched_target_os(items: &[NestedMetaItem]) -> Vec<(&str, Span)> { - let mut mismatched = Vec::new(); - - for item in items { - if let NestedMetaItem::MetaItem(meta) = item { - match &meta.kind { - MetaItemKind::List(list) => { - mismatched.extend(find_mismatched_target_os(list)); - }, - MetaItemKind::Word => { - if let Some(ident) = meta.ident() - && let Some(os) = find_os(ident.name.as_str()) - { - mismatched.push((os, ident.span)); - } - }, - MetaItemKind::NameValue(..) => {}, - } - } - } - - mismatched - } - - if attr.has_name(sym::cfg) - && let Some(list) = attr.meta_item_list() - && let mismatched = find_mismatched_target_os(&list) - && !mismatched.is_empty() - { - let mess = "operating system used in target family position"; - - span_lint_and_then(cx, MISMATCHED_TARGET_OS, attr.span, mess, |diag| { - // Avoid showing the unix suggestion multiple times in case - // we have more than one mismatch for unix-like systems - let mut unix_suggested = false; - - for (os, span) in mismatched { - let sugg = format!("target_os = \"{os}\""); - diag.span_suggestion(span, "try", sugg, Applicability::MaybeIncorrect); - - if !unix_suggested && is_unix(os) { - diag.help("did you mean `unix`?"); - unix_suggested = true; - } - } - }); - } -} diff --git a/src/tools/clippy/clippy_lints/src/attrs/mod.rs b/src/tools/clippy/clippy_lints/src/attrs/mod.rs index a24bd5ed44b..e4c98a32fd6 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mod.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mod.rs @@ -7,8 +7,6 @@ mod deprecated_semver; mod duplicated_attributes; mod empty_line_after; mod inline_always; -mod maybe_misused_cfg; -mod mismatched_target_os; mod mixed_attributes_style; mod non_minimal_cfg; mod should_panic_without_expect; @@ -272,39 +270,6 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for cfg attributes having operating systems used in target family position. - /// - /// ### Why is this bad? - /// The configuration option will not be recognised and the related item will not be included - /// by the conditional compilation engine. - /// - /// ### Example - /// ```no_run - /// #[cfg(linux)] - /// fn conditional() { } - /// ``` - /// - /// Use instead: - /// ```no_run - /// # mod hidden { - /// #[cfg(target_os = "linux")] - /// fn conditional() { } - /// # } - /// - /// // or - /// - /// #[cfg(unix)] - /// fn conditional() { } - /// ``` - /// Check the [Rust Reference](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os) for more details. - #[clippy::version = "1.45.0"] - pub MISMATCHED_TARGET_OS, - correctness, - "usage of `cfg(operating_system)` instead of `cfg(target_os = \"operating_system\")`" -} - -declare_clippy_lint! { - /// ### What it does /// Checks for attributes that allow lints without a reason. /// /// (This requires the `lint_reasons` feature) @@ -393,38 +358,6 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for `#[cfg(features = "...")]` and suggests to replace it with - /// `#[cfg(feature = "...")]`. - /// - /// It also checks if `cfg(test)` was misspelled. - /// - /// ### Why is this bad? - /// Misspelling `feature` as `features` or `test` as `tests` can be sometimes hard to spot. It - /// may cause conditional compilation not work quietly. - /// - /// ### Example - /// ```no_run - /// #[cfg(features = "some-feature")] - /// fn conditional() { } - /// #[cfg(tests)] - /// mod tests { } - /// ``` - /// - /// Use instead: - /// ```no_run - /// #[cfg(feature = "some-feature")] - /// fn conditional() { } - /// #[cfg(test)] - /// mod tests { } - /// ``` - #[clippy::version = "1.69.0"] - pub MAYBE_MISUSED_CFG, - suspicious, - "prevent from misusing the wrong attr name" -} - -declare_clippy_lint! { - /// ### What it does /// Checks for `#[cfg_attr(feature = "cargo-clippy", ...)]` and for /// `#[cfg(feature = "cargo-clippy")]` and suggests to replace it with /// `#[cfg_attr(clippy, ...)]` or `#[cfg(clippy)]`. @@ -530,7 +463,7 @@ declare_clippy_lint! { /// #[allow(dead_code)] /// fn foo() {} /// ``` - #[clippy::version = "1.78.0"] + #[clippy::version = "1.79.0"] pub DUPLICATED_ATTRIBUTES, suspicious, "duplicated attribute" @@ -612,11 +545,9 @@ pub struct EarlyAttributes { impl_lint_pass!(EarlyAttributes => [ DEPRECATED_CFG_ATTR, - MISMATCHED_TARGET_OS, EMPTY_LINE_AFTER_OUTER_ATTR, EMPTY_LINE_AFTER_DOC_COMMENTS, NON_MINIMAL_CFG, - MAYBE_MISUSED_CFG, DEPRECATED_CLIPPY_CFG_ATTR, UNNECESSARY_CLIPPY_CFG, ]); @@ -629,9 +560,7 @@ impl EarlyLintPass for EarlyAttributes { fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { deprecated_cfg_attr::check(cx, attr, &self.msrv); deprecated_cfg_attr::check_clippy(cx, attr); - mismatched_target_os::check(cx, attr); non_minimal_cfg::check(cx, attr); - maybe_misused_cfg::check(cx, attr); } extract_msrv_attr!(EarlyContext); diff --git a/src/tools/clippy/clippy_lints/src/blocks_in_conditions.rs b/src/tools/clippy/clippy_lints/src/blocks_in_conditions.rs index 171f3031860..eb05dc96cde 100644 --- a/src/tools/clippy/clippy_lints/src/blocks_in_conditions.rs +++ b/src/tools/clippy/clippy_lints/src/blocks_in_conditions.rs @@ -1,15 +1,11 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_block_with_applicability; -use clippy_utils::ty::implements_trait; -use clippy_utils::visitors::{for_each_expr, Descend}; -use clippy_utils::{get_parent_expr, higher, is_from_proc_macro}; -use core::ops::ControlFlow; +use clippy_utils::{higher, is_from_proc_macro}; use rustc_errors::Applicability; use rustc_hir::{BlockCheckMode, Expr, ExprKind, MatchSource}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::declare_lint_pass; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -124,30 +120,6 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInConditions { ); } } - } else { - let _: Option<!> = for_each_expr(cond, |e| { - if let ExprKind::Closure(closure) = e.kind { - // do not lint if the closure is called using an iterator (see #1141) - if let Some(parent) = get_parent_expr(cx, e) - && let ExprKind::MethodCall(_, self_arg, _, _) = &parent.kind - && let caller = cx.typeck_results().expr_ty(self_arg) - && let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator) - && implements_trait(cx, caller, iter_id, &[]) - { - return ControlFlow::Continue(Descend::No); - } - - let body = cx.tcx.hir().body(closure.body); - let ex = &body.value; - if let ExprKind::Block(block, _) = ex.kind { - if !body.value.span.from_expansion() && !block.stmts.is_empty() { - span_lint(cx, BLOCKS_IN_CONDITIONS, ex.span, complex_block_message.clone()); - return ControlFlow::Continue(Descend::No); - } - } - } - ControlFlow::Continue(Descend::Yes) - }); } } } diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs index b6341b3fe8e..a1c6c0b608f 100644 --- a/src/tools/clippy/clippy_lints/src/booleans.rs +++ b/src/tools/clippy/clippy_lints/src/booleans.rs @@ -174,6 +174,25 @@ fn check_inverted_bool_in_condition( ); } +fn check_simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) { + if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind + && !expr.span.from_expansion() + && !inner.span.from_expansion() + && let Some(suggestion) = simplify_not(cx, inner) + && cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow + { + span_lint_and_sugg( + cx, + NONMINIMAL_BOOL, + expr.span, + "this boolean expression can be simplified", + "try", + suggestion, + Applicability::MachineApplicable, + ); + } +} + struct NonminimalBoolVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, } @@ -232,6 +251,11 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> { _ => (), } } + + if self.cx.typeck_results().expr_ty(e).is_never() { + return Err("contains never type".to_owned()); + } + for (n, expr) in self.terminals.iter().enumerate() { if eq_expr_value(self.cx, e, expr) { #[expect(clippy::cast_possible_truncation)] @@ -542,8 +566,7 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> { } }; if improvements.is_empty() { - let mut visitor = NotSimplificationVisitor { cx: self.cx }; - visitor.visit_expr(e); + check_simplify_not(self.cx, e); } else { nonminimal_bool_lint( improvements @@ -586,30 +609,3 @@ fn implements_ord(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { .get_diagnostic_item(sym::Ord) .map_or(false, |id| implements_trait(cx, ty, id, &[])) } - -struct NotSimplificationVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, -} - -impl<'a, 'tcx> Visitor<'tcx> for NotSimplificationVisitor<'a, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind - && !expr.span.from_expansion() - && !inner.span.from_expansion() - && let Some(suggestion) = simplify_not(self.cx, inner) - && self.cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow - { - span_lint_and_sugg( - self.cx, - NONMINIMAL_BOOL, - expr.span, - "this boolean expression can be simplified", - "try", - suggestion, - Applicability::MachineApplicable, - ); - } - - walk_expr(self, expr); - } -} diff --git a/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs b/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs index 0d9eaac882f..e924542fea2 100644 --- a/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs +++ b/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs @@ -71,12 +71,6 @@ struct CargoToml { workspace: Workspace, } -#[derive(Default, Debug)] -struct LintsAndGroups { - lints: Vec<Spanned<String>>, - groups: Vec<(Spanned<String>, Spanned<LintConfig>)>, -} - fn toml_span(range: Range<usize>, file: &SourceFile) -> Span { Span::new( file.start_pos + BytePos::from_usize(range.start), @@ -86,27 +80,28 @@ fn toml_span(range: Range<usize>, file: &SourceFile) -> Span { ) } -fn check_table(cx: &LateContext<'_>, table: LintTable, groups: &FxHashSet<&str>, file: &SourceFile) { - let mut by_priority = BTreeMap::<_, LintsAndGroups>::new(); +fn check_table(cx: &LateContext<'_>, table: LintTable, known_groups: &FxHashSet<&str>, file: &SourceFile) { + let mut lints = Vec::new(); + let mut groups = Vec::new(); for (name, config) in table { - let lints_and_groups = by_priority.entry(config.as_ref().priority()).or_default(); - if groups.contains(name.get_ref().as_str()) { - lints_and_groups.groups.push((name, config)); + if name.get_ref() == "warnings" { + continue; + } + + if known_groups.contains(name.get_ref().as_str()) { + groups.push((name, config)); } else { - lints_and_groups.lints.push(name); + lints.push((name, config.into_inner())); } } - let low_priority = by_priority - .iter() - .find(|(_, lints_and_groups)| !lints_and_groups.lints.is_empty()) - .map_or(-1, |(&lowest_lint_priority, _)| lowest_lint_priority - 1); - for (priority, LintsAndGroups { lints, groups }) in by_priority { - let Some(last_lint_alphabetically) = lints.last() else { - continue; - }; - - for (group, config) in groups { + for (group, group_config) in groups { + let priority = group_config.get_ref().priority(); + let level = group_config.get_ref().level(); + if let Some((conflict, _)) = lints + .iter() + .rfind(|(_, lint_config)| lint_config.priority() == priority && lint_config.level() != level) + { span_lint_and_then( cx, LINT_GROUPS_PRIORITY, @@ -116,22 +111,23 @@ fn check_table(cx: &LateContext<'_>, table: LintTable, groups: &FxHashSet<&str>, group.as_ref() ), |diag| { - let config_span = toml_span(config.span(), file); - if config.as_ref().is_implicit() { + let config_span = toml_span(group_config.span(), file); + + if group_config.as_ref().is_implicit() { diag.span_label(config_span, "has an implicit priority of 0"); } - // add the label to next lint after this group that has the same priority - let lint = lints - .iter() - .filter(|lint| lint.span().start > group.span().start) - .min_by_key(|lint| lint.span().start) - .unwrap_or(last_lint_alphabetically); - diag.span_label(toml_span(lint.span(), file), "has the same priority as this lint"); + diag.span_label(toml_span(conflict.span(), file), "has the same priority as this lint"); diag.note("the order of the lints in the table is ignored by Cargo"); + let mut suggestion = String::new(); + let low_priority = lints + .iter() + .map(|(_, config)| config.priority().saturating_sub(1)) + .min() + .unwrap_or(-1); Serialize::serialize( &LintConfigTable { - level: config.as_ref().level().into(), + level: level.into(), priority: Some(low_priority), }, toml::ser::ValueSerializer::new(&mut suggestion), diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs index 864489ee3fc..8bbd41b0db1 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs @@ -3,7 +3,7 @@ use std::ops::ControlFlow; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::visitors::{for_each_expr, Descend}; +use clippy_utils::visitors::{for_each_expr_without_closures, Descend}; use clippy_utils::{method_chain_args, sext}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; @@ -266,7 +266,7 @@ fn expr_add_sign(cx: &LateContext<'_>, expr: &Expr<'_>) -> Sign { fn exprs_with_muldiv_binop_peeled<'e>(expr: &'e Expr<'_>) -> Vec<&'e Expr<'e>> { let mut res = vec![]; - for_each_expr(expr, |sub_expr| -> ControlFlow<Infallible, Descend> { + for_each_expr_without_closures(expr, |sub_expr| -> ControlFlow<Infallible, Descend> { // We don't check for mul/div/rem methods here, but we could. if let ExprKind::Binary(op, lhs, _rhs) = sub_expr.kind { if matches!(op.node, BinOpKind::Mul | BinOpKind::Div) { @@ -315,7 +315,7 @@ fn exprs_with_muldiv_binop_peeled<'e>(expr: &'e Expr<'_>) -> Vec<&'e Expr<'e>> { fn exprs_with_add_binop_peeled<'e>(expr: &'e Expr<'_>) -> Vec<&'e Expr<'e>> { let mut res = vec![]; - for_each_expr(expr, |sub_expr| -> ControlFlow<Infallible, Descend> { + for_each_expr_without_closures(expr, |sub_expr| -> ControlFlow<Infallible, Descend> { // We don't check for add methods here, but we could. if let ExprKind::Binary(op, _lhs, _rhs) = sub_expr.kind { if matches!(op.node, BinOpKind::Add) { diff --git a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs index a7f7bf7854e..fb0b0cba6a6 100644 --- a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs +++ b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::numeric_literal::NumericLiteral; use clippy_utils::source::snippet_opt; -use clippy_utils::visitors::{for_each_expr, Visitable}; +use clippy_utils::visitors::{for_each_expr_without_closures, Visitable}; use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local}; use rustc_ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; @@ -245,7 +245,7 @@ fn fp_ty_mantissa_nbits(typ: Ty<'_>) -> u32 { /// TODO: Maybe we should move this to `clippy_utils` so others won't need to go down this dark, /// dark path reimplementing this (or something similar). fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx>, cast_from: Ty<'tcx>) -> bool { - for_each_expr(expr, |expr| { + for_each_expr_without_closures(expr, |expr| { // Calls are a `Path`, and usage of locals are a `Path`. So, this checks // - call() as i32 // - local as i32 diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs index ee1bb63b50d..e41abf42234 100644 --- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::visitors::for_each_expr; +use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{get_async_fn_body, is_async_fn, LimitStack}; use core::ops::ControlFlow; use rustc_ast::ast::Attribute; @@ -65,7 +65,7 @@ impl CognitiveComplexity { let mut cc = 1u64; let mut returns = 0u64; - let _: Option<!> = for_each_expr(expr, |e| { + let _: Option<!> = for_each_expr_without_closures(expr, |e| { match e.kind { ExprKind::If(_, _, _) => { cc += 1; diff --git a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs index 70856b80881..28d9f68d504 100644 --- a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs +++ b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; -use clippy_utils::visitors::{for_each_expr_with_closures, Visitable}; +use clippy_utils::visitors::{for_each_expr, Visitable}; use clippy_utils::{get_enclosing_block, path_to_local_id}; use core::ops::ControlFlow; use rustc_hir::{Body, ExprKind, HirId, LangItem, LetStmt, Node, PatKind}; @@ -82,7 +82,7 @@ fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirI let mut has_read_access = false; // Inspect all expressions and sub-expressions in the block. - for_each_expr_with_closures(cx, block, |expr| { + for_each_expr(cx, block, |expr| { // Ignore expressions that are not simply `id`. if !path_to_local_id(expr, id) { return ControlFlow::Continue(()); diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs index ccf1d9d6f8c..480df675d75 100644 --- a/src/tools/clippy/clippy_lints/src/copies.rs +++ b/src/tools/clippy/clippy_lints/src/copies.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then}; use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt}; use clippy_utils::ty::{needs_ordered_drop, InteriorMut}; -use clippy_utils::visitors::for_each_expr; +use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{ capture_local_usage, eq_expr_value, find_binding_init, get_enclosing_block, hash_expr, hash_stmt, if_sequence, is_else_clause, is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq, @@ -362,7 +362,7 @@ fn eq_binding_names(s: &Stmt<'_>, names: &[(HirId, Symbol)]) -> bool { /// Checks if the statement modifies or moves any of the given locals. fn modifies_any_local<'tcx>(cx: &LateContext<'tcx>, s: &'tcx Stmt<'_>, locals: &HirIdSet) -> bool { - for_each_expr(s, |e| { + for_each_expr_without_closures(s, |e| { if let Some(id) = path_to_local(e) && locals.contains(&id) && !capture_local_usage(cx, e).is_imm_ref() @@ -413,7 +413,7 @@ fn scan_block_for_eq<'tcx>( let mut cond_locals = HirIdSet::default(); for &cond in conds { - let _: Option<!> = for_each_expr(cond, |e| { + let _: Option<!> = for_each_expr_without_closures(cond, |e| { if let Some(id) = path_to_local(e) { cond_locals.insert(id); } diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index df2ef465700..7e43a99e9f2 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -58,8 +58,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::attrs::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO, crate::attrs::EMPTY_LINE_AFTER_OUTER_ATTR_INFO, crate::attrs::INLINE_ALWAYS_INFO, - crate::attrs::MAYBE_MISUSED_CFG_INFO, - crate::attrs::MISMATCHED_TARGET_OS_INFO, crate::attrs::MIXED_ATTRIBUTES_STYLE_INFO, crate::attrs::NON_MINIMAL_CFG_INFO, crate::attrs::SHOULD_PANIC_WITHOUT_EXPECT_INFO, @@ -419,6 +417,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::MAP_UNWRAP_OR_INFO, crate::methods::MUT_MUTEX_LOCK_INFO, crate::methods::NAIVE_BYTECOUNT_INFO, + crate::methods::NEEDLESS_CHARACTER_ITERATION_INFO, crate::methods::NEEDLESS_COLLECT_INFO, crate::methods::NEEDLESS_OPTION_AS_DEREF_INFO, crate::methods::NEEDLESS_OPTION_TAKE_INFO, @@ -449,7 +448,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::SEEK_TO_START_INSTEAD_OF_REWIND_INFO, crate::methods::SHOULD_IMPLEMENT_TRAIT_INFO, crate::methods::SINGLE_CHAR_ADD_STR_INFO, - crate::methods::SINGLE_CHAR_PATTERN_INFO, crate::methods::SKIP_WHILE_NEXT_INFO, crate::methods::STABLE_SORT_PRIMITIVE_INFO, crate::methods::STRING_EXTEND_CHARS_INFO, @@ -531,6 +529,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::needless_for_each::NEEDLESS_FOR_EACH_INFO, crate::needless_if::NEEDLESS_IF_INFO, crate::needless_late_init::NEEDLESS_LATE_INIT_INFO, + crate::needless_maybe_sized::NEEDLESS_MAYBE_SIZED_INFO, crate::needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS_INFO, crate::needless_pass_by_ref_mut::NEEDLESS_PASS_BY_REF_MUT_INFO, crate::needless_pass_by_value::NEEDLESS_PASS_BY_VALUE_INFO, @@ -656,6 +655,8 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::std_instead_of_core::ALLOC_INSTEAD_OF_CORE_INFO, crate::std_instead_of_core::STD_INSTEAD_OF_ALLOC_INFO, crate::std_instead_of_core::STD_INSTEAD_OF_CORE_INFO, + crate::string_patterns::MANUAL_PATTERN_CHAR_COMPARISON_INFO, + crate::string_patterns::SINGLE_CHAR_PATTERN_INFO, crate::strings::STRING_ADD_INFO, crate::strings::STRING_ADD_ASSIGN_INFO, crate::strings::STRING_FROM_UTF8_AS_BYTES_INFO, diff --git a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs index 9aa5af3190f..a0900f46f6a 100644 --- a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs +++ b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs @@ -215,3 +215,29 @@ declare_deprecated_lint! { pub WRONG_PUB_SELF_CONVENTION, "set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items" } + +declare_deprecated_lint! { + /// ### What it does + /// Nothing. This lint has been deprecated. + /// + /// ### Deprecation reason + /// This lint has been superseded by rustc's own [`unexpected_cfgs`] lint that is able to detect the `#[cfg(features)]` and `#[cfg(tests)]` typos. + /// + /// [`unexpected_cfgs`]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#unexpected-cfgs + #[clippy::version = "1.80.0"] + pub MAYBE_MISUSED_CFG, + "this lint has been replaced by `unexpected_cfgs`" +} + +declare_deprecated_lint! { + /// ### What it does + /// Nothing. This lint has been deprecated. + /// + /// ### Deprecation reason + /// This lint has been superseded by rustc's own [`unexpected_cfgs`] lint that is able to detect invalid `#[cfg(linux)]` attributes. + /// + /// [`unexpected_cfgs`]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#unexpected-cfgs + #[clippy::version = "1.80.0"] + pub MISMATCHED_TARGET_OS, + "this lint has been replaced by `unexpected_cfgs`" +} diff --git a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs index 010fab803d9..e3d74840726 100644 --- a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs +++ b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs @@ -37,7 +37,7 @@ pub fn check( cx, MISSING_SAFETY_DOC, span, - "unsafe function's docs miss `# Safety` section", + "unsafe function's docs are missing a `# Safety` section", ), (true, Safety::Safe) => span_lint( cx, diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index 48c4c4206fe..42ec2c00823 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -9,8 +9,8 @@ use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, Param, PatKind, QPath, Saf use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{ - self, Binder, ClosureArgs, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, RegionKind, Ty, - TypeVisitableExt, TypeckResults, TyCtxt, + self, Binder, ClosureArgs, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, RegionKind, Ty, TyCtxt, + TypeVisitableExt, TypeckResults, }; use rustc_session::declare_lint_pass; use rustc_span::symbol::sym; @@ -123,7 +123,8 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { ExprKind::Path(QPath::Resolved(..) | QPath::TypeRelative(..)) ) => { - let callee_ty = typeck.expr_ty(callee).peel_refs(); + let callee_ty_raw = typeck.expr_ty(callee); + let callee_ty = callee_ty_raw.peel_refs(); if matches!(type_diagnostic_name(cx, callee_ty), Some(sym::Arc | sym::Rc)) || !check_inputs(typeck, body.params, None, args) { @@ -170,15 +171,25 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { { span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| { if let Some(mut snippet) = snippet_opt(cx, callee.span) { - if let Ok((ClosureKind::FnMut, _)) = cx.tcx.infer_ctxt().build().type_implements_fn_trait( - cx.param_env, - Binder::bind_with_vars(callee_ty_adjusted, List::empty()), - ty::PredicatePolarity::Positive, - ) && path_to_local(callee).map_or(false, |l| { + if path_to_local(callee).map_or(false, |l| { + // FIXME: Do we really need this `local_used_in` check? + // Isn't it checking something like... `callee(callee)`? + // If somehow this check is needed, add some test for it, + // 'cuz currently nothing changes after deleting this check. local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr) }) { - // Mutable closure is used after current expr; we cannot consume it. - snippet = format!("&mut {snippet}"); + match cx.tcx.infer_ctxt().build().type_implements_fn_trait( + cx.param_env, + Binder::bind_with_vars(callee_ty_adjusted, List::empty()), + ty::PredicatePolarity::Positive, + ) { + // Mutable closure is used after current expr; we cannot consume it. + Ok((ClosureKind::FnMut, _)) => snippet = format!("&mut {snippet}"), + Ok((ClosureKind::Fn, _)) if !callee_ty_raw.is_ref() => { + snippet = format!("&{snippet}"); + }, + _ => (), + } } diag.span_suggestion( expr.span, diff --git a/src/tools/clippy/clippy_lints/src/float_literal.rs b/src/tools/clippy/clippy_lints/src/float_literal.rs index 4ec9bd757ff..4d301daabe4 100644 --- a/src/tools/clippy/clippy_lints/src/float_literal.rs +++ b/src/tools/clippy/clippy_lints/src/float_literal.rs @@ -103,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral { return; } - if is_whole && !sym_str.contains(|c| c == 'e' || c == 'E') { + if is_whole && !sym_str.contains(['e', 'E']) { // Normalize the literal by stripping the fractional portion if sym_str.split('.').next().unwrap() != float_str { // If the type suffix is missing the suggestion would be diff --git a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs index 46d47e217b0..68bdf88d0a7 100644 --- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs @@ -2,7 +2,8 @@ use clippy_utils::consts::Constant::{Int, F32, F64}; use clippy_utils::consts::{constant, constant_simple, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::{ - eq_expr_value, get_parent_expr, higher, in_constant, is_no_std_crate, numeric_literal, peel_blocks, sugg, + eq_expr_value, get_parent_expr, higher, in_constant, is_inherent_method_call, is_no_std_crate, numeric_literal, + peel_blocks, sugg, }; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp}; @@ -759,7 +760,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { if let ExprKind::MethodCall(path, receiver, args, _) = &expr.kind { let recv_ty = cx.typeck_results().expr_ty(receiver); - if recv_ty.is_floating_point() && !is_no_std_crate(cx) { + if recv_ty.is_floating_point() && !is_no_std_crate(cx) && is_inherent_method_call(cx, expr) { match path.ident.name.as_str() { "ln" => check_ln1p(cx, expr, receiver), "log" => check_log_base(cx, expr, receiver, args), diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs index 86115807aa4..99def199af0 100644 --- a/src/tools/clippy/clippy_lints/src/format_args.rs +++ b/src/tools/clippy/clippy_lints/src/format_args.rs @@ -424,14 +424,14 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> { count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter()) && implements_trait(cx, target, display_trait_id, &[]) && let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait() - && let Some(receiver_snippet) = snippet_opt(cx, receiver.span) + && let Some(receiver_snippet) = snippet_opt(cx, receiver.span.source_callsite()) { let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]); if n_needed_derefs == 0 && !needs_ref { span_lint_and_sugg( cx, TO_STRING_IN_FORMAT_ARGS, - to_string_span.with_lo(receiver.span.hi()), + to_string_span.with_lo(receiver.span.source_callsite().hi()), format!("`to_string` applied to a type that implements `Display` in `{name}!` args"), "remove this", String::new(), diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs index e7ec2b3151e..cce8617821e 100644 --- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs +++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs @@ -14,7 +14,7 @@ use clippy_utils::attrs::is_proc_macro; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_must_use_ty; -use clippy_utils::visitors::for_each_expr; +use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{return_ty, trait_ref_of_method}; use core::ops::ControlFlow; @@ -226,7 +226,7 @@ fn is_mutated_static(e: &hir::Expr<'_>) -> bool { } fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bool { - for_each_expr(body.value, |e| { + for_each_expr_without_closures(body.value, |e| { use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall}; match e.kind { diff --git a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index b44a5f20ef6..1aeefe73cf6 100644 --- a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -5,7 +5,7 @@ use rustc_span::def_id::LocalDefId; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::type_is_unsafe_function; -use clippy_utils::visitors::for_each_expr_with_closures; +use clippy_utils::visitors::for_each_expr; use clippy_utils::{iter_input_pats, path_to_local}; use core::ops::ControlFlow; @@ -49,7 +49,7 @@ fn check_raw_ptr<'tcx>( if !raw_ptrs.is_empty() { let typeck = cx.tcx.typeck_body(body.id()); - let _: Option<!> = for_each_expr_with_closures(cx, body.value, |e| { + let _: Option<!> = for_each_expr(cx, body.value, |e| { match e.kind { hir::ExprKind::Call(f, args) if type_is_unsafe_function(cx, typeck.expr_ty(f)) => { for arg in args { diff --git a/src/tools/clippy/clippy_lints/src/implicit_return.rs b/src/tools/clippy/clippy_lints/src/implicit_return.rs index cc342007ec6..2f543781c44 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_return.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_return.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context}; -use clippy_utils::visitors::for_each_expr; +use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{get_async_fn_body, is_async_fn}; use core::ops::ControlFlow; use rustc_errors::Applicability; @@ -153,7 +153,7 @@ fn lint_implicit_returns( ExprKind::Loop(block, ..) => { let mut add_return = false; - let _: Option<!> = for_each_expr(block, |e| { + let _: Option<!> = for_each_expr_without_closures(block, |e| { if let ExprKind::Break(dest, sub_expr) = e.kind { if dest.target_id.ok() == Some(expr.hir_id) { if call_site_span.is_none() && e.span.ctxt() == ctxt { diff --git a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs index 2b389d4f9b1..170ecf896b4 100644 --- a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs +++ b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs @@ -3,8 +3,8 @@ use clippy_utils::source::snippet; use rustc_errors::{Applicability, SuggestionStyle}; use rustc_hir::def_id::DefId; use rustc_hir::{ - GenericArg, GenericBound, GenericBounds, ItemKind, PredicateOrigin, TraitBoundModifier, TyKind, AssocItemConstraint, - WherePredicate, + AssocItemConstraint, GenericArg, GenericBound, GenericBounds, ItemKind, PredicateOrigin, TraitBoundModifier, + TyKind, WherePredicate, }; use rustc_hir_analysis::lower_ty; use rustc_lint::{LateContext, LateLintPass}; @@ -83,8 +83,8 @@ fn emit_lint( let mut sugg = vec![(implied_span_extended, String::new())]; - // We also might need to include associated item constraints that were specified in the implied bound, - // but omitted in the implied-by bound: + // We also might need to include associated item constraints that were specified in the implied + // bound, but omitted in the implied-by bound: // `fn f() -> impl Deref<Target = u8> + DerefMut` // If we're going to suggest removing `Deref<..>`, we'll need to put `<Target = u8>` on `DerefMut` let omitted_constraints: Vec<_> = implied_constraints diff --git a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs index e3e79749bea..d54f2af65cd 100644 --- a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs @@ -2,12 +2,14 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::higher; +use clippy_utils::ty::{deref_chain, get_adt_inherent_method}; +use clippy_utils::{higher, is_from_proc_macro}; use rustc_ast::ast::RangeLimits; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; +use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -100,11 +102,21 @@ impl IndexingSlicing { impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if self.suppress_restriction_lint_in_const && cx.tcx.hir().is_inside_const_context(expr.hir_id) { + if (self.suppress_restriction_lint_in_const && cx.tcx.hir().is_inside_const_context(expr.hir_id)) + || is_from_proc_macro(cx, expr) + { return; } - if let ExprKind::Index(array, index, _) = &expr.kind { + if let ExprKind::Index(array, index, _) = &expr.kind + && let expr_ty = cx.typeck_results().expr_ty(array) + && let mut deref = deref_chain(cx, expr_ty) + && deref.any(|l| { + l.peel_refs().is_slice() + || l.peel_refs().is_array() + || ty_has_applicable_get_function(cx, l.peel_refs(), expr_ty, expr) + }) + { let note = "the suggestion might not be applicable in constant blocks"; let ty = cx.typeck_results().expr_ty(array).peel_refs(); if let Some(range) = higher::Range::hir(index) { @@ -231,3 +243,33 @@ fn to_const_range(cx: &LateContext<'_>, range: higher::Range<'_>, array_size: u1 (start, end) } + +/// Checks if the output Ty of the `get` method on this Ty (if any) matches the Ty returned by the +/// indexing operation (if any). +fn ty_has_applicable_get_function<'tcx>( + cx: &LateContext<'tcx>, + ty: Ty<'tcx>, + array_ty: Ty<'tcx>, + index_expr: &Expr<'_>, +) -> bool { + if let ty::Adt(_, _) = array_ty.kind() + && let Some(get_output_ty) = get_adt_inherent_method(cx, ty, sym!(get)).map(|m| { + cx.tcx + .fn_sig(m.def_id) + .skip_binder() + .output() + .skip_binder() + }) + && let ty::Adt(def, args) = get_output_ty.kind() + && cx.tcx.is_diagnostic_item(sym::Option, def.0.did) + && let Some(option_generic_param) = args.first() + && let generic_ty = option_generic_param.expect_ty().peel_refs() + // FIXME: ideally this would handle type params and projections properly, for now just assume it's the same type + && (cx.typeck_results().expr_ty(index_expr).peel_refs() == generic_ty.peel_refs() + || matches!(generic_ty.peel_refs().kind(), ty::Param(_) | ty::Alias(_, _))) + { + true + } else { + false + } +} diff --git a/src/tools/clippy/clippy_lints/src/integer_division_remainder_used.rs b/src/tools/clippy/clippy_lints/src/integer_division_remainder_used.rs index cf598d5045e..a1215491b48 100644 --- a/src/tools/clippy/clippy_lints/src/integer_division_remainder_used.rs +++ b/src/tools/clippy/clippy_lints/src/integer_division_remainder_used.rs @@ -22,7 +22,7 @@ declare_clippy_lint! { /// ```no_run /// let my_div = 10 >> 1; /// ``` - #[clippy::version = "1.78.0"] + #[clippy::version = "1.79.0"] pub INTEGER_DIVISION_REMAINDER_USED, restriction, "use of disallowed default division and remainder operations" diff --git a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs index 601d0e151aa..6b03f2597b0 100644 --- a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs +++ b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_parent_as_impl; use clippy_utils::source::snippet; -use clippy_utils::ty::{implements_trait, make_normalized_projection}; +use clippy_utils::ty::{deref_chain, get_adt_inherent_method, implements_trait, make_normalized_projection}; use rustc_ast::Mutability; use rustc_errors::Applicability; use rustc_hir::{FnRetTy, ImplItemKind, ImplicitSelfKind, ItemKind, TyKind}; @@ -9,8 +9,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Ty}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Symbol}; -use std::iter; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -124,33 +123,6 @@ fn is_ty_exported(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { .is_some_and(|did| cx.effective_visibilities.is_exported(did)) } -/// Returns the deref chain of a type, starting with the type itself. -fn deref_chain<'cx, 'tcx>(cx: &'cx LateContext<'tcx>, ty: Ty<'tcx>) -> impl Iterator<Item = Ty<'tcx>> + 'cx { - iter::successors(Some(ty), |&ty| { - if let Some(deref_did) = cx.tcx.lang_items().deref_trait() - && implements_trait(cx, ty, deref_did, &[]) - { - make_normalized_projection(cx.tcx, cx.param_env, deref_did, sym::Target, [ty]) - } else { - None - } - }) -} - -fn adt_has_inherent_method(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool { - if let Some(ty_did) = ty.ty_adt_def().map(ty::AdtDef::did) { - cx.tcx.inherent_impls(ty_did).into_iter().flatten().any(|&did| { - cx.tcx - .associated_items(did) - .filter_by_name_unhygienic(method_name) - .next() - .is_some_and(|item| item.kind == ty::AssocKind::Fn) - }) - } else { - false - } -} - impl LateLintPass<'_> for IterWithoutIntoIter { fn check_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::Item<'_>) { if !in_external_macro(cx.sess(), item.span) @@ -167,7 +139,7 @@ impl LateLintPass<'_> for IterWithoutIntoIter { } && !deref_chain(cx, ty).any(|ty| { // We can't check inherent impls for slices, but we know that they have an `iter(_mut)` method - ty.peel_refs().is_slice() || adt_has_inherent_method(cx, ty, expected_method_name) + ty.peel_refs().is_slice() || get_adt_inherent_method(cx, ty, expected_method_name).is_some() }) && let Some(iter_assoc_span) = imp.items.iter().find_map(|item| { if item.ident.name == sym!(IntoIter) { diff --git a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs index 00124dcdd91..eadfeb6e341 100644 --- a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs +++ b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs @@ -28,7 +28,7 @@ declare_clippy_lint! { /// ```rust /// let eps = f32::EPSILON; /// ``` - #[clippy::version = "1.72.0"] + #[clippy::version = "1.79.0"] pub LEGACY_NUMERIC_CONSTANTS, style, "checks for usage of legacy std numeric constants and methods" diff --git a/src/tools/clippy/clippy_lints/src/lib.deprecated.rs b/src/tools/clippy/clippy_lints/src/lib.deprecated.rs index 80bde1b1138..0d21261822d 100644 --- a/src/tools/clippy/clippy_lints/src/lib.deprecated.rs +++ b/src/tools/clippy/clippy_lints/src/lib.deprecated.rs @@ -67,4 +67,12 @@ "clippy::wrong_pub_self_convention", "set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items", ); + store.register_removed( + "clippy::maybe_misused_cfg", + "this lint has been replaced by `unexpected_cfgs`", + ); + store.register_removed( + "clippy::mismatched_target_os", + "this lint has been replaced by `unexpected_cfgs`", + ); } diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 3328d642bd8..c65581d5203 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -251,6 +251,7 @@ mod needless_else; mod needless_for_each; mod needless_if; mod needless_late_init; +mod needless_maybe_sized; mod needless_parens_on_range_literals; mod needless_pass_by_ref_mut; mod needless_pass_by_value; @@ -325,6 +326,7 @@ mod size_of_in_element_count; mod size_of_ref; mod slow_vector_initialization; mod std_instead_of_core; +mod string_patterns; mod strings; mod strlen_on_c_strings; mod suspicious_operation_groupings; @@ -1058,6 +1060,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi)); store.register_late_pass(|_| Box::new(collection_is_never_read::CollectionIsNeverRead)); store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage)); + store.register_late_pass(|_| Box::new(needless_maybe_sized::NeedlessMaybeSized)); store.register_late_pass(|_| Box::new(redundant_async_block::RedundantAsyncBlock)); store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped)); store.register_late_pass(|_| Box::new(allow_attributes::AllowAttribute)); @@ -1165,6 +1168,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { ..Default::default() }) }); + store.register_late_pass(|_| Box::new(string_patterns::StringPatterns)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs b/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs index 84fb183e3f7..17399fb2cc2 100644 --- a/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs +++ b/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs @@ -43,7 +43,7 @@ declare_clippy_lint! { /// let x: Option<Vec<String>> = Some(Vec::new()); /// let y: Vec<String> = x.unwrap_or_default(); /// ``` - #[clippy::version = "1.78.0"] + #[clippy::version = "1.79.0"] pub MANUAL_UNWRAP_OR_DEFAULT, suspicious, "check if a `match` or `if let` can be simplified with `unwrap_or_default`" @@ -57,7 +57,8 @@ fn get_some<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<HirId> { // Since it comes from a pattern binding, we need to get the parent to actually match // against it. && let Some(def_id) = cx.tcx.opt_parent(def_id) - && cx.tcx.lang_items().get(LangItem::OptionSome) == Some(def_id) + && (cx.tcx.lang_items().get(LangItem::OptionSome) == Some(def_id) + || cx.tcx.lang_items().get(LangItem::ResultOk) == Some(def_id)) { let mut bindings = Vec::new(); pat.each_binding(|_, id, _, _| bindings.push(id)); @@ -80,6 +81,14 @@ fn get_none<'tcx>(cx: &LateContext<'tcx>, arm: &Arm<'tcx>) -> Option<&'tcx Expr< && cx.tcx.lang_items().get(LangItem::OptionNone) == Some(def_id) { Some(arm.body) + } else if let PatKind::TupleStruct(QPath::Resolved(_, path), _, _)= arm.pat.kind + && let Some(def_id) = path.res.opt_def_id() + // Since it comes from a pattern binding, we need to get the parent to actually match + // against it. + && let Some(def_id) = cx.tcx.opt_parent(def_id) + && cx.tcx.lang_items().get(LangItem::ResultErr) == Some(def_id) + { + Some(arm.body) } else if let PatKind::Wild = arm.pat.kind { // We consider that the `Some` check will filter it out if it's not right. Some(arm.body) diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs index a75cf37945f..c2c0fbf439d 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs @@ -2,7 +2,7 @@ use clippy_config::msrvs::Msrv; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::source::snippet; -use clippy_utils::visitors::{for_each_expr, is_local_used}; +use clippy_utils::visitors::{for_each_expr_without_closures, is_local_used}; use clippy_utils::{in_constant, path_to_local}; use rustc_ast::{BorrowKind, LitKind}; use rustc_errors::Applicability; @@ -249,7 +249,7 @@ fn emit_redundant_guards<'tcx>( /// an error in the future, and rustc already actively warns against this (see rust#41620), /// so we don't consider those as usable within patterns for linting purposes. fn expr_can_be_pat(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - for_each_expr(expr, |expr| { + for_each_expr_without_closures(expr, |expr| { if match expr.kind { ExprKind::ConstBlock(..) => cx.tcx.features().inline_const_pat, ExprKind::Call(c, ..) if let ExprKind::Path(qpath) = c.kind => { diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs index 78973984fb0..0e4b2d9d34a 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, walk_span_to_context}; use clippy_utils::sugg::{make_unop, Sugg}; use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop}; -use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr}; +use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr_without_closures}; use clippy_utils::{higher, is_expn_of, is_trait_method}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -283,7 +283,7 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op // to see that there aren't any let chains anywhere in the guard, as that would break // if we suggest `t.is_none() && (let X = y && z)` for: // `match t { None if let X = y && z => true, _ => false }` - let has_nested_let_chain = for_each_expr(guard, |expr| { + let has_nested_let_chain = for_each_expr_without_closures(guard, |expr| { if matches!(expr.kind, ExprKind::Let(..)) { ControlFlow::Break(()) } else { diff --git a/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs b/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs index 5409ede6008..1fab6c0e499 100644 --- a/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs +++ b/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::visitors::for_each_expr; +use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{eq_expr_value, get_parent_expr}; use core::ops::ControlFlow; use rustc_errors::Applicability; @@ -46,7 +46,7 @@ fn collect_replace_calls<'tcx>( let mut methods = VecDeque::new(); let mut from_args = VecDeque::new(); - let _: Option<()> = for_each_expr(expr, |e| { + let _: Option<()> = for_each_expr_without_closures(expr, |e| { if let Some(("replace", _, [from, to], _, _)) = method_call(e) { if eq_expr_value(cx, to_arg, to) && cx.typeck_results().expr_ty(from).peel_refs().is_char() { methods.push_front(e); diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 75a86c0c834..6200716afbe 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -66,6 +66,7 @@ mod map_flatten; mod map_identity; mod map_unwrap_or; mod mut_mutex_lock; +mod needless_character_iteration; mod needless_collect; mod needless_option_as_deref; mod needless_option_take; @@ -93,7 +94,6 @@ mod seek_from_current; mod seek_to_start_instead_of_rewind; mod single_char_add_str; mod single_char_insert_string; -mod single_char_pattern; mod single_char_push_string; mod skip_while_next; mod stable_sort_primitive; @@ -1142,38 +1142,6 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for string methods that receive a single-character - /// `str` as an argument, e.g., `_.split("x")`. - /// - /// ### Why is this bad? - /// While this can make a perf difference on some systems, - /// benchmarks have proven inconclusive. But at least using a - /// char literal makes it clear that we are looking at a single - /// character. - /// - /// ### Known problems - /// Does not catch multi-byte unicode characters. This is by - /// design, on many machines, splitting by a non-ascii char is - /// actually slower. Please do your own measurements instead of - /// relying solely on the results of this lint. - /// - /// ### Example - /// ```rust,ignore - /// _.split("x"); - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// _.split('x'); - /// ``` - #[clippy::version = "pre 1.29.0"] - pub SINGLE_CHAR_PATTERN, - pedantic, - "using a single-character str where a char could be used, e.g., `_.split(\"x\")`" -} - -declare_clippy_lint! { - /// ### What it does /// Checks for calling `.step_by(0)` on iterators which panics. /// /// ### Why is this bad? @@ -4084,12 +4052,33 @@ declare_clippy_lint! { /// ```no_run /// println!("the string is empty"); /// ``` - #[clippy::version = "1.78.0"] + #[clippy::version = "1.79.0"] pub CONST_IS_EMPTY, suspicious, "is_empty() called on strings known at compile time" } +declare_clippy_lint! { + /// ### What it does + /// Checks if an iterator is used to check if a string is ascii. + /// + /// ### Why is this bad? + /// The `str` type already implements the `is_ascii` method. + /// + /// ### Example + /// ```no_run + /// "foo".chars().all(|c| c.is_ascii()); + /// ``` + /// Use instead: + /// ```no_run + /// "foo".is_ascii(); + /// ``` + #[clippy::version = "1.80.0"] + pub NEEDLESS_CHARACTER_ITERATION, + suspicious, + "is_ascii() called on a char iterator" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -4147,7 +4136,6 @@ impl_lint_pass!(Methods => [ FLAT_MAP_OPTION, INEFFICIENT_TO_STRING, NEW_RET_NO_SELF, - SINGLE_CHAR_PATTERN, SINGLE_CHAR_ADD_STR, SEARCH_IS_SOME, FILTER_NEXT, @@ -4255,6 +4243,7 @@ impl_lint_pass!(Methods => [ UNNECESSARY_RESULT_MAP_OR_ELSE, MANUAL_C_STR_LITERALS, UNNECESSARY_GET_THEN_CHECK, + NEEDLESS_CHARACTER_ITERATION, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -4301,7 +4290,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods { inefficient_to_string::check(cx, expr, method_call.ident.name, receiver, args); single_char_add_str::check(cx, expr, receiver, args); into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, receiver); - single_char_pattern::check(cx, expr, method_call.ident.name, receiver, args); unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, &self.msrv); }, ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => { @@ -4462,6 +4450,7 @@ impl Methods { }, ("all", [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); + needless_character_iteration::check(cx, expr, recv, arg, true); if let Some(("cloned", recv2, [], _, _)) = method_call(recv) { iter_overeager_cloned::check( cx, @@ -4482,6 +4471,7 @@ impl Methods { }, ("any", [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); + needless_character_iteration::check(cx, expr, recv, arg, false); match method_call(recv) { Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs b/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs new file mode 100644 index 00000000000..e3d78207715 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs @@ -0,0 +1,124 @@ +use rustc_errors::Applicability; +use rustc_hir::{Closure, Expr, ExprKind, HirId, StmtKind, UnOp}; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::Span; + +use super::utils::get_last_chain_binding_hir_id; +use super::NEEDLESS_CHARACTER_ITERATION; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_opt; +use clippy_utils::{match_def_path, path_to_local_id, peel_blocks}; + +fn peels_expr_ref<'a, 'tcx>(mut expr: &'a Expr<'tcx>) -> &'a Expr<'tcx> { + while let ExprKind::AddrOf(_, _, e) = expr.kind { + expr = e; + } + expr +} + +fn handle_expr( + cx: &LateContext<'_>, + expr: &Expr<'_>, + first_param: HirId, + span: Span, + before_chars: Span, + revert: bool, + is_all: bool, +) { + match expr.kind { + ExprKind::MethodCall(method, receiver, [], _) => { + // If we have `!is_ascii`, then only `.any()` should warn. And if the condition is + // `is_ascii`, then only `.all()` should warn. + if revert != is_all + && method.ident.name.as_str() == "is_ascii" + && path_to_local_id(receiver, first_param) + && let char_arg_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs() + && *char_arg_ty.kind() == ty::Char + && let Some(snippet) = snippet_opt(cx, before_chars) + { + span_lint_and_sugg( + cx, + NEEDLESS_CHARACTER_ITERATION, + span, + "checking if a string is ascii using iterators", + "try", + format!("{}{snippet}.is_ascii()", if revert { "!" } else { "" }), + Applicability::MachineApplicable, + ); + } + }, + ExprKind::Block(block, _) => { + if block.stmts.iter().any(|stmt| !matches!(stmt.kind, StmtKind::Let(_))) { + // If there is something else than let bindings, then better not emit the lint. + return; + } + if let Some(block_expr) = block.expr + // First we ensure that this is a "binding chain" (each statement is a binding + // of the previous one) and that it is a binding of the closure argument. + && let Some(last_chain_binding_id) = + get_last_chain_binding_hir_id(first_param, block.stmts) + { + handle_expr( + cx, + block_expr, + last_chain_binding_id, + span, + before_chars, + revert, + is_all, + ); + } + }, + ExprKind::Unary(UnOp::Not, expr) => handle_expr(cx, expr, first_param, span, before_chars, !revert, is_all), + ExprKind::Call(fn_path, [arg]) => { + // If we have `!is_ascii`, then only `.any()` should warn. And if the condition is + // `is_ascii`, then only `.all()` should warn. + if revert != is_all + && let ExprKind::Path(path) = fn_path.kind + && let Some(fn_def_id) = cx.qpath_res(&path, fn_path.hir_id).opt_def_id() + && match_def_path(cx, fn_def_id, &["core", "char", "methods", "<impl char>", "is_ascii"]) + && path_to_local_id(peels_expr_ref(arg), first_param) + && let Some(snippet) = snippet_opt(cx, before_chars) + { + span_lint_and_sugg( + cx, + NEEDLESS_CHARACTER_ITERATION, + span, + "checking if a string is ascii using iterators", + "try", + format!("{}{snippet}.is_ascii()", if revert { "!" } else { "" }), + Applicability::MachineApplicable, + ); + } + }, + _ => {}, + } +} + +pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, closure_arg: &Expr<'_>, is_all: bool) { + if let ExprKind::Closure(&Closure { body, .. }) = closure_arg.kind + && let body = cx.tcx.hir().body(body) + && let Some(first_param) = body.params.first() + && let ExprKind::MethodCall(method, mut recv, [], _) = recv.kind + && method.ident.name.as_str() == "chars" + && let str_ty = cx.typeck_results().expr_ty_adjusted(recv).peel_refs() + && *str_ty.kind() == ty::Str + { + let expr_start = recv.span; + while let ExprKind::MethodCall(_, new_recv, _, _) = recv.kind { + recv = new_recv; + } + let body_expr = peel_blocks(body.value); + + handle_expr( + cx, + body_expr, + first_param.pat.hir_id, + recv.span.with_hi(call_expr.span.hi()), + recv.span.with_hi(expr_start.hi()), + false, + is_all, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs index ca331f3e756..3d326bc99f9 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -60,7 +60,7 @@ pub(super) fn check<'tcx>( let map = cx.tcx.hir(); let body = map.body_owned_by(map.enclosing_body_owner(expr.hir_id)); - reference_visitor.visit_body(&body); + reference_visitor.visit_body(body); if reference_visitor.found_reference { return; diff --git a/src/tools/clippy/clippy_lints/src/methods/path_buf_push_overwrite.rs b/src/tools/clippy/clippy_lints/src/methods/path_buf_push_overwrite.rs index 04a27cc98f3..2d3007e50b8 100644 --- a/src/tools/clippy/clippy_lints/src/methods/path_buf_push_overwrite.rs +++ b/src/tools/clippy/clippy_lints/src/methods/path_buf_push_overwrite.rs @@ -27,7 +27,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t lit.span, "calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition", "try", - format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')), + format!("\"{}\"", pushed_path_lit.trim_start_matches(['/', '\\'])), Applicability::MachineApplicable, ); } diff --git a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs index f5f1e94bbf4..3b2dd285b8c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs +++ b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::deref_closure_args; use clippy_utils::ty::is_type_lang_item; -use clippy_utils::{get_parent_expr, is_trait_method, strip_pat_refs}; +use clippy_utils::{is_receiver_of_method_call, is_trait_method, strip_pat_refs}; use hir::ExprKind; use rustc_errors::Applicability; use rustc_hir as hir; @@ -156,13 +156,3 @@ pub(super) fn check<'tcx>( } } } - -fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { - if let Some(parent_expr) = get_parent_expr(cx, expr) - && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind - && receiver.hir_id == expr.hir_id - { - return true; - } - false -} diff --git a/src/tools/clippy/clippy_lints/src/methods/single_char_insert_string.rs b/src/tools/clippy/clippy_lints/src/methods/single_char_insert_string.rs index 20ec2b74d81..e2f76ac114c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/single_char_insert_string.rs +++ b/src/tools/clippy/clippy_lints/src/methods/single_char_insert_string.rs @@ -1,8 +1,8 @@ -use super::utils::get_hint_if_single_char_arg; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::{snippet_with_applicability, str_literal_to_char_literal}; +use rustc_ast::BorrowKind; use rustc_errors::Applicability; -use rustc_hir as hir; +use rustc_hir::{self as hir, ExprKind}; use rustc_lint::LateContext; use super::SINGLE_CHAR_ADD_STR; @@ -10,7 +10,7 @@ use super::SINGLE_CHAR_ADD_STR; /// lint for length-1 `str`s as argument for `insert_str` pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { let mut applicability = Applicability::MachineApplicable; - if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability, false) { + if let Some(extension_string) = str_literal_to_char_literal(cx, &args[1], &mut applicability, false) { let base_string_snippet = snippet_with_applicability(cx, receiver.span.source_callsite(), "_", &mut applicability); let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability); @@ -25,4 +25,43 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir:: applicability, ); } + + if let ExprKind::AddrOf(BorrowKind::Ref, _, arg) = &args[1].kind + && let ExprKind::MethodCall(path_segment, method_arg, _, _) = &arg.kind + && path_segment.ident.name == rustc_span::sym::to_string + && (is_ref_char(cx, method_arg) || is_char(cx, method_arg)) + { + let base_string_snippet = + snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability); + let extension_string = + snippet_with_applicability(cx, method_arg.span.source_callsite(), "..", &mut applicability); + let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability); + let deref_string = if is_ref_char(cx, method_arg) { "*" } else { "" }; + + let sugg = format!("{base_string_snippet}.insert({pos_arg}, {deref_string}{extension_string})"); + span_lint_and_sugg( + cx, + SINGLE_CHAR_ADD_STR, + expr.span, + "calling `insert_str()` using a single-character converted to string", + "consider using `insert` without `to_string()`", + sugg, + applicability, + ); + } +} + +fn is_ref_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { + if cx.typeck_results().expr_ty(expr).is_ref() + && let rustc_middle::ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(expr).kind() + && ty.is_char() + { + return true; + } + + false +} + +fn is_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { + cx.typeck_results().expr_ty(expr).is_char() } diff --git a/src/tools/clippy/clippy_lints/src/methods/single_char_pattern.rs b/src/tools/clippy/clippy_lints/src/methods/single_char_pattern.rs deleted file mode 100644 index 982a7901c45..00000000000 --- a/src/tools/clippy/clippy_lints/src/methods/single_char_pattern.rs +++ /dev/null @@ -1,64 +0,0 @@ -use super::utils::get_hint_if_single_char_arg; -use clippy_utils::diagnostics::span_lint_and_sugg; -use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_lint::LateContext; -use rustc_middle::ty; -use rustc_span::symbol::Symbol; - -use super::SINGLE_CHAR_PATTERN; - -const PATTERN_METHODS: [(&str, usize); 22] = [ - ("contains", 0), - ("starts_with", 0), - ("ends_with", 0), - ("find", 0), - ("rfind", 0), - ("split", 0), - ("split_inclusive", 0), - ("rsplit", 0), - ("split_terminator", 0), - ("rsplit_terminator", 0), - ("splitn", 1), - ("rsplitn", 1), - ("split_once", 0), - ("rsplit_once", 0), - ("matches", 0), - ("rmatches", 0), - ("match_indices", 0), - ("rmatch_indices", 0), - ("trim_start_matches", 0), - ("trim_end_matches", 0), - ("replace", 0), - ("replacen", 0), -]; - -/// lint for length-1 `str`s for methods in `PATTERN_METHODS` -pub(super) fn check( - cx: &LateContext<'_>, - _expr: &hir::Expr<'_>, - method_name: Symbol, - receiver: &hir::Expr<'_>, - args: &[hir::Expr<'_>], -) { - for &(method, pos) in &PATTERN_METHODS { - if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(receiver).kind() - && ty.is_str() - && method_name.as_str() == method - && args.len() > pos - && let arg = &args[pos] - && let mut applicability = Applicability::MachineApplicable - && let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability, true) - { - span_lint_and_sugg( - cx, - SINGLE_CHAR_PATTERN, - arg.span, - "single-character string constant used as pattern", - "consider using a `char`", - hint, - applicability, - ); - } - } -} diff --git a/src/tools/clippy/clippy_lints/src/methods/single_char_push_string.rs b/src/tools/clippy/clippy_lints/src/methods/single_char_push_string.rs index 97c13825bc1..4ae8634305d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/single_char_push_string.rs +++ b/src/tools/clippy/clippy_lints/src/methods/single_char_push_string.rs @@ -1,8 +1,8 @@ -use super::utils::get_hint_if_single_char_arg; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::{snippet_with_applicability, str_literal_to_char_literal}; +use rustc_ast::BorrowKind; use rustc_errors::Applicability; -use rustc_hir as hir; +use rustc_hir::{self as hir, ExprKind}; use rustc_lint::LateContext; use super::SINGLE_CHAR_ADD_STR; @@ -10,7 +10,7 @@ use super::SINGLE_CHAR_ADD_STR; /// lint for length-1 `str`s as argument for `push_str` pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { let mut applicability = Applicability::MachineApplicable; - if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[0], &mut applicability, false) { + if let Some(extension_string) = str_literal_to_char_literal(cx, &args[0], &mut applicability, false) { let base_string_snippet = snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability); let sugg = format!("{base_string_snippet}.push({extension_string})"); @@ -24,4 +24,42 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir:: applicability, ); } + + if let ExprKind::AddrOf(BorrowKind::Ref, _, arg) = &args[0].kind + && let ExprKind::MethodCall(path_segment, method_arg, _, _) = &arg.kind + && path_segment.ident.name == rustc_span::sym::to_string + && (is_ref_char(cx, method_arg) || is_char(cx, method_arg)) + { + let base_string_snippet = + snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability); + let extension_string = + snippet_with_applicability(cx, method_arg.span.source_callsite(), "..", &mut applicability); + let deref_string = if is_ref_char(cx, method_arg) { "*" } else { "" }; + + let sugg = format!("{base_string_snippet}.push({deref_string}{extension_string})"); + span_lint_and_sugg( + cx, + SINGLE_CHAR_ADD_STR, + expr.span, + "calling `push_str()` using a single-character converted to string", + "consider using `push` without `to_string()`", + sugg, + applicability, + ); + } +} + +fn is_ref_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { + if cx.typeck_results().expr_ty(expr).is_ref() + && let rustc_middle::ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(expr).kind() + && ty.is_char() + { + return true; + } + + false +} + +fn is_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { + cx.typeck_results().expr_ty(expr).is_char() } diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs index e8c12bbeea0..4f42fb73547 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs @@ -3,7 +3,7 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_context; use clippy_utils::usage::local_used_after_expr; -use clippy_utils::visitors::{for_each_expr_with_closures, Descend}; +use clippy_utils::visitors::{for_each_expr, Descend}; use clippy_utils::{is_diag_item_method, match_def_path, path_to_local_id, paths}; use core::ops::ControlFlow; use rustc_errors::Applicability; @@ -209,7 +209,7 @@ fn indirect_usage<'tcx>( }) = stmt.kind { let mut path_to_binding = None; - let _: Option<!> = for_each_expr_with_closures(cx, init_expr, |e| { + let _: Option<!> = for_each_expr(cx, init_expr, |e| { if path_to_local_id(e, binding) { path_to_binding = Some(e); } diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs index daf99d98614..c9b9d98dbe6 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -2,7 +2,7 @@ use super::utils::clone_or_copy_needed; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_copy; use clippy_utils::usage::mutated_variables; -use clippy_utils::visitors::{for_each_expr, Descend}; +use clippy_utils::visitors::{for_each_expr_without_closures, Descend}; use clippy_utils::{is_res_lang_ctor, is_trait_method, path_res, path_to_local_id}; use core::ops::ControlFlow; use rustc_hir as hir; @@ -26,7 +26,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a let (mut found_mapping, mut found_filtering) = check_expression(cx, arg_id, body.value); - let _: Option<!> = for_each_expr(body.value, |e| { + let _: Option<!> = for_each_expr_without_closures(body.value, |e| { if let hir::ExprKind::Ret(Some(e)) = &e.kind { let (found_mapping_res, found_filtering_res) = check_expression(cx, arg_id, e); found_mapping |= found_mapping_res; diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs index d1300dd43c2..70885e46e95 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::ForLoop; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{get_iterator_item_ty, implements_trait}; -use clippy_utils::visitors::for_each_expr; +use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr, path_to_local}; use core::ops::ControlFlow; use rustc_errors::Applicability; @@ -61,7 +61,7 @@ pub fn check_for_loop_iter( fn is_caller_or_fields_change(cx: &LateContext<'_>, body: &Expr<'_>, caller: &Expr<'_>) -> bool { let mut change = false; if let ExprKind::Block(block, ..) = body.kind { - for_each_expr(block, |e| { + for_each_expr_without_closures(block, |e| { match e.kind { ExprKind::Assign(assignee, _, _) | ExprKind::AssignOp(_, assignee, _) => { change |= !can_mut_borrow_both(cx, caller, assignee); diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_result_map_or_else.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_result_map_or_else.rs index cdfaa690d5f..c14a87c1534 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_result_map_or_else.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_result_map_or_else.rs @@ -4,10 +4,11 @@ use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::{Closure, Expr, ExprKind, HirId, QPath, Stmt, StmtKind}; +use rustc_hir::{Closure, Expr, ExprKind, HirId, QPath}; use rustc_lint::LateContext; use rustc_span::symbol::sym; +use super::utils::get_last_chain_binding_hir_id; use super::UNNECESSARY_RESULT_MAP_OR_ELSE; fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>) { @@ -25,22 +26,6 @@ fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &E ); } -fn get_last_chain_binding_hir_id(mut hir_id: HirId, statements: &[Stmt<'_>]) -> Option<HirId> { - for stmt in statements { - if let StmtKind::Let(local) = stmt.kind - && let Some(init) = local.init - && let ExprKind::Path(QPath::Resolved(_, path)) = init.kind - && let hir::def::Res::Local(local_hir_id) = path.res - && local_hir_id == hir_id - { - hir_id = local.pat.hir_id; - } else { - return None; - } - } - Some(hir_id) -} - fn handle_qpath( cx: &LateContext<'_>, expr: &Expr<'_>, diff --git a/src/tools/clippy/clippy_lints/src/methods/utils.rs b/src/tools/clippy/clippy_lints/src/methods/utils.rs index 1a55b7160fb..0d2b0a31317 100644 --- a/src/tools/clippy/clippy_lints/src/methods/utils.rs +++ b/src/tools/clippy/clippy_lints/src/methods/utils.rs @@ -1,10 +1,7 @@ -use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{get_parent_expr, path_to_local_id, usage}; -use rustc_ast::ast; -use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Mutability, Pat}; +use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, QPath, Stmt, StmtKind}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Ty}; @@ -49,48 +46,6 @@ pub(super) fn derefs_to_slice<'tcx>( } } -pub(super) fn get_hint_if_single_char_arg( - cx: &LateContext<'_>, - arg: &Expr<'_>, - applicability: &mut Applicability, - ascii_only: bool, -) -> Option<String> { - if let ExprKind::Lit(lit) = &arg.kind - && let ast::LitKind::Str(r, style) = lit.node - && let string = r.as_str() - && let len = if ascii_only { - string.len() - } else { - string.chars().count() - } - && len == 1 - { - let snip = snippet_with_applicability(cx, arg.span, string, applicability); - let ch = if let ast::StrStyle::Raw(nhash) = style { - let nhash = nhash as usize; - // for raw string: r##"a"## - &snip[(nhash + 2)..(snip.len() - 1 - nhash)] - } else { - // for regular string: "a" - &snip[1..(snip.len() - 1)] - }; - - let hint = format!( - "'{}'", - match ch { - "'" => "\\'", - r"\" => "\\\\", - "\\\"" => "\"", // no need to escape `"` in `'"'` - _ => ch, - } - ); - - Some(hint) - } else { - None - } -} - /// The core logic of `check_for_loop_iter` in `unnecessary_iter_cloned.rs`, this function wraps a /// use of `CloneOrCopyVisitor`. pub(super) fn clone_or_copy_needed<'tcx>( @@ -175,3 +130,19 @@ impl<'cx, 'tcx> CloneOrCopyVisitor<'cx, 'tcx> { .any(|hir_id| path_to_local_id(expr, *hir_id)) } } + +pub(super) fn get_last_chain_binding_hir_id(mut hir_id: HirId, statements: &[Stmt<'_>]) -> Option<HirId> { + for stmt in statements { + if let StmtKind::Let(local) = stmt.kind + && let Some(init) = local.init + && let ExprKind::Path(QPath::Resolved(_, path)) = init.kind + && let rustc_hir::def::Res::Local(local_hir_id) = path.res + && local_hir_id == hir_id + { + hir_id = local.pat.hir_id; + } else { + return None; + } + } + Some(hir_id) +} diff --git a/src/tools/clippy/clippy_lints/src/misc_early/zero_prefixed_literal.rs b/src/tools/clippy/clippy_lints/src/misc_early/zero_prefixed_literal.rs index 4f9578d1b25..61f4684c9e3 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/zero_prefixed_literal.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/zero_prefixed_literal.rs @@ -6,7 +6,7 @@ use rustc_span::Span; use super::ZERO_PREFIXED_LITERAL; pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, lit_snip: &str) { - let trimmed_lit_snip = lit_snip.trim_start_matches(|c| c == '_' || c == '0'); + let trimmed_lit_snip = lit_snip.trim_start_matches(['_', '0']); span_lint_and_then( cx, ZERO_PREFIXED_LITERAL, @@ -20,7 +20,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, lit_snip: &str) { Applicability::MaybeIncorrect, ); // do not advise to use octal form if the literal cannot be expressed in base 8. - if !lit_snip.contains(|c| c == '8' || c == '9') { + if !lit_snip.contains(['8', '9']) { diag.span_suggestion( lit_span, "if you mean to use an octal constant, use `0o`", diff --git a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs index 752723a0c68..94c91b09517 100644 --- a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs @@ -4,7 +4,7 @@ use std::ops::ControlFlow; use clippy_utils::comparisons::{normalize_comparison, Rel}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; -use clippy_utils::visitors::for_each_expr; +use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{eq_expr_value, hash_expr, higher}; use rustc_ast::{LitKind, RangeLimits}; use rustc_data_structures::packed::Pu128; @@ -405,7 +405,7 @@ impl LateLintPass<'_> for MissingAssertsForIndexing { fn check_body(&mut self, cx: &LateContext<'_>, body: &Body<'_>) { let mut map = UnhashMap::default(); - for_each_expr(body.value, |expr| { + for_each_expr_without_closures(body.value, |expr| { check_index(cx, expr, &mut map); check_assert(cx, expr, &mut map); ControlFlow::<!, ()>::Continue(()) diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs index 9ba19e0a865..4592324809f 100644 --- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs +++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs @@ -1,7 +1,6 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint; use clippy_utils::qualify_min_const_fn::is_min_const_fn; -use clippy_utils::ty::has_drop; use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, trait_ref_of_method}; use rustc_hir as hir; use rustc_hir::def_id::CRATE_DEF_ID; @@ -121,10 +120,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { } }, FnKind::Method(_, sig, ..) => { - if trait_ref_of_method(cx, def_id).is_some() - || already_const(sig.header) - || method_accepts_droppable(cx, def_id) - { + if trait_ref_of_method(cx, def_id).is_some() || already_const(sig.header) { return; } }, @@ -151,26 +147,13 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { let mir = cx.tcx.optimized_mir(def_id); - if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, &self.msrv) { - if cx.tcx.is_const_fn_raw(def_id.to_def_id()) { - cx.tcx.dcx().span_err(span, err); - } - } else { + if let Ok(()) = is_min_const_fn(cx.tcx, mir, &self.msrv) { span_lint(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`"); } } extract_msrv_attr!(LateContext); } -/// Returns true if any of the method parameters is a type that implements `Drop`. The method -/// can't be made const then, because `drop` can't be const-evaluated. -fn method_accepts_droppable(cx: &LateContext<'_>, def_id: LocalDefId) -> bool { - let sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder(); - - // If any of the params are droppable, return true - sig.inputs().iter().any(|&ty| has_drop(cx, ty)) -} - // We don't have to lint on something that's already `const` #[must_use] fn already_const(header: hir::FnHeader) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs index a64faa124f0..77595b121aa 100644 --- a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs +++ b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs @@ -110,7 +110,7 @@ fn should_lint<'tcx>( // Is there a call to `DebugStruct::debug_struct`? Do lint if there is. let mut has_debug_struct = false; - for_each_expr(block, |expr| { + for_each_expr(cx, block, |expr| { if let ExprKind::MethodCall(path, recv, ..) = &expr.kind { let recv_ty = typeck_results.expr_ty(recv).peel_refs(); @@ -165,7 +165,7 @@ fn check_struct<'tcx>( let mut has_direct_field_access = false; let mut field_accesses = FxHashSet::default(); - for_each_expr(block, |expr| { + for_each_expr(cx, block, |expr| { if let ExprKind::Field(target, ident) = expr.kind && let target_ty = typeck_results.expr_ty_adjusted(target).peel_refs() && target_ty == self_ty diff --git a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs index 5b4ef852f0d..da74a7c7145 100644 --- a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::visitors::{for_each_expr_with_closures, Descend, Visitable}; +use clippy_utils::visitors::{for_each_expr, Descend, Visitable}; use core::ops::ControlFlow::Continue; use hir::def::{DefKind, Res}; use hir::{BlockCheckMode, ExprKind, QPath, Safety, UnOp}; @@ -96,7 +96,7 @@ fn collect_unsafe_exprs<'tcx>( node: impl Visitable<'tcx>, unsafe_ops: &mut Vec<(&'static str, Span)>, ) { - for_each_expr_with_closures(cx, node, |expr| { + for_each_expr(cx, node, |expr| { match expr.kind { ExprKind::InlineAsm(_) => unsafe_ops.push(("inline assembly used here", expr.span)), diff --git a/src/tools/clippy/clippy_lints/src/needless_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bool.rs index e1866eaa18a..9cb4fa41c73 100644 --- a/src/tools/clippy/clippy_lints/src/needless_bool.rs +++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs @@ -6,8 +6,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::{ - higher, is_block_like, is_else_clause, is_expn_of, is_parent_stmt, peel_blocks, peel_blocks_with_stmt, - span_extract_comment, SpanlessEq, + get_parent_expr, higher, is_block_like, is_else_clause, is_expn_of, is_parent_stmt, is_receiver_of_method_call, + peel_blocks, peel_blocks_with_stmt, span_extract_comment, SpanlessEq, }; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -154,7 +154,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { snip = snip.blockify(); } - if condition_needs_parentheses(cond) && is_parent_stmt(cx, e.hir_id) { + if (condition_needs_parentheses(cond) && is_parent_stmt(cx, e.hir_id)) + || is_receiver_of_method_call(cx, e) + || is_as_argument(cx, e) + { snip = snip.maybe_par(); } @@ -442,3 +445,7 @@ fn fetch_assign<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, bool) None } } + +fn is_as_argument(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { + matches!(get_parent_expr(cx, e).map(|e| e.kind), Some(ExprKind::Cast(_, _))) +} diff --git a/src/tools/clippy/clippy_lints/src/needless_late_init.rs b/src/tools/clippy/clippy_lints/src/needless_late_init.rs index 5a0ae1a4d6d..4bfc30fa5cf 100644 --- a/src/tools/clippy/clippy_lints/src/needless_late_init.rs +++ b/src/tools/clippy/clippy_lints/src/needless_late_init.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::path_to_local; use clippy_utils::source::snippet_opt; use clippy_utils::ty::needs_ordered_drop; -use clippy_utils::visitors::{for_each_expr, for_each_expr_with_closures, is_local_used}; +use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures, is_local_used}; use core::ops::ControlFlow; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::{ @@ -63,7 +63,7 @@ declare_clippy_lint! { declare_lint_pass!(NeedlessLateInit => [NEEDLESS_LATE_INIT]); fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> bool { - for_each_expr_with_closures(cx, stmt, |e| { + for_each_expr(cx, stmt, |e| { if matches!(e.kind, ExprKind::Assign(..)) { ControlFlow::Break(()) } else { @@ -74,7 +74,7 @@ fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> } fn contains_let(cond: &Expr<'_>) -> bool { - for_each_expr(cond, |e| { + for_each_expr_without_closures(cond, |e| { if matches!(e.kind, ExprKind::Let(_)) { ControlFlow::Break(()) } else { diff --git a/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs new file mode 100644 index 00000000000..06ae1723a03 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs @@ -0,0 +1,164 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use rustc_errors::Applicability; +use rustc_hir::def_id::{DefId, DefIdMap}; +use rustc_hir::{GenericBound, Generics, PolyTraitRef, TraitBoundModifier, WherePredicate}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{ClauseKind, PredicatePolarity}; +use rustc_session::declare_lint_pass; +use rustc_span::symbol::Ident; + +declare_clippy_lint! { + /// ### What it does + /// Lints `?Sized` bounds applied to type parameters that cannot be unsized + /// + /// ### Why is this bad? + /// The `?Sized` bound is misleading because it cannot be satisfied by an + /// unsized type + /// + /// ### Example + /// ```rust + /// // `T` cannot be unsized because `Clone` requires it to be `Sized` + /// fn f<T: Clone + ?Sized>(t: &T) {} + /// ``` + /// Use instead: + /// ```rust + /// fn f<T: Clone>(t: &T) {} + /// + /// // or choose alternative bounds for `T` so that it can be unsized + /// ``` + #[clippy::version = "1.79.0"] + pub NEEDLESS_MAYBE_SIZED, + suspicious, + "a `?Sized` bound that is unusable due to a `Sized` requirement" +} +declare_lint_pass!(NeedlessMaybeSized => [NEEDLESS_MAYBE_SIZED]); + +#[allow(clippy::struct_field_names)] +struct Bound<'tcx> { + /// The [`DefId`] of the type parameter the bound refers to + param: DefId, + ident: Ident, + + trait_bound: &'tcx PolyTraitRef<'tcx>, + modifier: TraitBoundModifier, + + predicate_pos: usize, + bound_pos: usize, +} + +/// Finds all of the [`Bound`]s that refer to a type parameter and are not from a macro expansion +fn type_param_bounds<'tcx>(generics: &'tcx Generics<'tcx>) -> impl Iterator<Item = Bound<'tcx>> { + generics + .predicates + .iter() + .enumerate() + .filter_map(|(predicate_pos, predicate)| { + let WherePredicate::BoundPredicate(bound_predicate) = predicate else { + return None; + }; + + let (param, ident) = bound_predicate.bounded_ty.as_generic_param()?; + + Some( + bound_predicate + .bounds + .iter() + .enumerate() + .filter_map(move |(bound_pos, bound)| match bound { + &GenericBound::Trait(ref trait_bound, modifier) => Some(Bound { + param, + ident, + trait_bound, + modifier, + predicate_pos, + bound_pos, + }), + GenericBound::Outlives(_) => None, + }) + .filter(|bound| !bound.trait_bound.span.from_expansion()), + ) + }) + .flatten() +} + +/// Searches the supertraits of the trait referred to by `trait_bound` recursively, returning the +/// path taken to find a `Sized` bound if one is found +fn path_to_sized_bound(cx: &LateContext<'_>, trait_bound: &PolyTraitRef<'_>) -> Option<Vec<DefId>> { + fn search(cx: &LateContext<'_>, path: &mut Vec<DefId>) -> bool { + let trait_def_id = *path.last().unwrap(); + + if Some(trait_def_id) == cx.tcx.lang_items().sized_trait() { + return true; + } + + for &(predicate, _) in cx.tcx.super_predicates_of(trait_def_id).predicates { + if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() + && trait_predicate.polarity == PredicatePolarity::Positive + && !path.contains(&trait_predicate.def_id()) + { + path.push(trait_predicate.def_id()); + if search(cx, path) { + return true; + } + path.pop(); + } + } + + false + } + + let mut path = vec![trait_bound.trait_ref.trait_def_id()?]; + search(cx, &mut path).then_some(path) +} + +impl LateLintPass<'_> for NeedlessMaybeSized { + fn check_generics(&mut self, cx: &LateContext<'_>, generics: &Generics<'_>) { + let Some(sized_trait) = cx.tcx.lang_items().sized_trait() else { + return; + }; + + let maybe_sized_params: DefIdMap<_> = type_param_bounds(generics) + .filter(|bound| { + bound.trait_bound.trait_ref.trait_def_id() == Some(sized_trait) + && bound.modifier == TraitBoundModifier::Maybe + }) + .map(|bound| (bound.param, bound)) + .collect(); + + for bound in type_param_bounds(generics) { + if bound.modifier == TraitBoundModifier::None + && let Some(sized_bound) = maybe_sized_params.get(&bound.param) + && let Some(path) = path_to_sized_bound(cx, bound.trait_bound) + { + span_lint_and_then( + cx, + NEEDLESS_MAYBE_SIZED, + sized_bound.trait_bound.span, + "`?Sized` bound is ignored because of a `Sized` requirement", + |diag| { + let ty_param = sized_bound.ident; + diag.span_note( + bound.trait_bound.span, + format!("`{ty_param}` cannot be unsized because of the bound"), + ); + + for &[current_id, next_id] in path.array_windows() { + let current = cx.tcx.item_name(current_id); + let next = cx.tcx.item_name(next_id); + diag.note(format!("...because `{current}` has the bound `{next}`")); + } + + diag.span_suggestion_verbose( + generics.span_for_bound_removal(sized_bound.predicate_pos, sized_bound.bound_pos), + "change the bounds that require `Sized`, or remove the `?Sized` bound", + "", + Applicability::MaybeIncorrect, + ); + }, + ); + + return; + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs index da6ed5fb96f..57ba0da5331 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -1,7 +1,7 @@ use super::needless_pass_by_value::requires_exact_signature; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet; -use clippy_utils::visitors::for_each_expr_with_closures; +use clippy_utils::visitors::for_each_expr; use clippy_utils::{inherits_cfg, is_from_proc_macro, is_self}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::Applicability; @@ -205,7 +205,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { // We retrieve all the closures declared in the function because they will not be found // by `euv::Delegate`. let mut closures: FxHashSet<LocalDefId> = FxHashSet::default(); - for_each_expr_with_closures(cx, body, |expr| { + for_each_expr(cx, body, |expr| { if let ExprKind::Closure(closure) = expr.kind { closures.insert(closure.def_id); } diff --git a/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs index 932d6fe54d6..de6a1a36f3e 100644 --- a/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs +++ b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs @@ -1,10 +1,11 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_res_lang_ctor, last_path_segment, path_res, std_or_core}; +use clippy_utils::{is_from_proc_macro, is_res_lang_ctor, last_path_segment, path_res, std_or_core}; use rustc_errors::Applicability; use rustc_hir::def_id::LocalDefId; use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, LangItem, Node, UnOp}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; use rustc_middle::ty::EarlyBinder; use rustc_session::declare_lint_pass; use rustc_span::sym; @@ -111,7 +112,7 @@ declare_lint_pass!(NonCanonicalImpls => [NON_CANONICAL_CLONE_IMPL, NON_CANONICAL impl LateLintPass<'_> for NonCanonicalImpls { #[expect(clippy::too_many_lines)] - fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) { + fn check_impl_item<'tcx>(&mut self, cx: &LateContext<'tcx>, impl_item: &ImplItem<'tcx>) { let Node::Item(item) = cx.tcx.parent_hir_node(impl_item.hir_id()) else { return; }; @@ -128,6 +129,9 @@ impl LateLintPass<'_> for NonCanonicalImpls { let ExprKind::Block(block, ..) = body.value.kind else { return; }; + if in_external_macro(cx.sess(), block.span) || is_from_proc_macro(cx, impl_item) { + return; + } if cx.tcx.is_diagnostic_item(sym::Clone, trait_impl.def_id) && let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy) diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs index 20a97645af9..5cb8e7bfab2 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -7,7 +7,7 @@ use std::ptr; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::in_constant; use clippy_utils::macros::macro_backtrace; -use clippy_utils::ty::InteriorMut; +use clippy_utils::ty::{implements_trait, InteriorMut}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{ @@ -18,7 +18,7 @@ use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult, GlobalId}; use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::impl_lint_pass; -use rustc_span::{sym, InnerSpan, Span, DUMMY_SP}; +use rustc_span::{sym, Span, DUMMY_SP}; use rustc_target::abi::VariantIdx; // FIXME: this is a correctness problem but there's no suitable @@ -127,19 +127,19 @@ declare_clippy_lint! { } #[derive(Copy, Clone)] -enum Source { - Item { item: Span }, +enum Source<'tcx> { + Item { item: Span, ty: Ty<'tcx> }, Assoc { item: Span }, Expr { expr: Span }, } -impl Source { +impl Source<'_> { #[must_use] fn lint(&self) -> (&'static Lint, &'static str, Span) { match self { - Self::Item { item } | Self::Assoc { item, .. } => ( + Self::Item { item, .. } | Self::Assoc { item, .. } => ( DECLARE_INTERIOR_MUTABLE_CONST, - "a `const` item should never be interior mutable", + "a `const` item should not be interior mutable", *item, ), Self::Expr { expr } => ( @@ -151,16 +151,24 @@ impl Source { } } -fn lint(cx: &LateContext<'_>, source: Source) { +fn lint<'tcx>(cx: &LateContext<'tcx>, source: Source<'tcx>) { let (lint, msg, span) = source.lint(); span_lint_and_then(cx, lint, span, msg, |diag| { if span.from_expansion() { return; // Don't give suggestions into macros. } match source { - Source::Item { .. } => { - let const_kw_span = span.from_inner(InnerSpan::new(0, 5)); - diag.span_label(const_kw_span, "make this a static item (maybe with lazy_static)"); + Source::Item { ty, .. } => { + let Some(sync_trait) = cx.tcx.lang_items().sync_trait() else { + return; + }; + if implements_trait(cx, ty, sync_trait, &[]) { + diag.help("consider making this a static item"); + } else { + diag.help( + "consider making this `Sync` so that it can go in a static item or using a `thread_local`", + ); + } }, Source::Assoc { .. } => (), Source::Expr { .. } => { @@ -311,7 +319,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> { && self.interior_mut.is_interior_mut_ty(cx, ty) && Self::is_value_unfrozen_poly(cx, body_id, ty) { - lint(cx, Source::Item { item: it.span }); + lint(cx, Source::Item { item: it.span, ty }); } } } diff --git a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs index 910e584a7a0..641d881d974 100644 --- a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_opt; use clippy_utils::ty::implements_trait; -use clippy_utils::visitors::for_each_expr; +use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{binop_traits, eq_expr_value, trait_ref_of_method}; use core::ops::ControlFlow; use rustc_errors::Applicability; @@ -62,7 +62,7 @@ pub(super) fn check<'tcx>( }; let mut found = false; - let found_multiple = for_each_expr(e, |e| { + let found_multiple = for_each_expr_without_closures(e, |e| { if eq_expr_value(cx, assignee, e) { if found { return ControlFlow::Break(()); diff --git a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs index 806638c0505..381975199d2 100644 --- a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs +++ b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs @@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) { let mut panics = Vec::new(); - let _: Option<!> = for_each_expr(body.value, |e| { + let _: Option<!> = for_each_expr(cx, body.value, |e| { let Some(macro_call) = root_macro_call_first_node(cx, e) else { return ControlFlow::Continue(Descend::Yes); }; diff --git a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs index 0ed957f1f2f..313e4083256 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::peel_blocks; use clippy_utils::source::{snippet, walk_span_to_context}; use clippy_utils::ty::implements_trait; -use clippy_utils::visitors::for_each_expr; +use clippy_utils::visitors::for_each_expr_without_closures; use rustc_errors::Applicability; use rustc_hir::{ Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, MatchSource, @@ -107,7 +107,7 @@ fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind && let ExprKind::Call(_, [into_future_arg]) = match_value.kind && let ctxt = expr.span.ctxt() - && for_each_expr(into_future_arg, |e| { + && for_each_expr_without_closures(into_future_arg, |e| { walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(())) }) .is_none() diff --git a/src/tools/clippy/clippy_lints/src/reference.rs b/src/tools/clippy/clippy_lints/src/reference.rs index 16086ba6637..8f32cf5f2a1 100644 --- a/src/tools/clippy/clippy_lints/src/reference.rs +++ b/src/tools/clippy/clippy_lints/src/reference.rs @@ -48,6 +48,9 @@ impl EarlyLintPass for DerefAddrOf { fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) { if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind && let ExprKind::AddrOf(_, ref mutability, ref addrof_target) = without_parens(deref_target).kind + // NOTE(tesuji): `*&` forces rustc to const-promote the array to `.rodata` section. + // See #12854 for details. + && !matches!(addrof_target.kind, ExprKind::Array(_)) && deref_target.span.eq_ctxt(e.span) && !addrof_target.span.from_expansion() { diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs index 48d6fb3c037..c11da3147ef 100644 --- a/src/tools/clippy/clippy_lints/src/returns.rs +++ b/src/tools/clippy/clippy_lints/src/returns.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{snippet_opt, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::visitors::{for_each_expr_with_closures, Descend}; +use clippy_utils::visitors::{for_each_expr, Descend}; use clippy_utils::{ binary_expr_needs_parentheses, fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res, path_to_local_id, span_contains_cfg, span_find_starting_semi, @@ -436,7 +436,7 @@ fn emit_return_lint( } fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - for_each_expr_with_closures(cx, expr, |e| { + for_each_expr(cx, expr, |e| { if let Some(def_id) = fn_def_id(cx, e) && cx .tcx diff --git a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs index 038eb92d652..979d6dc77ae 100644 --- a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs +++ b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{indent_of, snippet}; use clippy_utils::{expr_or_init, get_attr, path_to_local, peel_hir_expr_unary}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{walk_expr, Visitor}; @@ -12,6 +12,7 @@ use rustc_session::impl_lint_pass; use rustc_span::symbol::Ident; use rustc_span::{sym, Span, DUMMY_SP}; use std::borrow::Cow; +use std::collections::hash_map::Entry; declare_clippy_lint! { /// ### What it does @@ -57,7 +58,6 @@ impl_lint_pass!(SignificantDropTightening<'_> => [SIGNIFICANT_DROP_TIGHTENING]); pub struct SignificantDropTightening<'tcx> { apas: FxIndexMap<HirId, AuxParamsAttr>, /// Auxiliary structure used to avoid having to verify the same type multiple times. - seen_types: FxHashSet<Ty<'tcx>>, type_cache: FxHashMap<Ty<'tcx>, bool>, } @@ -74,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for SignificantDropTightening<'tcx> { self.apas.clear(); let initial_dummy_stmt = dummy_stmt_expr(body.value); let mut ap = AuxParams::new(&mut self.apas, &initial_dummy_stmt); - StmtsChecker::new(&mut ap, cx, &mut self.seen_types, &mut self.type_cache).visit_body(body); + StmtsChecker::new(&mut ap, cx, &mut self.type_cache).visit_body(body); for apa in ap.apas.values() { if apa.counter <= 1 || !apa.has_expensive_expr_after_last_attr { continue; @@ -142,28 +142,25 @@ impl<'tcx> LateLintPass<'tcx> for SignificantDropTightening<'tcx> { /// Checks the existence of the `#[has_significant_drop]` attribute. struct AttrChecker<'cx, 'others, 'tcx> { cx: &'cx LateContext<'tcx>, - seen_types: &'others mut FxHashSet<Ty<'tcx>>, type_cache: &'others mut FxHashMap<Ty<'tcx>, bool>, } impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> { - pub(crate) fn new( - cx: &'cx LateContext<'tcx>, - seen_types: &'others mut FxHashSet<Ty<'tcx>>, - type_cache: &'others mut FxHashMap<Ty<'tcx>, bool>, - ) -> Self { - seen_types.clear(); - Self { - cx, - seen_types, - type_cache, - } + pub(crate) fn new(cx: &'cx LateContext<'tcx>, type_cache: &'others mut FxHashMap<Ty<'tcx>, bool>) -> Self { + Self { cx, type_cache } } fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>) -> bool { - // The borrow checker prevents us from using something fancier like or_insert_with. - if let Some(ty) = self.type_cache.get(&ty) { - return *ty; + let ty = self + .cx + .tcx + .try_normalize_erasing_regions(self.cx.param_env, ty) + .unwrap_or(ty); + match self.type_cache.entry(ty) { + Entry::Occupied(e) => return *e.get(), + Entry::Vacant(e) => { + e.insert(false); + }, } let value = self.has_sig_drop_attr_uncached(ty); self.type_cache.insert(ty, value); @@ -185,7 +182,7 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> { rustc_middle::ty::Adt(a, b) => { for f in a.all_fields() { let ty = f.ty(self.cx.tcx, b); - if !self.has_seen_ty(ty) && self.has_sig_drop_attr(ty) { + if self.has_sig_drop_attr(ty) { return true; } } @@ -205,16 +202,11 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> { _ => false, } } - - fn has_seen_ty(&mut self, ty: Ty<'tcx>) -> bool { - !self.seen_types.insert(ty) - } } struct StmtsChecker<'ap, 'lc, 'others, 'stmt, 'tcx> { ap: &'ap mut AuxParams<'others, 'stmt, 'tcx>, cx: &'lc LateContext<'tcx>, - seen_types: &'others mut FxHashSet<Ty<'tcx>>, type_cache: &'others mut FxHashMap<Ty<'tcx>, bool>, } @@ -222,15 +214,9 @@ impl<'ap, 'lc, 'others, 'stmt, 'tcx> StmtsChecker<'ap, 'lc, 'others, 'stmt, 'tcx fn new( ap: &'ap mut AuxParams<'others, 'stmt, 'tcx>, cx: &'lc LateContext<'tcx>, - seen_types: &'others mut FxHashSet<Ty<'tcx>>, type_cache: &'others mut FxHashMap<Ty<'tcx>, bool>, ) -> Self { - Self { - ap, - cx, - seen_types, - type_cache, - } + Self { ap, cx, type_cache } } fn manage_has_expensive_expr_after_last_attr(&mut self) { @@ -288,7 +274,7 @@ impl<'ap, 'lc, 'others, 'stmt, 'tcx> Visitor<'tcx> for StmtsChecker<'ap, 'lc, 'o apa.counter = apa.counter.wrapping_add(1); apa.has_expensive_expr_after_last_attr = false; }; - let mut ac = AttrChecker::new(self.cx, self.seen_types, self.type_cache); + let mut ac = AttrChecker::new(self.cx, self.type_cache); if ac.has_sig_drop_attr(self.cx.typeck_results().expr_ty(expr)) { if let hir::StmtKind::Let(local) = self.ap.curr_stmt.kind && let hir::PatKind::Binding(_, hir_id, ident, _) = local.pat.kind diff --git a/src/tools/clippy/clippy_lints/src/string_patterns.rs b/src/tools/clippy/clippy_lints/src/string_patterns.rs new file mode 100644 index 00000000000..64b5b8f9f27 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/string_patterns.rs @@ -0,0 +1,227 @@ +use std::ops::ControlFlow; + +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::eager_or_lazy::switch_to_eager_eval; +use clippy_utils::macros::matching_root_macro_call; +use clippy_utils::path_to_local_id; +use clippy_utils::source::{snippet, str_literal_to_char_literal}; +use clippy_utils::visitors::{for_each_expr, Descend}; +use itertools::Itertools; +use rustc_ast::{BinOpKind, LitKind}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, PatKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::declare_lint_pass; +use rustc_span::{sym, Span}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for manual `char` comparison in string patterns + /// + /// ### Why is this bad? + /// This can be written more concisely using a `char` or an array of `char`. + /// This is more readable and more optimized when comparing to only one `char`. + /// + /// ### Example + /// ```no_run + /// "Hello World!".trim_end_matches(|c| c == '.' || c == ',' || c == '!' || c == '?'); + /// ``` + /// Use instead: + /// ```no_run + /// "Hello World!".trim_end_matches(['.', ',', '!', '?']); + /// ``` + #[clippy::version = "1.80.0"] + pub MANUAL_PATTERN_CHAR_COMPARISON, + style, + "manual char comparison in string patterns" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for string methods that receive a single-character + /// `str` as an argument, e.g., `_.split("x")`. + /// + /// ### Why is this bad? + /// While this can make a perf difference on some systems, + /// benchmarks have proven inconclusive. But at least using a + /// char literal makes it clear that we are looking at a single + /// character. + /// + /// ### Known problems + /// Does not catch multi-byte unicode characters. This is by + /// design, on many machines, splitting by a non-ascii char is + /// actually slower. Please do your own measurements instead of + /// relying solely on the results of this lint. + /// + /// ### Example + /// ```rust,ignore + /// _.split("x"); + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// _.split('x'); + /// ``` + #[clippy::version = "pre 1.29.0"] + pub SINGLE_CHAR_PATTERN, + pedantic, + "using a single-character str where a char could be used, e.g., `_.split(\"x\")`" +} + +declare_lint_pass!(StringPatterns => [MANUAL_PATTERN_CHAR_COMPARISON, SINGLE_CHAR_PATTERN]); + +const PATTERN_METHODS: [(&str, usize); 22] = [ + ("contains", 0), + ("starts_with", 0), + ("ends_with", 0), + ("find", 0), + ("rfind", 0), + ("split", 0), + ("split_inclusive", 0), + ("rsplit", 0), + ("split_terminator", 0), + ("rsplit_terminator", 0), + ("splitn", 1), + ("rsplitn", 1), + ("split_once", 0), + ("rsplit_once", 0), + ("matches", 0), + ("rmatches", 0), + ("match_indices", 0), + ("rmatch_indices", 0), + ("trim_start_matches", 0), + ("trim_end_matches", 0), + ("replace", 0), + ("replacen", 0), +]; + +fn check_single_char_pattern_lint(cx: &LateContext<'_>, arg: &Expr<'_>) { + let mut applicability = Applicability::MachineApplicable; + if let Some(hint) = str_literal_to_char_literal(cx, arg, &mut applicability, true) { + span_lint_and_sugg( + cx, + SINGLE_CHAR_PATTERN, + arg.span, + "single-character string constant used as pattern", + "consider using a `char`", + hint, + applicability, + ); + } +} + +fn get_char_span<'tcx>(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<Span> { + if cx.typeck_results().expr_ty_adjusted(expr).is_char() + && !expr.span.from_expansion() + && switch_to_eager_eval(cx, expr) + { + Some(expr.span) + } else { + None + } +} + +fn check_manual_pattern_char_comparison(cx: &LateContext<'_>, method_arg: &Expr<'_>) { + if let ExprKind::Closure(closure) = method_arg.kind + && let body = cx.tcx.hir().body(closure.body) + && let Some(PatKind::Binding(_, binding, ..)) = body.params.first().map(|p| p.pat.kind) + { + let mut set_char_spans: Vec<Span> = Vec::new(); + + // We want to retrieve all the comparisons done. + // They are ordered in a nested way and so we need to traverse the AST to collect them all. + if for_each_expr(cx, body.value, |sub_expr| -> ControlFlow<(), Descend> { + match sub_expr.kind { + ExprKind::Binary(op, left, right) if op.node == BinOpKind::Eq => { + if path_to_local_id(left, binding) + && let Some(span) = get_char_span(cx, right) + { + set_char_spans.push(span); + ControlFlow::Continue(Descend::No) + } else if path_to_local_id(right, binding) + && let Some(span) = get_char_span(cx, left) + { + set_char_spans.push(span); + ControlFlow::Continue(Descend::No) + } else { + ControlFlow::Break(()) + } + }, + ExprKind::Binary(op, _, _) if op.node == BinOpKind::Or => ControlFlow::Continue(Descend::Yes), + ExprKind::Match(match_value, [arm, _], _) => { + if matching_root_macro_call(cx, sub_expr.span, sym::matches_macro).is_none() + || arm.guard.is_some() + || !path_to_local_id(match_value, binding) + { + return ControlFlow::Break(()); + } + if arm.pat.walk_short(|pat| match pat.kind { + PatKind::Lit(expr) if let ExprKind::Lit(lit) = expr.kind => { + if let LitKind::Char(_) = lit.node { + set_char_spans.push(lit.span); + } + true + }, + PatKind::Or(_) => true, + _ => false, + }) { + ControlFlow::Continue(Descend::No) + } else { + ControlFlow::Break(()) + } + }, + _ => ControlFlow::Break(()), + } + }) + .is_some() + { + return; + } + span_lint_and_then( + cx, + MANUAL_PATTERN_CHAR_COMPARISON, + method_arg.span, + "this manual char comparison can be written more succinctly", + |diag| { + if let [set_char_span] = set_char_spans[..] { + diag.span_suggestion( + method_arg.span, + "consider using a `char`", + snippet(cx, set_char_span, "c"), + Applicability::MachineApplicable, + ); + } else { + diag.span_suggestion( + method_arg.span, + "consider using an array of `char`", + format!( + "[{}]", + set_char_spans.into_iter().map(|span| snippet(cx, span, "c")).join(", ") + ), + Applicability::MachineApplicable, + ); + } + }, + ); + } +} + +impl<'tcx> LateLintPass<'tcx> for StringPatterns { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if !expr.span.from_expansion() + && let ExprKind::MethodCall(method, receiver, args, _) = expr.kind + && let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(receiver).kind() + && ty.is_str() + && let method_name = method.ident.name.as_str() + && let Some(&(_, pos)) = PATTERN_METHODS + .iter() + .find(|(array_method_name, _)| *array_method_name == method_name) + && let Some(arg) = args.get(pos) + { + check_single_char_pattern_lint(cx, arg); + + check_manual_pattern_char_comparison(cx, arg); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs index f8d36d6b92f..7da661485ab 100644 --- a/src/tools/clippy/clippy_lints/src/strings.rs +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -399,13 +399,17 @@ impl<'tcx> LateLintPass<'tcx> for StrToString { && let ty::Ref(_, ty, ..) = ty.kind() && ty.is_str() { - span_lint_and_help( + let mut applicability = Applicability::MachineApplicable; + let snippet = snippet_with_applicability(cx, self_arg.span, "..", &mut applicability); + + span_lint_and_sugg( cx, STR_TO_STRING, expr.span, "`to_string()` called on a `&str`", - None, - "consider using `.to_owned()`", + "try", + format!("{snippet}.to_owned()"), + applicability, ); } } diff --git a/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs b/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs index 3f030b80331..744d6392e06 100644 --- a/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs +++ b/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::visitors::for_each_expr; +use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{binop_traits, trait_ref_of_method, BINOP_TRAITS, OP_ASSIGN_TRAITS}; use core::ops::ControlFlow; use rustc_hir as hir; @@ -95,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { fn count_binops(expr: &hir::Expr<'_>) -> u32 { let mut count = 0u32; - let _: Option<!> = for_each_expr(expr, |e| { + let _: Option<!> = for_each_expr_without_closures(expr, |e| { if matches!( e.kind, hir::ExprKind::Binary(..) diff --git a/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs b/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs index f98ea59a15d..b2892d136fa 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs @@ -31,7 +31,6 @@ fn get_parent_local_binding_ty<'tcx>(cx: &LateContext<'tcx>, expr_hir_id: HirId) fn is_function_block(cx: &LateContext<'_>, expr_hir_id: HirId) -> bool { let def_id = cx.tcx.hir().enclosing_body_owner(expr_hir_id); if let Some(body) = cx.tcx.hir().maybe_body_owned_by(def_id) { - let body = cx.tcx.hir().body(body.id()); return body.value.peel_blocks().hir_id == expr_hir_id; } false diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs index 598032ccdeb..aa329ec3366 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/mod.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs @@ -546,7 +546,7 @@ declare_clippy_lint! { /// let x = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); /// # } /// ``` - #[clippy::version = "1.77.0"] + #[clippy::version = "1.79.0"] pub MISSING_TRANSMUTE_ANNOTATIONS, suspicious, "warns if a transmute call doesn't have all generics specified" diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs index 6cf9229fdd0..93a1089a970 100644 --- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -3,7 +3,7 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::is_lint_allowed; use clippy_utils::source::walk_span_to_context; -use clippy_utils::visitors::{for_each_expr_with_closures, Descend}; +use clippy_utils::visitors::{for_each_expr, Descend}; use hir::HirId; use rustc_data_structures::sync::Lrc; use rustc_hir as hir; @@ -296,7 +296,7 @@ fn expr_has_unnecessary_safety_comment<'tcx>( } // this should roughly be the reverse of `block_parents_have_safety_comment` - if for_each_expr_with_closures(cx, expr, |expr| match expr.kind { + if for_each_expr(cx, expr, |expr| match expr.kind { hir::ExprKind::Block( Block { rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), diff --git a/src/tools/clippy/clippy_lints/src/unused_self.rs b/src/tools/clippy/clippy_lints/src/unused_self.rs index a67f53f00ae..3e6102f5982 100644 --- a/src/tools/clippy/clippy_lints/src/unused_self.rs +++ b/src/tools/clippy/clippy_lints/src/unused_self.rs @@ -59,7 +59,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf { let parent_item = cx.tcx.hir().expect_item(parent); let assoc_item = cx.tcx.associated_item(impl_item.owner_id); let contains_todo = |cx, body: &'_ Body<'_>| -> bool { - clippy_utils::visitors::for_each_expr(body.value, |e| { + clippy_utils::visitors::for_each_expr_without_closures(body.value, |e| { if let Some(macro_call) = root_macro_call_first_node(cx, e) { if cx.tcx.item_name(macro_call.def_id).as_str() == "todo" { ControlFlow::Break(()) diff --git a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs index 197ab0f173b..f77badd97b7 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs @@ -77,7 +77,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tc let body = cx.tcx.hir().body(body_id); let typeck = cx.tcx.typeck(impl_item.owner_id.def_id); let mut result = Vec::new(); - let _: Option<!> = for_each_expr(body.value, |e| { + let _: Option<!> = for_each_expr(cx, body.value, |e| { // check for `expect` if let Some(arglists) = method_chain_args(e, &["expect"]) { let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs(); diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 18a31abddd0..e9d69407df8 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -733,7 +733,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { match stmt.value.kind { StmtKind::Let(local) => { bind!(self, local); - kind!("Local({local})"); + kind!("Let({local})"); self.option(field!(local.init), "init", |init| { self.expr(init); }); diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs index 9be225759df..84f84781e71 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs @@ -213,7 +213,7 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { output: &mut self.registered_lints, cx, }; - let body_id = cx.tcx.hir().body_owned_by( + let body = cx.tcx.hir().body_owned_by( impl_item_refs .iter() .find(|iiref| iiref.ident.as_str() == "get_lints") @@ -222,7 +222,7 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { .owner_id .def_id, ); - collector.visit_expr(cx.tcx.hir().body(body_id).value); + collector.visit_expr(body.value); } } } diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 5c1ebb922f1..1c149f20456 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -719,7 +719,7 @@ fn get_lint_group_and_level_or_lint( Some(sym::clippy), &std::iter::once(Ident::with_dummy_span(sym::clippy)).collect(), ); - if let CheckLintNameResult::Tool(Ok(lint_lst)) = result { + if let CheckLintNameResult::Tool(lint_lst, None) = result { if let Some(group) = get_lint_group(cx, lint_lst[0]) { if EXCLUDED_LINT_GROUPS.contains(&group.as_str()) { return None; diff --git a/src/tools/clippy/clippy_lints/src/zero_repeat_side_effects.rs b/src/tools/clippy/clippy_lints/src/zero_repeat_side_effects.rs index 8796b8f61d1..ad041e55bda 100644 --- a/src/tools/clippy/clippy_lints/src/zero_repeat_side_effects.rs +++ b/src/tools/clippy/clippy_lints/src/zero_repeat_side_effects.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet; -use clippy_utils::visitors::for_each_expr; +use clippy_utils::visitors::for_each_expr_without_closures; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{ExprKind, Node}; @@ -36,7 +36,7 @@ declare_clippy_lint! { /// side_effect(); /// let a: [i32; 0] = []; /// ``` - #[clippy::version = "1.75.0"] + #[clippy::version = "1.79.0"] pub ZERO_REPEAT_SIDE_EFFECTS, suspicious, "usage of zero-sized initializations of arrays or vecs causing side effects" @@ -65,7 +65,7 @@ impl LateLintPass<'_> for ZeroRepeatSideEffects { fn inner_check(cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>, inner_expr: &'_ rustc_hir::Expr<'_>, is_vec: bool) { // check if expr is a call or has a call inside it - if for_each_expr(inner_expr, |x| { + if for_each_expr_without_closures(inner_expr, |x| { if let ExprKind::Call(_, _) | ExprKind::MethodCall(_, _, _, _) = x.kind { std::ops::ControlFlow::Break(()) } else { diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml index ab883c25338..3a3aeb88216 100644 --- a/src/tools/clippy/clippy_utils/Cargo.toml +++ b/src/tools/clippy/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.80" +version = "0.1.81" edition = "2021" publish = false diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index e9e1aa7e445..cfd142fe1ff 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -412,7 +412,8 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { /// Simple constant folding: Insert an expression, get a constant or none. pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant<'tcx>> { match e.kind { - ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.lcx.tcx.hir().body(body).value), ExprKind::DropTemps(e) => self.expr(e), + ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.lcx.tcx.hir().body(body).value), + ExprKind::DropTemps(e) => self.expr(e), ExprKind::Path(ref qpath) => { self.fetch_path_and_apply(qpath, e.hir_id, self.typeck_results.expr_ty(e), |this, result| { let result = mir_to_const(this.lcx, result)?; @@ -490,7 +491,8 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { /// leaves the local crate. pub fn expr_is_empty(&mut self, e: &Expr<'_>) -> Option<bool> { match e.kind { - ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr_is_empty(self.lcx.tcx.hir().body(body).value), ExprKind::DropTemps(e) => self.expr_is_empty(e), + ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr_is_empty(self.lcx.tcx.hir().body(body).value), + ExprKind::DropTemps(e) => self.expr_is_empty(e), ExprKind::Path(ref qpath) => { if !self .typeck_results @@ -811,12 +813,8 @@ pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)), ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)), ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.to_bits(int.size()))), - ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits( - int.try_into().expect("invalid f32 bit representation"), - ))), - ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits( - int.try_into().expect("invalid f64 bit representation"), - ))), + ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits(int.into()))), + ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits(int.into()))), ty::RawPtr(_, _) => Some(Constant::RawPtr(int.to_bits(int.size()))), _ => None, }, diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 36634817fc9..50dd8430ac0 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -7,9 +7,9 @@ use rustc_data_structures::fx::FxHasher; use rustc_hir::def::Res; use rustc_hir::MatchSource::TryDesugar; use rustc_hir::{ - ArrayLen, BinOpKind, BindingMode, Block, BodyId, Closure, Expr, ExprField, ExprKind, FnRetTy, GenericArg, - GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeName, Pat, PatField, PatKind, Path, - PathSegment, PrimTy, QPath, Stmt, StmtKind, Ty, TyKind, AssocItemConstraint, + ArrayLen, AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, Expr, ExprField, ExprKind, FnRetTy, + GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeName, Pat, PatField, + PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, Ty, TyKind, }; use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::LateContext; @@ -519,7 +519,11 @@ impl HirEqInterExpr<'_, '_, '_> { } fn eq_assoc_type_binding(&mut self, left: &AssocItemConstraint<'_>, right: &AssocItemConstraint<'_>) -> bool { - left.ident.name == right.ident.name && self.eq_ty(left.ty().expect("expected assoc type binding"), right.ty().expect("expected assoc type binding")) + left.ident.name == right.ident.name + && self.eq_ty( + left.ty().expect("expected assoc type binding"), + right.ty().expect("expected assoc type binding"), + ) } fn check_ctxt(&mut self, left: SyntaxContext, right: SyntaxContext) -> bool { diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 2f6bf920967..7dc341ec8d7 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -126,7 +126,7 @@ use visitors::Visitable; use crate::consts::{constant, mir_to_const, Constant}; use crate::higher::Range; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; -use crate::visitors::for_each_expr; +use crate::visitors::for_each_expr_without_closures; use rustc_middle::hir::nested_filter; @@ -321,6 +321,15 @@ pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) .map_or(false, |trt_id| match_def_path(cx, trt_id, path)) } +/// Checks if the given method call expression calls an inherent method. +pub fn is_inherent_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { + cx.tcx.trait_of_item(method_id).is_none() + } else { + false + } +} + /// Checks if a method is defined in an impl of a diagnostic item pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool { if let Some(impl_did) = cx.tcx.impl_of_method(def_id) { @@ -1313,7 +1322,7 @@ pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext< /// Returns `true` if `expr` contains a return expression pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool { - for_each_expr(expr, |e| { + for_each_expr_without_closures(expr, |e| { if matches!(e.kind, ExprKind::Ret(..)) { ControlFlow::Break(()) } else { @@ -3392,3 +3401,14 @@ pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool { contains_block(expr, false) } + +/// Returns true if the specified expression is in a receiver position. +pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + if let Some(parent_expr) = get_parent_expr(cx, expr) + && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind + && receiver.hir_id == expr.hir_id + { + return true; + } + false +} diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs index 8daab9b0d92..455239cc37f 100644 --- a/src/tools/clippy/clippy_utils/src/macros.rs +++ b/src/tools/clippy/clippy_utils/src/macros.rs @@ -1,6 +1,6 @@ #![allow(clippy::similar_names)] // `expr` and `expn` -use crate::visitors::{for_each_expr, Descend}; +use crate::visitors::{for_each_expr_without_closures, Descend}; use arrayvec::ArrayVec; use rustc_ast::{FormatArgs, FormatArgument, FormatPlaceholder}; @@ -323,7 +323,7 @@ fn find_assert_args_inner<'a, const N: usize>( Some(inner_name) => find_assert_within_debug_assert(cx, expr, expn, Symbol::intern(inner_name))?, }; let mut args = ArrayVec::new(); - let panic_expn = for_each_expr(expr, |e| { + let panic_expn = for_each_expr_without_closures(expr, |e| { if args.is_full() { match PanicExpn::parse(e) { Some(expn) => ControlFlow::Break(expn), @@ -349,7 +349,7 @@ fn find_assert_within_debug_assert<'a>( expn: ExpnId, assert_name: Symbol, ) -> Option<(&'a Expr<'a>, ExpnId)> { - for_each_expr(expr, |e| { + for_each_expr_without_closures(expr, |e| { if !e.span.from_expansion() { return ControlFlow::Continue(Descend::No); } @@ -397,7 +397,7 @@ impl FormatArgsStorage { /// /// See also [`find_format_arg_expr`] pub fn get(&self, cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId) -> Option<&FormatArgs> { - let format_args_expr = for_each_expr(start, |expr| { + let format_args_expr = for_each_expr_without_closures(start, |expr| { let ctxt = expr.span.ctxt(); if ctxt.outer_expn().is_descendant_of(expn_id) { if macro_backtrace(expr.span) @@ -439,7 +439,7 @@ pub fn find_format_arg_expr<'hir, 'ast>( parent: _, } = target.expr.span.data(); - for_each_expr(start, |expr| { + for_each_expr_without_closures(start, |expr| { // When incremental compilation is enabled spans gain a parent during AST to HIR lowering, // since we're comparing an AST span to a HIR one we need to ignore the parent field let data = expr.span.data(); diff --git a/src/tools/clippy/clippy_utils/src/ptr.rs b/src/tools/clippy/clippy_utils/src/ptr.rs index 88837d8a143..991ea428dc3 100644 --- a/src/tools/clippy/clippy_utils/src/ptr.rs +++ b/src/tools/clippy/clippy_utils/src/ptr.rs @@ -1,5 +1,5 @@ use crate::source::snippet; -use crate::visitors::{for_each_expr, Descend}; +use crate::visitors::{for_each_expr_without_closures, Descend}; use crate::{path_to_local_id, strip_pat_refs}; use core::ops::ControlFlow; use rustc_hir::{Body, BodyId, ExprKind, HirId, PatKind}; @@ -31,7 +31,7 @@ fn extract_clone_suggestions<'tcx>( body: &'tcx Body<'_>, ) -> Option<Vec<(Span, Cow<'static, str>)>> { let mut spans = Vec::new(); - for_each_expr(body, |e| { + for_each_expr_without_closures(body, |e| { if let ExprKind::MethodCall(seg, recv, [], _) = e.kind && path_to_local_id(recv, id) { diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 81e94725a70..42b10f69c0c 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -40,9 +40,13 @@ pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv) )?; for bb in &*body.basic_blocks { - check_terminator(tcx, body, bb.terminator(), msrv)?; - for stmt in &bb.statements { - check_statement(tcx, body, def_id, stmt, msrv)?; + // Cleanup blocks are ignored entirely by const eval, so we can too: + // https://github.com/rust-lang/rust/blob/1dea922ea6e74f99a0e97de5cdb8174e4dea0444/compiler/rustc_const_eval/src/transform/check_consts/check.rs#L382 + if !bb.is_cleanup { + check_terminator(tcx, body, bb.terminator(), msrv)?; + for stmt in &bb.statements { + check_statement(tcx, body, def_id, stmt, msrv)?; + } } } Ok(()) diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs index fd67e039c29..e2e5cde947d 100644 --- a/src/tools/clippy/clippy_utils/src/source.rs +++ b/src/tools/clippy/clippy_utils/src/source.rs @@ -2,6 +2,7 @@ #![allow(clippy::module_name_repetitions)] +use rustc_ast::{LitKind, StrStyle}; use rustc_data_structures::sync::Lrc; use rustc_errors::Applicability; use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource}; @@ -500,6 +501,50 @@ pub fn expand_past_previous_comma(cx: &LateContext<'_>, span: Span) -> Span { extended.with_lo(extended.lo() - BytePos(1)) } +/// Converts `expr` to a `char` literal if it's a `str` literal containing a single +/// character (or a single byte with `ascii_only`) +pub fn str_literal_to_char_literal( + cx: &LateContext<'_>, + expr: &Expr<'_>, + applicability: &mut Applicability, + ascii_only: bool, +) -> Option<String> { + if let ExprKind::Lit(lit) = &expr.kind + && let LitKind::Str(r, style) = lit.node + && let string = r.as_str() + && let len = if ascii_only { + string.len() + } else { + string.chars().count() + } + && len == 1 + { + let snip = snippet_with_applicability(cx, expr.span, string, applicability); + let ch = if let StrStyle::Raw(nhash) = style { + let nhash = nhash as usize; + // for raw string: r##"a"## + &snip[(nhash + 2)..(snip.len() - 1 - nhash)] + } else { + // for regular string: "a" + &snip[1..(snip.len() - 1)] + }; + + let hint = format!( + "'{}'", + match ch { + "'" => "\\'", + r"\" => "\\\\", + "\\\"" => "\"", // no need to escape `"` in `'"'` + _ => ch, + } + ); + + Some(hint) + } else { + None + } +} + #[cfg(test)] mod test { use super::{reindent_multiline, without_block_comments}; diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index 6e5626297c9..7d4332a3d9d 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -17,9 +17,9 @@ use rustc_middle::mir::ConstValue; use rustc_middle::traits::EvaluationResult; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ - self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef, - GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, - TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, + self, AdtDef, AliasTy, AssocItem, AssocKind, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, + GenericArgsRef, GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, + TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, }; use rustc_span::symbol::Ident; use rustc_span::{sym, Span, Symbol, DUMMY_SP}; @@ -861,7 +861,6 @@ impl core::ops::Add<u32> for EnumValue { } /// Attempts to read the given constant as though it were an enum value. -#[expect(clippy::cast_possible_truncation, clippy::cast_possible_wrap)] pub fn read_explicit_enum_value(tcx: TyCtxt<'_>, id: DefId) -> Option<EnumValue> { if let Ok(ConstValue::Scalar(Scalar::Int(value))) = tcx.const_eval_poly(id) { match tcx.type_of(id).instantiate_identity().kind() { @@ -1314,3 +1313,39 @@ pub fn normalize_with_regions<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx> pub fn is_manually_drop(ty: Ty<'_>) -> bool { ty.ty_adt_def().map_or(false, AdtDef::is_manually_drop) } + +/// Returns the deref chain of a type, starting with the type itself. +pub fn deref_chain<'cx, 'tcx>(cx: &'cx LateContext<'tcx>, ty: Ty<'tcx>) -> impl Iterator<Item = Ty<'tcx>> + 'cx { + iter::successors(Some(ty), |&ty| { + if let Some(deref_did) = cx.tcx.lang_items().deref_trait() + && implements_trait(cx, ty, deref_did, &[]) + { + make_normalized_projection(cx.tcx, cx.param_env, deref_did, sym::Target, [ty]) + } else { + None + } + }) +} + +/// Checks if a Ty<'_> has some inherent method Symbol. +/// This does not look for impls in the type's `Deref::Target` type. +/// If you need this, you should wrap this call in `clippy_utils::ty::deref_chain().any(...)`. +pub fn get_adt_inherent_method<'a>(cx: &'a LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> Option<&'a AssocItem> { + if let Some(ty_did) = ty.ty_adt_def().map(AdtDef::did) { + cx.tcx + .inherent_impls(ty_did) + .into_iter() + .flatten() + .map(|&did| { + cx.tcx + .associated_items(did) + .filter_by_name_unhygienic(method_name) + .next() + .filter(|item| item.kind == AssocKind::Fn) + }) + .next() + .flatten() + } else { + None + } +} diff --git a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs index 80219303450..cba61c841ef 100644 --- a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs @@ -206,8 +206,18 @@ fn path_segment_certainty( // Checking `res_generics_def_id(..)` before calling `generics_of` avoids an ICE. if cx.tcx.res_generics_def_id(path_segment.res).is_some() { let generics = cx.tcx.generics_of(def_id); - let count = generics.own_params.len() - usize::from(generics.host_effect_index.is_some()); - let lhs = if (parent_certainty.is_certain() || generics.parent_count == 0) && count == 0 { + + let own_count = generics.own_params.len() + - usize::from(generics.host_effect_index.is_some_and(|index| { + // Check that the host index actually belongs to this resolution. + // E.g. for `Add::add`, host_effect_index is `Some(2)`, but it's part of the parent `Add` + // trait's generics. + // Add params: [Self#0, Rhs#1, host#2] parent_count=0, count=3 + // Add::add params: [] parent_count=3, count=3 + // (3..3).contains(&host_effect_index) => false + (generics.parent_count..generics.count()).contains(&index) + })); + let lhs = if (parent_certainty.is_certain() || generics.parent_count == 0) && own_count == 0 { Certainty::Certain(None) } else { Certainty::Uncertain diff --git a/src/tools/clippy/clippy_utils/src/usage.rs b/src/tools/clippy/clippy_utils/src/usage.rs index 2a25d51d8e5..fbf3d95365a 100644 --- a/src/tools/clippy/clippy_utils/src/usage.rs +++ b/src/tools/clippy/clippy_utils/src/usage.rs @@ -1,4 +1,4 @@ -use crate::visitors::{for_each_expr, for_each_expr_with_closures, Descend, Visitable}; +use crate::visitors::{for_each_expr, for_each_expr_without_closures, Descend, Visitable}; use crate::{self as utils, get_enclosing_loop_or_multi_call_closure}; use core::ops::ControlFlow; use hir::def::Res; @@ -145,7 +145,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> { } pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool { - for_each_expr(expression, |e| { + for_each_expr_without_closures(expression, |e| { match e.kind { ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => ControlFlow::Break(()), // Something special could be done here to handle while or for loop @@ -159,7 +159,7 @@ pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool { } pub fn local_used_in<'tcx>(cx: &LateContext<'tcx>, local_id: HirId, v: impl Visitable<'tcx>) -> bool { - for_each_expr_with_closures(cx, v, |e| { + for_each_expr(cx, v, |e| { if utils::path_to_local_id(e, local_id) { ControlFlow::Break(()) } else { @@ -184,7 +184,7 @@ pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr let loop_start = get_enclosing_loop_or_multi_call_closure(cx, after).map(|e| e.hir_id); let mut past_expr = false; - for_each_expr_with_closures(cx, block, |e| { + for_each_expr(cx, block, |e| { if past_expr { if utils::path_to_local_id(e, local_id) { ControlFlow::Break(()) diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index 90b56297bb5..3a39e178515 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -100,7 +100,7 @@ visitable_ref!(Stmt, visit_stmt); /// Calls the given function once for each expression contained. This does not enter any bodies or /// nested items. -pub fn for_each_expr<'tcx, B, C: Continue>( +pub fn for_each_expr_without_closures<'tcx, B, C: Continue>( node: impl Visitable<'tcx>, f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>, ) -> Option<B> { @@ -134,7 +134,7 @@ pub fn for_each_expr<'tcx, B, C: Continue>( /// Calls the given function once for each expression contained. This will enter bodies, but not /// nested items. -pub fn for_each_expr_with_closures<'tcx, B, C: Continue>( +pub fn for_each_expr<'tcx, B, C: Continue>( cx: &LateContext<'tcx>, node: impl Visitable<'tcx>, f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>, @@ -181,7 +181,7 @@ pub fn for_each_expr_with_closures<'tcx, B, C: Continue>( /// returns `true` if expr contains match expr desugared from try fn contains_try(expr: &Expr<'_>) -> bool { - for_each_expr(expr, |e| { + for_each_expr_without_closures(expr, |e| { if matches!(e.kind, ExprKind::Match(_, _, hir::MatchSource::TryDesugar(_))) { ControlFlow::Break(()) } else { @@ -286,7 +286,7 @@ where /// Checks if the given resolved path is used in the given body. pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool { - for_each_expr_with_closures(cx, cx.tcx.hir().body(body).value, |e| { + for_each_expr(cx, cx.tcx.hir().body(body).value, |e| { if let ExprKind::Path(p) = &e.kind { if cx.qpath_res(p, e.hir_id) == res { return ControlFlow::Break(()); @@ -299,7 +299,7 @@ pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool { /// Checks if the given local is used. pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool { - for_each_expr_with_closures(cx, visitable, |e| { + for_each_expr(cx, visitable, |e| { if path_to_local_id(e, id) { ControlFlow::Break(()) } else { @@ -757,7 +757,7 @@ pub fn for_each_local_assignment<'tcx, B>( } pub fn contains_break_or_continue(expr: &Expr<'_>) -> bool { - for_each_expr(expr, |e| { + for_each_expr_without_closures(expr, |e| { if matches!(e.kind, ExprKind::Break(..) | ExprKind::Continue(..)) { ControlFlow::Break(()) } else { @@ -776,7 +776,7 @@ pub fn local_used_once<'tcx>( ) -> Option<&'tcx Expr<'tcx>> { let mut expr = None; - let cf = for_each_expr_with_closures(cx, visitable, |e| { + let cf = for_each_expr(cx, visitable, |e| { if path_to_local_id(e, id) && expr.replace(e).is_some() { ControlFlow::Break(()) } else { diff --git a/src/tools/clippy/declare_clippy_lint/Cargo.toml b/src/tools/clippy/declare_clippy_lint/Cargo.toml index c8c734c3a7c..86d945c14a5 100644 --- a/src/tools/clippy/declare_clippy_lint/Cargo.toml +++ b/src/tools/clippy/declare_clippy_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.80" +version = "0.1.81" edition = "2021" publish = false diff --git a/src/tools/clippy/lintcheck/README.md b/src/tools/clippy/lintcheck/README.md index 37cc0453809..61b581ba0fa 100644 --- a/src/tools/clippy/lintcheck/README.md +++ b/src/tools/clippy/lintcheck/README.md @@ -1,6 +1,6 @@ ## `cargo lintcheck` -Runs clippy on a fixed set of crates read from +Runs Clippy on a fixed set of crates read from `lintcheck/lintcheck_crates.toml` and saves logs of the lint warnings into the repo. We can then check the diff and spot new or disappearing warnings. @@ -84,7 +84,7 @@ This lets us spot bad suggestions or false positives automatically in some cases > Note: Fix mode implies `--all-targets`, so it can fix as much code as it can. -Please note that the target dir should be cleaned afterwards since clippy will modify +Please note that the target dir should be cleaned afterwards since Clippy will modify the downloaded sources which can lead to unexpected results when running lintcheck again afterwards. ### Recursive mode diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index dd8b9ece773..842c2f3de0d 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2024-05-30" +channel = "nightly-2024-06-13" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr index 9177e99f8e6..4fe7f6f7a9e 100644 --- a/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr +++ b/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr @@ -1,17 +1,18 @@ error: lint group `rust_2018_idioms` has the same priority (0) as a lint - --> Cargo.toml:7:1 - | -7 | rust_2018_idioms = "warn" - | ^^^^^^^^^^^^^^^^ ------ has an implicit priority of 0 -8 | bare_trait_objects = "allow" - | ------------------ has the same priority as this lint - | - = note: the order of the lints in the table is ignored by Cargo - = note: `#[deny(clippy::lint_groups_priority)]` on by default + --> Cargo.toml:7:1 + | +7 | rust_2018_idioms = "warn" + | ^^^^^^^^^^^^^^^^ ------ has an implicit priority of 0 +... +12 | unused_attributes = { level = "allow" } + | ----------------- has the same priority as this lint + | + = note: the order of the lints in the table is ignored by Cargo + = note: `#[deny(clippy::lint_groups_priority)]` on by default help: to have lints override the group set `rust_2018_idioms` to a lower priority - | -7 | rust_2018_idioms = { level = "warn", priority = -1 } - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + | +7 | rust_2018_idioms = { level = "warn", priority = -1 } + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: lint group `unused` has the same priority (0) as a lint --> Cargo.toml:10:1 @@ -29,45 +30,45 @@ help: to have lints override the group set `unused` to a lower priority | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: lint group `pedantic` has the same priority (-1) as a lint - --> Cargo.toml:19:1 + --> Cargo.toml:15:1 | -19 | pedantic = { level = "warn", priority = -1 } +15 | pedantic = { level = "warn", priority = -1 } | ^^^^^^^^ -20 | similar_names = { level = "allow", priority = -1 } +16 | similar_names = { level = "allow", priority = -1 } | ------------- has the same priority as this lint | = note: the order of the lints in the table is ignored by Cargo help: to have lints override the group set `pedantic` to a lower priority | -19 | pedantic = { level = "warn", priority = -2 } +15 | pedantic = { level = "warn", priority = -2 } | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: lint group `rust_2018_idioms` has the same priority (0) as a lint - --> Cargo.toml:23:1 + --> Cargo.toml:19:1 | -23 | rust_2018_idioms = "warn" +19 | rust_2018_idioms = "warn" | ^^^^^^^^^^^^^^^^ ------ has an implicit priority of 0 -24 | bare_trait_objects = "allow" +20 | bare_trait_objects = "allow" | ------------------ has the same priority as this lint | = note: the order of the lints in the table is ignored by Cargo help: to have lints override the group set `rust_2018_idioms` to a lower priority | -23 | rust_2018_idioms = { level = "warn", priority = -1 } +19 | rust_2018_idioms = { level = "warn", priority = -1 } | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: lint group `pedantic` has the same priority (0) as a lint - --> Cargo.toml:27:1 + --> Cargo.toml:23:1 | -27 | pedantic = "warn" +23 | pedantic = "warn" | ^^^^^^^^ ------ has an implicit priority of 0 -28 | similar_names = "allow" +24 | similar_names = "allow" | ------------- has the same priority as this lint | = note: the order of the lints in the table is ignored by Cargo help: to have lints override the group set `pedantic` to a lower priority | -27 | pedantic = { level = "warn", priority = -1 } +23 | pedantic = { level = "warn", priority = -1 } | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: could not compile `fail` (lib) due to 5 previous errors diff --git a/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.toml b/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.toml index e4d4af9cd23..c87662f822e 100644 --- a/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.toml +++ b/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.toml @@ -11,10 +11,6 @@ unused = { level = "deny" } unused_braces = { level = "allow", priority = 1 } unused_attributes = { level = "allow" } -# `warnings` is not a group so the order it is passed does not matter -warnings = "deny" -deprecated = "allow" - [lints.clippy] pedantic = { level = "warn", priority = -1 } similar_names = { level = "allow", priority = -1 } diff --git a/src/tools/clippy/tests/ui-cargo/lint_groups_priority/pass/Cargo.toml b/src/tools/clippy/tests/ui-cargo/lint_groups_priority/pass/Cargo.toml index e9fcf803d93..979c915cf0c 100644 --- a/src/tools/clippy/tests/ui-cargo/lint_groups_priority/pass/Cargo.toml +++ b/src/tools/clippy/tests/ui-cargo/lint_groups_priority/pass/Cargo.toml @@ -3,6 +3,13 @@ name = "pass" version = "0.1.0" publish = false +[lints.rust] +# Warnings does not conflict with any group or lint +warnings = "deny" +# Groups & lints at the same level do not conflict +rust_2018_idioms = "warn" +unsafe_code = "warn" + [lints.clippy] pedantic = { level = "warn", priority = -1 } style = { level = "warn", priority = 1 } diff --git a/src/tools/clippy/tests/ui-toml/private-doc-errors/doc_lints.rs b/src/tools/clippy/tests/ui-toml/private-doc-errors/doc_lints.rs index ae4c3f84c29..79c8751468d 100644 --- a/src/tools/clippy/tests/ui-toml/private-doc-errors/doc_lints.rs +++ b/src/tools/clippy/tests/ui-toml/private-doc-errors/doc_lints.rs @@ -47,7 +47,7 @@ pub mod __macro { pub struct T; impl T { pub unsafe fn f() {} - //~^ ERROR: unsafe function's docs miss `# Safety` section + //~^ ERROR: unsafe function's docs are missing a `# Safety` section } } diff --git a/src/tools/clippy/tests/ui-toml/private-doc-errors/doc_lints.stderr b/src/tools/clippy/tests/ui-toml/private-doc-errors/doc_lints.stderr index 65ec1a7bebb..a8ee09b9df7 100644 --- a/src/tools/clippy/tests/ui-toml/private-doc-errors/doc_lints.stderr +++ b/src/tools/clippy/tests/ui-toml/private-doc-errors/doc_lints.stderr @@ -51,7 +51,7 @@ note: the lint level is defined here LL | clippy::missing_panics_doc | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: unsafe function's docs miss `# Safety` section +error: unsafe function's docs are missing a `# Safety` section --> tests/ui-toml/private-doc-errors/doc_lints.rs:49:9 | LL | pub unsafe fn f() {} diff --git a/src/tools/clippy/tests/ui-toml/type_repetition_in_bounds/main.rs b/src/tools/clippy/tests/ui-toml/type_repetition_in_bounds/main.rs index 2454c10382d..7f93d2071c9 100644 --- a/src/tools/clippy/tests/ui-toml/type_repetition_in_bounds/main.rs +++ b/src/tools/clippy/tests/ui-toml/type_repetition_in_bounds/main.rs @@ -1,3 +1,4 @@ +#![allow(clippy::needless_maybe_sized)] #![warn(clippy::type_repetition_in_bounds)] fn f<T>() diff --git a/src/tools/clippy/tests/ui-toml/type_repetition_in_bounds/main.stderr b/src/tools/clippy/tests/ui-toml/type_repetition_in_bounds/main.stderr index 6005f76b94b..c5102c39d1c 100644 --- a/src/tools/clippy/tests/ui-toml/type_repetition_in_bounds/main.stderr +++ b/src/tools/clippy/tests/ui-toml/type_repetition_in_bounds/main.stderr @@ -1,5 +1,5 @@ error: this type has already been used as a bound predicate - --> tests/ui-toml/type_repetition_in_bounds/main.rs:13:5 + --> tests/ui-toml/type_repetition_in_bounds/main.rs:14:5 | LL | T: Unpin + PartialEq, | ^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/author.stdout b/src/tools/clippy/tests/ui/author.stdout index d448db097a7..eed704e82fe 100644 --- a/src/tools/clippy/tests/ui/author.stdout +++ b/src/tools/clippy/tests/ui/author.stdout @@ -1,4 +1,4 @@ -if let StmtKind::Local(local) = stmt.kind +if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::Cast(expr, cast_ty) = init.kind && let TyKind::Path(ref qpath) = cast_ty.kind diff --git a/src/tools/clippy/tests/ui/author/blocks.stdout b/src/tools/clippy/tests/ui/author/blocks.stdout index 80b928dd6cb..6bf48d5ba4e 100644 --- a/src/tools/clippy/tests/ui/author/blocks.stdout +++ b/src/tools/clippy/tests/ui/author/blocks.stdout @@ -1,12 +1,12 @@ if let ExprKind::Block(block, None) = expr.kind && block.stmts.len() == 3 - && let StmtKind::Local(local) = block.stmts[0].kind + && let StmtKind::Let(local) = block.stmts[0].kind && let Some(init) = local.init && let ExprKind::Lit(ref lit) = init.kind && let LitKind::Int(42, LitIntType::Signed(IntTy::I32)) = lit.node && let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind && name.as_str() == "x" - && let StmtKind::Local(local1) = block.stmts[1].kind + && let StmtKind::Let(local1) = block.stmts[1].kind && let Some(init1) = local1.init && let ExprKind::Lit(ref lit1) = init1.kind && let LitKind::Float(_, LitFloatType::Suffixed(FloatTy::F32)) = lit1.node @@ -22,7 +22,7 @@ if let ExprKind::Block(block, None) = expr.kind } if let ExprKind::Block(block, None) = expr.kind && block.stmts.len() == 1 - && let StmtKind::Local(local) = block.stmts[0].kind + && let StmtKind::Let(local) = block.stmts[0].kind && let Some(init) = local.init && let ExprKind::Call(func, args) = init.kind && let ExprKind::Path(ref qpath) = func.kind diff --git a/src/tools/clippy/tests/ui/author/call.stdout b/src/tools/clippy/tests/ui/author/call.stdout index f040f6330a6..59d4da490fe 100644 --- a/src/tools/clippy/tests/ui/author/call.stdout +++ b/src/tools/clippy/tests/ui/author/call.stdout @@ -1,4 +1,4 @@ -if let StmtKind::Local(local) = stmt.kind +if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::Call(func, args) = init.kind && let ExprKind::Path(ref qpath) = func.kind diff --git a/src/tools/clippy/tests/ui/author/if.stdout b/src/tools/clippy/tests/ui/author/if.stdout index 5d79618820d..a85dcddd331 100644 --- a/src/tools/clippy/tests/ui/author/if.stdout +++ b/src/tools/clippy/tests/ui/author/if.stdout @@ -1,4 +1,4 @@ -if let StmtKind::Local(local) = stmt.kind +if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::If(cond, then, Some(else_expr)) = init.kind && let ExprKind::DropTemps(expr) = cond.kind diff --git a/src/tools/clippy/tests/ui/author/issue_3849.stdout b/src/tools/clippy/tests/ui/author/issue_3849.stdout index 32a3127b85a..a5a8c0304ee 100644 --- a/src/tools/clippy/tests/ui/author/issue_3849.stdout +++ b/src/tools/clippy/tests/ui/author/issue_3849.stdout @@ -1,4 +1,4 @@ -if let StmtKind::Local(local) = stmt.kind +if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::Call(func, args) = init.kind && let ExprKind::Path(ref qpath) = func.kind diff --git a/src/tools/clippy/tests/ui/author/loop.stdout b/src/tools/clippy/tests/ui/author/loop.stdout index 631105a2238..609d2491061 100644 --- a/src/tools/clippy/tests/ui/author/loop.stdout +++ b/src/tools/clippy/tests/ui/author/loop.stdout @@ -12,7 +12,7 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo && let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node && let ExprKind::Block(block, None) = body.kind && block.stmts.len() == 1 - && let StmtKind::Local(local) = block.stmts[0].kind + && let StmtKind::Let(local) = block.stmts[0].kind && let Some(init) = local.init && let ExprKind::Path(ref qpath1) = init.kind && match_qpath(qpath1, &["y"]) diff --git a/src/tools/clippy/tests/ui/author/macro_in_closure.stdout b/src/tools/clippy/tests/ui/author/macro_in_closure.stdout index b90c830e030..66caf382d89 100644 --- a/src/tools/clippy/tests/ui/author/macro_in_closure.stdout +++ b/src/tools/clippy/tests/ui/author/macro_in_closure.stdout @@ -1,4 +1,4 @@ -if let StmtKind::Local(local) = stmt.kind +if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::Closure { capture_clause: CaptureBy::Ref, fn_decl: fn_decl, body: body_id, closure_kind: ClosureKind::Closure, .. } = init.kind && let FnRetTy::DefaultReturn(_) = fn_decl.output diff --git a/src/tools/clippy/tests/ui/author/matches.stdout b/src/tools/clippy/tests/ui/author/matches.stdout index 30e4a9b2560..91b3b6f6877 100644 --- a/src/tools/clippy/tests/ui/author/matches.stdout +++ b/src/tools/clippy/tests/ui/author/matches.stdout @@ -1,4 +1,4 @@ -if let StmtKind::Local(local) = stmt.kind +if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::Match(scrutinee, arms, MatchSource::Normal) = init.kind && let ExprKind::Lit(ref lit) = scrutinee.kind @@ -16,7 +16,7 @@ if let StmtKind::Local(local) = stmt.kind && arms[1].guard.is_none() && let ExprKind::Block(block, None) = arms[1].body.kind && block.stmts.len() == 1 - && let StmtKind::Local(local1) = block.stmts[0].kind + && let StmtKind::Let(local1) = block.stmts[0].kind && let Some(init1) = local1.init && let ExprKind::Lit(ref lit4) = init1.kind && let LitKind::Int(3, LitIntType::Unsuffixed) = lit4.node 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 79a95d775b1..4c3df472269 100644 --- a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs +++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs @@ -169,3 +169,16 @@ pub fn derive_ignored_unit_pattern(_: TokenStream) -> TokenStream { } } } + +#[proc_macro_derive(NonCanonicalClone)] +pub fn non_canonical_clone_derive(_: TokenStream) -> TokenStream { + quote! { + struct NonCanonicalClone; + impl Clone for NonCanonicalClone { + fn clone(&self) -> Self { + todo!() + } + } + impl Copy for NonCanonicalClone {} + } +} diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs index 6e6919cd295..ed7412f7c40 100644 --- a/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs +++ b/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs @@ -58,7 +58,7 @@ fn group_with_span(delimiter: Delimiter, stream: TokenStream, span: Span) -> Gro const ESCAPE_CHAR: char = '$'; /// Takes a single token followed by a sequence of tokens. Returns the sequence of tokens with their -/// span set to that of the first token. Tokens may be escaped with either `#ident` or `#(tokens)`. +/// span set to that of the first token. Tokens may be escaped with either `$ident` or `$(tokens)`. #[proc_macro] pub fn with_span(input: TokenStream) -> TokenStream { let mut iter = input.into_iter(); @@ -72,7 +72,7 @@ pub fn with_span(input: TokenStream) -> TokenStream { } /// Takes a sequence of tokens and return the tokens with the span set such that they appear to be -/// from an external macro. Tokens may be escaped with either `#ident` or `#(tokens)`. +/// from an external macro. Tokens may be escaped with either `$ident` or `$(tokens)`. #[proc_macro] pub fn external(input: TokenStream) -> TokenStream { let mut res = TokenStream::new(); @@ -84,7 +84,7 @@ pub fn external(input: TokenStream) -> TokenStream { } /// Copies all the tokens, replacing all their spans with the given span. Tokens can be escaped -/// either by `#ident` or `#(tokens)`. +/// either by `$ident` or `$(tokens)`. fn write_with_span(s: Span, mut input: IntoIter, out: &mut TokenStream) -> Result<()> { while let Some(tt) = input.next() { match tt { diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions.fixed b/src/tools/clippy/tests/ui/blocks_in_conditions.fixed index a2da5f9c5fb..af8e65270d0 100644 --- a/src/tools/clippy/tests/ui/blocks_in_conditions.fixed +++ b/src/tools/clippy/tests/ui/blocks_in_conditions.fixed @@ -117,4 +117,17 @@ mod issue_12016 { } } +fn in_closure() { + let v = vec![1, 2, 3]; + if v.into_iter() + .filter(|x| { + let y = x + 1; + y > 3 + }) + .any(|x| x == 5) + { + println!("contains 4!"); + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions.rs b/src/tools/clippy/tests/ui/blocks_in_conditions.rs index 608ca4cf267..6adae951a29 100644 --- a/src/tools/clippy/tests/ui/blocks_in_conditions.rs +++ b/src/tools/clippy/tests/ui/blocks_in_conditions.rs @@ -117,4 +117,17 @@ mod issue_12016 { } } +fn in_closure() { + let v = vec![1, 2, 3]; + if v.into_iter() + .filter(|x| { + let y = x + 1; + y > 3 + }) + .any(|x| x == 5) + { + println!("contains 4!"); + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions_closure.rs b/src/tools/clippy/tests/ui/blocks_in_conditions_closure.rs deleted file mode 100644 index db31e4ae1a9..00000000000 --- a/src/tools/clippy/tests/ui/blocks_in_conditions_closure.rs +++ /dev/null @@ -1,89 +0,0 @@ -#![warn(clippy::blocks_in_conditions)] -#![allow( - unused, - clippy::let_and_return, - clippy::needless_if, - clippy::unnecessary_literal_unwrap -)] - -fn predicate<F: FnOnce(T) -> bool, T>(pfn: F, val: T) -> bool { - pfn(val) -} - -fn pred_test() { - let v = 3; - let sky = "blue"; - // This is a sneaky case, where the block isn't directly in the condition, - // but is actually inside a closure that the condition is using. - // The same principle applies -- add some extra expressions to make sure - // linter isn't confused by them. - if v == 3 - && sky == "blue" - && predicate( - |x| { - //~^ ERROR: in an `if` condition, avoid complex blocks or closures with blocks - //~| NOTE: `-D clippy::blocks-in-conditions` implied by `-D warnings` - let target = 3; - x == target - }, - v, - ) - {} - - if predicate( - |x| { - //~^ ERROR: in an `if` condition, avoid complex blocks or closures with blocks; in - let target = 3; - x == target - }, - v, - ) {} -} - -fn closure_without_block() { - if predicate(|x| x == 3, 6) {} -} - -fn macro_in_closure() { - let option = Some(true); - - if option.unwrap_or_else(|| unimplemented!()) { - unimplemented!() - } -} - -fn closure(_: impl FnMut()) -> bool { - true -} - -fn function_with_empty_closure() { - if closure(|| {}) {} -} - -// issue #11814 -fn match_with_pred() { - let v = 3; - match Some(predicate( - |x| { - //~^ ERROR: in a `match` scrutinee, avoid complex blocks or closures with blocks - let target = 3; - x == target - }, - v, - )) { - Some(true) => 1, - Some(false) => 2, - None => 3, - }; -} - -#[rustfmt::skip] -fn main() { - let mut range = 0..10; - range.all(|i| {i < 10} ); - - let v = vec![1, 2, 3]; - if v.into_iter().any(|x| {x == 4}) { - println!("contains 4!"); - } -} diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions_closure.stderr b/src/tools/clippy/tests/ui/blocks_in_conditions_closure.stderr deleted file mode 100644 index 2faae680ec0..00000000000 --- a/src/tools/clippy/tests/ui/blocks_in_conditions_closure.stderr +++ /dev/null @@ -1,39 +0,0 @@ -error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> tests/ui/blocks_in_conditions_closure.rs:23:17 - | -LL | |x| { - | _________________^ -LL | | -LL | | -LL | | let target = 3; -LL | | x == target -LL | | }, - | |_____________^ - | - = note: `-D clippy::blocks-in-conditions` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::blocks_in_conditions)]` - -error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> tests/ui/blocks_in_conditions_closure.rs:34:13 - | -LL | |x| { - | _____________^ -LL | | -LL | | let target = 3; -LL | | x == target -LL | | }, - | |_________^ - -error: in a `match` scrutinee, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> tests/ui/blocks_in_conditions_closure.rs:67:13 - | -LL | |x| { - | _____________^ -LL | | -LL | | let target = 3; -LL | | x == target -LL | | }, - | |_________^ - -error: aborting due to 3 previous errors - diff --git a/src/tools/clippy/tests/ui/cfg_features.fixed b/src/tools/clippy/tests/ui/cfg_features.fixed deleted file mode 100644 index 0fe38f169f9..00000000000 --- a/src/tools/clippy/tests/ui/cfg_features.fixed +++ /dev/null @@ -1,29 +0,0 @@ -#![warn(clippy::maybe_misused_cfg)] - -fn main() { - #[cfg(feature = "not-really-a-feature")] - //~^ ERROR: 'feature' may be misspelled as 'features' - //~| NOTE: `-D clippy::maybe-misused-cfg` implied by `-D warnings` - let _ = 1 + 2; - - #[cfg(all(feature = "right", feature = "wrong"))] - //~^ ERROR: 'feature' may be misspelled as 'features' - let _ = 1 + 2; - - #[cfg(all(feature = "wrong1", any(feature = "right", feature = "wrong2", feature, features)))] - //~^ ERROR: 'feature' may be misspelled as 'features' - //~| ERROR: 'feature' may be misspelled as 'features' - let _ = 1 + 2; - - #[cfg(test)] - //~^ ERROR: 'test' may be misspelled as 'tests' - let _ = 2; - #[cfg(test)] - //~^ ERROR: 'test' may be misspelled as 'Test' - let _ = 2; - - #[cfg(all(test, test))] - //~^ ERROR: 'test' may be misspelled as 'tests' - //~| ERROR: 'test' may be misspelled as 'Test' - let _ = 2; -} diff --git a/src/tools/clippy/tests/ui/cfg_features.rs b/src/tools/clippy/tests/ui/cfg_features.rs deleted file mode 100644 index 9c0db035eac..00000000000 --- a/src/tools/clippy/tests/ui/cfg_features.rs +++ /dev/null @@ -1,29 +0,0 @@ -#![warn(clippy::maybe_misused_cfg)] - -fn main() { - #[cfg(features = "not-really-a-feature")] - //~^ ERROR: 'feature' may be misspelled as 'features' - //~| NOTE: `-D clippy::maybe-misused-cfg` implied by `-D warnings` - let _ = 1 + 2; - - #[cfg(all(feature = "right", features = "wrong"))] - //~^ ERROR: 'feature' may be misspelled as 'features' - let _ = 1 + 2; - - #[cfg(all(features = "wrong1", any(feature = "right", features = "wrong2", feature, features)))] - //~^ ERROR: 'feature' may be misspelled as 'features' - //~| ERROR: 'feature' may be misspelled as 'features' - let _ = 1 + 2; - - #[cfg(tests)] - //~^ ERROR: 'test' may be misspelled as 'tests' - let _ = 2; - #[cfg(Test)] - //~^ ERROR: 'test' may be misspelled as 'Test' - let _ = 2; - - #[cfg(all(tests, Test))] - //~^ ERROR: 'test' may be misspelled as 'tests' - //~| ERROR: 'test' may be misspelled as 'Test' - let _ = 2; -} diff --git a/src/tools/clippy/tests/ui/cfg_features.stderr b/src/tools/clippy/tests/ui/cfg_features.stderr deleted file mode 100644 index d576271f1a2..00000000000 --- a/src/tools/clippy/tests/ui/cfg_features.stderr +++ /dev/null @@ -1,53 +0,0 @@ -error: 'feature' may be misspelled as 'features' - --> tests/ui/cfg_features.rs:4:11 - | -LL | #[cfg(features = "not-really-a-feature")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `feature = "not-really-a-feature"` - | - = note: `-D clippy::maybe-misused-cfg` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::maybe_misused_cfg)]` - -error: 'feature' may be misspelled as 'features' - --> tests/ui/cfg_features.rs:9:34 - | -LL | #[cfg(all(feature = "right", features = "wrong"))] - | ^^^^^^^^^^^^^^^^^^ help: did you mean: `feature = "wrong"` - -error: 'feature' may be misspelled as 'features' - --> tests/ui/cfg_features.rs:13:15 - | -LL | #[cfg(all(features = "wrong1", any(feature = "right", features = "wrong2", feature, features)))] - | ^^^^^^^^^^^^^^^^^^^ help: did you mean: `feature = "wrong1"` - -error: 'feature' may be misspelled as 'features' - --> tests/ui/cfg_features.rs:13:59 - | -LL | #[cfg(all(features = "wrong1", any(feature = "right", features = "wrong2", feature, features)))] - | ^^^^^^^^^^^^^^^^^^^ help: did you mean: `feature = "wrong2"` - -error: 'test' may be misspelled as 'tests' - --> tests/ui/cfg_features.rs:18:11 - | -LL | #[cfg(tests)] - | ^^^^^ help: did you mean: `test` - -error: 'test' may be misspelled as 'Test' - --> tests/ui/cfg_features.rs:21:11 - | -LL | #[cfg(Test)] - | ^^^^ help: did you mean: `test` - -error: 'test' may be misspelled as 'tests' - --> tests/ui/cfg_features.rs:25:15 - | -LL | #[cfg(all(tests, Test))] - | ^^^^^ help: did you mean: `test` - -error: 'test' may be misspelled as 'Test' - --> tests/ui/cfg_features.rs:25:22 - | -LL | #[cfg(all(tests, Test))] - | ^^^^ help: did you mean: `test` - -error: aborting due to 8 previous errors - diff --git a/src/tools/clippy/tests/ui/crashes/ice-6252.stderr b/src/tools/clippy/tests/ui/crashes/ice-6252.stderr index ce3b9495eb1..cd2031af1c6 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-6252.stderr +++ b/src/tools/clippy/tests/ui/crashes/ice-6252.stderr @@ -4,9 +4,7 @@ error[E0412]: cannot find type `PhantomData` in this scope LL | _n: PhantomData, | ^^^^^^^^^^^ not found in this scope | -help: consider importing one of these items - | -LL + use core::marker::PhantomData; +help: consider importing this struct | LL + use std::marker::PhantomData; | diff --git a/src/tools/clippy/tests/ui/crashes/ice-9445.rs b/src/tools/clippy/tests/ui/crashes/ice-9445.rs index b6afbd33c79..c67b22f6f8c 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-9445.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-9445.rs @@ -1,5 +1,3 @@ const UNINIT: core::mem::MaybeUninit<core::cell::Cell<&'static ()>> = core::mem::MaybeUninit::uninit(); -//~^ ERROR: a `const` item should never be interior mutable -//~| NOTE: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-9445.stderr b/src/tools/clippy/tests/ui/crashes/ice-9445.stderr index d6957e9549d..76689cd6f5c 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-9445.stderr +++ b/src/tools/clippy/tests/ui/crashes/ice-9445.stderr @@ -1,11 +1,10 @@ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/crashes/ice-9445.rs:1:1 | LL | const UNINIT: core::mem::MaybeUninit<core::cell::Cell<&'static ()>> = core::mem::MaybeUninit::uninit(); - | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | make this a static item (maybe with lazy_static) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]` diff --git a/src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs b/src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs deleted file mode 100644 index 92821b6ecbb..00000000000 --- a/src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs +++ /dev/null @@ -1,33 +0,0 @@ -#![deny(clippy::mut_mut, clippy::zero_ptr)] -#![allow(dead_code)] - -// FIXME: compiletest + extern crates doesn't work together. To make this test work, it would need -// the following three lines and the lazy_static crate. -// -// #[macro_use] -// extern crate lazy_static; -// use std::collections::HashMap; - -/// ensure that we don't suggest `is_null` inside constants -/// FIXME: once const fn is stable, suggest these functions again in constants - -const BAA: *const i32 = 0 as *const i32; -static mut BAR: *const i32 = BAA; -static mut FOO: *const i32 = 0 as *const i32; - -#[allow(unused_variables, unused_mut)] -fn main() { - /* - lazy_static! { - static ref MUT_MAP : HashMap<usize, &'static str> = { - let mut m = HashMap::new(); - m.insert(0, "zero"); - m - }; - static ref MUT_COUNT : usize = MUT_MAP.len(); - } - assert_eq!(*MUT_COUNT, 1); - */ - // FIXME: don't lint in array length, requires `check_body` - //let _ = [""; (42.0 < f32::NAN) as usize]; -} diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.stderr b/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.stderr index 6c0dce6b5ea..22329172c3a 100644 --- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.stderr +++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.stderr @@ -1,87 +1,84 @@ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/enums.rs:12:1 | LL | const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true)); - | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | make this a static item (maybe with lazy_static) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]` -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/enums.rs:23:1 | LL | const UNFROZEN_VARIANT_FROM_FN: OptionalCell = unfrozen_variant(); - | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | make this a static item (maybe with lazy_static) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/enums.rs:45:1 | -LL | const NESTED_UNFROZEN_VARIANT: NestedOutermost = NestedOutermost { - | ^---- - | | - | _make this a static item (maybe with lazy_static) - | | +LL | / const NESTED_UNFROZEN_VARIANT: NestedOutermost = NestedOutermost { LL | | LL | | outer: NestedOuter::NestedInner(NestedInner { LL | | inner: NestedInnermost::Unfrozen(AtomicUsize::new(2)), LL | | }), LL | | }; | |__^ + | + = help: consider making this a static item -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/enums.rs:60:5 | LL | const TO_BE_UNFROZEN_VARIANT: OptionalCell; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/enums.rs:61:5 | LL | const TO_BE_FROZEN_VARIANT: OptionalCell; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/enums.rs:64:5 | LL | const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/enums.rs:90:5 | LL | const TO_BE_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen> = Some(Self::ToBeUnfrozen::new(4)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/enums.rs:102:5 | LL | const UNFROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/enums.rs:105:5 | LL | const GENERIC_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Generic(std::ptr::null()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/enums.rs:111:5 | LL | const NO_ENUM: Cell<*const T> = Cell::new(std::ptr::null()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/enums.rs:118:5 | LL | / const UNFROZEN_VARIANT: BothOfCellAndGeneric<Self::AssocType> = LL | | BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); | |____________________________________________________________________^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/enums.rs:120:5 | LL | const GENERIC_VARIANT: BothOfCellAndGeneric<Self::AssocType> = BothOfCellAndGeneric::Generic(std::ptr::null()); diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.stderr b/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.stderr index 9dba0c95221..1f2b9561ce5 100644 --- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.stderr +++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.stderr @@ -1,31 +1,30 @@ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/others.rs:9:1 | LL | const ATOMIC: AtomicUsize = AtomicUsize::new(5); - | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | make this a static item (maybe with lazy_static) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: consider making this a static item = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]` -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/others.rs:10:1 | LL | const CELL: Cell<usize> = Cell::new(6); - | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | make this a static item (maybe with lazy_static) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/others.rs:11:1 | LL | const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], Vec::new(), 7); - | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | make this a static item (maybe with lazy_static) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider making this a static item -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/others.rs:16:9 | LL | const $name: $ty = $e; @@ -36,7 +35,7 @@ LL | declare_const!(_ONCE: Once = Once::new()); | = note: this error originates in the macro `declare_const` (in Nightly builds, run with -Z macro-backtrace for more info) -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/others.rs:44:13 | LL | const _BAZ: Cell<usize> = Cell::new(0); diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.stderr b/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.stderr index 1d1e9e2002f..4a793d985e5 100644 --- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.stderr +++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.stderr @@ -1,4 +1,4 @@ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/traits.rs:16:5 | LL | const ATOMIC: AtomicUsize; @@ -7,7 +7,7 @@ LL | const ATOMIC: AtomicUsize; = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]` -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/traits.rs:9:9 | LL | const $name: $ty = $e; @@ -18,67 +18,67 @@ LL | declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); | = note: this error originates in the macro `declare_const` (in Nightly builds, run with -Z macro-backtrace for more info) -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/traits.rs:44:5 | LL | const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/traits.rs:69:5 | LL | const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/traits.rs:70:5 | LL | const WRAPPED_TO_BE_UNFROZEN: Wrapper<Self::ToBeUnfrozen> = Wrapper(AtomicUsize::new(14)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/traits.rs:89:5 | LL | const BOUNDED: T::ToBeBounded; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/traits.rs:117:5 | LL | const SELF: Self = AtomicUsize::new(17); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/traits.rs:118:5 | LL | const WRAPPED_SELF: Option<Self> = Some(AtomicUsize::new(21)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/traits.rs:124:5 | LL | const DIRECT: Cell<T>; | ^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/traits.rs:125:5 | LL | const INDIRECT: Cell<*const T>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/traits.rs:129:5 | LL | const DIRECT: Cell<T> = Cell::new(T::DEFAULT); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/traits.rs:141:5 | LL | const ATOMIC: AtomicUsize = AtomicUsize::new(18); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/traits.rs:147:5 | LL | const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); diff --git a/src/tools/clippy/tests/ui/deprecated.rs b/src/tools/clippy/tests/ui/deprecated.rs index 07270bd7636..d3c34fb3716 100644 --- a/src/tools/clippy/tests/ui/deprecated.rs +++ b/src/tools/clippy/tests/ui/deprecated.rs @@ -18,5 +18,7 @@ #![warn(clippy::filter_map)] #![warn(clippy::pub_enum_variant_names)] #![warn(clippy::wrong_pub_self_convention)] +#![warn(clippy::maybe_misused_cfg)] +#![warn(clippy::mismatched_target_os)] fn main() {} diff --git a/src/tools/clippy/tests/ui/deprecated.stderr b/src/tools/clippy/tests/ui/deprecated.stderr index a9cf04bea52..49b90c70c06 100644 --- a/src/tools/clippy/tests/ui/deprecated.stderr +++ b/src/tools/clippy/tests/ui/deprecated.stderr @@ -97,5 +97,17 @@ error: lint `clippy::wrong_pub_self_convention` has been removed: set the `avoid LL | #![warn(clippy::wrong_pub_self_convention)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 16 previous errors +error: lint `clippy::maybe_misused_cfg` has been removed: this lint has been replaced by `unexpected_cfgs` + --> tests/ui/deprecated.rs:21:9 + | +LL | #![warn(clippy::maybe_misused_cfg)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::mismatched_target_os` has been removed: this lint has been replaced by `unexpected_cfgs` + --> tests/ui/deprecated.rs:22:9 + | +LL | #![warn(clippy::mismatched_target_os)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 18 previous errors diff --git a/src/tools/clippy/tests/ui/deref_addrof.fixed b/src/tools/clippy/tests/ui/deref_addrof.fixed index aa1cf19b76f..b6278c6ca8a 100644 --- a/src/tools/clippy/tests/ui/deref_addrof.fixed +++ b/src/tools/clippy/tests/ui/deref_addrof.fixed @@ -43,6 +43,10 @@ fn main() { let b = *aref; let _ = unsafe { *core::ptr::addr_of!(a) }; + + let _repeat = [0; 64]; + // do NOT lint for array as sematic differences with/out `*&`. + let _arr = *&[0, 1, 2, 3, 4]; } #[derive(Copy, Clone)] diff --git a/src/tools/clippy/tests/ui/deref_addrof.rs b/src/tools/clippy/tests/ui/deref_addrof.rs index 38796aef390..572b0fdb102 100644 --- a/src/tools/clippy/tests/ui/deref_addrof.rs +++ b/src/tools/clippy/tests/ui/deref_addrof.rs @@ -43,6 +43,10 @@ fn main() { let b = **&aref; let _ = unsafe { *core::ptr::addr_of!(a) }; + + let _repeat = *&[0; 64]; + // do NOT lint for array as sematic differences with/out `*&`. + let _arr = *&[0, 1, 2, 3, 4]; } #[derive(Copy, Clone)] diff --git a/src/tools/clippy/tests/ui/deref_addrof.stderr b/src/tools/clippy/tests/ui/deref_addrof.stderr index 5e3cb417aa0..20069f746c8 100644 --- a/src/tools/clippy/tests/ui/deref_addrof.stderr +++ b/src/tools/clippy/tests/ui/deref_addrof.stderr @@ -50,7 +50,13 @@ LL | let b = **&aref; | ^^^^^^ help: try: `aref` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:53:17 + --> tests/ui/deref_addrof.rs:47:19 + | +LL | let _repeat = *&[0; 64]; + | ^^^^^^^^^ help: try: `[0; 64]` + +error: immediately dereferencing a reference + --> tests/ui/deref_addrof.rs:57:17 | LL | inline!(*& $(@expr self)) | ^^^^^^^^^^^^^^^^ help: try: `$(@expr self)` @@ -58,12 +64,12 @@ LL | inline!(*& $(@expr self)) = note: this error originates in the macro `__inline_mac_impl` (in Nightly builds, run with -Z macro-backtrace for more info) error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:57:17 + --> tests/ui/deref_addrof.rs:61:17 | LL | inline!(*&mut $(@expr self)) | ^^^^^^^^^^^^^^^^^^^ help: try: `$(@expr self)` | = note: this error originates in the macro `__inline_mac_impl` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 10 previous errors +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/doc_unsafe.stderr b/src/tools/clippy/tests/ui/doc_unsafe.stderr index 4fcbe716951..929afbceb87 100644 --- a/src/tools/clippy/tests/ui/doc_unsafe.stderr +++ b/src/tools/clippy/tests/ui/doc_unsafe.stderr @@ -1,4 +1,4 @@ -error: unsafe function's docs miss `# Safety` section +error: unsafe function's docs are missing a `# Safety` section --> tests/ui/doc_unsafe.rs:9:1 | LL | pub unsafe fn destroy_the_planet() { @@ -7,13 +7,13 @@ LL | pub unsafe fn destroy_the_planet() { = note: `-D clippy::missing-safety-doc` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::missing_safety_doc)]` -error: unsafe function's docs miss `# Safety` section +error: unsafe function's docs are missing a `# Safety` section --> tests/ui/doc_unsafe.rs:32:5 | LL | pub unsafe fn republished() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: unsafe function's docs miss `# Safety` section +error: unsafe function's docs are missing a `# Safety` section --> tests/ui/doc_unsafe.rs:40:5 | LL | unsafe fn woefully_underdocumented(self); @@ -25,13 +25,13 @@ error: docs for unsafe trait missing `# Safety` section LL | pub unsafe trait UnsafeTrait { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: unsafe function's docs miss `# Safety` section +error: unsafe function's docs are missing a `# Safety` section --> tests/ui/doc_unsafe.rs:76:5 | LL | pub unsafe fn more_undocumented_unsafe() -> Self { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: unsafe function's docs miss `# Safety` section +error: unsafe function's docs are missing a `# Safety` section --> tests/ui/doc_unsafe.rs:92:9 | LL | pub unsafe fn whee() { diff --git a/src/tools/clippy/tests/ui/double_neg.rs b/src/tools/clippy/tests/ui/double_neg.rs index da82890443e..3be8c628873 100644 --- a/src/tools/clippy/tests/ui/double_neg.rs +++ b/src/tools/clippy/tests/ui/double_neg.rs @@ -5,6 +5,6 @@ fn main() { -x; -(-x); --x; - //~^ ERROR: `--x` could be misinterpreted as pre-decrement by C programmers, is usuall + //~^ ERROR: `--x` could be misinterpreted as pre-decrement by C programmers, is usually //~| NOTE: `-D clippy::double-neg` implied by `-D warnings` } diff --git a/src/tools/clippy/tests/ui/eta.fixed b/src/tools/clippy/tests/ui/eta.fixed index da28ec2e653..7126f279945 100644 --- a/src/tools/clippy/tests/ui/eta.fixed +++ b/src/tools/clippy/tests/ui/eta.fixed @@ -471,3 +471,14 @@ mod issue_10854 { } } } + +mod issue_12853 { + fn f_by_value<F: Fn(u32)>(f: F) { + let x = Box::new(|| None.map(&f)); + x(); + } + fn f_by_ref<F: Fn(u32)>(f: &F) { + let x = Box::new(|| None.map(f)); + x(); + } +} diff --git a/src/tools/clippy/tests/ui/eta.rs b/src/tools/clippy/tests/ui/eta.rs index f924100f8f4..0787abf5f3e 100644 --- a/src/tools/clippy/tests/ui/eta.rs +++ b/src/tools/clippy/tests/ui/eta.rs @@ -471,3 +471,14 @@ mod issue_10854 { } } } + +mod issue_12853 { + fn f_by_value<F: Fn(u32)>(f: F) { + let x = Box::new(|| None.map(|x| f(x))); + x(); + } + fn f_by_ref<F: Fn(u32)>(f: &F) { + let x = Box::new(|| None.map(|x| f(x))); + x(); + } +} diff --git a/src/tools/clippy/tests/ui/eta.stderr b/src/tools/clippy/tests/ui/eta.stderr index d9a8768a682..c757601042f 100644 --- a/src/tools/clippy/tests/ui/eta.stderr +++ b/src/tools/clippy/tests/ui/eta.stderr @@ -190,5 +190,17 @@ error: redundant closure LL | test.map(|t| t.method()) | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `crate::issue_10854::d::Test::method` -error: aborting due to 31 previous errors +error: redundant closure + --> tests/ui/eta.rs:477:38 + | +LL | let x = Box::new(|| None.map(|x| f(x))); + | ^^^^^^^^ help: replace the closure with the function itself: `&f` + +error: redundant closure + --> tests/ui/eta.rs:481:38 + | +LL | let x = Box::new(|| None.map(|x| f(x))); + | ^^^^^^^^ help: replace the closure with the function itself: `f` + +error: aborting due to 33 previous errors diff --git a/src/tools/clippy/tests/ui/floating_point_log.fixed b/src/tools/clippy/tests/ui/floating_point_log.fixed index 01f0fc5c671..15cc47eef0d 100644 --- a/src/tools/clippy/tests/ui/floating_point_log.fixed +++ b/src/tools/clippy/tests/ui/floating_point_log.fixed @@ -55,4 +55,19 @@ fn check_ln1p() { let _ = (1.0 + x - 2.0).ln(); } +fn issue12881() { + pub trait MyLog { + fn log(&self) -> Self; + } + + impl MyLog for f32 { + fn log(&self) -> Self { + 4. + } + } + + let x = 2.0; + x.log(); +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/floating_point_log.rs b/src/tools/clippy/tests/ui/floating_point_log.rs index 197e3e1ff90..1241af82859 100644 --- a/src/tools/clippy/tests/ui/floating_point_log.rs +++ b/src/tools/clippy/tests/ui/floating_point_log.rs @@ -55,4 +55,19 @@ fn check_ln1p() { let _ = (1.0 + x - 2.0).ln(); } +fn issue12881() { + pub trait MyLog { + fn log(&self) -> Self; + } + + impl MyLog for f32 { + fn log(&self) -> Self { + 4. + } + } + + let x = 2.0; + x.log(); +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/format_args.fixed b/src/tools/clippy/tests/ui/format_args.fixed index cab20b11e07..4d812f6bf1d 100644 --- a/src/tools/clippy/tests/ui/format_args.fixed +++ b/src/tools/clippy/tests/ui/format_args.fixed @@ -104,6 +104,7 @@ fn main() { println!("{foo}{bar}", foo = "foo", bar = "bar"); println!("{foo}{bar}", bar = "bar", foo = "foo"); println!("{foo}{bar}", bar = "bar", foo = "foo"); + println!("{}", my_other_macro!()); // negative tests println!("error: something failed at {}", Somewhere.to_string()); diff --git a/src/tools/clippy/tests/ui/format_args.rs b/src/tools/clippy/tests/ui/format_args.rs index bc3645cb2c2..d242623feb6 100644 --- a/src/tools/clippy/tests/ui/format_args.rs +++ b/src/tools/clippy/tests/ui/format_args.rs @@ -104,6 +104,7 @@ fn main() { println!("{foo}{bar}", foo = "foo", bar = "bar".to_string()); println!("{foo}{bar}", bar = "bar".to_string(), foo = "foo"); println!("{foo}{bar}", bar = "bar", foo = "foo".to_string()); + println!("{}", my_other_macro!().to_string()); // negative tests println!("error: something failed at {}", Somewhere.to_string()); diff --git a/src/tools/clippy/tests/ui/format_args.stderr b/src/tools/clippy/tests/ui/format_args.stderr index f20cf9eca2f..91a45e27008 100644 --- a/src/tools/clippy/tests/ui/format_args.stderr +++ b/src/tools/clippy/tests/ui/format_args.stderr @@ -127,29 +127,35 @@ error: `to_string` applied to a type that implements `Display` in `println!` arg LL | println!("{foo}{bar}", bar = "bar", foo = "foo".to_string()); | ^^^^^^^^^^^^ help: remove this +error: `to_string` applied to a type that implements `Display` in `println!` args + --> tests/ui/format_args.rs:107:37 + | +LL | println!("{}", my_other_macro!().to_string()); + | ^^^^^^^^^^^^ help: remove this + error: `to_string` applied to a type that implements `Display` in `print!` args - --> tests/ui/format_args.rs:118:37 + --> tests/ui/format_args.rs:119:37 | LL | print!("{}", (Location::caller().to_string())); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `print!` args - --> tests/ui/format_args.rs:119:39 + --> tests/ui/format_args.rs:120:39 | LL | print!("{}", ((Location::caller()).to_string())); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `format!` args - --> tests/ui/format_args.rs:147:38 + --> tests/ui/format_args.rs:148:38 | LL | let x = format!("{} {}", a, b.to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> tests/ui/format_args.rs:161:24 + --> tests/ui/format_args.rs:162:24 | LL | println!("{}", original[..10].to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use this: `&original[..10]` -error: aborting due to 25 previous errors +error: aborting due to 26 previous errors diff --git a/src/tools/clippy/tests/ui/indexing_slicing_index.rs b/src/tools/clippy/tests/ui/indexing_slicing_index.rs index 2e726141649..2af5fcc82a9 100644 --- a/src/tools/clippy/tests/ui/indexing_slicing_index.rs +++ b/src/tools/clippy/tests/ui/indexing_slicing_index.rs @@ -1,4 +1,5 @@ //@compile-flags: -Zdeduplicate-diagnostics=yes +//@aux-build: proc_macros.rs #![warn(clippy::indexing_slicing)] // We also check the out_of_bounds_indexing lint here, because it lints similar things and @@ -11,6 +12,9 @@ clippy::useless_vec )] +extern crate proc_macros; +use proc_macros::with_span; + const ARR: [i32; 2] = [1, 2]; const REF: &i32 = &ARR[idx()]; // This should be linted, since `suppress-restriction-lint-in-const` default is false. //~^ ERROR: indexing may panic @@ -22,6 +26,22 @@ const fn idx4() -> usize { 4 } +with_span!( + span + + fn dont_lint_proc_macro_array() { + let x = [1, 2, 3, 4]; + let index: usize = 1; + x[index]; + x[10]; + + let x = vec![0; 5]; + let index: usize = 1; + x[index]; + x[10]; + } +); + fn main() { let x = [1, 2, 3, 4]; let index: usize = 1; diff --git a/src/tools/clippy/tests/ui/indexing_slicing_index.stderr b/src/tools/clippy/tests/ui/indexing_slicing_index.stderr index 386f91becf1..71677584d25 100644 --- a/src/tools/clippy/tests/ui/indexing_slicing_index.stderr +++ b/src/tools/clippy/tests/ui/indexing_slicing_index.stderr @@ -1,5 +1,5 @@ error: indexing may panic - --> tests/ui/indexing_slicing_index.rs:15:20 + --> tests/ui/indexing_slicing_index.rs:19:20 | LL | const REF: &i32 = &ARR[idx()]; // This should be linted, since `suppress-restriction-lint-in-const` default is false. | ^^^^^^^^^^ @@ -10,19 +10,19 @@ LL | const REF: &i32 = &ARR[idx()]; // This should be linted, since `suppress-re = help: to override `-D warnings` add `#[allow(clippy::indexing_slicing)]` error[E0080]: evaluation of `main::{constant#3}` failed - --> tests/ui/indexing_slicing_index.rs:47:14 + --> tests/ui/indexing_slicing_index.rs:67:14 | LL | const { &ARR[idx4()] }; | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 note: erroneous constant encountered - --> tests/ui/indexing_slicing_index.rs:47:5 + --> tests/ui/indexing_slicing_index.rs:67:5 | LL | const { &ARR[idx4()] }; | ^^^^^^^^^^^^^^^^^^^^^^ error: indexing may panic - --> tests/ui/indexing_slicing_index.rs:28:5 + --> tests/ui/indexing_slicing_index.rs:48:5 | LL | x[index]; | ^^^^^^^^ @@ -30,7 +30,7 @@ LL | x[index]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: index is out of bounds - --> tests/ui/indexing_slicing_index.rs:31:5 + --> tests/ui/indexing_slicing_index.rs:51:5 | LL | x[4]; | ^^^^ @@ -39,13 +39,13 @@ LL | x[4]; = help: to override `-D warnings` add `#[allow(clippy::out_of_bounds_indexing)]` error: index is out of bounds - --> tests/ui/indexing_slicing_index.rs:33:5 + --> tests/ui/indexing_slicing_index.rs:53:5 | LL | x[1 << 3]; | ^^^^^^^^^ error: indexing may panic - --> tests/ui/indexing_slicing_index.rs:44:14 + --> tests/ui/indexing_slicing_index.rs:64:14 | LL | const { &ARR[idx()] }; | ^^^^^^^^^^ @@ -54,7 +54,7 @@ LL | const { &ARR[idx()] }; = note: the suggestion might not be applicable in constant blocks error: indexing may panic - --> tests/ui/indexing_slicing_index.rs:47:14 + --> tests/ui/indexing_slicing_index.rs:67:14 | LL | const { &ARR[idx4()] }; | ^^^^^^^^^^^ @@ -63,13 +63,13 @@ LL | const { &ARR[idx4()] }; = note: the suggestion might not be applicable in constant blocks error: index is out of bounds - --> tests/ui/indexing_slicing_index.rs:54:5 + --> tests/ui/indexing_slicing_index.rs:74:5 | LL | y[4]; | ^^^^ error: indexing may panic - --> tests/ui/indexing_slicing_index.rs:57:5 + --> tests/ui/indexing_slicing_index.rs:77:5 | LL | v[0]; | ^^^^ @@ -77,7 +77,7 @@ LL | v[0]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> tests/ui/indexing_slicing_index.rs:59:5 + --> tests/ui/indexing_slicing_index.rs:79:5 | LL | v[10]; | ^^^^^ @@ -85,7 +85,7 @@ LL | v[10]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> tests/ui/indexing_slicing_index.rs:61:5 + --> tests/ui/indexing_slicing_index.rs:81:5 | LL | v[1 << 3]; | ^^^^^^^^^ @@ -93,13 +93,13 @@ LL | v[1 << 3]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: index is out of bounds - --> tests/ui/indexing_slicing_index.rs:69:5 + --> tests/ui/indexing_slicing_index.rs:89:5 | LL | x[N]; | ^^^^ error: indexing may panic - --> tests/ui/indexing_slicing_index.rs:72:5 + --> tests/ui/indexing_slicing_index.rs:92:5 | LL | v[N]; | ^^^^ @@ -107,7 +107,7 @@ LL | v[N]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> tests/ui/indexing_slicing_index.rs:74:5 + --> tests/ui/indexing_slicing_index.rs:94:5 | LL | v[M]; | ^^^^ @@ -115,7 +115,7 @@ LL | v[M]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: index is out of bounds - --> tests/ui/indexing_slicing_index.rs:78:13 + --> tests/ui/indexing_slicing_index.rs:98:13 | LL | let _ = x[4]; | ^^^^ diff --git a/src/tools/clippy/tests/ui/indexing_slicing_slice.rs b/src/tools/clippy/tests/ui/indexing_slicing_slice.rs index fc591021ed6..f37bcc4aa0c 100644 --- a/src/tools/clippy/tests/ui/indexing_slicing_slice.rs +++ b/src/tools/clippy/tests/ui/indexing_slicing_slice.rs @@ -1,8 +1,111 @@ +//@aux-build: proc_macros.rs + #![warn(clippy::indexing_slicing)] // We also check the out_of_bounds_indexing lint here, because it lints similar things and // we want to avoid false positives. #![warn(clippy::out_of_bounds_indexing)] -#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::useless_vec)] +#![allow( + clippy::no_effect, + clippy::unnecessary_operation, + clippy::useless_vec, + unused_must_use, + unused +)] +#![warn(clippy::indexing_slicing)] + +extern crate proc_macros; +use proc_macros::with_span; + +use std::ops::Index; + +struct BoolMap<T> { + false_value: T, + true_value: T, +} + +impl<T> Index<bool> for BoolMap<T> { + type Output = T; + fn index(&self, index: bool) -> &T { + if index { &self.true_value } else { &self.false_value } + } +} + +struct BoolMapWithGet<T> { + false_value: T, + true_value: T, +} + +impl<T> Index<bool> for BoolMapWithGet<T> { + type Output = T; + fn index(&self, index: bool) -> &Self::Output { + if index { &self.true_value } else { &self.false_value } + } +} + +impl<T> BoolMapWithGet<T> { + fn get(&self, index: bool) -> Option<&T> { + if index { + Some(&self.true_value) + } else { + Some(&self.false_value) + } + } +} + +struct S<T>(T); +impl S<i32> { + fn get() -> Option<i32> { + unimplemented!() + } +} +impl<T> Index<i32> for S<T> { + type Output = T; + fn index(&self, _index: i32) -> &Self::Output { + &self.0 + } +} + +struct Y<T>(T); +impl Y<i32> { + fn get<U>() -> Option<U> { + unimplemented!() + } +} +impl<T> Index<i32> for Y<T> { + type Output = T; + fn index(&self, _index: i32) -> &Self::Output { + &self.0 + } +} + +struct Z<T>(T); +impl<T> Z<T> { + fn get<T2>() -> T2 { + unimplemented!() + } +} +impl<T> Index<i32> for Z<T> { + type Output = T; + fn index(&self, _index: i32) -> &Self::Output { + &self.0 + } +} + +with_span!( + span + + fn dont_lint_proc_macro() { + let x = [1, 2, 3, 4]; + let index: usize = 1; + &x[index..]; + &x[..10]; + + let x = vec![0; 5]; + let index: usize = 1; + &x[index..]; + &x[..10]; + } +); fn main() { let x = [1, 2, 3, 4]; @@ -51,4 +154,28 @@ fn main() { //~^ ERROR: slicing may panic &v[..]; // Ok, should not produce stderr. + + let map = BoolMap { + false_value: 2, + true_value: 4, + }; + + map[true]; // Ok, because `get` does not exist (custom indexing) + + let map_with_get = BoolMapWithGet { + false_value: 2, + true_value: 4, + }; + + // Lint on this, because `get` does exist with same signature + map_with_get[true]; + + let s = S::<i32>(1); + s[0]; + + let y = Y::<i32>(1); + y[0]; + + let z = Z::<i32>(1); + z[0]; } diff --git a/src/tools/clippy/tests/ui/indexing_slicing_slice.stderr b/src/tools/clippy/tests/ui/indexing_slicing_slice.stderr index 790d4a41f5b..1e72506746e 100644 --- a/src/tools/clippy/tests/ui/indexing_slicing_slice.stderr +++ b/src/tools/clippy/tests/ui/indexing_slicing_slice.stderr @@ -1,5 +1,5 @@ error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:12:6 + --> tests/ui/indexing_slicing_slice.rs:115:6 | LL | &x[index..]; | ^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | &x[index..]; = help: to override `-D warnings` add `#[allow(clippy::indexing_slicing)]` error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:14:6 + --> tests/ui/indexing_slicing_slice.rs:117:6 | LL | &x[..index]; | ^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | &x[..index]; = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:16:6 + --> tests/ui/indexing_slicing_slice.rs:119:6 | LL | &x[index_from..index_to]; | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | &x[index_from..index_to]; = help: consider using `.get(n..m)` or `.get_mut(n..m)` instead error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:18:6 + --> tests/ui/indexing_slicing_slice.rs:121:6 | LL | &x[index_from..][..index_to]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | &x[index_from..][..index_to]; = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:18:6 + --> tests/ui/indexing_slicing_slice.rs:121:6 | LL | &x[index_from..][..index_to]; | ^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | &x[index_from..][..index_to]; = help: consider using `.get(n..)` or .get_mut(n..)` instead error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:21:6 + --> tests/ui/indexing_slicing_slice.rs:124:6 | LL | &x[5..][..10]; | ^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL | &x[5..][..10]; = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: range is out of bounds - --> tests/ui/indexing_slicing_slice.rs:21:8 + --> tests/ui/indexing_slicing_slice.rs:124:8 | LL | &x[5..][..10]; | ^ @@ -58,7 +58,7 @@ LL | &x[5..][..10]; = help: to override `-D warnings` add `#[allow(clippy::out_of_bounds_indexing)]` error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:25:6 + --> tests/ui/indexing_slicing_slice.rs:128:6 | LL | &x[0..][..3]; | ^^^^^^^^^^^ @@ -66,7 +66,7 @@ LL | &x[0..][..3]; = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:27:6 + --> tests/ui/indexing_slicing_slice.rs:130:6 | LL | &x[1..][..5]; | ^^^^^^^^^^^ @@ -74,19 +74,19 @@ LL | &x[1..][..5]; = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: range is out of bounds - --> tests/ui/indexing_slicing_slice.rs:35:12 + --> tests/ui/indexing_slicing_slice.rs:138:12 | LL | &y[0..=4]; | ^ error: range is out of bounds - --> tests/ui/indexing_slicing_slice.rs:37:11 + --> tests/ui/indexing_slicing_slice.rs:140:11 | LL | &y[..=4]; | ^ error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:43:6 + --> tests/ui/indexing_slicing_slice.rs:146:6 | LL | &v[10..100]; | ^^^^^^^^^^ @@ -94,7 +94,7 @@ LL | &v[10..100]; = help: consider using `.get(n..m)` or `.get_mut(n..m)` instead error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:45:6 + --> tests/ui/indexing_slicing_slice.rs:148:6 | LL | &x[10..][..100]; | ^^^^^^^^^^^^^^ @@ -102,13 +102,13 @@ LL | &x[10..][..100]; = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: range is out of bounds - --> tests/ui/indexing_slicing_slice.rs:45:8 + --> tests/ui/indexing_slicing_slice.rs:148:8 | LL | &x[10..][..100]; | ^^ error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:48:6 + --> tests/ui/indexing_slicing_slice.rs:151:6 | LL | &v[10..]; | ^^^^^^^ @@ -116,12 +116,36 @@ LL | &v[10..]; = help: consider using `.get(n..)` or .get_mut(n..)` instead error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:50:6 + --> tests/ui/indexing_slicing_slice.rs:153:6 | LL | &v[..100]; | ^^^^^^^^ | = help: consider using `.get(..n)`or `.get_mut(..n)` instead -error: aborting due to 16 previous errors +error: indexing may panic + --> tests/ui/indexing_slicing_slice.rs:171:5 + | +LL | map_with_get[true]; + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + +error: indexing may panic + --> tests/ui/indexing_slicing_slice.rs:174:5 + | +LL | s[0]; + | ^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + +error: indexing may panic + --> tests/ui/indexing_slicing_slice.rs:177:5 + | +LL | y[0]; + | ^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + +error: aborting due to 19 previous errors diff --git a/src/tools/clippy/tests/ui/infinite_loops.rs b/src/tools/clippy/tests/ui/infinite_loops.rs index 52fbbaa8e31..b2d522fa011 100644 --- a/src/tools/clippy/tests/ui/infinite_loops.rs +++ b/src/tools/clippy/tests/ui/infinite_loops.rs @@ -137,7 +137,7 @@ fn can_break_both_inner_and_outer(cond: bool) { } fn break_wrong_loop(cond: bool) { - // 'inner has statement to break 'outer loop, but it was breaked early by a labeled child loop + // 'inner has statement to break 'outer loop, but it was broken out of early by a labeled child loop 'outer: loop { loop { //~^ ERROR: infinite loop detected diff --git a/src/tools/clippy/tests/ui/iter_over_hash_type.rs b/src/tools/clippy/tests/ui/iter_over_hash_type.rs index 7000c8bf96f..65bae1df421 100644 --- a/src/tools/clippy/tests/ui/iter_over_hash_type.rs +++ b/src/tools/clippy/tests/ui/iter_over_hash_type.rs @@ -59,7 +59,7 @@ fn main() { let _ = x; } - // shouldnt fire + // shouldn't fire for x in &vec { let _ = x; } diff --git a/src/tools/clippy/tests/ui/manual_pattern_char_comparison.fixed b/src/tools/clippy/tests/ui/manual_pattern_char_comparison.fixed new file mode 100644 index 00000000000..588226b87e8 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_pattern_char_comparison.fixed @@ -0,0 +1,49 @@ +#![warn(clippy::manual_pattern_char_comparison)] + +struct NotStr; + +impl NotStr { + fn find(&self, _: impl FnMut(char) -> bool) {} +} + +fn main() { + let sentence = "Hello, world!"; + sentence.trim_end_matches(['.', ',', '!', '?']); + sentence.split(['\n', 'X']); + sentence.split(['\n', 'X']); + sentence.splitn(3, 'X'); + sentence.splitn(3, |c: char| c.is_whitespace() || c == 'X'); + let char_compare = 'X'; + sentence.splitn(3, char_compare); + sentence.split(['\n', 'X', 'Y']); + sentence.splitn(3, 'X'); + sentence.splitn(3, ['X', 'W']); + sentence.find('🎈'); + + let not_str = NotStr; + not_str.find(|c: char| c == 'X'); + + "".find(|c| c == 'a' || c > 'z'); + + let x = true; + "".find(|c| c == 'a' || x || c == 'b'); + + let d = 'd'; + "".find(|c| c == 'a' || d == 'b'); + + "".find(|c| match c { + 'a' | 'b' => true, + _ => c.is_ascii(), + }); + + "".find(|c| matches!(c, 'a' | 'b' if false)); + + "".find(|c| matches!(c, 'a' | '1'..'4')); + "".find(|c| c == 'a' || matches!(c, '1'..'4')); + macro_rules! m { + ($e:expr) => { + $e == '?' + }; + } + "".find(|c| m!(c)); +} diff --git a/src/tools/clippy/tests/ui/manual_pattern_char_comparison.rs b/src/tools/clippy/tests/ui/manual_pattern_char_comparison.rs new file mode 100644 index 00000000000..5078f3ee27f --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_pattern_char_comparison.rs @@ -0,0 +1,49 @@ +#![warn(clippy::manual_pattern_char_comparison)] + +struct NotStr; + +impl NotStr { + fn find(&self, _: impl FnMut(char) -> bool) {} +} + +fn main() { + let sentence = "Hello, world!"; + sentence.trim_end_matches(|c: char| c == '.' || c == ',' || c == '!' || c == '?'); + sentence.split(|c: char| c == '\n' || c == 'X'); + sentence.split(|c| c == '\n' || c == 'X'); + sentence.splitn(3, |c: char| c == 'X'); + sentence.splitn(3, |c: char| c.is_whitespace() || c == 'X'); + let char_compare = 'X'; + sentence.splitn(3, |c: char| c == char_compare); + sentence.split(|c: char| matches!(c, '\n' | 'X' | 'Y')); + sentence.splitn(3, |c: char| matches!(c, 'X')); + sentence.splitn(3, |c: char| matches!(c, 'X' | 'W')); + sentence.find(|c| c == '🎈'); + + let not_str = NotStr; + not_str.find(|c: char| c == 'X'); + + "".find(|c| c == 'a' || c > 'z'); + + let x = true; + "".find(|c| c == 'a' || x || c == 'b'); + + let d = 'd'; + "".find(|c| c == 'a' || d == 'b'); + + "".find(|c| match c { + 'a' | 'b' => true, + _ => c.is_ascii(), + }); + + "".find(|c| matches!(c, 'a' | 'b' if false)); + + "".find(|c| matches!(c, 'a' | '1'..'4')); + "".find(|c| c == 'a' || matches!(c, '1'..'4')); + macro_rules! m { + ($e:expr) => { + $e == '?' + }; + } + "".find(|c| m!(c)); +} diff --git a/src/tools/clippy/tests/ui/manual_pattern_char_comparison.stderr b/src/tools/clippy/tests/ui/manual_pattern_char_comparison.stderr new file mode 100644 index 00000000000..b6b51794a11 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_pattern_char_comparison.stderr @@ -0,0 +1,59 @@ +error: this manual char comparison can be written more succinctly + --> tests/ui/manual_pattern_char_comparison.rs:11:31 + | +LL | sentence.trim_end_matches(|c: char| c == '.' || c == ',' || c == '!' || c == '?'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using an array of `char`: `['.', ',', '!', '?']` + | + = note: `-D clippy::manual-pattern-char-comparison` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_pattern_char_comparison)]` + +error: this manual char comparison can be written more succinctly + --> tests/ui/manual_pattern_char_comparison.rs:12:20 + | +LL | sentence.split(|c: char| c == '\n' || c == 'X'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using an array of `char`: `['\n', 'X']` + +error: this manual char comparison can be written more succinctly + --> tests/ui/manual_pattern_char_comparison.rs:13:20 + | +LL | sentence.split(|c| c == '\n' || c == 'X'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using an array of `char`: `['\n', 'X']` + +error: this manual char comparison can be written more succinctly + --> tests/ui/manual_pattern_char_comparison.rs:14:24 + | +LL | sentence.splitn(3, |c: char| c == 'X'); + | ^^^^^^^^^^^^^^^^^^ help: consider using a `char`: `'X'` + +error: this manual char comparison can be written more succinctly + --> tests/ui/manual_pattern_char_comparison.rs:17:24 + | +LL | sentence.splitn(3, |c: char| c == char_compare); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a `char`: `char_compare` + +error: this manual char comparison can be written more succinctly + --> tests/ui/manual_pattern_char_comparison.rs:18:20 + | +LL | sentence.split(|c: char| matches!(c, '\n' | 'X' | 'Y')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using an array of `char`: `['\n', 'X', 'Y']` + +error: this manual char comparison can be written more succinctly + --> tests/ui/manual_pattern_char_comparison.rs:19:24 + | +LL | sentence.splitn(3, |c: char| matches!(c, 'X')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a `char`: `'X'` + +error: this manual char comparison can be written more succinctly + --> tests/ui/manual_pattern_char_comparison.rs:20:24 + | +LL | sentence.splitn(3, |c: char| matches!(c, 'X' | 'W')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using an array of `char`: `['X', 'W']` + +error: this manual char comparison can be written more succinctly + --> tests/ui/manual_pattern_char_comparison.rs:21:19 + | +LL | sentence.find(|c| c == '🎈'); + | ^^^^^^^^^^^^^ help: consider using a `char`: `'🎈'` + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed b/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed index d6e736ba9cc..663de1a5f06 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed +++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed @@ -26,6 +26,12 @@ fn main() { Some(x) => x, None => &[], }; + + let x: Result<String, i64> = Ok(String::new()); + x.unwrap_or_default(); + + let x: Result<String, i64> = Ok(String::new()); + x.unwrap_or_default(); } // Issue #12531 diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs b/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs index 462d5d90ee7..75ffe09be9d 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs +++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs @@ -47,6 +47,21 @@ fn main() { Some(x) => x, None => &[], }; + + let x: Result<String, i64> = Ok(String::new()); + match x { + //~^ ERROR: match can be simplified with `.unwrap_or_default()` + Ok(v) => v, + Err(_) => String::new(), + }; + + let x: Result<String, i64> = Ok(String::new()); + if let Ok(v) = x { + //~^ ERROR: if let can be simplified with `.unwrap_or_default()` + v + } else { + String::new() + }; } // Issue #12531 diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr b/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr index 3f1da444301..9e3b1be5cb9 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr +++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr @@ -53,7 +53,28 @@ LL | | }; | |_____^ help: replace it with: `x.unwrap_or_default()` error: match can be simplified with `.unwrap_or_default()` - --> tests/ui/manual_unwrap_or_default.rs:56:20 + --> tests/ui/manual_unwrap_or_default.rs:52:5 + | +LL | / match x { +LL | | +LL | | Ok(v) => v, +LL | | Err(_) => String::new(), +LL | | }; + | |_____^ help: replace it with: `x.unwrap_or_default()` + +error: if let can be simplified with `.unwrap_or_default()` + --> tests/ui/manual_unwrap_or_default.rs:59:5 + | +LL | / if let Ok(v) = x { +LL | | +LL | | v +LL | | } else { +LL | | String::new() +LL | | }; + | |_____^ help: replace it with: `x.unwrap_or_default()` + +error: match can be simplified with `.unwrap_or_default()` + --> tests/ui/manual_unwrap_or_default.rs:71:20 | LL | Some(_) => match *b { | ____________________^ @@ -62,5 +83,5 @@ LL | | _ => 0, LL | | }, | |_________^ help: replace it with: `(*b).unwrap_or_default()` -error: aborting due to 6 previous errors +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/match_same_arms2.fixed b/src/tools/clippy/tests/ui/match_same_arms2.fixed index fba0cf33b3c..09e960ddd6a 100644 --- a/src/tools/clippy/tests/ui/match_same_arms2.fixed +++ b/src/tools/clippy/tests/ui/match_same_arms2.fixed @@ -239,3 +239,20 @@ fn main() { _ => false, }; } + +// issue #8919, fixed on https://github.com/rust-lang/rust/pull/97312 +mod with_lifetime { + enum MaybeStaticStr<'a> { + Static(&'static str), + Borrowed(&'a str), + } + + impl<'a> MaybeStaticStr<'a> { + fn get(&self) -> &'a str { + match *self { + MaybeStaticStr::Borrowed(s) | MaybeStaticStr::Static(s) => s, + //~^ ERROR: this match arm has an identical body to another arm + } + } + } +} diff --git a/src/tools/clippy/tests/ui/match_same_arms2.rs b/src/tools/clippy/tests/ui/match_same_arms2.rs index 8a4e3b325bb..cc7425135cc 100644 --- a/src/tools/clippy/tests/ui/match_same_arms2.rs +++ b/src/tools/clippy/tests/ui/match_same_arms2.rs @@ -262,3 +262,21 @@ fn main() { _ => false, }; } + +// issue #8919, fixed on https://github.com/rust-lang/rust/pull/97312 +mod with_lifetime { + enum MaybeStaticStr<'a> { + Static(&'static str), + Borrowed(&'a str), + } + + impl<'a> MaybeStaticStr<'a> { + fn get(&self) -> &'a str { + match *self { + MaybeStaticStr::Static(s) => s, + MaybeStaticStr::Borrowed(s) => s, + //~^ ERROR: this match arm has an identical body to another arm + } + } + } +} diff --git a/src/tools/clippy/tests/ui/match_same_arms2.stderr b/src/tools/clippy/tests/ui/match_same_arms2.stderr index 3d15176ccf9..a5d137c658b 100644 --- a/src/tools/clippy/tests/ui/match_same_arms2.stderr +++ b/src/tools/clippy/tests/ui/match_same_arms2.stderr @@ -221,5 +221,21 @@ help: and remove this obsolete arm LL - 0 => cfg!(not_enable), | -error: aborting due to 13 previous errors +error: this match arm has an identical body to another arm + --> tests/ui/match_same_arms2.rs:277:17 + | +LL | MaybeStaticStr::Borrowed(s) => s, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | MaybeStaticStr::Borrowed(s) | MaybeStaticStr::Static(s) => s, + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - MaybeStaticStr::Static(s) => s, + | + +error: aborting due to 14 previous errors diff --git a/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.fixed b/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.fixed deleted file mode 100644 index de02b2bee31..00000000000 --- a/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.fixed +++ /dev/null @@ -1,25 +0,0 @@ -#![warn(clippy::mismatched_target_os)] -#![allow(unused)] - -#[cfg(target_os = "hermit")] -fn hermit() {} - -#[cfg(target_os = "wasi")] -fn wasi() {} - -#[cfg(target_os = "none")] -fn none() {} - -// list with conditions -#[cfg(all(not(windows), target_os = "wasi"))] -fn list() {} - -// windows is a valid target family, should be ignored -#[cfg(windows)] -fn windows() {} - -// correct use, should be ignored -#[cfg(target_os = "hermit")] -fn correct() {} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.rs b/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.rs deleted file mode 100644 index a960518751b..00000000000 --- a/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.rs +++ /dev/null @@ -1,25 +0,0 @@ -#![warn(clippy::mismatched_target_os)] -#![allow(unused)] - -#[cfg(hermit)] -fn hermit() {} - -#[cfg(wasi)] -fn wasi() {} - -#[cfg(none)] -fn none() {} - -// list with conditions -#[cfg(all(not(windows), wasi))] -fn list() {} - -// windows is a valid target family, should be ignored -#[cfg(windows)] -fn windows() {} - -// correct use, should be ignored -#[cfg(target_os = "hermit")] -fn correct() {} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.stderr b/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.stderr deleted file mode 100644 index 7f7a4e9d6f6..00000000000 --- a/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.stderr +++ /dev/null @@ -1,37 +0,0 @@ -error: operating system used in target family position - --> tests/ui/mismatched_target_os_non_unix.rs:4:1 - | -LL | #[cfg(hermit)] - | ^^^^^^------^^ - | | - | help: try: `target_os = "hermit"` - | - = note: `-D clippy::mismatched-target-os` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::mismatched_target_os)]` - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_non_unix.rs:7:1 - | -LL | #[cfg(wasi)] - | ^^^^^^----^^ - | | - | help: try: `target_os = "wasi"` - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_non_unix.rs:10:1 - | -LL | #[cfg(none)] - | ^^^^^^----^^ - | | - | help: try: `target_os = "none"` - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_non_unix.rs:14:1 - | -LL | #[cfg(all(not(windows), wasi))] - | ^^^^^^^^^^^^^^^^^^^^^^^^----^^^ - | | - | help: try: `target_os = "wasi"` - -error: aborting due to 4 previous errors - diff --git a/src/tools/clippy/tests/ui/mismatched_target_os_unix.fixed b/src/tools/clippy/tests/ui/mismatched_target_os_unix.fixed deleted file mode 100644 index b945c4d9619..00000000000 --- a/src/tools/clippy/tests/ui/mismatched_target_os_unix.fixed +++ /dev/null @@ -1,60 +0,0 @@ -#![warn(clippy::mismatched_target_os)] -#![allow(unused)] - -#[cfg(target_os = "linux")] -fn linux() {} - -#[cfg(target_os = "freebsd")] -fn freebsd() {} - -#[cfg(target_os = "dragonfly")] -fn dragonfly() {} - -#[cfg(target_os = "openbsd")] -fn openbsd() {} - -#[cfg(target_os = "netbsd")] -fn netbsd() {} - -#[cfg(target_os = "macos")] -fn macos() {} - -#[cfg(target_os = "ios")] -fn ios() {} - -#[cfg(target_os = "android")] -fn android() {} - -#[cfg(target_os = "emscripten")] -fn emscripten() {} - -#[cfg(target_os = "fuchsia")] -fn fuchsia() {} - -#[cfg(target_os = "haiku")] -fn haiku() {} - -#[cfg(target_os = "illumos")] -fn illumos() {} - -#[cfg(target_os = "l4re")] -fn l4re() {} - -#[cfg(target_os = "redox")] -fn redox() {} - -#[cfg(target_os = "solaris")] -fn solaris() {} - -#[cfg(target_os = "vxworks")] -fn vxworks() {} - -// list with conditions -#[cfg(all(not(any(target_os = "solaris", target_os = "linux")), target_os = "freebsd"))] -fn list() {} - -// correct use, should be ignored -#[cfg(target_os = "freebsd")] -fn correct() {} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/mismatched_target_os_unix.rs b/src/tools/clippy/tests/ui/mismatched_target_os_unix.rs deleted file mode 100644 index 34307facd65..00000000000 --- a/src/tools/clippy/tests/ui/mismatched_target_os_unix.rs +++ /dev/null @@ -1,60 +0,0 @@ -#![warn(clippy::mismatched_target_os)] -#![allow(unused)] - -#[cfg(linux)] -fn linux() {} - -#[cfg(freebsd)] -fn freebsd() {} - -#[cfg(dragonfly)] -fn dragonfly() {} - -#[cfg(openbsd)] -fn openbsd() {} - -#[cfg(netbsd)] -fn netbsd() {} - -#[cfg(macos)] -fn macos() {} - -#[cfg(ios)] -fn ios() {} - -#[cfg(android)] -fn android() {} - -#[cfg(emscripten)] -fn emscripten() {} - -#[cfg(fuchsia)] -fn fuchsia() {} - -#[cfg(haiku)] -fn haiku() {} - -#[cfg(illumos)] -fn illumos() {} - -#[cfg(l4re)] -fn l4re() {} - -#[cfg(redox)] -fn redox() {} - -#[cfg(solaris)] -fn solaris() {} - -#[cfg(vxworks)] -fn vxworks() {} - -// list with conditions -#[cfg(all(not(any(solaris, linux)), freebsd))] -fn list() {} - -// correct use, should be ignored -#[cfg(target_os = "freebsd")] -fn correct() {} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/mismatched_target_os_unix.stderr b/src/tools/clippy/tests/ui/mismatched_target_os_unix.stderr deleted file mode 100644 index 3071bad1324..00000000000 --- a/src/tools/clippy/tests/ui/mismatched_target_os_unix.stderr +++ /dev/null @@ -1,184 +0,0 @@ -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:4:1 - | -LL | #[cfg(linux)] - | ^^^^^^-----^^ - | | - | help: try: `target_os = "linux"` - | - = help: did you mean `unix`? - = note: `-D clippy::mismatched-target-os` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::mismatched_target_os)]` - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:7:1 - | -LL | #[cfg(freebsd)] - | ^^^^^^-------^^ - | | - | help: try: `target_os = "freebsd"` - | - = help: did you mean `unix`? - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:10:1 - | -LL | #[cfg(dragonfly)] - | ^^^^^^---------^^ - | | - | help: try: `target_os = "dragonfly"` - | - = help: did you mean `unix`? - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:13:1 - | -LL | #[cfg(openbsd)] - | ^^^^^^-------^^ - | | - | help: try: `target_os = "openbsd"` - | - = help: did you mean `unix`? - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:16:1 - | -LL | #[cfg(netbsd)] - | ^^^^^^------^^ - | | - | help: try: `target_os = "netbsd"` - | - = help: did you mean `unix`? - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:19:1 - | -LL | #[cfg(macos)] - | ^^^^^^-----^^ - | | - | help: try: `target_os = "macos"` - | - = help: did you mean `unix`? - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:22:1 - | -LL | #[cfg(ios)] - | ^^^^^^---^^ - | | - | help: try: `target_os = "ios"` - | - = help: did you mean `unix`? - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:25:1 - | -LL | #[cfg(android)] - | ^^^^^^-------^^ - | | - | help: try: `target_os = "android"` - | - = help: did you mean `unix`? - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:28:1 - | -LL | #[cfg(emscripten)] - | ^^^^^^----------^^ - | | - | help: try: `target_os = "emscripten"` - | - = help: did you mean `unix`? - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:31:1 - | -LL | #[cfg(fuchsia)] - | ^^^^^^-------^^ - | | - | help: try: `target_os = "fuchsia"` - | - = help: did you mean `unix`? - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:34:1 - | -LL | #[cfg(haiku)] - | ^^^^^^-----^^ - | | - | help: try: `target_os = "haiku"` - | - = help: did you mean `unix`? - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:37:1 - | -LL | #[cfg(illumos)] - | ^^^^^^-------^^ - | | - | help: try: `target_os = "illumos"` - | - = help: did you mean `unix`? - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:40:1 - | -LL | #[cfg(l4re)] - | ^^^^^^----^^ - | | - | help: try: `target_os = "l4re"` - | - = help: did you mean `unix`? - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:43:1 - | -LL | #[cfg(redox)] - | ^^^^^^-----^^ - | | - | help: try: `target_os = "redox"` - | - = help: did you mean `unix`? - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:46:1 - | -LL | #[cfg(solaris)] - | ^^^^^^-------^^ - | | - | help: try: `target_os = "solaris"` - | - = help: did you mean `unix`? - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:49:1 - | -LL | #[cfg(vxworks)] - | ^^^^^^-------^^ - | | - | help: try: `target_os = "vxworks"` - | - = help: did you mean `unix`? - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:53:1 - | -LL | #[cfg(all(not(any(solaris, linux)), freebsd))] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: did you mean `unix`? -help: try - | -LL | #[cfg(all(not(any(target_os = "solaris", linux)), freebsd))] - | ~~~~~~~~~~~~~~~~~~~~~ -help: try - | -LL | #[cfg(all(not(any(solaris, target_os = "linux")), freebsd))] - | ~~~~~~~~~~~~~~~~~~~ -help: try - | -LL | #[cfg(all(not(any(solaris, linux)), target_os = "freebsd"))] - | ~~~~~~~~~~~~~~~~~~~~~ - -error: aborting due to 17 previous errors - diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs index 06dbbeb31c0..58e639cc7fd 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs @@ -141,3 +141,33 @@ mod msrv { let _ = unsafe { bar.val }; } } + +mod issue12677 { + pub struct Wrapper { + pub strings: Vec<String>, + } + + impl Wrapper { + #[must_use] + pub fn new(strings: Vec<String>) -> Self { + Self { strings } + } + + #[must_use] + pub fn empty() -> Self { + Self { strings: Vec::new() } + } + } + + pub struct Other { + pub text: String, + pub vec: Vec<String>, + } + + impl Other { + pub fn new(text: String) -> Self { + let vec = Vec::new(); + Self { text, vec } + } + } +} diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr index b2cade30563..1c61c3e8713 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr @@ -130,5 +130,30 @@ LL | | let _ = unsafe { bar.val }; LL | | } | |_____^ -error: aborting due to 14 previous errors +error: this could be a `const fn` + --> tests/ui/missing_const_for_fn/could_be_const.rs:152:9 + | +LL | / pub fn new(strings: Vec<String>) -> Self { +LL | | Self { strings } +LL | | } + | |_________^ + +error: this could be a `const fn` + --> tests/ui/missing_const_for_fn/could_be_const.rs:157:9 + | +LL | / pub fn empty() -> Self { +LL | | Self { strings: Vec::new() } +LL | | } + | |_________^ + +error: this could be a `const fn` + --> tests/ui/missing_const_for_fn/could_be_const.rs:168:9 + | +LL | / pub fn new(text: String) -> Self { +LL | | let vec = Vec::new(); +LL | | Self { text, vec } +LL | | } + | |_________^ + +error: aborting due to 17 previous errors diff --git a/src/tools/clippy/tests/ui/missing_fields_in_debug.rs b/src/tools/clippy/tests/ui/missing_fields_in_debug.rs index e91e8ab7f47..68867ec819d 100644 --- a/src/tools/clippy/tests/ui/missing_fields_in_debug.rs +++ b/src/tools/clippy/tests/ui/missing_fields_in_debug.rs @@ -4,6 +4,7 @@ use std::fmt; use std::marker::PhantomData; use std::ops::Deref; +use std::thread::LocalKey; struct NamedStruct1Ignored { data: u8, @@ -191,4 +192,21 @@ impl fmt::Debug for WithPD { } } +struct InClosure { + a: u8, + b: String, +} + +impl fmt::Debug for InClosure { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut d = f.debug_struct("InClosure"); + d.field("a", &self.a); + let mut c = || { + d.field("b", &self.b); + }; + c(); + d.finish() + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/missing_fields_in_debug.stderr b/src/tools/clippy/tests/ui/missing_fields_in_debug.stderr index 6f8a9abe922..8c1810909dd 100644 --- a/src/tools/clippy/tests/ui/missing_fields_in_debug.stderr +++ b/src/tools/clippy/tests/ui/missing_fields_in_debug.stderr @@ -1,5 +1,5 @@ error: manual `Debug` impl does not include all fields - --> tests/ui/missing_fields_in_debug.rs:13:1 + --> tests/ui/missing_fields_in_debug.rs:14:1 | LL | / impl fmt::Debug for NamedStruct1Ignored { LL | | @@ -11,7 +11,7 @@ LL | | } | |_^ | note: this field is unused - --> tests/ui/missing_fields_in_debug.rs:10:5 + --> tests/ui/missing_fields_in_debug.rs:11:5 | LL | hidden: u32, | ^^^^^^^^^^^ @@ -21,7 +21,7 @@ LL | hidden: u32, = help: to override `-D warnings` add `#[allow(clippy::missing_fields_in_debug)]` error: manual `Debug` impl does not include all fields - --> tests/ui/missing_fields_in_debug.rs:32:1 + --> tests/ui/missing_fields_in_debug.rs:33:1 | LL | / impl fmt::Debug for NamedStructMultipleIgnored { LL | | @@ -33,17 +33,17 @@ LL | | } | |_^ | note: this field is unused - --> tests/ui/missing_fields_in_debug.rs:26:5 + --> tests/ui/missing_fields_in_debug.rs:27:5 | LL | hidden: u32, | ^^^^^^^^^^^ note: this field is unused - --> tests/ui/missing_fields_in_debug.rs:27:5 + --> tests/ui/missing_fields_in_debug.rs:28:5 | LL | hidden2: String, | ^^^^^^^^^^^^^^^ note: this field is unused - --> tests/ui/missing_fields_in_debug.rs:29:5 + --> tests/ui/missing_fields_in_debug.rs:30:5 | LL | hidden4: ((((u8), u16), u32), u64), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -51,7 +51,7 @@ LL | hidden4: ((((u8), u16), u32), u64), = help: consider calling `.finish_non_exhaustive()` if you intend to ignore fields error: manual `Debug` impl does not include all fields - --> tests/ui/missing_fields_in_debug.rs:94:1 + --> tests/ui/missing_fields_in_debug.rs:95:1 | LL | / impl fmt::Debug for MultiExprDebugImpl { LL | | @@ -63,7 +63,7 @@ LL | | } | |_^ | note: this field is unused - --> tests/ui/missing_fields_in_debug.rs:90:5 + --> tests/ui/missing_fields_in_debug.rs:91:5 | LL | b: String, | ^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.fixed b/src/tools/clippy/tests/ui/needless_bool/fixable.fixed index 3059de8f89c..ec63c4fd6a2 100644 --- a/src/tools/clippy/tests/ui/needless_bool/fixable.fixed +++ b/src/tools/clippy/tests/ui/needless_bool/fixable.fixed @@ -131,3 +131,15 @@ fn needless_bool_condition() -> bool { foo() } + +fn issue12846() { + let a = true; + let b = false; + + // parentheses are needed here + let _x = (a && b).then(|| todo!()); + let _x = (a && b) as u8; + + // parentheses are not needed here + let _x = a.then(|| todo!()); +} diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.rs b/src/tools/clippy/tests/ui/needless_bool/fixable.rs index b2cbe86e223..8694aa71590 100644 --- a/src/tools/clippy/tests/ui/needless_bool/fixable.rs +++ b/src/tools/clippy/tests/ui/needless_bool/fixable.rs @@ -191,3 +191,15 @@ fn needless_bool_condition() -> bool { foo() } + +fn issue12846() { + let a = true; + let b = false; + + // parentheses are needed here + let _x = if a && b { true } else { false }.then(|| todo!()); + let _x = if a && b { true } else { false } as u8; + + // parentheses are not needed here + let _x = if a { true } else { false }.then(|| todo!()); +} diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.stderr b/src/tools/clippy/tests/ui/needless_bool/fixable.stderr index 9746e931f50..99b5b998344 100644 --- a/src/tools/clippy/tests/ui/needless_bool/fixable.stderr +++ b/src/tools/clippy/tests/ui/needless_bool/fixable.stderr @@ -191,5 +191,23 @@ error: this if-then-else expression returns a bool literal LL | if unsafe { no(4) } & 1 != 0 { true } else { false } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `(unsafe { no(4) } & 1 != 0)` -error: aborting due to 21 previous errors +error: this if-then-else expression returns a bool literal + --> tests/ui/needless_bool/fixable.rs:200:14 + | +LL | let _x = if a && b { true } else { false }.then(|| todo!()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `(a && b)` + +error: this if-then-else expression returns a bool literal + --> tests/ui/needless_bool/fixable.rs:201:14 + | +LL | let _x = if a && b { true } else { false } as u8; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `(a && b)` + +error: this if-then-else expression returns a bool literal + --> tests/ui/needless_bool/fixable.rs:204:14 + | +LL | let _x = if a { true } else { false }.then(|| todo!()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `a` + +error: aborting due to 24 previous errors diff --git a/src/tools/clippy/tests/ui/needless_character_iteration.fixed b/src/tools/clippy/tests/ui/needless_character_iteration.fixed new file mode 100644 index 00000000000..f0bf84a41d7 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_character_iteration.fixed @@ -0,0 +1,57 @@ +#![warn(clippy::needless_character_iteration)] +#![allow(clippy::map_identity, clippy::unnecessary_operation)] + +#[derive(Default)] +struct S { + field: &'static str, +} + +impl S { + fn field(&self) -> &str { + self.field + } +} + +fn magic(_: char) {} + +fn main() { + "foo".is_ascii(); + //~^ ERROR: checking if a string is ascii using iterators + !"foo".is_ascii(); + //~^ ERROR: checking if a string is ascii using iterators + "foo".is_ascii(); + //~^ ERROR: checking if a string is ascii using iterators + !"foo".is_ascii(); + //~^ ERROR: checking if a string is ascii using iterators + + let s = String::new(); + s.is_ascii(); + //~^ ERROR: checking if a string is ascii using iterators + !"foo".to_string().is_ascii(); + //~^ ERROR: checking if a string is ascii using iterators + + "foo".is_ascii(); + !"foo".is_ascii(); + + S::default().field().is_ascii(); + //~^ ERROR: checking if a string is ascii using iterators + + // Should not lint! + "foo".chars().all(|c| { + let x = c; + magic(x); + x.is_ascii() + }); + + // Should not lint! + "foo".chars().all(|c| c.is_ascii() && c.is_alphabetic()); + + // Should not lint! + "foo".chars().map(|c| c).all(|c| !char::is_ascii(&c)); + + // Should not lint! + "foo".chars().all(|c| !c.is_ascii()); + + // Should not lint! + "foo".chars().any(|c| c.is_ascii()); +} diff --git a/src/tools/clippy/tests/ui/needless_character_iteration.rs b/src/tools/clippy/tests/ui/needless_character_iteration.rs new file mode 100644 index 00000000000..2805d2438b4 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_character_iteration.rs @@ -0,0 +1,65 @@ +#![warn(clippy::needless_character_iteration)] +#![allow(clippy::map_identity, clippy::unnecessary_operation)] + +#[derive(Default)] +struct S { + field: &'static str, +} + +impl S { + fn field(&self) -> &str { + self.field + } +} + +fn magic(_: char) {} + +fn main() { + "foo".chars().all(|c| c.is_ascii()); + //~^ ERROR: checking if a string is ascii using iterators + "foo".chars().any(|c| !c.is_ascii()); + //~^ ERROR: checking if a string is ascii using iterators + "foo".chars().all(|c| char::is_ascii(&c)); + //~^ ERROR: checking if a string is ascii using iterators + "foo".chars().any(|c| !char::is_ascii(&c)); + //~^ ERROR: checking if a string is ascii using iterators + + let s = String::new(); + s.chars().all(|c| c.is_ascii()); + //~^ ERROR: checking if a string is ascii using iterators + "foo".to_string().chars().any(|c| !c.is_ascii()); + //~^ ERROR: checking if a string is ascii using iterators + + "foo".chars().all(|c| { + //~^ ERROR: checking if a string is ascii using iterators + let x = c; + x.is_ascii() + }); + "foo".chars().any(|c| { + //~^ ERROR: checking if a string is ascii using iterators + let x = c; + !x.is_ascii() + }); + + S::default().field().chars().all(|x| x.is_ascii()); + //~^ ERROR: checking if a string is ascii using iterators + + // Should not lint! + "foo".chars().all(|c| { + let x = c; + magic(x); + x.is_ascii() + }); + + // Should not lint! + "foo".chars().all(|c| c.is_ascii() && c.is_alphabetic()); + + // Should not lint! + "foo".chars().map(|c| c).all(|c| !char::is_ascii(&c)); + + // Should not lint! + "foo".chars().all(|c| !c.is_ascii()); + + // Should not lint! + "foo".chars().any(|c| c.is_ascii()); +} diff --git a/src/tools/clippy/tests/ui/needless_character_iteration.stderr b/src/tools/clippy/tests/ui/needless_character_iteration.stderr new file mode 100644 index 00000000000..7966033555f --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_character_iteration.stderr @@ -0,0 +1,67 @@ +error: checking if a string is ascii using iterators + --> tests/ui/needless_character_iteration.rs:18:5 + | +LL | "foo".chars().all(|c| c.is_ascii()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"foo".is_ascii()` + | + = note: `-D clippy::needless-character-iteration` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::needless_character_iteration)]` + +error: checking if a string is ascii using iterators + --> tests/ui/needless_character_iteration.rs:20:5 + | +LL | "foo".chars().any(|c| !c.is_ascii()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!"foo".is_ascii()` + +error: checking if a string is ascii using iterators + --> tests/ui/needless_character_iteration.rs:22:5 + | +LL | "foo".chars().all(|c| char::is_ascii(&c)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"foo".is_ascii()` + +error: checking if a string is ascii using iterators + --> tests/ui/needless_character_iteration.rs:24:5 + | +LL | "foo".chars().any(|c| !char::is_ascii(&c)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!"foo".is_ascii()` + +error: checking if a string is ascii using iterators + --> tests/ui/needless_character_iteration.rs:28:5 + | +LL | s.chars().all(|c| c.is_ascii()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.is_ascii()` + +error: checking if a string is ascii using iterators + --> tests/ui/needless_character_iteration.rs:30:5 + | +LL | "foo".to_string().chars().any(|c| !c.is_ascii()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!"foo".to_string().is_ascii()` + +error: checking if a string is ascii using iterators + --> tests/ui/needless_character_iteration.rs:33:5 + | +LL | / "foo".chars().all(|c| { +LL | | +LL | | let x = c; +LL | | x.is_ascii() +LL | | }); + | |______^ help: try: `"foo".is_ascii()` + +error: checking if a string is ascii using iterators + --> tests/ui/needless_character_iteration.rs:38:5 + | +LL | / "foo".chars().any(|c| { +LL | | +LL | | let x = c; +LL | | !x.is_ascii() +LL | | }); + | |______^ help: try: `!"foo".is_ascii()` + +error: checking if a string is ascii using iterators + --> tests/ui/needless_character_iteration.rs:44:5 + | +LL | S::default().field().chars().all(|x| x.is_ascii()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `S::default().field().is_ascii()` + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_maybe_sized.fixed b/src/tools/clippy/tests/ui/needless_maybe_sized.fixed new file mode 100644 index 00000000000..4d24a7cee61 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_maybe_sized.fixed @@ -0,0 +1,116 @@ +//@aux-build:proc_macros.rs + +#![allow(unused, clippy::multiple_bound_locations)] +#![warn(clippy::needless_maybe_sized)] + +extern crate proc_macros; +use proc_macros::external; + +fn directly<T: Sized>(t: &T) {} + +trait A: Sized {} +trait B: A {} + +fn depth_1<T: A>(t: &T) {} +fn depth_2<T: B>(t: &T) {} + +// We only need to show one +fn multiple_paths<T: A + B>(t: &T) {} + +fn in_where<T>(t: &T) +where + T: Sized, +{ +} + +fn mixed_1<T: Sized>(t: &T) +{ +} + +fn mixed_2<T>(t: &T) +where + T: Sized, +{ +} + +fn mixed_3<T>(t: &T) +where + T: Sized, +{ +} + +struct Struct<T: Sized>(T); + +impl<T: Sized> Struct<T> { + fn method<U: Sized>(&self) {} +} + +enum Enum<T: Sized + 'static> { + Variant(&'static T), +} + +union Union<'a, T: Sized> { + a: &'a T, +} + +trait Trait<T: Sized> { + fn trait_method<U: Sized>() {} + + type GAT<U: Sized>; + + type Assoc: Sized + ?Sized; // False negative +} + +trait SecondInTrait: Send + Sized {} +fn second_in_trait<T: SecondInTrait>() {} + +fn impl_trait(_: &(impl Sized)) {} + +trait GenericTrait<T>: Sized {} +fn in_generic_trait<T: GenericTrait<U>, U>() {} + +mod larger_graph { + // C1 C2 Sized + // \ /\ / + // B1 B2 + // \ / + // A1 + + trait C1 {} + trait C2 {} + trait B1: C1 + C2 {} + trait B2: C2 + Sized {} + trait A1: B1 + B2 {} + + fn larger_graph<T: A1>() {} +} + +// Should not lint + +fn sized<T: Sized>() {} +fn maybe_sized<T: ?Sized>() {} + +struct SeparateBounds<T: ?Sized>(T); +impl<T: Sized> SeparateBounds<T> {} + +trait P {} +trait Q: P {} + +fn ok_depth_1<T: P + ?Sized>() {} +fn ok_depth_2<T: Q + ?Sized>() {} + +external! { + fn in_macro<T: Clone + ?Sized>(t: &T) {} + + fn with_local_clone<T: $Clone + ?Sized>(t: &T) {} +} + +#[derive(Clone)] +struct InDerive<T: ?Sized> { + t: T, +} + +struct Refined<T: ?Sized>(T); +impl<T: Sized> Refined<T> {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_maybe_sized.rs b/src/tools/clippy/tests/ui/needless_maybe_sized.rs new file mode 100644 index 00000000000..ef66f9a4f2a --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_maybe_sized.rs @@ -0,0 +1,119 @@ +//@aux-build:proc_macros.rs + +#![allow(unused, clippy::multiple_bound_locations)] +#![warn(clippy::needless_maybe_sized)] + +extern crate proc_macros; +use proc_macros::external; + +fn directly<T: Sized + ?Sized>(t: &T) {} + +trait A: Sized {} +trait B: A {} + +fn depth_1<T: A + ?Sized>(t: &T) {} +fn depth_2<T: B + ?Sized>(t: &T) {} + +// We only need to show one +fn multiple_paths<T: A + B + ?Sized>(t: &T) {} + +fn in_where<T>(t: &T) +where + T: Sized + ?Sized, +{ +} + +fn mixed_1<T: Sized>(t: &T) +where + T: ?Sized, +{ +} + +fn mixed_2<T: ?Sized>(t: &T) +where + T: Sized, +{ +} + +fn mixed_3<T>(t: &T) +where + T: Sized, + T: ?Sized, +{ +} + +struct Struct<T: Sized + ?Sized>(T); + +impl<T: Sized + ?Sized> Struct<T> { + fn method<U: Sized + ?Sized>(&self) {} +} + +enum Enum<T: Sized + ?Sized + 'static> { + Variant(&'static T), +} + +union Union<'a, T: Sized + ?Sized> { + a: &'a T, +} + +trait Trait<T: Sized + ?Sized> { + fn trait_method<U: Sized + ?Sized>() {} + + type GAT<U: Sized + ?Sized>; + + type Assoc: Sized + ?Sized; // False negative +} + +trait SecondInTrait: Send + Sized {} +fn second_in_trait<T: ?Sized + SecondInTrait>() {} + +fn impl_trait(_: &(impl Sized + ?Sized)) {} + +trait GenericTrait<T>: Sized {} +fn in_generic_trait<T: GenericTrait<U> + ?Sized, U>() {} + +mod larger_graph { + // C1 C2 Sized + // \ /\ / + // B1 B2 + // \ / + // A1 + + trait C1 {} + trait C2 {} + trait B1: C1 + C2 {} + trait B2: C2 + Sized {} + trait A1: B1 + B2 {} + + fn larger_graph<T: A1 + ?Sized>() {} +} + +// Should not lint + +fn sized<T: Sized>() {} +fn maybe_sized<T: ?Sized>() {} + +struct SeparateBounds<T: ?Sized>(T); +impl<T: Sized> SeparateBounds<T> {} + +trait P {} +trait Q: P {} + +fn ok_depth_1<T: P + ?Sized>() {} +fn ok_depth_2<T: Q + ?Sized>() {} + +external! { + fn in_macro<T: Clone + ?Sized>(t: &T) {} + + fn with_local_clone<T: $Clone + ?Sized>(t: &T) {} +} + +#[derive(Clone)] +struct InDerive<T: ?Sized> { + t: T, +} + +struct Refined<T: ?Sized>(T); +impl<T: Sized> Refined<T> {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_maybe_sized.stderr b/src/tools/clippy/tests/ui/needless_maybe_sized.stderr new file mode 100644 index 00000000000..3b1d2b49b06 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_maybe_sized.stderr @@ -0,0 +1,353 @@ +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:9:24 + | +LL | fn directly<T: Sized + ?Sized>(t: &T) {} + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:9:16 + | +LL | fn directly<T: Sized + ?Sized>(t: &T) {} + | ^^^^^ + = note: `-D clippy::needless-maybe-sized` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::needless_maybe_sized)]` +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn directly<T: Sized + ?Sized>(t: &T) {} +LL + fn directly<T: Sized>(t: &T) {} + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:14:19 + | +LL | fn depth_1<T: A + ?Sized>(t: &T) {} + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:14:15 + | +LL | fn depth_1<T: A + ?Sized>(t: &T) {} + | ^ + = note: ...because `A` has the bound `Sized` +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn depth_1<T: A + ?Sized>(t: &T) {} +LL + fn depth_1<T: A>(t: &T) {} + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:15:19 + | +LL | fn depth_2<T: B + ?Sized>(t: &T) {} + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:15:15 + | +LL | fn depth_2<T: B + ?Sized>(t: &T) {} + | ^ + = note: ...because `B` has the bound `A` + = note: ...because `A` has the bound `Sized` +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn depth_2<T: B + ?Sized>(t: &T) {} +LL + fn depth_2<T: B>(t: &T) {} + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:18:30 + | +LL | fn multiple_paths<T: A + B + ?Sized>(t: &T) {} + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:18:22 + | +LL | fn multiple_paths<T: A + B + ?Sized>(t: &T) {} + | ^ + = note: ...because `A` has the bound `Sized` +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn multiple_paths<T: A + B + ?Sized>(t: &T) {} +LL + fn multiple_paths<T: A + B>(t: &T) {} + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:22:16 + | +LL | T: Sized + ?Sized, + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:22:8 + | +LL | T: Sized + ?Sized, + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - T: Sized + ?Sized, +LL + T: Sized, + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:28:8 + | +LL | T: ?Sized, + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:26:15 + | +LL | fn mixed_1<T: Sized>(t: &T) + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - where +LL - T: ?Sized, + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:32:15 + | +LL | fn mixed_2<T: ?Sized>(t: &T) + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:34:8 + | +LL | T: Sized, + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn mixed_2<T: ?Sized>(t: &T) +LL + fn mixed_2<T>(t: &T) + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:41:8 + | +LL | T: ?Sized, + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:40:8 + | +LL | T: Sized, + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - T: Sized, +LL - T: ?Sized, +LL + T: Sized, + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:45:26 + | +LL | struct Struct<T: Sized + ?Sized>(T); + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:45:18 + | +LL | struct Struct<T: Sized + ?Sized>(T); + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - struct Struct<T: Sized + ?Sized>(T); +LL + struct Struct<T: Sized>(T); + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:47:17 + | +LL | impl<T: Sized + ?Sized> Struct<T> { + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:47:9 + | +LL | impl<T: Sized + ?Sized> Struct<T> { + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - impl<T: Sized + ?Sized> Struct<T> { +LL + impl<T: Sized> Struct<T> { + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:48:26 + | +LL | fn method<U: Sized + ?Sized>(&self) {} + | ^^^^^^ + | +note: `U` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:48:18 + | +LL | fn method<U: Sized + ?Sized>(&self) {} + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn method<U: Sized + ?Sized>(&self) {} +LL + fn method<U: Sized>(&self) {} + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:51:22 + | +LL | enum Enum<T: Sized + ?Sized + 'static> { + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:51:14 + | +LL | enum Enum<T: Sized + ?Sized + 'static> { + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - enum Enum<T: Sized + ?Sized + 'static> { +LL + enum Enum<T: Sized + 'static> { + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:55:28 + | +LL | union Union<'a, T: Sized + ?Sized> { + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:55:20 + | +LL | union Union<'a, T: Sized + ?Sized> { + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - union Union<'a, T: Sized + ?Sized> { +LL + union Union<'a, T: Sized> { + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:59:24 + | +LL | trait Trait<T: Sized + ?Sized> { + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:59:16 + | +LL | trait Trait<T: Sized + ?Sized> { + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - trait Trait<T: Sized + ?Sized> { +LL + trait Trait<T: Sized> { + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:60:32 + | +LL | fn trait_method<U: Sized + ?Sized>() {} + | ^^^^^^ + | +note: `U` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:60:24 + | +LL | fn trait_method<U: Sized + ?Sized>() {} + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn trait_method<U: Sized + ?Sized>() {} +LL + fn trait_method<U: Sized>() {} + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:62:25 + | +LL | type GAT<U: Sized + ?Sized>; + | ^^^^^^ + | +note: `U` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:62:17 + | +LL | type GAT<U: Sized + ?Sized>; + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - type GAT<U: Sized + ?Sized>; +LL + type GAT<U: Sized>; + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:68:23 + | +LL | fn second_in_trait<T: ?Sized + SecondInTrait>() {} + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:68:32 + | +LL | fn second_in_trait<T: ?Sized + SecondInTrait>() {} + | ^^^^^^^^^^^^^ + = note: ...because `SecondInTrait` has the bound `Sized` +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn second_in_trait<T: ?Sized + SecondInTrait>() {} +LL + fn second_in_trait<T: SecondInTrait>() {} + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:70:33 + | +LL | fn impl_trait(_: &(impl Sized + ?Sized)) {} + | ^^^^^^ + | +note: `impl Sized + ?Sized` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:70:25 + | +LL | fn impl_trait(_: &(impl Sized + ?Sized)) {} + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn impl_trait(_: &(impl Sized + ?Sized)) {} +LL + fn impl_trait(_: &(impl Sized)) {} + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:73:42 + | +LL | fn in_generic_trait<T: GenericTrait<U> + ?Sized, U>() {} + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:73:24 + | +LL | fn in_generic_trait<T: GenericTrait<U> + ?Sized, U>() {} + | ^^^^^^^^^^^^^^^ + = note: ...because `GenericTrait` has the bound `Sized` +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn in_generic_trait<T: GenericTrait<U> + ?Sized, U>() {} +LL + fn in_generic_trait<T: GenericTrait<U>, U>() {} + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:88:29 + | +LL | fn larger_graph<T: A1 + ?Sized>() {} + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:88:24 + | +LL | fn larger_graph<T: A1 + ?Sized>() {} + | ^^ + = note: ...because `A1` has the bound `B2` + = note: ...because `B2` has the bound `Sized` +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn larger_graph<T: A1 + ?Sized>() {} +LL + fn larger_graph<T: A1>() {} + | + +error: aborting due to 20 previous errors + diff --git a/src/tools/clippy/tests/ui/new_ret_no_self.rs b/src/tools/clippy/tests/ui/new_ret_no_self.rs index b944f531ef6..175b14d815a 100644 --- a/src/tools/clippy/tests/ui/new_ret_no_self.rs +++ b/src/tools/clippy/tests/ui/new_ret_no_self.rs @@ -390,9 +390,7 @@ mod issue7344 { impl<T> RetImplTraitSelf2<T> { // should not trigger lint - fn new(t: T) -> impl Trait2<(), Self> { - unimplemented!() - } + fn new(t: T) -> impl Trait2<(), Self> {} } struct RetImplTraitNoSelf2<T>(T); @@ -401,7 +399,6 @@ mod issue7344 { // should trigger lint fn new(t: T) -> impl Trait2<(), i32> { //~^ ERROR: methods called `new` usually return `Self` - unimplemented!() } } diff --git a/src/tools/clippy/tests/ui/new_ret_no_self.stderr b/src/tools/clippy/tests/ui/new_ret_no_self.stderr index d440a9f45fc..3597ad65838 100644 --- a/src/tools/clippy/tests/ui/new_ret_no_self.stderr +++ b/src/tools/clippy/tests/ui/new_ret_no_self.stderr @@ -96,11 +96,10 @@ LL | | } | |_________^ error: methods called `new` usually return `Self` - --> tests/ui/new_ret_no_self.rs:402:9 + --> tests/ui/new_ret_no_self.rs:400:9 | LL | / fn new(t: T) -> impl Trait2<(), i32> { LL | | -LL | | unimplemented!() LL | | } | |_________^ diff --git a/src/tools/clippy/tests/ui/non_canonical_clone_impl.fixed b/src/tools/clippy/tests/ui/non_canonical_clone_impl.fixed index 7d1be412e54..11616f28825 100644 --- a/src/tools/clippy/tests/ui/non_canonical_clone_impl.fixed +++ b/src/tools/clippy/tests/ui/non_canonical_clone_impl.fixed @@ -1,7 +1,11 @@ +//@aux-build:proc_macro_derive.rs #![allow(clippy::clone_on_copy, unused)] #![allow(clippy::assigning_clones)] #![no_main] +extern crate proc_macros; +use proc_macros::with_span; + // lint struct A(u32); @@ -95,3 +99,19 @@ impl<A: Copy> Clone for Uwu<A> { } impl<A: std::fmt::Debug + Copy + Clone> Copy for Uwu<A> {} + +// should skip proc macros, see https://github.com/rust-lang/rust-clippy/issues/12788 +#[derive(proc_macro_derive::NonCanonicalClone)] +pub struct G; + +with_span!( + span + + #[derive(Copy)] + struct H; + impl Clone for H { + fn clone(&self) -> Self { + todo!() + } + } +); diff --git a/src/tools/clippy/tests/ui/non_canonical_clone_impl.rs b/src/tools/clippy/tests/ui/non_canonical_clone_impl.rs index beae05efb2f..a36c7ed44c2 100644 --- a/src/tools/clippy/tests/ui/non_canonical_clone_impl.rs +++ b/src/tools/clippy/tests/ui/non_canonical_clone_impl.rs @@ -1,7 +1,11 @@ +//@aux-build:proc_macro_derive.rs #![allow(clippy::clone_on_copy, unused)] #![allow(clippy::assigning_clones)] #![no_main] +extern crate proc_macros; +use proc_macros::with_span; + // lint struct A(u32); @@ -105,3 +109,19 @@ impl<A: Copy> Clone for Uwu<A> { } impl<A: std::fmt::Debug + Copy + Clone> Copy for Uwu<A> {} + +// should skip proc macros, see https://github.com/rust-lang/rust-clippy/issues/12788 +#[derive(proc_macro_derive::NonCanonicalClone)] +pub struct G; + +with_span!( + span + + #[derive(Copy)] + struct H; + impl Clone for H { + fn clone(&self) -> Self { + todo!() + } + } +); diff --git a/src/tools/clippy/tests/ui/non_canonical_clone_impl.stderr b/src/tools/clippy/tests/ui/non_canonical_clone_impl.stderr index 6bfc99d988b..f7cad58150f 100644 --- a/src/tools/clippy/tests/ui/non_canonical_clone_impl.stderr +++ b/src/tools/clippy/tests/ui/non_canonical_clone_impl.stderr @@ -1,5 +1,5 @@ error: non-canonical implementation of `clone` on a `Copy` type - --> tests/ui/non_canonical_clone_impl.rs:10:29 + --> tests/ui/non_canonical_clone_impl.rs:14:29 | LL | fn clone(&self) -> Self { | _____________________________^ @@ -11,7 +11,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::non_canonical_clone_impl)]` error: unnecessary implementation of `clone_from` on a `Copy` type - --> tests/ui/non_canonical_clone_impl.rs:14:5 + --> tests/ui/non_canonical_clone_impl.rs:18:5 | LL | / fn clone_from(&mut self, source: &Self) { LL | | source.clone(); @@ -20,7 +20,7 @@ LL | | } | |_____^ help: remove it error: non-canonical implementation of `clone` on a `Copy` type - --> tests/ui/non_canonical_clone_impl.rs:81:29 + --> tests/ui/non_canonical_clone_impl.rs:85:29 | LL | fn clone(&self) -> Self { | _____________________________^ @@ -29,7 +29,7 @@ LL | | } | |_____^ help: change this to: `{ *self }` error: unnecessary implementation of `clone_from` on a `Copy` type - --> tests/ui/non_canonical_clone_impl.rs:85:5 + --> tests/ui/non_canonical_clone_impl.rs:89:5 | LL | / fn clone_from(&mut self, source: &Self) { LL | | source.clone(); diff --git a/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed b/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed index aba599678e3..cc91ba6ec66 100644 --- a/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed +++ b/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed @@ -1,5 +1,3 @@ -//@compile-flags: -Zdeduplicate-diagnostics=yes - #![allow(unused, clippy::diverging_sub_expression, clippy::needless_if)] #![warn(clippy::nonminimal_bool)] diff --git a/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs b/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs index 35f22db1d36..c812f6f0ca4 100644 --- a/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs +++ b/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs @@ -1,5 +1,3 @@ -//@compile-flags: -Zdeduplicate-diagnostics=yes - #![allow(unused, clippy::diverging_sub_expression, clippy::needless_if)] #![warn(clippy::nonminimal_bool)] diff --git a/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr b/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr index 18da4e0d380..d7adc0638b3 100644 --- a/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr +++ b/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr @@ -1,5 +1,5 @@ error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:10:13 + --> tests/ui/nonminimal_bool_methods.rs:8:13 | LL | let _ = !a.is_some(); | ^^^^^^^^^^^^ help: try: `a.is_none()` @@ -8,91 +8,91 @@ LL | let _ = !a.is_some(); = help: to override `-D warnings` add `#[allow(clippy::nonminimal_bool)]` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:12:13 + --> tests/ui/nonminimal_bool_methods.rs:10:13 | LL | let _ = !a.is_none(); | ^^^^^^^^^^^^ help: try: `a.is_some()` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:14:13 + --> tests/ui/nonminimal_bool_methods.rs:12:13 | LL | let _ = !b.is_err(); | ^^^^^^^^^^^ help: try: `b.is_ok()` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:16:13 + --> tests/ui/nonminimal_bool_methods.rs:14:13 | LL | let _ = !b.is_ok(); | ^^^^^^^^^^ help: try: `b.is_err()` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:18:13 + --> tests/ui/nonminimal_bool_methods.rs:16:13 | LL | let _ = !(a.is_some() && !c); | ^^^^^^^^^^^^^^^^^^^^ help: try: `a.is_none() || c` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:19:13 + --> tests/ui/nonminimal_bool_methods.rs:17:13 | LL | let _ = !(a.is_some() || !c); | ^^^^^^^^^^^^^^^^^^^^ help: try: `a.is_none() && c` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:20:26 + --> tests/ui/nonminimal_bool_methods.rs:18:26 | LL | let _ = !(!c ^ c) || !a.is_some(); | ^^^^^^^^^^^^ help: try: `a.is_none()` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:21:25 + --> tests/ui/nonminimal_bool_methods.rs:19:25 | LL | let _ = (!c ^ c) || !a.is_some(); | ^^^^^^^^^^^^ help: try: `a.is_none()` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:22:23 + --> tests/ui/nonminimal_bool_methods.rs:20:23 | LL | let _ = !c ^ c || !a.is_some(); | ^^^^^^^^^^^^ help: try: `a.is_none()` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:94:8 + --> tests/ui/nonminimal_bool_methods.rs:92:8 | LL | if !res.is_ok() {} | ^^^^^^^^^^^^ help: try: `res.is_err()` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:95:8 + --> tests/ui/nonminimal_bool_methods.rs:93:8 | LL | if !res.is_err() {} | ^^^^^^^^^^^^^ help: try: `res.is_ok()` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:98:8 + --> tests/ui/nonminimal_bool_methods.rs:96:8 | LL | if !res.is_some() {} | ^^^^^^^^^^^^^^ help: try: `res.is_none()` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:99:8 + --> tests/ui/nonminimal_bool_methods.rs:97:8 | LL | if !res.is_none() {} | ^^^^^^^^^^^^^^ help: try: `res.is_some()` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:115:8 + --> tests/ui/nonminimal_bool_methods.rs:113:8 | LL | if !(a as u64 >= b) {} | ^^^^^^^^^^^^^^^^ help: try: `(a as u64) < b` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:116:8 + --> tests/ui/nonminimal_bool_methods.rs:114:8 | LL | if !((a as u64) >= b) {} | ^^^^^^^^^^^^^^^^^^ help: try: `(a as u64) < b` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:117:8 + --> tests/ui/nonminimal_bool_methods.rs:115:8 | LL | if !(a as u64 <= b) {} | ^^^^^^^^^^^^^^^^ help: try: `a as u64 > b` diff --git a/src/tools/clippy/tests/ui/or_fun_call.fixed b/src/tools/clippy/tests/ui/or_fun_call.fixed index e7ba54864ab..7657ef470c5 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.fixed +++ b/src/tools/clippy/tests/ui/or_fun_call.fixed @@ -311,4 +311,11 @@ mod lazy { } } +fn host_effect() { + // #12877 - make sure we don't ICE in type_certainty + use std::ops::Add; + + Add::<i32>::add(1, 1).add(i32::MIN); +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/or_fun_call.rs b/src/tools/clippy/tests/ui/or_fun_call.rs index 196632133d5..97cf496d3ac 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.rs +++ b/src/tools/clippy/tests/ui/or_fun_call.rs @@ -311,4 +311,11 @@ mod lazy { } } +fn host_effect() { + // #12877 - make sure we don't ICE in type_certainty + use std::ops::Add; + + Add::<i32>::add(1, 1).add(i32::MIN); +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/overly_complex_bool_expr.fixed b/src/tools/clippy/tests/ui/overly_complex_bool_expr.fixed index e44f6063156..439b1145431 100644 --- a/src/tools/clippy/tests/ui/overly_complex_bool_expr.fixed +++ b/src/tools/clippy/tests/ui/overly_complex_bool_expr.fixed @@ -37,3 +37,13 @@ fn check_expect() { #[expect(clippy::overly_complex_bool_expr)] let _ = a < b && a >= b; } + +#[allow(clippy::never_loop)] +fn check_never_type() { + loop { + _ = (break) || true; + } + loop { + _ = (return) || true; + } +} diff --git a/src/tools/clippy/tests/ui/overly_complex_bool_expr.rs b/src/tools/clippy/tests/ui/overly_complex_bool_expr.rs index f010a8537e7..b96fd1adf11 100644 --- a/src/tools/clippy/tests/ui/overly_complex_bool_expr.rs +++ b/src/tools/clippy/tests/ui/overly_complex_bool_expr.rs @@ -37,3 +37,13 @@ fn check_expect() { #[expect(clippy::overly_complex_bool_expr)] let _ = a < b && a >= b; } + +#[allow(clippy::never_loop)] +fn check_never_type() { + loop { + _ = (break) || true; + } + loop { + _ = (return) || true; + } +} diff --git a/src/tools/clippy/tests/ui/panic_in_result_fn.rs b/src/tools/clippy/tests/ui/panic_in_result_fn.rs index 41e2f522689..aaaf9a109ac 100644 --- a/src/tools/clippy/tests/ui/panic_in_result_fn.rs +++ b/src/tools/clippy/tests/ui/panic_in_result_fn.rs @@ -56,6 +56,11 @@ fn function_result_with_panic() -> Result<bool, String> // should emit lint panic!("error"); } +fn in_closure() -> Result<bool, String> { + let c = || panic!(); + c() +} + fn todo() { println!("something"); } diff --git a/src/tools/clippy/tests/ui/panic_in_result_fn.stderr b/src/tools/clippy/tests/ui/panic_in_result_fn.stderr index cd2234bdfb1..2d49b5ab1b8 100644 --- a/src/tools/clippy/tests/ui/panic_in_result_fn.stderr +++ b/src/tools/clippy/tests/ui/panic_in_result_fn.stderr @@ -34,5 +34,21 @@ note: return Err() instead of panicking LL | panic!("error"); | ^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: used `panic!()` or assertion in a function that returns `Result` + --> tests/ui/panic_in_result_fn.rs:59:1 + | +LL | / fn in_closure() -> Result<bool, String> { +LL | | let c = || panic!(); +LL | | c() +LL | | } + | |_^ + | + = help: `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> tests/ui/panic_in_result_fn.rs:60:16 + | +LL | let c = || panic!(); + | ^^^^^^^^ + +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/search_is_some.rs b/src/tools/clippy/tests/ui/search_is_some.rs index e8a0920b645..9a9aaba56ad 100644 --- a/src/tools/clippy/tests/ui/search_is_some.rs +++ b/src/tools/clippy/tests/ui/search_is_some.rs @@ -1,5 +1,6 @@ //@aux-build:option_helpers.rs #![warn(clippy::search_is_some)] +#![allow(clippy::manual_pattern_char_comparison)] #![allow(clippy::useless_vec)] #![allow(dead_code)] extern crate option_helpers; diff --git a/src/tools/clippy/tests/ui/search_is_some.stderr b/src/tools/clippy/tests/ui/search_is_some.stderr index b5f84d23284..b5ef5534177 100644 --- a/src/tools/clippy/tests/ui/search_is_some.stderr +++ b/src/tools/clippy/tests/ui/search_is_some.stderr @@ -1,5 +1,5 @@ error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some.rs:15:13 + --> tests/ui/search_is_some.rs:16:13 | LL | let _ = v.iter().find(|&x| { | _____________^ @@ -13,7 +13,7 @@ LL | | ).is_some(); = help: to override `-D warnings` add `#[allow(clippy::search_is_some)]` error: called `is_some()` after searching an `Iterator` with `position` - --> tests/ui/search_is_some.rs:21:13 + --> tests/ui/search_is_some.rs:22:13 | LL | let _ = v.iter().position(|&x| { | _____________^ @@ -25,7 +25,7 @@ LL | | ).is_some(); = help: this is more succinctly expressed by calling `any()` error: called `is_some()` after searching an `Iterator` with `rposition` - --> tests/ui/search_is_some.rs:27:13 + --> tests/ui/search_is_some.rs:28:13 | LL | let _ = v.iter().rposition(|&x| { | _____________^ @@ -37,13 +37,13 @@ LL | | ).is_some(); = help: this is more succinctly expressed by calling `any()` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some.rs:42:20 + --> tests/ui/search_is_some.rs:43:20 | LL | let _ = (0..1).find(some_closure).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(some_closure)` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some.rs:52:13 + --> tests/ui/search_is_some.rs:53:13 | LL | let _ = v.iter().find(|&x| { | _____________^ @@ -55,7 +55,7 @@ LL | | ).is_none(); = help: this is more succinctly expressed by calling `any()` with negation error: called `is_none()` after searching an `Iterator` with `position` - --> tests/ui/search_is_some.rs:58:13 + --> tests/ui/search_is_some.rs:59:13 | LL | let _ = v.iter().position(|&x| { | _____________^ @@ -67,7 +67,7 @@ LL | | ).is_none(); = help: this is more succinctly expressed by calling `any()` with negation error: called `is_none()` after searching an `Iterator` with `rposition` - --> tests/ui/search_is_some.rs:64:13 + --> tests/ui/search_is_some.rs:65:13 | LL | let _ = v.iter().rposition(|&x| { | _____________^ @@ -79,7 +79,7 @@ LL | | ).is_none(); = help: this is more succinctly expressed by calling `any()` with negation error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some.rs:79:13 + --> tests/ui/search_is_some.rs:80:13 | LL | let _ = (0..1).find(some_closure).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!(0..1).any(some_closure)` diff --git a/src/tools/clippy/tests/ui/single_char_add_str.fixed b/src/tools/clippy/tests/ui/single_char_add_str.fixed index eafd17f5387..aef15252b1b 100644 --- a/src/tools/clippy/tests/ui/single_char_add_str.fixed +++ b/src/tools/clippy/tests/ui/single_char_add_str.fixed @@ -21,6 +21,12 @@ fn main() { string.push('\u{0052}'); string.push('a'); + let c_ref = &'a'; + string.push(*c_ref); + let c = 'a'; + string.push(c); + string.push('a'); + get_string!().push('ö'); // `insert_str` tests @@ -41,5 +47,9 @@ fn main() { string.insert(Y, '"'); string.insert(Y, '\''); + string.insert(0, *c_ref); + string.insert(0, c); + string.insert(0, 'a'); + get_string!().insert(1, '?'); } diff --git a/src/tools/clippy/tests/ui/single_char_add_str.rs b/src/tools/clippy/tests/ui/single_char_add_str.rs index 5326c7cf24c..7f97250dacd 100644 --- a/src/tools/clippy/tests/ui/single_char_add_str.rs +++ b/src/tools/clippy/tests/ui/single_char_add_str.rs @@ -21,6 +21,12 @@ fn main() { string.push_str("\u{0052}"); string.push_str(r##"a"##); + let c_ref = &'a'; + string.push_str(&c_ref.to_string()); + let c = 'a'; + string.push_str(&c.to_string()); + string.push_str(&'a'.to_string()); + get_string!().push_str("ö"); // `insert_str` tests @@ -41,5 +47,9 @@ fn main() { string.insert_str(Y, r##"""##); string.insert_str(Y, r##"'"##); + string.insert_str(0, &c_ref.to_string()); + string.insert_str(0, &c.to_string()); + string.insert_str(0, &'a'.to_string()); + get_string!().insert_str(1, "?"); } diff --git a/src/tools/clippy/tests/ui/single_char_add_str.stderr b/src/tools/clippy/tests/ui/single_char_add_str.stderr index 89d75f20f55..7791c67578a 100644 --- a/src/tools/clippy/tests/ui/single_char_add_str.stderr +++ b/src/tools/clippy/tests/ui/single_char_add_str.stderr @@ -31,65 +31,101 @@ error: calling `push_str()` using a single-character string literal LL | string.push_str(r##"a"##); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('a')` +error: calling `push_str()` using a single-character converted to string + --> tests/ui/single_char_add_str.rs:25:5 + | +LL | string.push_str(&c_ref.to_string()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` without `to_string()`: `string.push(*c_ref)` + +error: calling `push_str()` using a single-character converted to string + --> tests/ui/single_char_add_str.rs:27:5 + | +LL | string.push_str(&c.to_string()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` without `to_string()`: `string.push(c)` + +error: calling `push_str()` using a single-character converted to string + --> tests/ui/single_char_add_str.rs:28:5 + | +LL | string.push_str(&'a'.to_string()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` without `to_string()`: `string.push('a')` + error: calling `push_str()` using a single-character string literal - --> tests/ui/single_char_add_str.rs:24:5 + --> tests/ui/single_char_add_str.rs:30:5 | LL | get_string!().push_str("ö"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `get_string!().push('ö')` error: calling `insert_str()` using a single-character string literal - --> tests/ui/single_char_add_str.rs:29:5 + --> tests/ui/single_char_add_str.rs:35:5 | LL | string.insert_str(0, "R"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, 'R')` error: calling `insert_str()` using a single-character string literal - --> tests/ui/single_char_add_str.rs:30:5 + --> tests/ui/single_char_add_str.rs:36:5 | LL | string.insert_str(1, "'"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(1, '\'')` error: calling `insert_str()` using a single-character string literal - --> tests/ui/single_char_add_str.rs:35:5 + --> tests/ui/single_char_add_str.rs:41:5 | LL | string.insert_str(0, "\x52"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '\x52')` error: calling `insert_str()` using a single-character string literal - --> tests/ui/single_char_add_str.rs:36:5 + --> tests/ui/single_char_add_str.rs:42:5 | LL | string.insert_str(0, "\u{0052}"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '\u{0052}')` error: calling `insert_str()` using a single-character string literal - --> tests/ui/single_char_add_str.rs:38:5 + --> tests/ui/single_char_add_str.rs:44:5 | LL | string.insert_str(x, r##"a"##); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(x, 'a')` error: calling `insert_str()` using a single-character string literal - --> tests/ui/single_char_add_str.rs:40:5 + --> tests/ui/single_char_add_str.rs:46:5 | LL | string.insert_str(Y, r##"a"##); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, 'a')` error: calling `insert_str()` using a single-character string literal - --> tests/ui/single_char_add_str.rs:41:5 + --> tests/ui/single_char_add_str.rs:47:5 | LL | string.insert_str(Y, r##"""##); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, '"')` error: calling `insert_str()` using a single-character string literal - --> tests/ui/single_char_add_str.rs:42:5 + --> tests/ui/single_char_add_str.rs:48:5 | LL | string.insert_str(Y, r##"'"##); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, '\'')` +error: calling `insert_str()` using a single-character converted to string + --> tests/ui/single_char_add_str.rs:50:5 + | +LL | string.insert_str(0, &c_ref.to_string()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` without `to_string()`: `string.insert(0, *c_ref)` + +error: calling `insert_str()` using a single-character converted to string + --> tests/ui/single_char_add_str.rs:51:5 + | +LL | string.insert_str(0, &c.to_string()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` without `to_string()`: `string.insert(0, c)` + +error: calling `insert_str()` using a single-character converted to string + --> tests/ui/single_char_add_str.rs:52:5 + | +LL | string.insert_str(0, &'a'.to_string()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` without `to_string()`: `string.insert(0, 'a')` + error: calling `insert_str()` using a single-character string literal - --> tests/ui/single_char_add_str.rs:44:5 + --> tests/ui/single_char_add_str.rs:54:5 | LL | get_string!().insert_str(1, "?"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `get_string!().insert(1, '?')` -error: aborting due to 15 previous errors +error: aborting due to 21 previous errors diff --git a/src/tools/clippy/tests/ui/str_to_string.fixed b/src/tools/clippy/tests/ui/str_to_string.fixed new file mode 100644 index 00000000000..52e40b45a8b --- /dev/null +++ b/src/tools/clippy/tests/ui/str_to_string.fixed @@ -0,0 +1,9 @@ +#![warn(clippy::str_to_string)] + +fn main() { + let hello = "hello world".to_owned(); + //~^ ERROR: `to_string()` called on a `&str` + let msg = &hello[..]; + msg.to_owned(); + //~^ ERROR: `to_string()` called on a `&str` +} diff --git a/src/tools/clippy/tests/ui/str_to_string.stderr b/src/tools/clippy/tests/ui/str_to_string.stderr index 13b73622d69..a761d96cd6b 100644 --- a/src/tools/clippy/tests/ui/str_to_string.stderr +++ b/src/tools/clippy/tests/ui/str_to_string.stderr @@ -2,9 +2,8 @@ error: `to_string()` called on a `&str` --> tests/ui/str_to_string.rs:4:17 | LL | let hello = "hello world".to_string(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"hello world".to_owned()` | - = help: consider using `.to_owned()` = note: `-D clippy::str-to-string` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::str_to_string)]` @@ -12,9 +11,7 @@ error: `to_string()` called on a `&str` --> tests/ui/str_to_string.rs:7:5 | LL | msg.to_string(); - | ^^^^^^^^^^^^^^^ - | - = help: consider using `.to_owned()` + | ^^^^^^^^^^^^^^^ help: try: `msg.to_owned()` error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs b/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs index 0039c805b7d..d325887bfba 100644 --- a/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs +++ b/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs @@ -1,5 +1,9 @@ #![deny(clippy::type_repetition_in_bounds)] -#![allow(clippy::extra_unused_type_parameters, clippy::multiple_bound_locations)] +#![allow( + clippy::extra_unused_type_parameters, + clippy::multiple_bound_locations, + clippy::needless_maybe_sized +)] use serde::Deserialize; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; diff --git a/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr b/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr index e9c6b41aaa8..77944c95045 100644 --- a/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr +++ b/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr @@ -1,5 +1,5 @@ error: this type has already been used as a bound predicate - --> tests/ui/type_repetition_in_bounds.rs:10:5 + --> tests/ui/type_repetition_in_bounds.rs:14:5 | LL | T: Clone, | ^^^^^^^^ @@ -12,7 +12,7 @@ LL | #![deny(clippy::type_repetition_in_bounds)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this type has already been used as a bound predicate - --> tests/ui/type_repetition_in_bounds.rs:28:5 + --> tests/ui/type_repetition_in_bounds.rs:32:5 | LL | Self: Copy + Default + Ord, | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20,7 +20,7 @@ LL | Self: Copy + Default + Ord, = help: consider combining the bounds: `Self: Clone + Copy + Default + Ord` error: this type has already been used as a bound predicate - --> tests/ui/type_repetition_in_bounds.rs:103:5 + --> tests/ui/type_repetition_in_bounds.rs:107:5 | LL | T: Clone, | ^^^^^^^^ @@ -28,7 +28,7 @@ LL | T: Clone, = help: consider combining the bounds: `T: ?Sized + Clone` error: this type has already been used as a bound predicate - --> tests/ui/type_repetition_in_bounds.rs:109:5 + --> tests/ui/type_repetition_in_bounds.rs:113:5 | LL | T: ?Sized, | ^^^^^^^^^ @@ -36,7 +36,7 @@ LL | T: ?Sized, = help: consider combining the bounds: `T: Clone + ?Sized` error: this type has already been used as a bound predicate - --> tests/ui/type_repetition_in_bounds.rs:135:9 + --> tests/ui/type_repetition_in_bounds.rs:139:9 | LL | T: Trait<Option<usize>, Box<[String]>, bool> + 'static, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/unnecessary_clippy_cfg.rs b/src/tools/clippy/tests/ui/unnecessary_clippy_cfg.rs index ff960520f5e..9915f8b843e 100644 --- a/src/tools/clippy/tests/ui/unnecessary_clippy_cfg.rs +++ b/src/tools/clippy/tests/ui/unnecessary_clippy_cfg.rs @@ -5,18 +5,18 @@ //~^ ERROR: no need to put clippy lints behind a `clippy` cfg #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] //~^ ERROR: no need to put clippy lints behind a `clippy` cfg -#![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg, clippy::maybe_misused_cfg))] +#![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] //~^ ERROR: no need to put clippy lints behind a `clippy` cfg -#![cfg_attr(clippy, deny(clippy::non_minimal_cfg, clippy::maybe_misused_cfg))] +#![cfg_attr(clippy, deny(clippy::non_minimal_cfg))] //~^ ERROR: no need to put clippy lints behind a `clippy` cfg #[cfg_attr(clippy, deny(clippy::non_minimal_cfg))] //~^ ERROR: no need to put clippy lints behind a `clippy` cfg #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] //~^ ERROR: no need to put clippy lints behind a `clippy` cfg -#[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg, clippy::maybe_misused_cfg))] +#[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] //~^ ERROR: no need to put clippy lints behind a `clippy` cfg -#[cfg_attr(clippy, deny(clippy::non_minimal_cfg, clippy::maybe_misused_cfg))] +#[cfg_attr(clippy, deny(clippy::non_minimal_cfg))] //~^ ERROR: no need to put clippy lints behind a `clippy` cfg pub struct Bar; diff --git a/src/tools/clippy/tests/ui/unnecessary_clippy_cfg.stderr b/src/tools/clippy/tests/ui/unnecessary_clippy_cfg.stderr index 3d58c9eb5da..16a86165295 100644 --- a/src/tools/clippy/tests/ui/unnecessary_clippy_cfg.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_clippy_cfg.stderr @@ -18,16 +18,16 @@ LL | #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] error: no need to put clippy lints behind a `clippy` cfg --> tests/ui/unnecessary_clippy_cfg.rs:17:36 | -LL | #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg, clippy::maybe_misused_cfg))] - | ^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] + | ^^^^^^^^^^^^^^^^^^^^^^^ | - = note: write instead: `#[deny(clippy::non_minimal_cfg,clippy::maybe_misused_cfg)]` + = note: write instead: `#[deny(clippy::non_minimal_cfg)]` error: no need to put clippy lints behind a `clippy` cfg --> tests/ui/unnecessary_clippy_cfg.rs:19:1 | -LL | #[cfg_attr(clippy, deny(clippy::non_minimal_cfg, clippy::maybe_misused_cfg))] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `#[deny(clippy::non_minimal_cfg, clippy::maybe_misused_cfg)]` +LL | #[cfg_attr(clippy, deny(clippy::non_minimal_cfg))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `#[deny(clippy::non_minimal_cfg)]` error: no need to put clippy lints behind a `clippy` cfg --> tests/ui/unnecessary_clippy_cfg.rs:4:1 @@ -46,21 +46,21 @@ LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] error: no need to put clippy lints behind a `clippy` cfg --> tests/ui/unnecessary_clippy_cfg.rs:8:37 | -LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg, clippy::maybe_misused_cfg))] - | ^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] + | ^^^^^^^^^^^^^^^^^^^^^^^ | - = note: write instead: `#![deny(clippy::non_minimal_cfg,clippy::maybe_misused_cfg)]` + = note: write instead: `#![deny(clippy::non_minimal_cfg)]` error: no need to put clippy lints behind a `clippy` cfg --> tests/ui/unnecessary_clippy_cfg.rs:10:1 | -LL | #![cfg_attr(clippy, deny(clippy::non_minimal_cfg, clippy::maybe_misused_cfg))] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `#![deny(clippy::non_minimal_cfg, clippy::maybe_misused_cfg)]` +LL | #![cfg_attr(clippy, deny(clippy::non_minimal_cfg))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `#![deny(clippy::non_minimal_cfg)]` error: duplicated attribute --> tests/ui/unnecessary_clippy_cfg.rs:8:26 | -LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg, clippy::maybe_misused_cfg))] +LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] | ^^^^^^^^^ | note: first defined here @@ -71,7 +71,7 @@ LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] help: remove this attribute --> tests/ui/unnecessary_clippy_cfg.rs:8:26 | -LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg, clippy::maybe_misused_cfg))] +LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] | ^^^^^^^^^ = note: `-D clippy::duplicated-attributes` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::duplicated_attributes)]` @@ -79,7 +79,7 @@ LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg, clippy::maybe_ error: duplicated attribute --> tests/ui/unnecessary_clippy_cfg.rs:17:25 | -LL | #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg, clippy::maybe_misused_cfg))] +LL | #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] | ^^^^^^^^^ | note: first defined here @@ -90,7 +90,7 @@ LL | #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] help: remove this attribute --> tests/ui/unnecessary_clippy_cfg.rs:17:25 | -LL | #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg, clippy::maybe_misused_cfg))] +LL | #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] | ^^^^^^^^^ error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/unwrap_in_result.rs b/src/tools/clippy/tests/ui/unwrap_in_result.rs index a19db46c667..62c6d959c84 100644 --- a/src/tools/clippy/tests/ui/unwrap_in_result.rs +++ b/src/tools/clippy/tests/ui/unwrap_in_result.rs @@ -38,6 +38,11 @@ impl A { } None } + + fn in_closure(a: Option<bool>) -> Option<bool> { + let c = || a.unwrap(); + Some(c()) + } } fn main() { diff --git a/src/tools/clippy/tests/ui/unwrap_in_result.stderr b/src/tools/clippy/tests/ui/unwrap_in_result.stderr index 29c7a9373aa..752177aaca5 100644 --- a/src/tools/clippy/tests/ui/unwrap_in_result.stderr +++ b/src/tools/clippy/tests/ui/unwrap_in_result.stderr @@ -38,5 +38,21 @@ note: potential non-recoverable error(s) LL | let i = i_str.parse::<i32>().expect("not a number"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: used unwrap or expect in a function that returns result or option + --> tests/ui/unwrap_in_result.rs:42:5 + | +LL | / fn in_closure(a: Option<bool>) -> Option<bool> { +LL | | let c = || a.unwrap(); +LL | | Some(c()) +LL | | } + | |_____^ + | + = help: unwrap and expect should not be used in a function that returns result or option +note: potential non-recoverable error(s) + --> tests/ui/unwrap_in_result.rs:43:20 + | +LL | let c = || a.unwrap(); + | ^^^^^^^^^^ + +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/useless_conversion_try.rs b/src/tools/clippy/tests/ui/useless_conversion_try.rs index 803d3b39f37..23edeae12b8 100644 --- a/src/tools/clippy/tests/ui/useless_conversion_try.rs +++ b/src/tools/clippy/tests/ui/useless_conversion_try.rs @@ -1,5 +1,9 @@ #![deny(clippy::useless_conversion)] -#![allow(clippy::needless_if, clippy::unnecessary_fallible_conversions)] +#![allow( + clippy::needless_if, + clippy::unnecessary_fallible_conversions, + clippy::manual_unwrap_or_default +)] fn test_generic<T: Copy>(val: T) -> T { let _ = T::try_from(val).unwrap(); diff --git a/src/tools/clippy/tests/ui/useless_conversion_try.stderr b/src/tools/clippy/tests/ui/useless_conversion_try.stderr index 11fb8f38ea5..30a43629dbd 100644 --- a/src/tools/clippy/tests/ui/useless_conversion_try.stderr +++ b/src/tools/clippy/tests/ui/useless_conversion_try.stderr @@ -1,5 +1,5 @@ error: useless conversion to the same type: `T` - --> tests/ui/useless_conversion_try.rs:5:13 + --> tests/ui/useless_conversion_try.rs:9:13 | LL | let _ = T::try_from(val).unwrap(); | ^^^^^^^^^^^^^^^^ @@ -12,7 +12,7 @@ LL | #![deny(clippy::useless_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: useless conversion to the same type: `T` - --> tests/ui/useless_conversion_try.rs:7:5 + --> tests/ui/useless_conversion_try.rs:11:5 | LL | val.try_into().unwrap() | ^^^^^^^^^^^^^^ @@ -20,7 +20,7 @@ LL | val.try_into().unwrap() = help: consider removing `.try_into()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion_try.rs:30:21 + --> tests/ui/useless_conversion_try.rs:34:21 | LL | let _: String = "foo".to_string().try_into().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -28,7 +28,7 @@ LL | let _: String = "foo".to_string().try_into().unwrap(); = help: consider removing `.try_into()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion_try.rs:32:21 + --> tests/ui/useless_conversion_try.rs:36:21 | LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -36,7 +36,7 @@ LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap(); = help: consider removing `TryFrom::try_from()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion_try.rs:34:13 + --> tests/ui/useless_conversion_try.rs:38:13 | LL | let _ = String::try_from("foo".to_string()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | let _ = String::try_from("foo".to_string()).unwrap(); = help: consider removing `String::try_from()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion_try.rs:36:13 + --> tests/ui/useless_conversion_try.rs:40:13 | LL | let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -52,7 +52,7 @@ LL | let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); = help: consider removing `String::try_from()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion_try.rs:38:21 + --> tests/ui/useless_conversion_try.rs:42:21 | LL | let _: String = format!("Hello {}", "world").try_into().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -60,7 +60,7 @@ LL | let _: String = format!("Hello {}", "world").try_into().unwrap(); = help: consider removing `.try_into()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion_try.rs:40:21 + --> tests/ui/useless_conversion_try.rs:44:21 | LL | let _: String = String::new().try_into().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -68,7 +68,7 @@ LL | let _: String = String::new().try_into().unwrap(); = help: consider removing `.try_into()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion_try.rs:42:27 + --> tests/ui/useless_conversion_try.rs:46:27 | LL | let _: String = match String::from("_").try_into() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/util/gh-pages/script.js b/src/tools/clippy/util/gh-pages/script.js index 7fd779fe9a4..7cca298df8e 100644 --- a/src/tools/clippy/util/gh-pages/script.js +++ b/src/tools/clippy/util/gh-pages/script.js @@ -397,7 +397,7 @@ $scope.bySearch = function (lint, index, array) { let searchStr = $scope.search; // It can be `null` I haven't missed this value - if (searchStr == null || searchStr.length < 3) { + if (searchStr == null) { return true; } searchStr = searchStr.toLowerCase(); diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index b0047770564..da7f03441e7 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -582,7 +582,7 @@ impl TargetCfgs { name, Some( value - .strip_suffix("\"") + .strip_suffix('\"') .expect("key-value pair should be properly quoted"), ), ) diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index da0196dad2f..ffc706d19a9 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -14,6 +14,7 @@ use crate::common::{Config, Debugger, FailMode, Mode, PassMode}; use crate::header::cfg::parse_cfg_name_directive; use crate::header::cfg::MatchOutcome; use crate::header::needs::CachedNeedsConditions; +use crate::util::static_regex; use crate::{extract_cdb_version, extract_gdb_version}; mod cfg; @@ -81,7 +82,7 @@ impl EarlyProps { panic!("errors encountered during EarlyProps parsing"); } - return props; + props } } @@ -381,7 +382,7 @@ impl TestProps { // Individual flags can be single-quoted to preserve spaces; see // <https://github.com/rust-lang/rust/pull/115948/commits/957c5db6>. flags - .split("'") + .split('\'') .enumerate() .flat_map(|(i, f)| { if i % 2 == 1 { vec![f] } else { f.split_whitespace().collect() } @@ -612,7 +613,7 @@ impl TestProps { for key in &["RUST_TEST_NOCAPTURE", "RUST_TEST_THREADS"] { if let Ok(val) = env::var(key) { - if self.exec_env.iter().find(|&&(ref x, _)| x == key).is_none() { + if !self.exec_env.iter().any(|&(ref x, _)| x == key) { self.exec_env.push(((*key).to_owned(), val)) } } @@ -990,7 +991,7 @@ pub(crate) fn check_directive(directive_ln: &str) -> CheckDirectiveResult<'_> { let trailing = post.trim().split_once(' ').map(|(pre, _)| pre).unwrap_or(post); let trailing_directive = { // 1. is the directive name followed by a space? (to exclude `:`) - matches!(directive_ln.get(directive_name.len()..), Some(s) if s.starts_with(" ")) + matches!(directive_ln.get(directive_name.len()..), Some(s) if s.starts_with(' ')) // 2. is what is after that directive also a directive (ex: "only-x86 only-arm") && KNOWN_DIRECTIVE_NAMES.contains(&trailing) } @@ -1186,11 +1187,11 @@ impl Config { } } - fn parse_custom_normalization(&self, mut line: &str, prefix: &str) -> Option<(String, String)> { + fn parse_custom_normalization(&self, line: &str, prefix: &str) -> Option<(String, String)> { if parse_cfg_name_directive(self, line, prefix).outcome == MatchOutcome::Match { - let from = parse_normalization_string(&mut line)?; - let to = parse_normalization_string(&mut line)?; - Some((from, to)) + let (regex, replacement) = parse_normalize_rule(line) + .unwrap_or_else(|| panic!("couldn't parse custom normalization rule: `{line}`")); + Some((regex, replacement)) } else { None } @@ -1311,24 +1312,29 @@ fn expand_variables(mut value: String, config: &Config) -> String { value } -/// Finds the next quoted string `"..."` in `line`, and extract the content from it. Move the `line` -/// variable after the end of the quoted string. -/// -/// # Examples -/// -/// ``` -/// let mut s = "normalize-stderr-32bit: \"something (32 bits)\" -> \"something ($WORD bits)\"."; -/// let first = parse_normalization_string(&mut s); -/// assert_eq!(first, Some("something (32 bits)".to_owned())); -/// assert_eq!(s, " -> \"something ($WORD bits)\"."); +/// Parses the regex and replacement values of a `//@ normalize-*` header, +/// in the format: +/// ```text +/// normalize-*: "REGEX" -> "REPLACEMENT" /// ``` -fn parse_normalization_string(line: &mut &str) -> Option<String> { - // FIXME support escapes in strings. - let begin = line.find('"')? + 1; - let end = line[begin..].find('"')? + begin; - let result = line[begin..end].to_owned(); - *line = &line[end + 1..]; - Some(result) +fn parse_normalize_rule(header: &str) -> Option<(String, String)> { + // FIXME(#126370): A colon after the header name should be mandatory, but + // currently is not, and there are many tests that lack the colon. + // FIXME: Support escaped double-quotes in strings. + let captures = static_regex!( + r#"(?x) # (verbose mode regex) + ^ + [^:\s]+:?\s* # (header name followed by optional colon) + "(?<regex>[^"]*)" # "REGEX" + \s+->\s+ # -> + "(?<replacement>[^"]*)" # "REPLACEMENT" + $ + "# + ) + .captures(header)?; + let regex = captures["regex"].to_owned(); + let replacement = captures["replacement"].to_owned(); + Some((regex, replacement)) } pub fn extract_llvm_version(version: &str) -> Option<u32> { @@ -1357,7 +1363,7 @@ pub fn extract_llvm_version_from_binary(binary_path: &str) -> Option<u32> { } let version = String::from_utf8(output.stdout).ok()?; for line in version.lines() { - if let Some(version) = line.split("LLVM version ").skip(1).next() { + if let Some(version) = line.split("LLVM version ").nth(1) { return extract_llvm_version(version); } } @@ -1388,7 +1394,7 @@ where let min = parse(min)?; let max = match max { - Some(max) if max.is_empty() => return None, + Some("") => return None, Some(max) => parse(max)?, _ => min, }; @@ -1460,12 +1466,12 @@ pub fn make_test_description<R: Read>( decision!(ignore_gdb(config, ln)); decision!(ignore_lldb(config, ln)); - if config.target == "wasm32-unknown-unknown" { - if config.parse_name_directive(ln, directives::CHECK_RUN_RESULTS) { - decision!(IgnoreDecision::Ignore { - reason: "ignored on WASM as the run results cannot be checked there".into(), - }); - } + if config.target == "wasm32-unknown-unknown" + && config.parse_name_directive(ln, directives::CHECK_RUN_RESULTS) + { + decision!(IgnoreDecision::Ignore { + reason: "ignored on WASM as the run results cannot be checked there".into(), + }); } should_fail |= config.parse_name_directive(ln, "should-fail"); diff --git a/src/tools/compiletest/src/header/cfg.rs b/src/tools/compiletest/src/header/cfg.rs index 510043e3bfd..522c52b1de2 100644 --- a/src/tools/compiletest/src/header/cfg.rs +++ b/src/tools/compiletest/src/header/cfg.rs @@ -58,7 +58,7 @@ pub(super) fn parse_cfg_name_directive<'a>( // Some of the matchers might be "" depending on what the target information is. To avoid // problems we outright reject empty directives. - if name == "" { + if name.is_empty() { return ParsedNameDirective::not_a_directive(); } diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs index 8a37a4d6d31..61a85b84ad6 100644 --- a/src/tools/compiletest/src/header/tests.rs +++ b/src/tools/compiletest/src/header/tests.rs @@ -3,7 +3,7 @@ use std::path::Path; use std::str::FromStr; use crate::common::{Config, Debugger, Mode}; -use crate::header::{parse_normalization_string, EarlyProps, HeadersCache}; +use crate::header::{parse_normalize_rule, EarlyProps, HeadersCache}; use super::iter_header; @@ -32,35 +32,41 @@ fn make_test_description<R: Read>( } #[test] -fn test_parse_normalization_string() { - let mut s = "normalize-stderr-32bit: \"something (32 bits)\" -> \"something ($WORD bits)\"."; - let first = parse_normalization_string(&mut s); - assert_eq!(first, Some("something (32 bits)".to_owned())); - assert_eq!(s, " -> \"something ($WORD bits)\"."); - - // Nothing to normalize (No quotes) - let mut s = "normalize-stderr-32bit: something (32 bits) -> something ($WORD bits)."; - let first = parse_normalization_string(&mut s); - assert_eq!(first, None); - assert_eq!(s, r#"normalize-stderr-32bit: something (32 bits) -> something ($WORD bits)."#); - - // Nothing to normalize (Only a single quote) - let mut s = "normalize-stderr-32bit: \"something (32 bits) -> something ($WORD bits)."; - let first = parse_normalization_string(&mut s); - assert_eq!(first, None); - assert_eq!(s, "normalize-stderr-32bit: \"something (32 bits) -> something ($WORD bits)."); - - // Nothing to normalize (Three quotes) - let mut s = "normalize-stderr-32bit: \"something (32 bits)\" -> \"something ($WORD bits)."; - let first = parse_normalization_string(&mut s); - assert_eq!(first, Some("something (32 bits)".to_owned())); - assert_eq!(s, " -> \"something ($WORD bits)."); - - // Nothing to normalize (No quotes, 16-bit) - let mut s = "normalize-stderr-16bit: something (16 bits) -> something ($WORD bits)."; - let first = parse_normalization_string(&mut s); - assert_eq!(first, None); - assert_eq!(s, r#"normalize-stderr-16bit: something (16 bits) -> something ($WORD bits)."#); +fn test_parse_normalize_rule() { + let good_data = &[ + ( + r#"normalize-stderr-32bit: "something (32 bits)" -> "something ($WORD bits)""#, + "something (32 bits)", + "something ($WORD bits)", + ), + // FIXME(#126370): A colon after the header name should be mandatory, + // but currently is not, and there are many tests that lack the colon. + ( + r#"normalize-stderr-32bit "something (32 bits)" -> "something ($WORD bits)""#, + "something (32 bits)", + "something ($WORD bits)", + ), + ]; + + for &(input, expected_regex, expected_replacement) in good_data { + let parsed = parse_normalize_rule(input); + let parsed = + parsed.as_ref().map(|(regex, replacement)| (regex.as_str(), replacement.as_str())); + assert_eq!(parsed, Some((expected_regex, expected_replacement))); + } + + let bad_data = &[ + r#"normalize-stderr-16bit: something (16 bits) -> something ($WORD bits)"#, + r#"normalize-stderr-32bit: something (32 bits) -> something ($WORD bits)"#, + r#"normalize-stderr-32bit: "something (32 bits) -> something ($WORD bits)"#, + r#"normalize-stderr-32bit: "something (32 bits)" -> "something ($WORD bits)"#, + r#"normalize-stderr-32bit: "something (32 bits)" -> "something ($WORD bits)"."#, + ]; + + for &input in bad_data { + let parsed = parse_normalize_rule(input); + assert_eq!(parsed, None); + } } #[derive(Default)] diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 62e71e9b59d..0cf05b32e96 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -1147,7 +1147,7 @@ fn extract_lldb_version(full_version_line: &str) -> Option<(u32, bool)> { } fn not_a_digit(c: char) -> bool { - !c.is_digit(10) + !c.is_ascii_digit() } fn check_overlapping_tests(found_paths: &HashSet<PathBuf>) { diff --git a/src/tools/compiletest/src/read2.rs b/src/tools/compiletest/src/read2.rs index 3f1921cb6bd..8c06339f3c3 100644 --- a/src/tools/compiletest/src/read2.rs +++ b/src/tools/compiletest/src/read2.rs @@ -6,7 +6,6 @@ mod tests; pub use self::imp::read2; use std::io::{self, Write}; -use std::mem::replace; use std::process::{Child, Output}; #[derive(Copy, Clone, Debug)] @@ -101,10 +100,10 @@ impl ProcOutput { return; } - let mut head = replace(bytes, Vec::new()); + let mut head = std::mem::take(bytes); // Don't truncate if this as a whole line. // That should make it less likely that we cut a JSON line in half. - if head.last() != Some(&('\n' as u8)) { + if head.last() != Some(&b'\n') { head.truncate(MAX_OUT_LEN); } let skipped = new_len - head.len(); diff --git a/src/tools/compiletest/src/read2/tests.rs b/src/tools/compiletest/src/read2/tests.rs index 5ad2db3cb83..9e052ff069b 100644 --- a/src/tools/compiletest/src/read2/tests.rs +++ b/src/tools/compiletest/src/read2/tests.rs @@ -64,9 +64,9 @@ fn test_abbreviate_filterss_are_detected() { #[test] fn test_abbreviate_filters_avoid_abbreviations() { let mut out = ProcOutput::new(); - let filters = &[std::iter::repeat('a').take(64).collect::<String>()]; + let filters = &["a".repeat(64)]; - let mut expected = vec![b'.'; MAX_OUT_LEN - FILTERED_PATHS_PLACEHOLDER_LEN as usize]; + let mut expected = vec![b'.'; MAX_OUT_LEN - FILTERED_PATHS_PLACEHOLDER_LEN]; expected.extend_from_slice(filters[0].as_bytes()); out.extend(&expected, filters); @@ -81,7 +81,7 @@ fn test_abbreviate_filters_avoid_abbreviations() { #[test] fn test_abbreviate_filters_can_still_cause_abbreviations() { let mut out = ProcOutput::new(); - let filters = &[std::iter::repeat('a').take(64).collect::<String>()]; + let filters = &["a".repeat(64)]; let mut input = vec![b'.'; MAX_OUT_LEN]; input.extend_from_slice(filters[0].as_bytes()); diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index dbe016b8305..72b57d91c23 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -15,7 +15,7 @@ use crate::errors::{self, Error, ErrorKind}; use crate::header::TestProps; use crate::json; use crate::read2::{read2_abbreviated, Truncated}; -use crate::util::{add_dylib_path, copy_dir_all, dylib_env_var, logv, PathBufExt}; +use crate::util::{add_dylib_path, copy_dir_all, dylib_env_var, logv, static_regex, PathBufExt}; use crate::ColorConfig; use colored::Colorize; use miropt_test_tools::{files_for_miropt_test, MiroptTest, MiroptTestFile}; @@ -48,14 +48,6 @@ use debugger::DebuggerCommands; #[cfg(test)] mod tests; -macro_rules! static_regex { - ($re:literal) => {{ - static RE: ::std::sync::OnceLock<::regex::Regex> = ::std::sync::OnceLock::new(); - RE.get_or_init(|| ::regex::Regex::new($re).unwrap()) - }}; -} -use static_regex; - const FAKE_SRC_BASE: &str = "fake-test-src-base"; #[cfg(windows)] @@ -382,11 +374,11 @@ impl<'test> TestCx<'test> { // if a test does not crash, consider it an error if proc_res.status.success() || matches!(proc_res.status.code(), Some(1 | 0)) { - self.fatal(&format!( + self.fatal( "test no longer crashes/triggers ICE! Please give it a mearningful name, \ add a doc-comment to the start of the test explaining why it exists and \ - move it to tests/ui or wherever you see fit." - )); + move it to tests/ui or wherever you see fit.", + ); } } @@ -705,10 +697,10 @@ impl<'test> TestCx<'test> { // since it is extensively used in the testsuite. check_cfg.push_str("cfg(FALSE"); for revision in &self.props.revisions { - check_cfg.push_str(","); - check_cfg.push_str(&normalize_revision(&revision)); + check_cfg.push(','); + check_cfg.push_str(&normalize_revision(revision)); } - check_cfg.push_str(")"); + check_cfg.push(')'); cmd.args(&["--check-cfg", &check_cfg]); } @@ -826,7 +818,7 @@ impl<'test> TestCx<'test> { // Append the other `cdb-command:`s for line in &dbg_cmds.commands { script_str.push_str(line); - script_str.push_str("\n"); + script_str.push('\n'); } script_str.push_str("qq\n"); // Quit the debugger (including remote debugger, if any) @@ -1208,7 +1200,7 @@ impl<'test> TestCx<'test> { // Append the other commands for line in &dbg_cmds.commands { script_str.push_str(line); - script_str.push_str("\n"); + script_str.push('\n'); } // Finally, quit the debugger @@ -1258,7 +1250,7 @@ impl<'test> TestCx<'test> { // Remove options that are either unwanted (-O) or may lead to duplicates due to RUSTFLAGS. let options_to_remove = ["-O".to_owned(), "-g".to_owned(), "--debuginfo".to_owned()]; - options.iter().filter(|x| !options_to_remove.contains(x)).map(|x| x.clone()).collect() + options.iter().filter(|x| !options_to_remove.contains(x)).cloned().collect() } fn maybe_add_external_args(&self, cmd: &mut Command, args: &Vec<String>) { @@ -2512,8 +2504,8 @@ impl<'test> TestCx<'test> { // This works with both `--emit asm` (as default output name for the assembly) // and `ptx-linker` because the latter can write output at requested location. let output_path = self.output_base_name().with_extension(extension); - let output_file = TargetLocation::ThisFile(output_path.clone()); - output_file + + TargetLocation::ThisFile(output_path.clone()) } } @@ -2760,7 +2752,7 @@ impl<'test> TestCx<'test> { for entry in walkdir::WalkDir::new(dir) { let entry = entry.expect("failed to read file"); if entry.file_type().is_file() - && entry.path().extension().and_then(|p| p.to_str()) == Some("html".into()) + && entry.path().extension().and_then(|p| p.to_str()) == Some("html") { let status = Command::new("tidy").args(&tidy_args).arg(entry.path()).status().unwrap(); @@ -2791,8 +2783,7 @@ impl<'test> TestCx<'test> { &compare_dir, self.config.verbose, |file_type, extension| { - file_type.is_file() - && (extension == Some("html".into()) || extension == Some("js".into())) + file_type.is_file() && (extension == Some("html") || extension == Some("js")) }, ) { return; @@ -2838,11 +2829,11 @@ impl<'test> TestCx<'test> { } match String::from_utf8(line.clone()) { Ok(line) => { - if line.starts_with("+") { + if line.starts_with('+') { write!(&mut out, "{}", line.green()).unwrap(); - } else if line.starts_with("-") { + } else if line.starts_with('-') { write!(&mut out, "{}", line.red()).unwrap(); - } else if line.starts_with("@") { + } else if line.starts_with('@') { write!(&mut out, "{}", line.blue()).unwrap(); } else { out.write_all(line.as_bytes()).unwrap(); @@ -2915,7 +2906,7 @@ impl<'test> TestCx<'test> { && line.ends_with(';') { if let Some(ref mut other_files) = other_files { - other_files.push(line.rsplit("mod ").next().unwrap().replace(";", "")); + other_files.push(line.rsplit("mod ").next().unwrap().replace(';', "")); } None } else { @@ -3147,7 +3138,7 @@ impl<'test> TestCx<'test> { let mut string = String::new(); for cgu in cgus { string.push_str(&cgu[..]); - string.push_str(" "); + string.push(' '); } string @@ -3180,10 +3171,7 @@ impl<'test> TestCx<'test> { // CGUs joined with "--". This function splits such composite CGU names // and handles each component individually. fn remove_crate_disambiguators_from_set_of_cgu_names(cgus: &str) -> String { - cgus.split("--") - .map(|cgu| remove_crate_disambiguator_from_cgu(cgu)) - .collect::<Vec<_>>() - .join("--") + cgus.split("--").map(remove_crate_disambiguator_from_cgu).collect::<Vec<_>>().join("--") } } @@ -3365,7 +3353,7 @@ impl<'test> TestCx<'test> { // endif } - if self.config.target.contains("msvc") && self.config.cc != "" { + if self.config.target.contains("msvc") && !self.config.cc.is_empty() { // We need to pass a path to `lib.exe`, so assume that `cc` is `cl.exe` // and that `lib.exe` lives next to it. let lib = Path::new(&self.config.cc).parent().unwrap().join("lib.exe"); @@ -3551,6 +3539,10 @@ impl<'test> TestCx<'test> { .env("TARGET_RPATH_DIR", cwd.join(&self.config.run_lib_path)) .env("LLVM_COMPONENTS", &self.config.llvm_components); + // In test code we want to be very pedantic about values being silently discarded that are + // annotated with `#[must_use]`. + cmd.arg("-Dunused_must_use"); + if std::env::var_os("COMPILETEST_FORCE_STAGE0").is_some() { let mut stage0_sysroot = build_root.clone(); stage0_sysroot.push("stage0-sysroot"); @@ -3643,7 +3635,7 @@ impl<'test> TestCx<'test> { // endif } - if self.config.target.contains("msvc") && self.config.cc != "" { + if self.config.target.contains("msvc") && !self.config.cc.is_empty() { // We need to pass a path to `lib.exe`, so assume that `cc` is `cl.exe` // and that `lib.exe` lives next to it. let lib = Path::new(&self.config.cc).parent().unwrap().join("lib.exe"); @@ -3834,7 +3826,7 @@ impl<'test> TestCx<'test> { && !self.props.dont_check_compiler_stderr { self.fatal_proc_rec( - &format!("compiler output got truncated, cannot compare with reference file"), + "compiler output got truncated, cannot compare with reference file", &proc_res, ); } @@ -4015,8 +4007,8 @@ impl<'test> TestCx<'test> { crate_name.to_str().expect("crate name implies file name must be valid UTF-8"); // replace `a.foo` -> `a__foo` for crate name purposes. // replace `revision-name-with-dashes` -> `revision_name_with_underscore` - let crate_name = crate_name.replace(".", "__"); - let crate_name = crate_name.replace("-", "_"); + let crate_name = crate_name.replace('.', "__"); + let crate_name = crate_name.replace('-', "_"); rustc.arg("--crate-name"); rustc.arg(crate_name); } @@ -4064,7 +4056,7 @@ impl<'test> TestCx<'test> { fn check_mir_dump(&self, test_info: MiroptTest) { let test_dir = self.testpaths.file.parent().unwrap(); let test_crate = - self.testpaths.file.file_stem().unwrap().to_str().unwrap().replace("-", "_"); + self.testpaths.file.file_stem().unwrap().to_str().unwrap().replace('-', "_"); let MiroptTest { run_filecheck, suffix, files, passes: _ } = test_info; diff --git a/src/tools/compiletest/src/runtest/coverage.rs b/src/tools/compiletest/src/runtest/coverage.rs index 8bd7c7e808d..6ee147da5a9 100644 --- a/src/tools/compiletest/src/runtest/coverage.rs +++ b/src/tools/compiletest/src/runtest/coverage.rs @@ -7,7 +7,8 @@ use std::process::Command; use glob::glob; use crate::common::{UI_COVERAGE, UI_COVERAGE_MAP}; -use crate::runtest::{static_regex, Emit, ProcRes, TestCx, WillExecute}; +use crate::runtest::{Emit, ProcRes, TestCx, WillExecute}; +use crate::util::static_regex; impl<'test> TestCx<'test> { fn coverage_dump_path(&self) -> &Path { diff --git a/src/tools/compiletest/src/runtest/debugger.rs b/src/tools/compiletest/src/runtest/debugger.rs index eebe5f3580b..ed6cc97a8ab 100644 --- a/src/tools/compiletest/src/runtest/debugger.rs +++ b/src/tools/compiletest/src/runtest/debugger.rs @@ -148,5 +148,5 @@ fn check_single_line(line: &str, check_line: &str) -> bool { rest = &rest[pos + current_fragment.len()..]; } - if !can_end_anywhere && !rest.is_empty() { false } else { true } + can_end_anywhere || rest.is_empty() } diff --git a/src/tools/compiletest/src/tests.rs b/src/tools/compiletest/src/tests.rs index e6725dba260..4292f234adc 100644 --- a/src/tools/compiletest/src/tests.rs +++ b/src/tools/compiletest/src/tests.rs @@ -58,11 +58,11 @@ fn test_extract_lldb_version() { #[test] fn is_test_test() { - assert_eq!(true, is_test(&OsString::from("a_test.rs"))); - assert_eq!(false, is_test(&OsString::from(".a_test.rs"))); - assert_eq!(false, is_test(&OsString::from("a_cat.gif"))); - assert_eq!(false, is_test(&OsString::from("#a_dog_gif"))); - assert_eq!(false, is_test(&OsString::from("~a_temp_file"))); + assert!(is_test(&OsString::from("a_test.rs"))); + assert!(!is_test(&OsString::from(".a_test.rs"))); + assert!(!is_test(&OsString::from("a_cat.gif"))); + assert!(!is_test(&OsString::from("#a_dog_gif"))); + assert!(!is_test(&OsString::from("~a_temp_file"))); } #[test] diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs index ec20bda8c18..cdec49a51d7 100644 --- a/src/tools/compiletest/src/util.rs +++ b/src/tools/compiletest/src/util.rs @@ -90,3 +90,11 @@ pub fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> std::io::Re } Ok(()) } + +macro_rules! static_regex { + ($re:literal) => {{ + static RE: ::std::sync::OnceLock<::regex::Regex> = ::std::sync::OnceLock::new(); + RE.get_or_init(|| ::regex::Regex::new($re).unwrap()) + }}; +} +pub(crate) use static_regex; diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs index 5e35ce242fe..1713a4d812c 100644 --- a/src/tools/jsondoclint/src/validator.rs +++ b/src/tools/jsondoclint/src/validator.rs @@ -418,15 +418,13 @@ impl<'a> Validator<'a> { } else { self.fail_expecting(id, expected); } - } else { - if !self.missing_ids.contains(id) { - self.missing_ids.insert(id); + } else if !self.missing_ids.contains(id) { + self.missing_ids.insert(id); - let sels = json_find::find_selector(&self.krate_json, &Value::String(id.0.clone())); - assert_ne!(sels.len(), 0); + let sels = json_find::find_selector(&self.krate_json, &Value::String(id.0.clone())); + assert_ne!(sels.len(), 0); - self.fail(id, ErrorKind::NotFound(sels)) - } + self.fail(id, ErrorKind::NotFound(sels)) } } diff --git a/src/tools/lint-docs/src/groups.rs b/src/tools/lint-docs/src/groups.rs index 73f5738469e..f246d71d499 100644 --- a/src/tools/lint-docs/src/groups.rs +++ b/src/tools/lint-docs/src/groups.rs @@ -121,13 +121,13 @@ impl<'a> LintExtractor<'a> { }; to_link.extend(group_lints); let brackets: Vec<_> = group_lints.iter().map(|l| format!("[{}]", l)).collect(); - write!(result, "| {} | {} | {} |\n", group_name, description, brackets.join(", ")) + writeln!(result, "| {} | {} | {} |", group_name, description, brackets.join(", ")) .unwrap(); } result.push('\n'); result.push_str("[warn-by-default]: listing/warn-by-default.md\n"); for lint_name in to_link { - let lint_def = match lints.iter().find(|l| l.name == lint_name.replace("-", "_")) { + let lint_def = match lints.iter().find(|l| l.name == lint_name.replace('-', "_")) { Some(def) => def, None => { let msg = format!( @@ -144,9 +144,9 @@ impl<'a> LintExtractor<'a> { } } }; - write!( + writeln!( result, - "[{}]: listing/{}#{}\n", + "[{}]: listing/{}#{}", lint_name, lint_def.level.doc_filename(), lint_name diff --git a/src/tools/lint-docs/src/lib.rs b/src/tools/lint-docs/src/lib.rs index c79b377727a..72d6a495e7e 100644 --- a/src/tools/lint-docs/src/lib.rs +++ b/src/tools/lint-docs/src/lib.rs @@ -84,8 +84,8 @@ impl Lint { for &expected in &["### Example", "### Explanation", "{{produces}}"] { if expected == "{{produces}}" && self.is_ignored() { if self.doc_contains("{{produces}}") { - return Err(format!( - "the lint example has `ignore`, but also contains the {{{{produces}}}} marker\n\ + return Err( + "the lint example has `ignore`, but also contains the {{produces}} marker\n\ \n\ The documentation generator cannot generate the example output when the \ example is ignored.\n\ @@ -111,7 +111,7 @@ impl Lint { Replacing the output with the text of the example you \ compiled manually yourself.\n\ " - ).into()); + .into()); } continue; } @@ -519,11 +519,11 @@ impl<'a> LintExtractor<'a> { let mut these_lints: Vec<_> = lints.iter().filter(|lint| lint.level == level).collect(); these_lints.sort_unstable_by_key(|lint| &lint.name); for lint in &these_lints { - write!(result, "* [`{}`](#{})\n", lint.name, lint.name.replace("_", "-")).unwrap(); + writeln!(result, "* [`{}`](#{})", lint.name, lint.name.replace('_', "-")).unwrap(); } result.push('\n'); for lint in &these_lints { - write!(result, "## {}\n\n", lint.name.replace("_", "-")).unwrap(); + write!(result, "## {}\n\n", lint.name.replace('_', "-")).unwrap(); for line in &lint.doc { result.push_str(line); result.push('\n'); @@ -583,7 +583,7 @@ fn add_rename_redirect(level: Level, output: &mut String) { let filename = level.doc_filename().replace(".md", ".html"); output.push_str(RENAME_START); for (from, to) in *names { - write!(output, " \"#{from}\": \"{filename}#{to}\",\n").unwrap(); + writeln!(output, " \"#{from}\": \"{filename}#{to}\",").unwrap(); } output.push_str(RENAME_END); } diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 11cbbde06aa..f8410db4dd0 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -13,6 +13,7 @@ #![feature(lint_reasons)] #![feature(trait_upcasting)] #![feature(strict_overflow_ops)] +#![feature(is_none_or)] // Configure clippy and other lints #![allow( clippy::collapsible_else_if, diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 898fc111fd4..911958e558c 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -396,12 +396,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // If the newly promised alignment is bigger than the native alignment of this // allocation, and bigger than the previously promised alignment, then set it. if align > alloc_align - && !this + && this .machine .symbolic_alignment .get_mut() .get(&alloc_id) - .is_some_and(|&(_, old_align)| align <= old_align) + .is_none_or(|&(_, old_align)| align > old_align) { this.machine.symbolic_alignment.get_mut().insert(alloc_id, (offset, align)); } diff --git a/src/tools/opt-dist/src/training.rs b/src/tools/opt-dist/src/training.rs index 89f4d8957c8..1237951b3f0 100644 --- a/src/tools/opt-dist/src/training.rs +++ b/src/tools/opt-dist/src/training.rs @@ -216,11 +216,9 @@ pub fn gather_bolt_profiles( log::info!("Profile file count: {}", profiles.len()); // Delete the gathered profiles - for profile in glob::glob(&format!("{profile_prefix}*"))?.into_iter() { - if let Ok(profile) = profile { - if let Err(error) = std::fs::remove_file(&profile) { - log::error!("Cannot delete BOLT profile {}: {error:?}", profile.display()); - } + for profile in glob::glob(&format!("{profile_prefix}*"))?.flatten() { + if let Err(error) = std::fs::remove_file(&profile) { + log::error!("Cannot delete BOLT profile {}: {error:?}", profile.display()); } } diff --git a/src/tools/remote-test-client/src/main.rs b/src/tools/remote-test-client/src/main.rs index 590c735596e..dd2c09c430b 100644 --- a/src/tools/remote-test-client/src/main.rs +++ b/src/tools/remote-test-client/src/main.rs @@ -317,13 +317,11 @@ fn run(support_lib_count: usize, exe: String, all_args: Vec<String>) { t!(io::copy(&mut (&mut client).take(amt), &mut stdout)); t!(stdout.flush()); } + } else if amt == 0 { + stderr_done = true; } else { - if amt == 0 { - stderr_done = true; - } else { - t!(io::copy(&mut (&mut client).take(amt), &mut stderr)); - t!(stderr.flush()); - } + t!(io::copy(&mut (&mut client).take(amt), &mut stderr)); + t!(stderr.flush()); } } diff --git a/src/tools/remote-test-server/src/main.rs b/src/tools/remote-test-server/src/main.rs index 68d7aa56c43..79f96c50223 100644 --- a/src/tools/remote-test-server/src/main.rs +++ b/src/tools/remote-test-server/src/main.rs @@ -282,7 +282,7 @@ fn handle_run(socket: TcpStream, work: &Path, tmp: &Path, lock: &Mutex<()>, conf cmd.env(library_path, env::join_paths(paths).unwrap()); // Some tests assume RUST_TEST_TMPDIR exists - cmd.env("RUST_TEST_TMPDIR", tmp.to_owned()); + cmd.env("RUST_TEST_TMPDIR", tmp); let socket = Arc::new(Mutex::new(reader.into_inner())); diff --git a/src/tools/run-make-support/CHANGELOG.md b/src/tools/run-make-support/CHANGELOG.md index a37de9fda80..c1b7b618a92 100644 --- a/src/tools/run-make-support/CHANGELOG.md +++ b/src/tools/run-make-support/CHANGELOG.md @@ -10,6 +10,22 @@ changes to the support library). This support library will probably never reach 1.0. Please bump the minor version in `Cargo.toml` if you make any breaking changes or other significant changes, or bump the patch version for bug fixes. +## [0.2.0] - 2024-06-11 + +### Added + +- Added `fs_wrapper` module which provides panic-on-fail helpers for their respective `std::fs` + counterparts, the motivation is to: + - Reduce littering `.unwrap()` or `.expect()` everywhere for fs operations + - Help the test writer avoid forgetting to check fs results (even though enforced by + `-Dunused_must_use`) + - Provide better panic messages by default +- Added `path()` helper which creates a `Path` relative to `cwd()` (but is less noisy). + +### Changed + +- Marked many functions with `#[must_use]`, and rmake.rs are now compiled with `-Dunused_must_use`. + ## [0.1.0] - 2024-06-09 ### Changed diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml index 5450620e7f0..2f7f51442f1 100644 --- a/src/tools/run-make-support/Cargo.toml +++ b/src/tools/run-make-support/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "run_make_support" -version = "0.1.0" +version = "0.2.0" edition = "2021" [dependencies] diff --git a/src/tools/run-make-support/src/cc.rs b/src/tools/run-make-support/src/cc.rs index 8694740cfc3..31b9e8a23b8 100644 --- a/src/tools/run-make-support/src/cc.rs +++ b/src/tools/run-make-support/src/cc.rs @@ -15,6 +15,7 @@ pub fn cc() -> Cc { /// A platform-specific C compiler invocation builder. The specific C compiler used is /// passed down from compiletest. #[derive(Debug)] +#[must_use] pub struct Cc { cmd: Command, } diff --git a/src/tools/run-make-support/src/clang.rs b/src/tools/run-make-support/src/clang.rs index a31004659c1..c23e41ebe21 100644 --- a/src/tools/run-make-support/src/clang.rs +++ b/src/tools/run-make-support/src/clang.rs @@ -11,6 +11,7 @@ pub fn clang() -> Clang { /// A `clang` invocation builder. #[derive(Debug)] +#[must_use] pub struct Clang { cmd: Command, } diff --git a/src/tools/run-make-support/src/command.rs b/src/tools/run-make-support/src/command.rs index d689dc2f979..7cbc61bdf33 100644 --- a/src/tools/run-make-support/src/command.rs +++ b/src/tools/run-make-support/src/command.rs @@ -148,14 +148,17 @@ pub struct CompletedProcess { } impl CompletedProcess { + #[must_use] pub fn stdout_utf8(&self) -> String { String::from_utf8(self.output.stdout.clone()).expect("stdout is not valid UTF-8") } + #[must_use] pub fn stderr_utf8(&self) -> String { String::from_utf8(self.output.stderr.clone()).expect("stderr is not valid UTF-8") } + #[must_use] pub fn status(&self) -> ExitStatus { self.output.status } diff --git a/src/tools/run-make-support/src/diff/mod.rs b/src/tools/run-make-support/src/diff/mod.rs index 0fb6fec9d58..105cdbc7b32 100644 --- a/src/tools/run-make-support/src/diff/mod.rs +++ b/src/tools/run-make-support/src/diff/mod.rs @@ -13,6 +13,7 @@ pub fn diff() -> Diff { } #[derive(Debug)] +#[must_use] pub struct Diff { expected: Option<String>, expected_name: Option<String>, diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index b920f9a07db..d077c500c5d 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -9,7 +9,7 @@ mod command; pub mod diff; mod drop_bomb; pub mod fs_wrapper; -pub mod llvm_readobj; +pub mod llvm; pub mod run; pub mod rustc; pub mod rustdoc; @@ -29,12 +29,15 @@ pub use wasmparser; pub use cc::{cc, extra_c_flags, extra_cxx_flags, Cc}; pub use clang::{clang, Clang}; pub use diff::{diff, Diff}; -pub use llvm_readobj::{llvm_readobj, LlvmReadobj}; -pub use run::{cmd, run, run_fail}; +pub use llvm::{ + llvm_filecheck, llvm_profdata, llvm_readobj, LlvmFilecheck, LlvmProfdata, LlvmReadobj, +}; +pub use run::{cmd, run, run_fail, run_with_args}; pub use rustc::{aux_build, rustc, Rustc}; pub use rustdoc::{bare_rustdoc, rustdoc, Rustdoc}; #[track_caller] +#[must_use] pub fn env_var(name: &str) -> String { match env::var(name) { Ok(v) => v, @@ -43,6 +46,7 @@ pub fn env_var(name: &str) -> String { } #[track_caller] +#[must_use] pub fn env_var_os(name: &str) -> OsString { match env::var_os(name) { Some(v) => v, @@ -51,32 +55,38 @@ pub fn env_var_os(name: &str) -> OsString { } /// `TARGET` +#[must_use] pub fn target() -> String { env_var("TARGET") } /// Check if target is windows-like. +#[must_use] pub fn is_windows() -> bool { target().contains("windows") } /// Check if target uses msvc. +#[must_use] pub fn is_msvc() -> bool { target().contains("msvc") } /// Check if target uses macOS. +#[must_use] pub fn is_darwin() -> bool { target().contains("darwin") } #[track_caller] +#[must_use] pub fn python_command() -> Command { let python_path = env_var("PYTHON"); Command::new(python_path) } #[track_caller] +#[must_use] pub fn htmldocck() -> Command { let mut python = python_command(); python.arg(source_root().join("src/etc/htmldocck.py")); @@ -89,11 +99,41 @@ pub fn path<P: AsRef<Path>>(p: P) -> PathBuf { } /// Path to the root rust-lang/rust source checkout. +#[must_use] pub fn source_root() -> PathBuf { env_var("SOURCE_ROOT").into() } +/// Creates a new symlink to a path on the filesystem, adjusting for Windows or Unix. +#[cfg(target_family = "windows")] +pub fn create_symlink<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) { + if link.as_ref().exists() { + std::fs::remove_dir(link.as_ref()).unwrap(); + } + use std::os::windows::fs; + fs::symlink_file(original.as_ref(), link.as_ref()).expect(&format!( + "failed to create symlink {:?} for {:?}", + link.as_ref().display(), + original.as_ref().display(), + )); +} + +/// Creates a new symlink to a path on the filesystem, adjusting for Windows or Unix. +#[cfg(target_family = "unix")] +pub fn create_symlink<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) { + if link.as_ref().exists() { + std::fs::remove_dir(link.as_ref()).unwrap(); + } + use std::os::unix::fs; + fs::symlink(original.as_ref(), link.as_ref()).expect(&format!( + "failed to create symlink {:?} for {:?}", + link.as_ref().display(), + original.as_ref().display(), + )); +} + /// Construct the static library name based on the platform. +#[must_use] pub fn static_lib_name(name: &str) -> String { // See tools.mk (irrelevant lines omitted): // @@ -118,6 +158,7 @@ pub fn static_lib_name(name: &str) -> String { } /// Construct the dynamic library name based on the platform. +#[must_use] pub fn dynamic_lib_name(name: &str) -> String { // See tools.mk (irrelevant lines omitted): // @@ -144,6 +185,7 @@ pub fn dynamic_lib_name(name: &str) -> String { } } +#[must_use] pub fn dynamic_lib_extension() -> &'static str { if is_darwin() { "dylib" @@ -155,16 +197,19 @@ pub fn dynamic_lib_extension() -> &'static str { } /// Generate the name a rust library (rlib) would have. +#[must_use] pub fn rust_lib_name(name: &str) -> String { format!("lib{name}.rlib") } /// Construct the binary name based on platform. +#[must_use] pub fn bin_name(name: &str) -> String { if is_windows() { format!("{name}.exe") } else { name.to_string() } } /// Return the current working directory. +#[must_use] pub fn cwd() -> PathBuf { env::current_dir().unwrap() } @@ -172,6 +217,7 @@ pub fn cwd() -> PathBuf { /// Use `cygpath -w` on a path to get a Windows path string back. This assumes that `cygpath` is /// available on the platform! #[track_caller] +#[must_use] pub fn cygpath_windows<P: AsRef<Path>>(path: P) -> String { let caller = panic::Location::caller(); let mut cygpath = Command::new("cygpath"); @@ -187,6 +233,7 @@ pub fn cygpath_windows<P: AsRef<Path>>(path: P) -> String { /// Run `uname`. This assumes that `uname` is available on the platform! #[track_caller] +#[must_use] pub fn uname() -> String { let caller = panic::Location::caller(); let mut uname = Command::new("uname"); diff --git a/src/tools/run-make-support/src/llvm.rs b/src/tools/run-make-support/src/llvm.rs new file mode 100644 index 00000000000..414251abda2 --- /dev/null +++ b/src/tools/run-make-support/src/llvm.rs @@ -0,0 +1,127 @@ +use std::path::{Path, PathBuf}; + +use crate::{env_var, Command}; + +/// Construct a new `llvm-readobj` invocation. This assumes that `llvm-readobj` is available +/// at `$LLVM_BIN_DIR/llvm-readobj`. +pub fn llvm_readobj() -> LlvmReadobj { + LlvmReadobj::new() +} + +/// Construct a new `llvm-profdata` invocation. This assumes that `llvm-profdata` is available +/// at `$LLVM_BIN_DIR/llvm-profdata`. +pub fn llvm_profdata() -> LlvmProfdata { + LlvmProfdata::new() +} + +/// Construct a new `llvm-filecheck` invocation. This assumes that `llvm-filecheck` is available +/// at `$LLVM_FILECHECK`. +pub fn llvm_filecheck() -> LlvmFilecheck { + LlvmFilecheck::new() +} + +/// A `llvm-readobj` invocation builder. +#[derive(Debug)] +#[must_use] +pub struct LlvmReadobj { + cmd: Command, +} + +/// A `llvm-profdata` invocation builder. +#[derive(Debug)] +#[must_use] +pub struct LlvmProfdata { + cmd: Command, +} + +/// A `llvm-filecheck` invocation builder. +#[derive(Debug)] +#[must_use] +pub struct LlvmFilecheck { + cmd: Command, +} + +crate::impl_common_helpers!(LlvmReadobj); +crate::impl_common_helpers!(LlvmProfdata); +crate::impl_common_helpers!(LlvmFilecheck); + +/// Generate the path to the bin directory of LLVM. +#[must_use] +pub fn llvm_bin_dir() -> PathBuf { + let llvm_bin_dir = env_var("LLVM_BIN_DIR"); + PathBuf::from(llvm_bin_dir) +} + +impl LlvmReadobj { + /// Construct a new `llvm-readobj` invocation. This assumes that `llvm-readobj` is available + /// at `$LLVM_BIN_DIR/llvm-readobj`. + pub fn new() -> Self { + let llvm_readobj = llvm_bin_dir().join("llvm-readobj"); + let cmd = Command::new(llvm_readobj); + Self { cmd } + } + + /// Provide an input file. + pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self { + self.cmd.arg(path.as_ref()); + self + } + + /// Pass `--file-header` to display file headers. + pub fn file_header(&mut self) -> &mut Self { + self.cmd.arg("--file-header"); + self + } +} + +impl LlvmProfdata { + /// Construct a new `llvm-profdata` invocation. This assumes that `llvm-profdata` is available + /// at `$LLVM_BIN_DIR/llvm-profdata`. + pub fn new() -> Self { + let llvm_profdata = llvm_bin_dir().join("llvm-profdata"); + let cmd = Command::new(llvm_profdata); + Self { cmd } + } + + /// Provide an input file. + pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self { + self.cmd.arg(path.as_ref()); + self + } + + /// Specify the output file path. + pub fn output<P: AsRef<Path>>(&mut self, path: P) -> &mut Self { + self.cmd.arg("-o"); + self.cmd.arg(path.as_ref()); + self + } + + /// Take several profile data files generated by PGO instrumentation and merge them + /// together into a single indexed profile data file. + pub fn merge(&mut self) -> &mut Self { + self.cmd.arg("merge"); + self + } +} + +impl LlvmFilecheck { + /// Construct a new `llvm-filecheck` invocation. This assumes that `llvm-filecheck` is available + /// at `$LLVM_FILECHECK`. + pub fn new() -> Self { + let llvm_filecheck = env_var("LLVM_FILECHECK"); + let cmd = Command::new(llvm_filecheck); + Self { cmd } + } + + /// Pipe a read file into standard input containing patterns that will be matched against the .patterns(path) call. + pub fn stdin<I: AsRef<[u8]>>(&mut self, input: I) -> &mut Self { + self.cmd.set_stdin(input.as_ref().to_vec().into_boxed_slice()); + self + } + + /// Provide the patterns that need to be matched. + pub fn patterns<P: AsRef<Path>>(&mut self, path: P) -> &mut Self { + self.cmd.arg(path.as_ref()); + self + } +} diff --git a/src/tools/run-make-support/src/llvm_readobj.rs b/src/tools/run-make-support/src/llvm_readobj.rs index 57ddfc205e6..3c719356e8f 100644 --- a/src/tools/run-make-support/src/llvm_readobj.rs +++ b/src/tools/run-make-support/src/llvm_readobj.rs @@ -12,6 +12,7 @@ pub fn llvm_readobj() -> LlvmReadobj { /// A `llvm-readobj` invocation builder. #[derive(Debug)] +#[must_use] pub struct LlvmReadobj { cmd: Command, } diff --git a/src/tools/run-make-support/src/run.rs b/src/tools/run-make-support/src/run.rs index 6fa1a75363c..54730bb7de7 100644 --- a/src/tools/run-make-support/src/run.rs +++ b/src/tools/run-make-support/src/run.rs @@ -9,12 +9,17 @@ use crate::{cwd, env_var, is_windows, set_host_rpath}; use super::handle_failed_output; #[track_caller] -fn run_common(name: &str) -> Command { +fn run_common(name: &str, args: Option<&[&str]>) -> Command { let mut bin_path = PathBuf::new(); bin_path.push(cwd()); bin_path.push(name); let ld_lib_path_envvar = env_var("LD_LIB_PATH_ENVVAR"); let mut cmd = Command::new(bin_path); + if let Some(args) = args { + for arg in args { + cmd.arg(arg); + } + } cmd.env(&ld_lib_path_envvar, { let mut paths = vec![]; paths.push(cwd()); @@ -43,7 +48,19 @@ fn run_common(name: &str) -> Command { #[track_caller] pub fn run(name: &str) -> CompletedProcess { let caller = panic::Location::caller(); - let mut cmd = run_common(name); + let mut cmd = run_common(name, None); + let output = cmd.run(); + if !output.status().success() { + handle_failed_output(&cmd, output, caller.line()); + } + output +} + +/// Run a built binary with one or more argument(s) and make sure it succeeds. +#[track_caller] +pub fn run_with_args(name: &str, args: &[&str]) -> CompletedProcess { + let caller = panic::Location::caller(); + let mut cmd = run_common(name, Some(args)); let output = cmd.run(); if !output.status().success() { handle_failed_output(&cmd, output, caller.line()); @@ -55,7 +72,7 @@ pub fn run(name: &str) -> CompletedProcess { #[track_caller] pub fn run_fail(name: &str) -> CompletedProcess { let caller = panic::Location::caller(); - let mut cmd = run_common(name); + let mut cmd = run_common(name, None); let output = cmd.run_fail(); if output.status().success() { handle_failed_output(&cmd, output, caller.line()); diff --git a/src/tools/run-make-support/src/rustc.rs b/src/tools/run-make-support/src/rustc.rs index 13cec0cb6be..331e1a5ab8b 100644 --- a/src/tools/run-make-support/src/rustc.rs +++ b/src/tools/run-make-support/src/rustc.rs @@ -18,6 +18,7 @@ pub fn aux_build() -> Rustc { /// A `rustc` invocation builder. #[derive(Debug)] +#[must_use] pub struct Rustc { cmd: Command, } @@ -152,6 +153,24 @@ impl Rustc { self } + /// Specify directory path used for profile generation + pub fn profile_generate<P: AsRef<Path>>(&mut self, path: P) -> &mut Self { + let mut arg = OsString::new(); + arg.push("-Cprofile-generate="); + arg.push(path.as_ref()); + self.cmd.arg(&arg); + self + } + + /// Specify directory path used for profile usage + pub fn profile_use<P: AsRef<Path>>(&mut self, path: P) -> &mut Self { + let mut arg = OsString::new(); + arg.push("-Cprofile-use="); + arg.push(path.as_ref()); + self.cmd.arg(&arg); + self + } + /// Specify error format to use pub fn error_format(&mut self, format: &str) -> &mut Self { self.cmd.arg(format!("--error-format={format}")); diff --git a/src/tools/run-make-support/src/rustdoc.rs b/src/tools/run-make-support/src/rustdoc.rs index 332906f739a..93078561254 100644 --- a/src/tools/run-make-support/src/rustdoc.rs +++ b/src/tools/run-make-support/src/rustdoc.rs @@ -17,6 +17,7 @@ pub fn rustdoc() -> Rustdoc { } #[derive(Debug)] +#[must_use] pub struct Rustdoc { cmd: Command, } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 5a9621bb69a..241690d0089 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -1387,6 +1387,7 @@ fn generic_args_sans_defaults<'ga>( } // otherwise, if the arg is equal to the param default, hide it (unless the // default is an error which can happen for the trait Self type) + #[allow(unstable_name_collisions)] default_parameters.get(i).is_none_or(|default_parameter| { // !is_err(default_parameter.skip_binders()) // && diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index 87185eef952..37da5d9c88d 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -168,7 +168,6 @@ run-make/pass-linker-flags/Makefile run-make/pass-non-c-like-enum-to-c/Makefile run-make/pdb-alt-path/Makefile run-make/pdb-buildinfo-cl-cmd/Makefile -run-make/pgo-branch-weights/Makefile run-make/pgo-gen-lto/Makefile run-make/pgo-gen-no-imp-symbols/Makefile run-make/pgo-gen/Makefile @@ -227,9 +226,6 @@ run-make/std-core-cycle/Makefile run-make/symbol-mangling-hashed/Makefile run-make/symbol-visibility/Makefile run-make/symbols-include-type-name/Makefile -run-make/symlinked-extern/Makefile -run-make/symlinked-libraries/Makefile -run-make/symlinked-rlib/Makefile run-make/sysroot-crates-are-unstable/Makefile run-make/target-cpu-native/Makefile run-make/target-specs/Makefile diff --git a/src/tools/tidy/src/alphabetical.rs b/src/tools/tidy/src/alphabetical.rs index 150a9594350..a29286fa2c5 100644 --- a/src/tools/tidy/src/alphabetical.rs +++ b/src/tools/tidy/src/alphabetical.rs @@ -88,7 +88,7 @@ fn check_section<'a>( let trimmed_line = line.trim_start_matches(' '); if trimmed_line.starts_with("//") - || (trimmed_line.starts_with("#") && !trimmed_line.starts_with("#!")) + || (trimmed_line.starts_with('#') && !trimmed_line.starts_with("#!")) || trimmed_line.starts_with(is_close_bracket) { continue; diff --git a/src/tools/tidy/src/bins.rs b/src/tools/tidy/src/bins.rs index 64ba79dc185..c82e8b6fee9 100644 --- a/src/tools/tidy/src/bins.rs +++ b/src/tools/tidy/src/bins.rs @@ -61,7 +61,7 @@ mod os_impl { fs::remove_file(&path).expect("Deleted temp file"); // If the file is executable, then we assume that this // filesystem does not track executability, so skip this check. - return if exec { Unsupported } else { Supported }; + if exec { Unsupported } else { Supported } } Err(e) => { // If the directory is read-only or we otherwise don't have rights, @@ -76,7 +76,7 @@ mod os_impl { panic!("unable to create temporary file `{:?}`: {:?}", path, e); } - }; + } } for &source_dir in sources { @@ -92,7 +92,7 @@ mod os_impl { } } - return true; + true } // FIXME: check when rust-installer test sh files will be removed, diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 2dd3d17f9e3..7337c9843c7 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -699,11 +699,9 @@ fn check_permitted_dependencies( for dep in deps { let dep = pkg_from_id(metadata, dep); // If this path is in-tree, we don't require it to be explicitly permitted. - if dep.source.is_some() { - if !permitted_dependencies.contains(dep.name.as_str()) { - tidy_error!(bad, "Dependency for {descr} not explicitly permitted: {}", dep.id); - has_permitted_dep_error = true; - } + if dep.source.is_some() && !permitted_dependencies.contains(dep.name.as_str()) { + tidy_error!(bad, "Dependency for {descr} not explicitly permitted: {}", dep.id); + has_permitted_dep_error = true; } } diff --git a/src/tools/tidy/src/error_codes.rs b/src/tools/tidy/src/error_codes.rs index 39f7e70b693..47e2be761e6 100644 --- a/src/tools/tidy/src/error_codes.rs +++ b/src/tools/tidy/src/error_codes.rs @@ -308,11 +308,9 @@ fn check_error_codes_tests( for line in file.lines() { let s = line.trim(); // Assuming the line starts with `error[E`, we can substring the error code out. - if s.starts_with("error[E") { - if &s[6..11] == code { - found_code = true; - break; - } + if s.starts_with("error[E") && &s[6..11] == code { + found_code = true; + break; }; } diff --git a/src/tools/tidy/src/ext_tool_checks.rs b/src/tools/tidy/src/ext_tool_checks.rs index 2c97a39f100..fb626cac3fa 100644 --- a/src/tools/tidy/src/ext_tool_checks.rs +++ b/src/tools/tidy/src/ext_tool_checks.rs @@ -78,9 +78,9 @@ fn check_impl( let mut py_path = None; let (cfg_args, file_args): (Vec<_>, Vec<_>) = pos_args - .into_iter() + .iter() .map(OsStr::new) - .partition(|arg| arg.to_str().is_some_and(|s| s.starts_with("-"))); + .partition(|arg| arg.to_str().is_some_and(|s| s.starts_with('-'))); if python_lint || python_fmt { let venv_path = outdir.join("venv"); @@ -274,13 +274,19 @@ fn create_venv_at_path(path: &Path) -> Result<(), Error> { if out.status.success() { return Ok(()); } - let err = if String::from_utf8_lossy(&out.stderr).contains("No module named virtualenv") { - Error::Generic(format!( + + let stderr = String::from_utf8_lossy(&out.stderr); + let err = if stderr.contains("No module named virtualenv") { + Error::Generic( "virtualenv not found: you may need to install it \ (`python3 -m pip install venv`)" - )) + .to_owned(), + ) } else { - Error::Generic(format!("failed to create venv at '{}' using {sys_py}", path.display())) + Error::Generic(format!( + "failed to create venv at '{}' using {sys_py}: {stderr}", + path.display() + )) }; Err(err) } diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs index 2d3888ec75f..7bcb85335e0 100644 --- a/src/tools/tidy/src/style.rs +++ b/src/tools/tidy/src/style.rs @@ -463,10 +463,13 @@ pub fn check(path: &Path, bad: &mut bool) { } } // for now we just check libcore - if trimmed.contains("unsafe {") && !trimmed.starts_with("//") && !last_safety_comment { - if file.components().any(|c| c.as_os_str() == "core") && !is_test { - suppressible_tidy_err!(err, skip_undocumented_unsafe, "undocumented unsafe"); - } + if trimmed.contains("unsafe {") + && !trimmed.starts_with("//") + && !last_safety_comment + && file.components().any(|c| c.as_os_str() == "core") + && !is_test + { + suppressible_tidy_err!(err, skip_undocumented_unsafe, "undocumented unsafe"); } if trimmed.contains("// SAFETY:") { last_safety_comment = true; @@ -487,10 +490,10 @@ pub fn check(path: &Path, bad: &mut bool) { "copyright notices attributed to the Rust Project Developers are deprecated" ); } - if !file.components().any(|c| c.as_os_str() == "rustc_baked_icu_data") { - if is_unexplained_ignore(&extension, line) { - err(UNEXPLAINED_IGNORE_DOCTEST_INFO); - } + if !file.components().any(|c| c.as_os_str() == "rustc_baked_icu_data") + && is_unexplained_ignore(&extension, line) + { + err(UNEXPLAINED_IGNORE_DOCTEST_INFO); } if filename.ends_with(".cpp") && line.contains("llvm_unreachable") { @@ -525,26 +528,24 @@ pub fn check(path: &Path, bad: &mut bool) { backtick_count += comment_text.chars().filter(|ch| *ch == '`').count(); } comment_block = Some((start_line, backtick_count)); - } else { - if let Some((start_line, backtick_count)) = comment_block.take() { - if backtick_count % 2 == 1 { - let mut err = |msg: &str| { - tidy_error!(bad, "{}:{start_line}: {msg}", file.display()); - }; - let block_len = (i + 1) - start_line; - if block_len == 1 { - suppressible_tidy_err!( - err, - skip_odd_backticks, - "comment with odd number of backticks" - ); - } else { - suppressible_tidy_err!( - err, - skip_odd_backticks, - "{block_len}-line comment block with odd number of backticks" - ); - } + } else if let Some((start_line, backtick_count)) = comment_block.take() { + if backtick_count % 2 == 1 { + let mut err = |msg: &str| { + tidy_error!(bad, "{}:{start_line}: {msg}", file.display()); + }; + let block_len = (i + 1) - start_line; + if block_len == 1 { + suppressible_tidy_err!( + err, + skip_odd_backticks, + "comment with odd number of backticks" + ); + } else { + suppressible_tidy_err!( + err, + skip_odd_backticks, + "{block_len}-line comment block with odd number of backticks" + ); } } } diff --git a/src/tools/tidy/src/walk.rs b/src/tools/tidy/src/walk.rs index 63a03834166..1cecf998e28 100644 --- a/src/tools/tidy/src/walk.rs +++ b/src/tools/tidy/src/walk.rs @@ -79,13 +79,11 @@ pub(crate) fn walk_no_read( let walker = walker.filter_entry(move |e| { !skip(e.path(), e.file_type().map(|ft| ft.is_dir()).unwrap_or(false)) }); - for entry in walker.build() { - if let Ok(entry) = entry { - if entry.file_type().map_or(true, |kind| kind.is_dir() || kind.is_symlink()) { - continue; - } - f(&entry); + for entry in walker.build().flatten() { + if entry.file_type().map_or(true, |kind| kind.is_dir() || kind.is_symlink()) { + continue; } + f(&entry); } } @@ -97,11 +95,9 @@ pub(crate) fn walk_dir( ) { let mut walker = ignore::WalkBuilder::new(path); let walker = walker.filter_entry(move |e| !skip(e.path())); - for entry in walker.build() { - if let Ok(entry) = entry { - if entry.path().is_dir() { - f(&entry); - } + for entry in walker.build().flatten() { + if entry.path().is_dir() { + f(&entry); } } } diff --git a/src/tools/tidy/src/x_version.rs b/src/tools/tidy/src/x_version.rs index c470d502a65..55bfbed8b0e 100644 --- a/src/tools/tidy/src/x_version.rs +++ b/src/tools/tidy/src/x_version.rs @@ -52,7 +52,7 @@ pub fn check(root: &Path, cargo: &Path, bad: &mut bool) { ); } } else { - return tidy_error!(bad, "failed to check version of `x`: {}", cargo_list.status); + tidy_error!(bad, "failed to check version of `x`: {}", cargo_list.status) } } diff --git a/tests/crashes/125811.rs b/tests/crashes/125811.rs deleted file mode 100644 index eb764e8d152..00000000000 --- a/tests/crashes/125811.rs +++ /dev/null @@ -1,34 +0,0 @@ -//@ known-bug: rust-lang/rust#125811 -mod assert { - use std::mem::{Assume, BikeshedIntrinsicFrom}; - - pub fn is_transmutable<Src, Dst>() - where - Dst: BikeshedIntrinsicFrom<Src>, - { - } -} - -#[repr(C)] -struct Zst; - -enum V0 { - B(!), -} - -enum V2 { - V = 2, -} - -enum Lopsided { - Smol(Zst), - Lorg(V0), -} - -#[repr(C)] -#[repr(C)] -struct Dst(Lopsided, V2); - -fn should_pad_variants() { - assert::is_transmutable::<Src, Dst>(); -} diff --git a/tests/run-make/pgo-branch-weights/Makefile b/tests/run-make/pgo-branch-weights/Makefile deleted file mode 100644 index 4c9f8b2493a..00000000000 --- a/tests/run-make/pgo-branch-weights/Makefile +++ /dev/null @@ -1,35 +0,0 @@ -# needs-profiler-support -# ignore-windows-gnu -# ignore-cross-compile - -# FIXME(mati865): MinGW GCC miscompiles compiler-rt profiling library but with Clang it works -# properly. Since we only have GCC on the CI ignore the test for now. - -include ../tools.mk - -# For some very small programs GNU ld seems to not properly handle -# instrumentation sections correctly. Neither Gold nor LLD have that problem. -ifeq ($(UNAME),Linux) -ifneq (,$(findstring x86,$(TARGET))) -COMMON_FLAGS=-Clink-args=-fuse-ld=gold -endif -endif - - -all: - # We don't compile `opaque` with either optimizations or instrumentation. - $(RUSTC) $(COMMON_FLAGS) opaque.rs || exit 1 - # Compile the test program with instrumentation - mkdir -p "$(TMPDIR)/prof_data_dir" || exit 1 - $(RUSTC) $(COMMON_FLAGS) interesting.rs \ - -Cprofile-generate="$(TMPDIR)/prof_data_dir" -O -Ccodegen-units=1 || exit 1 - $(RUSTC) $(COMMON_FLAGS) main.rs -Cprofile-generate="$(TMPDIR)/prof_data_dir" -O || exit 1 - # The argument below generates to the expected branch weights - $(call RUN,main aaaaaaaaaaaa2bbbbbbbbbbbb2bbbbbbbbbbbbbbbbcc) || exit 1 - "$(LLVM_BIN_DIR)/llvm-profdata" merge \ - -o "$(TMPDIR)/prof_data_dir/merged.profdata" \ - "$(TMPDIR)/prof_data_dir" || exit 1 - $(RUSTC) $(COMMON_FLAGS) interesting.rs \ - -Cprofile-use="$(TMPDIR)/prof_data_dir/merged.profdata" -O \ - -Ccodegen-units=1 --emit=llvm-ir || exit 1 - cat "$(TMPDIR)/interesting.ll" | "$(LLVM_FILECHECK)" filecheck-patterns.txt diff --git a/tests/run-make/pgo-branch-weights/rmake.rs b/tests/run-make/pgo-branch-weights/rmake.rs new file mode 100644 index 00000000000..55f6e7e56c5 --- /dev/null +++ b/tests/run-make/pgo-branch-weights/rmake.rs @@ -0,0 +1,45 @@ +// This test generates an instrumented binary - a program which +// will keep track of how many times it calls each function, a useful +// feature for optimization. Then, an argument (aaaaaaaaaaaa2bbbbbbbbbbbb2bbbbbbbbbbbbbbbbcc) +// is passed into the instrumented binary, which should react with a number of function calls +// fully known in advance. (For example, the letter 'a' results in calling f1()) + +// If the test passes, the expected function call count was added to the use-phase LLVM-IR. +// See https://github.com/rust-lang/rust/pull/66631 + +//@ needs-profiler-support +//@ ignore-cross-compile + +// FIXME(Oneirical): This test has problems generating profdata on mingw. +// For more information, see https://github.com/rust-lang/rust/pull/122613 +//@ ignore-windows-gnu + +use run_make_support::{fs_wrapper, llvm_filecheck, llvm_profdata, run_with_args, rustc}; +use std::path::Path; + +fn main() { + let path_prof_data_dir = Path::new("prof_data_dir"); + let path_merged_profdata = path_prof_data_dir.join("merged.profdata"); + rustc().input("opaque.rs").run(); + fs_wrapper::create_dir_all(&path_prof_data_dir); + rustc() + .input("interesting.rs") + .profile_generate(&path_prof_data_dir) + .opt() + .codegen_units(1) + .run(); + rustc().input("main.rs").profile_generate(&path_prof_data_dir).opt().run(); + run_with_args("main", &["aaaaaaaaaaaa2bbbbbbbbbbbb2bbbbbbbbbbbbbbbbcc"]); + llvm_profdata().merge().output(&path_merged_profdata).input(path_prof_data_dir).run(); + rustc() + .input("interesting.rs") + .profile_use(path_merged_profdata) + .opt() + .codegen_units(1) + .emit("llvm-ir") + .run(); + llvm_filecheck() + .patterns("filecheck-patterns.txt") + .stdin(fs_wrapper::read("interesting.ll")) + .run(); +} diff --git a/tests/run-make/symlinked-extern/Makefile b/tests/run-make/symlinked-extern/Makefile deleted file mode 100644 index 28c764b84e8..00000000000 --- a/tests/run-make/symlinked-extern/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -# ignore-windows -# `ln` is actually `cp` on msys. - -all: - $(RUSTC) foo.rs - mkdir -p $(TMPDIR)/other - ln -nsf $(TMPDIR)/libfoo.rlib $(TMPDIR)/other - $(RUSTC) bar.rs -L $(TMPDIR) - $(RUSTC) baz.rs --extern foo=$(TMPDIR)/other/libfoo.rlib -L $(TMPDIR) diff --git a/tests/run-make/symlinked-extern/rmake.rs b/tests/run-make/symlinked-extern/rmake.rs new file mode 100644 index 00000000000..98f69aefbe6 --- /dev/null +++ b/tests/run-make/symlinked-extern/rmake.rs @@ -0,0 +1,21 @@ +// Crates that are resolved normally have their path canonicalized and all +// symlinks resolved. This did not happen for paths specified +// using the --extern option to rustc, which could lead to rustc thinking +// that it encountered two different versions of a crate, when it's +// actually the same version found through different paths. +// See https://github.com/rust-lang/rust/pull/16505 + +// This test checks that --extern and symlinks together +// can result in successful compilation. + +//@ ignore-cross-compile + +use run_make_support::{create_symlink, cwd, fs_wrapper, rustc}; + +fn main() { + rustc().input("foo.rs").run(); + fs_wrapper::create_dir_all("other"); + create_symlink("libfoo.rlib", "other"); + rustc().input("bar.rs").library_search_path(cwd()).run(); + rustc().input("baz.rs").extern_("foo", "other").library_search_path(cwd()).run(); +} diff --git a/tests/run-make/symlinked-libraries/Makefile b/tests/run-make/symlinked-libraries/Makefile deleted file mode 100644 index fb0b6127e6f..00000000000 --- a/tests/run-make/symlinked-libraries/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -# ignore-windows -# `ln` is actually `cp` on msys. - -all: - $(RUSTC) foo.rs -C prefer-dynamic - mkdir -p $(TMPDIR)/other - ln -nsf $(TMPDIR)/$(call DYLIB_GLOB,foo) $(TMPDIR)/other - $(RUSTC) bar.rs -L $(TMPDIR)/other diff --git a/tests/run-make/symlinked-libraries/rmake.rs b/tests/run-make/symlinked-libraries/rmake.rs new file mode 100644 index 00000000000..eaf0c44206a --- /dev/null +++ b/tests/run-make/symlinked-libraries/rmake.rs @@ -0,0 +1,16 @@ +// When a directory and a symlink simultaneously exist with the same name, +// setting that name as the library search path should not cause rustc +// to avoid looking in the symlink and cause an error. This test creates +// a directory and a symlink named "other", and places the library in the symlink. +// If it succeeds, the library was successfully found. +// See https://github.com/rust-lang/rust/issues/12459 + +//@ ignore-cross-compile +use run_make_support::{create_symlink, dynamic_lib_name, fs_wrapper, rustc}; + +fn main() { + rustc().input("foo.rs").arg("-Cprefer-dynamic").run(); + fs_wrapper::create_dir_all("other"); + create_symlink(dynamic_lib_name("foo"), "other"); + rustc().input("bar.rs").library_search_path("other").run(); +} diff --git a/tests/run-make/symlinked-rlib/Makefile b/tests/run-make/symlinked-rlib/Makefile deleted file mode 100644 index a8565f683c3..00000000000 --- a/tests/run-make/symlinked-rlib/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -# ignore-windows -# `ln` is actually `cp` on msys. - -all: - $(RUSTC) foo.rs --crate-type=rlib -o $(TMPDIR)/foo.xxx - ln -nsf $(TMPDIR)/foo.xxx $(TMPDIR)/libfoo.rlib - $(RUSTC) bar.rs -L $(TMPDIR) diff --git a/tests/run-make/symlinked-rlib/rmake.rs b/tests/run-make/symlinked-rlib/rmake.rs new file mode 100644 index 00000000000..3759ca25928 --- /dev/null +++ b/tests/run-make/symlinked-rlib/rmake.rs @@ -0,0 +1,16 @@ +// Rustc did not recognize libraries which were symlinked +// to files having extension other than .rlib. This was fixed +// in #32828. This test creates a symlink to "foo.xxx", which has +// an unusual file extension, and checks that rustc can successfully +// use it as an rlib library. +// See https://github.com/rust-lang/rust/pull/32828 + +//@ ignore-cross-compile + +use run_make_support::{create_symlink, cwd, rustc}; + +fn main() { + rustc().input("foo.rs").crate_type("rlib").output("foo.xxx").run(); + create_symlink("foo.xxx", "libfoo.rlib"); + rustc().input("bar.rs").library_search_path(cwd()).run(); +} diff --git a/tests/rustdoc-gui/help-page.goml b/tests/rustdoc-gui/help-page.goml deleted file mode 100644 index 09d33af139c..00000000000 --- a/tests/rustdoc-gui/help-page.goml +++ /dev/null @@ -1,69 +0,0 @@ -// This test ensures that opening the help page in its own tab works. -include: "utils.goml" -go-to: "file://" + |DOC_PATH| + "/help.html" -set-window-size: (1000, 1000) // Try desktop size first. -wait-for: "#help" -assert-css: ("#help", {"display": "block"}) -assert-css: ("#help dd", {"font-size": "16px"}) -click: "#help-button > a" -assert-css: ("#help", {"display": "block"}) -compare-elements-property: (".sub", "#help", ["offsetWidth"]) -compare-elements-position: (".sub", "#help", ["x"]) -set-window-size: (500, 1000) // Try mobile next. -assert-css: ("#help", {"display": "block"}) -compare-elements-property: (".sub", "#help", ["offsetWidth"]) -compare-elements-position: (".sub", "#help", ["x"]) - -// Checking the color of the elements of the help menu. -show-text: true -define-function: ( - "check-colors", - [theme, color, background, box_shadow], - block { - call-function: ("switch-theme", {"theme": |theme|}) - assert-css: ("#help kbd", { - "color": |color|, - "background-color": |background|, - "box-shadow": |box_shadow| + " 0px -1px 0px 0px inset", - }, ALL) - }, -) - -call-function: ("check-colors", { - "theme": "ayu", - "color": "#c5c5c5", - "background": "#314559", - "box_shadow": "#5c6773", -}) -call-function: ("check-colors", { - "theme": "dark", - "color": "#000", - "background": "#fafbfc", - "box_shadow": "#c6cbd1", -}) -call-function: ("check-colors", { - "theme": "light", - "color": "#000", - "background": "#fafbfc", - "box_shadow": "#c6cbd1", -}) - -// This test ensures that opening the help popover without switching pages works. -go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" -set-window-size: (1000, 1000) // Only supported on desktop. -assert-false: "#help" -click: "#help-button > a" -assert-css: ("#help", {"display": "block"}) -assert-css: ("#help dd", {"font-size": "16px"}) -click: "#help-button > a" -assert-css: ("#help", {"display": "none"}) -compare-elements-property-false: (".sub", "#help", ["offsetWidth"]) -compare-elements-position-false: (".sub", "#help", ["x"]) - -// This test ensures that the "the rustdoc book" anchor link within the help popover works. -go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" -set-window-size: (1000, 1000) // Popover only appears when the screen width is >700px. -assert-false: "#help" -click: "#help-button > a" -click: "//*[@id='help']//a[text()='the rustdoc book']" -wait-for-document-property: ({"URL": "https://doc.rust-lang.org/"}, STARTS_WITH) diff --git a/tests/ui-fulldeps/stable-mir/check_def_ty.rs b/tests/ui-fulldeps/stable-mir/check_def_ty.rs new file mode 100644 index 00000000000..9f45b62d343 --- /dev/null +++ b/tests/ui-fulldeps/stable-mir/check_def_ty.rs @@ -0,0 +1,114 @@ +//@ run-pass +//! Test that users are able to use stable mir APIs to retrieve type information from a crate item +//! definition. + +//@ ignore-stage1 +//@ ignore-cross-compile +//@ ignore-remote +//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 +//@ edition: 2021 + +#![feature(rustc_private)] +#![feature(assert_matches)] +#![feature(control_flow_enum)] + +#[macro_use] +extern crate rustc_smir; +extern crate rustc_driver; +extern crate rustc_interface; +extern crate stable_mir; + +use rustc_smir::rustc_internal; +use stable_mir::ty::{Ty, ForeignItemKind}; +use stable_mir::*; +use std::io::Write; +use std::ops::ControlFlow; + +const CRATE_NAME: &str = "crate_def_ty"; + +/// Test if we can retrieve type information from different definitions. +fn test_def_tys() -> ControlFlow<()> { + let items = stable_mir::all_local_items(); + for item in &items { + // Type from crate items. + let ty = item.ty(); + match item.name().as_str() { + "STATIC_STR" => assert!(ty.kind().is_ref()), + "CONST_U32" => assert!(ty.kind().is_integral()), + "main" => { check_fn_def(ty) } + _ => unreachable!("Unexpected item: `{item:?}`") + } + } + + let foreign_items = stable_mir::local_crate().foreign_modules(); + for item in foreign_items[0].module().items() { + // Type from foreign items. + let ty = item.ty(); + let item_kind = item.kind(); + let name = item.name(); + match item_kind { + ForeignItemKind::Fn(fn_def) => { + assert_eq!(&name, "extern_fn"); + assert_eq!(ty, fn_def.ty()); + check_fn_def(ty) + } + ForeignItemKind::Static(def) => { + assert_eq!(&name, "EXT_STATIC"); + assert_eq!(ty, def.ty()); + assert!(ty.kind().is_integral()) + } + _ => unreachable!("Unexpected kind: {item_kind:?}") + }; + } + + ControlFlow::Continue(()) +} + +fn check_fn_def(ty: Ty) { + let kind = ty.kind(); + let (def, args) = kind.fn_def().expect(&format!("Expected function type, but found: {ty}")); + assert!(def.ty().kind().is_fn()); + assert_eq!(def.ty_with_args(args), ty); +} + +/// This test will generate and analyze a dummy crate using the stable mir. +/// For that, it will first write the dummy crate into a file. +/// Then it will create a `StableMir` using custom arguments and then +/// it will run the compiler. +fn main() { + let path = "defs_ty_input.rs"; + generate_input(&path).unwrap(); + let args = vec![ + "rustc".to_string(), + "-Cpanic=abort".to_string(), + "--crate-name".to_string(), + CRATE_NAME.to_string(), + path.to_string(), + ]; + run!(args, test_def_tys).unwrap(); +} + +fn generate_input(path: &str) -> std::io::Result<()> { + let mut file = std::fs::File::create(path)?; + write!( + file, + r#" + // We would like to check intrinsic definition. + #![feature(core_intrinsics)] + static STATIC_STR: &str = "foo"; + const CONST_U32: u32 = 0u32; + + fn main() {{ + let _c = core::char::from_u32(99); + let _v = Vec::<u8>::new(); + let _i = std::intrinsics::size_of::<u8>(); + }} + + extern "C" {{ + fn extern_fn(x: i32) -> i32; + static EXT_STATIC: i32; + }} + "# + )?; + Ok(()) +} diff --git a/tests/ui/auto-traits/typeck-default-trait-impl-precedence.stderr b/tests/ui/auto-traits/typeck-default-trait-impl-precedence.stderr index 47bb1a059be..4773ac4ccf7 100644 --- a/tests/ui/auto-traits/typeck-default-trait-impl-precedence.stderr +++ b/tests/ui/auto-traits/typeck-default-trait-impl-precedence.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `&'static u32: Defaulted` is not satisfied --> $DIR/typeck-default-trait-impl-precedence.rs:19:20 | LL | is_defaulted::<&'static u32>(); - | ^^^^^^^^^^^^ the trait `Signed` is not implemented for `&'static u32`, which is required by `&'static u32: Defaulted` + | ^^^^^^^^^^^^ the trait `Signed` is not implemented for `u32`, which is required by `&'static u32: Defaulted` | note: required for `&'static u32` to implement `Defaulted` --> $DIR/typeck-default-trait-impl-precedence.rs:10:19 diff --git a/tests/ui/coherence/indirect-impl-for-trait-obj-coherence.next.stderr b/tests/ui/coherence/indirect-impl-for-trait-obj-coherence.next.stderr index 6e41561f1a7..b6636d4de86 100644 --- a/tests/ui/coherence/indirect-impl-for-trait-obj-coherence.next.stderr +++ b/tests/ui/coherence/indirect-impl-for-trait-obj-coherence.next.stderr @@ -1,8 +1,8 @@ -error[E0284]: type annotations needed: cannot satisfy `<dyn Object<U, Output = T> as Object<U>>::Output == T` +error[E0284]: type annotations needed: cannot normalize `<dyn Object<U, Output = T> as Object<U>>::Output` --> $DIR/indirect-impl-for-trait-obj-coherence.rs:25:41 | LL | foo::<dyn Object<U, Output = T>, U>(x) - | ^ cannot satisfy `<dyn Object<U, Output = T> as Object<U>>::Output == T` + | ^ cannot normalize `<dyn Object<U, Output = T> as Object<U>>::Output` error: aborting due to 1 previous error diff --git a/tests/ui/coherence/indirect-impl-for-trait-obj-coherence.rs b/tests/ui/coherence/indirect-impl-for-trait-obj-coherence.rs index 0b66a6e7830..abfd51c2008 100644 --- a/tests/ui/coherence/indirect-impl-for-trait-obj-coherence.rs +++ b/tests/ui/coherence/indirect-impl-for-trait-obj-coherence.rs @@ -23,7 +23,7 @@ fn foo<T: ?Sized, U>(x: <T as Object<U>>::Output) -> U { #[allow(dead_code)] fn transmute<T, U>(x: T) -> U { foo::<dyn Object<U, Output = T>, U>(x) - //[next]~^ ERROR type annotations needed: cannot satisfy `<dyn Object<U, Output = T> as Object<U>>::Output == T` + //[next]~^ ERROR type annotations needed: cannot normalize `<dyn Object<U, Output = T> as Object<U>>::Output` } fn main() {} diff --git a/tests/ui/coherence/occurs-check/associated-type.next.stderr b/tests/ui/coherence/occurs-check/associated-type.next.stderr index f32bb1301da..bd65cee58d9 100644 --- a/tests/ui/coherence/occurs-check/associated-type.next.stderr +++ b/tests/ui/coherence/occurs-check/associated-type.next.stderr @@ -16,11 +16,11 @@ LL | | for<'a> *const T: ToUnit<'a>, | = note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details -error[E0284]: type annotations needed: cannot satisfy `<for<'a> fn(&'a (), ()) as Overlap<for<'a> fn(&'a (), ())>>::Assoc == usize` +error[E0284]: type annotations needed: cannot normalize `<for<'a> fn(&'a (), ()) as Overlap<for<'a> fn(&'a (), ())>>::Assoc` --> $DIR/associated-type.rs:44:59 | LL | foo::<for<'a> fn(&'a (), ()), for<'a> fn(&'a (), ())>(3usize); - | ^^^^^^ cannot satisfy `<for<'a> fn(&'a (), ()) as Overlap<for<'a> fn(&'a (), ())>>::Assoc == usize` + | ^^^^^^ cannot normalize `<for<'a> fn(&'a (), ()) as Overlap<for<'a> fn(&'a (), ())>>::Assoc` error: aborting due to 2 previous errors diff --git a/tests/ui/coherence/occurs-check/associated-type.rs b/tests/ui/coherence/occurs-check/associated-type.rs index ac1236c554a..df03d5f60a0 100644 --- a/tests/ui/coherence/occurs-check/associated-type.rs +++ b/tests/ui/coherence/occurs-check/associated-type.rs @@ -42,5 +42,5 @@ fn foo<T: Overlap<U>, U>(x: T::Assoc) -> T::Assoc { fn main() { foo::<for<'a> fn(&'a (), ()), for<'a> fn(&'a (), ())>(3usize); - //[next]~^ ERROR: cannot satisfy + //[next]~^ ERROR: cannot normalize } diff --git a/tests/ui/const-generics/issues/issue-82956.stderr b/tests/ui/const-generics/issues/issue-82956.stderr index d0fc7112426..a956fc741f4 100644 --- a/tests/ui/const-generics/issues/issue-82956.stderr +++ b/tests/ui/const-generics/issues/issue-82956.stderr @@ -4,7 +4,7 @@ error[E0433]: failed to resolve: use of undeclared type `IntoIter` LL | let mut iter = IntoIter::new(self); | ^^^^^^^^ use of undeclared type `IntoIter` | -help: consider importing one of these items +help: consider importing one of these structs | LL + use std::array::IntoIter; | diff --git a/tests/ui/consts/const_refs_to_static-ice-121413.rs b/tests/ui/consts/const_refs_to_static-ice-121413.rs index 8a24fb799b6..8fc3912efd0 100644 --- a/tests/ui/consts/const_refs_to_static-ice-121413.rs +++ b/tests/ui/consts/const_refs_to_static-ice-121413.rs @@ -5,12 +5,16 @@ // ignore-tidy-linelength #![feature(const_refs_to_static)] const REF_INTERIOR_MUT: &usize = { + //~^ HELP consider importing this struct static FOO: Sync = AtomicUsize::new(0); //~^ ERROR failed to resolve: use of undeclared type `AtomicUsize` //~| WARN trait objects without an explicit `dyn` are deprecated //~| ERROR the size for values of type `(dyn Sync + 'static)` cannot be known at compilation time //~| ERROR the size for values of type `(dyn Sync + 'static)` cannot be known at compilation time //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + //~| HELP if this is an object-safe trait, use `dyn` + //~| HELP the trait `Sized` is not implemented for `(dyn Sync + 'static)` + //~| HELP the trait `Sized` is not implemented for `(dyn Sync + 'static)` unsafe { &*(&FOO as *const _ as *const usize) } }; pub fn main() {} diff --git a/tests/ui/consts/const_refs_to_static-ice-121413.stderr b/tests/ui/consts/const_refs_to_static-ice-121413.stderr index c977c698a92..fbe32a70293 100644 --- a/tests/ui/consts/const_refs_to_static-ice-121413.stderr +++ b/tests/ui/consts/const_refs_to_static-ice-121413.stderr @@ -1,5 +1,5 @@ error[E0433]: failed to resolve: use of undeclared type `AtomicUsize` - --> $DIR/const_refs_to_static-ice-121413.rs:8:24 + --> $DIR/const_refs_to_static-ice-121413.rs:9:24 | LL | static FOO: Sync = AtomicUsize::new(0); | ^^^^^^^^^^^ use of undeclared type `AtomicUsize` @@ -10,7 +10,7 @@ LL + use std::sync::atomic::AtomicUsize; | warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/const_refs_to_static-ice-121413.rs:8:17 + --> $DIR/const_refs_to_static-ice-121413.rs:9:17 | LL | static FOO: Sync = AtomicUsize::new(0); | ^^^^ @@ -24,7 +24,7 @@ LL | static FOO: dyn Sync = AtomicUsize::new(0); | +++ error[E0277]: the size for values of type `(dyn Sync + 'static)` cannot be known at compilation time - --> $DIR/const_refs_to_static-ice-121413.rs:8:17 + --> $DIR/const_refs_to_static-ice-121413.rs:9:17 | LL | static FOO: Sync = AtomicUsize::new(0); | ^^^^ doesn't have a size known at compile-time @@ -32,7 +32,7 @@ LL | static FOO: Sync = AtomicUsize::new(0); = help: the trait `Sized` is not implemented for `(dyn Sync + 'static)` error[E0277]: the size for values of type `(dyn Sync + 'static)` cannot be known at compilation time - --> $DIR/const_refs_to_static-ice-121413.rs:8:24 + --> $DIR/const_refs_to_static-ice-121413.rs:9:24 | LL | static FOO: Sync = AtomicUsize::new(0); | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time diff --git a/tests/ui/delegation/not-supported.rs b/tests/ui/delegation/not-supported.rs index 5f78de97638..25d7a4cb895 100644 --- a/tests/ui/delegation/not-supported.rs +++ b/tests/ui/delegation/not-supported.rs @@ -70,12 +70,16 @@ mod opaque { pub fn opaque_arg(_: impl Trait) -> i32 { 0 } pub fn opaque_ret() -> impl Trait { unimplemented!() } + //~^ warn: this function depends on never type fallback being `()` + //~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! } reuse to_reuse::opaque_arg; //~^ ERROR delegation with early bound generics is not supported yet trait ToReuse { fn opaque_ret() -> impl Trait { unimplemented!() } + //~^ warn: this function depends on never type fallback being `()` + //~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! } // FIXME: Inherited `impl Trait`s create query cycles when used inside trait impls. diff --git a/tests/ui/delegation/not-supported.stderr b/tests/ui/delegation/not-supported.stderr index e2cb04f977b..339a8418b33 100644 --- a/tests/ui/delegation/not-supported.stderr +++ b/tests/ui/delegation/not-supported.stderr @@ -107,7 +107,7 @@ LL | reuse Trait::foo2 { &self.0 } | ^^^^ error: delegation with early bound generics is not supported yet - --> $DIR/not-supported.rs:74:21 + --> $DIR/not-supported.rs:76:21 | LL | pub fn opaque_arg(_: impl Trait) -> i32 { 0 } | --------------------------------------- callee defined here @@ -115,46 +115,67 @@ LL | pub fn opaque_arg(_: impl Trait) -> i32 { 0 } LL | reuse to_reuse::opaque_arg; | ^^^^^^^^^^ -error[E0391]: cycle detected when computing type of `opaque::<impl at $DIR/not-supported.rs:82:5: 82:24>::{synthetic#0}` - --> $DIR/not-supported.rs:83:25 +warning: this function depends on never type fallback being `()` + --> $DIR/not-supported.rs:80:9 + | +LL | fn opaque_ret() -> impl Trait { unimplemented!() } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748> + = help: specify the types explicitly + = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default + +error[E0391]: cycle detected when computing type of `opaque::<impl at $DIR/not-supported.rs:86:5: 86:24>::{synthetic#0}` + --> $DIR/not-supported.rs:87:25 | LL | reuse to_reuse::opaque_ret; | ^^^^^^^^^^ | note: ...which requires comparing an impl and trait method signature, inferring any hidden `impl Trait` types in the process... - --> $DIR/not-supported.rs:83:25 + --> $DIR/not-supported.rs:87:25 | LL | reuse to_reuse::opaque_ret; | ^^^^^^^^^^ - = note: ...which again requires computing type of `opaque::<impl at $DIR/not-supported.rs:82:5: 82:24>::{synthetic#0}`, completing the cycle -note: cycle used when checking that `opaque::<impl at $DIR/not-supported.rs:82:5: 82:24>` is well-formed - --> $DIR/not-supported.rs:82:5 + = note: ...which again requires computing type of `opaque::<impl at $DIR/not-supported.rs:86:5: 86:24>::{synthetic#0}`, completing the cycle +note: cycle used when checking that `opaque::<impl at $DIR/not-supported.rs:86:5: 86:24>` is well-formed + --> $DIR/not-supported.rs:86:5 | LL | impl ToReuse for u8 { | ^^^^^^^^^^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error[E0391]: cycle detected when computing type of `opaque::<impl at $DIR/not-supported.rs:85:5: 85:25>::{synthetic#0}` - --> $DIR/not-supported.rs:86:24 +warning: this function depends on never type fallback being `()` + --> $DIR/not-supported.rs:72:9 + | +LL | pub fn opaque_ret() -> impl Trait { unimplemented!() } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748> + = help: specify the types explicitly + +error[E0391]: cycle detected when computing type of `opaque::<impl at $DIR/not-supported.rs:89:5: 89:25>::{synthetic#0}` + --> $DIR/not-supported.rs:90:24 | LL | reuse ToReuse::opaque_ret; | ^^^^^^^^^^ | note: ...which requires comparing an impl and trait method signature, inferring any hidden `impl Trait` types in the process... - --> $DIR/not-supported.rs:86:24 + --> $DIR/not-supported.rs:90:24 | LL | reuse ToReuse::opaque_ret; | ^^^^^^^^^^ - = note: ...which again requires computing type of `opaque::<impl at $DIR/not-supported.rs:85:5: 85:25>::{synthetic#0}`, completing the cycle -note: cycle used when checking that `opaque::<impl at $DIR/not-supported.rs:85:5: 85:25>` is well-formed - --> $DIR/not-supported.rs:85:5 + = note: ...which again requires computing type of `opaque::<impl at $DIR/not-supported.rs:89:5: 89:25>::{synthetic#0}`, completing the cycle +note: cycle used when checking that `opaque::<impl at $DIR/not-supported.rs:89:5: 89:25>` is well-formed + --> $DIR/not-supported.rs:89:5 | LL | impl ToReuse for u16 { | ^^^^^^^^^^^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error: recursive delegation is not supported yet - --> $DIR/not-supported.rs:99:22 + --> $DIR/not-supported.rs:103:22 | LL | pub reuse to_reuse2::foo; | --- callee defined here @@ -162,7 +183,7 @@ LL | pub reuse to_reuse2::foo; LL | reuse to_reuse1::foo; | ^^^ -error: aborting due to 16 previous errors +error: aborting due to 16 previous errors; 2 warnings emitted Some errors have detailed explanations: E0049, E0195, E0391. For more information about an error, try `rustc --explain E0049`. diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr index 568cb8931a1..d189d2dbded 100644 --- a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr +++ b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr @@ -25,7 +25,7 @@ LL | SelectInt.check("bar"); = help: the trait `AsExpression<Text>` is implemented for `&str` = help: for that trait implementation, expected `Text`, found `Integer` -error[E0271]: type mismatch resolving `<&str as AsExpression<<SelectInt as Expression>::SqlType>>::Expression == _` +error[E0271]: type mismatch resolving `<SelectInt as Expression>::SqlType == Text` --> $DIR/as_expression.rs:57:5 | LL | SelectInt.check("bar"); diff --git a/tests/ui/did_you_mean/issue-56028-there-is-an-enum-variant.stderr b/tests/ui/did_you_mean/issue-56028-there-is-an-enum-variant.stderr index 5c610f36322..b1a15b8594a 100644 --- a/tests/ui/did_you_mean/issue-56028-there-is-an-enum-variant.stderr +++ b/tests/ui/did_you_mean/issue-56028-there-is-an-enum-variant.stderr @@ -22,7 +22,7 @@ error[E0425]: cannot find value `Set` in this scope LL | fn setup() -> Set { Set } | ^^^ not found in this scope | -help: consider importing one of these items +help: consider importing one of these unit variants | LL + use AffixHeart::Set; | diff --git a/tests/ui/editions/never-type-fallback-breaking.e2021.stderr b/tests/ui/editions/never-type-fallback-breaking.e2021.stderr new file mode 100644 index 00000000000..92c233a0d9c --- /dev/null +++ b/tests/ui/editions/never-type-fallback-breaking.e2021.stderr @@ -0,0 +1,23 @@ +warning: this function depends on never type fallback being `()` + --> $DIR/never-type-fallback-breaking.rs:15:1 + | +LL | fn m() { + | ^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748> + = help: specify the types explicitly + = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default + +warning: this function depends on never type fallback being `()` + --> $DIR/never-type-fallback-breaking.rs:27:1 + | +LL | fn q() -> Option<()> { + | ^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748> + = help: specify the types explicitly + +warning: 2 warnings emitted + diff --git a/tests/ui/editions/never-type-fallback-breaking.e2024.stderr b/tests/ui/editions/never-type-fallback-breaking.e2024.stderr index e9a8882eb6c..461e4ae0bdf 100644 --- a/tests/ui/editions/never-type-fallback-breaking.e2024.stderr +++ b/tests/ui/editions/never-type-fallback-breaking.e2024.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `!: Default` is not satisfied - --> $DIR/never-type-fallback-breaking.rs:17:17 + --> $DIR/never-type-fallback-breaking.rs:19:17 | LL | true => Default::default(), | ^^^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `!` @@ -8,7 +8,7 @@ LL | true => Default::default(), = help: did you intend to use the type `()` here instead? error[E0277]: the trait bound `!: Default` is not satisfied - --> $DIR/never-type-fallback-breaking.rs:30:5 + --> $DIR/never-type-fallback-breaking.rs:34:5 | LL | deserialize()?; | ^^^^^^^^^^^^^ the trait `Default` is not implemented for `!` @@ -16,7 +16,7 @@ LL | deserialize()?; = note: this error might have been caused by changes to Rust's type-inference algorithm (see issue #48950 <https://github.com/rust-lang/rust/issues/48950> for more information) = help: did you intend to use the type `()` here instead? note: required by a bound in `deserialize` - --> $DIR/never-type-fallback-breaking.rs:26:23 + --> $DIR/never-type-fallback-breaking.rs:30:23 | LL | fn deserialize<T: Default>() -> Option<T> { | ^^^^^^^ required by this bound in `deserialize` diff --git a/tests/ui/editions/never-type-fallback-breaking.rs b/tests/ui/editions/never-type-fallback-breaking.rs index 7dfa4702807..7b4a1b1de04 100644 --- a/tests/ui/editions/never-type-fallback-breaking.rs +++ b/tests/ui/editions/never-type-fallback-breaking.rs @@ -13,6 +13,8 @@ fn main() { } fn m() { + //[e2021]~^ this function depends on never type fallback being `()` + //[e2021]~| this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! let x = match true { true => Default::default(), //[e2024]~^ error: the trait bound `!: Default` is not satisfied @@ -23,6 +25,8 @@ fn m() { } fn q() -> Option<()> { + //[e2021]~^ this function depends on never type fallback being `()` + //[e2021]~| this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! fn deserialize<T: Default>() -> Option<T> { Some(T::default()) } diff --git a/tests/ui/generic-associated-types/assume-gat-normalization-for-nested-goals.next.stderr b/tests/ui/generic-associated-types/assume-gat-normalization-for-nested-goals.next.stderr index 2c372b6c3a7..9b5d84b5b09 100644 --- a/tests/ui/generic-associated-types/assume-gat-normalization-for-nested-goals.next.stderr +++ b/tests/ui/generic-associated-types/assume-gat-normalization-for-nested-goals.next.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `i32: Baz<Self>` is not satisfied --> $DIR/assume-gat-normalization-for-nested-goals.rs:9:30 | LL | type Bar<T>: Baz<Self> = i32; - | ^^^ the trait `Eq<i32>` is not implemented for `i32`, which is required by `i32: Baz<Self>` + | ^^^ the trait `Eq<i32>` is not implemented for `<Self as Foo>::Bar<()>`, which is required by `i32: Baz<Self>` | note: required for `i32` to implement `Baz<Self>` --> $DIR/assume-gat-normalization-for-nested-goals.rs:16:23 diff --git a/tests/ui/generic-associated-types/static-lifetime-tip-with-default-type.rs b/tests/ui/generic-associated-types/static-lifetime-tip-with-default-type.rs new file mode 100644 index 00000000000..68879a0af29 --- /dev/null +++ b/tests/ui/generic-associated-types/static-lifetime-tip-with-default-type.rs @@ -0,0 +1,26 @@ +//@ error-pattern: r#T: 'static +//@ error-pattern: r#K: 'static +//@ error-pattern: T: 'static= + +// https://github.com/rust-lang/rust/issues/124785 + +struct Foo<T, K = i32>(&'static T, &'static K); +//~^ ERROR: the parameter type `T` may not live long enough +//~| ERROR: the parameter type `K` may not live long enough + +struct Bar<r#T, r#K = i32>(&'static r#T, &'static r#K); +//~^ ERROR: the parameter type `T` may not live long enough +//~| ERROR: the parameter type `K` may not live long enough + +struct Boo<T= i32>(&'static T); +//~^ ERROR: the parameter type `T` may not live long enough + +struct Far<T += i32>(&'static T); +//~^ ERROR: the parameter type `T` may not live long enough + +struct S<'a, K: 'a = i32>(&'static K); +//~^ ERROR: lifetime parameter `'a` is never used +//~| ERROR: the parameter type `K` may not live long enough + +fn main() {} diff --git a/tests/ui/generic-associated-types/static-lifetime-tip-with-default-type.stderr b/tests/ui/generic-associated-types/static-lifetime-tip-with-default-type.stderr new file mode 100644 index 00000000000..7d985a9013f --- /dev/null +++ b/tests/ui/generic-associated-types/static-lifetime-tip-with-default-type.stderr @@ -0,0 +1,110 @@ +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/static-lifetime-tip-with-default-type.rs:7:24 + | +LL | struct Foo<T, K = i32>(&'static T, &'static K); + | ^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the reference type `&'static T` does not outlive the data it points at + | +help: consider adding an explicit lifetime bound + | +LL | struct Foo<T: 'static, K = i32>(&'static T, &'static K); + | +++++++++ + +error[E0310]: the parameter type `K` may not live long enough + --> $DIR/static-lifetime-tip-with-default-type.rs:7:36 + | +LL | struct Foo<T, K = i32>(&'static T, &'static K); + | ^^^^^^^^^^ + | | + | the parameter type `K` must be valid for the static lifetime... + | ...so that the reference type `&'static K` does not outlive the data it points at + | +help: consider adding an explicit lifetime bound + | +LL | struct Foo<T, K: 'static = i32>(&'static T, &'static K); + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/static-lifetime-tip-with-default-type.rs:11:28 + | +LL | struct Bar<r#T, r#K = i32>(&'static r#T, &'static r#K); + | ^^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the reference type `&'static T` does not outlive the data it points at + | +help: consider adding an explicit lifetime bound + | +LL | struct Bar<r#T: 'static, r#K = i32>(&'static r#T, &'static r#K); + | +++++++++ + +error[E0310]: the parameter type `K` may not live long enough + --> $DIR/static-lifetime-tip-with-default-type.rs:11:42 + | +LL | struct Bar<r#T, r#K = i32>(&'static r#T, &'static r#K); + | ^^^^^^^^^^^^ + | | + | the parameter type `K` must be valid for the static lifetime... + | ...so that the reference type `&'static K` does not outlive the data it points at + | +help: consider adding an explicit lifetime bound + | +LL | struct Bar<r#T, r#K: 'static = i32>(&'static r#T, &'static r#K); + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/static-lifetime-tip-with-default-type.rs:15:20 + | +LL | struct Boo<T= i32>(&'static T); + | ^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the reference type `&'static T` does not outlive the data it points at + | +help: consider adding an explicit lifetime bound + | +LL | struct Boo<T: 'static= i32>(&'static T); + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/static-lifetime-tip-with-default-type.rs:19:8 + | +LL | = i32>(&'static T); + | ^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the reference type `&'static T` does not outlive the data it points at + | +help: consider adding an explicit lifetime bound + | +LL | struct Far<T: 'static + | +++++++++ + +error[E0310]: the parameter type `K` may not live long enough + --> $DIR/static-lifetime-tip-with-default-type.rs:22:27 + | +LL | struct S<'a, K: 'a = i32>(&'static K); + | ^^^^^^^^^^ + | | + | the parameter type `K` must be valid for the static lifetime... + | ...so that the reference type `&'static K` does not outlive the data it points at + | +help: consider adding an explicit lifetime bound + | +LL | struct S<'a, K: 'a + 'static = i32>(&'static K); + | +++++++++ + +error[E0392]: lifetime parameter `'a` is never used + --> $DIR/static-lifetime-tip-with-default-type.rs:22:10 + | +LL | struct S<'a, K: 'a = i32>(&'static K); + | ^^ unused lifetime parameter + | + = help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData` + +error: aborting due to 8 previous errors + +Some errors have detailed explanations: E0310, E0392. +For more information about an error, try `rustc --explain E0310`. diff --git a/tests/ui/higher-ranked/trait-bounds/rigid-equate-projections-in-higher-ranked-fn-signature.next.stderr b/tests/ui/higher-ranked/trait-bounds/rigid-equate-projections-in-higher-ranked-fn-signature.next.stderr index 14a3d5e178d..31d74d1c022 100644 --- a/tests/ui/higher-ranked/trait-bounds/rigid-equate-projections-in-higher-ranked-fn-signature.next.stderr +++ b/tests/ui/higher-ranked/trait-bounds/rigid-equate-projections-in-higher-ranked-fn-signature.next.stderr @@ -1,8 +1,8 @@ -error[E0284]: type annotations needed: cannot satisfy `for<'a> <_ as Trait<'a>>::Assoc <: <T as Trait<'_>>::Assoc` +error[E0284]: type annotations needed: cannot satisfy `for<'a> <_ as Trait<'a>>::Assoc normalizes-to <T as Trait<'_>>::Assoc` --> $DIR/rigid-equate-projections-in-higher-ranked-fn-signature.rs:27:50 | LL | let _: for<'a> fn(<_ as Trait<'a>>::Assoc) = foo::<T>(); - | ^^^^^^^^^^ cannot satisfy `for<'a> <_ as Trait<'a>>::Assoc <: <T as Trait<'_>>::Assoc` + | ^^^^^^^^^^ cannot satisfy `for<'a> <_ as Trait<'a>>::Assoc normalizes-to <T as Trait<'_>>::Assoc` error: aborting due to 1 previous error diff --git a/tests/ui/hygiene/extern-prelude-from-opaque-fail-2018.stderr b/tests/ui/hygiene/extern-prelude-from-opaque-fail-2018.stderr index 78e6376bca2..cc229764ad3 100644 --- a/tests/ui/hygiene/extern-prelude-from-opaque-fail-2018.stderr +++ b/tests/ui/hygiene/extern-prelude-from-opaque-fail-2018.stderr @@ -24,8 +24,7 @@ LL | fn f() { my_core::mem::drop(0); } LL | a!(); | ---- in this macro invocation | - = help: consider importing one of these items: - core::mem + = help: consider importing this module: std::mem = note: this error originates in the macro `a` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -35,9 +34,7 @@ error[E0433]: failed to resolve: use of undeclared crate or module `my_core` LL | fn f() { my_core::mem::drop(0); } | ^^^^^^^ use of undeclared crate or module `my_core` | -help: consider importing one of these items - | -LL + use core::mem; +help: consider importing this module | LL + use std::mem; | diff --git a/tests/ui/impl-trait/associated-type-undefine.rs b/tests/ui/impl-trait/associated-type-undefine.rs new file mode 100644 index 00000000000..c8f07021fbf --- /dev/null +++ b/tests/ui/impl-trait/associated-type-undefine.rs @@ -0,0 +1,28 @@ +#![feature(impl_trait_in_assoc_type)] + +trait Foo: Sized { + type Bar; + type Gat<T: Foo>; + fn foo(self) -> (<Self as Foo>::Gat<u32>, <Self as Foo>::Gat<Self>); +} + +impl Foo for u32 { + type Bar = (); + type Gat<T: Foo> = (); + fn foo(self) -> (<Self as Foo>::Gat<u32>, <Self as Foo>::Gat<Self>) { + ((), ()) + } +} + +impl Foo for () { + type Bar = impl Sized; + type Gat<T: Foo> = <T as Foo>::Bar; + // Because we encounter `Gat<u32>` first, we never walk into another `Gat` + // again, thus missing the opaque type that we could be defining. + fn foo(self) -> (<Self as Foo>::Gat<u32>, <Self as Foo>::Gat<Self>) { + ((), ()) + //~^ ERROR: mismatched types + } +} + +fn main() {} diff --git a/tests/ui/impl-trait/associated-type-undefine.stderr b/tests/ui/impl-trait/associated-type-undefine.stderr new file mode 100644 index 00000000000..5d9d525eb93 --- /dev/null +++ b/tests/ui/impl-trait/associated-type-undefine.stderr @@ -0,0 +1,20 @@ +error[E0308]: mismatched types + --> $DIR/associated-type-undefine.rs:23:14 + | +LL | type Bar = impl Sized; + | ---------- the expected opaque type +... +LL | ((), ()) + | ^^ expected opaque type, found `()` + | + = note: expected opaque type `<() as Foo>::Bar` + found unit type `()` +note: this item must have the opaque type in its signature in order to be able to register hidden types + --> $DIR/associated-type-undefine.rs:22:8 + | +LL | fn foo(self) -> (<Self as Foo>::Gat<u32>, <Self as Foo>::Gat<Self>) { + | ^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/impl-trait/nested_impl_trait.stderr b/tests/ui/impl-trait/nested_impl_trait.stderr index 1f9a2a5e9d6..a53312e5c0b 100644 --- a/tests/ui/impl-trait/nested_impl_trait.stderr +++ b/tests/ui/impl-trait/nested_impl_trait.stderr @@ -46,7 +46,7 @@ error[E0277]: the trait bound `impl Into<u32>: Into<impl Debug>` is not satisfie --> $DIR/nested_impl_trait.rs:6:46 | LL | fn bad_in_ret_position(x: impl Into<u32>) -> impl Into<impl Debug> { x } - | ^^^^^^^^^^^^^^^^^^^^^ the trait `From<impl Into<u32>>` is not implemented for `impl Into<u32>`, which is required by `impl Into<u32>: Into<impl Debug>` + | ^^^^^^^^^^^^^^^^^^^^^ the trait `From<impl Into<u32>>` is not implemented for `impl Debug`, which is required by `impl Into<u32>: Into<impl Debug>` | = help: the trait `Into<U>` is implemented for `T` = note: required for `impl Into<u32>` to implement `Into<impl Debug>` @@ -55,7 +55,7 @@ error[E0277]: the trait bound `impl Into<u32>: Into<impl Debug>` is not satisfie --> $DIR/nested_impl_trait.rs:19:34 | LL | fn bad(x: impl Into<u32>) -> impl Into<impl Debug> { x } - | ^^^^^^^^^^^^^^^^^^^^^ the trait `From<impl Into<u32>>` is not implemented for `impl Into<u32>`, which is required by `impl Into<u32>: Into<impl Debug>` + | ^^^^^^^^^^^^^^^^^^^^^ the trait `From<impl Into<u32>>` is not implemented for `impl Debug`, which is required by `impl Into<u32>: Into<impl Debug>` | = help: the trait `Into<U>` is implemented for `T` = note: required for `impl Into<u32>` to implement `Into<impl Debug>` diff --git a/tests/ui/imports/cycle-import-in-std-1.stderr b/tests/ui/imports/cycle-import-in-std-1.stderr index d4e6f32cc10..a7dfc6231ba 100644 --- a/tests/ui/imports/cycle-import-in-std-1.stderr +++ b/tests/ui/imports/cycle-import-in-std-1.stderr @@ -4,8 +4,7 @@ error[E0432]: unresolved import `ops` LL | use ops::{self as std}; | ^^^^^^^^^^^ no external crate `ops` | - = help: consider importing one of these items instead: - core::ops + = help: consider importing this module instead: std::ops error: aborting due to 1 previous error diff --git a/tests/ui/imports/cycle-import-in-std-2.stderr b/tests/ui/imports/cycle-import-in-std-2.stderr index dc0270dffe4..8d94693cd51 100644 --- a/tests/ui/imports/cycle-import-in-std-2.stderr +++ b/tests/ui/imports/cycle-import-in-std-2.stderr @@ -4,8 +4,7 @@ error[E0432]: unresolved import `ops` LL | use ops::{self as std}; | ^^^^^^^^^^^ no external crate `ops` | - = help: consider importing one of these items instead: - core::ops + = help: consider importing this module instead: std::ops error: aborting due to 1 previous error diff --git a/tests/ui/imports/import-alias-issue-121168.edition2018.stderr b/tests/ui/imports/import-alias-issue-121168.edition2018.stderr index b61a0e3edd5..e14e700c33d 100644 --- a/tests/ui/imports/import-alias-issue-121168.edition2018.stderr +++ b/tests/ui/imports/import-alias-issue-121168.edition2018.stderr @@ -4,7 +4,7 @@ error[E0412]: cannot find type `Foo` in this scope LL | let _: Foo<i32> = todo!(); | ^^^ not found in this scope | -help: consider importing one of these items +help: consider importing one of these structs | LL + use crate::nice_crate_name::Foo; | diff --git a/tests/ui/imports/import-alias-issue-121168.edition2021.stderr b/tests/ui/imports/import-alias-issue-121168.edition2021.stderr index b61a0e3edd5..e14e700c33d 100644 --- a/tests/ui/imports/import-alias-issue-121168.edition2021.stderr +++ b/tests/ui/imports/import-alias-issue-121168.edition2021.stderr @@ -4,7 +4,7 @@ error[E0412]: cannot find type `Foo` in this scope LL | let _: Foo<i32> = todo!(); | ^^^ not found in this scope | -help: consider importing one of these items +help: consider importing one of these structs | LL + use crate::nice_crate_name::Foo; | diff --git a/tests/ui/imports/issue-56125.stderr b/tests/ui/imports/issue-56125.stderr index d2a0f436c42..0c4a569c7ea 100644 --- a/tests/ui/imports/issue-56125.stderr +++ b/tests/ui/imports/issue-56125.stderr @@ -4,16 +4,16 @@ error[E0432]: unresolved import `empty::issue_56125` LL | use empty::issue_56125; | ^^^^^^^^^^^^^^^^^^ no `issue_56125` in `m3::empty` | -help: consider importing one of these items instead +help: consider importing one of these modules instead | +LL | use crate::m3::last_segment::issue_56125; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | use crate::m3::non_last_segment::non_last_segment::issue_56125; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LL | use ::issue_56125::issue_56125; | ~~~~~~~~~~~~~~~~~~~~~~~~~~ LL | use ::issue_56125::last_segment::issue_56125; | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -LL | use ::issue_56125::non_last_segment::non_last_segment::issue_56125; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -LL | use crate::m3::last_segment::issue_56125; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ and 1 other candidate error[E0659]: `issue_56125` is ambiguous diff --git a/tests/ui/kindck/kindck-send-object.stderr b/tests/ui/kindck/kindck-send-object.stderr index 9f1ff4f3644..7d0c711abc4 100644 --- a/tests/ui/kindck/kindck-send-object.stderr +++ b/tests/ui/kindck/kindck-send-object.stderr @@ -4,7 +4,7 @@ error[E0277]: `&'static (dyn Dummy + 'static)` cannot be sent between threads sa LL | assert_send::<&'static (dyn Dummy + 'static)>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `&'static (dyn Dummy + 'static)` cannot be sent between threads safely | - = help: the trait `Sync` is not implemented for `&'static (dyn Dummy + 'static)`, which is required by `&'static (dyn Dummy + 'static): Send` + = help: the trait `Sync` is not implemented for `(dyn Dummy + 'static)`, which is required by `&'static (dyn Dummy + 'static): Send` = note: required for `&'static (dyn Dummy + 'static)` to implement `Send` note: required by a bound in `assert_send` --> $DIR/kindck-send-object.rs:5:18 diff --git a/tests/ui/kindck/kindck-send-object1.stderr b/tests/ui/kindck/kindck-send-object1.stderr index f2aa814676f..7f39dab2086 100644 --- a/tests/ui/kindck/kindck-send-object1.stderr +++ b/tests/ui/kindck/kindck-send-object1.stderr @@ -4,7 +4,7 @@ error[E0277]: `&'a (dyn Dummy + 'a)` cannot be sent between threads safely LL | assert_send::<&'a dyn Dummy>(); | ^^^^^^^^^^^^^ `&'a (dyn Dummy + 'a)` cannot be sent between threads safely | - = help: the trait `Sync` is not implemented for `&'a (dyn Dummy + 'a)`, which is required by `&'a (dyn Dummy + 'a): Send` + = help: the trait `Sync` is not implemented for `(dyn Dummy + 'a)`, which is required by `&'a (dyn Dummy + 'a): Send` = note: required for `&'a (dyn Dummy + 'a)` to implement `Send` note: required by a bound in `assert_send` --> $DIR/kindck-send-object1.rs:5:18 diff --git a/tests/ui/kindck/kindck-send-object2.stderr b/tests/ui/kindck/kindck-send-object2.stderr index cd4d74360f8..a481a132cce 100644 --- a/tests/ui/kindck/kindck-send-object2.stderr +++ b/tests/ui/kindck/kindck-send-object2.stderr @@ -4,7 +4,7 @@ error[E0277]: `&'static (dyn Dummy + 'static)` cannot be sent between threads sa LL | assert_send::<&'static dyn Dummy>(); | ^^^^^^^^^^^^^^^^^^ `&'static (dyn Dummy + 'static)` cannot be sent between threads safely | - = help: the trait `Sync` is not implemented for `&'static (dyn Dummy + 'static)`, which is required by `&'static (dyn Dummy + 'static): Send` + = help: the trait `Sync` is not implemented for `(dyn Dummy + 'static)`, which is required by `&'static (dyn Dummy + 'static): Send` = note: required for `&'static (dyn Dummy + 'static)` to implement `Send` note: required by a bound in `assert_send` --> $DIR/kindck-send-object2.rs:3:18 diff --git a/tests/ui/lint/dead-code/allow-unconstructed-pub-struct.rs b/tests/ui/lint/dead-code/allow-unconstructed-pub-struct.rs new file mode 100644 index 00000000000..8cd1524045b --- /dev/null +++ b/tests/ui/lint/dead-code/allow-unconstructed-pub-struct.rs @@ -0,0 +1,33 @@ +//@ check-pass + +mod ffi { + use super::*; + + extern "C" { + pub fn DomPromise_AddRef(promise: *const Promise); + pub fn DomPromise_Release(promise: *const Promise); + } +} + +#[repr(C)] +#[allow(unused)] +pub struct Promise { + private: [u8; 0], + __nosync: ::std::marker::PhantomData<::std::rc::Rc<u8>>, +} + +pub unsafe trait RefCounted { + unsafe fn addref(&self); + unsafe fn release(&self); +} + +unsafe impl RefCounted for Promise { + unsafe fn addref(&self) { + ffi::DomPromise_AddRef(self) + } + unsafe fn release(&self) { + ffi::DomPromise_Release(self) + } +} + +fn main() {} diff --git a/tests/ui/lint/use_suggestion_json.stderr b/tests/ui/lint/use_suggestion_json.stderr index acc36550642..4683e5dd8f3 100644 --- a/tests/ui/lint/use_suggestion_json.stderr +++ b/tests/ui/lint/use_suggestion_json.stderr @@ -95,7 +95,7 @@ mod foo { ], "children": [ { - "message": "consider importing one of these items", + "message": "consider importing one of these structs", "code": null, "level": "help", "spans": [ @@ -386,7 +386,7 @@ mod foo { \u001b[0m\u001b[1m\u001b[38;5;12mLL\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m let x: Iter;\u001b[0m \u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mnot found in this scope\u001b[0m \u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m -\u001b[0m\u001b[1m\u001b[38;5;14mhelp\u001b[0m\u001b[0m: consider importing one of these items\u001b[0m +\u001b[0m\u001b[1m\u001b[38;5;14mhelp\u001b[0m\u001b[0m: consider importing one of these structs\u001b[0m \u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m \u001b[0m\u001b[1m\u001b[38;5;12mLL\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[38;5;10m+ use std::collections::binary_heap::Iter;\u001b[0m \u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m diff --git a/tests/ui/namespace/namespace-mix.stderr b/tests/ui/namespace/namespace-mix.stderr index 4eff08ead42..b80363fe8f8 100644 --- a/tests/ui/namespace/namespace-mix.stderr +++ b/tests/ui/namespace/namespace-mix.stderr @@ -12,7 +12,7 @@ help: a tuple struct with a similar name exists | LL | check(m1::TS); | ~~ -help: consider importing one of these items instead +help: consider importing one of these constants instead | LL + use m2::S; | @@ -40,7 +40,7 @@ help: a tuple struct with a similar name exists | LL | check(xm1::TS); | ~~ -help: consider importing one of these items instead +help: consider importing one of these constants instead | LL + use m2::S; | @@ -66,7 +66,7 @@ help: a tuple variant with a similar name exists | LL | check(m7::TV); | ~~ -help: consider importing one of these items instead +help: consider importing one of these constants instead | LL + use m8::V; | @@ -94,7 +94,7 @@ help: a tuple variant with a similar name exists | LL | check(xm7::TV); | ~~ -help: consider importing one of these items instead +help: consider importing one of these constants instead | LL + use m8::V; | diff --git a/tests/ui/never_type/defaulted-never-note.fallback.stderr b/tests/ui/never_type/defaulted-never-note.fallback.stderr index 92fa9068cfd..fe9a924f64a 100644 --- a/tests/ui/never_type/defaulted-never-note.fallback.stderr +++ b/tests/ui/never_type/defaulted-never-note.fallback.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `!: ImplementedForUnitButNotNever` is not satisfied - --> $DIR/defaulted-never-note.rs:30:9 + --> $DIR/defaulted-never-note.rs:32:9 | LL | foo(_x); | --- ^^ the trait `ImplementedForUnitButNotNever` is not implemented for `!` diff --git a/tests/ui/never_type/defaulted-never-note.nofallback.stderr b/tests/ui/never_type/defaulted-never-note.nofallback.stderr new file mode 100644 index 00000000000..b69b8dda8f1 --- /dev/null +++ b/tests/ui/never_type/defaulted-never-note.nofallback.stderr @@ -0,0 +1,13 @@ +warning: this function depends on never type fallback being `()` + --> $DIR/defaulted-never-note.rs:28:1 + | +LL | fn smeg() { + | ^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748> + = help: specify the types explicitly + = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/never_type/defaulted-never-note.rs b/tests/ui/never_type/defaulted-never-note.rs index f4e5273b33a..40861e73b39 100644 --- a/tests/ui/never_type/defaulted-never-note.rs +++ b/tests/ui/never_type/defaulted-never-note.rs @@ -26,6 +26,8 @@ fn foo<T: ImplementedForUnitButNotNever>(_t: T) {} //[fallback]~^ NOTE required by this bound in `foo` //[fallback]~| NOTE required by a bound in `foo` fn smeg() { + //[nofallback]~^ warn: this function depends on never type fallback being `()` + //[nofallback]~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! let _x = return; foo(_x); //[fallback]~^ ERROR the trait bound diff --git a/tests/ui/never_type/dependency-on-fallback-to-unit.rs b/tests/ui/never_type/dependency-on-fallback-to-unit.rs new file mode 100644 index 00000000000..5448d0be2c6 --- /dev/null +++ b/tests/ui/never_type/dependency-on-fallback-to-unit.rs @@ -0,0 +1,28 @@ +//@ check-pass + +fn main() { + def(); + _ = question_mark(); +} + +fn def() { + //~^ warn: this function depends on never type fallback being `()` + //~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + match true { + false => <_>::default(), + true => return, + }; +} + +// <https://github.com/rust-lang/rust/issues/51125> +// <https://github.com/rust-lang/rust/issues/39216> +fn question_mark() -> Result<(), ()> { + //~^ warn: this function depends on never type fallback being `()` + //~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + deserialize()?; + Ok(()) +} + +fn deserialize<T: Default>() -> Result<T, ()> { + Ok(T::default()) +} diff --git a/tests/ui/never_type/dependency-on-fallback-to-unit.stderr b/tests/ui/never_type/dependency-on-fallback-to-unit.stderr new file mode 100644 index 00000000000..36c82b6d1bf --- /dev/null +++ b/tests/ui/never_type/dependency-on-fallback-to-unit.stderr @@ -0,0 +1,23 @@ +warning: this function depends on never type fallback being `()` + --> $DIR/dependency-on-fallback-to-unit.rs:8:1 + | +LL | fn def() { + | ^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748> + = help: specify the types explicitly + = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default + +warning: this function depends on never type fallback being `()` + --> $DIR/dependency-on-fallback-to-unit.rs:19:1 + | +LL | fn question_mark() -> Result<(), ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748> + = help: specify the types explicitly + +warning: 2 warnings emitted + diff --git a/tests/ui/never_type/diverging-fallback-control-flow.nofallback.stderr b/tests/ui/never_type/diverging-fallback-control-flow.nofallback.stderr new file mode 100644 index 00000000000..5fbdc04ed3b --- /dev/null +++ b/tests/ui/never_type/diverging-fallback-control-flow.nofallback.stderr @@ -0,0 +1,23 @@ +warning: this function depends on never type fallback being `()` + --> $DIR/diverging-fallback-control-flow.rs:30:1 + | +LL | fn assignment() { + | ^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748> + = help: specify the types explicitly + = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default + +warning: this function depends on never type fallback being `()` + --> $DIR/diverging-fallback-control-flow.rs:42:1 + | +LL | fn assignment_rev() { + | ^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748> + = help: specify the types explicitly + +warning: 2 warnings emitted + diff --git a/tests/ui/never_type/diverging-fallback-control-flow.rs b/tests/ui/never_type/diverging-fallback-control-flow.rs index e209a990885..575e2e9273c 100644 --- a/tests/ui/never_type/diverging-fallback-control-flow.rs +++ b/tests/ui/never_type/diverging-fallback-control-flow.rs @@ -28,6 +28,8 @@ impl UnitDefault for () { } fn assignment() { + //[nofallback]~^ warn: this function depends on never type fallback being `()` + //[nofallback]~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! let x; if true { @@ -38,6 +40,8 @@ fn assignment() { } fn assignment_rev() { + //[nofallback]~^ warn: this function depends on never type fallback being `()` + //[nofallback]~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! let x; if true { diff --git a/tests/ui/never_type/diverging-fallback-no-leak.fallback.stderr b/tests/ui/never_type/diverging-fallback-no-leak.fallback.stderr index 01abf2e17f1..c5463814475 100644 --- a/tests/ui/never_type/diverging-fallback-no-leak.fallback.stderr +++ b/tests/ui/never_type/diverging-fallback-no-leak.fallback.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `!: Test` is not satisfied - --> $DIR/diverging-fallback-no-leak.rs:17:23 + --> $DIR/diverging-fallback-no-leak.rs:20:23 | LL | unconstrained_arg(return); | ----------------- ^^^^^^ the trait `Test` is not implemented for `!` diff --git a/tests/ui/never_type/diverging-fallback-no-leak.nofallback.stderr b/tests/ui/never_type/diverging-fallback-no-leak.nofallback.stderr new file mode 100644 index 00000000000..d11097323b3 --- /dev/null +++ b/tests/ui/never_type/diverging-fallback-no-leak.nofallback.stderr @@ -0,0 +1,13 @@ +warning: this function depends on never type fallback being `()` + --> $DIR/diverging-fallback-no-leak.rs:14:1 + | +LL | fn main() { + | ^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748> + = help: specify the types explicitly + = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/never_type/diverging-fallback-no-leak.rs b/tests/ui/never_type/diverging-fallback-no-leak.rs index 425437da207..c6d59c7f273 100644 --- a/tests/ui/never_type/diverging-fallback-no-leak.rs +++ b/tests/ui/never_type/diverging-fallback-no-leak.rs @@ -12,6 +12,9 @@ impl Test for () {} fn unconstrained_arg<T: Test>(_: T) {} fn main() { + //[nofallback]~^ warn: this function depends on never type fallback being `()` + //[nofallback]~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + // Here the type variable falls back to `!`, // and hence we get a type error. unconstrained_arg(return); diff --git a/tests/ui/never_type/diverging-fallback-unconstrained-return.nofallback.stderr b/tests/ui/never_type/diverging-fallback-unconstrained-return.nofallback.stderr new file mode 100644 index 00000000000..750bcfb7f89 --- /dev/null +++ b/tests/ui/never_type/diverging-fallback-unconstrained-return.nofallback.stderr @@ -0,0 +1,13 @@ +warning: this function depends on never type fallback being `()` + --> $DIR/diverging-fallback-unconstrained-return.rs:28:1 + | +LL | fn main() { + | ^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748> + = help: specify the types explicitly + = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/never_type/diverging-fallback-unconstrained-return.rs b/tests/ui/never_type/diverging-fallback-unconstrained-return.rs index aeb6ee6e26e..927991db513 100644 --- a/tests/ui/never_type/diverging-fallback-unconstrained-return.rs +++ b/tests/ui/never_type/diverging-fallback-unconstrained-return.rs @@ -26,6 +26,9 @@ fn unconstrained_return<T: UnitReturn>() -> T { } fn main() { + //[nofallback]~^ warn: this function depends on never type fallback being `()` + //[nofallback]~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + // In Ye Olde Days, the `T` parameter of `unconstrained_return` // winds up "entangled" with the `!` type that results from // `panic!`, and hence falls back to `()`. This is kind of unfortunate diff --git a/tests/ui/never_type/fallback-closure-ret.nofallback.stderr b/tests/ui/never_type/fallback-closure-ret.nofallback.stderr new file mode 100644 index 00000000000..9f0b9f6daea --- /dev/null +++ b/tests/ui/never_type/fallback-closure-ret.nofallback.stderr @@ -0,0 +1,13 @@ +warning: this function depends on never type fallback being `()` + --> $DIR/fallback-closure-ret.rs:21:1 + | +LL | fn main() { + | ^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748> + = help: specify the types explicitly + = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/never_type/fallback-closure-ret.rs b/tests/ui/never_type/fallback-closure-ret.rs index dcf38e03a13..30f9ac54d0b 100644 --- a/tests/ui/never_type/fallback-closure-ret.rs +++ b/tests/ui/never_type/fallback-closure-ret.rs @@ -12,12 +12,14 @@ #![cfg_attr(fallback, feature(never_type_fallback))] -trait Bar { } -impl Bar for () { } -impl Bar for u32 { } +trait Bar {} +impl Bar for () {} +impl Bar for u32 {} fn foo<R: Bar>(_: impl Fn() -> R) {} fn main() { + //[nofallback]~^ warn: this function depends on never type fallback being `()` + //[nofallback]~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! foo(|| panic!()); } diff --git a/tests/ui/never_type/impl_trait_fallback.rs b/tests/ui/never_type/impl_trait_fallback.rs index ce06f8f7817..fbe13dbe2ac 100644 --- a/tests/ui/never_type/impl_trait_fallback.rs +++ b/tests/ui/never_type/impl_trait_fallback.rs @@ -6,5 +6,7 @@ trait T {} impl T for () {} fn should_ret_unit() -> impl T { + //~^ warn: this function depends on never type fallback being `()` + //~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! panic!() } diff --git a/tests/ui/never_type/impl_trait_fallback.stderr b/tests/ui/never_type/impl_trait_fallback.stderr new file mode 100644 index 00000000000..87638940332 --- /dev/null +++ b/tests/ui/never_type/impl_trait_fallback.stderr @@ -0,0 +1,13 @@ +warning: this function depends on never type fallback being `()` + --> $DIR/impl_trait_fallback.rs:8:1 + | +LL | fn should_ret_unit() -> impl T { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748> + = help: specify the types explicitly + = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/resolve/issue-16058.stderr b/tests/ui/resolve/issue-16058.stderr index 914990c35ff..12b1ac8a5fb 100644 --- a/tests/ui/resolve/issue-16058.stderr +++ b/tests/ui/resolve/issue-16058.stderr @@ -4,7 +4,7 @@ error[E0574]: expected struct, variant or union type, found enum `Result` LL | Result { | ^^^^^^ not a struct, variant or union type | -help: consider importing one of these items instead +help: consider importing one of these type aliases instead | LL + use std::fmt::Result; | diff --git a/tests/ui/resolve/issue-21221-1.stderr b/tests/ui/resolve/issue-21221-1.stderr index a38116cd728..ccf03afaa19 100644 --- a/tests/ui/resolve/issue-21221-1.stderr +++ b/tests/ui/resolve/issue-21221-1.stderr @@ -4,14 +4,14 @@ error[E0405]: cannot find trait `Mul` in this scope LL | impl Mul for Foo { | ^^^ not found in this scope | -help: consider importing one of these items +help: consider importing one of these traits + | +LL + use std::ops::Mul; | LL + use mul1::Mul; | LL + use mul2::Mul; | -LL + use std::ops::Mul; - | error[E0412]: cannot find type `Mul` in this scope --> $DIR/issue-21221-1.rs:58:16 @@ -19,14 +19,14 @@ error[E0412]: cannot find type `Mul` in this scope LL | fn getMul() -> Mul { | ^^^ not found in this scope | -help: consider importing one of these items +help: consider importing one of these traits + | +LL + use std::ops::Mul; | LL + use mul1::Mul; | LL + use mul2::Mul; | -LL + use std::ops::Mul; - | error[E0405]: cannot find trait `ThisTraitReallyDoesntExistInAnyModuleReally` in this scope --> $DIR/issue-21221-1.rs:63:6 diff --git a/tests/ui/resolve/issue-21221-2.stderr b/tests/ui/resolve/issue-21221-2.stderr index 3bd4c1a5d13..5db327955eb 100644 --- a/tests/ui/resolve/issue-21221-2.stderr +++ b/tests/ui/resolve/issue-21221-2.stderr @@ -4,7 +4,7 @@ error[E0405]: cannot find trait `T` in this scope LL | impl T for Foo { } | ^ not found in this scope | -help: consider importing one of these items +help: consider importing one of these traits | LL + use baz::T; | diff --git a/tests/ui/resolve/issue-50599.stderr b/tests/ui/resolve/issue-50599.stderr index e5eacd741fb..24fb3d580b8 100644 --- a/tests/ui/resolve/issue-50599.stderr +++ b/tests/ui/resolve/issue-50599.stderr @@ -4,7 +4,7 @@ error[E0425]: cannot find value `LOG10_2` in module `std::f64` LL | const M: usize = (f64::from(N) * std::f64::LOG10_2) as usize; | ^^^^^^^ not found in `std::f64` | -help: consider importing one of these items +help: consider importing one of these constants | LL + use std::f128::consts::LOG10_2; | diff --git a/tests/ui/resolve/issue-73427.stderr b/tests/ui/resolve/issue-73427.stderr index c5e245d884b..0a9a504f79c 100644 --- a/tests/ui/resolve/issue-73427.stderr +++ b/tests/ui/resolve/issue-73427.stderr @@ -105,7 +105,7 @@ help: the following enum variant is available | LL | (E::TupleWithFields(/* fields */)).foo(); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -help: consider importing one of these items instead +help: consider importing one of these constants instead | LL + use std::f128::consts::E; | diff --git a/tests/ui/resolve/privacy-enum-ctor.stderr b/tests/ui/resolve/privacy-enum-ctor.stderr index 01c8a38d2a4..ee3aecddcc3 100644 --- a/tests/ui/resolve/privacy-enum-ctor.stderr +++ b/tests/ui/resolve/privacy-enum-ctor.stderr @@ -82,7 +82,7 @@ help: a function with a similar name exists | LL | let _: E = m::f; | ~ -help: consider importing one of these items instead +help: consider importing one of these constants instead | LL + use std::f128::consts::E; | @@ -123,7 +123,7 @@ help: alternatively, the following enum variant is available | LL | let _: E = (E::Fn(/* fields */)); | ~~~~~~~~~~~~~~~~~~~~~ -help: consider importing one of these items instead +help: consider importing one of these constants instead | LL + use std::f128::consts::E; | diff --git a/tests/ui/rfcs/rfc-2126-extern-absolute-paths/not-allowed.stderr b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/not-allowed.stderr index 7f989c15f1c..d0c084f7bd5 100644 --- a/tests/ui/rfcs/rfc-2126-extern-absolute-paths/not-allowed.stderr +++ b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/not-allowed.stderr @@ -4,10 +4,8 @@ error[E0432]: unresolved import `alloc` LL | use alloc; | ^^^^^ no external crate `alloc` | -help: consider importing one of these items instead +help: consider importing this module instead | -LL | use core::alloc; - | ~~~~~~~~~~~ LL | use std::alloc; | ~~~~~~~~~~ diff --git a/tests/ui/rust-2018/issue-52202-use-suggestions.stderr b/tests/ui/rust-2018/issue-52202-use-suggestions.stderr index 49736205f20..ee1a336ea98 100644 --- a/tests/ui/rust-2018/issue-52202-use-suggestions.stderr +++ b/tests/ui/rust-2018/issue-52202-use-suggestions.stderr @@ -4,7 +4,7 @@ error[E0422]: cannot find struct, variant or union type `Drain` in this scope LL | let _d = Drain {}; | ^^^^^ not found in this scope | -help: consider importing one of these items +help: consider importing one of these structs | LL + use crate::plumbing::Drain; | diff --git a/tests/ui/suggestions/core-std-import-order-issue-83564.no_std.fixed b/tests/ui/suggestions/core-std-import-order-issue-83564.no_std.fixed new file mode 100644 index 00000000000..02d667d9844 --- /dev/null +++ b/tests/ui/suggestions/core-std-import-order-issue-83564.no_std.fixed @@ -0,0 +1,22 @@ +//@ edition:2018 +// +// This is a regression test for #83564. +// For some reason, Rust 2018 or higher is required to reproduce the bug. +//@ run-rustfix +//@ revisions: no_std std +//@ [no_std]compile-flags: --cfg=no_std -C panic=abort +#![cfg_attr(no_std, no_std)] + +use core::num::NonZero; + +fn main() { + //~^ HELP consider importing this struct + let _x = NonZero::new(5u32).unwrap(); + //~^ ERROR failed to resolve: use of undeclared type `NonZero` +} + +#[allow(dead_code)] +#[cfg_attr(no_std, panic_handler)] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} diff --git a/tests/ui/suggestions/core-std-import-order-issue-83564.stderr b/tests/ui/suggestions/core-std-import-order-issue-83564.no_std.stderr index 8665cc6d87c..d73f613bf9c 100644 --- a/tests/ui/suggestions/core-std-import-order-issue-83564.stderr +++ b/tests/ui/suggestions/core-std-import-order-issue-83564.no_std.stderr @@ -1,15 +1,13 @@ error[E0433]: failed to resolve: use of undeclared type `NonZero` - --> $DIR/core-std-import-order-issue-83564.rs:8:14 + --> $DIR/core-std-import-order-issue-83564.rs:12:14 | LL | let _x = NonZero::new(5u32).unwrap(); | ^^^^^^^ use of undeclared type `NonZero` | -help: consider importing one of these items +help: consider importing this struct | LL + use core::num::NonZero; | -LL + use std::num::NonZero; - | error: aborting due to 1 previous error diff --git a/tests/ui/suggestions/core-std-import-order-issue-83564.rs b/tests/ui/suggestions/core-std-import-order-issue-83564.rs index 6f2bdd7a38a..5bb5bfe176b 100644 --- a/tests/ui/suggestions/core-std-import-order-issue-83564.rs +++ b/tests/ui/suggestions/core-std-import-order-issue-83564.rs @@ -2,9 +2,19 @@ // // This is a regression test for #83564. // For some reason, Rust 2018 or higher is required to reproduce the bug. +//@ run-rustfix +//@ revisions: no_std std +//@ [no_std]compile-flags: --cfg=no_std -C panic=abort +#![cfg_attr(no_std, no_std)] fn main() { - //~^ HELP consider importing one of these items + //~^ HELP consider importing this struct let _x = NonZero::new(5u32).unwrap(); //~^ ERROR failed to resolve: use of undeclared type `NonZero` } + +#[allow(dead_code)] +#[cfg_attr(no_std, panic_handler)] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} diff --git a/tests/ui/suggestions/core-std-import-order-issue-83564.std.fixed b/tests/ui/suggestions/core-std-import-order-issue-83564.std.fixed new file mode 100644 index 00000000000..3492b42c685 --- /dev/null +++ b/tests/ui/suggestions/core-std-import-order-issue-83564.std.fixed @@ -0,0 +1,22 @@ +//@ edition:2018 +// +// This is a regression test for #83564. +// For some reason, Rust 2018 or higher is required to reproduce the bug. +//@ run-rustfix +//@ revisions: no_std std +//@ [no_std]compile-flags: --cfg=no_std -C panic=abort +#![cfg_attr(no_std, no_std)] + +use std::num::NonZero; + +fn main() { + //~^ HELP consider importing this struct + let _x = NonZero::new(5u32).unwrap(); + //~^ ERROR failed to resolve: use of undeclared type `NonZero` +} + +#[allow(dead_code)] +#[cfg_attr(no_std, panic_handler)] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} diff --git a/tests/ui/suggestions/core-std-import-order-issue-83564.std.stderr b/tests/ui/suggestions/core-std-import-order-issue-83564.std.stderr new file mode 100644 index 00000000000..ebfe197b45d --- /dev/null +++ b/tests/ui/suggestions/core-std-import-order-issue-83564.std.stderr @@ -0,0 +1,14 @@ +error[E0433]: failed to resolve: use of undeclared type `NonZero` + --> $DIR/core-std-import-order-issue-83564.rs:12:14 + | +LL | let _x = NonZero::new(5u32).unwrap(); + | ^^^^^^^ use of undeclared type `NonZero` + | +help: consider importing this struct + | +LL + use std::num::NonZero; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0433`. diff --git a/tests/ui/suggestions/crate-or-module-typo.stderr b/tests/ui/suggestions/crate-or-module-typo.stderr index 457d7790646..084d0408a8e 100644 --- a/tests/ui/suggestions/crate-or-module-typo.stderr +++ b/tests/ui/suggestions/crate-or-module-typo.stderr @@ -30,9 +30,7 @@ help: there is a crate or module with a similar name | LL | bar: std::cell::Cell<bool> | ~~~ -help: consider importing one of these items - | -LL + use core::cell; +help: consider importing this module | LL + use std::cell; | diff --git a/tests/ui/suggestions/suggest-remove-refs-5.stderr b/tests/ui/suggestions/suggest-remove-refs-5.stderr index 3b6994b45d1..b132c56473e 100644 --- a/tests/ui/suggestions/suggest-remove-refs-5.stderr +++ b/tests/ui/suggestions/suggest-remove-refs-5.stderr @@ -4,7 +4,7 @@ error[E0277]: `&mut &mut &mut &mut Vec<i32>` is not an iterator LL | for _ in &mut &mut v {} | ^^^^^^^^^^^ `&mut &mut &mut &mut Vec<i32>` is not an iterator | - = help: the trait `Iterator` is not implemented for `&mut &mut &mut &mut Vec<i32>`, which is required by `&mut &mut &mut &mut Vec<i32>: IntoIterator` + = help: the trait `Iterator` is not implemented for `Vec<i32>`, which is required by `&mut &mut &mut &mut Vec<i32>: IntoIterator` = note: required for `&mut Vec<i32>` to implement `Iterator` = note: 3 redundant requirements hidden = note: required for `&mut &mut &mut &mut Vec<i32>` to implement `Iterator` @@ -21,7 +21,7 @@ error[E0277]: `&mut &mut &mut [u8; 1]` is not an iterator LL | for _ in &mut v {} | ^^^^^^ `&mut &mut &mut [u8; 1]` is not an iterator | - = help: the trait `Iterator` is not implemented for `&mut &mut &mut [u8; 1]`, which is required by `&mut &mut &mut [u8; 1]: IntoIterator` + = help: the trait `Iterator` is not implemented for `[u8; 1]`, which is required by `&mut &mut &mut [u8; 1]: IntoIterator` = note: required for `&mut [u8; 1]` to implement `Iterator` = note: 2 redundant requirements hidden = note: required for `&mut &mut &mut [u8; 1]` to implement `Iterator` diff --git a/tests/ui/suggestions/suggest-tryinto-edition-change.rs b/tests/ui/suggestions/suggest-tryinto-edition-change.rs index 37321774874..f45670ae7c1 100644 --- a/tests/ui/suggestions/suggest-tryinto-edition-change.rs +++ b/tests/ui/suggestions/suggest-tryinto-edition-change.rs @@ -1,5 +1,5 @@ // Make sure that trying to access `TryInto`, `TryFrom`, `FromIterator` in pre-2021 mentions -// Edition 2021 change +// Edition 2021 change. //@ edition:2018 fn test() { @@ -11,19 +11,16 @@ fn test() { //~^ ERROR failed to resolve: use of undeclared type //~| NOTE use of undeclared type //~| NOTE 'std::convert::TryFrom' is included in the prelude starting in Edition 2021 - //~| NOTE 'core::convert::TryFrom' is included in the prelude starting in Edition 2021 let _i: i16 = TryInto::try_into(0_i32).unwrap(); //~^ ERROR failed to resolve: use of undeclared type //~| NOTE use of undeclared type //~| NOTE 'std::convert::TryInto' is included in the prelude starting in Edition 2021 - //~| NOTE 'core::convert::TryInto' is included in the prelude starting in Edition 2021 let _v: Vec<_> = FromIterator::from_iter(&[1]); //~^ ERROR failed to resolve: use of undeclared type //~| NOTE use of undeclared type //~| NOTE 'std::iter::FromIterator' is included in the prelude starting in Edition 2021 - //~| NOTE 'core::iter::FromIterator' is included in the prelude starting in Edition 2021 } fn main() { diff --git a/tests/ui/suggestions/suggest-tryinto-edition-change.stderr b/tests/ui/suggestions/suggest-tryinto-edition-change.stderr index db7c40101cd..5be55f75cd1 100644 --- a/tests/ui/suggestions/suggest-tryinto-edition-change.stderr +++ b/tests/ui/suggestions/suggest-tryinto-edition-change.stderr @@ -4,45 +4,36 @@ error[E0433]: failed to resolve: use of undeclared type `TryFrom` LL | let _i: i16 = TryFrom::try_from(0_i32).unwrap(); | ^^^^^^^ use of undeclared type `TryFrom` | - = note: 'core::convert::TryFrom' is included in the prelude starting in Edition 2021 = note: 'std::convert::TryFrom' is included in the prelude starting in Edition 2021 -help: consider importing one of these items - | -LL + use core::convert::TryFrom; +help: consider importing this trait | LL + use std::convert::TryFrom; | error[E0433]: failed to resolve: use of undeclared type `TryInto` - --> $DIR/suggest-tryinto-edition-change.rs:16:19 + --> $DIR/suggest-tryinto-edition-change.rs:15:19 | LL | let _i: i16 = TryInto::try_into(0_i32).unwrap(); | ^^^^^^^ use of undeclared type `TryInto` | - = note: 'core::convert::TryInto' is included in the prelude starting in Edition 2021 = note: 'std::convert::TryInto' is included in the prelude starting in Edition 2021 -help: consider importing one of these items - | -LL + use core::convert::TryInto; +help: consider importing this trait | LL + use std::convert::TryInto; | error[E0433]: failed to resolve: use of undeclared type `FromIterator` - --> $DIR/suggest-tryinto-edition-change.rs:22:22 + --> $DIR/suggest-tryinto-edition-change.rs:20:22 | LL | let _v: Vec<_> = FromIterator::from_iter(&[1]); | ^^^^^^^^^^^^ use of undeclared type `FromIterator` | - = note: 'core::iter::FromIterator' is included in the prelude starting in Edition 2021 = note: 'std::iter::FromIterator' is included in the prelude starting in Edition 2021 help: a trait with a similar name exists | LL | let _v: Vec<_> = IntoIterator::from_iter(&[1]); | ~~~~~~~~~~~~ -help: consider importing one of these items - | -LL + use core::iter::FromIterator; +help: consider importing this trait | LL + use std::iter::FromIterator; | diff --git a/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.rs b/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.rs index 2607f047024..35757f2339d 100644 --- a/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.rs +++ b/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.rs @@ -18,3 +18,4 @@ fn weird1() -> impl !Sized + Sized {} //~^ ERROR type mismatch resolving `impl !Sized + Sized == ()` fn weird2() -> impl !Sized {} //~^ ERROR type mismatch resolving `impl !Sized == ()` +//~| ERROR the size for values of type `impl !Sized` cannot be known at compilation time diff --git a/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.stderr b/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.stderr index ceaf42431fe..3dd2b27b55b 100644 --- a/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.stderr +++ b/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.stderr @@ -16,6 +16,15 @@ error[E0271]: type mismatch resolving `impl !Sized == ()` LL | fn weird2() -> impl !Sized {} | ^^^^^^^^^^^ types differ +error[E0277]: the size for values of type `impl !Sized` cannot be known at compilation time + --> $DIR/opaque-type-unsatisfied-bound.rs:19:16 + | +LL | fn weird2() -> impl !Sized {} + | ^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `impl !Sized` + = note: the return type of a function must have a statically known size + error[E0277]: the trait bound `impl !Trait: Trait` is not satisfied --> $DIR/opaque-type-unsatisfied-bound.rs:12:13 | @@ -30,7 +39,7 @@ note: required by a bound in `consume` LL | fn consume(_: impl Trait) {} | ^^^^^ required by this bound in `consume` -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors Some errors have detailed explanations: E0271, E0277. For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/traits/next-solver/auto-with-drop_tracking_mir.fail.stderr b/tests/ui/traits/next-solver/auto-with-drop_tracking_mir.fail.stderr index 562d7ccf9fe..e0b23bd8110 100644 --- a/tests/ui/traits/next-solver/auto-with-drop_tracking_mir.fail.stderr +++ b/tests/ui/traits/next-solver/auto-with-drop_tracking_mir.fail.stderr @@ -4,7 +4,7 @@ error: future cannot be sent between threads safely LL | is_send(foo()); | ^^^^^ future returned by `foo` is not `Send` | - = help: the trait `Sync` is not implemented for `impl Future<Output = ()>`, which is required by `impl Future<Output = ()>: Send` + = help: the trait `Sync` is not implemented for `NotSync`, which is required by `impl Future<Output = ()>: Send` note: future is not `Send` as this value is used across an await --> $DIR/auto-with-drop_tracking_mir.rs:16:11 | diff --git a/tests/ui/traits/next-solver/canonical/const-region-infer-to-static-in-binder.stderr b/tests/ui/traits/next-solver/canonical/const-region-infer-to-static-in-binder.stderr index 9dde1963bd4..e0cbee88aa1 100644 --- a/tests/ui/traits/next-solver/canonical/const-region-infer-to-static-in-binder.stderr +++ b/tests/ui/traits/next-solver/canonical/const-region-infer-to-static-in-binder.stderr @@ -1,8 +1,8 @@ -error[E0284]: type annotations needed: cannot satisfy `{ || {} } == _` +error[E0284]: type annotations needed: cannot normalize `X::{constant#0}` --> $DIR/const-region-infer-to-static-in-binder.rs:4:10 | LL | struct X<const FN: fn() = { || {} }>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `{ || {} } == _` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot normalize `X::{constant#0}` error: using function pointers as const generic parameters is forbidden --> $DIR/const-region-infer-to-static-in-binder.rs:4:20 diff --git a/tests/ui/traits/next-solver/diagnostics/point-at-failing-nested.stderr b/tests/ui/traits/next-solver/diagnostics/point-at-failing-nested.stderr index 6bf4e3cb534..9a18a58debd 100644 --- a/tests/ui/traits/next-solver/diagnostics/point-at-failing-nested.stderr +++ b/tests/ui/traits/next-solver/diagnostics/point-at-failing-nested.stderr @@ -4,6 +4,11 @@ error[E0277]: the trait bound `(): Foo` is not satisfied LL | needs_foo::<()>(); | ^^ the trait `Bar` is not implemented for `()`, which is required by `(): Foo` | +help: this trait has no implementations, consider adding one + --> $DIR/point-at-failing-nested.rs:4:1 + | +LL | trait Bar {} + | ^^^^^^^^^ note: required for `()` to implement `Foo` --> $DIR/point-at-failing-nested.rs:9:12 | diff --git a/tests/ui/traits/next-solver/diagnostics/projection-trait-ref.rs b/tests/ui/traits/next-solver/diagnostics/projection-trait-ref.rs new file mode 100644 index 00000000000..a3ab7bf03e5 --- /dev/null +++ b/tests/ui/traits/next-solver/diagnostics/projection-trait-ref.rs @@ -0,0 +1,17 @@ +//@ compile-flags: -Znext-solver + +trait Trait { + type Assoc; +} + +fn test_poly<T>() { + let x: <T as Trait>::Assoc = (); + //~^ ERROR the trait bound `T: Trait` is not satisfied +} + +fn test() { + let x: <i32 as Trait>::Assoc = (); + //~^ ERROR the trait bound `i32: Trait` is not satisfied +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/diagnostics/projection-trait-ref.stderr b/tests/ui/traits/next-solver/diagnostics/projection-trait-ref.stderr new file mode 100644 index 00000000000..cd8d8b3ffcd --- /dev/null +++ b/tests/ui/traits/next-solver/diagnostics/projection-trait-ref.stderr @@ -0,0 +1,26 @@ +error[E0277]: the trait bound `T: Trait` is not satisfied + --> $DIR/projection-trait-ref.rs:8:12 + | +LL | let x: <T as Trait>::Assoc = (); + | ^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `T` + | +help: consider restricting type parameter `T` + | +LL | fn test_poly<T: Trait>() { + | +++++++ + +error[E0277]: the trait bound `i32: Trait` is not satisfied + --> $DIR/projection-trait-ref.rs:13:12 + | +LL | let x: <i32 as Trait>::Assoc = (); + | ^^^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `i32` + | +help: this trait has no implementations, consider adding one + --> $DIR/projection-trait-ref.rs:3:1 + | +LL | trait Trait { + | ^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/diagnostics/where-clause-doesnt-apply.stderr b/tests/ui/traits/next-solver/diagnostics/where-clause-doesnt-apply.stderr index 77a0cc49754..ab1d4a56c02 100644 --- a/tests/ui/traits/next-solver/diagnostics/where-clause-doesnt-apply.stderr +++ b/tests/ui/traits/next-solver/diagnostics/where-clause-doesnt-apply.stderr @@ -6,6 +6,11 @@ LL | needs_foo(()); | | | required by a bound introduced by this call | +help: this trait has no implementations, consider adding one + --> $DIR/where-clause-doesnt-apply.rs:2:1 + | +LL | trait Bar {} + | ^^^^^^^^^ note: required for `()` to implement `Foo` --> $DIR/where-clause-doesnt-apply.rs:4:9 | diff --git a/tests/ui/traits/next-solver/env-shadows-impls/param-candidate-shadows-project.stderr b/tests/ui/traits/next-solver/env-shadows-impls/param-candidate-shadows-project.stderr index 3ef0afa38bf..0c72fc25dca 100644 --- a/tests/ui/traits/next-solver/env-shadows-impls/param-candidate-shadows-project.stderr +++ b/tests/ui/traits/next-solver/env-shadows-impls/param-candidate-shadows-project.stderr @@ -2,13 +2,8 @@ error[E0271]: type mismatch resolving `<T as Foo>::Assoc == i32` --> $DIR/param-candidate-shadows-project.rs:27:19 | LL | require_bar::<T>(); - | ^ type mismatch resolving `<T as Foo>::Assoc == i32` + | ^ types differ | -note: types differ - --> $DIR/param-candidate-shadows-project.rs:10:18 - | -LL | type Assoc = i32; - | ^^^ note: required for `T` to implement `Bar` --> $DIR/param-candidate-shadows-project.rs:13:9 | diff --git a/tests/ui/traits/next-solver/normalize/two-projection-param-candidates-are-ambiguous.rs b/tests/ui/traits/next-solver/normalize/two-projection-param-candidates-are-ambiguous.rs index 12ea1bf142a..5239474ff44 100644 --- a/tests/ui/traits/next-solver/normalize/two-projection-param-candidates-are-ambiguous.rs +++ b/tests/ui/traits/next-solver/normalize/two-projection-param-candidates-are-ambiguous.rs @@ -24,7 +24,7 @@ fn needs_bar<T: Bar>() {} fn foo<T: Foo<Assoc = i32> + Foo<Assoc = u32>>() { needs_bar::<T>(); - //~^ ERROR type annotations needed: cannot satisfy `<T as Foo>::Assoc == i32` + //~^ ERROR type annotations needed: cannot normalize } fn main() {} diff --git a/tests/ui/traits/next-solver/normalize/two-projection-param-candidates-are-ambiguous.stderr b/tests/ui/traits/next-solver/normalize/two-projection-param-candidates-are-ambiguous.stderr index 21f3fbfeb87..270ad851717 100644 --- a/tests/ui/traits/next-solver/normalize/two-projection-param-candidates-are-ambiguous.stderr +++ b/tests/ui/traits/next-solver/normalize/two-projection-param-candidates-are-ambiguous.stderr @@ -1,8 +1,8 @@ -error[E0284]: type annotations needed: cannot satisfy `<T as Foo>::Assoc == i32` +error[E0284]: type annotations needed: cannot normalize `<T as Foo>::Assoc` --> $DIR/two-projection-param-candidates-are-ambiguous.rs:26:17 | LL | needs_bar::<T>(); - | ^ cannot satisfy `<T as Foo>::Assoc == i32` + | ^ cannot normalize `<T as Foo>::Assoc` | note: required for `T` to implement `Bar` --> $DIR/two-projection-param-candidates-are-ambiguous.rs:21:9 diff --git a/tests/ui/traits/next-solver/specialization-transmute.stderr b/tests/ui/traits/next-solver/specialization-transmute.stderr index 524522bef09..b96bfab927d 100644 --- a/tests/ui/traits/next-solver/specialization-transmute.stderr +++ b/tests/ui/traits/next-solver/specialization-transmute.stderr @@ -10,23 +10,23 @@ LL | #![feature(specialization)] error: cannot normalize `<T as Default>::Id: '_` -error[E0284]: type annotations needed: cannot satisfy `<T as Default>::Id == _` +error[E0284]: type annotations needed: cannot normalize `<T as Default>::Id` --> $DIR/specialization-transmute.rs:15:23 | LL | fn intu(&self) -> &Self::Id { - | ^^^^^^^^^ cannot satisfy `<T as Default>::Id == _` + | ^^^^^^^^^ cannot normalize `<T as Default>::Id` -error[E0284]: type annotations needed: cannot satisfy `T <: <T as Default>::Id` +error[E0284]: type annotations needed: cannot satisfy `<T as Default>::Id normalizes-to T` --> $DIR/specialization-transmute.rs:17:9 | LL | self - | ^^^^ cannot satisfy `T <: <T as Default>::Id` + | ^^^^ cannot satisfy `<T as Default>::Id normalizes-to T` -error[E0284]: type annotations needed: cannot satisfy `<u8 as Default>::Id == Option<NonZero<u8>>` +error[E0284]: type annotations needed: cannot satisfy `<u8 as Default>::Id normalizes-to Option<NonZero<u8>>` --> $DIR/specialization-transmute.rs:28:13 | LL | let s = transmute::<u8, Option<NonZero<u8>>>(0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `<u8 as Default>::Id == Option<NonZero<u8>>` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `<u8 as Default>::Id normalizes-to Option<NonZero<u8>>` | note: required by a bound in `transmute` --> $DIR/specialization-transmute.rs:21:25 diff --git a/tests/ui/traits/next-solver/specialization-unconstrained.rs b/tests/ui/traits/next-solver/specialization-unconstrained.rs index e44246a1262..2bbe7840110 100644 --- a/tests/ui/traits/next-solver/specialization-unconstrained.rs +++ b/tests/ui/traits/next-solver/specialization-unconstrained.rs @@ -18,5 +18,5 @@ fn test<T: Default<Id = U>, U>() {} fn main() { test::<u32, ()>(); - //~^ ERROR cannot satisfy `<u32 as Default>::Id == ()` + //~^ ERROR cannot satisfy `<u32 as Default>::Id normalizes-to ()` } diff --git a/tests/ui/traits/next-solver/specialization-unconstrained.stderr b/tests/ui/traits/next-solver/specialization-unconstrained.stderr index a6f6a4f260d..e1d785b554b 100644 --- a/tests/ui/traits/next-solver/specialization-unconstrained.stderr +++ b/tests/ui/traits/next-solver/specialization-unconstrained.stderr @@ -8,11 +8,11 @@ LL | #![feature(specialization)] = help: consider using `min_specialization` instead, which is more stable and complete = note: `#[warn(incomplete_features)]` on by default -error[E0284]: type annotations needed: cannot satisfy `<u32 as Default>::Id == ()` +error[E0284]: type annotations needed: cannot satisfy `<u32 as Default>::Id normalizes-to ()` --> $DIR/specialization-unconstrained.rs:20:5 | LL | test::<u32, ()>(); - | ^^^^^^^^^^^^^^^^^ cannot satisfy `<u32 as Default>::Id == ()` + | ^^^^^^^^^^^^^^^^^ cannot satisfy `<u32 as Default>::Id normalizes-to ()` | note: required by a bound in `test` --> $DIR/specialization-unconstrained.rs:17:20 diff --git a/tests/ui/traits/suggest-dereferences/root-obligation.stderr b/tests/ui/traits/suggest-dereferences/root-obligation.stderr index 14b2ecbb9f2..bbfbb98fba7 100644 --- a/tests/ui/traits/suggest-dereferences/root-obligation.stderr +++ b/tests/ui/traits/suggest-dereferences/root-obligation.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `&char: Pattern<'_>` is not satisfied --> $DIR/root-obligation.rs:6:38 | LL | .filter(|c| "aeiou".contains(c)) - | -------- ^ the trait `Fn(char)` is not implemented for `&char`, which is required by `&char: Pattern<'_>` + | -------- ^ the trait `Fn(char)` is not implemented for `char`, which is required by `&char: Pattern<'_>` | | | required by a bound introduced by this call | diff --git a/tests/ui/transmutability/enums/uninhabited_optimization.rs b/tests/ui/transmutability/enums/uninhabited_optimization.rs new file mode 100644 index 00000000000..04a8eb40c8b --- /dev/null +++ b/tests/ui/transmutability/enums/uninhabited_optimization.rs @@ -0,0 +1,26 @@ +//@ check-pass +//! Tests that we do not regress rust-lang/rust#125811 +#![feature(transmutability)] + +fn assert_transmutable<T>() +where + (): std::mem::BikeshedIntrinsicFrom<T> +{} + +enum Uninhabited {} + +enum SingleInhabited { + X, + Y(Uninhabited) +} + +enum SingleUninhabited { + X(Uninhabited), + Y(Uninhabited), +} + +fn main() { + assert_transmutable::<Uninhabited>(); + assert_transmutable::<SingleInhabited>(); + assert_transmutable::<SingleUninhabited>(); +} diff --git a/tests/ui/transmutability/references/unsafecell.stderr b/tests/ui/transmutability/references/unsafecell.stderr index 651eb8ceb26..8bb32359355 100644 --- a/tests/ui/transmutability/references/unsafecell.stderr +++ b/tests/ui/transmutability/references/unsafecell.stderr @@ -2,7 +2,7 @@ error[E0277]: `&u8` cannot be safely transmuted into `&UnsafeCell<u8>` --> $DIR/unsafecell.rs:27:50 | LL | assert::is_maybe_transmutable::<&'static u8, &'static UnsafeCell<u8>>(); - | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Freeze` is not implemented for `&'static UnsafeCell<u8>` + | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Freeze` is not implemented for `UnsafeCell<u8>` | note: required by a bound in `is_maybe_transmutable` --> $DIR/unsafecell.rs:12:14 @@ -17,7 +17,7 @@ error[E0277]: `&UnsafeCell<u8>` cannot be safely transmuted into `&UnsafeCell<u8 --> $DIR/unsafecell.rs:29:62 | LL | assert::is_maybe_transmutable::<&'static UnsafeCell<u8>, &'static UnsafeCell<u8>>(); - | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Freeze` is not implemented for `&'static UnsafeCell<u8>` + | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Freeze` is not implemented for `UnsafeCell<u8>` | note: required by a bound in `is_maybe_transmutable` --> $DIR/unsafecell.rs:12:14 diff --git a/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.next.stderr b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.next.stderr index 4c8a25edfed..5a728a00138 100644 --- a/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.next.stderr +++ b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.next.stderr @@ -1,4 +1,4 @@ -error[E0284]: type annotations needed: cannot satisfy `<Out as Trait<Bar, In>>::Out == ()` +error[E0284]: type annotations needed: cannot satisfy `Bar == _` --> $DIR/issue-84660-unsoundness.rs:22:37 | LL | fn convert(_i: In) -> Self::Out { @@ -7,7 +7,7 @@ LL | | LL | | LL | | unreachable!(); LL | | } - | |_____^ cannot satisfy `<Out as Trait<Bar, In>>::Out == ()` + | |_____^ cannot satisfy `Bar == _` error[E0119]: conflicting implementations of trait `Trait<Bar, _>` --> $DIR/issue-84660-unsoundness.rs:29:1 diff --git a/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.rs b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.rs index 73c8deb3a4d..f3234bafd11 100644 --- a/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.rs +++ b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.rs @@ -20,7 +20,7 @@ trait Trait<T, In> { impl<In, Out> Trait<Bar, In> for Out { type Out = Out; fn convert(_i: In) -> Self::Out { - //[next]~^ ERROR: cannot satisfy `<Out as Trait<Bar, In>>::Out == ()` + //[next]~^ ERROR: cannot satisfy `Bar == _` //[current]~^^ ERROR: item does not constrain `Bar::{opaque#0}`, but has it in its signature unreachable!(); } diff --git a/tests/ui/typeck/suggest-similar-impls-for-root-obligation.rs b/tests/ui/typeck/suggest-similar-impls-for-root-obligation.rs new file mode 100644 index 00000000000..d00b4f33132 --- /dev/null +++ b/tests/ui/typeck/suggest-similar-impls-for-root-obligation.rs @@ -0,0 +1,16 @@ +trait Foo<'s> {} + +impl<'s> Foo<'s> for () {} + +struct Bar; + +impl<'s, T: Foo<'s>> From<T> for Bar { + fn from(_: T) -> Self { + Bar + } +} + +fn main() { + let _: Bar = ((),).into(); + //~^ ERROR he trait bound `((),): Into<Bar>` is not satisfied +} diff --git a/tests/ui/typeck/suggest-similar-impls-for-root-obligation.stderr b/tests/ui/typeck/suggest-similar-impls-for-root-obligation.stderr new file mode 100644 index 00000000000..8410574e311 --- /dev/null +++ b/tests/ui/typeck/suggest-similar-impls-for-root-obligation.stderr @@ -0,0 +1,20 @@ +error[E0277]: the trait bound `((),): Into<Bar>` is not satisfied + --> $DIR/suggest-similar-impls-for-root-obligation.rs:14:24 + | +LL | let _: Bar = ((),).into(); + | ^^^^ the trait `Foo<'_>` is not implemented for `((),)`, which is required by `((),): Into<_>` + | + = help: the trait `Foo<'_>` is implemented for `()` + = help: for that trait implementation, expected `()`, found `((),)` +note: required for `Bar` to implement `From<((),)>` + --> $DIR/suggest-similar-impls-for-root-obligation.rs:7:22 + | +LL | impl<'s, T: Foo<'s>> From<T> for Bar { + | ------- ^^^^^^^ ^^^ + | | + | unsatisfied trait bound introduced here + = note: required for `((),)` to implement `Into<Bar>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. |
