diff options
163 files changed, 4503 insertions, 2117 deletions
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index f02c7b2d2e1..74e194750fa 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -166,6 +166,13 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>( pointee_type: Ty<'tcx>, unique_type_id: UniqueTypeId<'tcx>, ) -> DINodeCreationResult<'ll> { + // The debuginfo generated by this function is only valid if `ptr_type` is really just + // a (fat) pointer. Make sure it is not called for e.g. `Box<T, NonZSTAllocator>`. + debug_assert_eq!( + cx.size_and_align_of(ptr_type), + cx.size_and_align_of(cx.tcx.mk_mut_ptr(pointee_type)) + ); + let pointee_type_di_node = type_di_node(cx, pointee_type); return_if_di_node_created_in_meantime!(cx, unique_type_id); @@ -212,7 +219,17 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>( DIFlags::FlagZero, ), |cx, owner| { - let layout = cx.layout_of(ptr_type); + // FIXME: If this fat pointer is a `Box` then we don't want to use its + // type layout and instead use the layout of the raw pointer inside + // of it. + // The proper way to handle this is to not treat Box as a pointer + // at all and instead emit regular struct debuginfo for it. We just + // need to make sure that we don't break existing debuginfo consumers + // by doing that (at least not without a warning period). + let layout_type = + if ptr_type.is_box() { cx.tcx.mk_mut_ptr(pointee_type) } else { ptr_type }; + + let layout = cx.layout_of(layout_type); let addr_field = layout.field(cx, abi::FAT_PTR_ADDR); let extra_field = layout.field(cx, abi::FAT_PTR_EXTRA); diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs index 30764f689c9..faea2111d92 100644 --- a/compiler/rustc_const_eval/src/transform/promote_consts.rs +++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs @@ -42,7 +42,7 @@ pub struct PromoteTemps<'tcx> { impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> { fn phase_change(&self) -> Option<MirPhase> { - Some(MirPhase::ConstPromotion) + Some(MirPhase::ConstsPromoted) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index cf15fc4ddc3..deeca78b75d 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -266,22 +266,15 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ); } } - // The deaggregator currently does not deaggreagate arrays. - // So for now, we ignore them here. - Rvalue::Aggregate(box AggregateKind::Array { .. }, _) => {} - // All other aggregates must be gone after some phases. - Rvalue::Aggregate(box kind, _) => { - if self.mir_phase > MirPhase::DropLowering - && !matches!(kind, AggregateKind::Generator(..)) - { - // Generators persist until the state machine transformation, but all - // other aggregates must have been lowered. - self.fail( - location, - format!("{:?} have been lowered to field assignments", rvalue), - ) - } else if self.mir_phase > MirPhase::GeneratorLowering { - // No more aggregates after drop and generator lowering. + Rvalue::Aggregate(agg_kind, _) => { + let disallowed = match **agg_kind { + AggregateKind::Array(..) => false, + AggregateKind::Generator(..) => { + self.mir_phase >= MirPhase::GeneratorsLowered + } + _ => self.mir_phase >= MirPhase::Deaggregated, + }; + if disallowed { self.fail( location, format!("{:?} have been lowered to field assignments", rvalue), @@ -289,7 +282,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } Rvalue::Ref(_, BorrowKind::Shallow, _) => { - if self.mir_phase > MirPhase::DropLowering { + if self.mir_phase >= MirPhase::DropsLowered { self.fail( location, "`Assign` statement with a `Shallow` borrow should have been removed after drop lowering phase", @@ -300,7 +293,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } StatementKind::AscribeUserType(..) => { - if self.mir_phase > MirPhase::DropLowering { + if self.mir_phase >= MirPhase::DropsLowered { self.fail( location, "`AscribeUserType` should have been removed after drop lowering phase", @@ -308,7 +301,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } StatementKind::FakeRead(..) => { - if self.mir_phase > MirPhase::DropLowering { + if self.mir_phase >= MirPhase::DropsLowered { self.fail( location, "`FakeRead` should have been removed after drop lowering phase", @@ -351,10 +344,18 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.fail(location, format!("bad arg ({:?} != usize)", op_cnt_ty)) } } - StatementKind::SetDiscriminant { .. } - | StatementKind::StorageLive(..) + StatementKind::SetDiscriminant { .. } => { + if self.mir_phase < MirPhase::DropsLowered { + self.fail(location, "`SetDiscriminant` is not allowed until drop elaboration"); + } + } + StatementKind::Retag(_, _) => { + // FIXME(JakobDegen) The validator should check that `self.mir_phase < + // DropsLowered`. However, this causes ICEs with generation of drop shims, which + // seem to fail to set their `MirPhase` correctly. + } + StatementKind::StorageLive(..) | StatementKind::StorageDead(..) - | StatementKind::Retag(_, _) | StatementKind::Coverage(_) | StatementKind::Nop => {} } @@ -424,10 +425,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } TerminatorKind::DropAndReplace { target, unwind, .. } => { - if self.mir_phase > MirPhase::DropLowering { + if self.mir_phase >= MirPhase::DropsLowered { self.fail( location, - "`DropAndReplace` is not permitted to exist after drop elaboration", + "`DropAndReplace` should have been removed during drop elaboration", ); } self.check_edge(location, *target, EdgeKind::Normal); @@ -494,7 +495,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } TerminatorKind::Yield { resume, drop, .. } => { - if self.mir_phase > MirPhase::GeneratorLowering { + if self.mir_phase >= MirPhase::GeneratorsLowered { self.fail(location, "`Yield` should have been replaced by generator lowering"); } self.check_edge(location, *resume, EdgeKind::Normal); @@ -503,10 +504,22 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } TerminatorKind::FalseEdge { real_target, imaginary_target } => { + if self.mir_phase >= MirPhase::DropsLowered { + self.fail( + location, + "`FalseEdge` should have been removed after drop elaboration", + ); + } self.check_edge(location, *real_target, EdgeKind::Normal); self.check_edge(location, *imaginary_target, EdgeKind::Normal); } TerminatorKind::FalseUnwind { real_target, unwind } => { + if self.mir_phase >= MirPhase::DropsLowered { + self.fail( + location, + "`FalseUnwind` should have been removed after drop elaboration", + ); + } self.check_edge(location, *real_target, EdgeKind::Normal); if let Some(unwind) = unwind { self.check_edge(location, *unwind, EdgeKind::Unwind); @@ -520,12 +533,19 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.check_edge(location, *cleanup, EdgeKind::Unwind); } } + TerminatorKind::GeneratorDrop => { + if self.mir_phase >= MirPhase::GeneratorsLowered { + self.fail( + location, + "`GeneratorDrop` should have been replaced by generator lowering", + ); + } + } // Nothing to validate for these. TerminatorKind::Resume | TerminatorKind::Abort | TerminatorKind::Return - | TerminatorKind::Unreachable - | TerminatorKind::GeneratorDrop => {} + | TerminatorKind::Unreachable => {} } self.super_terminator(terminator, location); diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index d8071bf159a..5e97fc90320 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -101,7 +101,14 @@ struct MatcherTtFrame<'tt> { idx: usize, } -type NamedMatchVec = SmallVec<[NamedMatch; 4]>; +// One element is enough to cover 95-99% of vectors for most benchmarks. Also, +// vectors longer than one frequently have many elements, not just two or +// three. +type NamedMatchVec = SmallVec<[NamedMatch; 1]>; + +// This type is used a lot. Make sure it doesn't unintentionally get bigger. +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +rustc_data_structures::static_assert_size!(NamedMatchVec, 48); /// Represents a single "position" (aka "matcher position", aka "item"), as /// described in the module documentation. @@ -153,7 +160,7 @@ struct MatcherPos<'tt> { // This type is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(MatcherPos<'_>, 136); +rustc_data_structures::static_assert_size!(MatcherPos<'_>, 112); impl<'tt> MatcherPos<'tt> { /// `len` `Vec`s (initially shared and empty) that will store matches of metavars. @@ -202,11 +209,7 @@ impl<'tt> MatcherPos<'tt> { match_lo: up.match_cur, match_cur: up.match_cur, match_hi: up.match_cur + seq.num_captures, - repetition: Some(MatcherPosRepetition { - up, - sep: seq.separator.clone(), - seq_op: seq.kleene.op, - }), + repetition: Some(MatcherPosRepetition { up, seq }), stack: smallvec![], } } @@ -220,15 +223,12 @@ impl<'tt> MatcherPos<'tt> { #[derive(Clone)] struct MatcherPosRepetition<'tt> { - /// The KleeneOp of this sequence. - seq_op: mbe::KleeneOp, - - /// The separator. - sep: Option<Token>, - /// The "parent" matcher position. That is, the matcher position just before we enter the /// sequence. up: Box<MatcherPos<'tt>>, + + /// The sequence itself. + seq: &'tt SequenceRepetition, } enum EofItems<'tt> { @@ -274,22 +274,20 @@ pub(super) fn count_names(ms: &[TokenTree]) -> usize { }) } -/// `NamedMatch` is a pattern-match result for a single `token::MATCH_NONTERMINAL`: -/// so it is associated with a single ident in a parse, and all -/// `MatchedNonterminal`s in the `NamedMatch` have the same non-terminal type -/// (expr, item, etc). Each leaf in a single `NamedMatch` corresponds to a -/// single `token::MATCH_NONTERMINAL` in the `TokenTree` that produced it. +/// `NamedMatch` is a pattern-match result for a single metavar. All +/// `MatchedNtNonTt`s in the `NamedMatch` have the same non-terminal type +/// (expr, item, etc). /// /// The in-memory structure of a particular `NamedMatch` represents the match /// that occurred when a particular subset of a matcher was applied to a /// particular token tree. /// /// The width of each `MatchedSeq` in the `NamedMatch`, and the identity of -/// the `MatchedNonterminal`s, will depend on the token tree it was applied -/// to: each `MatchedSeq` corresponds to a single `TTSeq` in the originating +/// the `MatchedNtNonTts`s, will depend on the token tree it was applied +/// to: each `MatchedSeq` corresponds to a single repetition in the originating /// token tree. The depth of the `NamedMatch` structure will therefore depend -/// only on the nesting depth of `ast::TTSeq`s in the originating -/// token tree it was derived from. +/// only on the nesting depth of repetitions in the originating token tree it +/// was derived from. /// /// In layman's terms: `NamedMatch` will form a tree representing nested matches of a particular /// meta variable. For example, if we are matching the following macro against the following @@ -308,24 +306,32 @@ pub(super) fn count_names(ms: &[TokenTree]) -> usize { /// ```rust /// MatchedSeq([ /// MatchedSeq([ -/// MatchedNonterminal(a), -/// MatchedNonterminal(b), -/// MatchedNonterminal(c), -/// MatchedNonterminal(d), +/// MatchedNtNonTt(a), +/// MatchedNtNonTt(b), +/// MatchedNtNonTt(c), +/// MatchedNtNonTt(d), /// ]), /// MatchedSeq([ -/// MatchedNonterminal(a), -/// MatchedNonterminal(b), -/// MatchedNonterminal(c), -/// MatchedNonterminal(d), -/// MatchedNonterminal(e), +/// MatchedNtNonTt(a), +/// MatchedNtNonTt(b), +/// MatchedNtNonTt(c), +/// MatchedNtNonTt(d), +/// MatchedNtNonTt(e), /// ]) /// ]) /// ``` #[derive(Debug, Clone)] crate enum NamedMatch { MatchedSeq(Lrc<NamedMatchVec>), - MatchedNonterminal(Lrc<Nonterminal>), + + // This variant should never hold an `NtTT`. `MatchedNtTt` should be used + // for that case. + MatchedNtNonTt(Lrc<Nonterminal>), + + // `NtTT` is handled without any cloning when transcribing, unlike other + // nonterminals. Therefore, an `Lrc` isn't helpful and causes unnecessary + // allocations. Hence this separate variant. + MatchedNtTt(rustc_ast::tokenstream::TokenTree), } /// Takes a slice of token trees `ms` representing a matcher which successfully matched input @@ -546,14 +552,19 @@ impl<'tt> TtParser<'tt> { self.cur_items.push(new_pos); } - if idx == len && repetition.sep.is_some() { - if repetition.sep.as_ref().map_or(false, |sep| token_name_eq(token, sep)) { + if idx == len && repetition.seq.separator.is_some() { + if repetition + .seq + .separator + .as_ref() + .map_or(false, |sep| token_name_eq(token, sep)) + { // The matcher has a separator, and it matches the current token. We can // advance past the separator token. item.idx += 1; self.next_items.push(item); } - } else if repetition.seq_op != mbe::KleeneOp::ZeroOrOne { + } else if repetition.seq.kleene.op != mbe::KleeneOp::ZeroOrOne { // We don't need a separator. Move the "dot" back to the beginning of the // matcher and try to match again UNLESS we are only allowed to have _one_ // repetition. @@ -665,7 +676,11 @@ impl<'tt> TtParser<'tt> { } Ok(nt) => nt, }; - item.push_match(match_cur, MatchedNonterminal(Lrc::new(nt))); + let m = match nt { + Nonterminal::NtTT(tt) => MatchedNtTt(tt), + _ => MatchedNtNonTt(Lrc::new(nt)), + }; + item.push_match(match_cur, m); item.idx += 1; item.match_cur += 1; } else { diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index f13b97251d2..7837de5c18d 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -4,11 +4,11 @@ use crate::expand::{ensure_complete_parse, parse_ast_fragment, AstFragment, AstF use crate::mbe; use crate::mbe::macro_check; use crate::mbe::macro_parser::{Error, ErrorReported, Failure, Success, TtParser}; -use crate::mbe::macro_parser::{MatchedNonterminal, MatchedSeq}; +use crate::mbe::macro_parser::{MatchedNtTt, MatchedSeq}; use crate::mbe::transcribe::transcribe; use rustc_ast as ast; -use rustc_ast::token::{self, NonterminalKind, NtTT, Token, TokenKind::*}; +use rustc_ast::token::{self, NonterminalKind, Token, TokenKind::*}; use rustc_ast::tokenstream::{DelimSpan, TokenStream}; use rustc_ast::{NodeId, DUMMY_NODE_ID}; use rustc_ast_pretty::pprust; @@ -470,22 +470,20 @@ pub fn compile_declarative_macro( MatchedSeq(ref s) => s .iter() .map(|m| { - if let MatchedNonterminal(ref nt) = *m { - if let NtTT(ref tt) = **nt { - let mut tts = vec![]; - mbe::quoted::parse( - tt.clone().into(), - true, - &sess.parse_sess, - def.id, - features, - edition, - &mut tts, - ); - let tt = tts.pop().unwrap(); - valid &= check_lhs_nt_follows(&sess.parse_sess, features, &def, &tt); - return tt; - } + if let MatchedNtTt(ref tt) = *m { + let mut tts = vec![]; + mbe::quoted::parse( + tt.clone().into(), + true, + &sess.parse_sess, + def.id, + features, + edition, + &mut tts, + ); + let tt = tts.pop().unwrap(); + valid &= check_lhs_nt_follows(&sess.parse_sess, features, &def, &tt); + return tt; } sess.parse_sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs") }) @@ -497,20 +495,18 @@ pub fn compile_declarative_macro( MatchedSeq(ref s) => s .iter() .map(|m| { - if let MatchedNonterminal(ref nt) = *m { - if let NtTT(ref tt) = **nt { - let mut tts = vec![]; - mbe::quoted::parse( - tt.clone().into(), - false, - &sess.parse_sess, - def.id, - features, - edition, - &mut tts, - ); - return tts.pop().unwrap(); - } + if let MatchedNtTt(ref tt) = *m { + let mut tts = vec![]; + mbe::quoted::parse( + tt.clone().into(), + false, + &sess.parse_sess, + def.id, + features, + edition, + &mut tts, + ); + return tts.pop().unwrap(); } sess.parse_sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs") }) diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index e097f9d9c02..228ed04548d 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -1,8 +1,8 @@ use crate::base::ExtCtxt; -use crate::mbe::macro_parser::{MatchedNonterminal, MatchedSeq, NamedMatch}; +use crate::mbe::macro_parser::{MatchedNtNonTt, MatchedNtTt, MatchedSeq, NamedMatch}; use crate::mbe::{self, MetaVarExpr}; use rustc_ast::mut_visit::{self, MutVisitor}; -use rustc_ast::token::{self, NtTT, Token, TokenKind}; +use rustc_ast::token::{self, Nonterminal, Token, TokenKind}; use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndSpacing}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; @@ -233,25 +233,29 @@ pub(super) fn transcribe<'a>( // the meta-var. let ident = MacroRulesNormalizedIdent::new(orignal_ident); if let Some(cur_matched) = lookup_cur_matched(ident, interp, &repeats) { - if let MatchedNonterminal(nt) = cur_matched { - let token = if let NtTT(tt) = &**nt { + match cur_matched { + MatchedNtTt(ref tt) => { // `tt`s are emitted into the output stream directly as "raw tokens", // without wrapping them into groups. - tt.clone() - } else { + let token = tt.clone(); + result.push(token.into()); + } + MatchedNtNonTt(ref nt) => { // Other variables are emitted into the output stream as groups with // `Delimiter::None` to maintain parsing priorities. // `Interpolated` is currently used for such groups in rustc parser. + debug_assert!(!matches!(**nt, Nonterminal::NtTT(_))); marker.visit_span(&mut sp); - TokenTree::token(token::Interpolated(nt.clone()), sp) - }; - result.push(token.into()); - } else { - // We were unable to descend far enough. This is an error. - return Err(cx.struct_span_err( - sp, /* blame the macro writer */ - &format!("variable '{}' is still repeating at this depth", ident), - )); + let token = TokenTree::token(token::Interpolated(nt.clone()), sp); + result.push(token.into()); + } + MatchedSeq(..) => { + // We were unable to descend far enough. This is an error. + return Err(cx.struct_span_err( + sp, /* blame the macro writer */ + &format!("variable '{}' is still repeating at this depth", ident), + )); + } } } else { // If we aren't able to match the meta-var, we push it back into the result but @@ -308,7 +312,7 @@ fn lookup_cur_matched<'a>( let mut matched = matched; for &(idx, _) in repeats { match matched { - MatchedNonterminal(_) => break, + MatchedNtTt(_) | MatchedNtNonTt(_) => break, MatchedSeq(ref ads) => matched = ads.get(idx).unwrap(), } } @@ -398,7 +402,7 @@ fn lockstep_iter_size( let name = MacroRulesNormalizedIdent::new(name); match lookup_cur_matched(name, interpolations, repeats) { Some(matched) => match matched { - MatchedNonterminal(_) => LockstepIterSize::Unconstrained, + MatchedNtTt(_) | MatchedNtNonTt(_) => LockstepIterSize::Unconstrained, MatchedSeq(ref ads) => LockstepIterSize::Constraint(ads.len(), name), }, _ => LockstepIterSize::Unconstrained, @@ -445,7 +449,7 @@ fn count_repetitions<'a>( sp: &DelimSpan, ) -> PResult<'a, usize> { match matched { - MatchedNonterminal(_) => { + MatchedNtTt(_) | MatchedNtNonTt(_) => { if declared_lhs_depth == 0 { return Err(cx.struct_span_err( sp.entire(), diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index 94991fdb201..ff8082840e1 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -28,7 +28,7 @@ use super::*; use rustc_middle::ty::relate::{Relate, TypeRelation}; -use rustc_middle::ty::Const; +use rustc_middle::ty::{Const, ImplSubject}; pub struct At<'a, 'tcx> { pub infcx: &'a InferCtxt<'a, 'tcx>, @@ -272,6 +272,29 @@ impl<'a, 'tcx> Trace<'a, 'tcx> { } } +impl<'tcx> ToTrace<'tcx> for ImplSubject<'tcx> { + fn to_trace( + tcx: TyCtxt<'tcx>, + cause: &ObligationCause<'tcx>, + a_is_expected: bool, + a: Self, + b: Self, + ) -> TypeTrace<'tcx> { + match (a, b) { + (ImplSubject::Trait(trait_ref_a), ImplSubject::Trait(trait_ref_b)) => { + ToTrace::to_trace(tcx, cause, a_is_expected, trait_ref_a, trait_ref_b) + } + (ImplSubject::Inherent(ty_a), ImplSubject::Inherent(ty_b)) => { + ToTrace::to_trace(tcx, cause, a_is_expected, ty_a, ty_b) + } + (ImplSubject::Trait(_), ImplSubject::Inherent(_)) + | (ImplSubject::Inherent(_), ImplSubject::Trait(_)) => { + bug!("can not trace TraitRef and Ty"); + } + } + } +} + impl<'tcx> ToTrace<'tcx> for Ty<'tcx> { fn to_trace( _: TyCtxt<'tcx>, diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 5697e73f73a..2886d921c70 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -20,8 +20,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues}; use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType}; -use rustc_middle::mir::interpret::ErrorHandled; -use rustc_middle::mir::interpret::EvalToConstValueResult; +use rustc_middle::mir::interpret::{ErrorHandled, EvalToConstValueResult}; use rustc_middle::traits::select; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; @@ -71,7 +70,6 @@ mod sub; pub mod type_variable; mod undo_log; -use crate::infer::canonical::OriginalQueryValues; pub use rustc_middle::infer::unify_key; #[must_use] @@ -687,15 +685,28 @@ pub struct CombinedSnapshot<'a, 'tcx> { impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// calls `tcx.try_unify_abstract_consts` after /// canonicalizing the consts. + #[instrument(skip(self), level = "debug")] pub fn try_unify_abstract_consts( &self, a: ty::Unevaluated<'tcx, ()>, b: ty::Unevaluated<'tcx, ()>, + param_env: ty::ParamEnv<'tcx>, ) -> bool { - let canonical = self.canonicalize_query((a, b), &mut OriginalQueryValues::default()); - debug!("canonical consts: {:?}", &canonical.value); + // Reject any attempt to unify two unevaluated constants that contain inference + // variables, since inference variables in queries lead to ICEs. + if a.substs.has_infer_types_or_consts() + || b.substs.has_infer_types_or_consts() + || param_env.has_infer_types_or_consts() + { + debug!("a or b or param_env contain infer vars in its substs -> cannot unify"); + return false; + } + + let param_env_and = param_env.and((a, b)); + let erased = self.tcx.erase_regions(param_env_and); + debug!("after erase_regions: {:?}", erased); - self.tcx.try_unify_abstract_consts(canonical.value) + self.tcx.try_unify_abstract_consts(erased) } pub fn is_in_snapshot(&self) -> bool { @@ -1598,6 +1609,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// /// This handles inferences variables within both `param_env` and `substs` by /// performing the operation on their respective canonical forms. + #[instrument(skip(self), level = "debug")] pub fn const_eval_resolve( &self, param_env: ty::ParamEnv<'tcx>, @@ -1605,15 +1617,19 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { span: Option<Span>, ) -> EvalToConstValueResult<'tcx> { let substs = self.resolve_vars_if_possible(unevaluated.substs); + debug!(?substs); // Postpone the evaluation of constants whose substs depend on inference // variables if substs.has_infer_types_or_consts() { + debug!("substs have infer types or consts: {:?}", substs); return Err(ErrorHandled::TooGeneric); } let param_env_erased = self.tcx.erase_regions(param_env); let substs_erased = self.tcx.erase_regions(substs); + debug!(?param_env_erased); + debug!(?substs_erased); let unevaluated = ty::Unevaluated { def: unevaluated.def, diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 1053f0cefbe..a9e22d16ee0 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -7,10 +7,10 @@ pub mod nested_filter; pub mod place; use crate::ty::query::Providers; -use crate::ty::TyCtxt; +use crate::ty::{ImplSubject, TyCtxt}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_hir::def_id::LocalDefId; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::*; use rustc_query_system::ich::StableHashingContext; use rustc_span::DUMMY_SP; @@ -54,6 +54,12 @@ impl<'tcx> TyCtxt<'tcx> { pub fn parent_module(self, id: HirId) -> LocalDefId { self.parent_module_from_def_id(id.owner) } + + pub fn impl_subject(self, def_id: DefId) -> ImplSubject<'tcx> { + self.impl_trait_ref(def_id) + .map(ImplSubject::Trait) + .unwrap_or_else(|| ImplSubject::Inherent(self.type_of(def_id))) + } } pub fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index 8b0f92d23d7..7e5989b4112 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -1,6 +1,7 @@ use super::{ErrorHandled, EvalToConstValueResult, GlobalId}; use crate::mir; +use crate::ty::fold::TypeFoldable; use crate::ty::subst::InternalSubsts; use crate::ty::{self, TyCtxt}; use rustc_hir::def_id::DefId; @@ -38,6 +39,16 @@ impl<'tcx> TyCtxt<'tcx> { ct: ty::Unevaluated<'tcx>, span: Option<Span>, ) -> EvalToConstValueResult<'tcx> { + // Cannot resolve `Unevaluated` constants that contain inference + // variables. We reject those here since `resolve_opt_const_arg` + // would fail otherwise. + // + // When trying to evaluate constants containing inference variables, + // use `Infcx::const_eval_resolve` instead. + if ct.substs.has_infer_types_or_consts() { + bug!("did not expect inference variables here"); + } + match ty::Instance::resolve_opt_const_arg(self, param_env, ct.def, ct.substs) { Ok(Some(instance)) => { let cid = GlobalId { instance, promoted: ct.promoted }; diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index a5468b3f4f2..ea71cff1616 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -127,14 +127,11 @@ pub trait MirPass<'tcx> { /// These phases all describe dialects of MIR. Since all MIR uses the same datastructures, the /// dialects forbid certain variants or values in certain phases. /// -/// Note: Each phase's validation checks all invariants of the *previous* phases' dialects. A phase -/// that changes the dialect documents what invariants must be upheld *after* that phase finishes. -/// /// Warning: ordering of variants is significant. #[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(HashStable)] pub enum MirPhase { - Build = 0, + Built = 0, // FIXME(oli-obk): it's unclear whether we still need this phase (and its corresponding query). // We used to have this for pre-miri MIR based const eval. Const = 1, @@ -142,17 +139,32 @@ pub enum MirPhase { /// by creating a new MIR body per promoted element. After this phase (and thus the termination /// of the `mir_promoted` query), these promoted elements are available in the `promoted_mir` /// query. - ConstPromotion = 2, - /// After this phase - /// * the only `AggregateKind`s allowed are `Array` and `Generator`, - /// * `DropAndReplace` is gone for good - /// * `Drop` now uses explicit drop flags visible in the MIR and reaching a `Drop` terminator - /// means that the auto-generated drop glue will be invoked. - DropLowering = 3, - /// After this phase, generators are explicit state machines (no more `Yield`). - /// `AggregateKind::Generator` is gone for good. - GeneratorLowering = 4, - Optimization = 5, + ConstsPromoted = 2, + /// Beginning with this phase, the following variants are disallowed: + /// * [`TerminatorKind::DropAndReplace`](terminator::TerminatorKind::DropAndReplace) + /// * [`TerminatorKind::FalseUnwind`](terminator::TerminatorKind::FalseUnwind) + /// * [`TerminatorKind::FalseEdge`](terminator::TerminatorKind::FalseEdge) + /// * [`StatementKind::FakeRead`] + /// * [`StatementKind::AscribeUserType`] + /// * [`Rvalue::Ref`] with `BorrowKind::Shallow` + /// + /// And the following variant is allowed: + /// * [`StatementKind::Retag`] + /// + /// Furthermore, `Drop` now uses explicit drop flags visible in the MIR and reaching a `Drop` + /// terminator means that the auto-generated drop glue will be invoked. + DropsLowered = 3, + /// Beginning with this phase, the following variant is disallowed: + /// * [`Rvalue::Aggregate`] for any `AggregateKind` except `Array` + /// + /// And the following variant is allowed: + /// * [`StatementKind::SetDiscriminant`] + Deaggregated = 4, + /// Beginning with this phase, the following variants are disallowed: + /// * [`TerminatorKind::Yield`](terminator::TerminatorKind::Yield) + /// * [`TerminatorKind::GeneratorDrop](terminator::TerminatorKind::GeneratorDrop) + GeneratorsLowered = 5, + Optimized = 6, } impl MirPhase { @@ -311,7 +323,7 @@ impl<'tcx> Body<'tcx> { ); let mut body = Body { - phase: MirPhase::Build, + phase: MirPhase::Built, source, basic_blocks, source_scopes, @@ -346,7 +358,7 @@ impl<'tcx> Body<'tcx> { /// crate. pub fn new_cfg_only(basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>) -> Self { let mut body = Body { - phase: MirPhase::Build, + phase: MirPhase::Built, source: MirSource::item(DefId::local(CRATE_DEF_INDEX)), basic_blocks, source_scopes: IndexVec::new(), @@ -1541,9 +1553,16 @@ impl Statement<'_> { } } +/// The various kinds of statements that can appear in MIR. +/// +/// Not all of these are allowed at every [`MirPhase`]. Check the documentation there to see which +/// ones you do not have to worry about. The MIR validator will generally enforce such restrictions, +/// causing an ICE if they are violated. #[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable)] pub enum StatementKind<'tcx> { /// Write the RHS Rvalue to the LHS Place. + /// + /// The LHS place may not overlap with any memory accessed on the RHS. Assign(Box<(Place<'tcx>, Rvalue<'tcx>)>), /// This represents all the reading that a pattern match may do @@ -1761,6 +1780,19 @@ static_assert_size!(Place<'_>, 16); pub enum ProjectionElem<V, T> { Deref, Field(Field, T), + /// Index into a slice/array. + /// + /// Note that this does not also dereference, and so it does not exactly correspond to slice + /// indexing in Rust. In other words, in the below Rust code: + /// + /// ```rust + /// let x = &[1, 2, 3, 4]; + /// let i = 2; + /// x[i]; + /// ``` + /// + /// The `x[i]` is turned into a `Deref` followed by an `Index`, not just an `Index`. The same + /// thing is true of the `ConstantIndex` and `Subslice` projections below. Index(V), /// These indices are generated by slice patterns. Easiest to explain @@ -2223,6 +2255,11 @@ impl<'tcx> Operand<'tcx> { /// Rvalues #[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)] +/// The various kinds of rvalues that can appear in MIR. +/// +/// Not all of these are allowed at every [`MirPhase`]. Check the documentation there to see which +/// ones you do not have to worry about. The MIR validator will generally enforce such restrictions, +/// causing an ICE if they are violated. pub enum Rvalue<'tcx> { /// x (either a move or copy, depending on type of x) Use(Operand<'tcx>), diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 7a82533218b..95260e9e917 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -331,12 +331,12 @@ rustc_queries! { } } - query try_unify_abstract_consts(key: ( - ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()> - )) -> bool { + query try_unify_abstract_consts(key: + ty::ParamEnvAnd<'tcx, (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()> + )>) -> bool { desc { |tcx| "trying to unify the generic constants {} and {}", - tcx.def_path_str(key.0.def.did), tcx.def_path_str(key.1.def.did) + tcx.def_path_str(key.value.0.def.did), tcx.def_path_str(key.value.1.def.did) } } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 3ed7836074b..d852531bc68 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -491,7 +491,7 @@ pub enum SelectionError<'tcx> { /// A given constant couldn't be evaluated. NotConstEvaluatable(NotConstEvaluatable), /// Exceeded the recursion depth during type projection. - Overflow, + Overflow(OverflowError), /// Signaling that an error has already been emitted, to avoid /// multiple errors being shown. ErrorReporting, diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 56d42706f67..5297825a92f 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -5,6 +5,7 @@ use self::EvaluationResult::*; use super::{SelectionError, SelectionResult}; +use rustc_errors::ErrorGuaranteed; use crate::ty; @@ -264,14 +265,26 @@ impl EvaluationResult { /// Indicates that trait evaluation caused overflow and in which pass. #[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable)] pub enum OverflowError { + Error(ErrorGuaranteed), Canonical, ErrorReporting, } +impl From<ErrorGuaranteed> for OverflowError { + fn from(e: ErrorGuaranteed) -> OverflowError { + OverflowError::Error(e) + } +} + +TrivialTypeFoldableAndLiftImpls! { + OverflowError, +} + impl<'tcx> From<OverflowError> for SelectionError<'tcx> { fn from(overflow_error: OverflowError) -> SelectionError<'tcx> { match overflow_error { - OverflowError::Canonical => SelectionError::Overflow, + OverflowError::Error(e) => SelectionError::Overflow(OverflowError::Error(e)), + OverflowError::Canonical => SelectionError::Overflow(OverflowError::Canonical), OverflowError::ErrorReporting => SelectionError::ErrorReporting, } } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 3124cc5dba8..31db66dc242 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -44,6 +44,7 @@ use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::Span; use rustc_target::abi::Align; +use std::fmt::Debug; use std::hash::Hash; use std::ops::ControlFlow; use std::{fmt, str}; @@ -172,6 +173,12 @@ pub struct ImplHeader<'tcx> { pub predicates: Vec<Predicate<'tcx>>, } +#[derive(Copy, Clone, Debug, TypeFoldable)] +pub enum ImplSubject<'tcx> { + Trait(TraitRef<'tcx>), + Inherent(Ty<'tcx>), +} + #[derive( Copy, Clone, diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 81ee7942c4d..d9b55563996 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -7,7 +7,7 @@ use crate::mir::interpret::{get_slice_bytes, ConstValue, GlobalAlloc, Scalar}; use crate::ty::error::{ExpectedFound, TypeError}; use crate::ty::subst::{GenericArg, GenericArgKind, Subst, SubstsRef}; -use crate::ty::{self, Term, Ty, TyCtxt, TypeFoldable}; +use crate::ty::{self, ImplSubject, Term, Ty, TyCtxt, TypeFoldable}; use rustc_hir as ast; use rustc_hir::def_id::DefId; use rustc_span::DUMMY_SP; @@ -356,6 +356,30 @@ impl<'tcx> Relate<'tcx> for GeneratorWitness<'tcx> { } } +impl<'tcx> Relate<'tcx> for ImplSubject<'tcx> { + #[inline] + fn relate<R: TypeRelation<'tcx>>( + relation: &mut R, + a: ImplSubject<'tcx>, + b: ImplSubject<'tcx>, + ) -> RelateResult<'tcx, ImplSubject<'tcx>> { + match (a, b) { + (ImplSubject::Trait(trait_ref_a), ImplSubject::Trait(trait_ref_b)) => { + let trait_ref = ty::TraitRef::relate(relation, trait_ref_a, trait_ref_b)?; + Ok(ImplSubject::Trait(trait_ref)) + } + (ImplSubject::Inherent(ty_a), ImplSubject::Inherent(ty_b)) => { + let ty = Ty::relate(relation, ty_a, ty_b)?; + Ok(ImplSubject::Inherent(ty)) + } + (ImplSubject::Trait(_), ImplSubject::Inherent(_)) + | (ImplSubject::Inherent(_), ImplSubject::Trait(_)) => { + bug!("can not relate TraitRef and Ty"); + } + } + } +} + impl<'tcx> Relate<'tcx> for Ty<'tcx> { #[inline] fn relate<R: TypeRelation<'tcx>>( @@ -585,7 +609,7 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>( (ty::ConstKind::Unevaluated(au), ty::ConstKind::Unevaluated(bu)) if tcx.features().generic_const_exprs => { - tcx.try_unify_abstract_consts((au.shrink(), bu.shrink())) + tcx.try_unify_abstract_consts(relation.param_env().and((au.shrink(), bu.shrink()))) } // While this is slightly incorrect, it shouldn't matter for `min_const_generics` diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs index c4d15d4d187..4f5fc38917f 100644 --- a/compiler/rustc_mir_transform/src/const_prop.rs +++ b/compiler/rustc_mir_transform/src/const_prop.rs @@ -6,30 +6,25 @@ use std::cell::Cell; use rustc_ast::Mutability; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::DefKind; -use rustc_hir::HirId; use rustc_index::bit_set::BitSet; use rustc_index::vec::IndexVec; use rustc_middle::mir::visit::{ MutVisitor, MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor, }; use rustc_middle::mir::{ - AssertKind, BasicBlock, BinOp, Body, Constant, ConstantKind, Local, LocalDecl, LocalKind, - Location, Operand, Place, Rvalue, SourceInfo, SourceScope, SourceScopeData, Statement, - StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE, + BasicBlock, BinOp, Body, Constant, ConstantKind, Local, LocalDecl, LocalKind, Location, + Operand, Place, Rvalue, SourceInfo, Statement, StatementKind, Terminator, TerminatorKind, UnOp, + RETURN_PLACE, }; use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::subst::{InternalSubsts, Subst}; -use rustc_middle::ty::{ - self, ConstInt, ConstKind, Instance, ParamEnv, ScalarInt, Ty, TyCtxt, TypeFoldable, -}; -use rustc_session::lint; +use rustc_middle::ty::{self, ConstKind, Instance, ParamEnv, Ty, TyCtxt, TypeFoldable}; use rustc_span::{def_id::DefId, Span}; use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout}; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; use crate::MirPass; -use rustc_const_eval::const_eval::ConstEvalErr; use rustc_const_eval::interpret::{ self, compile_time_machine, AllocId, ConstAllocation, ConstValue, CtfeValidationMode, Frame, ImmTy, Immediate, InterpCx, InterpResult, LocalState, LocalValue, MemPlace, MemoryKind, OpTy, @@ -318,9 +313,8 @@ struct ConstPropagator<'mir, 'tcx> { ecx: InterpCx<'mir, 'tcx, ConstPropMachine<'mir, 'tcx>>, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, - // FIXME(eddyb) avoid cloning these two fields more than once, - // by accessing them through `ecx` instead. - source_scopes: IndexVec<SourceScope, SourceScopeData<'tcx>>, + // FIXME(eddyb) avoid cloning this field more than once, + // by accessing it through `ecx` instead. local_decls: IndexVec<Local, LocalDecl<'tcx>>, // Because we have `MutVisitor` we can't obtain the `SourceInfo` from a `Location`. So we store // the last known `SourceInfo` here and just keep revisiting it. @@ -412,9 +406,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { ecx, tcx, param_env, - // FIXME(eddyb) avoid cloning these two fields more than once, - // by accessing them through `ecx` instead. - source_scopes: body.source_scopes.clone(), + // FIXME(eddyb) avoid cloning this field more than once, + // by accessing it through `ecx` instead. //FIXME(wesleywiser) we can't steal this because `Visitor::super_visit_body()` needs it local_decls: body.local_decls.clone(), source_info: None, @@ -445,10 +438,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { LocalState { value: LocalValue::Unallocated, layout: Cell::new(None) }; } - fn lint_root(&self, source_info: SourceInfo) -> Option<HirId> { - source_info.scope.lint_root(&self.source_scopes) - } - fn use_ecx<F, T>(&mut self, f: F) -> Option<T> where F: FnOnce(&mut Self) -> InterpResult<'tcx, T>, @@ -471,45 +460,13 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } /// Returns the value, if any, of evaluating `c`. - fn eval_constant(&mut self, c: &Constant<'tcx>, source_info: SourceInfo) -> Option<OpTy<'tcx>> { + fn eval_constant(&mut self, c: &Constant<'tcx>) -> Option<OpTy<'tcx>> { // FIXME we need to revisit this for #67176 if c.needs_subst() { return None; } - match self.ecx.mir_const_to_op(&c.literal, None) { - Ok(op) => Some(op), - Err(error) => { - let tcx = self.ecx.tcx.at(c.span); - let err = ConstEvalErr::new(&self.ecx, error, Some(c.span)); - if let Some(lint_root) = self.lint_root(source_info) { - let lint_only = match c.literal { - ConstantKind::Ty(ct) => match ct.val() { - // Promoteds must lint and not error as the user didn't ask for them - ConstKind::Unevaluated(ty::Unevaluated { - def: _, - substs: _, - promoted: Some(_), - }) => true, - // Out of backwards compatibility we cannot report hard errors in unused - // generic functions using associated constants of the generic parameters. - _ => c.literal.needs_subst(), - }, - ConstantKind::Val(_, ty) => ty.needs_subst(), - }; - if lint_only { - // Out of backwards compatibility we cannot report hard errors in unused - // generic functions using associated constants of the generic parameters. - err.report_as_lint(tcx, "erroneous constant used", lint_root, Some(c.span)); - } else { - err.report_as_error(tcx, "erroneous constant used"); - } - } else { - err.report_as_error(tcx, "erroneous constant used"); - } - None - } - } + self.ecx.mir_const_to_op(&c.literal, None).ok() } /// Returns the value, if any, of evaluating `place`. @@ -520,49 +477,22 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { /// Returns the value, if any, of evaluating `op`. Calls upon `eval_constant` /// or `eval_place`, depending on the variant of `Operand` used. - fn eval_operand(&mut self, op: &Operand<'tcx>, source_info: SourceInfo) -> Option<OpTy<'tcx>> { + fn eval_operand(&mut self, op: &Operand<'tcx>) -> Option<OpTy<'tcx>> { match *op { - Operand::Constant(ref c) => self.eval_constant(c, source_info), + Operand::Constant(ref c) => self.eval_constant(c), Operand::Move(place) | Operand::Copy(place) => self.eval_place(place), } } - fn report_assert_as_lint( - &self, - lint: &'static lint::Lint, - source_info: SourceInfo, - message: &'static str, - panic: AssertKind<impl std::fmt::Debug>, - ) { - if let Some(lint_root) = self.lint_root(source_info) { - self.tcx.struct_span_lint_hir(lint, lint_root, source_info.span, |lint| { - let mut err = lint.build(message); - err.span_label(source_info.span, format!("{:?}", panic)); - err.emit(); - }); - } - } - - fn check_unary_op( - &mut self, - op: UnOp, - arg: &Operand<'tcx>, - source_info: SourceInfo, - ) -> Option<()> { - if let (val, true) = self.use_ecx(|this| { + fn check_unary_op(&mut self, op: UnOp, arg: &Operand<'tcx>) -> Option<()> { + if self.use_ecx(|this| { let val = this.ecx.read_immediate(&this.ecx.eval_operand(arg, None)?)?; let (_res, overflow, _ty) = this.ecx.overflowing_unary_op(op, &val)?; - Ok((val, overflow)) + Ok(overflow) })? { // `AssertKind` only has an `OverflowNeg` variant, so make sure that is // appropriate to use. assert_eq!(op, UnOp::Neg, "Neg is the only UnOp that can overflow"); - self.report_assert_as_lint( - lint::builtin::ARITHMETIC_OVERFLOW, - source_info, - "this arithmetic operation will overflow", - AssertKind::OverflowNeg(val.to_const_int()), - ); return None; } @@ -574,7 +504,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { op: BinOp, left: &Operand<'tcx>, right: &Operand<'tcx>, - source_info: SourceInfo, ) -> Option<()> { let r = self.use_ecx(|this| this.ecx.read_immediate(&this.ecx.eval_operand(right, None)?)); let l = self.use_ecx(|this| this.ecx.read_immediate(&this.ecx.eval_operand(left, None)?)); @@ -589,25 +518,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { let r_bits = r.to_scalar().ok(); let r_bits = r_bits.and_then(|r| r.to_bits(right_size).ok()); if r_bits.map_or(false, |b| b >= left_size.bits() as u128) { - debug!("check_binary_op: reporting assert for {:?}", source_info); - self.report_assert_as_lint( - lint::builtin::ARITHMETIC_OVERFLOW, - source_info, - "this arithmetic operation will overflow", - AssertKind::Overflow( - op, - match l { - Some(l) => l.to_const_int(), - // Invent a dummy value, the diagnostic ignores it anyway - None => ConstInt::new( - ScalarInt::try_from_uint(1_u8, left_size).unwrap(), - left_ty.is_signed(), - left_ty.is_ptr_sized_integral(), - ), - }, - r.to_const_int(), - ), - ); return None; } } @@ -618,12 +528,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { let (_res, overflow, _ty) = this.ecx.overflowing_binary_op(op, l, r)?; Ok(overflow) })? { - self.report_assert_as_lint( - lint::builtin::ARITHMETIC_OVERFLOW, - source_info, - "this arithmetic operation will overflow", - AssertKind::Overflow(op, l.to_const_int(), r.to_const_int()), - ); return None; } } @@ -656,12 +560,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } } - fn const_prop( - &mut self, - rvalue: &Rvalue<'tcx>, - source_info: SourceInfo, - place: Place<'tcx>, - ) -> Option<()> { + fn const_prop(&mut self, rvalue: &Rvalue<'tcx>, place: Place<'tcx>) -> Option<()> { // Perform any special handling for specific Rvalue types. // Generally, checks here fall into one of two categories: // 1. Additional checking to provide useful lints to the user @@ -676,11 +575,11 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { // lint. Rvalue::UnaryOp(op, arg) => { trace!("checking UnaryOp(op = {:?}, arg = {:?})", op, arg); - self.check_unary_op(*op, arg, source_info)?; + self.check_unary_op(*op, arg)?; } Rvalue::BinaryOp(op, box (left, right)) => { trace!("checking BinaryOp(op = {:?}, left = {:?}, right = {:?})", op, left, right); - self.check_binary_op(*op, left, right, source_info)?; + self.check_binary_op(*op, left, right)?; } Rvalue::CheckedBinaryOp(op, box (left, right)) => { trace!( @@ -689,7 +588,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { left, right ); - self.check_binary_op(*op, left, right, source_info)?; + self.check_binary_op(*op, left, right)?; } // Do not try creating references (#67862) @@ -1071,7 +970,7 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> { fn visit_constant(&mut self, constant: &mut Constant<'tcx>, location: Location) { trace!("visit_constant: {:?}", constant); self.super_constant(constant, location); - self.eval_constant(constant, self.source_info.unwrap()); + self.eval_constant(constant); } fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) { @@ -1080,7 +979,7 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> { self.source_info = Some(source_info); if let StatementKind::Assign(box (place, ref mut rval)) = statement.kind { let can_const_prop = self.ecx.machine.can_const_prop[place.local]; - if let Some(()) = self.const_prop(rval, source_info, place) { + if let Some(()) = self.const_prop(rval, place) { // This will return None if the above `const_prop` invocation only "wrote" a // type whose creation requires no write. E.g. a generator whose initial state // consists solely of uninitialized memory (so it doesn't capture any locals). @@ -1164,57 +1063,12 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> { self.source_info = Some(source_info); self.super_terminator(terminator, location); match &mut terminator.kind { - TerminatorKind::Assert { expected, ref msg, ref mut cond, .. } => { - if let Some(ref value) = self.eval_operand(&cond, source_info) { + TerminatorKind::Assert { expected, ref mut cond, .. } => { + if let Some(ref value) = self.eval_operand(&cond) { trace!("assertion on {:?} should be {:?}", value, expected); let expected = ScalarMaybeUninit::from(Scalar::from_bool(*expected)); let value_const = self.ecx.read_scalar(&value).unwrap(); if expected != value_const { - enum DbgVal<T> { - Val(T), - Underscore, - } - impl<T: std::fmt::Debug> std::fmt::Debug for DbgVal<T> { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Val(val) => val.fmt(fmt), - Self::Underscore => fmt.write_str("_"), - } - } - } - let mut eval_to_int = |op| { - // This can be `None` if the lhs wasn't const propagated and we just - // triggered the assert on the value of the rhs. - self.eval_operand(op, source_info).map_or(DbgVal::Underscore, |op| { - DbgVal::Val(self.ecx.read_immediate(&op).unwrap().to_const_int()) - }) - }; - let msg = match msg { - AssertKind::DivisionByZero(op) => { - Some(AssertKind::DivisionByZero(eval_to_int(op))) - } - AssertKind::RemainderByZero(op) => { - Some(AssertKind::RemainderByZero(eval_to_int(op))) - } - AssertKind::Overflow(bin_op @ (BinOp::Div | BinOp::Rem), op1, op2) => { - // Division overflow is *UB* in the MIR, and different than the - // other overflow checks. - Some(AssertKind::Overflow( - *bin_op, - eval_to_int(op1), - eval_to_int(op2), - )) - } - AssertKind::BoundsCheck { ref len, ref index } => { - let len = eval_to_int(len); - let index = eval_to_int(index); - Some(AssertKind::BoundsCheck { len, index }) - } - // Remaining overflow errors are already covered by checks on the binary operators. - AssertKind::Overflow(..) | AssertKind::OverflowNeg(_) => None, - // Need proper const propagator for these. - _ => None, - }; // Poison all places this operand references so that further code // doesn't use the invalid value match cond { @@ -1223,14 +1077,6 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> { } Operand::Constant(_) => {} } - if let Some(msg) = msg { - self.report_assert_as_lint( - lint::builtin::UNCONDITIONAL_PANIC, - source_info, - "this operation will panic at runtime", - msg, - ); - } } else { if self.should_const_prop(value) { if let ScalarMaybeUninit::Scalar(scalar) = value_const { diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs new file mode 100644 index 00000000000..5be745bc1de --- /dev/null +++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs @@ -0,0 +1,1035 @@ +//! Propagates constants for early reporting of statically known +//! assertion failures + +use std::cell::Cell; + +use rustc_ast::Mutability; +use rustc_data_structures::fx::FxHashSet; +use rustc_hir::def::DefKind; +use rustc_hir::HirId; +use rustc_index::bit_set::BitSet; +use rustc_index::vec::IndexVec; +use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; +use rustc_middle::mir::{ + AssertKind, BasicBlock, BinOp, Body, Constant, ConstantKind, Local, LocalDecl, LocalKind, + Location, Operand, Place, Rvalue, SourceInfo, SourceScope, SourceScopeData, Statement, + StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE, +}; +use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout}; +use rustc_middle::ty::subst::{InternalSubsts, Subst}; +use rustc_middle::ty::{ + self, ConstInt, ConstKind, Instance, ParamEnv, ScalarInt, Ty, TyCtxt, TypeFoldable, +}; +use rustc_session::lint; +use rustc_span::{def_id::DefId, Span}; +use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout}; +use rustc_target::spec::abi::Abi; +use rustc_trait_selection::traits; + +use crate::MirLint; +use rustc_const_eval::const_eval::ConstEvalErr; +use rustc_const_eval::interpret::{ + self, compile_time_machine, AllocId, ConstAllocation, Frame, ImmTy, InterpCx, InterpResult, + LocalState, LocalValue, MemPlace, MemoryKind, OpTy, Operand as InterpOperand, PlaceTy, Scalar, + ScalarMaybeUninit, StackPopCleanup, StackPopUnwind, +}; + +/// The maximum number of bytes that we'll allocate space for a local or the return value. +/// Needed for #66397, because otherwise we eval into large places and that can cause OOM or just +/// Severely regress performance. +const MAX_ALLOC_LIMIT: u64 = 1024; + +/// Macro for machine-specific `InterpError` without allocation. +/// (These will never be shown to the user, but they help diagnose ICEs.) +macro_rules! throw_machine_stop_str { + ($($tt:tt)*) => {{ + // We make a new local type for it. The type itself does not carry any information, + // but its vtable (for the `MachineStopType` trait) does. + struct Zst; + // Printing this type shows the desired string. + impl std::fmt::Display for Zst { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, $($tt)*) + } + } + impl rustc_middle::mir::interpret::MachineStopType for Zst {} + throw_machine_stop!(Zst) + }}; +} + +pub struct ConstProp; + +impl<'tcx> MirLint<'tcx> for ConstProp { + fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { + // will be evaluated by miri and produce its errors there + if body.source.promoted.is_some() { + return; + } + + let def_id = body.source.def_id().expect_local(); + let is_fn_like = tcx.hir().get_by_def_id(def_id).fn_kind().is_some(); + let is_assoc_const = tcx.def_kind(def_id) == DefKind::AssocConst; + + // Only run const prop on functions, methods, closures and associated constants + if !is_fn_like && !is_assoc_const { + // skip anon_const/statics/consts because they'll be evaluated by miri anyway + trace!("ConstProp skipped for {:?}", def_id); + return; + } + + let is_generator = tcx.type_of(def_id.to_def_id()).is_generator(); + // FIXME(welseywiser) const prop doesn't work on generators because of query cycles + // computing their layout. + if is_generator { + trace!("ConstProp skipped for generator {:?}", def_id); + return; + } + + // Check if it's even possible to satisfy the 'where' clauses + // for this item. + // This branch will never be taken for any normal function. + // However, it's possible to `#!feature(trivial_bounds)]` to write + // a function with impossible to satisfy clauses, e.g.: + // `fn foo() where String: Copy {}` + // + // We don't usually need to worry about this kind of case, + // since we would get a compilation error if the user tried + // to call it. However, since we can do const propagation + // even without any calls to the function, we need to make + // sure that it even makes sense to try to evaluate the body. + // If there are unsatisfiable where clauses, then all bets are + // off, and we just give up. + // + // We manually filter the predicates, skipping anything that's not + // "global". We are in a potentially generic context + // (e.g. we are evaluating a function without substituting generic + // parameters, so this filtering serves two purposes: + // + // 1. We skip evaluating any predicates that we would + // never be able prove are unsatisfiable (e.g. `<T as Foo>` + // 2. We avoid trying to normalize predicates involving generic + // parameters (e.g. `<T as Foo>::MyItem`). This can confuse + // the normalization code (leading to cycle errors), since + // it's usually never invoked in this way. + let predicates = tcx + .predicates_of(def_id.to_def_id()) + .predicates + .iter() + .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None }); + if traits::impossible_predicates( + tcx, + traits::elaborate_predicates(tcx, predicates).map(|o| o.predicate).collect(), + ) { + trace!("ConstProp skipped for {:?}: found unsatisfiable predicates", def_id); + return; + } + + trace!("ConstProp starting for {:?}", def_id); + + let dummy_body = &Body::new( + body.source, + body.basic_blocks().clone(), + body.source_scopes.clone(), + body.local_decls.clone(), + Default::default(), + body.arg_count, + Default::default(), + body.span, + body.generator_kind(), + body.tainted_by_errors, + ); + + // FIXME(oli-obk, eddyb) Optimize locals (or even local paths) to hold + // constants, instead of just checking for const-folding succeeding. + // That would require a uniform one-def no-mutation analysis + // and RPO (or recursing when needing the value of a local). + let mut optimization_finder = ConstPropagator::new(body, dummy_body, tcx); + optimization_finder.visit_body(body); + + trace!("ConstProp done for {:?}", def_id); + } +} + +struct ConstPropMachine<'mir, 'tcx> { + /// The virtual call stack. + stack: Vec<Frame<'mir, 'tcx>>, + /// `OnlyInsideOwnBlock` locals that were written in the current block get erased at the end. + written_only_inside_own_block_locals: FxHashSet<Local>, + /// Locals that need to be cleared after every block terminates. + only_propagate_inside_block_locals: BitSet<Local>, + can_const_prop: IndexVec<Local, ConstPropMode>, +} + +impl ConstPropMachine<'_, '_> { + fn new( + only_propagate_inside_block_locals: BitSet<Local>, + can_const_prop: IndexVec<Local, ConstPropMode>, + ) -> Self { + Self { + stack: Vec::new(), + written_only_inside_own_block_locals: Default::default(), + only_propagate_inside_block_locals, + can_const_prop, + } + } +} + +impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> { + compile_time_machine!(<'mir, 'tcx>); + const PANIC_ON_ALLOC_FAIL: bool = true; // all allocations are small (see `MAX_ALLOC_LIMIT`) + + type MemoryKind = !; + + type MemoryExtra = (); + + fn load_mir( + _ecx: &InterpCx<'mir, 'tcx, Self>, + _instance: ty::InstanceDef<'tcx>, + ) -> InterpResult<'tcx, &'tcx Body<'tcx>> { + throw_machine_stop_str!("calling functions isn't supported in ConstProp") + } + + fn find_mir_or_eval_fn( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _instance: ty::Instance<'tcx>, + _abi: Abi, + _args: &[OpTy<'tcx>], + _ret: Option<(&PlaceTy<'tcx>, BasicBlock)>, + _unwind: StackPopUnwind, + ) -> InterpResult<'tcx, Option<(&'mir Body<'tcx>, ty::Instance<'tcx>)>> { + Ok(None) + } + + fn call_intrinsic( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _instance: ty::Instance<'tcx>, + _args: &[OpTy<'tcx>], + _ret: Option<(&PlaceTy<'tcx>, BasicBlock)>, + _unwind: StackPopUnwind, + ) -> InterpResult<'tcx> { + throw_machine_stop_str!("calling intrinsics isn't supported in ConstProp") + } + + fn assert_panic( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _msg: &rustc_middle::mir::AssertMessage<'tcx>, + _unwind: Option<rustc_middle::mir::BasicBlock>, + ) -> InterpResult<'tcx> { + bug!("panics terminators are not evaluated in ConstProp") + } + + fn binary_ptr_op( + _ecx: &InterpCx<'mir, 'tcx, Self>, + _bin_op: BinOp, + _left: &ImmTy<'tcx>, + _right: &ImmTy<'tcx>, + ) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> { + // We can't do this because aliasing of memory can differ between const eval and llvm + throw_machine_stop_str!("pointer arithmetic or comparisons aren't supported in ConstProp") + } + + fn access_local( + _ecx: &InterpCx<'mir, 'tcx, Self>, + frame: &Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>, + local: Local, + ) -> InterpResult<'tcx, InterpOperand<Self::PointerTag>> { + let l = &frame.locals[local]; + + if l.value == LocalValue::Unallocated { + throw_machine_stop_str!("tried to access an uninitialized local") + } + + l.access() + } + + fn access_local_mut<'a>( + ecx: &'a mut InterpCx<'mir, 'tcx, Self>, + frame: usize, + local: Local, + ) -> InterpResult<'tcx, Result<&'a mut LocalValue<Self::PointerTag>, MemPlace<Self::PointerTag>>> + { + if ecx.machine.can_const_prop[local] == ConstPropMode::NoPropagation { + throw_machine_stop_str!("tried to write to a local that is marked as not propagatable") + } + if frame == 0 && ecx.machine.only_propagate_inside_block_locals.contains(local) { + trace!( + "mutating local {:?} which is restricted to its block. \ + Will remove it from const-prop after block is finished.", + local + ); + ecx.machine.written_only_inside_own_block_locals.insert(local); + } + ecx.machine.stack[frame].locals[local].access_mut() + } + + fn before_access_global( + _memory_extra: &(), + _alloc_id: AllocId, + alloc: ConstAllocation<'tcx, Self::PointerTag, Self::AllocExtra>, + _static_def_id: Option<DefId>, + is_write: bool, + ) -> InterpResult<'tcx> { + if is_write { + throw_machine_stop_str!("can't write to global"); + } + // If the static allocation is mutable, then we can't const prop it as its content + // might be different at runtime. + if alloc.inner().mutability == Mutability::Mut { + throw_machine_stop_str!("can't access mutable globals in ConstProp"); + } + + Ok(()) + } + + #[inline(always)] + fn init_frame_extra( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + frame: Frame<'mir, 'tcx>, + ) -> InterpResult<'tcx, Frame<'mir, 'tcx>> { + Ok(frame) + } + + #[inline(always)] + fn stack<'a>( + ecx: &'a InterpCx<'mir, 'tcx, Self>, + ) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>] { + &ecx.machine.stack + } + + #[inline(always)] + fn stack_mut<'a>( + ecx: &'a mut InterpCx<'mir, 'tcx, Self>, + ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>> { + &mut ecx.machine.stack + } +} + +/// Finds optimization opportunities on the MIR. +struct ConstPropagator<'mir, 'tcx> { + ecx: InterpCx<'mir, 'tcx, ConstPropMachine<'mir, 'tcx>>, + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + // FIXME(eddyb) avoid cloning these two fields more than once, + // by accessing them through `ecx` instead. + source_scopes: IndexVec<SourceScope, SourceScopeData<'tcx>>, + local_decls: IndexVec<Local, LocalDecl<'tcx>>, + // Because we have `MutVisitor` we can't obtain the `SourceInfo` from a `Location`. So we store + // the last known `SourceInfo` here and just keep revisiting it. + source_info: Option<SourceInfo>, +} + +impl<'tcx> LayoutOfHelpers<'tcx> for ConstPropagator<'_, 'tcx> { + type LayoutOfResult = Result<TyAndLayout<'tcx>, LayoutError<'tcx>>; + + #[inline] + fn handle_layout_err(&self, err: LayoutError<'tcx>, _: Span, _: Ty<'tcx>) -> LayoutError<'tcx> { + err + } +} + +impl HasDataLayout for ConstPropagator<'_, '_> { + #[inline] + fn data_layout(&self) -> &TargetDataLayout { + &self.tcx.data_layout + } +} + +impl<'tcx> ty::layout::HasTyCtxt<'tcx> for ConstPropagator<'_, 'tcx> { + #[inline] + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } +} + +impl<'tcx> ty::layout::HasParamEnv<'tcx> for ConstPropagator<'_, 'tcx> { + #[inline] + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.param_env + } +} + +impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { + fn new( + body: &Body<'tcx>, + dummy_body: &'mir Body<'tcx>, + tcx: TyCtxt<'tcx>, + ) -> ConstPropagator<'mir, 'tcx> { + let def_id = body.source.def_id(); + let substs = &InternalSubsts::identity_for_item(tcx, def_id); + let param_env = tcx.param_env_reveal_all_normalized(def_id); + + let span = tcx.def_span(def_id); + // FIXME: `CanConstProp::check` computes the layout of all locals, return those layouts + // so we can write them to `ecx.frame_mut().locals.layout, reducing the duplication in + // `layout_of` query invocations. + let can_const_prop = CanConstProp::check(tcx, param_env, body); + let mut only_propagate_inside_block_locals = BitSet::new_empty(can_const_prop.len()); + for (l, mode) in can_const_prop.iter_enumerated() { + if *mode == ConstPropMode::OnlyInsideOwnBlock { + only_propagate_inside_block_locals.insert(l); + } + } + let mut ecx = InterpCx::new( + tcx, + span, + param_env, + ConstPropMachine::new(only_propagate_inside_block_locals, can_const_prop), + (), + ); + + let ret = ecx + .layout_of(body.return_ty().subst(tcx, substs)) + .ok() + // Don't bother allocating memory for ZST types which have no values + // or for large values. + .filter(|ret_layout| { + !ret_layout.is_zst() && ret_layout.size < Size::from_bytes(MAX_ALLOC_LIMIT) + }) + .map(|ret_layout| { + ecx.allocate(ret_layout, MemoryKind::Stack) + .expect("couldn't perform small allocation") + .into() + }); + + ecx.push_stack_frame( + Instance::new(def_id, substs), + dummy_body, + ret.as_ref(), + StackPopCleanup::Root { cleanup: false }, + ) + .expect("failed to push initial stack frame"); + + ConstPropagator { + ecx, + tcx, + param_env, + // FIXME(eddyb) avoid cloning these two fields more than once, + // by accessing them through `ecx` instead. + source_scopes: body.source_scopes.clone(), + //FIXME(wesleywiser) we can't steal this because `Visitor::super_visit_body()` needs it + local_decls: body.local_decls.clone(), + source_info: None, + } + } + + fn get_const(&self, place: Place<'tcx>) -> Option<OpTy<'tcx>> { + let op = match self.ecx.eval_place_to_op(place, None) { + Ok(op) => op, + Err(e) => { + trace!("get_const failed: {}", e); + return None; + } + }; + + // Try to read the local as an immediate so that if it is representable as a scalar, we can + // handle it as such, but otherwise, just return the value as is. + Some(match self.ecx.try_read_immediate(&op) { + Ok(Ok(imm)) => imm.into(), + _ => op, + }) + } + + /// Remove `local` from the pool of `Locals`. Allows writing to them, + /// but not reading from them anymore. + fn remove_const(ecx: &mut InterpCx<'mir, 'tcx, ConstPropMachine<'mir, 'tcx>>, local: Local) { + ecx.frame_mut().locals[local] = + LocalState { value: LocalValue::Unallocated, layout: Cell::new(None) }; + } + + fn lint_root(&self, source_info: SourceInfo) -> Option<HirId> { + source_info.scope.lint_root(&self.source_scopes) + } + + fn use_ecx<F, T>(&mut self, f: F) -> Option<T> + where + F: FnOnce(&mut Self) -> InterpResult<'tcx, T>, + { + match f(self) { + Ok(val) => Some(val), + Err(error) => { + trace!("InterpCx operation failed: {:?}", error); + // Some errors shouldn't come up because creating them causes + // an allocation, which we should avoid. When that happens, + // dedicated error variants should be introduced instead. + assert!( + !error.kind().formatted_string(), + "const-prop encountered formatting error: {}", + error + ); + None + } + } + } + + /// Returns the value, if any, of evaluating `c`. + fn eval_constant(&mut self, c: &Constant<'tcx>, source_info: SourceInfo) -> Option<OpTy<'tcx>> { + // FIXME we need to revisit this for #67176 + if c.needs_subst() { + return None; + } + + match self.ecx.mir_const_to_op(&c.literal, None) { + Ok(op) => Some(op), + Err(error) => { + let tcx = self.ecx.tcx.at(c.span); + let err = ConstEvalErr::new(&self.ecx, error, Some(c.span)); + if let Some(lint_root) = self.lint_root(source_info) { + let lint_only = match c.literal { + ConstantKind::Ty(ct) => match ct.val() { + // Promoteds must lint and not error as the user didn't ask for them + ConstKind::Unevaluated(ty::Unevaluated { + def: _, + substs: _, + promoted: Some(_), + }) => true, + // Out of backwards compatibility we cannot report hard errors in unused + // generic functions using associated constants of the generic parameters. + _ => c.literal.needs_subst(), + }, + ConstantKind::Val(_, ty) => ty.needs_subst(), + }; + if lint_only { + // Out of backwards compatibility we cannot report hard errors in unused + // generic functions using associated constants of the generic parameters. + err.report_as_lint(tcx, "erroneous constant used", lint_root, Some(c.span)); + } else { + err.report_as_error(tcx, "erroneous constant used"); + } + } else { + err.report_as_error(tcx, "erroneous constant used"); + } + None + } + } + } + + /// Returns the value, if any, of evaluating `place`. + fn eval_place(&mut self, place: Place<'tcx>) -> Option<OpTy<'tcx>> { + trace!("eval_place(place={:?})", place); + self.use_ecx(|this| this.ecx.eval_place_to_op(place, None)) + } + + /// Returns the value, if any, of evaluating `op`. Calls upon `eval_constant` + /// or `eval_place`, depending on the variant of `Operand` used. + fn eval_operand(&mut self, op: &Operand<'tcx>, source_info: SourceInfo) -> Option<OpTy<'tcx>> { + match *op { + Operand::Constant(ref c) => self.eval_constant(c, source_info), + Operand::Move(place) | Operand::Copy(place) => self.eval_place(place), + } + } + + fn report_assert_as_lint( + &self, + lint: &'static lint::Lint, + source_info: SourceInfo, + message: &'static str, + panic: AssertKind<impl std::fmt::Debug>, + ) { + if let Some(lint_root) = self.lint_root(source_info) { + self.tcx.struct_span_lint_hir(lint, lint_root, source_info.span, |lint| { + let mut err = lint.build(message); + err.span_label(source_info.span, format!("{:?}", panic)); + err.emit(); + }); + } + } + + fn check_unary_op( + &mut self, + op: UnOp, + arg: &Operand<'tcx>, + source_info: SourceInfo, + ) -> Option<()> { + if let (val, true) = self.use_ecx(|this| { + let val = this.ecx.read_immediate(&this.ecx.eval_operand(arg, None)?)?; + let (_res, overflow, _ty) = this.ecx.overflowing_unary_op(op, &val)?; + Ok((val, overflow)) + })? { + // `AssertKind` only has an `OverflowNeg` variant, so make sure that is + // appropriate to use. + assert_eq!(op, UnOp::Neg, "Neg is the only UnOp that can overflow"); + self.report_assert_as_lint( + lint::builtin::ARITHMETIC_OVERFLOW, + source_info, + "this arithmetic operation will overflow", + AssertKind::OverflowNeg(val.to_const_int()), + ); + return None; + } + + Some(()) + } + + fn check_binary_op( + &mut self, + op: BinOp, + left: &Operand<'tcx>, + right: &Operand<'tcx>, + source_info: SourceInfo, + ) -> Option<()> { + let r = self.use_ecx(|this| this.ecx.read_immediate(&this.ecx.eval_operand(right, None)?)); + let l = self.use_ecx(|this| this.ecx.read_immediate(&this.ecx.eval_operand(left, None)?)); + // Check for exceeding shifts *even if* we cannot evaluate the LHS. + if op == BinOp::Shr || op == BinOp::Shl { + let r = r?; + // We need the type of the LHS. We cannot use `place_layout` as that is the type + // of the result, which for checked binops is not the same! + let left_ty = left.ty(&self.local_decls, self.tcx); + let left_size = self.ecx.layout_of(left_ty).ok()?.size; + let right_size = r.layout.size; + let r_bits = r.to_scalar().ok(); + let r_bits = r_bits.and_then(|r| r.to_bits(right_size).ok()); + if r_bits.map_or(false, |b| b >= left_size.bits() as u128) { + debug!("check_binary_op: reporting assert for {:?}", source_info); + self.report_assert_as_lint( + lint::builtin::ARITHMETIC_OVERFLOW, + source_info, + "this arithmetic operation will overflow", + AssertKind::Overflow( + op, + match l { + Some(l) => l.to_const_int(), + // Invent a dummy value, the diagnostic ignores it anyway + None => ConstInt::new( + ScalarInt::try_from_uint(1_u8, left_size).unwrap(), + left_ty.is_signed(), + left_ty.is_ptr_sized_integral(), + ), + }, + r.to_const_int(), + ), + ); + return None; + } + } + + if let (Some(l), Some(r)) = (&l, &r) { + // The remaining operators are handled through `overflowing_binary_op`. + if self.use_ecx(|this| { + let (_res, overflow, _ty) = this.ecx.overflowing_binary_op(op, l, r)?; + Ok(overflow) + })? { + self.report_assert_as_lint( + lint::builtin::ARITHMETIC_OVERFLOW, + source_info, + "this arithmetic operation will overflow", + AssertKind::Overflow(op, l.to_const_int(), r.to_const_int()), + ); + return None; + } + } + Some(()) + } + + fn const_prop( + &mut self, + rvalue: &Rvalue<'tcx>, + source_info: SourceInfo, + place: Place<'tcx>, + ) -> Option<()> { + // Perform any special handling for specific Rvalue types. + // Generally, checks here fall into one of two categories: + // 1. Additional checking to provide useful lints to the user + // - In this case, we will do some validation and then fall through to the + // end of the function which evals the assignment. + // 2. Working around bugs in other parts of the compiler + // - In this case, we'll return `None` from this function to stop evaluation. + match rvalue { + // Additional checking: give lints to the user if an overflow would occur. + // We do this here and not in the `Assert` terminator as that terminator is + // only sometimes emitted (overflow checks can be disabled), but we want to always + // lint. + Rvalue::UnaryOp(op, arg) => { + trace!("checking UnaryOp(op = {:?}, arg = {:?})", op, arg); + self.check_unary_op(*op, arg, source_info)?; + } + Rvalue::BinaryOp(op, box (left, right)) => { + trace!("checking BinaryOp(op = {:?}, left = {:?}, right = {:?})", op, left, right); + self.check_binary_op(*op, left, right, source_info)?; + } + Rvalue::CheckedBinaryOp(op, box (left, right)) => { + trace!( + "checking CheckedBinaryOp(op = {:?}, left = {:?}, right = {:?})", + op, + left, + right + ); + self.check_binary_op(*op, left, right, source_info)?; + } + + // Do not try creating references (#67862) + Rvalue::AddressOf(_, place) | Rvalue::Ref(_, _, place) => { + trace!("skipping AddressOf | Ref for {:?}", place); + + // This may be creating mutable references or immutable references to cells. + // If that happens, the pointed to value could be mutated via that reference. + // Since we aren't tracking references, the const propagator loses track of what + // value the local has right now. + // Thus, all locals that have their reference taken + // must not take part in propagation. + Self::remove_const(&mut self.ecx, place.local); + + return None; + } + Rvalue::ThreadLocalRef(def_id) => { + trace!("skipping ThreadLocalRef({:?})", def_id); + + return None; + } + + // There's no other checking to do at this time. + Rvalue::Aggregate(..) + | Rvalue::Use(..) + | Rvalue::Repeat(..) + | Rvalue::Len(..) + | Rvalue::Cast(..) + | Rvalue::ShallowInitBox(..) + | Rvalue::Discriminant(..) + | Rvalue::NullaryOp(..) => {} + } + + // FIXME we need to revisit this for #67176 + if rvalue.needs_subst() { + return None; + } + + self.use_ecx(|this| this.ecx.eval_rvalue_into_place(rvalue, place)) + } +} + +/// The mode that `ConstProp` is allowed to run in for a given `Local`. +#[derive(Clone, Copy, Debug, PartialEq)] +enum ConstPropMode { + /// The `Local` can be propagated into and reads of this `Local` can also be propagated. + FullConstProp, + /// The `Local` can only be propagated into and from its own block. + OnlyInsideOwnBlock, + /// The `Local` can be propagated into but reads cannot be propagated. + OnlyPropagateInto, + /// The `Local` cannot be part of propagation at all. Any statement + /// referencing it either for reading or writing will not get propagated. + NoPropagation, +} + +struct CanConstProp { + can_const_prop: IndexVec<Local, ConstPropMode>, + // False at the beginning. Once set, no more assignments are allowed to that local. + found_assignment: BitSet<Local>, + // Cache of locals' information + local_kinds: IndexVec<Local, LocalKind>, +} + +impl CanConstProp { + /// Returns true if `local` can be propagated + fn check<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + body: &Body<'tcx>, + ) -> IndexVec<Local, ConstPropMode> { + let mut cpv = CanConstProp { + can_const_prop: IndexVec::from_elem(ConstPropMode::FullConstProp, &body.local_decls), + found_assignment: BitSet::new_empty(body.local_decls.len()), + local_kinds: IndexVec::from_fn_n( + |local| body.local_kind(local), + body.local_decls.len(), + ), + }; + for (local, val) in cpv.can_const_prop.iter_enumerated_mut() { + let ty = body.local_decls[local].ty; + match tcx.layout_of(param_env.and(ty)) { + Ok(layout) if layout.size < Size::from_bytes(MAX_ALLOC_LIMIT) => {} + // Either the layout fails to compute, then we can't use this local anyway + // or the local is too large, then we don't want to. + _ => { + *val = ConstPropMode::NoPropagation; + continue; + } + } + // Cannot use args at all + // Cannot use locals because if x < y { y - x } else { x - y } would + // lint for x != y + // FIXME(oli-obk): lint variables until they are used in a condition + // FIXME(oli-obk): lint if return value is constant + if cpv.local_kinds[local] == LocalKind::Arg { + *val = ConstPropMode::OnlyPropagateInto; + trace!( + "local {:?} can't be const propagated because it's a function argument", + local + ); + } else if cpv.local_kinds[local] == LocalKind::Var { + *val = ConstPropMode::OnlyInsideOwnBlock; + trace!( + "local {:?} will only be propagated inside its block, because it's a user variable", + local + ); + } + } + cpv.visit_body(&body); + cpv.can_const_prop + } +} + +impl Visitor<'_> for CanConstProp { + fn visit_local(&mut self, &local: &Local, context: PlaceContext, _: Location) { + use rustc_middle::mir::visit::PlaceContext::*; + match context { + // Projections are fine, because `&mut foo.x` will be caught by + // `MutatingUseContext::Borrow` elsewhere. + MutatingUse(MutatingUseContext::Projection) + // These are just stores, where the storing is not propagatable, but there may be later + // mutations of the same local via `Store` + | MutatingUse(MutatingUseContext::Call) + | MutatingUse(MutatingUseContext::AsmOutput) + // Actual store that can possibly even propagate a value + | MutatingUse(MutatingUseContext::Store) => { + if !self.found_assignment.insert(local) { + match &mut self.can_const_prop[local] { + // If the local can only get propagated in its own block, then we don't have + // to worry about multiple assignments, as we'll nuke the const state at the + // end of the block anyway, and inside the block we overwrite previous + // states as applicable. + ConstPropMode::OnlyInsideOwnBlock => {} + ConstPropMode::NoPropagation => {} + ConstPropMode::OnlyPropagateInto => {} + other @ ConstPropMode::FullConstProp => { + trace!( + "local {:?} can't be propagated because of multiple assignments. Previous state: {:?}", + local, other, + ); + *other = ConstPropMode::OnlyInsideOwnBlock; + } + } + } + } + // Reading constants is allowed an arbitrary number of times + NonMutatingUse(NonMutatingUseContext::Copy) + | NonMutatingUse(NonMutatingUseContext::Move) + | NonMutatingUse(NonMutatingUseContext::Inspect) + | NonMutatingUse(NonMutatingUseContext::Projection) + | NonUse(_) => {} + + // These could be propagated with a smarter analysis or just some careful thinking about + // whether they'd be fine right now. + MutatingUse(MutatingUseContext::Yield) + | MutatingUse(MutatingUseContext::Drop) + | MutatingUse(MutatingUseContext::Retag) + // These can't ever be propagated under any scheme, as we can't reason about indirect + // mutation. + | NonMutatingUse(NonMutatingUseContext::SharedBorrow) + | NonMutatingUse(NonMutatingUseContext::ShallowBorrow) + | NonMutatingUse(NonMutatingUseContext::UniqueBorrow) + | NonMutatingUse(NonMutatingUseContext::AddressOf) + | MutatingUse(MutatingUseContext::Borrow) + | MutatingUse(MutatingUseContext::AddressOf) => { + trace!("local {:?} can't be propagaged because it's used: {:?}", local, context); + self.can_const_prop[local] = ConstPropMode::NoPropagation; + } + } + } +} + +impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> { + fn visit_body(&mut self, body: &Body<'tcx>) { + for (bb, data) in body.basic_blocks().iter_enumerated() { + self.visit_basic_block_data(bb, data); + } + } + + fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) { + self.super_operand(operand, location); + } + + fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) { + trace!("visit_constant: {:?}", constant); + self.super_constant(constant, location); + self.eval_constant(constant, self.source_info.unwrap()); + } + + fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { + trace!("visit_statement: {:?}", statement); + let source_info = statement.source_info; + self.source_info = Some(source_info); + if let StatementKind::Assign(box (place, ref rval)) = statement.kind { + let can_const_prop = self.ecx.machine.can_const_prop[place.local]; + if let Some(()) = self.const_prop(rval, source_info, place) { + match can_const_prop { + ConstPropMode::OnlyInsideOwnBlock => { + trace!( + "found local restricted to its block. \ + Will remove it from const-prop after block is finished. Local: {:?}", + place.local + ); + } + ConstPropMode::OnlyPropagateInto | ConstPropMode::NoPropagation => { + trace!("can't propagate into {:?}", place); + if place.local != RETURN_PLACE { + Self::remove_const(&mut self.ecx, place.local); + } + } + ConstPropMode::FullConstProp => {} + } + } else { + // Const prop failed, so erase the destination, ensuring that whatever happens + // from here on, does not know about the previous value. + // This is important in case we have + // ```rust + // let mut x = 42; + // x = SOME_MUTABLE_STATIC; + // // x must now be uninit + // ``` + // FIXME: we overzealously erase the entire local, because that's easier to + // implement. + trace!( + "propagation into {:?} failed. + Nuking the entire site from orbit, it's the only way to be sure", + place, + ); + Self::remove_const(&mut self.ecx, place.local); + } + } else { + match statement.kind { + StatementKind::SetDiscriminant { ref place, .. } => { + match self.ecx.machine.can_const_prop[place.local] { + ConstPropMode::FullConstProp | ConstPropMode::OnlyInsideOwnBlock => { + if self.use_ecx(|this| this.ecx.statement(statement)).is_some() { + trace!("propped discriminant into {:?}", place); + } else { + Self::remove_const(&mut self.ecx, place.local); + } + } + ConstPropMode::OnlyPropagateInto | ConstPropMode::NoPropagation => { + Self::remove_const(&mut self.ecx, place.local); + } + } + } + StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => { + let frame = self.ecx.frame_mut(); + frame.locals[local].value = + if let StatementKind::StorageLive(_) = statement.kind { + LocalValue::Unallocated + } else { + LocalValue::Dead + }; + } + _ => {} + } + } + + self.super_statement(statement, location); + } + + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { + let source_info = terminator.source_info; + self.source_info = Some(source_info); + self.super_terminator(terminator, location); + match &terminator.kind { + TerminatorKind::Assert { expected, ref msg, ref cond, .. } => { + if let Some(ref value) = self.eval_operand(&cond, source_info) { + trace!("assertion on {:?} should be {:?}", value, expected); + let expected = ScalarMaybeUninit::from(Scalar::from_bool(*expected)); + let value_const = self.ecx.read_scalar(&value).unwrap(); + if expected != value_const { + enum DbgVal<T> { + Val(T), + Underscore, + } + impl<T: std::fmt::Debug> std::fmt::Debug for DbgVal<T> { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Val(val) => val.fmt(fmt), + Self::Underscore => fmt.write_str("_"), + } + } + } + let mut eval_to_int = |op| { + // This can be `None` if the lhs wasn't const propagated and we just + // triggered the assert on the value of the rhs. + self.eval_operand(op, source_info).map_or(DbgVal::Underscore, |op| { + DbgVal::Val(self.ecx.read_immediate(&op).unwrap().to_const_int()) + }) + }; + let msg = match msg { + AssertKind::DivisionByZero(op) => { + Some(AssertKind::DivisionByZero(eval_to_int(op))) + } + AssertKind::RemainderByZero(op) => { + Some(AssertKind::RemainderByZero(eval_to_int(op))) + } + AssertKind::Overflow(bin_op @ (BinOp::Div | BinOp::Rem), op1, op2) => { + // Division overflow is *UB* in the MIR, and different than the + // other overflow checks. + Some(AssertKind::Overflow( + *bin_op, + eval_to_int(op1), + eval_to_int(op2), + )) + } + AssertKind::BoundsCheck { ref len, ref index } => { + let len = eval_to_int(len); + let index = eval_to_int(index); + Some(AssertKind::BoundsCheck { len, index }) + } + // Remaining overflow errors are already covered by checks on the binary operators. + AssertKind::Overflow(..) | AssertKind::OverflowNeg(_) => None, + // Need proper const propagator for these. + _ => None, + }; + // Poison all places this operand references so that further code + // doesn't use the invalid value + match cond { + Operand::Move(ref place) | Operand::Copy(ref place) => { + Self::remove_const(&mut self.ecx, place.local); + } + Operand::Constant(_) => {} + } + if let Some(msg) = msg { + self.report_assert_as_lint( + lint::builtin::UNCONDITIONAL_PANIC, + source_info, + "this operation will panic at runtime", + msg, + ); + } + } + } + } + // None of these have Operands to const-propagate. + TerminatorKind::Goto { .. } + | TerminatorKind::Resume + | TerminatorKind::Abort + | TerminatorKind::Return + | TerminatorKind::Unreachable + | TerminatorKind::Drop { .. } + | TerminatorKind::DropAndReplace { .. } + | TerminatorKind::Yield { .. } + | TerminatorKind::GeneratorDrop + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::SwitchInt { .. } + | TerminatorKind::Call { .. } + | TerminatorKind::InlineAsm { .. } => {} + } + + // We remove all Locals which are restricted in propagation to their containing blocks and + // which were modified in the current block. + // Take it out of the ecx so we can get a mutable reference to the ecx for `remove_const`. + let mut locals = std::mem::take(&mut self.ecx.machine.written_only_inside_own_block_locals); + for &local in locals.iter() { + Self::remove_const(&mut self.ecx, local); + } + locals.clear(); + // Put it back so we reuse the heap of the storage + self.ecx.machine.written_only_inside_own_block_locals = locals; + if cfg!(debug_assertions) { + // Ensure we are correctly erasing locals with the non-debug-assert logic. + for local in self.ecx.machine.only_propagate_inside_block_locals.iter() { + assert!( + self.get_const(local.into()).is_none() + || self + .layout_of(self.local_decls[local].ty) + .map_or(true, |layout| layout.is_zst()) + ) + } + } + } +} diff --git a/compiler/rustc_mir_transform/src/deaggregator.rs b/compiler/rustc_mir_transform/src/deaggregator.rs index 44753c5f631..616ba819982 100644 --- a/compiler/rustc_mir_transform/src/deaggregator.rs +++ b/compiler/rustc_mir_transform/src/deaggregator.rs @@ -6,6 +6,10 @@ use rustc_middle::ty::TyCtxt; pub struct Deaggregator; impl<'tcx> MirPass<'tcx> for Deaggregator { + fn phase_change(&self) -> Option<MirPhase> { + Some(MirPhase::Deaggregated) + } + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); let local_decls = &*local_decls; diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs index a4b1d86ff61..f78c7a084d8 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drops.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs @@ -20,7 +20,7 @@ pub struct ElaborateDrops; impl<'tcx> MirPass<'tcx> for ElaborateDrops { fn phase_change(&self) -> Option<MirPhase> { - Some(MirPhase::DropLowering) + Some(MirPhase::DropsLowered) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs index 8bc7f5e9ce2..ad96bf544cb 100644 --- a/compiler/rustc_mir_transform/src/generator.rs +++ b/compiler/rustc_mir_transform/src/generator.rs @@ -1235,7 +1235,7 @@ fn create_cases<'tcx>( impl<'tcx> MirPass<'tcx> for StateTransform { fn phase_change(&self) -> Option<MirPhase> { - Some(MirPhase::GeneratorLowering) + Some(MirPhase::GeneratorsLowered) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 3b2332a6e31..2fca498a125 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -49,6 +49,7 @@ pub mod cleanup_post_borrowck; mod const_debuginfo; mod const_goto; mod const_prop; +mod const_prop_lint; mod coverage; mod deaggregator; mod deduplicate_blocks; @@ -341,7 +342,7 @@ fn inner_mir_for_ctfe(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) - pm::run_passes( tcx, &mut body, - &[&const_prop::ConstProp, &marker::PhaseChange(MirPhase::Optimization)], + &[&const_prop::ConstProp, &marker::PhaseChange(MirPhase::Optimized)], ); } } @@ -398,7 +399,7 @@ fn mir_drops_elaborated_and_const_checked<'tcx>( } run_post_borrowck_cleanup_passes(tcx, &mut body); - assert!(body.phase == MirPhase::DropLowering); + assert!(body.phase == MirPhase::Deaggregated); tcx.alloc_steal_mir(body) } @@ -430,6 +431,7 @@ fn run_post_borrowck_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tc // `Deaggregator` is conceptually part of MIR building, some backends rely on it happening // and it can help optimizations. &deaggregator::Deaggregator, + &Lint(const_prop_lint::ConstProp), ]; pm::run_passes(tcx, body, post_borrowck_cleanup); @@ -458,7 +460,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { ], ); - assert!(body.phase == MirPhase::GeneratorLowering); + assert!(body.phase == MirPhase::GeneratorsLowered); // The main optimizations that we do on MIR. pm::run_passes( @@ -495,7 +497,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &deduplicate_blocks::DeduplicateBlocks, // Some cleanup necessary at least for LLVM and potentially other codegen backends. &add_call_guards::CriticalCallEdges, - &marker::PhaseChange(MirPhase::Optimization), + &marker::PhaseChange(MirPhase::Optimized), // Dump the end result for testing and debugging purposes. &dump_mir::Marker("PreCodegen"), ], diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs index 8725eae8709..740a2168b41 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -114,7 +114,7 @@ pub fn run_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, passes: &[&dyn } } - if validate || body.phase == MirPhase::Optimization { + if validate || body.phase == MirPhase::Optimized { validate_body(tcx, body, format!("end of phase transition to {:?}", body.phase)); } } diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 2c3ddae9cb4..34218e87b51 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -9,7 +9,7 @@ use crate::def_collector::collect_definitions; use crate::imports::{Import, ImportKind}; use crate::macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef}; use crate::Namespace::{self, MacroNS, TypeNS, ValueNS}; -use crate::{CrateLint, Determinacy, ExternPreludeEntry, Module, ModuleKind, ModuleOrUniformRoot}; +use crate::{Determinacy, ExternPreludeEntry, Finalize, Module, ModuleKind, ModuleOrUniformRoot}; use crate::{NameBinding, NameBindingKind, ParentScope, PathResult, PerNS, ResolutionError}; use crate::{Resolver, ResolverArenas, Segment, ToNameBinding, VisResolutionError}; @@ -235,16 +235,16 @@ impl<'a> AsMut<Resolver<'a>> for BuildReducedGraphVisitor<'a, '_> { impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { fn resolve_visibility(&mut self, vis: &ast::Visibility) -> ty::Visibility { - self.resolve_visibility_speculative(vis, false).unwrap_or_else(|err| { + self.try_resolve_visibility(vis, true).unwrap_or_else(|err| { self.r.report_vis_error(err); ty::Visibility::Public }) } - fn resolve_visibility_speculative<'ast>( + fn try_resolve_visibility<'ast>( &mut self, vis: &'ast ast::Visibility, - speculative: bool, + finalize: bool, ) -> Result<ty::Visibility, VisResolutionError<'ast>> { let parent_scope = &self.parent_scope; match vis.kind { @@ -296,13 +296,11 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { &segments, Some(TypeNS), parent_scope, - !speculative, - path.span, - CrateLint::SimplePath(id), + if finalize { Finalize::SimplePath(id, path.span) } else { Finalize::No }, ) { PathResult::Module(ModuleOrUniformRoot::Module(module)) => { let res = module.res().expect("visibility resolved to unnamed block"); - if !speculative { + if finalize { self.r.record_partial_res(id, PartialRes::new(res)); } if module.is_normal() { @@ -772,7 +770,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { // correct visibilities for unnamed field placeholders specifically, so the // constructor visibility should still be determined correctly. let field_vis = self - .resolve_visibility_speculative(&field.vis, true) + .try_resolve_visibility(&field.vis, false) .unwrap_or(ty::Visibility::Public); if ctor_vis.is_at_least(field_vis, &*self.r) { ctor_vis = field_vis; @@ -1131,8 +1129,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { ident, MacroNS, &self.parent_scope, - false, - ident.span, + None, ); if let Ok(binding) = result { let import = macro_use_import(self, ident.span); @@ -1272,9 +1269,9 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { let vis = match item.kind { // Visibilities must not be resolved non-speculatively twice // and we already resolved this one as a `fn` item visibility. - ItemKind::Fn(..) => self - .resolve_visibility_speculative(&item.vis, true) - .unwrap_or(ty::Visibility::Public), + ItemKind::Fn(..) => { + self.try_resolve_visibility(&item.vis, false).unwrap_or(ty::Visibility::Public) + } _ => self.resolve_visibility(&item.vis), }; if vis != ty::Visibility::Public { diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index e88b6a57376..d1685006881 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -24,10 +24,8 @@ use tracing::debug; use crate::imports::{Import, ImportKind, ImportResolver}; use crate::path_names_to_string; use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind}; -use crate::{ - BindingError, CrateLint, HasGenericParams, MacroRulesScope, Module, ModuleOrUniformRoot, -}; -use crate::{NameBinding, NameBindingKind, PrivacyError, VisResolutionError}; +use crate::{BindingError, HasGenericParams, MacroRulesScope, Module, ModuleOrUniformRoot}; +use crate::{Finalize, NameBinding, NameBindingKind, PrivacyError, VisResolutionError}; use crate::{ParentScope, PathResult, ResolutionError, Resolver, Scope, ScopeSet, Segment}; type Res = def::Res<ast::NodeId>; @@ -1076,9 +1074,8 @@ impl<'a> Resolver<'a> { ident, ScopeSet::All(ns, false), &parent_scope, + None, false, - false, - ident.span, ) { let desc = match binding.res() { Res::Def(DefKind::Macro(MacroKind::Bang), _) => { @@ -1405,10 +1402,10 @@ impl<'a, 'b> ImportResolver<'a, 'b> { _ => return None, } - self.make_missing_self_suggestion(span, path.clone(), parent_scope) - .or_else(|| self.make_missing_crate_suggestion(span, path.clone(), parent_scope)) - .or_else(|| self.make_missing_super_suggestion(span, path.clone(), parent_scope)) - .or_else(|| self.make_external_crate_suggestion(span, path, parent_scope)) + self.make_missing_self_suggestion(path.clone(), parent_scope) + .or_else(|| self.make_missing_crate_suggestion(path.clone(), parent_scope)) + .or_else(|| self.make_missing_super_suggestion(path.clone(), parent_scope)) + .or_else(|| self.make_external_crate_suggestion(path, parent_scope)) } /// Suggest a missing `self::` if that resolves to an correct module. @@ -1420,13 +1417,12 @@ impl<'a, 'b> ImportResolver<'a, 'b> { /// ``` fn make_missing_self_suggestion( &mut self, - span: Span, mut path: Vec<Segment>, parent_scope: &ParentScope<'b>, ) -> Option<(Vec<Segment>, Vec<String>)> { // Replace first ident with `self` and check if that is valid. path[0].ident.name = kw::SelfLower; - let result = self.r.resolve_path(&path, None, parent_scope, false, span, CrateLint::No); + let result = self.r.resolve_path(&path, None, parent_scope, Finalize::No); debug!("make_missing_self_suggestion: path={:?} result={:?}", path, result); if let PathResult::Module(..) = result { Some((path, Vec::new())) } else { None } } @@ -1440,13 +1436,12 @@ impl<'a, 'b> ImportResolver<'a, 'b> { /// ``` fn make_missing_crate_suggestion( &mut self, - span: Span, mut path: Vec<Segment>, parent_scope: &ParentScope<'b>, ) -> Option<(Vec<Segment>, Vec<String>)> { // Replace first ident with `crate` and check if that is valid. path[0].ident.name = kw::Crate; - let result = self.r.resolve_path(&path, None, parent_scope, false, span, CrateLint::No); + let result = self.r.resolve_path(&path, None, parent_scope, Finalize::No); debug!("make_missing_crate_suggestion: path={:?} result={:?}", path, result); if let PathResult::Module(..) = result { Some(( @@ -1472,13 +1467,12 @@ impl<'a, 'b> ImportResolver<'a, 'b> { /// ``` fn make_missing_super_suggestion( &mut self, - span: Span, mut path: Vec<Segment>, parent_scope: &ParentScope<'b>, ) -> Option<(Vec<Segment>, Vec<String>)> { // Replace first ident with `crate` and check if that is valid. path[0].ident.name = kw::Super; - let result = self.r.resolve_path(&path, None, parent_scope, false, span, CrateLint::No); + let result = self.r.resolve_path(&path, None, parent_scope, Finalize::No); debug!("make_missing_super_suggestion: path={:?} result={:?}", path, result); if let PathResult::Module(..) = result { Some((path, Vec::new())) } else { None } } @@ -1495,7 +1489,6 @@ impl<'a, 'b> ImportResolver<'a, 'b> { /// name as the first part of path. fn make_external_crate_suggestion( &mut self, - span: Span, mut path: Vec<Segment>, parent_scope: &ParentScope<'b>, ) -> Option<(Vec<Segment>, Vec<String>)> { @@ -1513,7 +1506,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { for name in extern_crate_names.into_iter() { // Replace first ident with a crate name and check if that is valid. path[0].ident.name = name; - let result = self.r.resolve_path(&path, None, parent_scope, false, span, CrateLint::No); + let result = self.r.resolve_path(&path, None, parent_scope, Finalize::No); debug!( "make_external_crate_suggestion: name={:?} path={:?} result={:?}", name, path, result diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 70ade7a5600..4f0dad13b5b 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -6,7 +6,7 @@ use crate::Namespace::{self, MacroNS, TypeNS}; use crate::{module_to_string, names_to_string}; use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind}; use crate::{BindingKey, ModuleKind, ResolutionError, Resolver, Segment}; -use crate::{CrateLint, Module, ModuleOrUniformRoot, ParentScope, PerNS, ScopeSet, Weak}; +use crate::{Finalize, Module, ModuleOrUniformRoot, ParentScope, PerNS, ScopeSet, Weak}; use crate::{NameBinding, NameBindingKind, PathResult, PrivacyError, ToNameBinding}; use rustc_ast::NodeId; @@ -123,10 +123,6 @@ impl<'a> Import<'a> { _ => false, } } - - crate fn crate_lint(&self) -> CrateLint { - CrateLint::UsePath { root_id: self.root_id, root_span: self.root_span } - } } #[derive(Clone, Default, Debug)] @@ -179,8 +175,7 @@ impl<'a> Resolver<'a> { ident: Ident, ns: Namespace, parent_scope: &ParentScope<'a>, - record_used: bool, - path_span: Span, + finalize: Option<Span>, ) -> Result<&'a NameBinding<'a>, Determinacy> { self.resolve_ident_in_module_unadjusted_ext( module, @@ -188,14 +183,13 @@ impl<'a> Resolver<'a> { ns, parent_scope, false, - record_used, - path_span, + finalize, ) .map_err(|(determinacy, _)| determinacy) } /// Attempts to resolve `ident` in namespaces `ns` of `module`. - /// Invariant: if `record_used` is `Some`, expansion and import resolution must be complete. + /// Invariant: if `finalize` is `Some`, expansion and import resolution must be complete. crate fn resolve_ident_in_module_unadjusted_ext( &mut self, module: ModuleOrUniformRoot<'a>, @@ -203,8 +197,7 @@ impl<'a> Resolver<'a> { ns: Namespace, parent_scope: &ParentScope<'a>, restricted_shadowing: bool, - record_used: bool, - path_span: Span, + finalize: Option<Span>, ) -> Result<&'a NameBinding<'a>, (Determinacy, Weak)> { let module = match module { ModuleOrUniformRoot::Module(module) => module, @@ -214,9 +207,8 @@ impl<'a> Resolver<'a> { ident, ScopeSet::AbsolutePath(ns), parent_scope, - record_used, - record_used, - path_span, + finalize, + finalize.is_some(), ); return binding.map_err(|determinacy| (determinacy, Weak::No)); } @@ -224,7 +216,7 @@ impl<'a> Resolver<'a> { assert!(!restricted_shadowing); return if ns != TypeNS { Err((Determined, Weak::No)) - } else if let Some(binding) = self.extern_prelude_get(ident, !record_used) { + } else if let Some(binding) = self.extern_prelude_get(ident, finalize.is_some()) { Ok(binding) } else if !self.graph_root.unexpanded_invocations.borrow().is_empty() { // Macro-expanded `extern crate` items can add names to extern prelude. @@ -254,9 +246,8 @@ impl<'a> Resolver<'a> { ident, scopes, parent_scope, - record_used, - record_used, - path_span, + finalize, + finalize.is_some(), ); return binding.map_err(|determinacy| (determinacy, Weak::No)); } @@ -266,7 +257,7 @@ impl<'a> Resolver<'a> { let resolution = self.resolution(module, key).try_borrow_mut().map_err(|_| (Determined, Weak::No))?; // This happens when there is a cycle of imports. - if let Some(binding) = resolution.binding { + if let Some(binding) = resolution.binding && let Some(path_span) = finalize { if !restricted_shadowing && binding.expansion != LocalExpnId::ROOT { if let NameBindingKind::Res(_, true) = binding.kind { self.macro_expanded_macro_export_errors.insert((path_span, binding.span)); @@ -284,7 +275,7 @@ impl<'a> Resolver<'a> { if usable { Ok(binding) } else { Err((Determined, Weak::No)) } }; - if record_used { + if let Some(path_span) = finalize { return resolution .binding .and_then(|binding| { @@ -357,14 +348,8 @@ impl<'a> Resolver<'a> { let ImportKind::Single { source: ident, .. } = single_import.kind else { unreachable!(); }; - match self.resolve_ident_in_module( - module, - ident, - ns, - &single_import.parent_scope, - false, - path_span, - ) { + match self.resolve_ident_in_module(module, ident, ns, &single_import.parent_scope, None) + { Err(Determined) => continue, Ok(binding) if !self.is_accessible_from(binding.vis, single_import.parent_scope.module) => @@ -438,8 +423,7 @@ impl<'a> Resolver<'a> { ident, ns, adjusted_parent_scope, - false, - path_span, + None, ); match result { @@ -787,14 +771,8 @@ impl<'a, 'b> ImportResolver<'a, 'b> { // For better failure detection, pretend that the import will // not define any names while resolving its module path. let orig_vis = import.vis.replace(ty::Visibility::Invisible); - let path_res = self.r.resolve_path( - &import.module_path, - None, - &import.parent_scope, - false, - import.span, - import.crate_lint(), - ); + let path_res = + self.r.resolve_path(&import.module_path, None, &import.parent_scope, Finalize::No); import.vis.set(orig_vis); match path_res { @@ -833,8 +811,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { source, ns, &import.parent_scope, - false, - import.span, + None, ); import.vis.set(orig_vis); source_bindings[ns].set(binding); @@ -887,14 +864,13 @@ impl<'a, 'b> ImportResolver<'a, 'b> { _ => None, }; let prev_ambiguity_errors_len = self.r.ambiguity_errors.len(); - let path_res = self.r.resolve_path( - &import.module_path, - None, - &import.parent_scope, - true, - import.span, - import.crate_lint(), - ); + let finalize = Finalize::UsePath { + root_id: import.root_id, + root_span: import.root_span, + path_span: import.span, + }; + let path_res = + self.r.resolve_path(&import.module_path, None, &import.parent_scope, finalize); let no_ambiguity = self.r.ambiguity_errors.len() == prev_ambiguity_errors_len; if let Some(orig_unusable_binding) = orig_unusable_binding { self.r.unusable_binding = orig_unusable_binding; @@ -981,12 +957,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { // 2 segments, so the `resolve_path` above won't trigger it. let mut full_path = import.module_path.clone(); full_path.push(Segment::from_ident(Ident::empty())); - self.r.lint_if_path_starts_with_module( - import.crate_lint(), - &full_path, - import.span, - None, - ); + self.r.lint_if_path_starts_with_module(finalize, &full_path, None); } if let ModuleOrUniformRoot::Module(module) = module { @@ -1024,8 +995,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { ident, ns, &import.parent_scope, - true, - import.span, + Some(import.span), ); this.last_import_segment = orig_last_import_segment; this.unusable_binding = orig_unusable_binding; @@ -1086,8 +1056,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { ident, ns, &import.parent_scope, - true, - import.span, + Some(import.span), ); if binding.is_ok() { all_ns_failed = false; @@ -1253,12 +1222,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { full_path.push(Segment::from_ident(ident)); self.r.per_ns(|this, ns| { if let Ok(binding) = source_bindings[ns].get() { - this.lint_if_path_starts_with_module( - import.crate_lint(), - &full_path, - import.span, - Some(binding), - ); + this.lint_if_path_starts_with_module(finalize, &full_path, Some(binding)); } }); } @@ -1314,9 +1278,8 @@ impl<'a, 'b> ImportResolver<'a, 'b> { target, ScopeSet::All(ns, false), &import.parent_scope, + None, false, - false, - import.span, ) { Ok(other_binding) => { is_redundant[ns] = Some( diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index d5b1aa00e52..bb05a3d7510 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -7,7 +7,7 @@ use RibKind::*; -use crate::{path_names_to_string, BindingError, CrateLint, LexicalScopeBinding}; +use crate::{path_names_to_string, BindingError, Finalize, LexicalScopeBinding}; use crate::{Module, ModuleOrUniformRoot, ParentScope, PathResult}; use crate::{ResolutionError, Resolver, Segment, UseError}; @@ -483,7 +483,11 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { TyKind::ImplicitSelf => { let self_ty = Ident::with_dummy_span(kw::SelfUpper); let res = self - .resolve_ident_in_lexical_scope(self_ty, TypeNS, Some(ty.id), ty.span) + .resolve_ident_in_lexical_scope( + self_ty, + TypeNS, + Finalize::SimplePath(ty.id, ty.span), + ) .map_or(Res::Err, |d| d.res()); self.r.record_partial_res(ty.id, PartialRes::new(res)); } @@ -675,8 +679,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { self.resolve_ident_in_lexical_scope( path.segments[0].ident, ns, - None, - path.span, + Finalize::No, ) .is_some() }; @@ -751,15 +754,13 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { &mut self, ident: Ident, ns: Namespace, - record_used_id: Option<NodeId>, - path_span: Span, + finalize: Finalize, ) -> Option<LexicalScopeBinding<'a>> { self.r.resolve_ident_in_lexical_scope( ident, ns, &self.parent_scope, - record_used_id, - path_span, + finalize, &self.ribs[ns], ) } @@ -768,19 +769,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { &mut self, path: &[Segment], opt_ns: Option<Namespace>, // `None` indicates a module path in import - record_used: bool, - path_span: Span, - crate_lint: CrateLint, + finalize: Finalize, ) -> PathResult<'a> { - self.r.resolve_path_with_ribs( - path, - opt_ns, - &self.parent_scope, - record_used, - path_span, - crate_lint, - Some(&self.ribs), - ) + self.r.resolve_path_with_ribs(path, opt_ns, &self.parent_scope, finalize, Some(&self.ribs)) } // AST resolution @@ -943,15 +934,15 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { }; for &ns in nss { - match self.resolve_ident_in_lexical_scope(ident, ns, None, use_tree.prefix.span) { + match self.resolve_ident_in_lexical_scope(ident, ns, Finalize::No) { Some(LexicalScopeBinding::Res(..)) => { report_error(self, ns); } Some(LexicalScopeBinding::Item(binding)) => { let orig_unusable_binding = replace(&mut self.r.unusable_binding, Some(binding)); - if let Some(LexicalScopeBinding::Res(..)) = self - .resolve_ident_in_lexical_scope(ident, ns, None, use_tree.prefix.span) + if let Some(LexicalScopeBinding::Res(..)) = + self.resolve_ident_in_lexical_scope(ident, ns, Finalize::No) { report_error(self, ns); } @@ -1246,25 +1237,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { if let Some(trait_ref) = opt_trait_ref { let path: Vec<_> = Segment::from_path(&trait_ref.path); let res = self.smart_resolve_path_fragment( - trait_ref.ref_id, None, &path, - trait_ref.path.span, PathSource::Trait(AliasPossibility::No), - CrateLint::SimplePath(trait_ref.ref_id), + Finalize::SimplePath(trait_ref.ref_id, trait_ref.path.span), ); - let res = res.base_res(); - if res != Res::Err { - if let PathResult::Module(ModuleOrUniformRoot::Module(module)) = self.resolve_path( - &path, - Some(TypeNS), - true, - trait_ref.path.span, - CrateLint::SimplePath(trait_ref.ref_id), - ) { - new_id = Some(res.def_id()); - new_val = Some((module, trait_ref.clone())); - } + if let Some(def_id) = res.base_res().opt_def_id() { + new_id = Some(def_id); + new_val = Some((self.r.expect_module(def_id), trait_ref.clone())); } } let original_trait_ref = replace(&mut self.current_trait_ref, new_val); @@ -1702,7 +1682,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // then fall back to a fresh binding. let has_sub = sub.is_some(); let res = self - .try_resolve_as_non_binding(pat_src, pat, bmode, ident, has_sub) + .try_resolve_as_non_binding(pat_src, bmode, ident, has_sub) .unwrap_or_else(|| self.fresh_binding(ident, pat.id, pat_src, bindings)); self.r.record_partial_res(pat.id, PartialRes::new(res)); self.r.record_pat_span(pat.id, pat.span); @@ -1813,7 +1793,6 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { fn try_resolve_as_non_binding( &mut self, pat_src: PatternSource, - pat: &Pat, bm: BindingMode, ident: Ident, has_sub: bool, @@ -1823,7 +1802,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // also be interpreted as a path to e.g. a constant, variant, etc. let is_syntactic_ambiguity = !has_sub && bm == BindingMode::ByValue(Mutability::Not); - let ls_binding = self.resolve_ident_in_lexical_scope(ident, ValueNS, None, pat.span)?; + let ls_binding = self.resolve_ident_in_lexical_scope(ident, ValueNS, Finalize::No)?; let (res, binding) = match ls_binding { LexicalScopeBinding::Item(binding) if is_syntactic_ambiguity && binding.is_ambiguity() => @@ -1912,35 +1891,34 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { source: PathSource<'ast>, ) { self.smart_resolve_path_fragment( - id, qself, &Segment::from_path(path), - path.span, source, - CrateLint::SimplePath(id), + Finalize::SimplePath(id, path.span), ); } fn smart_resolve_path_fragment( &mut self, - id: NodeId, qself: Option<&QSelf>, path: &[Segment], - span: Span, source: PathSource<'ast>, - crate_lint: CrateLint, + finalize: Finalize, ) -> PartialRes { tracing::debug!( - "smart_resolve_path_fragment(id={:?}, qself={:?}, path={:?})", - id, + "smart_resolve_path_fragment(qself={:?}, path={:?}, finalize={:?})", qself, - path + path, + finalize, ); let ns = source.namespace(); + let (id, path_span) = + finalize.node_id_and_path_span().expect("unexpected speculative resolution"); let report_errors = |this: &mut Self, res: Option<Res>| { if this.should_report_errs() { - let (err, candidates) = this.smart_resolve_report_errors(path, span, source, res); + let (err, candidates) = + this.smart_resolve_report_errors(path, path_span, source, res); let def_id = this.parent_scope.module.nearest_parent_mod(); let instead = res.is_some(); @@ -1978,7 +1956,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { }; let (mut err, candidates) = - this.smart_resolve_report_errors(path, span, PathSource::Type, None); + this.smart_resolve_report_errors(path, path_span, PathSource::Type, None); if candidates.is_empty() { err.cancel(); @@ -2027,13 +2005,12 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { }; let partial_res = match self.resolve_qpath_anywhere( - id, qself, path, ns, - span, + path_span, source.defer_to_typeck(), - crate_lint, + finalize, ) { Ok(Some(partial_res)) if partial_res.unresolved_segments() == 0 => { if source.is_expected(partial_res.base_res()) || partial_res.base_res() == Res::Err @@ -2060,14 +2037,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { std_path.push(Segment::from_ident(Ident::with_dummy_span(sym::std))); std_path.extend(path); if let PathResult::Module(_) | PathResult::NonModule(_) = - self.resolve_path(&std_path, Some(ns), false, span, CrateLint::No) + self.resolve_path(&std_path, Some(ns), Finalize::No) { // Check if we wrote `str::from_utf8` instead of `std::str::from_utf8` let item_span = - path.iter().last().map_or(span, |segment| segment.ident.span); + path.iter().last().map_or(path_span, |segment| segment.ident.span); - self.r.confused_type_with_std_module.insert(item_span, span); - self.r.confused_type_with_std_module.insert(span, span); + self.r.confused_type_with_std_module.insert(item_span, path_span); + self.r.confused_type_with_std_module.insert(path_span, path_span); } } @@ -2093,19 +2070,18 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { partial_res } - fn self_type_is_available(&mut self, span: Span) -> bool { + fn self_type_is_available(&mut self) -> bool { let binding = self.resolve_ident_in_lexical_scope( Ident::with_dummy_span(kw::SelfUpper), TypeNS, - None, - span, + Finalize::No, ); if let Some(LexicalScopeBinding::Res(res)) = binding { res != Res::Err } else { false } } - fn self_value_is_available(&mut self, self_span: Span, path_span: Span) -> bool { + fn self_value_is_available(&mut self, self_span: Span) -> bool { let ident = Ident::new(kw::SelfLower, self_span); - let binding = self.resolve_ident_in_lexical_scope(ident, ValueNS, None, path_span); + let binding = self.resolve_ident_in_lexical_scope(ident, ValueNS, Finalize::No); if let Some(LexicalScopeBinding::Res(res)) = binding { res != Res::Err } else { false } } @@ -2127,19 +2103,18 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // Resolve in alternative namespaces if resolution in the primary namespace fails. fn resolve_qpath_anywhere( &mut self, - id: NodeId, qself: Option<&QSelf>, path: &[Segment], primary_ns: Namespace, span: Span, defer_to_typeck: bool, - crate_lint: CrateLint, + finalize: Finalize, ) -> Result<Option<PartialRes>, Spanned<ResolutionError<'a>>> { let mut fin_res = None; for (i, &ns) in [primary_ns, TypeNS, ValueNS].iter().enumerate() { if i == 0 || ns != primary_ns { - match self.resolve_qpath(id, qself, path, ns, span, crate_lint)? { + match self.resolve_qpath(qself, path, ns, finalize)? { Some(partial_res) if partial_res.unresolved_segments() == 0 || defer_to_typeck => { @@ -2172,16 +2147,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { /// Handles paths that may refer to associated items. fn resolve_qpath( &mut self, - id: NodeId, qself: Option<&QSelf>, path: &[Segment], ns: Namespace, - span: Span, - crate_lint: CrateLint, + finalize: Finalize, ) -> Result<Option<PartialRes>, Spanned<ResolutionError<'a>>> { debug!( - "resolve_qpath(id={:?}, qself={:?}, path={:?}, ns={:?}, span={:?})", - id, qself, path, ns, span, + "resolve_qpath(qself={:?}, path={:?}, ns={:?}, finalize={:?})", + qself, path, ns, finalize, ); if let Some(qself) = qself { @@ -2208,15 +2181,15 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // *actually* appears, so for the purposes of the crate // lint we pass along information that this is the trait // name from a fully qualified path, and this also - // contains the full span (the `CrateLint::QPathTrait`). + // contains the full span (the `Finalize::QPathTrait`). let ns = if qself.position + 1 == path.len() { ns } else { TypeNS }; let partial_res = self.smart_resolve_path_fragment( - id, None, &path[..=qself.position], - span, PathSource::TraitItem(ns), - CrateLint::QPathTrait { qpath_id: id, qpath_span: qself.path_span }, + finalize.node_id_and_path_span().map_or(Finalize::No, |(qpath_id, path_span)| { + Finalize::QPathTrait { qpath_id, qpath_span: qself.path_span, path_span } + }), ); // The remaining segments (the `C` in our example) will @@ -2228,7 +2201,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { ))); } - let result = match self.resolve_path(&path, Some(ns), true, span, crate_lint) { + let result = match self.resolve_path(&path, Some(ns), finalize) { PathResult::NonModule(path_res) => path_res, PathResult::Module(ModuleOrUniformRoot::Module(module)) if !module.is_normal() => { PartialRes::new(module.res().unwrap()) @@ -2266,15 +2239,10 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { && result.base_res() != Res::Err && path[0].ident.name != kw::PathRoot && path[0].ident.name != kw::DollarCrate + && let Some((id, path_span)) = finalize.node_id_and_path_span() { let unqualified_result = { - match self.resolve_path( - &[*path.last().unwrap()], - Some(ns), - false, - span, - CrateLint::No, - ) { + match self.resolve_path(&[*path.last().unwrap()], Some(ns), Finalize::No) { PathResult::NonModule(path_res) => path_res.base_res(), PathResult::Module(ModuleOrUniformRoot::Module(module)) => { module.res().unwrap() @@ -2284,7 +2252,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { }; if result.base_res() == unqualified_result { let lint = lint::builtin::UNUSED_QUALIFICATIONS; - self.r.lint_buffer.buffer_lint(lint, id, span, "unnecessary qualification") + self.r.lint_buffer.buffer_lint(lint, id, path_span, "unnecessary qualification") } } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 1394f4083d0..038ba220608 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -2,7 +2,7 @@ use crate::diagnostics::{ImportSuggestion, LabelSuggestion, TypoSuggestion}; use crate::late::lifetimes::{ElisionFailureInfo, LifetimeContext}; use crate::late::{AliasPossibility, LateResolutionVisitor, RibKind}; use crate::path_names_to_string; -use crate::{CrateLint, Module, ModuleKind, ModuleOrUniformRoot}; +use crate::{Finalize, Module, ModuleKind, ModuleOrUniformRoot}; use crate::{PathResult, PathSource, Segment}; use rustc_ast::visit::FnKind; @@ -187,12 +187,11 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { (String::new(), "the crate root".to_string()) } else { let mod_path = &path[..path.len() - 1]; - let mod_prefix = - match self.resolve_path(mod_path, Some(TypeNS), false, span, CrateLint::No) { - PathResult::Module(ModuleOrUniformRoot::Module(module)) => module.res(), - _ => None, - } - .map_or_else(String::new, |res| format!("{} ", res.descr())); + let mod_prefix = match self.resolve_path(mod_path, Some(TypeNS), Finalize::No) { + PathResult::Module(ModuleOrUniformRoot::Module(module)) => module.res(), + _ => None, + } + .map_or_else(String::new, |res| format!("{} ", res.descr())); (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path))) }; ( @@ -232,7 +231,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { _ => {} } - let is_assoc_fn = self.self_type_is_available(span); + let is_assoc_fn = self.self_type_is_available(); // Emit help message for fake-self from other languages (e.g., `this` in Javascript). if ["this", "my"].contains(&item_str.as_str()) && is_assoc_fn { err.span_suggestion_short( @@ -241,7 +240,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { "self".to_string(), Applicability::MaybeIncorrect, ); - if !self.self_value_is_available(path[0].ident.span, span) { + if !self.self_value_is_available(path[0].ident.span) { if let Some((FnKind::Fn(_, _, sig, ..), fn_span)) = &self.diagnostic_metadata.current_function { @@ -402,9 +401,9 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { ); } } - if path.len() == 1 && self.self_type_is_available(span) { + if path.len() == 1 && self.self_type_is_available() { if let Some(candidate) = self.lookup_assoc_candidate(ident, ns, is_expected) { - let self_is_available = self.self_value_is_available(path[0].ident.span, span); + let self_is_available = self.self_value_is_available(path[0].ident.span); match candidate { AssocSuggestion::Field => { if self_is_available { @@ -461,7 +460,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } // Try Levenshtein algorithm. - let typo_sugg = self.lookup_typo_candidate(path, ns, is_expected, span); + let typo_sugg = self.lookup_typo_candidate(path, ns, is_expected); // Try context-dependent help if relaxed lookup didn't work. if let Some(res) = res { if self.smart_resolve_context_dependent_help( @@ -562,7 +561,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } // If the trait has a single item (which wasn't matched by Levenshtein), suggest it - let suggestion = self.get_single_associated_item(&path, span, &source, is_expected); + let suggestion = self.get_single_associated_item(&path, &source, is_expected); self.r.add_typo_suggestion(&mut err, suggestion, ident_span); } if fallback { @@ -641,14 +640,13 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { fn get_single_associated_item( &mut self, path: &[Segment], - span: Span, source: &PathSource<'_>, filter_fn: &impl Fn(Res) -> bool, ) -> Option<TypoSuggestion> { if let crate::PathSource::TraitItem(_) = source { let mod_path = &path[..path.len() - 1]; if let PathResult::Module(ModuleOrUniformRoot::Module(module)) = - self.resolve_path(mod_path, None, false, span, CrateLint::No) + self.resolve_path(mod_path, None, Finalize::No) { let resolutions = self.r.resolutions(module).borrow(); let targets: Vec<_> = @@ -699,13 +697,12 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { { // use this to verify that ident is a type param. let Ok(Some(partial_res)) = self.resolve_qpath_anywhere( - bounded_ty.id, None, &Segment::from_path(path), Namespace::TypeNS, span, true, - CrateLint::No, + Finalize::No, ) else { return false; }; @@ -724,13 +721,12 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { if let ast::TyKind::Path(None, type_param_path) = &ty.peel_refs().kind { // Confirm that the `SelfTy` is a type parameter. let Ok(Some(partial_res)) = self.resolve_qpath_anywhere( - bounded_ty.id, None, &Segment::from_path(type_param_path), Namespace::TypeNS, span, true, - CrateLint::No, + Finalize::No, ) else { return false; }; @@ -1292,8 +1288,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { ident, ns, &self.parent_scope, - false, - module.span, + None, ) { let res = binding.res(); if filter_fn(res) { @@ -1323,7 +1318,6 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { path: &[Segment], ns: Namespace, filter_fn: &impl Fn(Res) -> bool, - span: Span, ) -> Option<TypoSuggestion> { let mut names = Vec::new(); if path.len() == 1 { @@ -1384,7 +1378,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { // Search in module. let mod_path = &path[..path.len() - 1]; if let PathResult::Module(ModuleOrUniformRoot::Module(module)) = - self.resolve_path(mod_path, Some(TypeNS), false, span, CrateLint::No) + self.resolve_path(mod_path, Some(TypeNS), Finalize::No) { self.r.add_module_candidates(module, &mut names, &filter_fn); } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 77fd7115938..19eeae4cf23 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -13,6 +13,7 @@ #![feature(drain_filter)] #![feature(bool_to_option)] #![feature(crate_visibility_modifier)] +#![feature(let_chains)] #![feature(let_else)] #![feature(never_type)] #![feature(nll)] @@ -54,9 +55,9 @@ use rustc_index::vec::IndexVec; use rustc_metadata::creader::{CStore, CrateLoader}; use rustc_middle::metadata::ModChild; use rustc_middle::middle::privacy::AccessLevels; -use rustc_middle::span_bug; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, DefIdTree, MainDefinition, RegisteredTools, ResolverOutputs}; +use rustc_middle::{bug, span_bug}; use rustc_query_system::ich::StableHashingContext; use rustc_session::cstore::{CrateStore, MetadataLoaderDyn}; use rustc_session::lint; @@ -71,7 +72,7 @@ use rustc_span::{Span, DUMMY_SP}; use smallvec::{smallvec, SmallVec}; use std::cell::{Cell, RefCell}; use std::collections::BTreeSet; -use std::{cmp, fmt, iter, mem, ptr}; +use std::{cmp, fmt, mem, ptr}; use tracing::debug; use diagnostics::{extend_span_to_previous_binding, find_span_of_binding_until_next_binding}; @@ -449,6 +450,19 @@ enum PathResult<'a> { }, } +impl<'a> PathResult<'a> { + fn failed( + span: Span, + is_error_from_last_segment: bool, + finalize: bool, + label_and_suggestion: impl FnOnce() -> (String, Option<Suggestion>), + ) -> PathResult<'a> { + let (label, suggestion) = + if finalize { label_and_suggestion() } else { (String::new(), None) }; + PathResult::Failed { span, label, suggestion, is_error_from_last_segment } + } +} + #[derive(Debug)] enum ModuleKind { /// An anonymous module; e.g., just a block. @@ -1936,8 +1950,7 @@ impl<'a> Resolver<'a> { mut ident: Ident, ns: Namespace, parent_scope: &ParentScope<'a>, - record_used_id: Option<NodeId>, - path_span: Span, + finalize_full: Finalize, ribs: &[Rib<'a>], ) -> Option<LexicalScopeBinding<'a>> { assert!(ns == TypeNS || ns == ValueNS); @@ -1959,7 +1972,7 @@ impl<'a> Resolver<'a> { let normalized_ident = Ident { span: normalized_span, ..ident }; // Walk backwards up the ribs in scope. - let record_used = record_used_id.is_some(); + let finalize = finalize_full.path_span(); let mut module = self.graph_root; for i in (0..ribs.len()).rev() { debug!("walk rib\n{:?}", ribs[i].bindings); @@ -1973,8 +1986,7 @@ impl<'a> Resolver<'a> { i, rib_ident, *res, - record_used, - path_span, + finalize, *original_rib_ident_def, ribs, ))); @@ -2001,8 +2013,7 @@ impl<'a> Resolver<'a> { ident, ns, parent_scope, - record_used, - path_span, + finalize, ); if let Ok(binding) = item { // The ident resolves to an item. @@ -2011,11 +2022,10 @@ impl<'a> Resolver<'a> { } self.early_resolve_ident_in_lexical_scope( orig_ident, - ScopeSet::Late(ns, module, record_used_id), + ScopeSet::Late(ns, module, finalize_full.node_id()), parent_scope, - record_used, - record_used, - path_span, + finalize, + finalize.is_some(), ) .ok() .map(LexicalScopeBinding::Item) @@ -2075,10 +2085,9 @@ impl<'a> Resolver<'a> { ident: Ident, ns: Namespace, parent_scope: &ParentScope<'a>, - record_used: bool, - path_span: Span, + finalize: Option<Span>, ) -> Result<&'a NameBinding<'a>, Determinacy> { - self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, record_used, path_span) + self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, finalize) .map_err(|(determinacy, _)| determinacy) } @@ -2088,8 +2097,7 @@ impl<'a> Resolver<'a> { mut ident: Ident, ns: Namespace, parent_scope: &ParentScope<'a>, - record_used: bool, - path_span: Span, + finalize: Option<Span>, ) -> Result<&'a NameBinding<'a>, (Determinacy, Weak)> { let tmp_parent_scope; let mut adjusted_parent_scope = parent_scope; @@ -2114,8 +2122,7 @@ impl<'a> Resolver<'a> { ns, adjusted_parent_scope, false, - record_used, - path_span, + finalize, ) } @@ -2206,19 +2213,9 @@ impl<'a> Resolver<'a> { path: &[Segment], opt_ns: Option<Namespace>, // `None` indicates a module path in import parent_scope: &ParentScope<'a>, - record_used: bool, - path_span: Span, - crate_lint: CrateLint, + finalize: Finalize, ) -> PathResult<'a> { - self.resolve_path_with_ribs( - path, - opt_ns, - parent_scope, - record_used, - path_span, - crate_lint, - None, - ) + self.resolve_path_with_ribs(path, opt_ns, parent_scope, finalize, None) } fn resolve_path_with_ribs( @@ -2226,25 +2223,20 @@ impl<'a> Resolver<'a> { path: &[Segment], opt_ns: Option<Namespace>, // `None` indicates a module path in import parent_scope: &ParentScope<'a>, - record_used: bool, - path_span: Span, - crate_lint: CrateLint, + finalize_full: Finalize, ribs: Option<&PerNS<Vec<Rib<'a>>>>, ) -> PathResult<'a> { + debug!("resolve_path(path={:?}, opt_ns={:?}, finalize={:?})", path, opt_ns, finalize_full); + + let finalize = finalize_full.path_span(); let mut module = None; let mut allow_super = true; let mut second_binding = None; - debug!( - "resolve_path(path={:?}, opt_ns={:?}, record_used={:?}, \ - path_span={:?}, crate_lint={:?})", - path, opt_ns, record_used, path_span, crate_lint, - ); - for (i, &Segment { ident, id, has_generic_args: _ }) in path.iter().enumerate() { debug!("resolve_path ident {} {:?} {:?}", i, ident, id); let record_segment_res = |this: &mut Self, res| { - if record_used { + if finalize.is_some() { if let Some(id) = id { if !this.partial_res_map.contains_key(&id) { assert!(id != ast::DUMMY_NODE_ID, "Trying to resolve dummy id"); @@ -2278,13 +2270,9 @@ impl<'a> Resolver<'a> { continue; } } - let msg = "there are too many leading `super` keywords".to_string(); - return PathResult::Failed { - span: ident.span, - label: msg, - suggestion: None, - is_error_from_last_segment: false, - }; + return PathResult::failed(ident.span, false, finalize.is_some(), || { + ("there are too many leading `super` keywords".to_string(), None) + }); } if i == 0 { if name == kw::SelfLower { @@ -2313,22 +2301,19 @@ impl<'a> Resolver<'a> { // Report special messages for path segment keywords in wrong positions. if ident.is_path_segment_keyword() && i != 0 { - let name_str = if name == kw::PathRoot { - "crate root".to_string() - } else { - format!("`{}`", name) - }; - let label = if i == 1 && path[0].ident.name == kw::PathRoot { - format!("global paths cannot start with {}", name_str) - } else { - format!("{} in paths can only be used in start position", name_str) - }; - return PathResult::Failed { - span: ident.span, - label, - suggestion: None, - is_error_from_last_segment: false, - }; + return PathResult::failed(ident.span, false, finalize.is_some(), || { + let name_str = if name == kw::PathRoot { + "crate root".to_string() + } else { + format!("`{}`", name) + }; + let label = if i == 1 && path[0].ident.name == kw::PathRoot { + format!("global paths cannot start with {}", name_str) + } else { + format!("{} in paths can only be used in start position", name_str) + }; + (label, None) + }); } enum FindBindingResult<'a> { @@ -2337,36 +2322,22 @@ impl<'a> Resolver<'a> { } let find_binding_in_ns = |this: &mut Self, ns| { let binding = if let Some(module) = module { - this.resolve_ident_in_module( - module, - ident, - ns, - parent_scope, - record_used, - path_span, - ) + this.resolve_ident_in_module(module, ident, ns, parent_scope, finalize) } else if ribs.is_none() || opt_ns.is_none() || opt_ns == Some(MacroNS) { let scopes = ScopeSet::All(ns, opt_ns.is_none()); this.early_resolve_ident_in_lexical_scope( ident, scopes, parent_scope, - record_used, - record_used, - path_span, + finalize, + finalize.is_some(), ) } else { - let record_used_id = if record_used { - crate_lint.node_id().or(Some(CRATE_NODE_ID)) - } else { - None - }; match this.resolve_ident_in_lexical_scope( ident, ns, parent_scope, - record_used_id, - path_span, + finalize_full, &ribs.unwrap()[ns], ) { // we found a locally-imported or available item/module @@ -2380,7 +2351,7 @@ impl<'a> Resolver<'a> { PartialRes::with_unresolved_segments(res, path.len() - 1), )); } - _ => Err(Determinacy::determined(record_used)), + _ => Err(Determinacy::determined(finalize.is_some())), } }; FindBindingResult::Binding(binding) @@ -2414,30 +2385,20 @@ impl<'a> Resolver<'a> { } else if res == Res::Err { return PathResult::NonModule(PartialRes::new(Res::Err)); } else if opt_ns.is_some() && (is_last || maybe_assoc) { - self.lint_if_path_starts_with_module( - crate_lint, - path, - path_span, - second_binding, - ); + self.lint_if_path_starts_with_module(finalize_full, path, second_binding); return PathResult::NonModule(PartialRes::with_unresolved_segments( res, path.len() - i - 1, )); } else { - let label = format!( - "`{}` is {} {}, not a module", - ident, - res.article(), - res.descr(), - ); - - return PathResult::Failed { - span: ident.span, - label, - suggestion: None, - is_error_from_last_segment: is_last, - }; + return PathResult::failed(ident.span, is_last, finalize.is_some(), || { + let label = format!( + "`{ident}` is {} {}, not a module", + res.article(), + res.descr() + ); + (label, None) + }); } } Err(Undetermined) => return PathResult::Indeterminate, @@ -2450,196 +2411,192 @@ impl<'a> Resolver<'a> { )); } } - let module_res = match module { - Some(ModuleOrUniformRoot::Module(module)) => module.res(), - _ => None, - }; - let (label, suggestion) = if module_res == self.graph_root.res() { - let is_mod = |res| matches!(res, Res::Def(DefKind::Mod, _)); - // Don't look up import candidates if this is a speculative resolve - let mut candidates = if record_used { - self.lookup_import_candidates(ident, TypeNS, parent_scope, is_mod) - } else { - Vec::new() - }; - candidates.sort_by_cached_key(|c| { - (c.path.segments.len(), pprust::path_to_string(&c.path)) - }); - if let Some(candidate) = candidates.get(0) { - ( - String::from("unresolved import"), - Some(( - vec![(ident.span, pprust::path_to_string(&candidate.path))], - String::from("a similar path exists"), - Applicability::MaybeIncorrect, - )), - ) - } else if self.session.edition() == Edition::Edition2015 { - (format!("maybe a missing crate `{}`?", ident), None) - } else { - (format!("could not find `{}` in the crate root", ident), None) - } - } else if i == 0 { - if ident - .name - .as_str() - .chars() - .next() - .map_or(false, |c| c.is_ascii_uppercase()) - { - // Check whether the name refers to an item in the value namespace. - let suggestion = if ribs.is_some() { - let match_span = match self.resolve_ident_in_lexical_scope( - ident, - ValueNS, - parent_scope, - None, - path_span, - &ribs.unwrap()[ValueNS], - ) { - // Name matches a local variable. For example: - // ``` - // fn f() { - // let Foo: &str = ""; - // println!("{}", Foo::Bar); // Name refers to local - // // variable `Foo`. - // } - // ``` - Some(LexicalScopeBinding::Res(Res::Local(id))) => { - Some(*self.pat_span_map.get(&id).unwrap()) - } - // Name matches item from a local name binding - // created by `use` declaration. For example: - // ``` - // pub Foo: &str = ""; - // - // mod submod { - // use super::Foo; - // println!("{}", Foo::Bar); // Name refers to local - // // binding `Foo`. - // } - // ``` - Some(LexicalScopeBinding::Item(name_binding)) => { - Some(name_binding.span) - } - _ => None, - }; - - if let Some(span) = match_span { + return PathResult::failed(ident.span, is_last, finalize.is_some(), || { + let module_res = match module { + Some(ModuleOrUniformRoot::Module(module)) => module.res(), + _ => None, + }; + if module_res == self.graph_root.res() { + let is_mod = |res| matches!(res, Res::Def(DefKind::Mod, _)); + let mut candidates = + self.lookup_import_candidates(ident, TypeNS, parent_scope, is_mod); + candidates.sort_by_cached_key(|c| { + (c.path.segments.len(), pprust::path_to_string(&c.path)) + }); + if let Some(candidate) = candidates.get(0) { + ( + String::from("unresolved import"), Some(( - vec![(span, String::from(""))], - format!("`{}` is defined here, but is not a type", ident), + vec![(ident.span, pprust::path_to_string(&candidate.path))], + String::from("a similar path exists"), Applicability::MaybeIncorrect, - )) - } else { - None - } + )), + ) + } else if self.session.edition() == Edition::Edition2015 { + (format!("maybe a missing crate `{}`?", ident), None) } else { - None - }; + (format!("could not find `{}` in the crate root", ident), None) + } + } else if i == 0 { + if ident + .name + .as_str() + .chars() + .next() + .map_or(false, |c| c.is_ascii_uppercase()) + { + // Check whether the name refers to an item in the value namespace. + let suggestion = if ribs.is_some() { + let match_span = match self.resolve_ident_in_lexical_scope( + ident, + ValueNS, + parent_scope, + Finalize::No, + &ribs.unwrap()[ValueNS], + ) { + // Name matches a local variable. For example: + // ``` + // fn f() { + // let Foo: &str = ""; + // println!("{}", Foo::Bar); // Name refers to local + // // variable `Foo`. + // } + // ``` + Some(LexicalScopeBinding::Res(Res::Local(id))) => { + Some(*self.pat_span_map.get(&id).unwrap()) + } + + // Name matches item from a local name binding + // created by `use` declaration. For example: + // ``` + // pub Foo: &str = ""; + // + // mod submod { + // use super::Foo; + // println!("{}", Foo::Bar); // Name refers to local + // // binding `Foo`. + // } + // ``` + Some(LexicalScopeBinding::Item(name_binding)) => { + Some(name_binding.span) + } + _ => None, + }; - (format!("use of undeclared type `{}`", ident), suggestion) - } else { - ( - format!("use of undeclared crate or module `{}`", ident), - if ident.name == sym::alloc { - Some(( - vec![], - String::from( - "add `extern crate alloc` to use the `alloc` crate", - ), - Applicability::MaybeIncorrect, - )) + if let Some(span) = match_span { + Some(( + vec![(span, String::from(""))], + format!( + "`{}` is defined here, but is not a type", + ident + ), + Applicability::MaybeIncorrect, + )) + } else { + None + } } else { - self.find_similarly_named_module_or_crate( - ident.name, - &parent_scope.module, - ) - .map(|sugg| { - ( - vec![(ident.span, sugg.to_string())], + None + }; + + (format!("use of undeclared type `{}`", ident), suggestion) + } else { + ( + format!("use of undeclared crate or module `{}`", ident), + if ident.name == sym::alloc { + Some(( + vec![], String::from( - "there is a crate or module with a similar name", + "add `extern crate alloc` to use the `alloc` crate", ), Applicability::MaybeIncorrect, + )) + } else { + self.find_similarly_named_module_or_crate( + ident.name, + &parent_scope.module, ) - }) - }, - ) - } - } else { - let parent = path[i - 1].ident.name; - let parent = match parent { - // ::foo is mounted at the crate root for 2015, and is the extern - // prelude for 2018+ - kw::PathRoot if self.session.edition() > Edition::Edition2015 => { - "the list of imported crates".to_owned() - } - kw::PathRoot | kw::Crate => "the crate root".to_owned(), - _ => { - format!("`{}`", parent) + .map(|sugg| { + ( + vec![(ident.span, sugg.to_string())], + String::from( + "there is a crate or module with a similar name", + ), + Applicability::MaybeIncorrect, + ) + }) + }, + ) } - }; - - let mut msg = format!("could not find `{}` in {}", ident, parent); - if ns == TypeNS || ns == ValueNS { - let ns_to_try = if ns == TypeNS { ValueNS } else { TypeNS }; - if let FindBindingResult::Binding(Ok(binding)) = - find_binding_in_ns(self, ns_to_try) - { - let mut found = |what| { - msg = format!( - "expected {}, found {} `{}` in {}", - ns.descr(), - what, - ident, - parent - ) - }; - if binding.module().is_some() { - found("module") - } else { - match binding.res() { - def::Res::<NodeId>::Def(kind, id) => found(kind.descr(id)), - _ => found(ns_to_try.descr()), - } + } else { + let parent = path[i - 1].ident.name; + let parent = match parent { + // ::foo is mounted at the crate root for 2015, and is the extern + // prelude for 2018+ + kw::PathRoot if self.session.edition() > Edition::Edition2015 => { + "the list of imported crates".to_owned() + } + kw::PathRoot | kw::Crate => "the crate root".to_owned(), + _ => { + format!("`{}`", parent) } }; + + let mut msg = format!("could not find `{}` in {}", ident, parent); + if ns == TypeNS || ns == ValueNS { + let ns_to_try = if ns == TypeNS { ValueNS } else { TypeNS }; + if let FindBindingResult::Binding(Ok(binding)) = + find_binding_in_ns(self, ns_to_try) + { + let mut found = |what| { + msg = format!( + "expected {}, found {} `{}` in {}", + ns.descr(), + what, + ident, + parent + ) + }; + if binding.module().is_some() { + found("module") + } else { + match binding.res() { + def::Res::<NodeId>::Def(kind, id) => { + found(kind.descr(id)) + } + _ => found(ns_to_try.descr()), + } + } + }; + } + (msg, None) } - (msg, None) - }; - return PathResult::Failed { - span: ident.span, - label, - suggestion, - is_error_from_last_segment: is_last, - }; + }); } } } - self.lint_if_path_starts_with_module(crate_lint, path, path_span, second_binding); + self.lint_if_path_starts_with_module(finalize_full, path, second_binding); PathResult::Module(match module { Some(module) => module, None if path.is_empty() => ModuleOrUniformRoot::CurrentScope, - _ => span_bug!(path_span, "resolve_path: non-empty path `{:?}` has no module", path), + _ => bug!("resolve_path: non-empty path `{:?}` has no module", path), }) } fn lint_if_path_starts_with_module( &mut self, - crate_lint: CrateLint, + finalize: Finalize, path: &[Segment], - path_span: Span, second_binding: Option<&NameBinding<'_>>, ) { - let (diag_id, diag_span) = match crate_lint { - CrateLint::No => return, - CrateLint::SimplePath(id) => (id, path_span), - CrateLint::UsePath { root_id, root_span } => (root_id, root_span), - CrateLint::QPathTrait { qpath_id, qpath_span } => (qpath_id, qpath_span), + let (diag_id, diag_span) = match finalize { + Finalize::No => return, + Finalize::SimplePath(id, path_span) => (id, path_span), + Finalize::UsePath { root_id, root_span, .. } => (root_id, root_span), + Finalize::QPathTrait { qpath_id, qpath_span, .. } => (qpath_id, qpath_span), }; let first_name = match path.get(0) { @@ -2694,8 +2651,7 @@ impl<'a> Resolver<'a> { rib_index: usize, rib_ident: Ident, mut res: Res, - record_used: bool, - span: Span, + finalize: Option<Span>, original_rib_ident_def: Ident, all_ribs: &[Rib<'a>], ) -> Res { @@ -2705,7 +2661,7 @@ impl<'a> Resolver<'a> { // An invalid forward use of a generic parameter from a previous default. if let ForwardGenericParamBanRibKind = all_ribs[rib_index].kind { - if record_used { + if let Some(span) = finalize { let res_error = if rib_ident.name == kw::SelfUpper { ResolutionError::SelfInGenericParamDefault } else { @@ -2735,17 +2691,17 @@ impl<'a> Resolver<'a> { // This was an attempt to access an upvar inside a // named function item. This is not allowed, so we // report an error. - if record_used { + if let Some(span) = finalize { // We don't immediately trigger a resolve error, because // we want certain other resolution errors (namely those // emitted for `ConstantItemRibKind` below) to take // precedence. - res_err = Some(CannotCaptureDynamicEnvironmentInFnItem); + res_err = Some((span, CannotCaptureDynamicEnvironmentInFnItem)); } } ConstantItemRibKind(_, item) => { // Still doesn't deal with upvars - if record_used { + if let Some(span) = finalize { let (span, resolution_error) = if let Some((ident, constant_item_kind)) = item { let kind_str = match constant_item_kind { @@ -2773,14 +2729,14 @@ impl<'a> Resolver<'a> { return Res::Err; } ConstParamTyRibKind => { - if record_used { + if let Some(span) = finalize { self.report_error(span, ParamInTyOfConstParam(rib_ident.name)); } return Res::Err; } } } - if let Some(res_err) = res_err { + if let Some((span, res_err)) = res_err { self.report_error(span, res_err); return Res::Err; } @@ -2808,7 +2764,7 @@ impl<'a> Resolver<'a> { if let Res::SelfTy { trait_, alias_to: Some((def, _)) } = res { res = Res::SelfTy { trait_, alias_to: Some((def, true)) } } else { - if record_used { + if let Some(span) = finalize { self.report_error( span, ResolutionError::ParamInNonTrivialAnonConst { @@ -2816,9 +2772,9 @@ impl<'a> Resolver<'a> { is_type: true, }, ); + self.session.delay_span_bug(span, CG_BUG_STR); } - self.session.delay_span_bug(span, CG_BUG_STR); return Res::Err; } } @@ -2830,7 +2786,7 @@ impl<'a> Resolver<'a> { ItemRibKind(has_generic_params) => has_generic_params, FnItemRibKind => HasGenericParams::Yes, ConstParamTyRibKind => { - if record_used { + if let Some(span) = finalize { self.report_error( span, ResolutionError::ParamInTyOfConstParam(rib_ident.name), @@ -2840,7 +2796,7 @@ impl<'a> Resolver<'a> { } }; - if record_used { + if let Some(span) = finalize { self.report_error( span, ResolutionError::GenericParamsFromOuterFunction( @@ -2874,7 +2830,7 @@ impl<'a> Resolver<'a> { let features = self.session.features_untracked(); // HACK(min_const_generics): We currently only allow `N` or `{ N }`. if !(trivial || features.generic_const_exprs) { - if record_used { + if let Some(span) = finalize { self.report_error( span, ResolutionError::ParamInNonTrivialAnonConst { @@ -2882,9 +2838,9 @@ impl<'a> Resolver<'a> { is_type: false, }, ); + self.session.delay_span_bug(span, CG_BUG_STR); } - self.session.delay_span_bug(span, CG_BUG_STR); return Res::Err; } @@ -2894,7 +2850,7 @@ impl<'a> Resolver<'a> { ItemRibKind(has_generic_params) => has_generic_params, FnItemRibKind => HasGenericParams::Yes, ConstParamTyRibKind => { - if record_used { + if let Some(span) = finalize { self.report_error( span, ResolutionError::ParamInTyOfConstParam(rib_ident.name), @@ -2905,7 +2861,7 @@ impl<'a> Resolver<'a> { }; // This was an attempt to use a const parameter outside its scope. - if record_used { + if let Some(span) = finalize { self.report_error( span, ResolutionError::GenericParamsFromOuterFunction( @@ -3293,23 +3249,19 @@ impl<'a> Resolver<'a> { err.span_suggestion(span, message, String::new(), Applicability::MachineApplicable); } - fn extern_prelude_get( - &mut self, - ident: Ident, - speculative: bool, - ) -> Option<&'a NameBinding<'a>> { + fn extern_prelude_get(&mut self, ident: Ident, finalize: bool) -> Option<&'a NameBinding<'a>> { if ident.is_path_segment_keyword() { // Make sure `self`, `super` etc produce an error when passed to here. return None; } self.extern_prelude.get(&ident.normalize_to_macros_2_0()).cloned().and_then(|entry| { if let Some(binding) = entry.extern_crate_item { - if !speculative && entry.introduced_by_item { + if finalize && entry.introduced_by_item { self.record_use(ident, binding, false); } Some(binding) } else { - let crate_id = if !speculative { + let crate_id = if finalize { let Some(crate_id) = self.crate_loader.process_path_extern(ident.name, ident.span) else { return Some(self.dummy_binding); }; crate_id @@ -3325,83 +3277,38 @@ impl<'a> Resolver<'a> { }) } - /// Rustdoc uses this to resolve things in a recoverable way. `ResolutionError<'a>` + /// Rustdoc uses this to resolve doc link paths in a recoverable way. `PathResult<'a>` /// isn't something that can be returned because it can't be made to live that long, /// and also it's a private type. Fortunately rustdoc doesn't need to know the error, /// just that an error occurred. - // FIXME(Manishearth): intra-doc links won't get warned of epoch changes. - pub fn resolve_str_path_error( + pub fn resolve_rustdoc_path( &mut self, - span: Span, path_str: &str, ns: Namespace, module_id: DefId, - ) -> Result<(ast::Path, Res), ()> { - let path = if path_str.starts_with("::") { - ast::Path { - span, - segments: iter::once(Ident::with_dummy_span(kw::PathRoot)) - .chain(path_str.split("::").skip(1).map(Ident::from_str)) - .map(|i| self.new_ast_path_segment(i)) - .collect(), - tokens: None, - } - } else { - ast::Path { - span, - segments: path_str - .split("::") - .map(Ident::from_str) - .map(|i| self.new_ast_path_segment(i)) - .collect(), - tokens: None, - } - }; - let module = self.expect_module(module_id); - let parent_scope = &ParentScope::module(module, self); - let res = self.resolve_ast_path(&path, ns, parent_scope).map_err(|_| ())?; - Ok((path, res)) - } + ) -> Option<Res> { + let mut segments = + Vec::from_iter(path_str.split("::").map(Ident::from_str).map(Segment::from_ident)); + if path_str.starts_with("::") { + segments[0].ident.name = kw::PathRoot; + } - // Resolve a path passed from rustdoc or HIR lowering. - fn resolve_ast_path( - &mut self, - path: &ast::Path, - ns: Namespace, - parent_scope: &ParentScope<'a>, - ) -> Result<Res, (Span, ResolutionError<'a>)> { + let module = self.expect_module(module_id); match self.resolve_path( - &Segment::from_path(path), + &segments, Some(ns), - parent_scope, - false, - path.span, - CrateLint::No, + &ParentScope::module(module, self), + Finalize::No, ) { - PathResult::Module(ModuleOrUniformRoot::Module(module)) => Ok(module.res().unwrap()), + PathResult::Module(ModuleOrUniformRoot::Module(module)) => Some(module.res().unwrap()), PathResult::NonModule(path_res) if path_res.unresolved_segments() == 0 => { - Ok(path_res.base_res()) + Some(path_res.base_res()) } - PathResult::NonModule(..) => Err(( - path.span, - ResolutionError::FailedToResolve { - label: String::from("type-relative paths are not supported in this context"), - suggestion: None, - }, - )), + PathResult::NonModule(..) | PathResult::Failed { .. } => None, PathResult::Module(..) | PathResult::Indeterminate => unreachable!(), - PathResult::Failed { span, label, suggestion, .. } => { - Err((span, ResolutionError::FailedToResolve { label, suggestion })) - } } } - fn new_ast_path_segment(&mut self, ident: Ident) -> ast::PathSegment { - let mut seg = ast::PathSegment::from_ident(ident); - seg.id = self.next_node_id(); - seg - } - // For rustdoc. pub fn graph_root(&self) -> Module<'a> { self.graph_root @@ -3485,8 +3392,7 @@ impl<'a> Resolver<'a> { ident, ValueNS, parent_scope, - false, - DUMMY_SP, + None ) else { return; }; @@ -3544,35 +3450,43 @@ fn module_to_string(module: Module<'_>) -> Option<String> { } #[derive(Copy, Clone, Debug)] -enum CrateLint { +enum Finalize { /// Do not issue the lint. No, /// This lint applies to some arbitrary path; e.g., `impl ::foo::Bar`. /// In this case, we can take the span of that path. - SimplePath(NodeId), + SimplePath(NodeId, Span), /// This lint comes from a `use` statement. In this case, what we /// care about really is the *root* `use` statement; e.g., if we /// have nested things like `use a::{b, c}`, we care about the /// `use a` part. - UsePath { root_id: NodeId, root_span: Span }, + UsePath { root_id: NodeId, root_span: Span, path_span: Span }, /// This is the "trait item" from a fully qualified path. For example, /// we might be resolving `X::Y::Z` from a path like `<T as X::Y>::Z`. /// The `path_span` is the span of the to the trait itself (`X::Y`). - QPathTrait { qpath_id: NodeId, qpath_span: Span }, + QPathTrait { qpath_id: NodeId, qpath_span: Span, path_span: Span }, } -impl CrateLint { - fn node_id(&self) -> Option<NodeId> { +impl Finalize { + fn node_id_and_path_span(&self) -> Option<(NodeId, Span)> { match *self { - CrateLint::No => None, - CrateLint::SimplePath(id) - | CrateLint::UsePath { root_id: id, .. } - | CrateLint::QPathTrait { qpath_id: id, .. } => Some(id), + Finalize::No => None, + Finalize::SimplePath(id, path_span) + | Finalize::UsePath { root_id: id, path_span, .. } + | Finalize::QPathTrait { qpath_id: id, path_span, .. } => Some((id, path_span)), } } + + fn node_id(&self) -> Option<NodeId> { + self.node_id_and_path_span().map(|(id, _)| id) + } + + fn path_span(&self) -> Option<Span> { + self.node_id_and_path_span().map(|(_, path_span)| path_span) + } } pub fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index e34d3e605ec..dc94ba49a54 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -4,7 +4,7 @@ use crate::imports::ImportResolver; use crate::Namespace::*; use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, BuiltinMacroState, Determinacy}; -use crate::{CrateLint, DeriveData, ParentScope, ResolutionError, Resolver, Scope, ScopeSet, Weak}; +use crate::{DeriveData, Finalize, ParentScope, ResolutionError, Resolver, Scope, ScopeSet, Weak}; use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment, ToNameBinding}; use rustc_ast::{self as ast, Inline, ItemKind, ModKind, NodeId}; use rustc_ast_lowering::ResolverAstLowering; @@ -415,7 +415,7 @@ impl<'a> ResolverExpand for Resolver<'a> { let mut indeterminate = false; for ns in [TypeNS, ValueNS, MacroNS].iter().copied() { - match self.resolve_path(path, Some(ns), &parent_scope, false, span, CrateLint::No) { + match self.resolve_path(path, Some(ns), &parent_scope, Finalize::No) { PathResult::Module(ModuleOrUniformRoot::Module(_)) => return Ok(true), PathResult::NonModule(partial_res) if partial_res.unresolved_segments() == 0 => { return Ok(true); @@ -575,14 +575,7 @@ impl<'a> Resolver<'a> { } let res = if path.len() > 1 { - let res = match self.resolve_path( - &path, - Some(MacroNS), - parent_scope, - false, - path_span, - CrateLint::No, - ) { + let res = match self.resolve_path(&path, Some(MacroNS), parent_scope, Finalize::No) { PathResult::NonModule(path_res) if path_res.unresolved_segments() == 0 => { Ok(path_res.base_res()) } @@ -612,9 +605,8 @@ impl<'a> Resolver<'a> { path[0].ident, scope_set, parent_scope, - false, + None, force, - path_span, ); if let Err(Determinacy::Undetermined) = binding { return Err(Determinacy::Undetermined); @@ -648,9 +640,8 @@ impl<'a> Resolver<'a> { orig_ident: Ident, scope_set: ScopeSet<'a>, parent_scope: &ParentScope<'a>, - record_used: bool, + finalize: Option<Span>, force: bool, - path_span: Span, ) -> Result<&'a NameBinding<'a>, Determinacy> { bitflags::bitflags! { struct Flags: u8 { @@ -662,7 +653,7 @@ impl<'a> Resolver<'a> { } } - assert!(force || !record_used); // `record_used` implies `force` + assert!(force || !finalize.is_some()); // `finalize` implies `force` // Make sure `self`, `super` etc produce an error when passed to here. if orig_ident.is_path_segment_keyword() { @@ -769,8 +760,7 @@ impl<'a> Resolver<'a> { ident, ns, parent_scope, - record_used, - path_span, + finalize, ); match binding { Ok(binding) => Ok((binding, Flags::MODULE | Flags::MISC_SUGGEST_CRATE)), @@ -791,8 +781,7 @@ impl<'a> Resolver<'a> { ns, adjusted_parent_scope, !matches!(scope_set, ScopeSet::Late(..)), - record_used, - path_span, + finalize, ); match binding { Ok(binding) => { @@ -856,12 +845,14 @@ impl<'a> Resolver<'a> { Err(Determinacy::Determined) } } - Scope::ExternPrelude => match this.extern_prelude_get(ident, !record_used) { - Some(binding) => Ok((binding, Flags::empty())), - None => Err(Determinacy::determined( - this.graph_root.unexpanded_invocations.borrow().is_empty(), - )), - }, + Scope::ExternPrelude => { + match this.extern_prelude_get(ident, finalize.is_some()) { + Some(binding) => Ok((binding, Flags::empty())), + None => Err(Determinacy::determined( + this.graph_root.unexpanded_invocations.borrow().is_empty(), + )), + } + } Scope::ToolPrelude => match this.registered_tools.get(&ident).cloned() { Some(ident) => ok(Res::ToolMod, ident.span, this.arenas), None => Err(Determinacy::Determined), @@ -874,8 +865,7 @@ impl<'a> Resolver<'a> { ident, ns, parent_scope, - false, - path_span, + None, ) { if use_prelude || this.is_builtin_macro(binding.res()) { result = Ok((binding, Flags::MISC_FROM_PRELUDE)); @@ -894,7 +884,7 @@ impl<'a> Resolver<'a> { Ok((binding, flags)) if sub_namespace_match(binding.macro_kind(), macro_kind) => { - if !record_used || matches!(scope_set, ScopeSet::Late(..)) { + if finalize.is_none() || matches!(scope_set, ScopeSet::Late(..)) { return Some(Ok(binding)); } @@ -1033,9 +1023,7 @@ impl<'a> Resolver<'a> { &path, Some(MacroNS), &parent_scope, - true, - path_span, - CrateLint::No, + Finalize::SimplePath(ast::CRATE_NODE_ID, path_span), ) { PathResult::NonModule(path_res) if path_res.unresolved_segments() == 0 => { let res = path_res.base_res(); @@ -1069,9 +1057,8 @@ impl<'a> Resolver<'a> { ident, ScopeSet::Macro(kind), &parent_scope, + Some(ident.span), true, - true, - ident.span, ) { Ok(binding) => { let initial_res = initial_binding.map(|initial_binding| { @@ -1111,9 +1098,8 @@ impl<'a> Resolver<'a> { ident, ScopeSet::Macro(MacroKind::Attr), &parent_scope, + Some(ident.span), true, - true, - ident.span, ); } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 48ca321b737..5cf362bfa7e 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1347,6 +1347,10 @@ symbols! { store, str, str_alloc, + str_split_whitespace, + str_trim, + str_trim_end, + str_trim_start, stringify, stringify_macro, struct_field_attributes, diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 94a4001bbb9..ec4f8084e21 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -7,7 +7,7 @@ use crate::infer::outlives::env::OutlivesEnvironment; use crate::infer::{CombinedSnapshot, InferOk, RegionckMode}; use crate::traits::select::IntercrateAmbiguityCause; -use crate::traits::util::impl_trait_ref_and_oblig; +use crate::traits::util::impl_subject_and_oblig; use crate::traits::SkipLeakCheck; use crate::traits::{ self, FulfillmentContext, Normalized, Obligation, ObligationCause, PredicateObligation, @@ -23,9 +23,10 @@ use rustc_middle::traits::specialization_graph::OverlapMode; use rustc_middle::ty::fast_reject::{self, TreatParams}; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::subst::Subst; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt}; use rustc_span::symbol::sym; use rustc_span::DUMMY_SP; +use std::fmt::Debug; use std::iter; /// Whether we do the orphan check relative to this crate or @@ -300,60 +301,62 @@ fn negative_impl<'cx, 'tcx>( debug!("negative_impl(impl1_def_id={:?}, impl2_def_id={:?})", impl1_def_id, impl2_def_id); let tcx = selcx.infcx().tcx; - // create a parameter environment corresponding to a (placeholder) instantiation of impl1 - let impl1_env = tcx.param_env(impl1_def_id); - let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id).unwrap(); - // Create an infcx, taking the predicates of impl1 as assumptions: tcx.infer_ctxt().enter(|infcx| { - // Normalize the trait reference. The WF rules ought to ensure - // that this always succeeds. - let impl1_trait_ref = match traits::fully_normalize( + // create a parameter environment corresponding to a (placeholder) instantiation of impl1 + let impl_env = tcx.param_env(impl1_def_id); + let subject1 = match traits::fully_normalize( &infcx, FulfillmentContext::new(), ObligationCause::dummy(), - impl1_env, - impl1_trait_ref, + impl_env, + tcx.impl_subject(impl1_def_id), ) { - Ok(impl1_trait_ref) => impl1_trait_ref, - Err(err) => { - bug!("failed to fully normalize {:?}: {:?}", impl1_trait_ref, err); - } + Ok(s) => s, + Err(err) => bug!("failed to fully normalize {:?}: {:?}", impl1_def_id, err), }; // Attempt to prove that impl2 applies, given all of the above. let selcx = &mut SelectionContext::new(&infcx); let impl2_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl2_def_id); - let (impl2_trait_ref, obligations) = - impl_trait_ref_and_oblig(selcx, impl1_env, impl2_def_id, impl2_substs); - - // do the impls unify? If not, not disjoint. - let Ok(InferOk { obligations: more_obligations, .. }) = infcx - .at(&ObligationCause::dummy(), impl1_env) - .eq(impl1_trait_ref, impl2_trait_ref) - else { - debug!( - "explicit_disjoint: {:?} does not unify with {:?}", - impl1_trait_ref, impl2_trait_ref - ); - return false; - }; + let (subject2, obligations) = + impl_subject_and_oblig(selcx, impl_env, impl2_def_id, impl2_substs); - let opt_failing_obligation = obligations - .into_iter() - .chain(more_obligations) - .find(|o| negative_impl_exists(selcx, impl1_env, impl1_def_id, o)); - - if let Some(failing_obligation) = opt_failing_obligation { - debug!("overlap: obligation unsatisfiable {:?}", failing_obligation); - true - } else { - false - } + !equate(&infcx, impl_env, impl1_def_id, subject1, subject2, obligations) }) } -/// Try to prove that a negative impl exist for the given obligation and their super predicates. +fn equate<'cx, 'tcx>( + infcx: &InferCtxt<'cx, 'tcx>, + impl_env: ty::ParamEnv<'tcx>, + impl1_def_id: DefId, + subject1: ImplSubject<'tcx>, + subject2: ImplSubject<'tcx>, + obligations: impl Iterator<Item = PredicateObligation<'tcx>>, +) -> bool { + // do the impls unify? If not, not disjoint. + let Ok(InferOk { obligations: more_obligations, .. }) = + infcx.at(&ObligationCause::dummy(), impl_env).eq(subject1, subject2) + else { + debug!("explicit_disjoint: {:?} does not unify with {:?}", subject1, subject2); + return true; + }; + + let selcx = &mut SelectionContext::new(&infcx); + let opt_failing_obligation = obligations + .into_iter() + .chain(more_obligations) + .find(|o| negative_impl_exists(selcx, impl_env, impl1_def_id, o)); + + if let Some(failing_obligation) = opt_failing_obligation { + debug!("overlap: obligation unsatisfiable {:?}", failing_obligation); + false + } else { + true + } +} + +/// Try to prove that a negative impl exist for the given obligation and its super predicates. #[instrument(level = "debug", skip(selcx))] fn negative_impl_exists<'cx, 'tcx>( selcx: &SelectionContext<'cx, 'tcx>, @@ -367,7 +370,7 @@ fn negative_impl_exists<'cx, 'tcx>( return true; } - // Try to prove a negative obligation exist for super predicates + // Try to prove a negative obligation exists for super predicates for o in util::elaborate_predicates(infcx.tcx, iter::once(o.predicate)) { if resolve_negative_obligation(infcx, param_env, region_context, &o) { return true; diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 490c04e7be2..959b644becd 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -188,6 +188,7 @@ pub fn is_const_evaluatable<'cx, 'tcx>( } } +#[instrument(skip(tcx), level = "debug")] fn satisfied_from_param_env<'tcx>( tcx: TyCtxt<'tcx>, ct: AbstractConst<'tcx>, @@ -197,14 +198,17 @@ fn satisfied_from_param_env<'tcx>( match pred.kind().skip_binder() { ty::PredicateKind::ConstEvaluatable(uv) => { if let Some(b_ct) = AbstractConst::new(tcx, uv)? { + let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env }; + // Try to unify with each subtree in the AbstractConst to allow for // `N + 1` being const evaluatable even if theres only a `ConstEvaluatable` // predicate for `(N + 1) * 2` - let result = - walk_abstract_const(tcx, b_ct, |b_ct| match try_unify(tcx, ct, b_ct) { + let result = walk_abstract_const(tcx, b_ct, |b_ct| { + match const_unify_ctxt.try_unify(ct, b_ct) { true => ControlFlow::BREAK, false => ControlFlow::CONTINUE, - }); + } + }); if let ControlFlow::Break(()) = result { debug!("is_const_evaluatable: abstract_const ~~> ok"); @@ -637,11 +641,13 @@ pub(super) fn thir_abstract_const<'tcx>( pub(super) fn try_unify_abstract_consts<'tcx>( tcx: TyCtxt<'tcx>, (a, b): (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>), + param_env: ty::ParamEnv<'tcx>, ) -> bool { (|| { if let Some(a) = AbstractConst::new(tcx, a)? { if let Some(b) = AbstractConst::new(tcx, b)? { - return Ok(try_unify(tcx, a, b)); + let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env }; + return Ok(const_unify_ctxt.try_unify(a, b)); } } @@ -689,88 +695,115 @@ where recurse(tcx, ct, &mut f) } -/// Tries to unify two abstract constants using structural equality. -pub(super) fn try_unify<'tcx>( +struct ConstUnifyCtxt<'tcx> { tcx: TyCtxt<'tcx>, - mut a: AbstractConst<'tcx>, - mut b: AbstractConst<'tcx>, -) -> bool { - // We substitute generics repeatedly to allow AbstractConsts to unify where a + param_env: ty::ParamEnv<'tcx>, +} + +impl<'tcx> ConstUnifyCtxt<'tcx> { + // Substitutes generics repeatedly to allow AbstractConsts to unify where a // ConstKind::Unevalated could be turned into an AbstractConst that would unify e.g. // Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])] - while let Node::Leaf(a_ct) = a.root(tcx) { - match AbstractConst::from_const(tcx, a_ct) { - Ok(Some(a_act)) => a = a_act, - Ok(None) => break, - Err(_) => return true, - } - } - while let Node::Leaf(b_ct) = b.root(tcx) { - match AbstractConst::from_const(tcx, b_ct) { - Ok(Some(b_act)) => b = b_act, - Ok(None) => break, - Err(_) => return true, + #[inline] + #[instrument(skip(self), level = "debug")] + fn try_replace_substs_in_root( + &self, + mut abstr_const: AbstractConst<'tcx>, + ) -> Option<AbstractConst<'tcx>> { + while let Node::Leaf(ct) = abstr_const.root(self.tcx) { + match AbstractConst::from_const(self.tcx, ct) { + Ok(Some(act)) => abstr_const = act, + Ok(None) => break, + Err(_) => return None, + } } - } - match (a.root(tcx), b.root(tcx)) { - (Node::Leaf(a_ct), Node::Leaf(b_ct)) => { - if a_ct.ty() != b_ct.ty() { - return false; - } + Some(abstr_const) + } - match (a_ct.val(), b_ct.val()) { - // We can just unify errors with everything to reduce the amount of - // emitted errors here. - (ty::ConstKind::Error(_), _) | (_, ty::ConstKind::Error(_)) => true, - (ty::ConstKind::Param(a_param), ty::ConstKind::Param(b_param)) => { - a_param == b_param + /// Tries to unify two abstract constants using structural equality. + #[instrument(skip(self), level = "debug")] + fn try_unify(&self, a: AbstractConst<'tcx>, b: AbstractConst<'tcx>) -> bool { + let a = if let Some(a) = self.try_replace_substs_in_root(a) { + a + } else { + return true; + }; + + let b = if let Some(b) = self.try_replace_substs_in_root(b) { + b + } else { + return true; + }; + + let a_root = a.root(self.tcx); + let b_root = b.root(self.tcx); + debug!(?a_root, ?b_root); + + match (a_root, b_root) { + (Node::Leaf(a_ct), Node::Leaf(b_ct)) => { + let a_ct = a_ct.eval(self.tcx, self.param_env); + debug!("a_ct evaluated: {:?}", a_ct); + let b_ct = b_ct.eval(self.tcx, self.param_env); + debug!("b_ct evaluated: {:?}", b_ct); + + if a_ct.ty() != b_ct.ty() { + return false; } - (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val, - // If we have `fn a<const N: usize>() -> [u8; N + 1]` and `fn b<const M: usize>() -> [u8; 1 + M]` - // we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This - // means that we only allow inference variables if they are equal. - (ty::ConstKind::Infer(a_val), ty::ConstKind::Infer(b_val)) => a_val == b_val, - // We expand generic anonymous constants at the start of this function, so this - // branch should only be taking when dealing with associated constants, at - // which point directly comparing them seems like the desired behavior. - // - // FIXME(generic_const_exprs): This isn't actually the case. - // We also take this branch for concrete anonymous constants and - // expand generic anonymous constants with concrete substs. - (ty::ConstKind::Unevaluated(a_uv), ty::ConstKind::Unevaluated(b_uv)) => { - a_uv == b_uv + + match (a_ct.val(), b_ct.val()) { + // We can just unify errors with everything to reduce the amount of + // emitted errors here. + (ty::ConstKind::Error(_), _) | (_, ty::ConstKind::Error(_)) => true, + (ty::ConstKind::Param(a_param), ty::ConstKind::Param(b_param)) => { + a_param == b_param + } + (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val, + // If we have `fn a<const N: usize>() -> [u8; N + 1]` and `fn b<const M: usize>() -> [u8; 1 + M]` + // we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This + // means that we only allow inference variables if they are equal. + (ty::ConstKind::Infer(a_val), ty::ConstKind::Infer(b_val)) => a_val == b_val, + // We expand generic anonymous constants at the start of this function, so this + // branch should only be taking when dealing with associated constants, at + // which point directly comparing them seems like the desired behavior. + // + // FIXME(generic_const_exprs): This isn't actually the case. + // We also take this branch for concrete anonymous constants and + // expand generic anonymous constants with concrete substs. + (ty::ConstKind::Unevaluated(a_uv), ty::ConstKind::Unevaluated(b_uv)) => { + a_uv == b_uv + } + // FIXME(generic_const_exprs): We may want to either actually try + // to evaluate `a_ct` and `b_ct` if they are are fully concrete or something like + // this, for now we just return false here. + _ => false, } - // FIXME(generic_const_exprs): We may want to either actually try - // to evaluate `a_ct` and `b_ct` if they are are fully concrete or something like - // this, for now we just return false here. - _ => false, } + (Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => { + self.try_unify(a.subtree(al), b.subtree(bl)) + && self.try_unify(a.subtree(ar), b.subtree(br)) + } + (Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => { + self.try_unify(a.subtree(av), b.subtree(bv)) + } + (Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args)) + if a_args.len() == b_args.len() => + { + self.try_unify(a.subtree(a_f), b.subtree(b_f)) + && iter::zip(a_args, b_args) + .all(|(&an, &bn)| self.try_unify(a.subtree(an), b.subtree(bn))) + } + (Node::Cast(a_kind, a_operand, a_ty), Node::Cast(b_kind, b_operand, b_ty)) + if (a_ty == b_ty) && (a_kind == b_kind) => + { + self.try_unify(a.subtree(a_operand), b.subtree(b_operand)) + } + // use this over `_ => false` to make adding variants to `Node` less error prone + (Node::Cast(..), _) + | (Node::FunctionCall(..), _) + | (Node::UnaryOp(..), _) + | (Node::Binop(..), _) + | (Node::Leaf(..), _) => false, } - (Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => { - try_unify(tcx, a.subtree(al), b.subtree(bl)) - && try_unify(tcx, a.subtree(ar), b.subtree(br)) - } - (Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => { - try_unify(tcx, a.subtree(av), b.subtree(bv)) - } - (Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args)) - if a_args.len() == b_args.len() => - { - try_unify(tcx, a.subtree(a_f), b.subtree(b_f)) - && iter::zip(a_args, b_args) - .all(|(&an, &bn)| try_unify(tcx, a.subtree(an), b.subtree(bn))) - } - (Node::Cast(a_kind, a_operand, a_ty), Node::Cast(b_kind, b_operand, b_ty)) - if (a_ty == b_ty) && (a_kind == b_kind) => - { - try_unify(tcx, a.subtree(a_operand), b.subtree(b_operand)) - } - // use this over `_ => false` to make adding variants to `Node` less error prone - (Node::Cast(..), _) - | (Node::FunctionCall(..), _) - | (Node::UnaryOp(..), _) - | (Node::Binop(..), _) - | (Node::Leaf(..), _) => false, } } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 229e108d5d6..5e220173cae 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -22,6 +22,7 @@ use rustc_hir::GenericParam; use rustc_hir::Item; use rustc_hir::Node; use rustc_middle::thir::abstract_const::NotConstEvaluatable; +use rustc_middle::traits::select::OverflowError; use rustc_middle::ty::error::ExpectedFound; use rustc_middle::ty::fold::TypeFolder; use rustc_middle::ty::{ @@ -928,8 +929,12 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { self.tcx.sess.delay_span_bug(span, "`ErrorGuaranteed` without an error"); return; } - - Overflow => { + // Already reported. + Overflow(OverflowError::Error(_)) => { + self.tcx.sess.delay_span_bug(span, "`OverflowError` has been reported"); + return; + } + Overflow(_) => { bug!("overflow should be handled before the `report_selection_error` path"); } SelectionError::ErrorReporting => { diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 9ac8dc59a1d..1b862834467 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -580,7 +580,11 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) = (c1.val(), c2.val()) { - if infcx.try_unify_abstract_consts(a.shrink(), b.shrink()) { + if infcx.try_unify_abstract_consts( + a.shrink(), + b.shrink(), + obligation.param_env, + ) { return ProcessResult::Changed(vec![]); } } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 3a6ca9b7624..88750f272c8 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -862,7 +862,10 @@ pub fn provide(providers: &mut ty::query::Providers) { ty::WithOptConstParam { did, const_param_did: Some(param_did) }, ) }, - try_unify_abstract_consts: const_evaluatable::try_unify_abstract_consts, + try_unify_abstract_consts: |tcx, param_env_and| { + let (param_env, (a, b)) = param_env_and.into_parts(); + const_evaluatable::try_unify_abstract_consts(tcx, (a, b), param_env) + }, ..*providers }; } diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 390381752f9..b61e6873571 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -27,6 +27,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_infer::infer::resolve::OpportunisticRegionResolver; +use rustc_middle::traits::select::OverflowError; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, Term, ToPredicate, Ty, TyCtxt}; @@ -1139,7 +1140,9 @@ fn project<'cx, 'tcx>( if !selcx.tcx().recursion_limit().value_within_limit(obligation.recursion_depth) { // This should really be an immediate error, but some existing code // relies on being able to recover from this. - return Err(ProjectionError::TraitSelectionError(SelectionError::Overflow)); + return Err(ProjectionError::TraitSelectionError(SelectionError::Overflow( + OverflowError::Canonical, + ))); } if obligation.predicate.references_error() { diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs index 4874ba6f58c..db45ee3fed7 100644 --- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs +++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs @@ -108,9 +108,11 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> { ) } OverflowError::ErrorReporting => EvaluationResult::EvaluatedToErr, + OverflowError::Error(_) => EvaluationResult::EvaluatedToErr, }) } Err(OverflowError::ErrorReporting) => EvaluationResult::EvaluatedToErr, + Err(OverflowError::Error(_)) => EvaluationResult::EvaluatedToErr, } } } diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index c0a283d2eda..3e7a2252318 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -164,8 +164,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval })) } Ok(_) => Ok(None), - Err(OverflowError::Canonical) => Err(Overflow), + Err(OverflowError::Canonical) => Err(Overflow(OverflowError::Canonical)), Err(OverflowError::ErrorReporting) => Err(ErrorReporting), + Err(OverflowError::Error(e)) => Err(Overflow(OverflowError::Error(e))), }) .flat_map(Result::transpose) .collect::<Result<Vec<_>, _>>()?; diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 3a5bca49296..6d232d86d8a 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -25,7 +25,7 @@ use crate::traits::project::ProjectionCacheKeyExt; use crate::traits::ProjectionCacheKey; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_errors::Diagnostic; +use rustc_errors::{Diagnostic, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_infer::infer::LateBoundRegionConversionTime; @@ -316,11 +316,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &TraitObligation<'tcx>, ) -> SelectionResult<'tcx, Selection<'tcx>> { let candidate = match self.select_from_obligation(obligation) { - Err(SelectionError::Overflow) => { + Err(SelectionError::Overflow(OverflowError::Canonical)) => { // In standard mode, overflow must have been caught and reported // earlier. assert!(self.query_mode == TraitQueryMode::Canonical); - return Err(SelectionError::Overflow); + return Err(SelectionError::Overflow(OverflowError::Canonical)); } Err(SelectionError::Ambiguous(_)) => { return Ok(None); @@ -335,9 +335,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }; match self.confirm_candidate(obligation, candidate) { - Err(SelectionError::Overflow) => { + Err(SelectionError::Overflow(OverflowError::Canonical)) => { assert!(self.query_mode == TraitQueryMode::Canonical); - Err(SelectionError::Overflow) + Err(SelectionError::Overflow(OverflowError::Canonical)) } Err(e) => Err(e), Ok(candidate) => { @@ -639,7 +639,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) = (c1.val(), c2.val()) { - if self.infcx.try_unify_abstract_consts(a.shrink(), b.shrink()) { + if self.infcx.try_unify_abstract_consts( + a.shrink(), + b.shrink(), + obligation.param_env, + ) { return Ok(EvaluatedToOk); } } @@ -954,7 +958,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(Some(c)) => self.evaluate_candidate(stack, &c), Err(SelectionError::Ambiguous(_)) => Ok(EvaluatedToAmbig), Ok(None) => Ok(EvaluatedToAmbig), - Err(Overflow) => Err(OverflowError::Canonical), + Err(Overflow(OverflowError::Canonical)) => Err(OverflowError::Canonical), Err(ErrorReporting) => Err(OverflowError::ErrorReporting), Err(..) => Ok(EvaluatedToErr), } @@ -1113,7 +1117,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match self.query_mode { TraitQueryMode::Standard => { if self.infcx.is_tainted_by_errors() { - return Err(OverflowError::ErrorReporting); + return Err(OverflowError::Error( + ErrorGuaranteed::unchecked_claim_error_was_emitted(), + )); } self.infcx.report_overflow_error(error_obligation, true); } @@ -1349,7 +1355,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } if self.can_use_global_caches(param_env) { - if let Err(Overflow) = candidate { + if let Err(Overflow(OverflowError::Canonical)) = candidate { // Don't cache overflow globally; we only produce this in certain modes. } else if !pred.needs_infer() { if !candidate.needs_infer() { diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index 79471065ccc..328e0d2e0e9 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -20,12 +20,12 @@ use rustc_errors::{struct_span_err, EmissionGuarantee}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::lint::LintDiagnosticBuilder; use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{self, ImplSubject, TyCtxt}; use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK; use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS; use rustc_span::{Span, DUMMY_SP}; -use super::util::impl_trait_ref_and_oblig; +use super::util; use super::{FulfillmentContext, SelectionContext}; /// Information pertinent to an overlapping impl error. @@ -186,18 +186,20 @@ fn fulfill_implication<'a, 'tcx>( param_env, source_trait_ref, target_impl ); + let source_trait = ImplSubject::Trait(source_trait_ref); + let selcx = &mut SelectionContext::new(&infcx); let target_substs = infcx.fresh_substs_for_item(DUMMY_SP, target_impl); - let (target_trait_ref, obligations) = - impl_trait_ref_and_oblig(selcx, param_env, target_impl, target_substs); + let (target_trait, obligations) = + util::impl_subject_and_oblig(selcx, param_env, target_impl, target_substs); // do the impls unify? If not, no specialization. let Ok(InferOk { obligations: more_obligations, .. }) = - infcx.at(&ObligationCause::dummy(), param_env).eq(source_trait_ref, target_trait_ref) + infcx.at(&ObligationCause::dummy(), param_env).eq(source_trait, target_trait) else { debug!( "fulfill_implication: {:?} does not unify with {:?}", - source_trait_ref, target_trait_ref + source_trait, target_trait ); return Err(()); }; @@ -225,7 +227,7 @@ fn fulfill_implication<'a, 'tcx>( [] => { debug!( "fulfill_implication: an impl for {:?} specializes {:?}", - source_trait_ref, target_trait_ref + source_trait, target_trait ); // Now resolve the *substitution* we built for the target earlier, replacing @@ -237,8 +239,8 @@ fn fulfill_implication<'a, 'tcx>( debug!( "fulfill_implication: for impls on {:?} and {:?}, \ could not fulfill: {:?} given {:?}", - source_trait_ref, - target_trait_ref, + source_trait, + target_trait, errors, param_env.caller_bounds() ); diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index f800e7a1402..7543d1f9a7b 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -6,7 +6,7 @@ use smallvec::SmallVec; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::DefId; use rustc_middle::ty::subst::{GenericArg, Subst, SubstsRef}; -use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, ImplSubject, ToPredicate, Ty, TyCtxt, TypeFoldable}; use super::{Normalized, Obligation, ObligationCause, PredicateObligation, SelectionContext}; pub use rustc_infer::traits::{self, util::*}; @@ -190,19 +190,19 @@ impl Iterator for SupertraitDefIds<'_> { // Other /////////////////////////////////////////////////////////////////////////// -/// Instantiate all bound parameters of the impl with the given substs, -/// returning the resulting trait ref and all obligations that arise. +/// Instantiate all bound parameters of the impl subject with the given substs, +/// returning the resulting subject and all obligations that arise. /// The obligations are closed under normalization. -pub fn impl_trait_ref_and_oblig<'a, 'tcx>( +pub fn impl_subject_and_oblig<'a, 'tcx>( selcx: &mut SelectionContext<'a, 'tcx>, param_env: ty::ParamEnv<'tcx>, impl_def_id: DefId, impl_substs: SubstsRef<'tcx>, -) -> (ty::TraitRef<'tcx>, impl Iterator<Item = PredicateObligation<'tcx>>) { - let impl_trait_ref = selcx.tcx().impl_trait_ref(impl_def_id).unwrap(); - let impl_trait_ref = impl_trait_ref.subst(selcx.tcx(), impl_substs); - let Normalized { value: impl_trait_ref, obligations: normalization_obligations1 } = - super::normalize(selcx, param_env, ObligationCause::dummy(), impl_trait_ref); +) -> (ImplSubject<'tcx>, impl Iterator<Item = PredicateObligation<'tcx>>) { + let subject = selcx.tcx().impl_subject(impl_def_id); + let subject = subject.subst(selcx.tcx(), impl_substs); + let Normalized { value: subject, obligations: normalization_obligations1 } = + super::normalize(selcx, param_env, ObligationCause::dummy(), subject); let predicates = selcx.tcx().predicates_of(impl_def_id); let predicates = predicates.instantiate(selcx.tcx(), impl_substs); @@ -215,7 +215,7 @@ pub fn impl_trait_ref_and_oblig<'a, 'tcx>( .chain(normalization_obligations1.into_iter()) .chain(normalization_obligations2.into_iter()); - (impl_trait_ref, impl_obligations) + (subject, impl_obligations) } pub fn predicates_for_generics<'tcx>( diff --git a/compiler/rustc_typeck/src/check/dropck.rs b/compiler/rustc_typeck/src/check/dropck.rs index d38777bea59..4ab94f39357 100644 --- a/compiler/rustc_typeck/src/check/dropck.rs +++ b/compiler/rustc_typeck/src/check/dropck.rs @@ -243,7 +243,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( ( ty::PredicateKind::ConstEvaluatable(a), ty::PredicateKind::ConstEvaluatable(b), - ) => tcx.try_unify_abstract_consts((a, b)), + ) => tcx.try_unify_abstract_consts(self_param_env.and((a, b))), ( ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_a, lt_a)), ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_b, lt_b)), diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index baa23e08fe7..4751faeb936 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -2226,6 +2226,7 @@ macro_rules! uint_impl { #[doc = concat!("assert_eq!(3", stringify!($SelfT), ".wrapping_next_power_of_two(), 4);")] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.wrapping_next_power_of_two(), 0);")] /// ``` + #[inline] #[unstable(feature = "wrapping_next_power_of_two", issue = "32463", reason = "needs decision on wrapping behaviour")] #[rustc_const_unstable(feature = "wrapping_next_power_of_two", issue = "32463")] diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index b1d36f27107..c603420f0f8 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -904,6 +904,7 @@ impl str { #[must_use = "this returns the split string as an iterator, \ without modifying the original"] #[stable(feature = "split_whitespace", since = "1.1.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "str_split_whitespace")] #[inline] pub fn split_whitespace(&self) -> SplitWhitespace<'_> { SplitWhitespace { inner: self.split(IsWhitespace).filter(IsNotEmpty) } @@ -1846,6 +1847,7 @@ impl str { #[must_use = "this returns the trimmed string as a slice, \ without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "str_trim")] pub fn trim(&self) -> &str { self.trim_matches(|c: char| c.is_whitespace()) } @@ -1884,6 +1886,7 @@ impl str { #[must_use = "this returns the trimmed string as a new slice, \ without modifying the original"] #[stable(feature = "trim_direction", since = "1.30.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "str_trim_start")] pub fn trim_start(&self) -> &str { self.trim_start_matches(|c: char| c.is_whitespace()) } @@ -1922,6 +1925,7 @@ impl str { #[must_use = "this returns the trimmed string as a new slice, \ without modifying the original"] #[stable(feature = "trim_direction", since = "1.30.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "str_trim_end")] pub fn trim_end(&self) -> &str { self.trim_end_matches(|c: char| c.is_whitespace()) } diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index a810a57feb7..6d7ca9a94cf 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -568,7 +568,7 @@ fn configure_cmake( // We also do this if the user explicitly requested static libstdc++. if builder.config.llvm_static_stdcpp { if !target.contains("msvc") && !target.contains("netbsd") { - if target.contains("apple") { + if target.contains("apple") || target.contains("windows") { ldflags.push_all("-static-libstdc++"); } else { ldflags.push_all("-Wl,-Bsymbolic -static-libstdc++"); diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 8db5f8b0cff..41efda980a2 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -399,7 +399,7 @@ fn run_test( eprint!("{}", self.0); } } - let mut out_lines = str::from_utf8(&output.stderr) + let mut out = str::from_utf8(&output.stderr) .unwrap() .lines() .filter(|l| { @@ -410,15 +410,15 @@ fn run_test( true } }) - .collect::<Vec<_>>(); + .intersperse_with(|| "\n") + .collect::<String>(); // Add a \n to the end to properly terminate the last line, // but only if there was output to be printed - if !out_lines.is_empty() { - out_lines.push(""); + if !out.is_empty() { + out.push('\n'); } - let out = out_lines.join("\n"); let _bomb = Bomb(&out); match (output.status.success(), lang_string.compile_fail) { (true, true) => { diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 3d8a62d50e0..5b14aca064e 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -487,12 +487,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { module_id: DefId, ) -> Result<Res, ResolutionFailure<'a>> { self.cx.enter_resolver(|resolver| { - // NOTE: this needs 2 separate lookups because `resolve_str_path_error` doesn't take + // NOTE: this needs 2 separate lookups because `resolve_rustdoc_path` doesn't take // lexical scope into account (it ignores all macros not defined at the mod-level) debug!("resolving {} as a macro in the module {:?}", path_str, module_id); - if let Ok((_, res)) = - resolver.resolve_str_path_error(DUMMY_SP, path_str, MacroNS, module_id) - { + if let Some(res) = resolver.resolve_rustdoc_path(path_str, MacroNS, module_id) { // don't resolve builtins like `#[derive]` if let Ok(res) = res.try_into() { return Ok(res); @@ -540,10 +538,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { }) } - /// Convenience wrapper around `resolve_str_path_error`. + /// Convenience wrapper around `resolve_rustdoc_path`. /// /// This also handles resolving `true` and `false` as booleans. - /// NOTE: `resolve_str_path_error` knows only about paths, not about types. + /// NOTE: `resolve_rustdoc_path` knows only about paths, not about types. /// Associated items will never be resolved by this function. fn resolve_path( &self, @@ -556,18 +554,14 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { return res; } - let result = self.cx.enter_resolver(|resolver| { - resolver - .resolve_str_path_error(DUMMY_SP, path_str, ns, module_id) - .and_then(|(_, res)| res.try_into()) - }); + // Resolver doesn't know about true, false, and types that aren't paths (e.g. `()`). + let result = self + .cx + .enter_resolver(|resolver| resolver.resolve_rustdoc_path(path_str, ns, module_id)) + .and_then(|res| res.try_into().ok()) + .or_else(|| resolve_primitive(path_str, ns)); debug!("{} resolved to {:?} in namespace {:?}", path_str, result, ns); - match result { - // resolver doesn't know about true, false, and types that aren't paths (e.g. `()`) - // manually as bool - Err(()) => resolve_primitive(path_str, ns), - Ok(res) => Some(res), - } + result } /// Resolves a string as a path within a particular namespace. Returns an diff --git a/src/librustdoc/passes/collect_intra_doc_links/early.rs b/src/librustdoc/passes/collect_intra_doc_links/early.rs index 1d28bbde79c..30636faf98c 100644 --- a/src/librustdoc/passes/collect_intra_doc_links/early.rs +++ b/src/librustdoc/passes/collect_intra_doc_links/early.rs @@ -13,7 +13,7 @@ use rustc_hir::TraitCandidate; use rustc_middle::ty::{DefIdTree, Visibility}; use rustc_resolve::{ParentScope, Resolver}; use rustc_session::config::Externs; -use rustc_span::{Span, SyntaxContext, DUMMY_SP}; +use rustc_span::SyntaxContext; use std::collections::hash_map::Entry; use std::mem; @@ -39,7 +39,7 @@ crate fn early_resolve_intra_doc_links( // Overridden `visit_item` below doesn't apply to the crate root, // so we have to visit its attributes and reexports separately. - loader.load_links_in_attrs(&krate.attrs, krate.spans.inner_span); + loader.load_links_in_attrs(&krate.attrs); loader.process_module_children_or_reexports(CRATE_DEF_ID.to_def_id()); visit::walk_crate(&mut loader, krate); loader.add_foreign_traits_in_scope(); @@ -49,12 +49,7 @@ crate fn early_resolve_intra_doc_links( // DO NOT REMOVE THIS without first testing on the reproducer in // https://github.com/jyn514/objr/commit/edcee7b8124abf0e4c63873e8422ff81beb11ebb for (extern_name, _) in externs.iter().filter(|(_, entry)| entry.add_prelude) { - let _ = loader.resolver.resolve_str_path_error( - DUMMY_SP, - extern_name, - TypeNS, - CRATE_DEF_ID.to_def_id(), - ); + loader.resolver.resolve_rustdoc_path(extern_name, TypeNS, CRATE_DEF_ID.to_def_id()); } ResolverCaches { @@ -151,7 +146,7 @@ impl IntraLinkCrateLoader<'_, '_> { } } - fn load_links_in_attrs(&mut self, attrs: &[ast::Attribute], span: Span) { + fn load_links_in_attrs(&mut self, attrs: &[ast::Attribute]) { // FIXME: this needs to consider reexport inlining. let attrs = clean::Attributes::from_ast(attrs, None); for (parent_module, doc) in attrs.collapsed_doc_value_by_module_level() { @@ -165,7 +160,7 @@ impl IntraLinkCrateLoader<'_, '_> { } else { continue; }; - let _ = self.resolver.resolve_str_path_error(span, &path_str, TypeNS, module_id); + self.resolver.resolve_rustdoc_path(&path_str, TypeNS, module_id); } } } @@ -201,7 +196,7 @@ impl Visitor<'_> for IntraLinkCrateLoader<'_, '_> { // loaded, even if the module itself has no doc comments. self.add_traits_in_parent_scope(self.current_mod.to_def_id()); - self.load_links_in_attrs(&item.attrs, item.span); + self.load_links_in_attrs(&item.attrs); self.process_module_children_or_reexports(self.current_mod.to_def_id()); visit::walk_item(self, item); @@ -216,28 +211,28 @@ impl Visitor<'_> for IntraLinkCrateLoader<'_, '_> { } _ => {} } - self.load_links_in_attrs(&item.attrs, item.span); + self.load_links_in_attrs(&item.attrs); visit::walk_item(self, item); } } fn visit_assoc_item(&mut self, item: &ast::AssocItem, ctxt: AssocCtxt) { - self.load_links_in_attrs(&item.attrs, item.span); + self.load_links_in_attrs(&item.attrs); visit::walk_assoc_item(self, item, ctxt) } fn visit_foreign_item(&mut self, item: &ast::ForeignItem) { - self.load_links_in_attrs(&item.attrs, item.span); + self.load_links_in_attrs(&item.attrs); visit::walk_foreign_item(self, item) } fn visit_variant(&mut self, v: &ast::Variant) { - self.load_links_in_attrs(&v.attrs, v.span); + self.load_links_in_attrs(&v.attrs); visit::walk_variant(self, v) } fn visit_field_def(&mut self, field: &ast::FieldDef) { - self.load_links_in_attrs(&field.attrs, field.span); + self.load_links_in_attrs(&field.attrs); visit::walk_field_def(self, field) } diff --git a/src/test/codegen/consts.rs b/src/test/codegen/consts.rs index 1a84f1a4479..e6afbe49864 100644 --- a/src/test/codegen/consts.rs +++ b/src/test/codegen/consts.rs @@ -10,7 +10,7 @@ // CHECK: @STATIC = {{.*}}, align 4 // This checks the constants from inline_enum_const -// CHECK: @alloc9 = {{.*}}, align 2 +// CHECK: @alloc12 = {{.*}}, align 2 // This checks the constants from {low,high}_align_const, they share the same // constant, but the alignment differs, so the higher one should be used diff --git a/src/test/debuginfo/unsized.rs b/src/test/debuginfo/unsized.rs index 7ccc88ef940..7cb0002ca51 100644 --- a/src/test/debuginfo/unsized.rs +++ b/src/test/debuginfo/unsized.rs @@ -16,13 +16,17 @@ // gdbg-check:$3 = {pointer = [...], vtable = [...]} // gdbr-check:$3 = &unsized::Foo<dyn core::fmt::Debug> {pointer: [...], vtable: [...]} +// gdb-command:print _box +// gdbg-check:$4 = {pointer = [...], vtable = [...]} +// gdbr-check:$4 = alloc::boxed::Box<unsized::Foo<dyn core::fmt::Debug>, alloc::alloc::Global> {pointer: [...], vtable: [...]} + // gdb-command:print tuple_slice -// gdbg-check:$4 = {data_ptr = [...], length = 2} -// gdbr-check:$4 = &(i32, i32, [i32]) {data_ptr: [...], length: 2} +// gdbg-check:$5 = {data_ptr = [...], length = 2} +// gdbr-check:$5 = &(i32, i32, [i32]) {data_ptr: [...], length: 2} // gdb-command:print tuple_dyn -// gdbg-check:$5 = {pointer = [...], vtable = [...]} -// gdbr-check:$5 = &(i32, i32, dyn core::fmt::Debug) {pointer: [...], vtable: [...]} +// gdbg-check:$6 = {pointer = [...], vtable = [...]} +// gdbr-check:$6 = &(i32, i32, dyn core::fmt::Debug) {pointer: [...], vtable: [...]} // === CDB TESTS =================================================================================== @@ -42,6 +46,12 @@ // cdb-check: [+0x000] pointer : 0x[...] [Type: unsized::Foo<dyn$<core::fmt::Debug> > *] // cdb-check: [...] vtable : 0x[...] [Type: unsigned [...]int[...] (*)[3]] +// cdb-command:dx _box +// cdb-check: +// cdb-check:_box [Type: alloc::boxed::Box<unsized::Foo<dyn$<core::fmt::Debug> >,alloc::alloc::Global>] +// cdb-check:[+0x000] pointer : 0x[...] [Type: unsized::Foo<dyn$<core::fmt::Debug> > *] +// cdb-check:[...] vtable : 0x[...] [Type: unsigned [...]int[...] (*)[3]] + // cdb-command:dx tuple_slice // cdb-check:tuple_slice [Type: ref$<tuple$<i32,i32,slice$<i32> > >] // cdb-check: [+0x000] data_ptr : 0x[...] [Type: tuple$<i32,i32,slice$<i32> > *] @@ -69,6 +79,7 @@ fn main() { let a: &Foo<[u8]> = &foo.value; let b: &Foo<Foo<[u8]>> = &foo; let c: &Foo<dyn std::fmt::Debug> = &Foo { value: 7i32 }; + let _box: Box<Foo<dyn std::fmt::Debug>> = Box::new(Foo { value: 8i32 }); // Also check unsized tuples let tuple_slice: &(i32, i32, [i32]) = &(0, 1, [2, 3]); diff --git a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.rs b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.rs index be8162c86b9..0315938a7ed 100644 --- a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.rs +++ b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.rs @@ -4,12 +4,12 @@ trait Foo { const BAR: u32; } -const IMPL_REF_BAR: u32 = GlobalImplRef::BAR; //~ ERROR E0391 +const IMPL_REF_BAR: u32 = GlobalImplRef::BAR; struct GlobalImplRef; impl GlobalImplRef { - const BAR: u32 = IMPL_REF_BAR; + const BAR: u32 = IMPL_REF_BAR; //~ ERROR E0391 } fn main() {} diff --git a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr index 61b16cb9d58..3d696bb049c 100644 --- a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr +++ b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr @@ -1,9 +1,15 @@ -error[E0391]: cycle detected when simplifying constant for the type system `IMPL_REF_BAR` +error[E0391]: cycle detected when elaborating drops for `<impl at $DIR/issue-24949-assoc-const-static-recursion-impl.rs:11:1: 13:2>::BAR` + --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:12:5 + | +LL | const BAR: u32 = IMPL_REF_BAR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: ...which requires normalizing `IMPL_REF_BAR`... +note: ...which requires simplifying constant for the type system `IMPL_REF_BAR`... --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:7:1 | LL | const IMPL_REF_BAR: u32 = GlobalImplRef::BAR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | note: ...which requires simplifying constant for the type system `IMPL_REF_BAR`... --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:7:1 | @@ -35,8 +41,7 @@ note: ...which requires caching mir of `<impl at $DIR/issue-24949-assoc-const-st | LL | const BAR: u32 = IMPL_REF_BAR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: ...which requires normalizing `IMPL_REF_BAR`... - = note: ...which again requires simplifying constant for the type system `IMPL_REF_BAR`, completing the cycle + = note: ...which again requires elaborating drops for `<impl at $DIR/issue-24949-assoc-const-static-recursion-impl.rs:11:1: 13:2>::BAR`, completing the cycle = note: cycle used when running analysis passes on this crate error: aborting due to previous error diff --git a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.rs b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.rs index cec75ae19f4..4e89f686288 100644 --- a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.rs +++ b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.rs @@ -5,10 +5,10 @@ trait Foo { } trait FooDefault { - const BAR: u32 = DEFAULT_REF_BAR; + const BAR: u32 = DEFAULT_REF_BAR; //~ ERROR E0391 } -const DEFAULT_REF_BAR: u32 = <GlobalDefaultRef>::BAR; //~ ERROR E0391 +const DEFAULT_REF_BAR: u32 = <GlobalDefaultRef>::BAR; struct GlobalDefaultRef; diff --git a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.stderr b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.stderr index 494dc0c0ed4..71e26245e16 100644 --- a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.stderr +++ b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.stderr @@ -1,9 +1,15 @@ -error[E0391]: cycle detected when simplifying constant for the type system `DEFAULT_REF_BAR` +error[E0391]: cycle detected when elaborating drops for `FooDefault::BAR` + --> $DIR/issue-24949-assoc-const-static-recursion-trait-default.rs:8:5 + | +LL | const BAR: u32 = DEFAULT_REF_BAR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: ...which requires normalizing `DEFAULT_REF_BAR`... +note: ...which requires simplifying constant for the type system `DEFAULT_REF_BAR`... --> $DIR/issue-24949-assoc-const-static-recursion-trait-default.rs:11:1 | LL | const DEFAULT_REF_BAR: u32 = <GlobalDefaultRef>::BAR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | note: ...which requires simplifying constant for the type system `DEFAULT_REF_BAR`... --> $DIR/issue-24949-assoc-const-static-recursion-trait-default.rs:11:1 | @@ -35,8 +41,7 @@ note: ...which requires caching mir of `FooDefault::BAR` for CTFE... | LL | const BAR: u32 = DEFAULT_REF_BAR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: ...which requires normalizing `DEFAULT_REF_BAR`... - = note: ...which again requires simplifying constant for the type system `DEFAULT_REF_BAR`, completing the cycle + = note: ...which again requires elaborating drops for `FooDefault::BAR`, completing the cycle = note: cycle used when running analysis passes on this crate error: aborting due to previous error diff --git a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.rs b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.rs index 62af8534340..68b653ff3c5 100644 --- a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.rs +++ b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.rs @@ -4,12 +4,12 @@ trait Foo { const BAR: u32; } -const TRAIT_REF_BAR: u32 = <GlobalTraitRef>::BAR; //~ ERROR E0391 +const TRAIT_REF_BAR: u32 = <GlobalTraitRef>::BAR; struct GlobalTraitRef; impl Foo for GlobalTraitRef { - const BAR: u32 = TRAIT_REF_BAR; + const BAR: u32 = TRAIT_REF_BAR; //~ ERROR E0391 } fn main() {} diff --git a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.stderr b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.stderr index 4ff253bffcb..020d758e153 100644 --- a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.stderr +++ b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.stderr @@ -1,9 +1,15 @@ -error[E0391]: cycle detected when simplifying constant for the type system `TRAIT_REF_BAR` +error[E0391]: cycle detected when elaborating drops for `<impl at $DIR/issue-24949-assoc-const-static-recursion-trait.rs:11:1: 13:2>::BAR` + --> $DIR/issue-24949-assoc-const-static-recursion-trait.rs:12:5 + | +LL | const BAR: u32 = TRAIT_REF_BAR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: ...which requires normalizing `TRAIT_REF_BAR`... +note: ...which requires simplifying constant for the type system `TRAIT_REF_BAR`... --> $DIR/issue-24949-assoc-const-static-recursion-trait.rs:7:1 | LL | const TRAIT_REF_BAR: u32 = <GlobalTraitRef>::BAR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | note: ...which requires simplifying constant for the type system `TRAIT_REF_BAR`... --> $DIR/issue-24949-assoc-const-static-recursion-trait.rs:7:1 | @@ -35,8 +41,7 @@ note: ...which requires caching mir of `<impl at $DIR/issue-24949-assoc-const-st | LL | const BAR: u32 = TRAIT_REF_BAR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: ...which requires normalizing `TRAIT_REF_BAR`... - = note: ...which again requires simplifying constant for the type system `TRAIT_REF_BAR`, completing the cycle + = note: ...which again requires elaborating drops for `<impl at $DIR/issue-24949-assoc-const-static-recursion-trait.rs:11:1: 13:2>::BAR`, completing the cycle = note: cycle used when running analysis passes on this crate error: aborting due to previous error diff --git a/src/test/ui/coherence/coherence-negative-inherent-where-bounds.rs b/src/test/ui/coherence/coherence-negative-inherent-where-bounds.rs new file mode 100644 index 00000000000..39ccaa6ac35 --- /dev/null +++ b/src/test/ui/coherence/coherence-negative-inherent-where-bounds.rs @@ -0,0 +1,25 @@ +// check-pass + +#![feature(negative_impls)] +#![feature(rustc_attrs)] +#![feature(with_negative_coherence)] + +trait Foo {} + +impl !Foo for u32 {} + +#[rustc_strict_coherence] +struct MyStruct<T>(T); + +impl MyStruct<u32> { + fn method(&self) {} +} + +impl<T> MyStruct<T> +where + T: Foo, +{ + fn method(&self) {} +} + +fn main() {} diff --git a/src/test/ui/coherence/coherence-negative-inherent.rs b/src/test/ui/coherence/coherence-negative-inherent.rs new file mode 100644 index 00000000000..a9e1acc8044 --- /dev/null +++ b/src/test/ui/coherence/coherence-negative-inherent.rs @@ -0,0 +1,22 @@ +// check-pass + +#![feature(negative_impls)] +#![feature(rustc_attrs)] +#![feature(with_negative_coherence)] + +#[rustc_strict_coherence] +trait Foo {} + +impl !Foo for u32 {} + +struct MyStruct<T>(T); + +impl<T: Foo> MyStruct<T> { + fn method(&self) {} +} + +impl MyStruct<u32> { + fn method(&self) {} +} + +fn main() {} diff --git a/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.rs b/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.rs new file mode 100644 index 00000000000..c59d62e576d --- /dev/null +++ b/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.rs @@ -0,0 +1,26 @@ +// build-pass + +#![feature(generic_const_exprs)] +//~^ WARNING the feature `generic_const_exprs` is incomplete + +trait Generic { + const ASSOC: usize; +} + +impl Generic for u8 { + const ASSOC: usize = 17; +} +impl Generic for u16 { + const ASSOC: usize = 13; +} + + +fn uses_assoc_type<T: Generic, const N: usize>() -> [u8; N + T::ASSOC] { + [0; N + T::ASSOC] +} + +fn only_generic_n<const N: usize>() -> [u8; N + 13] { + uses_assoc_type::<u16, N>() +} + +fn main() {} diff --git a/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.stderr b/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.stderr new file mode 100644 index 00000000000..b5719b3fe1d --- /dev/null +++ b/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.stderr @@ -0,0 +1,11 @@ +warning: the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/eval-try-unify.rs:3:12 + | +LL | #![feature(generic_const_exprs)] + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #76560 <https://github.com/rust-lang/rust/issues/76560> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/const-generics/issues/issue-83765.rs b/src/test/ui/const-generics/issues/issue-83765.rs index 68536348d38..71c164ab0a5 100644 --- a/src/test/ui/const-generics/issues/issue-83765.rs +++ b/src/test/ui/const-generics/issues/issue-83765.rs @@ -2,114 +2,115 @@ #![allow(incomplete_features)] trait TensorDimension { - const DIM : usize; - const ISSCALAR : bool = Self::DIM == 0; - fn is_scalar(&self) -> bool {Self::ISSCALAR} + const DIM: usize; + //~^ ERROR cycle detected when resolving instance + // FIXME Given the current state of the compiler its expected that we cycle here, + // but the cycle is still wrong. + const ISSCALAR: bool = Self::DIM == 0; + fn is_scalar(&self) -> bool { + Self::ISSCALAR + } } -trait TensorSize : TensorDimension { - fn size(&self) -> [usize;Self::DIM]; - fn inbounds(&self,index : [usize;Self::DIM]) -> bool { - index.iter().zip(self.size().iter()).all(|(i,s)| i < s) +trait TensorSize: TensorDimension { + fn size(&self) -> [usize; Self::DIM]; + fn inbounds(&self, index: [usize; Self::DIM]) -> bool { + index.iter().zip(self.size().iter()).all(|(i, s)| i < s) } } - trait Broadcastable: TensorSize + Sized { type Element; - fn bget(&self, index:[usize;Self::DIM]) -> Option<Self::Element>; - fn lazy_updim<const NEWDIM : usize>(&self, size : [usize;NEWDIM] ) -> - LazyUpdim<Self,{Self::DIM},NEWDIM> - { - assert!(NEWDIM >= Self::DIM, - "Updimmed tensor cannot have fewer indices than the initial one."); - LazyUpdim {size,reference:&self} + fn bget(&self, index: [usize; Self::DIM]) -> Option<Self::Element>; + fn lazy_updim<const NEWDIM: usize>( + &self, + size: [usize; NEWDIM], + ) -> LazyUpdim<Self, { Self::DIM }, NEWDIM> { + assert!( + NEWDIM >= Self::DIM, + "Updimmed tensor cannot have fewer indices than the initial one." + ); + LazyUpdim { size, reference: &self } } - fn bmap<T,F :Fn(Self::Element) -> T>(&self,foo : F) -> BMap<T,Self,F,{Self::DIM}>{ - BMap {reference:self,closure : foo} + fn bmap<T, F: Fn(Self::Element) -> T>(&self, foo: F) -> BMap<T, Self, F, { Self::DIM }> { + BMap { reference: self, closure: foo } } } - -struct LazyUpdim<'a,T : Broadcastable,const OLDDIM : usize, const DIM : usize> { - size : [usize;DIM], - reference : &'a T +struct LazyUpdim<'a, T: Broadcastable, const OLDDIM: usize, const DIM: usize> { + size: [usize; DIM], + reference: &'a T, } -impl<'a,T : Broadcastable,const DIM : usize> TensorDimension for LazyUpdim<'a,T,{T::DIM},DIM> { - const DIM : usize = DIM; +impl<'a, T: Broadcastable, const DIM: usize> TensorDimension for LazyUpdim<'a, T, { T::DIM }, DIM> { + const DIM: usize = DIM; } -impl<'a,T : Broadcastable,const DIM : usize> TensorSize for LazyUpdim<'a,T,{T::DIM},DIM> { - fn size(&self) -> [usize;DIM] {self.size} - //~^ ERROR method not compatible with trait +impl<'a, T: Broadcastable, const DIM: usize> TensorSize for LazyUpdim<'a, T, { T::DIM }, DIM> { + fn size(&self) -> [usize; DIM] { + self.size + } } -impl<'a,T : Broadcastable,const DIM : usize> Broadcastable for LazyUpdim<'a,T,{T::DIM},DIM> -{ +impl<'a, T: Broadcastable, const DIM: usize> Broadcastable for LazyUpdim<'a, T, { T::DIM }, DIM> { type Element = T::Element; - fn bget(&self,index:[usize;DIM]) -> Option<Self::Element> { - //~^ ERROR method not compatible with trait + fn bget(&self, index: [usize; DIM]) -> Option<Self::Element> { assert!(DIM >= T::DIM); - if !self.inbounds(index) {return None} - //~^ ERROR unconstrained generic constant - //~| ERROR mismatched types + if !self.inbounds(index) { + return None; + } let size = self.size(); - //~^ ERROR unconstrained generic constant - let newindex : [usize;T::DIM] = Default::default(); - //~^ ERROR the trait bound `[usize; _]: Default` is not satisfied + let newindex: [usize; T::DIM] = Default::default(); self.reference.bget(newindex) } } -struct BMap<'a,R, T : Broadcastable, F : Fn(T::Element) -> R , const DIM: usize> { - reference : &'a T, - closure : F +struct BMap<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> { + reference: &'a T, + closure: F, } -impl<'a,R, T : Broadcastable, F : Fn(T::Element) -> R, - const DIM: usize> TensorDimension for BMap<'a,R,T,F,DIM> { - - const DIM : usize = DIM; +impl<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> TensorDimension + for BMap<'a, R, T, F, DIM> +{ + const DIM: usize = DIM; } -impl<'a,R, T : Broadcastable, F : Fn(T::Element) -> R , - const DIM: usize> TensorSize for BMap<'a,R,T,F,DIM> { - - fn size(&self) -> [usize;DIM] {self.reference.size()} - //~^ ERROR unconstrained generic constant - //~| ERROR mismatched types - //~| ERROR method not compatible with trait +impl<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> TensorSize + for BMap<'a, R, T, F, DIM> +{ + fn size(&self) -> [usize; DIM] { + self.reference.size() + } } -impl<'a,R, T : Broadcastable, F : Fn(T::Element) -> R , - const DIM: usize> Broadcastable for BMap<'a,R,T,F,DIM> { - +impl<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> Broadcastable + for BMap<'a, R, T, F, DIM> +{ type Element = R; - fn bget(&self,index:[usize;DIM]) -> Option<Self::Element> { - //~^ ERROR method not compatible with trait + fn bget(&self, index: [usize; DIM]) -> Option<Self::Element> { self.reference.bget(index).map(&self.closure) - //~^ ERROR unconstrained generic constant - //~| ERROR mismatched types } } impl<T> TensorDimension for Vec<T> { - const DIM : usize = 1; + const DIM: usize = 1; } impl<T> TensorSize for Vec<T> { - fn size(&self) -> [usize;1] {[self.len()]} + fn size(&self) -> [usize; 1] { + [self.len()] + } } impl<T: Clone> Broadcastable for Vec<T> { type Element = T; - fn bget(& self,index : [usize;1]) -> Option<T> { + fn bget(&self, index: [usize; 1]) -> Option<T> { self.get(index[0]).cloned() } } fn main() { - let v = vec![1,2,3]; - let bv = v.lazy_updim([3,4]); - let bbv = bv.bmap(|x| x*x); + let v = vec![1, 2, 3]; + let bv = v.lazy_updim([3, 4]); + let bbv = bv.bmap(|x| x * x); - println!("The size of v is {:?}",bbv.bget([0,2]).expect("Out of bounds.")); + println!("The size of v is {:?}", bbv.bget([0, 2]).expect("Out of bounds.")); } diff --git a/src/test/ui/const-generics/issues/issue-83765.stderr b/src/test/ui/const-generics/issues/issue-83765.stderr index a49f850717f..8705a39fa4b 100644 --- a/src/test/ui/const-generics/issues/issue-83765.stderr +++ b/src/test/ui/const-generics/issues/issue-83765.stderr @@ -1,130 +1,17 @@ -error[E0308]: method not compatible with trait - --> $DIR/issue-83765.rs:44:5 +error[E0391]: cycle detected when resolving instance `<LazyUpdim<T, { T::DIM }, DIM> as TensorDimension>::DIM` + --> $DIR/issue-83765.rs:5:5 | -LL | fn size(&self) -> [usize;DIM] {self.size} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM` +LL | const DIM: usize; + | ^^^^^^^^^^^^^^^^^ | - = note: expected type `Self::DIM` - found type `DIM` - -error[E0308]: method not compatible with trait - --> $DIR/issue-83765.rs:51:5 - | -LL | fn bget(&self,index:[usize;DIM]) -> Option<Self::Element> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM` - | - = note: expected type `Self::DIM` - found type `DIM` - -error[E0308]: method not compatible with trait - --> $DIR/issue-83765.rs:78:5 - | -LL | fn size(&self) -> [usize;DIM] {self.reference.size()} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM` - | - = note: expected type `Self::DIM` - found type `DIM` - -error[E0308]: method not compatible with trait - --> $DIR/issue-83765.rs:88:5 - | -LL | fn bget(&self,index:[usize;DIM]) -> Option<Self::Element> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM` - | - = note: expected type `Self::DIM` - found type `DIM` - -error: unconstrained generic constant - --> $DIR/issue-83765.rs:54:18 - | -LL | if !self.inbounds(index) {return None} - | ^^^^^^^^ - | - = help: try adding a `where` bound using this expression: `where [(); Self::DIM]:` -note: required by a bound in `TensorSize::inbounds` - --> $DIR/issue-83765.rs:12:38 - | -LL | fn inbounds(&self,index : [usize;Self::DIM]) -> bool { - | ^^^^^^^^^ required by this bound in `TensorSize::inbounds` - -error[E0308]: mismatched types - --> $DIR/issue-83765.rs:54:27 - | -LL | if !self.inbounds(index) {return None} - | ^^^^^ expected `Self::DIM`, found `DIM` - | - = note: expected type `Self::DIM` - found type `DIM` - -error: unconstrained generic constant - --> $DIR/issue-83765.rs:57:25 - | -LL | let size = self.size(); - | ^^^^ - | - = help: try adding a `where` bound using this expression: `where [(); Self::DIM]:` -note: required by a bound in `TensorSize::size` - --> $DIR/issue-83765.rs:11:30 - | -LL | fn size(&self) -> [usize;Self::DIM]; - | ^^^^^^^^^ required by this bound in `TensorSize::size` - -error[E0277]: the trait bound `[usize; _]: Default` is not satisfied - --> $DIR/issue-83765.rs:59:41 - | -LL | let newindex : [usize;T::DIM] = Default::default(); - | ^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `[usize; _]` - | -help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement - | -LL | impl<'a,T : Broadcastable,const DIM : usize> Broadcastable for LazyUpdim<'a,T,{T::DIM},DIM> where [usize; _]: Default - | +++++++++++++++++++++++++ - -error: unconstrained generic constant - --> $DIR/issue-83765.rs:78:51 - | -LL | fn size(&self) -> [usize;DIM] {self.reference.size()} - | ^^^^ - | - = help: try adding a `where` bound using this expression: `where [(); Self::DIM]:` -note: required by a bound in `TensorSize::size` - --> $DIR/issue-83765.rs:11:30 - | -LL | fn size(&self) -> [usize;Self::DIM]; - | ^^^^^^^^^ required by this bound in `TensorSize::size` - -error[E0308]: mismatched types - --> $DIR/issue-83765.rs:78:36 - | -LL | fn size(&self) -> [usize;DIM] {self.reference.size()} - | ^^^^^^^^^^^^^^^^^^^^^ expected `DIM`, found `Self::DIM` - | - = note: expected type `DIM` - found type `Self::DIM` - -error: unconstrained generic constant - --> $DIR/issue-83765.rs:90:24 - | -LL | self.reference.bget(index).map(&self.closure) - | ^^^^ - | - = help: try adding a `where` bound using this expression: `where [(); Self::DIM]:` -note: required by a bound in `Broadcastable::bget` - --> $DIR/issue-83765.rs:20:33 - | -LL | fn bget(&self, index:[usize;Self::DIM]) -> Option<Self::Element>; - | ^^^^^^^^^ required by this bound in `Broadcastable::bget` - -error[E0308]: mismatched types - --> $DIR/issue-83765.rs:90:29 - | -LL | self.reference.bget(index).map(&self.closure) - | ^^^^^ expected `Self::DIM`, found `DIM` +note: ...which requires checking if `TensorDimension` fulfills its obligations... + --> $DIR/issue-83765.rs:4:1 | - = note: expected type `Self::DIM` - found type `DIM` +LL | trait TensorDimension { + | ^^^^^^^^^^^^^^^^^^^^^ + = note: ...which again requires resolving instance `<LazyUpdim<T, { T::DIM }, DIM> as TensorDimension>::DIM`, completing the cycle + = note: cycle used when normalizing `<LazyUpdim<T, { T::DIM }, DIM> as TensorDimension>::DIM` -error: aborting due to 12 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0277, E0308. -For more information about an error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0391`. diff --git a/src/test/ui/const_prop/inline_spans.rs b/src/test/ui/const_prop/inline_spans.rs index f1cfcfee557..504f2781156 100644 --- a/src/test/ui/const_prop/inline_spans.rs +++ b/src/test/ui/const_prop/inline_spans.rs @@ -1,18 +1,15 @@ -// build-fail +// build-pass // compile-flags: -Zmir-opt-level=3 +// Overflow can't be detected by const prop +// could only be detected after optimizations #![deny(warnings)] fn main() { let _ = add(u8::MAX, 1); - //~^ NOTE in this expansion of inlined source - //~| NOTE in this expansion of inlined source } #[inline(always)] fn add(x: u8, y: u8) -> u8 { x + y - //~^ ERROR this arithmetic operation will overflow - //~| NOTE attempt to compute `u8::MAX + 1_u8`, which would overflow - //~| NOTE `#[deny(arithmetic_overflow)]` on by default } diff --git a/src/test/ui/const_prop/inline_spans.stderr b/src/test/ui/const_prop/inline_spans.stderr deleted file mode 100644 index f99a3142386..00000000000 --- a/src/test/ui/const_prop/inline_spans.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error: this arithmetic operation will overflow - --> $DIR/inline_spans.rs:14:5 - | -LL | let _ = add(u8::MAX, 1); - | --------------- in this inlined function call -... -LL | x + y - | ^^^^^ attempt to compute `u8::MAX + 1_u8`, which would overflow - | - = note: `#[deny(arithmetic_overflow)]` on by default - -error: aborting due to previous error - diff --git a/src/test/ui/consts/const-eval/const-eval-query-stack.stderr b/src/test/ui/consts/const-eval/const-eval-query-stack.stderr index 45a3d901c98..8bd5c08dc46 100644 --- a/src/test/ui/consts/const-eval/const-eval-query-stack.stderr +++ b/src/test/ui/consts/const-eval/const-eval-query-stack.stderr @@ -21,6 +21,7 @@ LL | let x: &'static i32 = &X; | ^ referenced constant has errors query stack during panic: #0 [try_normalize_mir_const_after_erasing_regions] normalizing `main::promoted[1]` -#1 [optimized_mir] optimizing MIR for `main` -#2 [collect_and_partition_mono_items] collect_and_partition_mono_items +#1 [mir_drops_elaborated_and_const_checked] elaborating drops for `main` +#2 [optimized_mir] optimizing MIR for `main` +#3 [collect_and_partition_mono_items] collect_and_partition_mono_items end of query stack diff --git a/src/test/ui/consts/const-eval/issue-49296.stderr b/src/test/ui/consts/const-eval/issue-49296.stderr index 1864a284579..cc4f1594c32 100644 --- a/src/test/ui/consts/const-eval/issue-49296.stderr +++ b/src/test/ui/consts/const-eval/issue-49296.stderr @@ -2,7 +2,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/issue-49296.rs:9:16 | LL | const X: u64 = *wat(42); - | ^^^^^^^^ pointer to alloc2 was dereferenced after this allocation got freed + | ^^^^^^^^ pointer to alloc3 was dereferenced after this allocation got freed error: aborting due to previous error diff --git a/src/test/ui/consts/const-eval/panic-assoc-never-type.rs b/src/test/ui/consts/const-eval/panic-assoc-never-type.rs index 2bdf5d54765..80b0a1432fb 100644 --- a/src/test/ui/consts/const-eval/panic-assoc-never-type.rs +++ b/src/test/ui/consts/const-eval/panic-assoc-never-type.rs @@ -13,4 +13,5 @@ impl PrintName { fn main() { let _ = PrintName::VOID; + //~^ ERROR erroneous constant used [E0080] } diff --git a/src/test/ui/consts/const-eval/panic-assoc-never-type.stderr b/src/test/ui/consts/const-eval/panic-assoc-never-type.stderr index 9631b7748b5..0116a83910d 100644 --- a/src/test/ui/consts/const-eval/panic-assoc-never-type.stderr +++ b/src/test/ui/consts/const-eval/panic-assoc-never-type.stderr @@ -6,6 +6,12 @@ LL | const VOID: ! = panic!(); | = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to previous error +error[E0080]: erroneous constant used + --> $DIR/panic-assoc-never-type.rs:15:13 + | +LL | let _ = PrintName::VOID; + | ^^^^^^^^^^^^^^^ referenced constant has errors + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/promoted_errors.noopt.stderr b/src/test/ui/consts/const-eval/promoted_errors.noopt.stderr index 77e7d484071..5bfd4ef92a9 100644 --- a/src/test/ui/consts/const-eval/promoted_errors.noopt.stderr +++ b/src/test/ui/consts/const-eval/promoted_errors.noopt.stderr @@ -1,3 +1,45 @@ +warning: this arithmetic operation will overflow + --> $DIR/promoted_errors.rs:15:5 + | +LL | 0 - 1 + | ^^^^^ attempt to compute `0_u32 - 1_u32`, which would overflow + | +note: the lint level is defined here + --> $DIR/promoted_errors.rs:11:20 + | +LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)] + | ^^^^^^^^^^^^^^^^^^^ + +warning: this operation will panic at runtime + --> $DIR/promoted_errors.rs:21:5 + | +LL | 1 / 0 + | ^^^^^ attempt to divide `1_i32` by zero + | +note: the lint level is defined here + --> $DIR/promoted_errors.rs:11:41 + | +LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)] + | ^^^^^^^^^^^^^^^^^^^ + +warning: this operation will panic at runtime + --> $DIR/promoted_errors.rs:27:5 + | +LL | 1 / (1 - 1) + | ^^^^^^^^^^^ attempt to divide `1_i32` by zero + +warning: this operation will panic at runtime + --> $DIR/promoted_errors.rs:31:5 + | +LL | 1 / (false as i32) + | ^^^^^^^^^^^^^^^^^^ attempt to divide `1_i32` by zero + +warning: this operation will panic at runtime + --> $DIR/promoted_errors.rs:35:5 + | +LL | [1, 2, 3][4] + | ^^^^^^^^^^^^ index out of bounds: the length is 3 but the index is 4 + warning: any use of this value will cause an error --> $DIR/promoted_errors.rs:15:5 | @@ -6,7 +48,7 @@ LL | 0 - 1 | | | attempt to compute `0_u32 - 1_u32`, which would overflow | inside `overflow` at $DIR/promoted_errors.rs:15:5 - | inside `X` at $DIR/promoted_errors.rs:38:29 + | inside `X` at $DIR/promoted_errors.rs:43:29 ... LL | / const X: () = { LL | | let _x: &'static u32 = &overflow(); @@ -26,7 +68,7 @@ LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)] = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> warning: any use of this value will cause an error - --> $DIR/promoted_errors.rs:38:28 + --> $DIR/promoted_errors.rs:43:28 | LL | / const X: () = { LL | | let _x: &'static u32 = &overflow(); @@ -41,5 +83,5 @@ LL | | }; = 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 #71800 <https://github.com/rust-lang/rust/issues/71800> -warning: 2 warnings emitted +warning: 7 warnings emitted diff --git a/src/test/ui/consts/const-eval/promoted_errors.opt.stderr b/src/test/ui/consts/const-eval/promoted_errors.opt.stderr index 6b17346e6ec..0a8a8aef3cf 100644 --- a/src/test/ui/consts/const-eval/promoted_errors.opt.stderr +++ b/src/test/ui/consts/const-eval/promoted_errors.opt.stderr @@ -1,12 +1,54 @@ +warning: this arithmetic operation will overflow + --> $DIR/promoted_errors.rs:15:5 + | +LL | 0 - 1 + | ^^^^^ attempt to compute `0_u32 - 1_u32`, which would overflow + | +note: the lint level is defined here + --> $DIR/promoted_errors.rs:11:20 + | +LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)] + | ^^^^^^^^^^^^^^^^^^^ + +warning: this operation will panic at runtime + --> $DIR/promoted_errors.rs:21:5 + | +LL | 1 / 0 + | ^^^^^ attempt to divide `1_i32` by zero + | +note: the lint level is defined here + --> $DIR/promoted_errors.rs:11:41 + | +LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)] + | ^^^^^^^^^^^^^^^^^^^ + +warning: this operation will panic at runtime + --> $DIR/promoted_errors.rs:27:5 + | +LL | 1 / (1 - 1) + | ^^^^^^^^^^^ attempt to divide `1_i32` by zero + +warning: this operation will panic at runtime + --> $DIR/promoted_errors.rs:31:5 + | +LL | 1 / (false as i32) + | ^^^^^^^^^^^^^^^^^^ attempt to divide `1_i32` by zero + +warning: this operation will panic at runtime + --> $DIR/promoted_errors.rs:35:5 + | +LL | [1, 2, 3][4] + | ^^^^^^^^^^^^ index out of bounds: the length is 3 but the index is 4 + warning: any use of this value will cause an error - --> $DIR/promoted_errors.rs:20:5 + --> $DIR/promoted_errors.rs:21:5 | LL | 1 / 0 | ^^^^^ | | | attempt to divide `1_i32` by zero - | inside `div_by_zero1` at $DIR/promoted_errors.rs:20:5 - | inside `X` at $DIR/promoted_errors.rs:41:29 + | inside `div_by_zero1` at $DIR/promoted_errors.rs:21:5 + | inside `X` at $DIR/promoted_errors.rs:46:29 ... LL | / const X: () = { LL | | let _x: &'static u32 = &overflow(); @@ -26,7 +68,7 @@ LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)] = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> warning: any use of this value will cause an error - --> $DIR/promoted_errors.rs:41:28 + --> $DIR/promoted_errors.rs:46:28 | LL | / const X: () = { LL | | let _x: &'static u32 = &overflow(); @@ -42,5 +84,5 @@ LL | | }; = 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 #71800 <https://github.com/rust-lang/rust/issues/71800> -warning: 2 warnings emitted +warning: 7 warnings emitted diff --git a/src/test/ui/consts/const-eval/promoted_errors.opt_with_overflow_checks.stderr b/src/test/ui/consts/const-eval/promoted_errors.opt_with_overflow_checks.stderr index 77e7d484071..5bfd4ef92a9 100644 --- a/src/test/ui/consts/const-eval/promoted_errors.opt_with_overflow_checks.stderr +++ b/src/test/ui/consts/const-eval/promoted_errors.opt_with_overflow_checks.stderr @@ -1,3 +1,45 @@ +warning: this arithmetic operation will overflow + --> $DIR/promoted_errors.rs:15:5 + | +LL | 0 - 1 + | ^^^^^ attempt to compute `0_u32 - 1_u32`, which would overflow + | +note: the lint level is defined here + --> $DIR/promoted_errors.rs:11:20 + | +LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)] + | ^^^^^^^^^^^^^^^^^^^ + +warning: this operation will panic at runtime + --> $DIR/promoted_errors.rs:21:5 + | +LL | 1 / 0 + | ^^^^^ attempt to divide `1_i32` by zero + | +note: the lint level is defined here + --> $DIR/promoted_errors.rs:11:41 + | +LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)] + | ^^^^^^^^^^^^^^^^^^^ + +warning: this operation will panic at runtime + --> $DIR/promoted_errors.rs:27:5 + | +LL | 1 / (1 - 1) + | ^^^^^^^^^^^ attempt to divide `1_i32` by zero + +warning: this operation will panic at runtime + --> $DIR/promoted_errors.rs:31:5 + | +LL | 1 / (false as i32) + | ^^^^^^^^^^^^^^^^^^ attempt to divide `1_i32` by zero + +warning: this operation will panic at runtime + --> $DIR/promoted_errors.rs:35:5 + | +LL | [1, 2, 3][4] + | ^^^^^^^^^^^^ index out of bounds: the length is 3 but the index is 4 + warning: any use of this value will cause an error --> $DIR/promoted_errors.rs:15:5 | @@ -6,7 +48,7 @@ LL | 0 - 1 | | | attempt to compute `0_u32 - 1_u32`, which would overflow | inside `overflow` at $DIR/promoted_errors.rs:15:5 - | inside `X` at $DIR/promoted_errors.rs:38:29 + | inside `X` at $DIR/promoted_errors.rs:43:29 ... LL | / const X: () = { LL | | let _x: &'static u32 = &overflow(); @@ -26,7 +68,7 @@ LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)] = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> warning: any use of this value will cause an error - --> $DIR/promoted_errors.rs:38:28 + --> $DIR/promoted_errors.rs:43:28 | LL | / const X: () = { LL | | let _x: &'static u32 = &overflow(); @@ -41,5 +83,5 @@ LL | | }; = 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 #71800 <https://github.com/rust-lang/rust/issues/71800> -warning: 2 warnings emitted +warning: 7 warnings emitted diff --git a/src/test/ui/consts/const-eval/promoted_errors.rs b/src/test/ui/consts/const-eval/promoted_errors.rs index 5bafea1ed46..eb891de33c4 100644 --- a/src/test/ui/consts/const-eval/promoted_errors.rs +++ b/src/test/ui/consts/const-eval/promoted_errors.rs @@ -15,20 +15,25 @@ const fn overflow() -> u32 { 0 - 1 //[opt_with_overflow_checks,noopt]~^ WARN any use of this value will cause an error //[opt_with_overflow_checks,noopt]~| WARN this was previously accepted by the compiler + //~^^^ WARN this arithmetic operation will overflow } const fn div_by_zero1() -> i32 { 1 / 0 //[opt]~^ WARN any use of this value will cause an error //[opt]~| WARN this was previously accepted by the compiler but is being phased out + //~^^^ WARN this operation will panic at runtime } const fn div_by_zero2() -> i32 { 1 / (1 - 1) + //~^ WARN this operation will panic at runtime } const fn div_by_zero3() -> i32 { 1 / (false as i32) + //~^ WARN this operation will panic at runtime } const fn oob() -> i32 { [1, 2, 3][4] + //~^ WARN this operation will panic at runtime } // An unused constant containing failing promoteds. diff --git a/src/test/ui/consts/const-eval/union-const-eval-field.rs b/src/test/ui/consts/const-eval/union-const-eval-field.rs index 80263718330..a1e48cac4fa 100644 --- a/src/test/ui/consts/const-eval/union-const-eval-field.rs +++ b/src/test/ui/consts/const-eval/union-const-eval-field.rs @@ -28,6 +28,7 @@ const fn read_field3() -> Field3 { const FIELD3: Field3 = unsafe { UNION.field3 }; //~^ ERROR it is undefined behavior to use this value FIELD3 + //~^ ERROR erroneous constant used [E0080] } fn main() { diff --git a/src/test/ui/consts/const-eval/union-const-eval-field.stderr b/src/test/ui/consts/const-eval/union-const-eval-field.stderr index e5a107ff011..8f818462781 100644 --- a/src/test/ui/consts/const-eval/union-const-eval-field.stderr +++ b/src/test/ui/consts/const-eval/union-const-eval-field.stderr @@ -9,6 +9,12 @@ LL | const FIELD3: Field3 = unsafe { UNION.field3 }; __ __ __ __ __ __ __ __ │ ░░░░░░░░ } -error: aborting due to previous error +error[E0080]: erroneous constant used + --> $DIR/union-const-eval-field.rs:30:5 + | +LL | FIELD3 + | ^^^^^^ referenced constant has errors + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/issue-56164.rs b/src/test/ui/consts/issue-56164.rs index 22ac75514f6..094ca377e03 100644 --- a/src/test/ui/consts/issue-56164.rs +++ b/src/test/ui/consts/issue-56164.rs @@ -1,5 +1,7 @@ const fn foo() { (||{})() } //~^ ERROR cannot call non-const closure +//~| ERROR erroneous constant used [const_err] +//~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! const fn bad(input: fn()) { input() diff --git a/src/test/ui/consts/issue-56164.stderr b/src/test/ui/consts/issue-56164.stderr index 803424eedf3..b997aff0e83 100644 --- a/src/test/ui/consts/issue-56164.stderr +++ b/src/test/ui/consts/issue-56164.stderr @@ -8,11 +8,21 @@ LL | const fn foo() { (||{})() } = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants error: function pointers are not allowed in const fn - --> $DIR/issue-56164.rs:5:5 + --> $DIR/issue-56164.rs:7:5 | LL | input() | ^^^^^^^ -error: aborting due to 2 previous errors +error: erroneous constant used + --> $DIR/issue-56164.rs:1:18 + | +LL | const fn foo() { (||{})() } + | ^^^^^^ referenced constant has errors + | + = note: `#[deny(const_err)]` on by default + = 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 #71800 <https://github.com/rust-lang/rust/issues/71800> + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0015`. diff --git a/src/test/ui/consts/issue-66693.rs b/src/test/ui/consts/issue-66693.rs index 909bef7aefb..99d28eb773f 100644 --- a/src/test/ui/consts/issue-66693.rs +++ b/src/test/ui/consts/issue-66693.rs @@ -8,7 +8,10 @@ static _FOO: () = panic!(true); //~^ ERROR: argument to `panic!()` in a const context must have type `&str` const fn _foo() { - panic!(&1); //~ ERROR: argument to `panic!()` in a const context must have type `&str` + panic!(&1); + //~^ ERROR: argument to `panic!()` in a const context must have type `&str` + //~| ERROR: erroneous constant used [const_err] + //~| WARNING: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! } // ensure that conforming panics don't cause an error diff --git a/src/test/ui/consts/issue-66693.stderr b/src/test/ui/consts/issue-66693.stderr index 3530324bda2..b8257684983 100644 --- a/src/test/ui/consts/issue-66693.stderr +++ b/src/test/ui/consts/issue-66693.stderr @@ -22,5 +22,15 @@ LL | panic!(&1); | = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 3 previous errors +error: erroneous constant used + --> $DIR/issue-66693.rs:11:12 + | +LL | panic!(&1); + | ^^ referenced constant has errors + | + = note: `#[deny(const_err)]` on by default + = 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 #71800 <https://github.com/rust-lang/rust/issues/71800> + +error: aborting due to 4 previous errors diff --git a/src/test/ui/consts/promotion.rs b/src/test/ui/consts/promotion.rs index 580c6d62f10..e379e3aea13 100644 --- a/src/test/ui/consts/promotion.rs +++ b/src/test/ui/consts/promotion.rs @@ -4,11 +4,13 @@ //[opt_with_overflow_checks]compile-flags: -C overflow-checks=on -O // build-pass -#[allow(arithmetic_overflow)] const fn assert_static<T>(_: &'static T) {} -const fn fail() -> i32 { 1/0 } +#[allow(unconditional_panic)] +const fn fail() -> i32 { + 1/0 +} const C: i32 = { // Promoted that fails to evaluate in dead code -- this must work // (for backwards compatibility reasons). diff --git a/src/test/ui/recursion/issue-26548-recursion-via-normalize.rs b/src/test/ui/recursion/issue-26548-recursion-via-normalize.rs index 4fd5622b062..366ea7d3b3f 100644 --- a/src/test/ui/recursion/issue-26548-recursion-via-normalize.rs +++ b/src/test/ui/recursion/issue-26548-recursion-via-normalize.rs @@ -2,7 +2,6 @@ //~| NOTE ...which requires computing layout of `core::option::Option<<S as Mirror>::It>`... //~| NOTE ...which requires computing layout of `core::option::Option<S>`... //~| NOTE ...which again requires computing layout of `S`, completing the cycle -//~| NOTE cycle used when computing layout of `core::option::Option<S>` // build-fail @@ -15,5 +14,6 @@ impl<T: ?Sized> Mirror for T { struct S(Option<<S as Mirror>::It>); fn main() { + //~^ NOTE cycle used when elaborating drops for `main` let _s = S(None); } diff --git a/src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr b/src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr index 6042379a918..5b675dc9f81 100644 --- a/src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr +++ b/src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr @@ -3,7 +3,11 @@ error[E0391]: cycle detected when computing layout of `S` = note: ...which requires computing layout of `core::option::Option<<S as Mirror>::It>`... = note: ...which requires computing layout of `core::option::Option<S>`... = note: ...which again requires computing layout of `S`, completing the cycle - = note: cycle used when computing layout of `core::option::Option<S>` +note: cycle used when elaborating drops for `main` + --> $DIR/issue-26548-recursion-via-normalize.rs:16:1 + | +LL | fn main() { + | ^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/rust-2018/edition-lint-nested-empty-paths.fixed b/src/test/ui/rust-2018/edition-lint-nested-empty-paths.fixed index acb0aa420ab..5786ed7b1d5 100644 --- a/src/test/ui/rust-2018/edition-lint-nested-empty-paths.fixed +++ b/src/test/ui/rust-2018/edition-lint-nested-empty-paths.fixed @@ -17,28 +17,18 @@ crate mod foo { use crate::foo::{bar::{baz::{}}}; //~^ ERROR absolute paths must start with //~| WARN this is accepted in the current edition -//~| ERROR absolute paths must start with -//~| WARN this is accepted in the current edition use crate::foo::{bar::{XX, baz::{}}}; //~^ ERROR absolute paths must start with //~| WARN this is accepted in the current edition //~| ERROR absolute paths must start with //~| WARN this is accepted in the current edition -//~| ERROR absolute paths must start with -//~| WARN this is accepted in the current edition -//~| ERROR absolute paths must start with -//~| WARN this is accepted in the current edition use crate::foo::{bar::{baz::{}, baz1::{}}}; //~^ ERROR absolute paths must start with //~| WARN this is accepted in the current edition //~| ERROR absolute paths must start with //~| WARN this is accepted in the current edition -//~| ERROR absolute paths must start with -//~| WARN this is accepted in the current edition -//~| ERROR absolute paths must start with -//~| WARN this is accepted in the current edition fn main() { } diff --git a/src/test/ui/rust-2018/edition-lint-nested-empty-paths.rs b/src/test/ui/rust-2018/edition-lint-nested-empty-paths.rs index 4825528e97f..b7c86088c75 100644 --- a/src/test/ui/rust-2018/edition-lint-nested-empty-paths.rs +++ b/src/test/ui/rust-2018/edition-lint-nested-empty-paths.rs @@ -17,28 +17,18 @@ crate mod foo { use foo::{bar::{baz::{}}}; //~^ ERROR absolute paths must start with //~| WARN this is accepted in the current edition -//~| ERROR absolute paths must start with -//~| WARN this is accepted in the current edition use foo::{bar::{XX, baz::{}}}; //~^ ERROR absolute paths must start with //~| WARN this is accepted in the current edition //~| ERROR absolute paths must start with //~| WARN this is accepted in the current edition -//~| ERROR absolute paths must start with -//~| WARN this is accepted in the current edition -//~| ERROR absolute paths must start with -//~| WARN this is accepted in the current edition use foo::{bar::{baz::{}, baz1::{}}}; //~^ ERROR absolute paths must start with //~| WARN this is accepted in the current edition //~| ERROR absolute paths must start with //~| WARN this is accepted in the current edition -//~| ERROR absolute paths must start with -//~| WARN this is accepted in the current edition -//~| ERROR absolute paths must start with -//~| WARN this is accepted in the current edition fn main() { } diff --git a/src/test/ui/rust-2018/edition-lint-nested-empty-paths.stderr b/src/test/ui/rust-2018/edition-lint-nested-empty-paths.stderr index 8a3113729b4..e47c320f78f 100644 --- a/src/test/ui/rust-2018/edition-lint-nested-empty-paths.stderr +++ b/src/test/ui/rust-2018/edition-lint-nested-empty-paths.stderr @@ -13,16 +13,7 @@ LL | #![deny(absolute_paths_not_starting_with_crate)] = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130> error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition - --> $DIR/edition-lint-nested-empty-paths.rs:17:5 - | -LL | use foo::{bar::{baz::{}}}; - | ^^^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::foo::{bar::{baz::{}}}` - | - = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! - = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130> - -error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition - --> $DIR/edition-lint-nested-empty-paths.rs:23:5 + --> $DIR/edition-lint-nested-empty-paths.rs:21:5 | LL | use foo::{bar::{XX, baz::{}}}; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::foo::{bar::{XX, baz::{}}}` @@ -31,7 +22,7 @@ LL | use foo::{bar::{XX, baz::{}}}; = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130> error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition - --> $DIR/edition-lint-nested-empty-paths.rs:23:5 + --> $DIR/edition-lint-nested-empty-paths.rs:21:5 | LL | use foo::{bar::{XX, baz::{}}}; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::foo::{bar::{XX, baz::{}}}` @@ -40,43 +31,7 @@ LL | use foo::{bar::{XX, baz::{}}}; = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130> error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition - --> $DIR/edition-lint-nested-empty-paths.rs:23:5 - | -LL | use foo::{bar::{XX, baz::{}}}; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::foo::{bar::{XX, baz::{}}}` - | - = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! - = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130> - -error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition - --> $DIR/edition-lint-nested-empty-paths.rs:23:5 - | -LL | use foo::{bar::{XX, baz::{}}}; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::foo::{bar::{XX, baz::{}}}` - | - = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! - = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130> - -error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition - --> $DIR/edition-lint-nested-empty-paths.rs:33:5 - | -LL | use foo::{bar::{baz::{}, baz1::{}}}; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::foo::{bar::{baz::{}, baz1::{}}}` - | - = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! - = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130> - -error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition - --> $DIR/edition-lint-nested-empty-paths.rs:33:5 - | -LL | use foo::{bar::{baz::{}, baz1::{}}}; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::foo::{bar::{baz::{}, baz1::{}}}` - | - = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! - = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130> - -error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition - --> $DIR/edition-lint-nested-empty-paths.rs:33:5 + --> $DIR/edition-lint-nested-empty-paths.rs:27:5 | LL | use foo::{bar::{baz::{}, baz1::{}}}; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::foo::{bar::{baz::{}, baz1::{}}}` @@ -85,7 +40,7 @@ LL | use foo::{bar::{baz::{}, baz1::{}}}; = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130> error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition - --> $DIR/edition-lint-nested-empty-paths.rs:33:5 + --> $DIR/edition-lint-nested-empty-paths.rs:27:5 | LL | use foo::{bar::{baz::{}, baz1::{}}}; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::foo::{bar::{baz::{}, baz1::{}}}` @@ -93,5 +48,5 @@ LL | use foo::{bar::{baz::{}, baz1::{}}}; = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130> -error: aborting due to 10 previous errors +error: aborting due to 5 previous errors diff --git a/src/test/ui/rust-2018/edition-lint-nested-paths.fixed b/src/test/ui/rust-2018/edition-lint-nested-paths.fixed index 4eb1184cb6d..c4546f8c821 100644 --- a/src/test/ui/rust-2018/edition-lint-nested-paths.fixed +++ b/src/test/ui/rust-2018/edition-lint-nested-paths.fixed @@ -8,10 +8,6 @@ use crate::foo::{a, b}; //~| this is accepted in the current edition //~| ERROR absolute paths must start with //~| this is accepted in the current edition -//~| ERROR absolute paths must start with -//~| this is accepted in the current edition -//~| ERROR absolute paths must start with -//~| this is accepted in the current edition mod foo { crate fn a() {} @@ -29,8 +25,6 @@ fn main() { //~| this is accepted in the current edition //~| ERROR absolute paths must start with //~| this is accepted in the current edition - //~| ERROR absolute paths must start with - //~| this is accepted in the current edition x::a(); c(); } diff --git a/src/test/ui/rust-2018/edition-lint-nested-paths.rs b/src/test/ui/rust-2018/edition-lint-nested-paths.rs index 2a358224d9b..a7e34e407a3 100644 --- a/src/test/ui/rust-2018/edition-lint-nested-paths.rs +++ b/src/test/ui/rust-2018/edition-lint-nested-paths.rs @@ -8,10 +8,6 @@ use foo::{a, b}; //~| this is accepted in the current edition //~| ERROR absolute paths must start with //~| this is accepted in the current edition -//~| ERROR absolute paths must start with -//~| this is accepted in the current edition -//~| ERROR absolute paths must start with -//~| this is accepted in the current edition mod foo { crate fn a() {} @@ -29,8 +25,6 @@ fn main() { //~| this is accepted in the current edition //~| ERROR absolute paths must start with //~| this is accepted in the current edition - //~| ERROR absolute paths must start with - //~| this is accepted in the current edition x::a(); c(); } diff --git a/src/test/ui/rust-2018/edition-lint-nested-paths.stderr b/src/test/ui/rust-2018/edition-lint-nested-paths.stderr index 3d596022b0a..24b17f212eb 100644 --- a/src/test/ui/rust-2018/edition-lint-nested-paths.stderr +++ b/src/test/ui/rust-2018/edition-lint-nested-paths.stderr @@ -22,34 +22,7 @@ LL | use foo::{a, b}; = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130> error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition - --> $DIR/edition-lint-nested-paths.rs:6:5 - | -LL | use foo::{a, b}; - | ^^^^^^^^^^^ help: use `crate`: `crate::foo::{a, b}` - | - = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! - = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130> - -error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition - --> $DIR/edition-lint-nested-paths.rs:6:5 - | -LL | use foo::{a, b}; - | ^^^^^^^^^^^ help: use `crate`: `crate::foo::{a, b}` - | - = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! - = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130> - -error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition - --> $DIR/edition-lint-nested-paths.rs:27:13 - | -LL | use foo::{self as x, c}; - | ^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::foo::{self as x, c}` - | - = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! - = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130> - -error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition - --> $DIR/edition-lint-nested-paths.rs:27:13 + --> $DIR/edition-lint-nested-paths.rs:23:13 | LL | use foo::{self as x, c}; | ^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::foo::{self as x, c}` @@ -58,7 +31,7 @@ LL | use foo::{self as x, c}; = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130> error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition - --> $DIR/edition-lint-nested-paths.rs:27:13 + --> $DIR/edition-lint-nested-paths.rs:23:13 | LL | use foo::{self as x, c}; | ^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::foo::{self as x, c}` @@ -66,5 +39,5 @@ LL | use foo::{self as x, c}; = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130> -error: aborting due to 7 previous errors +error: aborting due to 4 previous errors diff --git a/src/test/ui/rust-2018/edition-lint-paths.fixed b/src/test/ui/rust-2018/edition-lint-paths.fixed index 46adf02a391..47f82c51dae 100644 --- a/src/test/ui/rust-2018/edition-lint-paths.fixed +++ b/src/test/ui/rust-2018/edition-lint-paths.fixed @@ -12,8 +12,6 @@ pub mod foo { use crate::bar::Bar; //~^ ERROR absolute //~| WARN this is accepted in the current edition - //~| ERROR absolute - //~| WARN this is accepted in the current edition use super::bar::Bar2; use crate::bar::Bar3; @@ -42,8 +40,6 @@ pub mod foo { use crate::bar::Bar; //~^ ERROR absolute //~| WARN this is accepted in the current edition -//~| ERROR absolute -//~| WARN this is accepted in the current edition pub mod bar { use edition_lint_paths as foo; @@ -61,8 +57,6 @@ mod baz { impl crate::foo::SomeTrait for u32 {} //~^ ERROR absolute //~| WARN this is accepted in the current edition -//~| ERROR absolute -//~| WARN this is accepted in the current edition fn main() { let x = crate::bar::Bar; diff --git a/src/test/ui/rust-2018/edition-lint-paths.rs b/src/test/ui/rust-2018/edition-lint-paths.rs index f70bf90d681..e278983da4a 100644 --- a/src/test/ui/rust-2018/edition-lint-paths.rs +++ b/src/test/ui/rust-2018/edition-lint-paths.rs @@ -12,8 +12,6 @@ pub mod foo { use bar::Bar; //~^ ERROR absolute //~| WARN this is accepted in the current edition - //~| ERROR absolute - //~| WARN this is accepted in the current edition use super::bar::Bar2; use crate::bar::Bar3; @@ -42,8 +40,6 @@ pub mod foo { use bar::Bar; //~^ ERROR absolute //~| WARN this is accepted in the current edition -//~| ERROR absolute -//~| WARN this is accepted in the current edition pub mod bar { use edition_lint_paths as foo; @@ -61,8 +57,6 @@ mod baz { impl ::foo::SomeTrait for u32 {} //~^ ERROR absolute //~| WARN this is accepted in the current edition -//~| ERROR absolute -//~| WARN this is accepted in the current edition fn main() { let x = ::bar::Bar; diff --git a/src/test/ui/rust-2018/edition-lint-paths.stderr b/src/test/ui/rust-2018/edition-lint-paths.stderr index 481c68e1033..1ded8cd3694 100644 --- a/src/test/ui/rust-2018/edition-lint-paths.stderr +++ b/src/test/ui/rust-2018/edition-lint-paths.stderr @@ -13,16 +13,7 @@ LL | #![deny(absolute_paths_not_starting_with_crate)] = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130> error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition - --> $DIR/edition-lint-paths.rs:12:9 - | -LL | use bar::Bar; - | ^^^^^^^^ help: use `crate`: `crate::bar::Bar` - | - = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! - = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130> - -error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition - --> $DIR/edition-lint-paths.rs:21:9 + --> $DIR/edition-lint-paths.rs:19:9 | LL | use bar; | ^^^ help: use `crate`: `crate::bar` @@ -31,7 +22,7 @@ LL | use bar; = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130> error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition - --> $DIR/edition-lint-paths.rs:27:9 + --> $DIR/edition-lint-paths.rs:25:9 | LL | use {main, Bar as SomethingElse}; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::{main, Bar as SomethingElse}` @@ -40,7 +31,7 @@ LL | use {main, Bar as SomethingElse}; = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130> error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition - --> $DIR/edition-lint-paths.rs:27:9 + --> $DIR/edition-lint-paths.rs:25:9 | LL | use {main, Bar as SomethingElse}; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::{main, Bar as SomethingElse}` @@ -49,7 +40,7 @@ LL | use {main, Bar as SomethingElse}; = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130> error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition - --> $DIR/edition-lint-paths.rs:27:9 + --> $DIR/edition-lint-paths.rs:25:9 | LL | use {main, Bar as SomethingElse}; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::{main, Bar as SomethingElse}` @@ -58,7 +49,7 @@ LL | use {main, Bar as SomethingElse}; = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130> error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition - --> $DIR/edition-lint-paths.rs:42:5 + --> $DIR/edition-lint-paths.rs:40:5 | LL | use bar::Bar; | ^^^^^^^^ help: use `crate`: `crate::bar::Bar` @@ -67,16 +58,7 @@ LL | use bar::Bar; = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130> error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition - --> $DIR/edition-lint-paths.rs:42:5 - | -LL | use bar::Bar; - | ^^^^^^^^ help: use `crate`: `crate::bar::Bar` - | - = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! - = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130> - -error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition - --> $DIR/edition-lint-paths.rs:56:9 + --> $DIR/edition-lint-paths.rs:52:9 | LL | use *; | ^ help: use `crate`: `crate::*` @@ -85,16 +67,7 @@ LL | use *; = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130> error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition - --> $DIR/edition-lint-paths.rs:61:6 - | -LL | impl ::foo::SomeTrait for u32 {} - | ^^^^^^^^^^^^^^^^ help: use `crate`: `crate::foo::SomeTrait` - | - = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! - = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130> - -error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition - --> $DIR/edition-lint-paths.rs:61:6 + --> $DIR/edition-lint-paths.rs:57:6 | LL | impl ::foo::SomeTrait for u32 {} | ^^^^^^^^^^^^^^^^ help: use `crate`: `crate::foo::SomeTrait` @@ -103,7 +76,7 @@ LL | impl ::foo::SomeTrait for u32 {} = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130> error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition - --> $DIR/edition-lint-paths.rs:68:13 + --> $DIR/edition-lint-paths.rs:62:13 | LL | let x = ::bar::Bar; | ^^^^^^^^^^ help: use `crate`: `crate::bar::Bar` @@ -111,5 +84,5 @@ LL | let x = ::bar::Bar; = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130> -error: aborting due to 12 previous errors +error: aborting due to 9 previous errors diff --git a/src/test/ui/rust-2018/extern-crate-rename.fixed b/src/test/ui/rust-2018/extern-crate-rename.fixed index 05b881a9b08..ea832ef3e7d 100644 --- a/src/test/ui/rust-2018/extern-crate-rename.fixed +++ b/src/test/ui/rust-2018/extern-crate-rename.fixed @@ -12,8 +12,6 @@ extern crate edition_lint_paths as my_crate; use crate::my_crate::foo; //~^ ERROR absolute paths must start //~| WARNING this is accepted in the current edition -//~| ERROR absolute paths must start -//~| WARNING this is accepted in the current edition fn main() { foo(); diff --git a/src/test/ui/rust-2018/extern-crate-rename.rs b/src/test/ui/rust-2018/extern-crate-rename.rs index 6e327be193b..b1f617dd884 100644 --- a/src/test/ui/rust-2018/extern-crate-rename.rs +++ b/src/test/ui/rust-2018/extern-crate-rename.rs @@ -12,8 +12,6 @@ extern crate edition_lint_paths as my_crate; use my_crate::foo; //~^ ERROR absolute paths must start //~| WARNING this is accepted in the current edition -//~| ERROR absolute paths must start -//~| WARNING this is accepted in the current edition fn main() { foo(); diff --git a/src/test/ui/rust-2018/extern-crate-rename.stderr b/src/test/ui/rust-2018/extern-crate-rename.stderr index f2f379ca396..4bccbc51223 100644 --- a/src/test/ui/rust-2018/extern-crate-rename.stderr +++ b/src/test/ui/rust-2018/extern-crate-rename.stderr @@ -12,14 +12,5 @@ LL | #![deny(absolute_paths_not_starting_with_crate)] = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130> -error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition - --> $DIR/extern-crate-rename.rs:12:5 - | -LL | use my_crate::foo; - | ^^^^^^^^^^^^^ help: use `crate`: `crate::my_crate::foo` - | - = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! - = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130> - -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/src/test/ui/rust-2018/extern-crate-submod.fixed b/src/test/ui/rust-2018/extern-crate-submod.fixed index fdbd893abed..9b0b0dd8ee1 100644 --- a/src/test/ui/rust-2018/extern-crate-submod.fixed +++ b/src/test/ui/rust-2018/extern-crate-submod.fixed @@ -19,9 +19,6 @@ mod m { use crate::m::edition_lint_paths::foo; //~^ ERROR absolute paths must start //~| WARNING this is accepted in the current edition -//~| ERROR absolute paths must start -//~| WARNING this is accepted in the current edition - fn main() { foo(); diff --git a/src/test/ui/rust-2018/extern-crate-submod.rs b/src/test/ui/rust-2018/extern-crate-submod.rs index c2b915849f0..dfce9128c51 100644 --- a/src/test/ui/rust-2018/extern-crate-submod.rs +++ b/src/test/ui/rust-2018/extern-crate-submod.rs @@ -19,9 +19,6 @@ mod m { use m::edition_lint_paths::foo; //~^ ERROR absolute paths must start //~| WARNING this is accepted in the current edition -//~| ERROR absolute paths must start -//~| WARNING this is accepted in the current edition - fn main() { foo(); diff --git a/src/test/ui/rust-2018/extern-crate-submod.stderr b/src/test/ui/rust-2018/extern-crate-submod.stderr index c4c3168bc97..3c75319aeda 100644 --- a/src/test/ui/rust-2018/extern-crate-submod.stderr +++ b/src/test/ui/rust-2018/extern-crate-submod.stderr @@ -12,14 +12,5 @@ LL | #![deny(absolute_paths_not_starting_with_crate)] = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130> -error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition - --> $DIR/extern-crate-submod.rs:19:5 - | -LL | use m::edition_lint_paths::foo; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::m::edition_lint_paths::foo` - | - = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! - = note: for more information, see issue #53130 <https://github.com/rust-lang/rust/issues/53130> - -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/src/test/ui/typeck/issue-90319.rs b/src/test/ui/typeck/issue-90319.rs new file mode 100644 index 00000000000..57e6ac7cf34 --- /dev/null +++ b/src/test/ui/typeck/issue-90319.rs @@ -0,0 +1,17 @@ +struct Wrapper<T>(T); + +trait Trait { + fn method(&self) {} +} + +impl<'a, T> Trait for Wrapper<&'a T> where Wrapper<T>: Trait {} + +fn get<T>() -> T { + unimplemented!() +} + +fn main() { + let thing = get::<Thing>();//~ERROR cannot find type `Thing` in this scope [E0412] + let wrapper = Wrapper(thing); + Trait::method(&wrapper); +} diff --git a/src/test/ui/typeck/issue-90319.stderr b/src/test/ui/typeck/issue-90319.stderr new file mode 100644 index 00000000000..61549dd701e --- /dev/null +++ b/src/test/ui/typeck/issue-90319.stderr @@ -0,0 +1,9 @@ +error[E0412]: cannot find type `Thing` in this scope + --> $DIR/issue-90319.rs:14:23 + | +LL | let thing = get::<Thing>(); + | ^^^^^ not found in this scope + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0412`. diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.yml b/src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.yml index 68877efc9e1..b6f70a7f183 100644 --- a/src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.yml @@ -18,7 +18,7 @@ body: id: reproducer attributes: label: Reproducer - description: Please provide the code and steps to repoduce the bug + description: Please provide the code and steps to reproduce the bug value: | I tried this code: diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 2bc393d6042..88f71931d92 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -3069,6 +3069,7 @@ Released 2018-09-13 [`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth [`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata [`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons +[`cast_enum_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_constructor [`cast_enum_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_truncation [`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless [`cast_possible_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_truncation @@ -3366,6 +3367,7 @@ Released 2018-09-13 [`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn [`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option [`or_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call +[`or_then_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_then_unwrap [`out_of_bounds_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#out_of_bounds_indexing [`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional [`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic @@ -3506,6 +3508,7 @@ Released 2018-09-13 [`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map [`unnecessary_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_find_map [`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold +[`unnecessary_join`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_join [`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_enum_constructor.rs b/src/tools/clippy/clippy_lints/src/casts/cast_enum_constructor.rs new file mode 100644 index 00000000000..1973692e105 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/casts/cast_enum_constructor.rs @@ -0,0 +1,21 @@ +use clippy_utils::diagnostics::span_lint; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; + +use super::CAST_ENUM_CONSTRUCTOR; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>) { + if matches!(cast_from.kind(), ty::FnDef(..)) + && let ExprKind::Path(path) = &cast_expr.kind + && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), _) = cx.qpath_res(path, cast_expr.hir_id) + { + span_lint( + cx, + CAST_ENUM_CONSTRUCTOR, + expr.span, + "cast of an enum tuple constructor to an integer", + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs index 6a0eabd089d..be59145afa0 100644 --- a/src/tools/clippy/clippy_lints/src/casts/mod.rs +++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs @@ -1,3 +1,4 @@ +mod cast_enum_constructor; mod cast_lossless; mod cast_possible_truncation; mod cast_possible_wrap; @@ -454,6 +455,24 @@ declare_clippy_lint! { "casting using `as` between raw pointers to slices of types with different sizes" } +declare_clippy_lint! { + /// ### What it does + /// Checks for casts from an enum tuple constructor to an integer. + /// + /// ### Why is this bad? + /// The cast is easily confused with casting a c-like enum value to an integer. + /// + /// ### Example + /// ```rust + /// enum E { X(i32) }; + /// let _ = E::X as usize; + /// ``` + #[clippy::version = "1.61.0"] + pub CAST_ENUM_CONSTRUCTOR, + suspicious, + "casts from an enum tuple constructor to an integer" +} + pub struct Casts { msrv: Option<RustcVersion>, } @@ -481,6 +500,7 @@ impl_lint_pass!(Casts => [ CHAR_LIT_AS_U8, PTR_AS_PTR, CAST_ENUM_TRUNCATION, + CAST_ENUM_CONSTRUCTOR ]); impl<'tcx> LateLintPass<'tcx> for Casts { @@ -518,6 +538,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to); } cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv); + cast_enum_constructor::check(cx, expr, cast_expr, cast_from); } } diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs index f03f34e5a4b..eae2723a7da 100644 --- a/src/tools/clippy/clippy_lints/src/collapsible_if.rs +++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs @@ -42,7 +42,7 @@ declare_clippy_lint! { /// /// Should be written: /// - /// ```rust.ignore + /// ```rust,ignore /// if x && y { /// … /// } @@ -76,7 +76,7 @@ declare_clippy_lint! { /// /// Should be written: /// - /// ```rust.ignore + /// ```rust,ignore /// if x { /// … /// } else if y { diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs index 16173580fd4..703aa458f44 100644 --- a/src/tools/clippy/clippy_lints/src/doc.rs +++ b/src/tools/clippy/clippy_lints/src/doc.rs @@ -637,12 +637,6 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { loop { match parser.parse_item(ForceCollect::No) { Ok(Some(item)) => match &item.kind { - // Tests with one of these items are ignored - ItemKind::Static(..) - | ItemKind::Const(..) - | ItemKind::ExternCrate(..) - | ItemKind::ForeignMod(..) => return false, - // We found a main function ... ItemKind::Fn(box Fn { sig, body: Some(block), .. }) if item.ident.name == sym::main => { @@ -661,8 +655,13 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { return false; } }, - // Another function was found; this case is ignored too - ItemKind::Fn(..) => return false, + // Tests with one of these items are ignored + ItemKind::Static(..) + | ItemKind::Const(..) + | ItemKind::ExternCrate(..) + | ItemKind::ForeignMod(..) + // Another function was found; this case is ignored + | ItemKind::Fn(..) => return false, _ => {}, }, Ok(None) => break, diff --git a/src/tools/clippy/clippy_lints/src/lib.register_all.rs b/src/tools/clippy/clippy_lints/src/lib.register_all.rs index 23bca5a0eab..132a4662676 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_all.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_all.rs @@ -23,6 +23,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON), LintId::of(booleans::LOGIC_BUG), LintId::of(booleans::NONMINIMAL_BOOL), + LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_TRUNCATION), LintId::of(casts::CAST_REF_TO_MUT), LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES), @@ -166,7 +167,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(methods::ITER_NTH_ZERO), LintId::of(methods::ITER_OVEREAGER_CLONED), LintId::of(methods::ITER_SKIP_NEXT), - LintId::of(methods::ITER_WITH_DRAIN), LintId::of(methods::MANUAL_FILTER_MAP), LintId::of(methods::MANUAL_FIND_MAP), LintId::of(methods::MANUAL_SATURATING_ARITHMETIC), @@ -182,6 +182,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(methods::OPTION_FILTER_MAP), LintId::of(methods::OPTION_MAP_OR_NONE), LintId::of(methods::OR_FUN_CALL), + LintId::of(methods::OR_THEN_UNWRAP), LintId::of(methods::RESULT_MAP_OR_INTO_OPTION), LintId::of(methods::SEARCH_IS_SOME), LintId::of(methods::SHOULD_IMPLEMENT_TRAIT), @@ -290,7 +291,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), LintId::of(transmute::WRONG_TRANSMUTE), LintId::of(transmuting_null::TRANSMUTING_NULL), - LintId::of(try_err::TRY_ERR), LintId::of(types::BORROWED_BOX), LintId::of(types::BOX_COLLECTION), LintId::of(types::REDUNDANT_ALLOCATION), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs b/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs index 68d6c6ce5f7..a2ce69065f9 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs @@ -47,6 +47,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(methods::NEEDLESS_SPLITN), LintId::of(methods::OPTION_AS_REF_DEREF), LintId::of(methods::OPTION_FILTER_MAP), + LintId::of(methods::OR_THEN_UNWRAP), LintId::of(methods::SEARCH_IS_SOME), LintId::of(methods::SKIP_WHILE_NEXT), LintId::of(methods::UNNECESSARY_FILTER_MAP), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_lints.rs b/src/tools/clippy/clippy_lints/src/lib.register_lints.rs index 1a45763a869..21f1ef562b5 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_lints.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_lints.rs @@ -70,6 +70,7 @@ store.register_lints(&[ cargo::REDUNDANT_FEATURE_NAMES, cargo::WILDCARD_DEPENDENCIES, case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, + casts::CAST_ENUM_CONSTRUCTOR, casts::CAST_ENUM_TRUNCATION, casts::CAST_LOSSLESS, casts::CAST_POSSIBLE_TRUNCATION, @@ -319,6 +320,7 @@ store.register_lints(&[ methods::OPTION_FILTER_MAP, methods::OPTION_MAP_OR_NONE, methods::OR_FUN_CALL, + methods::OR_THEN_UNWRAP, methods::RESULT_MAP_OR_INTO_OPTION, methods::SEARCH_IS_SOME, methods::SHOULD_IMPLEMENT_TRAIT, @@ -332,6 +334,7 @@ store.register_lints(&[ methods::UNNECESSARY_FILTER_MAP, methods::UNNECESSARY_FIND_MAP, methods::UNNECESSARY_FOLD, + methods::UNNECESSARY_JOIN, methods::UNNECESSARY_LAZY_EVALUATIONS, methods::UNNECESSARY_TO_OWNED, methods::UNWRAP_OR_ELSE_DEFAULT, diff --git a/src/tools/clippy/clippy_lints/src/lib.register_nursery.rs b/src/tools/clippy/clippy_lints/src/lib.register_nursery.rs index 8d4dde42bbe..c2fc67afba5 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_nursery.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_nursery.rs @@ -13,6 +13,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(future_not_send::FUTURE_NOT_SEND), LintId::of(index_refutable_slice::INDEX_REFUTABLE_SLICE), LintId::of(let_if_seq::USELESS_LET_IF_SEQ), + LintId::of(methods::ITER_WITH_DRAIN), LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN), LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL), LintId::of(mutex_atomic::MUTEX_ATOMIC), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs b/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs index 00d30513181..eb6534cb8ca 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs @@ -63,6 +63,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(methods::IMPLICIT_CLONE), LintId::of(methods::INEFFICIENT_TO_STRING), LintId::of(methods::MAP_UNWRAP_OR), + LintId::of(methods::UNNECESSARY_JOIN), LintId::of(misc::FLOAT_CMP), LintId::of(misc::USED_UNDERSCORE_BINDING), LintId::of(mut_mut::MUT_MUT), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_perf.rs b/src/tools/clippy/clippy_lints/src/lib.register_perf.rs index 6e9c0ee33a1..f2f5c988d8f 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_perf.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_perf.rs @@ -16,7 +16,6 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ LintId::of(methods::EXTEND_WITH_DRAIN), LintId::of(methods::ITER_NTH), LintId::of(methods::ITER_OVEREAGER_CLONED), - LintId::of(methods::ITER_WITH_DRAIN), LintId::of(methods::MANUAL_STR_REPEAT), LintId::of(methods::OR_FUN_CALL), LintId::of(methods::SINGLE_CHAR_PATTERN), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs b/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs index 4e30fc38197..6ab139b2fb6 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs @@ -62,6 +62,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(strings::STRING_SLICE), LintId::of(strings::STRING_TO_STRING), LintId::of(strings::STR_TO_STRING), + LintId::of(try_err::TRY_ERR), LintId::of(types::RC_BUFFER), LintId::of(types::RC_MUTEX), LintId::of(undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_style.rs b/src/tools/clippy/clippy_lints/src/lib.register_style.rs index 05211476ff2..dcf399cf956 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_style.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_style.rs @@ -105,7 +105,6 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME), - LintId::of(try_err::TRY_ERR), LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(unused_unit::UNUSED_UNIT), LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs b/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs index 465baa82581..fa3a88e1368 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs @@ -7,6 +7,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK), LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), + LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_TRUNCATION), LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE), LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 504235d0d1e..f2a07999144 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -23,6 +23,7 @@ // FIXME: switch to something more ergonomic here, once available. // (Currently there is no way to opt into sysroot crates without `extern crate`.) +extern crate rustc_arena; extern crate rustc_ast; extern crate rustc_ast_pretty; extern crate rustc_attr; diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs index d11dda57e6f..b8591fe0db0 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs @@ -1,19 +1,66 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::{path_to_local, search_same, SpanlessEq, SpanlessHash}; -use rustc_hir::{Arm, Expr, HirId, HirIdMap, HirIdSet, Pat, PatKind}; +use core::cmp::Ordering; +use core::iter; +use core::slice; +use rustc_arena::DroplessArena; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::def_id::DefId; +use rustc_hir::{Arm, Expr, ExprKind, HirId, HirIdMap, HirIdSet, Pat, PatKind, RangeEnd}; use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::Symbol; use std::collections::hash_map::Entry; use super::MATCH_SAME_ARMS; -pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { +#[allow(clippy::too_many_lines)] +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 { let mut h = SpanlessHash::new(cx); h.hash_expr(arm.body); h.finish() }; + let arena = DroplessArena::default(); + let normalized_pats: Vec<_> = arms + .iter() + .map(|a| NormalizedPat::from_pat(cx, &arena, a.pat)) + .collect(); + + // The furthast forwards a pattern can move without semantic changes + let forwards_blocking_idxs: Vec<_> = normalized_pats + .iter() + .enumerate() + .map(|(i, pat)| { + normalized_pats[i + 1..] + .iter() + .enumerate() + .find_map(|(j, other)| pat.has_overlapping_values(other).then(|| i + 1 + j)) + .unwrap_or(normalized_pats.len()) + }) + .collect(); + + // The furthast backwards a pattern can move without semantic changes + let backwards_blocking_idxs: Vec<_> = normalized_pats + .iter() + .enumerate() + .map(|(i, pat)| { + normalized_pats[..i] + .iter() + .enumerate() + .rev() + .zip(forwards_blocking_idxs[..i].iter().copied().rev()) + .skip_while(|&(_, forward_block)| forward_block > i) + .find_map(|((j, other), forward_block)| { + (forward_block == i || pat.has_overlapping_values(other)).then(|| j) + }) + .unwrap_or(0) + }) + .collect(); + let eq = |&(lindex, lhs): &(usize, &Arm<'_>), &(rindex, rhs): &(usize, &Arm<'_>)| -> bool { let min_index = usize::min(lindex, rindex); let max_index = usize::max(lindex, rindex); @@ -42,53 +89,316 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { } }; // Arms with a guard are ignored, those can’t always be merged together - // This is also the case for arms in-between each there is an arm with a guard - (min_index..=max_index).all(|index| arms[index].guard.is_none()) - && SpanlessEq::new(cx) - .expr_fallback(eq_fallback) - .eq_expr(lhs.body, rhs.body) - // these checks could be removed to allow unused bindings - && bindings_eq(lhs.pat, local_map.keys().copied().collect()) - && bindings_eq(rhs.pat, local_map.values().copied().collect()) + // If both arms overlap with an arm in between then these can't be merged either. + !(backwards_blocking_idxs[max_index] > min_index && forwards_blocking_idxs[min_index] < max_index) + && lhs.guard.is_none() + && rhs.guard.is_none() + && SpanlessEq::new(cx) + .expr_fallback(eq_fallback) + .eq_expr(lhs.body, rhs.body) + // these checks could be removed to allow unused bindings + && bindings_eq(lhs.pat, local_map.keys().copied().collect()) + && bindings_eq(rhs.pat, local_map.values().copied().collect()) }; let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect(); - for (&(_, i), &(_, j)) in search_same(&indexed_arms, hash, eq) { - span_lint_and_then( - cx, - MATCH_SAME_ARMS, - j.body.span, - "this `match` has identical arm bodies", - |diag| { - diag.span_note(i.body.span, "same as this"); - - // Note: this does not use `span_suggestion` on purpose: - // there is no clean way - // to remove the other arm. Building a span and suggest to replace it to "" - // makes an even more confusing error message. Also in order not to make up a - // span for the whole pattern, the suggestion is only shown when there is only - // one pattern. The user should know about `|` if they are already using it… - - let lhs = snippet(cx, i.pat.span, "<pat1>"); - let rhs = snippet(cx, j.pat.span, "<pat2>"); - - if let PatKind::Wild = j.pat.kind { - // if the last arm is _, then i could be integrated into _ - // note that i.pat cannot be _, because that would mean that we're - // hiding all the subsequent arms, and rust won't compile - diag.span_note( - i.body.span, - &format!( - "`{}` has the same arm body as the `_` wildcard, consider removing it", - lhs - ), - ); + for (&(i, arm1), &(j, arm2)) in search_same(&indexed_arms, hash, eq) { + if matches!(arm2.pat.kind, PatKind::Wild) { + span_lint_and_then( + cx, + MATCH_SAME_ARMS, + arm1.span, + "this match arm has an identical body to the `_` wildcard arm", + |diag| { + diag.span_suggestion( + arm1.span, + "try removing the arm", + String::new(), + Applicability::MaybeIncorrect, + ) + .help("or try changing either arm body") + .span_note(arm2.span, "`_` wildcard arm here"); + }, + ); + } else { + let back_block = backwards_blocking_idxs[j]; + let (keep_arm, move_arm) = if back_block < i || (back_block == 0 && forwards_blocking_idxs[i] <= j) { + (arm1, arm2) + } else { + (arm2, arm1) + }; + + span_lint_and_then( + cx, + MATCH_SAME_ARMS, + keep_arm.span, + "this match arm has an identical body to another arm", + |diag| { + let move_pat_snip = snippet(cx, move_arm.pat.span, "<pat2>"); + let keep_pat_snip = snippet(cx, keep_arm.pat.span, "<pat1>"); + + diag.span_suggestion( + keep_arm.pat.span, + "try merging the arm patterns", + format!("{} | {}", keep_pat_snip, move_pat_snip), + Applicability::MaybeIncorrect, + ) + .help("or try changing either arm body") + .span_note(move_arm.span, "other arm here"); + }, + ); + } + } +} + +#[derive(Clone, Copy)] +enum NormalizedPat<'a> { + Wild, + Struct(Option<DefId>, &'a [(Symbol, Self)]), + Tuple(Option<DefId>, &'a [Self]), + Or(&'a [Self]), + Path(Option<DefId>), + LitStr(Symbol), + LitBytes(&'a [u8]), + LitInt(u128), + LitBool(bool), + Range(PatRange), + /// A slice pattern. If the second value is `None`, then this matches an exact size. Otherwise + /// the first value contains everything before the `..` wildcard pattern, and the second value + /// contains everything afterwards. Note that either side, or both sides, may contain zero + /// patterns. + Slice(&'a [Self], Option<&'a [Self]>), +} + +#[derive(Clone, Copy)] +struct PatRange { + start: u128, + end: u128, + bounds: RangeEnd, +} +impl PatRange { + fn contains(&self, x: u128) -> bool { + x >= self.start + && match self.bounds { + RangeEnd::Included => x <= self.end, + RangeEnd::Excluded => x < self.end, + } + } + + fn overlaps(&self, other: &Self) -> bool { + // Note: Empty ranges are impossible, so this is correct even though it would return true if an + // empty exclusive range were to reside within an inclusive range. + (match self.bounds { + RangeEnd::Included => self.end >= other.start, + RangeEnd::Excluded => self.end > other.start, + } && match other.bounds { + RangeEnd::Included => self.start <= other.end, + RangeEnd::Excluded => self.start < other.end, + }) + } +} + +/// Iterates over the pairs of fields with matching names. +fn iter_matching_struct_fields<'a>( + left: &'a [(Symbol, NormalizedPat<'a>)], + right: &'a [(Symbol, NormalizedPat<'a>)], +) -> impl Iterator<Item = (&'a NormalizedPat<'a>, &'a NormalizedPat<'a>)> + 'a { + struct Iter<'a>( + slice::Iter<'a, (Symbol, NormalizedPat<'a>)>, + slice::Iter<'a, (Symbol, NormalizedPat<'a>)>, + ); + impl<'a> Iterator for Iter<'a> { + type Item = (&'a NormalizedPat<'a>, &'a NormalizedPat<'a>); + fn next(&mut self) -> Option<Self::Item> { + // Note: all the fields in each slice are sorted by symbol value. + let mut left = self.0.next()?; + let mut right = self.1.next()?; + loop { + match left.0.cmp(&right.0) { + Ordering::Equal => return Some((&left.1, &right.1)), + Ordering::Less => left = self.0.next()?, + Ordering::Greater => right = self.1.next()?, + } + } + } + } + Iter(left.iter(), right.iter()) +} + +#[allow(clippy::similar_names)] +impl<'a> NormalizedPat<'a> { + #[allow(clippy::too_many_lines)] + fn from_pat(cx: &LateContext<'_>, arena: &'a DroplessArena, pat: &'a Pat<'_>) -> Self { + match pat.kind { + PatKind::Wild | PatKind::Binding(.., None) => Self::Wild, + PatKind::Binding(.., Some(pat)) | PatKind::Box(pat) | PatKind::Ref(pat, _) => { + Self::from_pat(cx, arena, pat) + }, + PatKind::Struct(ref path, fields, _) => { + let fields = + arena.alloc_from_iter(fields.iter().map(|f| (f.ident.name, Self::from_pat(cx, arena, f.pat)))); + fields.sort_by_key(|&(name, _)| name); + Self::Struct(cx.qpath_res(path, pat.hir_id).opt_def_id(), fields) + }, + PatKind::TupleStruct(ref path, pats, wild_idx) => { + let adt = match cx.typeck_results().pat_ty(pat).ty_adt_def() { + Some(x) => x, + None => return Self::Wild, + }; + let (var_id, variant) = if adt.is_enum() { + match cx.qpath_res(path, pat.hir_id).opt_def_id() { + Some(x) => (Some(x), adt.variant_with_ctor_id(x)), + None => return Self::Wild, + } } else { - diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs,)) - .help("...or consider changing the match arm bodies"); + (None, adt.non_enum_variant()) + }; + let (front, back) = match wild_idx { + Some(i) => pats.split_at(i), + None => (pats, [].as_slice()), + }; + let pats = arena.alloc_from_iter( + front + .iter() + .map(|pat| Self::from_pat(cx, arena, pat)) + .chain(iter::repeat_with(|| Self::Wild).take(variant.fields.len() - pats.len())) + .chain(back.iter().map(|pat| Self::from_pat(cx, arena, pat))), + ); + Self::Tuple(var_id, pats) + }, + PatKind::Or(pats) => Self::Or(arena.alloc_from_iter(pats.iter().map(|pat| Self::from_pat(cx, arena, pat)))), + PatKind::Path(ref path) => Self::Path(cx.qpath_res(path, pat.hir_id).opt_def_id()), + PatKind::Tuple(pats, wild_idx) => { + let field_count = match cx.typeck_results().pat_ty(pat).kind() { + ty::Tuple(subs) => subs.len(), + _ => return Self::Wild, + }; + let (front, back) = match wild_idx { + Some(i) => pats.split_at(i), + None => (pats, [].as_slice()), + }; + let pats = arena.alloc_from_iter( + front + .iter() + .map(|pat| Self::from_pat(cx, arena, pat)) + .chain(iter::repeat_with(|| Self::Wild).take(field_count - pats.len())) + .chain(back.iter().map(|pat| Self::from_pat(cx, arena, pat))), + ); + Self::Tuple(None, pats) + }, + PatKind::Lit(e) => match &e.kind { + // TODO: Handle negative integers. They're currently treated as a wild match. + ExprKind::Lit(lit) => match lit.node { + LitKind::Str(sym, _) => Self::LitStr(sym), + LitKind::ByteStr(ref bytes) => Self::LitBytes(&**bytes), + LitKind::Byte(val) => Self::LitInt(val.into()), + LitKind::Char(val) => Self::LitInt(val.into()), + LitKind::Int(val, _) => Self::LitInt(val), + LitKind::Bool(val) => Self::LitBool(val), + LitKind::Float(..) | LitKind::Err(_) => Self::Wild, + }, + _ => Self::Wild, + }, + PatKind::Range(start, end, bounds) => { + // TODO: Handle negative integers. They're currently treated as a wild match. + let start = match start { + None => 0, + Some(e) => match &e.kind { + ExprKind::Lit(lit) => match lit.node { + LitKind::Int(val, _) => val, + LitKind::Char(val) => val.into(), + LitKind::Byte(val) => val.into(), + _ => return Self::Wild, + }, + _ => return Self::Wild, + }, + }; + let (end, bounds) = match end { + None => (u128::MAX, RangeEnd::Included), + Some(e) => match &e.kind { + ExprKind::Lit(lit) => match lit.node { + LitKind::Int(val, _) => (val, bounds), + LitKind::Char(val) => (val.into(), bounds), + LitKind::Byte(val) => (val.into(), bounds), + _ => return Self::Wild, + }, + _ => return Self::Wild, + }, + }; + Self::Range(PatRange { start, end, bounds }) + }, + PatKind::Slice(front, wild_pat, back) => Self::Slice( + arena.alloc_from_iter(front.iter().map(|pat| Self::from_pat(cx, arena, pat))), + wild_pat.map(|_| &*arena.alloc_from_iter(back.iter().map(|pat| Self::from_pat(cx, arena, pat)))), + ), + } + } + + /// Checks if two patterns overlap in the values they can match assuming they are for the same + /// type. + fn has_overlapping_values(&self, other: &Self) -> bool { + match (*self, *other) { + (Self::Wild, _) | (_, Self::Wild) => true, + (Self::Or(pats), ref other) | (ref other, Self::Or(pats)) => { + pats.iter().any(|pat| pat.has_overlapping_values(other)) + }, + (Self::Struct(lpath, lfields), Self::Struct(rpath, rfields)) => { + if lpath != rpath { + return false; + } + iter_matching_struct_fields(lfields, rfields).all(|(lpat, rpat)| lpat.has_overlapping_values(rpat)) + }, + (Self::Tuple(lpath, lpats), Self::Tuple(rpath, rpats)) => { + if lpath != rpath { + return false; } + lpats + .iter() + .zip(rpats.iter()) + .all(|(lpat, rpat)| lpat.has_overlapping_values(rpat)) + }, + (Self::Path(x), Self::Path(y)) => x == y, + (Self::LitStr(x), Self::LitStr(y)) => x == y, + (Self::LitBytes(x), Self::LitBytes(y)) => x == y, + (Self::LitInt(x), Self::LitInt(y)) => x == y, + (Self::LitBool(x), Self::LitBool(y)) => x == y, + (Self::Range(ref x), Self::Range(ref y)) => x.overlaps(y), + (Self::Range(ref range), Self::LitInt(x)) | (Self::LitInt(x), Self::Range(ref range)) => range.contains(x), + (Self::Slice(lpats, None), Self::Slice(rpats, None)) => { + lpats.len() == rpats.len() && lpats.iter().zip(rpats.iter()).all(|(x, y)| x.has_overlapping_values(y)) }, - ); + (Self::Slice(pats, None), Self::Slice(front, Some(back))) + | (Self::Slice(front, Some(back)), Self::Slice(pats, None)) => { + // Here `pats` is an exact size match. If the combined lengths of `front` and `back` are greater + // then the minium length required will be greater than the length of `pats`. + if pats.len() < front.len() + back.len() { + return false; + } + pats[..front.len()] + .iter() + .zip(front.iter()) + .chain(pats[pats.len() - back.len()..].iter().zip(back.iter())) + .all(|(x, y)| x.has_overlapping_values(y)) + }, + (Self::Slice(lfront, Some(lback)), Self::Slice(rfront, Some(rback))) => lfront + .iter() + .zip(rfront.iter()) + .chain(lback.iter().rev().zip(rback.iter().rev())) + .all(|(x, y)| x.has_overlapping_values(y)), + + // Enums can mix unit variants with tuple/struct variants. These can never overlap. + (Self::Path(_), Self::Tuple(..) | Self::Struct(..)) + | (Self::Tuple(..) | Self::Struct(..), Self::Path(_)) => false, + + // Tuples can be matched like a struct. + (Self::Tuple(x, _), Self::Struct(y, _)) | (Self::Struct(x, _), Self::Tuple(y, _)) => { + // TODO: check fields here. + x == y + }, + + // TODO: Lit* with Path, Range with Path, LitBytes with Slice + _ => true, + } } } diff --git a/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs index e1212c31cfb..f447940ea3b 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs @@ -1,83 +1,73 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_sugg_for_edges; use clippy_utils::is_trait_method; -use clippy_utils::source::snippet; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; -use rustc_hir as hir; +use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::symbol::sym; +use rustc_span::{symbol::sym, Span}; use super::MAP_FLATTEN; /// lint use of `map().flatten()` for `Iterators` and 'Options' -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - recv: &'tcx hir::Expr<'_>, - map_arg: &'tcx hir::Expr<'_>, -) { - // lint if caller of `.map().flatten()` is an Iterator - if is_trait_method(cx, expr, sym::Iterator) { - let map_closure_ty = cx.typeck_results().expr_ty(map_arg); - let is_map_to_option = match map_closure_ty.kind() { - ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => { - let map_closure_sig = match map_closure_ty.kind() { - ty::Closure(_, substs) => substs.as_closure().sig(), - _ => map_closure_ty.fn_sig(cx.tcx), - }; - let map_closure_return_ty = cx.tcx.erase_late_bound_regions(map_closure_sig.output()); - is_type_diagnostic_item(cx, map_closure_return_ty, sym::Option) - }, - _ => false, - }; - - let method_to_use = if is_map_to_option { - // `(...).map(...)` has type `impl Iterator<Item=Option<...>> - "filter_map" - } else { - // `(...).map(...)` has type `impl Iterator<Item=impl Iterator<...>> - "flat_map" - }; - let func_snippet = snippet(cx, map_arg.span, ".."); - let hint = format!(".{0}({1})", method_to_use, func_snippet); - span_lint_and_sugg( +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, map_arg: &Expr<'_>, map_span: Span) { + if let Some((caller_ty_name, method_to_use)) = try_get_caller_ty_name_and_method_name(cx, expr, recv, map_arg) { + let mut applicability = Applicability::MachineApplicable; + let help_msgs = [ + &format!("try replacing `map` with `{}`", method_to_use), + "and remove the `.flatten()`", + ]; + let closure_snippet = snippet_with_applicability(cx, map_arg.span, "..", &mut applicability); + span_lint_and_sugg_for_edges( cx, MAP_FLATTEN, - expr.span.with_lo(recv.span.hi()), - "called `map(..).flatten()` on an `Iterator`", - &format!("try using `{}` instead", method_to_use), - hint, - Applicability::MachineApplicable, + expr.span.with_lo(map_span.lo()), + &format!("called `map(..).flatten()` on `{}`", caller_ty_name), + &help_msgs, + format!("{}({})", method_to_use, closure_snippet), + applicability, ); } +} - // lint if caller of `.map().flatten()` is an Option or Result - let caller_type = match cx.typeck_results().expr_ty(recv).kind() { - ty::Adt(adt, _) => { +fn try_get_caller_ty_name_and_method_name( + cx: &LateContext<'_>, + expr: &Expr<'_>, + caller_expr: &Expr<'_>, + map_arg: &Expr<'_>, +) -> Option<(&'static str, &'static str)> { + if is_trait_method(cx, expr, sym::Iterator) { + if is_map_to_option(cx, map_arg) { + // `(...).map(...)` has type `impl Iterator<Item=Option<...>> + Some(("Iterator", "filter_map")) + } else { + // `(...).map(...)` has type `impl Iterator<Item=impl Iterator<...>> + Some(("Iterator", "flat_map")) + } + } else { + if let ty::Adt(adt, _) = cx.typeck_results().expr_ty(caller_expr).kind() { if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) { - "Option" + return Some(("Option", "and_then")); } else if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) { - "Result" - } else { - return; + return Some(("Result", "and_then")); } - }, - _ => { - return; - }, - }; + } + None + } +} - let func_snippet = snippet(cx, map_arg.span, ".."); - let hint = format!(".and_then({})", func_snippet); - let lint_info = format!("called `map(..).flatten()` on an `{}`", caller_type); - span_lint_and_sugg( - cx, - MAP_FLATTEN, - expr.span.with_lo(recv.span.hi()), - &lint_info, - "try using `and_then` instead", - hint, - Applicability::MachineApplicable, - ); +fn is_map_to_option(cx: &LateContext<'_>, map_arg: &Expr<'_>) -> bool { + let map_closure_ty = cx.typeck_results().expr_ty(map_arg); + match map_closure_ty.kind() { + ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => { + let map_closure_sig = match map_closure_ty.kind() { + ty::Closure(_, substs) => substs.as_closure().sig(), + _ => map_closure_ty.fn_sig(cx.tcx), + }; + let map_closure_return_ty = cx.tcx.erase_late_bound_regions(map_closure_sig.output()); + is_type_diagnostic_item(cx, map_closure_return_ty, sym::Option) + }, + _ => false, + } } diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 5edd22cd14c..9d4e1fa3994 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -45,6 +45,7 @@ mod option_as_ref_deref; mod option_map_or_none; mod option_map_unwrap_or; mod or_fun_call; +mod or_then_unwrap; mod search_is_some; mod single_char_add_str; mod single_char_insert_string; @@ -59,6 +60,7 @@ mod uninit_assumed_init; mod unnecessary_filter_map; mod unnecessary_fold; mod unnecessary_iter_cloned; +mod unnecessary_join; mod unnecessary_lazy_eval; mod unnecessary_to_owned; mod unwrap_or_else_default; @@ -780,6 +782,42 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does + /// Checks for `.or(…).unwrap()` calls to Options and Results. + /// + /// ### Why is this bad? + /// You should use `.unwrap_or(…)` instead for clarity. + /// + /// ### Example + /// ```rust + /// # let fallback = "fallback"; + /// // Result + /// # type Error = &'static str; + /// # let result: Result<&str, Error> = Err("error"); + /// let value = result.or::<Error>(Ok(fallback)).unwrap(); + /// + /// // Option + /// # let option: Option<&str> = None; + /// let value = option.or(Some(fallback)).unwrap(); + /// ``` + /// Use instead: + /// ```rust + /// # let fallback = "fallback"; + /// // Result + /// # let result: Result<&str, &str> = Err("error"); + /// let value = result.unwrap_or(fallback); + /// + /// // Option + /// # let option: Option<&str> = None; + /// let value = option.unwrap_or(fallback); + /// ``` + #[clippy::version = "1.61.0"] + pub OR_THEN_UNWRAP, + complexity, + "checks for `.or(…).unwrap()` calls to Options and Results." +} + +declare_clippy_lint! { + /// ### What it does /// Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`, /// etc., and suggests to use `unwrap_or_else` instead /// @@ -1140,7 +1178,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.61.0"] pub ITER_WITH_DRAIN, - perf, + nursery, "replace `.drain(..)` with `.into_iter()`" } @@ -2012,6 +2050,35 @@ declare_clippy_lint! { "unnecessary calls to `to_owned`-like functions" } +declare_clippy_lint! { + /// ### What it does + /// Checks for use of `.collect::<Vec<String>>().join("")` on iterators. + /// + /// ### Why is this bad? + /// `.collect::<String>()` is more concise and usually more performant + /// + /// ### Example + /// ```rust + /// let vector = vec!["hello", "world"]; + /// let output = vector.iter().map(|item| item.to_uppercase()).collect::<Vec<String>>().join(""); + /// println!("{}", output); + /// ``` + /// The correct use would be: + /// ```rust + /// let vector = vec!["hello", "world"]; + /// let output = vector.iter().map(|item| item.to_uppercase()).collect::<String>(); + /// println!("{}", output); + /// ``` + /// ### Known problems + /// While `.collect::<String>()` is more performant in most cases, there are cases where + /// using `.collect::<String>()` over `.collect::<Vec<String>>().join("")` + /// will prevent loop unrolling and will result in a negative performance impact. + #[clippy::version = "1.61.0"] + pub UNNECESSARY_JOIN, + pedantic, + "using `.collect::<Vec<String>>().join(\"\")` on an iterator" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Option<RustcVersion>, @@ -2039,6 +2106,7 @@ impl_lint_pass!(Methods => [ OPTION_MAP_OR_NONE, BIND_INSTEAD_OF_MAP, OR_FUN_CALL, + OR_THEN_UNWRAP, EXPECT_FUN_CALL, CHARS_NEXT_CMP, CHARS_LAST_CMP, @@ -2096,6 +2164,7 @@ impl_lint_pass!(Methods => [ MANUAL_SPLIT_ONCE, NEEDLESS_SPLITN, UNNECESSARY_TO_OWNED, + UNNECESSARY_JOIN, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -2377,7 +2446,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio flat_map_option::check(cx, expr, arg, span); }, (name @ "flatten", args @ []) => match method_call(recv) { - Some(("map", [recv, map_arg], _)) => map_flatten::check(cx, expr, recv, map_arg), + Some(("map", [recv, map_arg], map_span)) => map_flatten::check(cx, expr, recv, map_arg, map_span), Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args), _ => {}, }, @@ -2391,6 +2460,11 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio ("is_file", []) => filetype_is_file::check(cx, expr, recv), ("is_none", []) => check_is_some_is_none(cx, expr, recv, false), ("is_some", []) => check_is_some_is_none(cx, expr, recv, true), + ("join", [join_arg]) => { + if let Some(("collect", _, span)) = method_call(recv) { + unnecessary_join::check(cx, expr, recv, join_arg, span); + } + }, ("last", args @ []) | ("skip", args @ [_]) => { if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) { if let ("cloned", []) = (name2, args2) { @@ -2474,6 +2548,9 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio Some(("get_mut", [recv, get_arg], _)) => { get_unwrap::check(cx, expr, recv, get_arg, true); }, + Some(("or", [recv, or_arg], or_span)) => { + or_then_unwrap::check(cx, expr, recv, or_arg, or_span); + }, _ => {}, } unwrap_used::check(cx, expr, recv); diff --git a/src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs new file mode 100644 index 00000000000..be5768c3545 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs @@ -0,0 +1,68 @@ +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{diagnostics::span_lint_and_sugg, is_lang_ctor}; +use rustc_errors::Applicability; +use rustc_hir::{lang_items::LangItem, Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_span::{sym, Span}; + +use super::OR_THEN_UNWRAP; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + unwrap_expr: &Expr<'_>, + recv: &'tcx Expr<'tcx>, + or_arg: &'tcx Expr<'_>, + or_span: Span, +) { + let ty = cx.typeck_results().expr_ty(recv); // get type of x (we later check if it's Option or Result) + let title; + let or_arg_content: Span; + + if is_type_diagnostic_item(cx, ty, sym::Option) { + title = "found `.or(Some(…)).unwrap()`"; + if let Some(content) = get_content_if_ctor_matches(cx, or_arg, LangItem::OptionSome) { + or_arg_content = content; + } else { + return; + } + } else if is_type_diagnostic_item(cx, ty, sym::Result) { + title = "found `.or(Ok(…)).unwrap()`"; + if let Some(content) = get_content_if_ctor_matches(cx, or_arg, LangItem::ResultOk) { + or_arg_content = content; + } else { + return; + } + } else { + // Someone has implemented a struct with .or(...).unwrap() chaining, + // but it's not an Option or a Result, so bail + return; + } + + let mut applicability = Applicability::MachineApplicable; + let suggestion = format!( + "unwrap_or({})", + snippet_with_applicability(cx, or_arg_content, "..", &mut applicability) + ); + + span_lint_and_sugg( + cx, + OR_THEN_UNWRAP, + unwrap_expr.span.with_lo(or_span.lo()), + title, + "try this", + suggestion, + applicability, + ); +} + +fn get_content_if_ctor_matches(cx: &LateContext<'_>, expr: &Expr<'_>, item: LangItem) -> Option<Span> { + if let ExprKind::Call(some_expr, [arg]) = expr.kind + && let ExprKind::Path(qpath) = &some_expr.kind + && is_lang_ctor(cx, qpath, item) + { + Some(arg.span) + } else { + None + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_join.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_join.rs new file mode 100644 index 00000000000..973b8a7e6bf --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_join.rs @@ -0,0 +1,41 @@ +use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_diagnostic_item}; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::{Ref, Slice}; +use rustc_span::{sym, Span}; + +use super::UNNECESSARY_JOIN; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + join_self_arg: &'tcx Expr<'tcx>, + join_arg: &'tcx Expr<'tcx>, + span: Span, +) { + let applicability = Applicability::MachineApplicable; + let collect_output_adjusted_type = cx.typeck_results().expr_ty_adjusted(join_self_arg); + if_chain! { + // the turbofish for collect is ::<Vec<String>> + if let Ref(_, ref_type, _) = collect_output_adjusted_type.kind(); + if let Slice(slice) = ref_type.kind(); + if is_type_diagnostic_item(cx, *slice, sym::String); + // the argument for join is "" + if let ExprKind::Lit(spanned) = &join_arg.kind; + if let LitKind::Str(symbol, _) = spanned.node; + if symbol.is_empty(); + then { + span_lint_and_sugg( + cx, + UNNECESSARY_JOIN, + span.with_hi(expr.span.hi()), + r#"called `.collect<Vec<String>>().join("")` on an iterator"#, + "try using", + "collect::<String>()".to_owned(), + applicability, + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 1e2765263c8..2369be70812 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{eager_or_lazy, usage}; @@ -48,20 +48,19 @@ pub(super) fn check<'tcx>( Applicability::MaybeIncorrect }; - span_lint_and_sugg( - cx, - UNNECESSARY_LAZY_EVALUATIONS, - expr.span, - msg, - &format!("use `{}` instead", simplify_using), - format!( - "{0}.{1}({2})", - snippet(cx, recv.span, ".."), - simplify_using, - snippet(cx, body_expr.span, ".."), - ), - applicability, - ); + // This is a duplicate of what's happening in clippy_lints::methods::method_call, + // which isn't ideal, We want to get the method call span, + // but prefer to avoid changing the signature of the function itself. + if let hir::ExprKind::MethodCall(_, _, span) = expr.kind { + span_lint_and_then(cx, UNNECESSARY_LAZY_EVALUATIONS, expr.span, msg, |diag| { + diag.span_suggestion( + span, + &format!("use `{}(..)` instead", simplify_using), + format!("{}({})", simplify_using, snippet(cx, body_expr.span, "..")), + applicability, + ); + }); + } } } } diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index 7916fb8e3b4..1555758fc4a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -2,7 +2,9 @@ use super::implicit_clone::is_clone_like; use super::unnecessary_iter_cloned::{self, is_into_iter}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::{get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs}; +use clippy_utils::ty::{ + contains_ty, get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs, +}; use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item}; use rustc_errors::Applicability; use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind}; @@ -114,7 +116,12 @@ fn check_addr_of_expr( parent.span, &format!("unnecessary use of `{}`", method_name), "use", - format!("{:&>width$}{}", "", receiver_snippet, width = n_target_refs - n_receiver_refs), + format!( + "{:&>width$}{}", + "", + receiver_snippet, + width = n_target_refs - n_receiver_refs + ), Applicability::MachineApplicable, ); return true; @@ -182,20 +189,10 @@ fn check_into_iter_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: if let Some(item_ty) = get_iterator_item_ty(cx, parent_ty); if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); then { - if unnecessary_iter_cloned::check_for_loop_iter( - cx, - parent, - method_name, - receiver, - true, - ) { + if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) { return true; } - let cloned_or_copied = if is_copy(cx, item_ty) { - "copied" - } else { - "cloned" - }; + let cloned_or_copied = if is_copy(cx, item_ty) { "copied" } else { "cloned" }; // The next suggestion may be incorrect because the removal of the `to_owned`-like // function could cause the iterator to hold a reference to a resource that is used // mutably. See https://github.com/rust-lang/rust-clippy/issues/8148. @@ -243,10 +240,11 @@ fn check_other_call_arg<'tcx>( if if trait_predicate.def_id() == deref_trait_id { if let [projection_predicate] = projection_predicates[..] { let normalized_ty = - cx.tcx.subst_and_normalize_erasing_regions(call_substs, cx.param_env, projection_predicate.term); + cx.tcx + .subst_and_normalize_erasing_regions(call_substs, cx.param_env, projection_predicate.term); implements_trait(cx, receiver_ty, deref_trait_id, &[]) - && get_associated_type(cx, receiver_ty, deref_trait_id, - "Target").map_or(false, |ty| ty::Term::Ty(ty) == normalized_ty) + && get_associated_type(cx, receiver_ty, deref_trait_id, "Target") + .map_or(false, |ty| ty::Term::Ty(ty) == normalized_ty) } else { false } @@ -254,7 +252,7 @@ fn check_other_call_arg<'tcx>( let composed_substs = compose_substs( cx, &trait_predicate.trait_ref.substs.iter().skip(1).collect::<Vec<_>>()[..], - call_substs + call_substs, ); implements_trait(cx, receiver_ty, as_ref_trait_id, &composed_substs) } else { @@ -264,6 +262,12 @@ fn check_other_call_arg<'tcx>( // `Target = T`. if n_refs > 0 || is_copy(cx, receiver_ty) || trait_predicate.def_id() != deref_trait_id; let n_refs = max(n_refs, if is_copy(cx, receiver_ty) { 0 } else { 1 }); + // If the trait is `AsRef` and the input type variable `T` occurs in the output type, then + // `T` must not be instantiated with a reference + // (https://github.com/rust-lang/rust-clippy/issues/8507). + if (n_refs == 0 && !receiver_ty.is_ref()) + || trait_predicate.def_id() != as_ref_trait_id + || !contains_ty(fn_sig.output(), input); if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); then { span_lint_and_sugg( @@ -339,11 +343,7 @@ fn get_input_traits_and_projections<'tcx>( if let Some(arg) = substs.iter().next(); if let GenericArgKind::Type(arg_ty) = arg.unpack(); if arg_ty == input; - then { - true - } else { - false - } + then { true } else { false } } }; match predicate.kind().skip_binder() { diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs index 9c776437d7f..ba1997e70e1 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -436,7 +436,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>( DerefTy::Path, None, ), - Some(sym::Cow) => { + Some(sym::Cow) if mutability == Mutability::Not => { let ty_name = name.args .and_then(|args| { args.args.iter().find_map(|a| match a { diff --git a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs index 961cdb317e7..66b79513032 100644 --- a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs +++ b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; -use rustc_ast::{ptr::P, Crate, Item, ItemKind, MacroDef, ModKind, UseTreeKind, VisibilityKind}; +use rustc_ast::{ptr::P, Crate, Item, ItemKind, MacroDef, ModKind, UseTreeKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -76,14 +76,13 @@ fn check_mod(cx: &EarlyContext<'_>, items: &[P<Item>]) { ); } - for single_use in &single_use_usages { - if !imports_reused_with_self.contains(&single_use.0) { - let can_suggest = single_use.2; + for (name, span, can_suggest) in single_use_usages { + if !imports_reused_with_self.contains(&name) { if can_suggest { span_lint_and_sugg( cx, SINGLE_COMPONENT_PATH_IMPORTS, - single_use.1, + span, "this import is redundant", "remove it entirely", String::new(), @@ -93,7 +92,7 @@ fn check_mod(cx: &EarlyContext<'_>, items: &[P<Item>]) { span_lint_and_help( cx, SINGLE_COMPONENT_PATH_IMPORTS, - single_use.1, + span, "this import is redundant", None, "remove this import", @@ -124,14 +123,11 @@ fn track_uses( ItemKind::Use(use_tree) => { let segments = &use_tree.prefix.segments; - let should_report = - |name: &Symbol| !macros.contains(name) || matches!(item.vis.kind, VisibilityKind::Inherited); - // keep track of `use some_module;` usages if segments.len() == 1 { if let UseTreeKind::Simple(None, _, _) = use_tree.kind { let name = segments[0].ident.name; - if should_report(&name) { + if !macros.contains(&name) { single_use_usages.push((name, item.span, true)); } } @@ -146,7 +142,7 @@ fn track_uses( if segments.len() == 1 { if let UseTreeKind::Simple(None, _, _) = tree.0.kind { let name = segments[0].ident.name; - if should_report(&name) { + if !macros.contains(&name) { single_use_usages.push((name, tree.0.span, false)); } } diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs index 23cb9d40dfd..02569bd3a47 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/mod.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs @@ -415,7 +415,8 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { // And see https://github.com/rust-lang/rust/issues/51911 for dereferencing raw pointers. let const_context = in_constant(cx, e.hir_id); - let from_ty = cx.typeck_results().expr_ty(arg); + let from_ty = cx.typeck_results().expr_ty_adjusted(arg); + // Adjustments for `to_ty` happen after the call to `transmute`, so don't use them. let to_ty = cx.typeck_results().expr_ty(e); // If useless_transmute is triggered, the other lints can be skipped. diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs index 6edff224092..f5e21267a89 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -3,8 +3,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_c_void; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_middle::ty::subst::Subst; -use rustc_middle::ty::{self, Ty, TypeAndMut}; +use rustc_middle::ty::subst::{Subst, SubstsRef}; +use rustc_middle::ty::{self, IntTy, Ty, TypeAndMut, UintTy}; use rustc_span::Span; #[allow(clippy::too_many_lines)] @@ -23,7 +23,8 @@ pub(super) fn check<'tcx>( unsized_ty, to_ty: to_sub_ty, } => match reduce_ty(cx, to_sub_ty) { - ReducedTy::IntArray | ReducedTy::TypeErasure => break, + ReducedTy::TypeErasure => break, + ReducedTy::UnorderedFields(ty) if is_size_pair(ty) => break, ReducedTy::Ref(to_sub_ty) => { from_ty = unsized_ty; to_ty = to_sub_ty; @@ -48,7 +49,8 @@ pub(super) fn check<'tcx>( unsized_ty, from_ty: from_sub_ty, } => match reduce_ty(cx, from_sub_ty) { - ReducedTy::IntArray | ReducedTy::TypeErasure => break, + ReducedTy::TypeErasure => break, + ReducedTy::UnorderedFields(ty) if is_size_pair(ty) => break, ReducedTy::Ref(from_sub_ty) => { from_ty = from_sub_ty; to_ty = unsized_ty; @@ -123,9 +125,19 @@ pub(super) fn check<'tcx>( from_ty: from_sub_ty, to_ty: to_sub_ty, } => match (reduce_ty(cx, from_sub_ty), reduce_ty(cx, to_sub_ty)) { - (ReducedTy::IntArray | ReducedTy::TypeErasure, _) - | (_, ReducedTy::IntArray | ReducedTy::TypeErasure) => return false, + (ReducedTy::TypeErasure, _) | (_, ReducedTy::TypeErasure) => return false, (ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty)) if from_ty != to_ty => { + let same_adt_did = if let (ty::Adt(from_def, from_subs), ty::Adt(to_def, to_subs)) + = (from_ty.kind(), to_ty.kind()) + && from_def == to_def + { + if same_except_params(from_subs, to_subs) { + return false; + } + Some(from_def.did()) + } else { + None + }; span_lint_and_then( cx, TRANSMUTE_UNDEFINED_REPR, @@ -135,21 +147,17 @@ pub(super) fn check<'tcx>( from_ty_orig, to_ty_orig ), |diag| { - if_chain! { - if let (Some(from_def), Some(to_def)) = (from_ty.ty_adt_def(), to_ty.ty_adt_def()); - if from_def == to_def; - then { - diag.note(&format!( - "two instances of the same generic type (`{}`) may have different layouts", - cx.tcx.item_name(from_def.did()) - )); - } else { - if from_ty_orig.peel_refs() != from_ty { - diag.note(&format!("the contained type `{}` has an undefined layout", from_ty)); - } - if to_ty_orig.peel_refs() != to_ty { - diag.note(&format!("the contained type `{}` has an undefined layout", to_ty)); - } + if let Some(same_adt_did) = same_adt_did { + diag.note(&format!( + "two instances of the same generic type (`{}`) may have different layouts", + cx.tcx.item_name(same_adt_did) + )); + } else { + if from_ty_orig.peel_refs() != from_ty { + diag.note(&format!("the contained type `{}` has an undefined layout", from_ty)); + } + if to_ty_orig.peel_refs() != to_ty { + diag.note(&format!("the contained type `{}` has an undefined layout", to_ty)); } } }, @@ -196,10 +204,13 @@ pub(super) fn check<'tcx>( continue; }, ( - ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_), - ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_), + ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_) | ReducedTy::Param, + ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_) | ReducedTy::Param, ) - | (ReducedTy::UnorderedFields(_), ReducedTy::UnorderedFields(_)) => break, + | ( + ReducedTy::UnorderedFields(_) | ReducedTy::Param, + ReducedTy::UnorderedFields(_) | ReducedTy::Param, + ) => break, }, } } @@ -263,9 +274,8 @@ enum ReducedTy<'tcx> { UnorderedFields(Ty<'tcx>), /// The type is a reference to the contained type. Ref(Ty<'tcx>), - /// The type is an array of a primitive integer type. These can be used as storage for a value - /// of another type. - IntArray, + /// The type is a generic parameter. + Param, /// Any other type. Other(Ty<'tcx>), } @@ -275,17 +285,18 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> loop { ty = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty).unwrap_or(ty); return match *ty.kind() { - ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => ReducedTy::IntArray, + ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => ReducedTy::TypeErasure, ty::Array(sub_ty, _) | ty::Slice(sub_ty) => { ty = sub_ty; continue; }, ty::Tuple(args) if args.is_empty() => ReducedTy::TypeErasure, ty::Tuple(args) => { - let Some(sized_ty) = args.iter().find(|&ty| !is_zero_sized_ty(cx, ty)) else { + let mut iter = args.iter(); + let Some(sized_ty) = iter.find(|&ty| !is_zero_sized_ty(cx, ty)) else { return ReducedTy::OrderedFields(ty); }; - if args.iter().all(|ty| is_zero_sized_ty(cx, ty)) { + if iter.all(|ty| is_zero_sized_ty(cx, ty)) { ty = sized_ty; continue; } @@ -313,9 +324,12 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> ty::Adt(def, _) if def.is_enum() && (def.variants().is_empty() || is_c_void(cx, ty)) => { ReducedTy::TypeErasure }, + // TODO: Check if the conversion to or from at least one of a union's fields is valid. + ty::Adt(def, _) if def.is_union() => ReducedTy::TypeErasure, ty::Foreign(_) => ReducedTy::TypeErasure, ty::Ref(_, ty, _) => ReducedTy::Ref(ty), ty::RawPtr(ty) => ReducedTy::Ref(ty.ty), + ty::Param(_) => ReducedTy::Param, _ => ReducedTy::Other(ty), }; } @@ -332,3 +346,27 @@ fn is_zero_sized_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { } } } + +fn is_size_pair(ty: Ty<'_>) -> bool { + if let ty::Tuple(tys) = *ty.kind() + && let [ty1, ty2] = &**tys + { + matches!(ty1.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize)) + && matches!(ty2.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize)) + } else { + false + } +} + +fn same_except_params(subs1: SubstsRef<'_>, subs2: SubstsRef<'_>) -> bool { + // TODO: check const parameters as well. Currently this will consider `Array<5>` the same as + // `Array<6>` + for (ty1, ty2) in subs1.types().zip(subs2.types()).filter(|(ty1, ty2)| ty1 != ty2) { + match (ty1.kind(), ty2.kind()) { + (ty::Param(_), _) | (_, ty::Param(_)) => (), + (ty::Adt(adt1, subs1), ty::Adt(adt2, subs2)) if adt1 == adt2 && same_except_params(subs1, subs2) => (), + _ => return false, + } + } + true +} diff --git a/src/tools/clippy/clippy_lints/src/try_err.rs b/src/tools/clippy/clippy_lints/src/try_err.rs index 80d6f3c6336..e108f7be12e 100644 --- a/src/tools/clippy/clippy_lints/src/try_err.rs +++ b/src/tools/clippy/clippy_lints/src/try_err.rs @@ -43,7 +43,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.38.0"] pub TRY_ERR, - style, + restriction, "return errors explicitly rather than hiding them behind a `?`" } 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 a617422bbeb..b3fad6ce7b6 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 @@ -85,7 +85,7 @@ macro_rules! CONFIGURATION_VALUE_TEMPLATE { }; } -const LINT_EMISSION_FUNCTIONS: [&[&str]; 7] = [ +const LINT_EMISSION_FUNCTIONS: [&[&str]; 8] = [ &["clippy_utils", "diagnostics", "span_lint"], &["clippy_utils", "diagnostics", "span_lint_and_help"], &["clippy_utils", "diagnostics", "span_lint_and_note"], @@ -93,6 +93,7 @@ const LINT_EMISSION_FUNCTIONS: [&[&str]; 7] = [ &["clippy_utils", "diagnostics", "span_lint_and_sugg"], &["clippy_utils", "diagnostics", "span_lint_and_then"], &["clippy_utils", "diagnostics", "span_lint_hir_and_then"], + &["clippy_utils", "diagnostics", "span_lint_and_sugg_for_edges"], ]; const SUGGESTION_DIAGNOSTIC_BUILDER_METHODS: [(&str, bool); 9] = [ ("span_suggestion", false), diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index 532bd810a2e..f3d818cc348 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -581,14 +581,19 @@ impl Write { }; let replacement: String = match lit.token.kind { - LitKind::Integer | LitKind::Float | LitKind::Err => continue, LitKind::StrRaw(_) | LitKind::ByteStrRaw(_) if matches!(fmtstr.style, StrStyle::Raw(_)) => { lit.token.symbol.as_str().replace('{', "{{").replace('}', "}}") }, LitKind::Str | LitKind::ByteStr if matches!(fmtstr.style, StrStyle::Cooked) => { lit.token.symbol.as_str().replace('{', "{{").replace('}', "}}") }, - LitKind::StrRaw(_) | LitKind::Str | LitKind::ByteStrRaw(_) | LitKind::ByteStr => continue, + LitKind::StrRaw(_) + | LitKind::Str + | LitKind::ByteStrRaw(_) + | LitKind::ByteStr + | LitKind::Integer + | LitKind::Float + | LitKind::Err => continue, LitKind::Byte | LitKind::Char => match lit.token.symbol.as_str() { "\"" if matches!(fmtstr.style, StrStyle::Cooked) => "\\\"", "\"" if matches!(fmtstr.style, StrStyle::Raw(0)) => continue, diff --git a/src/tools/clippy/clippy_utils/src/diagnostics.rs b/src/tools/clippy/clippy_utils/src/diagnostics.rs index a927788e6a4..625a53899df 100644 --- a/src/tools/clippy/clippy_utils/src/diagnostics.rs +++ b/src/tools/clippy/clippy_utils/src/diagnostics.rs @@ -8,7 +8,7 @@ //! Thank you! //! ~The `INTERNAL_METADATA_COLLECTOR` lint -use rustc_errors::{Applicability, Diagnostic}; +use rustc_errors::{emitter::MAX_SUGGESTION_HIGHLIGHT_LINES, Applicability, Diagnostic}; use rustc_hir::HirId; use rustc_lint::{LateContext, Lint, LintContext}; use rustc_span::source_map::{MultiSpan, Span}; @@ -213,6 +213,90 @@ pub fn span_lint_and_sugg<'a, T: LintContext>( }); } +/// Like [`span_lint_and_sugg`] with a focus on the edges. The output will either +/// emit single span or multispan suggestion depending on the number of its lines. +/// +/// If the given suggestion string has more lines than the maximum display length defined by +/// [`MAX_SUGGESTION_HIGHLIGHT_LINES`][`rustc_errors::emitter::MAX_SUGGESTION_HIGHLIGHT_LINES`], +/// this function will split the suggestion and span to showcase the change for the top and +/// bottom edge of the code. For normal suggestions, in one display window, the help message +/// will be combined with a colon. +/// +/// Multipart suggestions like the one being created here currently cannot be +/// applied by rustfix (See [rustfix#141](https://github.com/rust-lang/rustfix/issues/141)). +/// Testing rustfix with this lint emission function might require a file with +/// suggestions that can be fixed and those that can't. See +/// [clippy#8520](https://github.com/rust-lang/rust-clippy/pull/8520/files) for +/// an example and of this. +/// +/// # Example for a long suggestion +/// +/// ```text +/// error: called `map(..).flatten()` on `Option` +/// --> $DIR/map_flatten.rs:8:10 +/// | +/// LL | .map(|x| { +/// | __________^ +/// LL | | if x <= 5 { +/// LL | | Some(x) +/// LL | | } else { +/// ... | +/// LL | | }) +/// LL | | .flatten(); +/// | |__________________^ +/// | +/// = note: `-D clippy::map-flatten` implied by `-D warnings` +/// help: try replacing `map` with `and_then` +/// | +/// LL ~ .and_then(|x| { +/// LL + if x <= 5 { +/// LL + Some(x) +/// | +/// help: and remove the `.flatten()` +/// | +/// LL + None +/// LL + } +/// LL ~ }); +/// | +/// ``` +pub fn span_lint_and_sugg_for_edges( + cx: &LateContext<'_>, + lint: &'static Lint, + sp: Span, + msg: &str, + helps: &[&str; 2], + sugg: String, + applicability: Applicability, +) { + span_lint_and_then(cx, lint, sp, msg, |diag| { + let sugg_lines_count = sugg.lines().count(); + if sugg_lines_count > MAX_SUGGESTION_HIGHLIGHT_LINES { + let sm = cx.sess().source_map(); + if let (Ok(line_upper), Ok(line_bottom)) = (sm.lookup_line(sp.lo()), sm.lookup_line(sp.hi())) { + let split_idx = MAX_SUGGESTION_HIGHLIGHT_LINES / 2; + let span_upper = sm.span_until_char(sp.with_hi(line_upper.sf.lines[line_upper.line + split_idx]), '\n'); + let span_bottom = sp.with_lo(line_bottom.sf.lines[line_bottom.line - split_idx]); + + let sugg_lines_vec = sugg.lines().collect::<Vec<&str>>(); + let sugg_upper = sugg_lines_vec[..split_idx].join("\n"); + let sugg_bottom = sugg_lines_vec[sugg_lines_count - split_idx..].join("\n"); + + diag.span_suggestion(span_upper, helps[0], sugg_upper, applicability); + diag.span_suggestion(span_bottom, helps[1], sugg_bottom, applicability); + + return; + } + } + diag.span_suggestion_with_style( + sp, + &helps.join(", "), + sugg, + applicability, + rustc_errors::SuggestionStyle::ShowAlways, + ); + }); +} + /// Create a suggestion made from several `span → replacement`. /// /// Note: in the JSON format (used by `compiletest_rs`), the help message will diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index 63c442e7008..1fc9979f3dd 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -808,7 +808,7 @@ pub fn deref_closure_args<'tcx>(cx: &LateContext<'_>, closure: &'tcx hir::Expr<' closure_arg_is_type_annotated_double_ref, next_pos: closure.span.lo(), suggestion_start: String::new(), - applicability: Applicability::MaybeIncorrect, + applicability: Applicability::MachineApplicable, }; let fn_def_id = cx.tcx.hir().local_def_id(closure.hir_id); diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index 9d5da4ed68f..5befb856a02 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-03-14" +channel = "nightly-2022-03-24" components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/tools/clippy/tests/ui/cast_enum_constructor.rs b/src/tools/clippy/tests/ui/cast_enum_constructor.rs new file mode 100644 index 00000000000..0193454ad14 --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_enum_constructor.rs @@ -0,0 +1,17 @@ +#![warn(clippy::cast_enum_constructor)] +#![allow(clippy::fn_to_numeric_cast)] + +fn main() { + enum Foo { + Y(u32), + } + + enum Bar { + X, + } + + let _ = Foo::Y as usize; + let _ = Foo::Y as isize; + let _ = Foo::Y as fn(u32) -> Foo; + let _ = Bar::X as usize; +} diff --git a/src/tools/clippy/tests/ui/cast_enum_constructor.stderr b/src/tools/clippy/tests/ui/cast_enum_constructor.stderr new file mode 100644 index 00000000000..710909dd26f --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_enum_constructor.stderr @@ -0,0 +1,16 @@ +error: cast of an enum tuple constructor to an integer + --> $DIR/cast_enum_constructor.rs:13:13 + | +LL | let _ = Foo::Y as usize; + | ^^^^^^^^^^^^^^^ + | + = note: `-D clippy::cast-enum-constructor` implied by `-D warnings` + +error: cast of an enum tuple constructor to an integer + --> $DIR/cast_enum_constructor.rs:14:13 + | +LL | let _ = Foo::Y as isize; + | ^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/crashes/ice-5497.stderr b/src/tools/clippy/tests/ui/crashes/ice-5497.stderr new file mode 100644 index 00000000000..e75e7dc9136 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-5497.stderr @@ -0,0 +1,10 @@ +error: this operation will panic at runtime + --> $DIR/ice-5497.rs:9:22 + | +LL | const OOB: i32 = [1][1] + T::OOB; + | ^^^^^^ index out of bounds: the length is 1 but the index is 1 + | + = note: `#[deny(unconditional_panic)]` on by default + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/map_flatten.rs b/src/tools/clippy/tests/ui/map_flatten.rs index aa1f76e335a..7d47ee09dc1 100644 --- a/src/tools/clippy/tests/ui/map_flatten.rs +++ b/src/tools/clippy/tests/ui/map_flatten.rs @@ -1,31 +1,55 @@ -// run-rustfix - -#![warn(clippy::all, clippy::pedantic)] -#![allow(clippy::let_underscore_drop)] -#![allow(clippy::missing_docs_in_private_items)] -#![allow(clippy::map_identity)] -#![allow(clippy::redundant_closure)] -#![allow(clippy::unnecessary_wraps)] +#![warn(clippy::map_flatten)] #![feature(result_flattening)] -fn main() { - // mapping to Option on Iterator - fn option_id(x: i8) -> Option<i8> { - Some(x) - } - let option_id_ref: fn(i8) -> Option<i8> = option_id; - let option_id_closure = |x| Some(x); - let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect(); - let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); - let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); - let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); +// issue #8506, multi-line +#[rustfmt::skip] +fn long_span() { + let _: Option<i32> = Some(1) + .map(|x| { + if x <= 5 { + Some(x) + } else { + None + } + }) + .flatten(); - // mapping to Iterator on Iterator - let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); + let _: Result<i32, i32> = Ok(1) + .map(|x| { + if x == 1 { + Ok(x) + } else { + Err(0) + } + }) + .flatten(); - // mapping to Option on Option - let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); + let result: Result<i32, i32> = Ok(2); + fn do_something() { } + let _: Result<i32, i32> = result + .map(|res| { + if res > 0 { + do_something(); + Ok(res) + } else { + Err(0) + } + }) + .flatten(); + + let _: Vec<_> = vec![5_i8; 6] + .into_iter() + .map(|some_value| { + if some_value > 3 { + Some(some_value) + } else { + None + } + }) + .flatten() + .collect(); +} - // mapping to Result on Result - let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten(); +fn main() { + long_span(); } diff --git a/src/tools/clippy/tests/ui/map_flatten.stderr b/src/tools/clippy/tests/ui/map_flatten.stderr index bcd2047e6fa..c9c60df838f 100644 --- a/src/tools/clippy/tests/ui/map_flatten.stderr +++ b/src/tools/clippy/tests/ui/map_flatten.stderr @@ -1,46 +1,107 @@ -error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:18:46 +error: called `map(..).flatten()` on `Option` + --> $DIR/map_flatten.rs:8:10 | -LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id)` +LL | .map(|x| { + | __________^ +LL | | if x <= 5 { +LL | | Some(x) +LL | | } else { +... | +LL | | }) +LL | | .flatten(); + | |__________________^ | = note: `-D clippy::map-flatten` implied by `-D warnings` - -error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:19:46 +help: try replacing `map` with `and_then` | -LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_ref)` - -error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:20:46 +LL ~ .and_then(|x| { +LL + if x <= 5 { +LL + Some(x) | -LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_closure)` - -error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:21:46 +help: and remove the `.flatten()` + | +LL + None +LL + } +LL ~ }); | -LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(|x| x.checked_add(1))` -error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:24:46 +error: called `map(..).flatten()` on `Result` + --> $DIR/map_flatten.rs:18:10 + | +LL | .map(|x| { + | __________^ +LL | | if x == 1 { +LL | | Ok(x) +LL | | } else { +... | +LL | | }) +LL | | .flatten(); + | |__________________^ + | +help: try replacing `map` with `and_then` + | +LL ~ .and_then(|x| { +LL + if x == 1 { +LL + Ok(x) + | +help: and remove the `.flatten()` + | +LL + Err(0) +LL + } +LL ~ }); | -LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `.flat_map(|x| 0..x)` -error: called `map(..).flatten()` on an `Option` - --> $DIR/map_flatten.rs:27:39 +error: called `map(..).flatten()` on `Result` + --> $DIR/map_flatten.rs:30:10 + | +LL | .map(|res| { + | __________^ +LL | | if res > 0 { +LL | | do_something(); +LL | | Ok(res) +... | +LL | | }) +LL | | .flatten(); + | |__________________^ + | +help: try replacing `map` with `and_then` + | +LL ~ .and_then(|res| { +LL + if res > 0 { +LL + do_something(); + | +help: and remove the `.flatten()` + | +LL + Err(0) +LL + } +LL ~ }); | -LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); - | ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)` -error: called `map(..).flatten()` on an `Result` - --> $DIR/map_flatten.rs:30:41 +error: called `map(..).flatten()` on `Iterator` + --> $DIR/map_flatten.rs:42:10 + | +LL | .map(|some_value| { + | __________^ +LL | | if some_value > 3 { +LL | | Some(some_value) +LL | | } else { +... | +LL | | }) +LL | | .flatten() + | |__________________^ + | +help: try replacing `map` with `filter_map` + | +LL ~ .filter_map(|some_value| { +LL + if some_value > 3 { +LL + Some(some_value) + | +help: and remove the `.flatten()` + | +LL + None +LL + } +LL + }) | -LL | let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten(); - | ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)` -error: aborting due to 7 previous errors +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/map_flatten.fixed b/src/tools/clippy/tests/ui/map_flatten_fixable.fixed index fec3a95edd6..fec3a95edd6 100644 --- a/src/tools/clippy/tests/ui/map_flatten.fixed +++ b/src/tools/clippy/tests/ui/map_flatten_fixable.fixed diff --git a/src/tools/clippy/tests/ui/map_flatten_fixable.rs b/src/tools/clippy/tests/ui/map_flatten_fixable.rs new file mode 100644 index 00000000000..aa1f76e335a --- /dev/null +++ b/src/tools/clippy/tests/ui/map_flatten_fixable.rs @@ -0,0 +1,31 @@ +// run-rustfix + +#![warn(clippy::all, clippy::pedantic)] +#![allow(clippy::let_underscore_drop)] +#![allow(clippy::missing_docs_in_private_items)] +#![allow(clippy::map_identity)] +#![allow(clippy::redundant_closure)] +#![allow(clippy::unnecessary_wraps)] +#![feature(result_flattening)] + +fn main() { + // mapping to Option on Iterator + fn option_id(x: i8) -> Option<i8> { + Some(x) + } + let option_id_ref: fn(i8) -> Option<i8> = option_id; + let option_id_closure = |x| Some(x); + let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect(); + let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); + let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); + let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); + + // mapping to Iterator on Iterator + let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); + + // mapping to Option on Option + let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); + + // mapping to Result on Result + let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten(); +} diff --git a/src/tools/clippy/tests/ui/map_flatten_fixable.stderr b/src/tools/clippy/tests/ui/map_flatten_fixable.stderr new file mode 100644 index 00000000000..c91c73846b6 --- /dev/null +++ b/src/tools/clippy/tests/ui/map_flatten_fixable.stderr @@ -0,0 +1,80 @@ +error: called `map(..).flatten()` on `Iterator` + --> $DIR/map_flatten_fixable.rs:18:47 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::map-flatten` implied by `-D warnings` +help: try replacing `map` with `filter_map`, and remove the `.flatten()` + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id).collect(); + | ~~~~~~~~~~~~~~~~~~~~~ + +error: called `map(..).flatten()` on `Iterator` + --> $DIR/map_flatten_fixable.rs:19:47 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try replacing `map` with `filter_map`, and remove the `.flatten()` + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id_ref).collect(); + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: called `map(..).flatten()` on `Iterator` + --> $DIR/map_flatten_fixable.rs:20:47 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try replacing `map` with `filter_map`, and remove the `.flatten()` + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id_closure).collect(); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: called `map(..).flatten()` on `Iterator` + --> $DIR/map_flatten_fixable.rs:21:47 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try replacing `map` with `filter_map`, and remove the `.flatten()` + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(|x| x.checked_add(1)).collect(); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: called `map(..).flatten()` on `Iterator` + --> $DIR/map_flatten_fixable.rs:24:47 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try replacing `map` with `flat_map`, and remove the `.flatten()` + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().flat_map(|x| 0..x).collect(); + | ~~~~~~~~~~~~~~~~~~ + +error: called `map(..).flatten()` on `Option` + --> $DIR/map_flatten_fixable.rs:27:40 + | +LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); + | ^^^^^^^^^^^^^^^^^^^^ + | +help: try replacing `map` with `and_then`, and remove the `.flatten()` + | +LL | let _: Option<_> = (Some(Some(1))).and_then(|x| x); + | ~~~~~~~~~~~~~~~ + +error: called `map(..).flatten()` on `Result` + --> $DIR/map_flatten_fixable.rs:30:42 + | +LL | let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten(); + | ^^^^^^^^^^^^^^^^^^^^ + | +help: try replacing `map` with `and_then`, and remove the `.flatten()` + | +LL | let _: Result<_, &str> = (Ok(Ok(1))).and_then(|x| x); + | ~~~~~~~~~~~~~~~ + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/match_same_arms.stderr b/src/tools/clippy/tests/ui/match_same_arms.stderr index 7752a8a6ff2..b6d04263b37 100644 --- a/src/tools/clippy/tests/ui/match_same_arms.stderr +++ b/src/tools/clippy/tests/ui/match_same_arms.stderr @@ -1,128 +1,121 @@ -error: this `match` has identical arm bodies - --> $DIR/match_same_arms.rs:13:14 +error: this match arm has an identical body to the `_` wildcard arm + --> $DIR/match_same_arms.rs:11:9 | -LL | _ => 0, //~ ERROR match arms have same body - | ^ +LL | Abc::A => 0, + | ^^^^^^^^^^^ help: try removing the arm | = note: `-D clippy::match-same-arms` implied by `-D warnings` -note: same as this - --> $DIR/match_same_arms.rs:11:19 + = help: or try changing either arm body +note: `_` wildcard arm here + --> $DIR/match_same_arms.rs:13:9 | -LL | Abc::A => 0, - | ^ -note: `Abc::A` has the same arm body as the `_` wildcard, consider removing it - --> $DIR/match_same_arms.rs:11:19 - | -LL | Abc::A => 0, - | ^ +LL | _ => 0, //~ ERROR match arms have same body + | ^^^^^^ -error: this `match` has identical arm bodies - --> $DIR/match_same_arms.rs:18:20 - | -LL | (.., 3) => 42, //~ ERROR match arms have same body - | ^^ - | -note: same as this - --> $DIR/match_same_arms.rs:17:23 - | -LL | (1, .., 3) => 42, - | ^^ -help: consider refactoring into `(1, .., 3) | (.., 3)` +error: this match arm has an identical body to another arm --> $DIR/match_same_arms.rs:17:9 | LL | (1, .., 3) => 42, - | ^^^^^^^^^^ - = help: ...or consider changing the match arm bodies + | ----------^^^^^^ + | | + | help: try merging the arm patterns: `(1, .., 3) | (.., 3)` + | + = help: or try changing either arm body +note: other arm here + --> $DIR/match_same_arms.rs:18:9 + | +LL | (.., 3) => 42, //~ ERROR match arms have same body + | ^^^^^^^^^^^^^ -error: this `match` has identical arm bodies - --> $DIR/match_same_arms.rs:24:15 +error: this match arm has an identical body to another arm + --> $DIR/match_same_arms.rs:24:9 | LL | 51 => 1, //~ ERROR match arms have same body - | ^ + | --^^^^^ + | | + | help: try merging the arm patterns: `51 | 42` | -note: same as this - --> $DIR/match_same_arms.rs:23:15 - | -LL | 42 => 1, - | ^ -help: consider refactoring into `42 | 51` + = help: or try changing either arm body +note: other arm here --> $DIR/match_same_arms.rs:23:9 | LL | 42 => 1, - | ^^ - = help: ...or consider changing the match arm bodies + | ^^^^^^^ -error: this `match` has identical arm bodies - --> $DIR/match_same_arms.rs:26:15 - | -LL | 52 => 2, //~ ERROR match arms have same body - | ^ - | -note: same as this - --> $DIR/match_same_arms.rs:25:15 - | -LL | 41 => 2, - | ^ -help: consider refactoring into `41 | 52` +error: this match arm has an identical body to another arm --> $DIR/match_same_arms.rs:25:9 | LL | 41 => 2, - | ^^ - = help: ...or consider changing the match arm bodies + | --^^^^^ + | | + | help: try merging the arm patterns: `41 | 52` + | + = help: or try changing either arm body +note: other arm here + --> $DIR/match_same_arms.rs:26:9 + | +LL | 52 => 2, //~ ERROR match arms have same body + | ^^^^^^^ -error: this `match` has identical arm bodies - --> $DIR/match_same_arms.rs:32:14 +error: this match arm has an identical body to another arm + --> $DIR/match_same_arms.rs:32:9 | LL | 2 => 2, //~ ERROR 2nd matched arms have same body - | ^ - | -note: same as this - --> $DIR/match_same_arms.rs:31:14 + | -^^^^^ + | | + | help: try merging the arm patterns: `2 | 1` | -LL | 1 => 2, - | ^ -help: consider refactoring into `1 | 2` + = help: or try changing either arm body +note: other arm here --> $DIR/match_same_arms.rs:31:9 | LL | 1 => 2, - | ^ - = help: ...or consider changing the match arm bodies + | ^^^^^^ -error: this `match` has identical arm bodies - --> $DIR/match_same_arms.rs:33:14 +error: this match arm has an identical body to another arm + --> $DIR/match_same_arms.rs:33:9 | LL | 3 => 2, //~ ERROR 3rd matched arms have same body - | ^ - | -note: same as this - --> $DIR/match_same_arms.rs:31:14 + | -^^^^^ + | | + | help: try merging the arm patterns: `3 | 1` | -LL | 1 => 2, - | ^ -help: consider refactoring into `1 | 3` + = help: or try changing either arm body +note: other arm here --> $DIR/match_same_arms.rs:31:9 | LL | 1 => 2, - | ^ - = help: ...or consider changing the match arm bodies + | ^^^^^^ -error: this `match` has identical arm bodies - --> $DIR/match_same_arms.rs:50:55 +error: this match arm has an identical body to another arm + --> $DIR/match_same_arms.rs:32:9 | -LL | CommandInfo::External { name, .. } => name.to_string(), - | ^^^^^^^^^^^^^^^^ +LL | 2 => 2, //~ ERROR 2nd matched arms have same body + | -^^^^^ + | | + | help: try merging the arm patterns: `2 | 3` | -note: same as this - --> $DIR/match_same_arms.rs:49:54 + = help: or try changing either arm body +note: other arm here + --> $DIR/match_same_arms.rs:33:9 | -LL | CommandInfo::BuiltIn { name, .. } => name.to_string(), - | ^^^^^^^^^^^^^^^^ -help: consider refactoring into `CommandInfo::BuiltIn { name, .. } | CommandInfo::External { name, .. }` +LL | 3 => 2, //~ ERROR 3rd matched arms have same body + | ^^^^^^ + +error: this match arm has an identical body to another arm + --> $DIR/match_same_arms.rs:50:17 + | +LL | CommandInfo::External { name, .. } => name.to_string(), + | ----------------------------------^^^^^^^^^^^^^^^^^^^^ + | | + | help: try merging the arm patterns: `CommandInfo::External { name, .. } | CommandInfo::BuiltIn { name, .. }` + | + = help: or try changing either arm body +note: other arm here --> $DIR/match_same_arms.rs:49:17 | LL | CommandInfo::BuiltIn { name, .. } => name.to_string(), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: ...or consider changing the match arm bodies + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 7 previous errors +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/match_same_arms2.rs b/src/tools/clippy/tests/ui/match_same_arms2.rs index 67e1d518483..dbfeb4379d5 100644 --- a/src/tools/clippy/tests/ui/match_same_arms2.rs +++ b/src/tools/clippy/tests/ui/match_same_arms2.rs @@ -174,4 +174,57 @@ fn main() { Some(2) => 2, _ => 1, }; + + enum Foo { + X(u32), + Y(u32), + Z(u32), + } + + // Don't lint. `Foo::X(0)` and `Foo::Z(_)` overlap with the arm in between. + let _ = match Foo::X(0) { + Foo::X(0) => 1, + Foo::X(_) | Foo::Y(_) | Foo::Z(0) => 2, + Foo::Z(_) => 1, + _ => 0, + }; + + // Suggest moving `Foo::Z(_)` up. + let _ = match Foo::X(0) { + Foo::X(0) => 1, + Foo::X(_) | Foo::Y(_) => 2, + Foo::Z(_) => 1, + _ => 0, + }; + + // Suggest moving `Foo::X(0)` down. + let _ = match Foo::X(0) { + Foo::X(0) => 1, + Foo::Y(_) | Foo::Z(0) => 2, + Foo::Z(_) => 1, + _ => 0, + }; + + // Don't lint. + let _ = match 0 { + -2 => 1, + -5..=50 => 2, + -150..=88 => 1, + _ => 3, + }; + + struct Bar { + x: u32, + y: u32, + z: u32, + } + + // Lint. + let _ = match None { + Some(Bar { x: 0, y: 5, .. }) => 1, + Some(Bar { y: 10, z: 0, .. }) => 2, + None => 50, + Some(Bar { y: 0, x: 5, .. }) => 1, + _ => 200, + }; } diff --git a/src/tools/clippy/tests/ui/match_same_arms2.stderr b/src/tools/clippy/tests/ui/match_same_arms2.stderr index e1ed12f9370..14a672ba2fe 100644 --- a/src/tools/clippy/tests/ui/match_same_arms2.stderr +++ b/src/tools/clippy/tests/ui/match_same_arms2.stderr @@ -1,175 +1,138 @@ -error: this `match` has identical arm bodies - --> $DIR/match_same_arms2.rs:20:14 +error: this match arm has an identical body to the `_` wildcard arm + --> $DIR/match_same_arms2.rs:11:9 | -LL | _ => { - | ______________^ -LL | | //~ ERROR match arms have same body +LL | / 42 => { LL | | foo(); LL | | let mut a = 42 + [23].len() as i32; +LL | | if true { ... | LL | | a LL | | }, - | |_________^ + | |_________^ help: try removing the arm | = note: `-D clippy::match-same-arms` implied by `-D warnings` -note: same as this - --> $DIR/match_same_arms2.rs:11:15 + = help: or try changing either arm body +note: `_` wildcard arm here + --> $DIR/match_same_arms2.rs:20:9 | -LL | 42 => { - | _______________^ -LL | | foo(); -LL | | let mut a = 42 + [23].len() as i32; -LL | | if true { -... | -LL | | a -LL | | }, - | |_________^ -note: `42` has the same arm body as the `_` wildcard, consider removing it - --> $DIR/match_same_arms2.rs:11:15 - | -LL | 42 => { - | _______________^ +LL | / _ => { +LL | | //~ ERROR match arms have same body LL | | foo(); LL | | let mut a = 42 + [23].len() as i32; -LL | | if true { ... | LL | | a LL | | }, | |_________^ -error: this `match` has identical arm bodies - --> $DIR/match_same_arms2.rs:34:15 +error: this match arm has an identical body to another arm + --> $DIR/match_same_arms2.rs:34:9 | LL | 51 => foo(), //~ ERROR match arms have same body - | ^^^^^ + | --^^^^^^^^^ + | | + | help: try merging the arm patterns: `51 | 42` | -note: same as this - --> $DIR/match_same_arms2.rs:33:15 - | -LL | 42 => foo(), - | ^^^^^ -help: consider refactoring into `42 | 51` + = help: or try changing either arm body +note: other arm here --> $DIR/match_same_arms2.rs:33:9 | LL | 42 => foo(), - | ^^ - = help: ...or consider changing the match arm bodies + | ^^^^^^^^^^^ -error: this `match` has identical arm bodies - --> $DIR/match_same_arms2.rs:40:17 +error: this match arm has an identical body to another arm + --> $DIR/match_same_arms2.rs:40:9 | LL | None => 24, //~ ERROR match arms have same body - | ^^ + | ----^^^^^^ + | | + | help: try merging the arm patterns: `None | Some(_)` | -note: same as this - --> $DIR/match_same_arms2.rs:39:20 - | -LL | Some(_) => 24, - | ^^ -help: consider refactoring into `Some(_) | None` + = help: or try changing either arm body +note: other arm here --> $DIR/match_same_arms2.rs:39:9 | LL | Some(_) => 24, - | ^^^^^^^ - = help: ...or consider changing the match arm bodies + | ^^^^^^^^^^^^^ -error: this `match` has identical arm bodies - --> $DIR/match_same_arms2.rs:62:28 +error: this match arm has an identical body to another arm + --> $DIR/match_same_arms2.rs:62:9 | LL | (None, Some(a)) => bar(a), //~ ERROR match arms have same body - | ^^^^^^ - | -note: same as this - --> $DIR/match_same_arms2.rs:61:28 + | ---------------^^^^^^^^^^ + | | + | help: try merging the arm patterns: `(None, Some(a)) | (Some(a), None)` | -LL | (Some(a), None) => bar(a), - | ^^^^^^ -help: consider refactoring into `(Some(a), None) | (None, Some(a))` + = help: or try changing either arm body +note: other arm here --> $DIR/match_same_arms2.rs:61:9 | LL | (Some(a), None) => bar(a), - | ^^^^^^^^^^^^^^^ - = help: ...or consider changing the match arm bodies + | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: this `match` has identical arm bodies - --> $DIR/match_same_arms2.rs:68:26 - | -LL | (.., Some(a)) => bar(a), //~ ERROR match arms have same body - | ^^^^^^ - | -note: same as this - --> $DIR/match_same_arms2.rs:67:26 - | -LL | (Some(a), ..) => bar(a), - | ^^^^^^ -help: consider refactoring into `(Some(a), ..) | (.., Some(a))` +error: this match arm has an identical body to another arm --> $DIR/match_same_arms2.rs:67:9 | LL | (Some(a), ..) => bar(a), - | ^^^^^^^^^^^^^ - = help: ...or consider changing the match arm bodies - -error: this `match` has identical arm bodies - --> $DIR/match_same_arms2.rs:102:29 - | -LL | (Ok(_), Some(x)) => println!("ok {}", x), - | ^^^^^^^^^^^^^^^^^^^^ + | -------------^^^^^^^^^^ + | | + | help: try merging the arm patterns: `(Some(a), ..) | (.., Some(a))` | -note: same as this - --> $DIR/match_same_arms2.rs:101:29 + = help: or try changing either arm body +note: other arm here + --> $DIR/match_same_arms2.rs:68:9 | -LL | (Ok(x), Some(_)) => println!("ok {}", x), - | ^^^^^^^^^^^^^^^^^^^^ -help: consider refactoring into `(Ok(x), Some(_)) | (Ok(_), Some(x))` +LL | (.., Some(a)) => bar(a), //~ ERROR match arms have same body + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: this match arm has an identical body to another arm --> $DIR/match_same_arms2.rs:101:9 | LL | (Ok(x), Some(_)) => println!("ok {}", x), - | ^^^^^^^^^^^^^^^^ - = help: ...or consider changing the match arm bodies - = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + | ----------------^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: try merging the arm patterns: `(Ok(x), Some(_)) | (Ok(_), Some(x))` + | + = help: or try changing either arm body +note: other arm here + --> $DIR/match_same_arms2.rs:102:9 + | +LL | (Ok(_), Some(x)) => println!("ok {}", x), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: this `match` has identical arm bodies - --> $DIR/match_same_arms2.rs:117:18 +error: this match arm has an identical body to another arm + --> $DIR/match_same_arms2.rs:117:9 | LL | Ok(_) => println!("ok"), - | ^^^^^^^^^^^^^^ - | -note: same as this - --> $DIR/match_same_arms2.rs:116:18 + | -----^^^^^^^^^^^^^^^^^^ + | | + | help: try merging the arm patterns: `Ok(_) | Ok(3)` | -LL | Ok(3) => println!("ok"), - | ^^^^^^^^^^^^^^ -help: consider refactoring into `Ok(3) | Ok(_)` + = help: or try changing either arm body +note: other arm here --> $DIR/match_same_arms2.rs:116:9 | LL | Ok(3) => println!("ok"), - | ^^^^^ - = help: ...or consider changing the match arm bodies - = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + | ^^^^^^^^^^^^^^^^^^^^^^^ -error: this `match` has identical arm bodies - --> $DIR/match_same_arms2.rs:144:14 +error: this match arm has an identical body to another arm + --> $DIR/match_same_arms2.rs:144:9 | LL | 1 => { - | ______________^ + | ^ help: try merging the arm patterns: `1 | 0` + | _________| + | | LL | | empty!(0); LL | | }, | |_________^ | -note: same as this - --> $DIR/match_same_arms2.rs:141:14 + = help: or try changing either arm body +note: other arm here + --> $DIR/match_same_arms2.rs:141:9 | -LL | 0 => { - | ______________^ +LL | / 0 => { LL | | empty!(0); LL | | }, | |_________^ -help: consider refactoring into `0 | 1` - --> $DIR/match_same_arms2.rs:141:9 - | -LL | 0 => { - | ^ - = help: ...or consider changing the match arm bodies error: match expression looks like `matches!` macro --> $DIR/match_same_arms2.rs:162:16 @@ -184,5 +147,50 @@ LL | | }; | = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` -error: aborting due to 9 previous errors +error: this match arm has an identical body to another arm + --> $DIR/match_same_arms2.rs:194:9 + | +LL | Foo::X(0) => 1, + | ---------^^^^^ + | | + | help: try merging the arm patterns: `Foo::X(0) | Foo::Z(_)` + | + = help: or try changing either arm body +note: other arm here + --> $DIR/match_same_arms2.rs:196:9 + | +LL | Foo::Z(_) => 1, + | ^^^^^^^^^^^^^^ + +error: this match arm has an identical body to another arm + --> $DIR/match_same_arms2.rs:204:9 + | +LL | Foo::Z(_) => 1, + | ---------^^^^^ + | | + | help: try merging the arm patterns: `Foo::Z(_) | Foo::X(0)` + | + = help: or try changing either arm body +note: other arm here + --> $DIR/match_same_arms2.rs:202:9 + | +LL | Foo::X(0) => 1, + | ^^^^^^^^^^^^^^ + +error: this match arm has an identical body to another arm + --> $DIR/match_same_arms2.rs:227:9 + | +LL | Some(Bar { y: 0, x: 5, .. }) => 1, + | ----------------------------^^^^^ + | | + | help: try merging the arm patterns: `Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. })` + | + = help: or try changing either arm body +note: other arm here + --> $DIR/match_same_arms2.rs:224:9 + | +LL | Some(Bar { x: 0, y: 5, .. }) => 1, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/or_then_unwrap.fixed b/src/tools/clippy/tests/ui/or_then_unwrap.fixed new file mode 100644 index 00000000000..27d4b795a5e --- /dev/null +++ b/src/tools/clippy/tests/ui/or_then_unwrap.fixed @@ -0,0 +1,52 @@ +// run-rustfix + +#![warn(clippy::or_then_unwrap)] +#![allow(clippy::map_identity)] + +struct SomeStruct {} +impl SomeStruct { + fn or(self, _: Option<Self>) -> Self { + self + } + fn unwrap(&self) {} +} + +struct SomeOtherStruct {} +impl SomeOtherStruct { + fn or(self) -> Self { + self + } + fn unwrap(&self) {} +} + +fn main() { + let option: Option<&str> = None; + let _ = option.unwrap_or("fallback"); // should trigger lint + + let result: Result<&str, &str> = Err("Error"); + let _ = result.unwrap_or("fallback"); // should trigger lint + + // as part of a method chain + let option: Option<&str> = None; + let _ = option.map(|v| v).unwrap_or("fallback").to_string().chars(); // should trigger lint + + // Not Option/Result + let instance = SomeStruct {}; + let _ = instance.or(Some(SomeStruct {})).unwrap(); // should not trigger lint + + // or takes no argument + let instance = SomeOtherStruct {}; + let _ = instance.or().unwrap(); // should not trigger lint and should not panic + + // None in or + let option: Option<&str> = None; + let _ = option.or(None).unwrap(); // should not trigger lint + + // Not Err in or + let result: Result<&str, &str> = Err("Error"); + let _ = result.or::<&str>(Err("Other Error")).unwrap(); // should not trigger lint + + // other function between + let option: Option<&str> = None; + let _ = option.or(Some("fallback")).map(|v| v).unwrap(); // should not trigger lint +} diff --git a/src/tools/clippy/tests/ui/or_then_unwrap.rs b/src/tools/clippy/tests/ui/or_then_unwrap.rs new file mode 100644 index 00000000000..0dab5ae2f1c --- /dev/null +++ b/src/tools/clippy/tests/ui/or_then_unwrap.rs @@ -0,0 +1,52 @@ +// run-rustfix + +#![warn(clippy::or_then_unwrap)] +#![allow(clippy::map_identity)] + +struct SomeStruct {} +impl SomeStruct { + fn or(self, _: Option<Self>) -> Self { + self + } + fn unwrap(&self) {} +} + +struct SomeOtherStruct {} +impl SomeOtherStruct { + fn or(self) -> Self { + self + } + fn unwrap(&self) {} +} + +fn main() { + let option: Option<&str> = None; + let _ = option.or(Some("fallback")).unwrap(); // should trigger lint + + let result: Result<&str, &str> = Err("Error"); + let _ = result.or::<&str>(Ok("fallback")).unwrap(); // should trigger lint + + // as part of a method chain + let option: Option<&str> = None; + let _ = option.map(|v| v).or(Some("fallback")).unwrap().to_string().chars(); // should trigger lint + + // Not Option/Result + let instance = SomeStruct {}; + let _ = instance.or(Some(SomeStruct {})).unwrap(); // should not trigger lint + + // or takes no argument + let instance = SomeOtherStruct {}; + let _ = instance.or().unwrap(); // should not trigger lint and should not panic + + // None in or + let option: Option<&str> = None; + let _ = option.or(None).unwrap(); // should not trigger lint + + // Not Err in or + let result: Result<&str, &str> = Err("Error"); + let _ = result.or::<&str>(Err("Other Error")).unwrap(); // should not trigger lint + + // other function between + let option: Option<&str> = None; + let _ = option.or(Some("fallback")).map(|v| v).unwrap(); // should not trigger lint +} diff --git a/src/tools/clippy/tests/ui/or_then_unwrap.stderr b/src/tools/clippy/tests/ui/or_then_unwrap.stderr new file mode 100644 index 00000000000..da88154c59f --- /dev/null +++ b/src/tools/clippy/tests/ui/or_then_unwrap.stderr @@ -0,0 +1,22 @@ +error: found `.or(Some(…)).unwrap()` + --> $DIR/or_then_unwrap.rs:24:20 + | +LL | let _ = option.or(Some("fallback")).unwrap(); // should trigger lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or("fallback")` + | + = note: `-D clippy::or-then-unwrap` implied by `-D warnings` + +error: found `.or(Ok(…)).unwrap()` + --> $DIR/or_then_unwrap.rs:27:20 + | +LL | let _ = result.or::<&str>(Ok("fallback")).unwrap(); // should trigger lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or("fallback")` + +error: found `.or(Some(…)).unwrap()` + --> $DIR/or_then_unwrap.rs:31:31 + | +LL | let _ = option.map(|v| v).or(Some("fallback")).unwrap().to_string().chars(); // should trigger lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or("fallback")` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/ptr_arg.rs b/src/tools/clippy/tests/ui/ptr_arg.rs index 97990fedd51..03dd938a233 100644 --- a/src/tools/clippy/tests/ui/ptr_arg.rs +++ b/src/tools/clippy/tests/ui/ptr_arg.rs @@ -194,3 +194,10 @@ fn two_vecs(a: &mut Vec<u32>, b: &mut Vec<u32>) { a.push(0); b.push(1); } + +// Issue #8495 +fn cow_conditional_to_mut(a: &mut Cow<str>) { + if a.is_empty() { + a.to_mut().push_str("foo"); + } +} diff --git a/src/tools/clippy/tests/ui/single_component_path_imports_macro.fixed b/src/tools/clippy/tests/ui/single_component_path_imports_macro.fixed deleted file mode 100644 index e43f5d381aa..00000000000 --- a/src/tools/clippy/tests/ui/single_component_path_imports_macro.fixed +++ /dev/null @@ -1,20 +0,0 @@ -// run-rustfix -#![warn(clippy::single_component_path_imports)] -#![allow(unused_imports)] - -// #7106: use statements exporting a macro within a crate should not trigger lint - -macro_rules! m1 { - () => {}; -} -pub(crate) use m1; // ok - -macro_rules! m2 { - () => {}; -} - // fail - -fn main() { - m1!(); - m2!(); -} diff --git a/src/tools/clippy/tests/ui/single_component_path_imports_macro.rs b/src/tools/clippy/tests/ui/single_component_path_imports_macro.rs index 3c65ca3054c..fda294a6154 100644 --- a/src/tools/clippy/tests/ui/single_component_path_imports_macro.rs +++ b/src/tools/clippy/tests/ui/single_component_path_imports_macro.rs @@ -1,8 +1,8 @@ -// run-rustfix #![warn(clippy::single_component_path_imports)] #![allow(unused_imports)] // #7106: use statements exporting a macro within a crate should not trigger lint +// #7923: normal `use` statements of macros should also not trigger the lint macro_rules! m1 { () => {}; @@ -12,7 +12,7 @@ pub(crate) use m1; // ok macro_rules! m2 { () => {}; } -use m2; // fail +use m2; // ok fn main() { m1!(); diff --git a/src/tools/clippy/tests/ui/single_component_path_imports_macro.stderr b/src/tools/clippy/tests/ui/single_component_path_imports_macro.stderr deleted file mode 100644 index 37d5176129f..00000000000 --- a/src/tools/clippy/tests/ui/single_component_path_imports_macro.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: this import is redundant - --> $DIR/single_component_path_imports_macro.rs:15:1 - | -LL | use m2; // fail - | ^^^^^^^ help: remove it entirely - | - = note: `-D clippy::single-component-path-imports` implied by `-D warnings` - -error: aborting due to previous error - diff --git a/src/tools/clippy/tests/ui/transmute_undefined_repr.rs b/src/tools/clippy/tests/ui/transmute_undefined_repr.rs index b163d605634..b06ed4a9173 100644 --- a/src/tools/clippy/tests/ui/transmute_undefined_repr.rs +++ b/src/tools/clippy/tests/ui/transmute_undefined_repr.rs @@ -1,8 +1,9 @@ #![warn(clippy::transmute_undefined_repr)] #![allow(clippy::unit_arg, clippy::transmute_ptr_to_ref)] +use core::any::TypeId; use core::ffi::c_void; -use core::mem::{size_of, transmute}; +use core::mem::{size_of, transmute, MaybeUninit}; fn value<T>() -> T { unimplemented!() @@ -87,5 +88,57 @@ fn main() { let _: *const [u8] = transmute(value::<Box<[u8]>>()); // Ok let _: Box<[u8]> = transmute(value::<*mut [u8]>()); // Ok + + let _: Ty2<u32, u32> = transmute(value::<(Ty2<u32, u32>,)>()); // Ok + let _: (Ty2<u32, u32>,) = transmute(value::<Ty2<u32, u32>>()); // Ok + + let _: Ty2<u32, u32> = transmute(value::<(Ty2<u32, u32>, ())>()); // Ok + let _: (Ty2<u32, u32>, ()) = transmute(value::<Ty2<u32, u32>>()); // Ok + + let _: Ty2<u32, u32> = transmute(value::<((), Ty2<u32, u32>)>()); // Ok + let _: ((), Ty2<u32, u32>) = transmute(value::<Ty2<u32, u32>>()); // Ok + + let _: (usize, usize) = transmute(value::<&[u8]>()); // Ok + let _: &[u8] = transmute(value::<(usize, usize)>()); // Ok + + trait Trait {} + let _: (isize, isize) = transmute(value::<&dyn Trait>()); // Ok + let _: &dyn Trait = transmute(value::<(isize, isize)>()); // Ok + + let _: MaybeUninit<Ty2<u32, u32>> = transmute(value::<Ty2<u32, u32>>()); // Ok + let _: Ty2<u32, u32> = transmute(value::<MaybeUninit<Ty2<u32, u32>>>()); // Ok + + let _: Ty<&[u32]> = transmute::<&[u32], _>(value::<&Vec<u32>>()); // Ok + } +} + +fn _with_generics<T: 'static, U: 'static>() { + if TypeId::of::<T>() != TypeId::of::<u32>() || TypeId::of::<T>() != TypeId::of::<U>() { + return; + } + unsafe { + let _: &u32 = transmute(value::<&T>()); // Ok + let _: &T = transmute(value::<&u32>()); // Ok + + let _: Vec<U> = transmute(value::<Vec<T>>()); // Ok + let _: Vec<T> = transmute(value::<Vec<U>>()); // Ok + + let _: Ty<&u32> = transmute(value::<&T>()); // Ok + let _: Ty<&T> = transmute(value::<&u32>()); // Ok + + let _: Vec<u32> = transmute(value::<Vec<T>>()); // Ok + let _: Vec<T> = transmute(value::<Vec<u32>>()); // Ok + + let _: &Ty2<u32, u32> = transmute(value::<&Ty2<T, U>>()); // Ok + let _: &Ty2<T, U> = transmute(value::<&Ty2<u32, u32>>()); // Ok + + let _: Vec<Vec<u32>> = transmute(value::<Vec<Vec<T>>>()); // Ok + let _: Vec<Vec<T>> = transmute(value::<Vec<Vec<u32>>>()); // Ok + + let _: Vec<Ty2<T, u32>> = transmute(value::<Vec<Ty2<U, i32>>>()); // Err + let _: Vec<Ty2<U, i32>> = transmute(value::<Vec<Ty2<T, u32>>>()); // Err + + let _: *const u32 = transmute(value::<Box<T>>()); // Ok + let _: Box<T> = transmute(value::<*const u32>()); // Ok } } diff --git a/src/tools/clippy/tests/ui/transmute_undefined_repr.stderr b/src/tools/clippy/tests/ui/transmute_undefined_repr.stderr index 42d544fc954..28bfba6c757 100644 --- a/src/tools/clippy/tests/ui/transmute_undefined_repr.stderr +++ b/src/tools/clippy/tests/ui/transmute_undefined_repr.stderr @@ -1,5 +1,5 @@ error: transmute from `Ty2<u32, i32>` which has an undefined layout - --> $DIR/transmute_undefined_repr.rs:26:33 + --> $DIR/transmute_undefined_repr.rs:27:33 | LL | let _: Ty2C<u32, i32> = transmute(value::<Ty2<u32, i32>>()); // Lint, Ty2 is unordered | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,13 +7,13 @@ LL | let _: Ty2C<u32, i32> = transmute(value::<Ty2<u32, i32>>()); // Lin = note: `-D clippy::transmute-undefined-repr` implied by `-D warnings` error: transmute into `Ty2<u32, i32>` which has an undefined layout - --> $DIR/transmute_undefined_repr.rs:27:32 + --> $DIR/transmute_undefined_repr.rs:28:32 | LL | let _: Ty2<u32, i32> = transmute(value::<Ty2C<u32, i32>>()); // Lint, Ty2 is unordered | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from `Ty<Ty2<u32, i32>>` to `Ty2<u32, f32>`, both of which have an undefined layout - --> $DIR/transmute_undefined_repr.rs:32:32 + --> $DIR/transmute_undefined_repr.rs:33:32 | LL | let _: Ty2<u32, f32> = transmute(value::<Ty<Ty2<u32, i32>>>()); // Lint, different Ty2 instances | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -21,7 +21,7 @@ LL | let _: Ty2<u32, f32> = transmute(value::<Ty<Ty2<u32, i32>>>()); // = note: two instances of the same generic type (`Ty2`) may have different layouts error: transmute from `Ty2<u32, f32>` to `Ty<Ty2<u32, i32>>`, both of which have an undefined layout - --> $DIR/transmute_undefined_repr.rs:33:36 + --> $DIR/transmute_undefined_repr.rs:34:36 | LL | let _: Ty<Ty2<u32, i32>> = transmute(value::<Ty2<u32, f32>>()); // Lint, different Ty2 instances | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -29,7 +29,7 @@ LL | let _: Ty<Ty2<u32, i32>> = transmute(value::<Ty2<u32, f32>>()); // = note: two instances of the same generic type (`Ty2`) may have different layouts error: transmute from `Ty<&Ty2<u32, i32>>` to `&Ty2<u32, f32>`, both of which have an undefined layout - --> $DIR/transmute_undefined_repr.rs:38:33 + --> $DIR/transmute_undefined_repr.rs:39:33 | LL | let _: &Ty2<u32, f32> = transmute(value::<Ty<&Ty2<u32, i32>>>()); // Lint, different Ty2 instances | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL | let _: &Ty2<u32, f32> = transmute(value::<Ty<&Ty2<u32, i32>>>()); / = note: two instances of the same generic type (`Ty2`) may have different layouts error: transmute from `&Ty2<u32, f32>` to `Ty<&Ty2<u32, i32>>`, both of which have an undefined layout - --> $DIR/transmute_undefined_repr.rs:39:37 + --> $DIR/transmute_undefined_repr.rs:40:37 | LL | let _: Ty<&Ty2<u32, i32>> = transmute(value::<&Ty2<u32, f32>>()); // Lint, different Ty2 instances | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -45,7 +45,7 @@ LL | let _: Ty<&Ty2<u32, i32>> = transmute(value::<&Ty2<u32, f32>>()); / = note: two instances of the same generic type (`Ty2`) may have different layouts error: transmute from `std::boxed::Box<Ty2<u32, u32>>` to `&mut Ty2<u32, f32>`, both of which have an undefined layout - --> $DIR/transmute_undefined_repr.rs:56:45 + --> $DIR/transmute_undefined_repr.rs:57:45 | LL | let _: &'static mut Ty2<u32, f32> = transmute(value::<Box<Ty2<u32, u32>>>()); // Lint, different Ty2 instances | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -53,12 +53,28 @@ LL | let _: &'static mut Ty2<u32, f32> = transmute(value::<Box<Ty2<u32, = note: two instances of the same generic type (`Ty2`) may have different layouts error: transmute from `&mut Ty2<u32, f32>` to `std::boxed::Box<Ty2<u32, u32>>`, both of which have an undefined layout - --> $DIR/transmute_undefined_repr.rs:57:37 + --> $DIR/transmute_undefined_repr.rs:58:37 | LL | let _: Box<Ty2<u32, u32>> = transmute(value::<&'static mut Ty2<u32, f32>>()); // Lint, different Ty2 instances | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: two instances of the same generic type (`Ty2`) may have different layouts -error: aborting due to 8 previous errors +error: transmute from `std::vec::Vec<Ty2<U, i32>>` to `std::vec::Vec<Ty2<T, u32>>`, both of which have an undefined layout + --> $DIR/transmute_undefined_repr.rs:138:35 + | +LL | let _: Vec<Ty2<T, u32>> = transmute(value::<Vec<Ty2<U, i32>>>()); // Err + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: two instances of the same generic type (`Vec`) may have different layouts + +error: transmute from `std::vec::Vec<Ty2<T, u32>>` to `std::vec::Vec<Ty2<U, i32>>`, both of which have an undefined layout + --> $DIR/transmute_undefined_repr.rs:139:35 + | +LL | let _: Vec<Ty2<U, i32>> = transmute(value::<Vec<Ty2<T, u32>>>()); // Err + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: two instances of the same generic type (`Vec`) may have different layouts + +error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr index d9b64a0ed7b..de9418c8d1a 100644 --- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr +++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr @@ -34,13 +34,13 @@ error: transmute from a reference to a pointer LL | let _array_ptr_transmute = unsafe { transmute::<&[i32; 4], *const [i32; 4]>(array_ref) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `array_ref as *const [i32; 4]` -error: transmute from `fn(usize) -> u8 {main::foo}` to `*const usize` which could be expressed as a pointer cast instead +error: transmute from `fn(usize) -> u8` to `*const usize` which could be expressed as a pointer cast instead --> $DIR/transmutes_expressible_as_ptr_casts.rs:48:41 | LL | let _usize_ptr_transmute = unsafe { transmute::<fn(usize) -> u8, *const usize>(foo) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as *const usize` -error: transmute from `fn(usize) -> u8 {main::foo}` to `usize` which could be expressed as a pointer cast instead +error: transmute from `fn(usize) -> u8` to `usize` which could be expressed as a pointer cast instead --> $DIR/transmutes_expressible_as_ptr_casts.rs:52:49 | LL | let _usize_from_fn_ptr_transmute = unsafe { transmute::<fn(usize) -> u8, usize>(foo) }; diff --git a/src/tools/clippy/tests/ui/unnecessary_join.fixed b/src/tools/clippy/tests/ui/unnecessary_join.fixed new file mode 100644 index 00000000000..7e12c6ae4be --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_join.fixed @@ -0,0 +1,35 @@ +// run-rustfix + +#![warn(clippy::unnecessary_join)] + +fn main() { + // should be linted + let vector = vec!["hello", "world"]; + let output = vector + .iter() + .map(|item| item.to_uppercase()) + .collect::<String>(); + println!("{}", output); + + // should be linted + let vector = vec!["hello", "world"]; + let output = vector + .iter() + .map(|item| item.to_uppercase()) + .collect::<String>(); + println!("{}", output); + + // should not be linted + let vector = vec!["hello", "world"]; + let output = vector + .iter() + .map(|item| item.to_uppercase()) + .collect::<Vec<String>>() + .join("\n"); + println!("{}", output); + + // should not be linted + let vector = vec!["hello", "world"]; + let output = vector.iter().map(|item| item.to_uppercase()).collect::<String>(); + println!("{}", output); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_join.rs b/src/tools/clippy/tests/ui/unnecessary_join.rs new file mode 100644 index 00000000000..0a21656a755 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_join.rs @@ -0,0 +1,37 @@ +// run-rustfix + +#![warn(clippy::unnecessary_join)] + +fn main() { + // should be linted + let vector = vec!["hello", "world"]; + let output = vector + .iter() + .map(|item| item.to_uppercase()) + .collect::<Vec<String>>() + .join(""); + println!("{}", output); + + // should be linted + let vector = vec!["hello", "world"]; + let output = vector + .iter() + .map(|item| item.to_uppercase()) + .collect::<Vec<_>>() + .join(""); + println!("{}", output); + + // should not be linted + let vector = vec!["hello", "world"]; + let output = vector + .iter() + .map(|item| item.to_uppercase()) + .collect::<Vec<String>>() + .join("\n"); + println!("{}", output); + + // should not be linted + let vector = vec!["hello", "world"]; + let output = vector.iter().map(|item| item.to_uppercase()).collect::<String>(); + println!("{}", output); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_join.stderr b/src/tools/clippy/tests/ui/unnecessary_join.stderr new file mode 100644 index 00000000000..0b14b143aff --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_join.stderr @@ -0,0 +1,20 @@ +error: called `.collect<Vec<String>>().join("")` on an iterator + --> $DIR/unnecessary_join.rs:11:10 + | +LL | .collect::<Vec<String>>() + | __________^ +LL | | .join(""); + | |_________________^ help: try using: `collect::<String>()` + | + = note: `-D clippy::unnecessary-join` implied by `-D warnings` + +error: called `.collect<Vec<String>>().join("")` on an iterator + --> $DIR/unnecessary_join.rs:20:10 + | +LL | .collect::<Vec<_>>() + | __________^ +LL | | .join(""); + | |_________________^ help: try using: `collect::<String>()` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed index 4ba2a0a5dbc..65fcdc43061 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed @@ -115,6 +115,14 @@ fn main() { let _: Result<usize, usize> = res.or(Ok(2)); let _: Result<usize, usize> = res.or(Ok(astronomers_pi)); let _: Result<usize, usize> = res.or(Ok(ext_str.some_field)); + let _: Result<usize, usize> = res. + // some lines + // some lines + // some lines + // some lines + // some lines + // some lines + or(Ok(ext_str.some_field)); // neither bind_instead_of_map nor unnecessary_lazy_eval applies here let _: Result<usize, usize> = res.and_then(|x| Err(x)); diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs index 466915217e4..206080ed69a 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs @@ -115,6 +115,14 @@ fn main() { let _: Result<usize, usize> = res.or_else(|_| Ok(2)); let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi)); let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field)); + let _: Result<usize, usize> = res. + // some lines + // some lines + // some lines + // some lines + // some lines + // some lines + or_else(|_| Ok(ext_str.some_field)); // neither bind_instead_of_map nor unnecessary_lazy_eval applies here let _: Result<usize, usize> = res.and_then(|x| Err(x)); diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr index cc94bd5cd9e..7e4dd7730e7 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr @@ -2,7 +2,9 @@ error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:35:13 | LL | let _ = opt.unwrap_or_else(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `opt.unwrap_or(2)` + | ^^^^-------------------- + | | + | help: use `unwrap_or(..)` instead: `unwrap_or(2)` | = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings` @@ -10,187 +12,264 @@ error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:36:13 | LL | let _ = opt.unwrap_or_else(|| astronomers_pi); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `opt.unwrap_or(astronomers_pi)` + | ^^^^--------------------------------- + | | + | help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:37:13 | LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `opt.unwrap_or(ext_str.some_field)` + | ^^^^------------------------------------- + | | + | help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:39:13 | LL | let _ = opt.and_then(|_| ext_opt); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `opt.and(ext_opt)` + | ^^^^--------------------- + | | + | help: use `and(..)` instead: `and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:40:13 | LL | let _ = opt.or_else(|| ext_opt); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `opt.or(ext_opt)` + | ^^^^------------------- + | | + | help: use `or(..)` instead: `or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:41:13 | LL | let _ = opt.or_else(|| None); - | ^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `opt.or(None)` + | ^^^^---------------- + | | + | help: use `or(..)` instead: `or(None)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:42:13 | LL | let _ = opt.get_or_insert_with(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `get_or_insert` instead: `opt.get_or_insert(2)` + | ^^^^------------------------ + | | + | help: use `get_or_insert(..)` instead: `get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:43:13 | LL | let _ = opt.ok_or_else(|| 2); - | ^^^^^^^^^^^^^^^^^^^^ help: use `ok_or` instead: `opt.ok_or(2)` + | ^^^^---------------- + | | + | help: use `ok_or(..)` instead: `ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:44:13 | LL | let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `nested_tuple_opt.unwrap_or(Some((1, 2)))` + | ^^^^^^^^^^^^^^^^^------------------------------- + | | + | help: use `unwrap_or(..)` instead: `unwrap_or(Some((1, 2)))` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:47:13 | LL | let _ = Some(10).unwrap_or_else(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `Some(10).unwrap_or(2)` + | ^^^^^^^^^-------------------- + | | + | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:48:13 | LL | let _ = Some(10).and_then(|_| ext_opt); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `Some(10).and(ext_opt)` + | ^^^^^^^^^--------------------- + | | + | help: use `and(..)` instead: `and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:49:28 | LL | let _: Option<usize> = None.or_else(|| ext_opt); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `None.or(ext_opt)` + | ^^^^^------------------- + | | + | help: use `or(..)` instead: `or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:50:13 | LL | let _ = None.get_or_insert_with(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `get_or_insert` instead: `None.get_or_insert(2)` + | ^^^^^------------------------ + | | + | help: use `get_or_insert(..)` instead: `get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:51:35 | LL | let _: Result<usize, usize> = None.ok_or_else(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^ help: use `ok_or` instead: `None.ok_or(2)` + | ^^^^^---------------- + | | + | help: use `ok_or(..)` instead: `ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:52:28 | LL | let _: Option<usize> = None.or_else(|| None); - | ^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `None.or(None)` + | ^^^^^---------------- + | | + | help: use `or(..)` instead: `or(None)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:55:13 | LL | let _ = deep.0.unwrap_or_else(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `deep.0.unwrap_or(2)` + | ^^^^^^^-------------------- + | | + | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:56:13 | LL | let _ = deep.0.and_then(|_| ext_opt); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `deep.0.and(ext_opt)` + | ^^^^^^^--------------------- + | | + | help: use `and(..)` instead: `and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:57:13 | LL | let _ = deep.0.or_else(|| None); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `deep.0.or(None)` + | ^^^^^^^---------------- + | | + | help: use `or(..)` instead: `or(None)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:58:13 | LL | let _ = deep.0.get_or_insert_with(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `get_or_insert` instead: `deep.0.get_or_insert(2)` + | ^^^^^^^------------------------ + | | + | help: use `get_or_insert(..)` instead: `get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:59:13 | LL | let _ = deep.0.ok_or_else(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `ok_or` instead: `deep.0.ok_or(2)` + | ^^^^^^^---------------- + | | + | help: use `ok_or(..)` instead: `ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:79:28 | LL | let _: Option<usize> = None.or_else(|| Some(3)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `None.or(Some(3))` + | ^^^^^------------------- + | | + | help: use `or(..)` instead: `or(Some(3))` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:80:13 | LL | let _ = deep.0.or_else(|| Some(3)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `deep.0.or(Some(3))` + | ^^^^^^^------------------- + | | + | help: use `or(..)` instead: `or(Some(3))` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:81:13 | LL | let _ = opt.or_else(|| Some(3)); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `opt.or(Some(3))` + | ^^^^------------------- + | | + | help: use `or(..)` instead: `or(Some(3))` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:87:13 | LL | let _ = res2.unwrap_or_else(|_| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `res2.unwrap_or(2)` + | ^^^^^--------------------- + | | + | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:88:13 | LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `res2.unwrap_or(astronomers_pi)` + | ^^^^^---------------------------------- + | | + | help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:89:13 | LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `res2.unwrap_or(ext_str.some_field)` + | ^^^^^-------------------------------------- + | | + | help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:111:35 | LL | let _: Result<usize, usize> = res.and_then(|_| Err(2)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `res.and(Err(2))` + | ^^^^-------------------- + | | + | help: use `and(..)` instead: `and(Err(2))` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:112:35 | LL | let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `res.and(Err(astronomers_pi))` + | ^^^^--------------------------------- + | | + | help: use `and(..)` instead: `and(Err(astronomers_pi))` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:113:35 | LL | let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `res.and(Err(ext_str.some_field))` + | ^^^^------------------------------------- + | | + | help: use `and(..)` instead: `and(Err(ext_str.some_field))` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:115:35 | LL | let _: Result<usize, usize> = res.or_else(|_| Ok(2)); - | ^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `res.or(Ok(2))` + | ^^^^------------------ + | | + | help: use `or(..)` instead: `or(Ok(2))` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:116:35 | LL | let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `res.or(Ok(astronomers_pi))` + | ^^^^------------------------------- + | | + | help: use `or(..)` instead: `or(Ok(astronomers_pi))` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:117:35 | LL | let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `res.or(Ok(ext_str.some_field))` + | ^^^^----------------------------------- + | | + | help: use `or(..)` instead: `or(Ok(ext_str.some_field))` -error: aborting due to 32 previous errors +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:118:35 + | +LL | let _: Result<usize, usize> = res. + | ___________________________________^ +LL | | // some lines +LL | | // some lines +LL | | // some lines +... | +LL | | // some lines +LL | | or_else(|_| Ok(ext_str.some_field)); + | |_________----------------------------------^ + | | + | help: use `or(..)` instead: `or(Ok(ext_str.some_field))` + +error: aborting due to 33 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr index 75674b0a9d2..20acab6e844 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr @@ -2,7 +2,9 @@ error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval_unfixable.rs:12:13 | LL | let _ = Ok(1).unwrap_or_else(|()| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `Ok(1).unwrap_or(2)` + | ^^^^^^---------------------- + | | + | help: use `unwrap_or(..)` instead: `unwrap_or(2)` | = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings` @@ -10,13 +12,17 @@ error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval_unfixable.rs:16:13 | LL | let _ = Ok(1).unwrap_or_else(|e::E| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `Ok(1).unwrap_or(2)` + | ^^^^^^------------------------ + | | + | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval_unfixable.rs:17:13 | LL | let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `Ok(1).unwrap_or(2)` + | ^^^^^^------------------------------------- + | | + | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed index 720138db137..38ba41ac54e 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed @@ -212,3 +212,51 @@ fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::E } fn require_string(_: &String) {} + +// https://github.com/rust-lang/rust-clippy/issues/8507 +mod issue_8507 { + #![allow(dead_code)] + + struct Opaque<P>(P); + + pub trait Abstracted {} + + impl<P> Abstracted for Opaque<P> {} + + fn build<P>(p: P) -> Opaque<P> + where + P: AsRef<str>, + { + Opaque(p) + } + + // Should not lint. + fn test_str(s: &str) -> Box<dyn Abstracted> { + Box::new(build(s.to_string())) + } + + // Should not lint. + fn test_x(x: super::X) -> Box<dyn Abstracted> { + Box::new(build(x)) + } + + #[derive(Clone, Copy)] + struct Y(&'static str); + + impl AsRef<str> for Y { + fn as_ref(&self) -> &str { + self.0 + } + } + + impl ToString for Y { + fn to_string(&self) -> String { + self.0.to_string() + } + } + + // Should lint because Y is copy. + fn test_y(y: Y) -> Box<dyn Abstracted> { + Box::new(build(y)) + } +} diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs index 60b2e718f5d..15fb7ee83e3 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs @@ -212,3 +212,51 @@ fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::E } fn require_string(_: &String) {} + +// https://github.com/rust-lang/rust-clippy/issues/8507 +mod issue_8507 { + #![allow(dead_code)] + + struct Opaque<P>(P); + + pub trait Abstracted {} + + impl<P> Abstracted for Opaque<P> {} + + fn build<P>(p: P) -> Opaque<P> + where + P: AsRef<str>, + { + Opaque(p) + } + + // Should not lint. + fn test_str(s: &str) -> Box<dyn Abstracted> { + Box::new(build(s.to_string())) + } + + // Should not lint. + fn test_x(x: super::X) -> Box<dyn Abstracted> { + Box::new(build(x)) + } + + #[derive(Clone, Copy)] + struct Y(&'static str); + + impl AsRef<str> for Y { + fn as_ref(&self) -> &str { + self.0 + } + } + + impl ToString for Y { + fn to_string(&self) -> String { + self.0.to_string() + } + } + + // Should lint because Y is copy. + fn test_y(y: Y) -> Box<dyn Abstracted> { + Box::new(build(y.to_string())) + } +} diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr index 1dfc65e22e2..c53ce32be77 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr @@ -491,5 +491,11 @@ LL - let path = match get_file_path(&t) { LL + let path = match get_file_path(t) { | -error: aborting due to 76 previous errors +error: unnecessary use of `to_string` + --> $DIR/unnecessary_to_owned.rs:260:24 + | +LL | Box::new(build(y.to_string())) + | ^^^^^^^^^^^^^ help: use: `y` + +error: aborting due to 77 previous errors |
