Auto merge of #90352 - camsteffen:for-loop-desugar, r=oli-obk

Simplify `for` loop desugar

Basically two intermediate bindings are inlined. I could have left one intermediate binding in place as this would simplify some diagnostic logic, but I think the difference in that regard would be negligible, so it is better to have a minimal HIR.

For checking that the pattern is irrefutable, I added a special case when the `match` is found to be non-exhaustive.

The reordering of the arms is purely stylistic. I don't *think* there are any perf implications.

```diff
  match IntoIterator::into_iter($head) {
      mut iter => {
          $label: loop {
-             let mut __next;
              match Iterator::next(&mut iter) {
-                 Some(val) => __next = val,
                  None => break,
+                 Some($pat) => $block,
              }
-             let $pat = __next;
-             $block
          }
      }
  }
```
This commit is contained in:
bors 2021-11-21 21:20:20 +00:00
commit cebd2dda1d
42 changed files with 323 additions and 571 deletions

View File

@ -13,7 +13,7 @@ use rustc_session::parse::feature_err;
use rustc_span::hygiene::ExpnId;
use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned};
use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::{hygiene::ForLoopLoc, DUMMY_SP};
use rustc_span::DUMMY_SP;
impl<'hir> LoweringContext<'_, 'hir> {
fn lower_exprs(&mut self, exprs: &[AstP<Expr>]) -> &'hir [hir::Expr<'hir>] {
@ -1308,16 +1308,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
/// Desugar `ExprForLoop` from: `[opt_ident]: for <pat> in <head> <body>` into:
/// ```rust
/// {
/// let result = match ::std::iter::IntoIterator::into_iter(<head>) {
/// let result = match IntoIterator::into_iter(<head>) {
/// mut iter => {
/// [opt_ident]: loop {
/// let mut __next;
/// match ::std::iter::Iterator::next(&mut iter) {
/// ::std::option::Option::Some(val) => __next = val,
/// ::std::option::Option::None => break
/// match Iterator::next(&mut iter) {
/// None => break,
/// Some(<pat>) => <body>,
/// };
/// let <pat> = __next;
/// StmtKind::Expr(<body>);
/// }
/// }
/// };
@ -1332,133 +1329,75 @@ impl<'hir> LoweringContext<'_, 'hir> {
body: &Block,
opt_label: Option<Label>,
) -> hir::Expr<'hir> {
// expand <head>
let head = self.lower_expr_mut(head);
let desugared_span =
self.mark_span_with_reason(DesugaringKind::ForLoop(ForLoopLoc::Head), head.span, None);
let e_span = self.lower_span(e.span);
let pat = self.lower_pat(pat);
let for_span =
self.mark_span_with_reason(DesugaringKind::ForLoop, self.lower_span(e.span), None);
let head_span = self.mark_span_with_reason(DesugaringKind::ForLoop, head.span, None);
let pat_span = self.mark_span_with_reason(DesugaringKind::ForLoop, pat.span, None);
let iter = Ident::with_dummy_span(sym::iter);
let next_ident = Ident::with_dummy_span(sym::__next);
let (next_pat, next_pat_hid) = self.pat_ident_binding_mode(
desugared_span,
next_ident,
hir::BindingAnnotation::Mutable,
);
// `::std::option::Option::Some(val) => __next = val`
let pat_arm = {
let val_ident = Ident::with_dummy_span(sym::val);
let pat_span = self.lower_span(pat.span);
let (val_pat, val_pat_hid) = self.pat_ident(pat_span, val_ident);
let val_expr = self.expr_ident(pat_span, val_ident, val_pat_hid);
let next_expr = self.expr_ident(pat_span, next_ident, next_pat_hid);
let assign = self.arena.alloc(self.expr(
pat_span,
hir::ExprKind::Assign(next_expr, val_expr, self.lower_span(pat_span)),
ThinVec::new(),
));
let some_pat = self.pat_some(pat_span, val_pat);
self.arm(some_pat, assign)
};
// `::std::option::Option::None => break`
let break_arm = {
// `None => break`
let none_arm = {
let break_expr =
self.with_loop_scope(e.id, |this| this.expr_break_alloc(e_span, ThinVec::new()));
let pat = self.pat_none(e_span);
self.with_loop_scope(e.id, |this| this.expr_break_alloc(for_span, ThinVec::new()));
let pat = self.pat_none(for_span);
self.arm(pat, break_expr)
};
// `mut iter`
let (iter_pat, iter_pat_nid) =
self.pat_ident_binding_mode(desugared_span, iter, hir::BindingAnnotation::Mutable);
// Some(<pat>) => <body>,
let some_arm = {
let some_pat = self.pat_some(pat_span, pat);
let body_block = self.with_loop_scope(e.id, |this| this.lower_block(body, false));
let body_expr = self.arena.alloc(self.expr_block(body_block, ThinVec::new()));
self.arm(some_pat, body_expr)
};
// `match ::std::iter::Iterator::next(&mut iter) { ... }`
// `mut iter`
let iter = Ident::with_dummy_span(sym::iter);
let (iter_pat, iter_pat_nid) =
self.pat_ident_binding_mode(head_span, iter, hir::BindingAnnotation::Mutable);
// `match Iterator::next(&mut iter) { ... }`
let match_expr = {
let iter = self.expr_ident(desugared_span, iter, iter_pat_nid);
let ref_mut_iter = self.expr_mut_addr_of(desugared_span, iter);
let iter = self.expr_ident(head_span, iter, iter_pat_nid);
let ref_mut_iter = self.expr_mut_addr_of(head_span, iter);
let next_expr = self.expr_call_lang_item_fn(
desugared_span,
head_span,
hir::LangItem::IteratorNext,
arena_vec![self; ref_mut_iter],
);
let arms = arena_vec![self; pat_arm, break_arm];
let arms = arena_vec![self; none_arm, some_arm];
self.expr_match(desugared_span, next_expr, arms, hir::MatchSource::ForLoopDesugar)
self.expr_match(head_span, next_expr, arms, hir::MatchSource::ForLoopDesugar)
};
let match_stmt = self.stmt_expr(desugared_span, match_expr);
let match_stmt = self.stmt_expr(for_span, match_expr);
let next_expr = self.expr_ident(desugared_span, next_ident, next_pat_hid);
// `let mut __next`
let next_let = self.stmt_let_pat(
None,
desugared_span,
None,
next_pat,
hir::LocalSource::ForLoopDesugar,
);
// `let <pat> = __next`
let pat = self.lower_pat(pat);
let pat_let = self.stmt_let_pat(
None,
desugared_span,
Some(next_expr),
pat,
hir::LocalSource::ForLoopDesugar,
);
let body_block = self.with_loop_scope(e.id, |this| this.lower_block(body, false));
let body_expr = self.expr_block(body_block, ThinVec::new());
let body_stmt = self.stmt_expr(body_block.span, body_expr);
let loop_block = self.block_all(
e_span,
arena_vec![self; next_let, match_stmt, pat_let, body_stmt],
None,
);
let loop_block = self.block_all(for_span, arena_vec![self; match_stmt], None);
// `[opt_ident]: loop { ... }`
let kind = hir::ExprKind::Loop(
loop_block,
self.lower_label(opt_label),
hir::LoopSource::ForLoop,
self.lower_span(e_span.with_hi(head.span.hi())),
self.lower_span(for_span.with_hi(head.span.hi())),
);
let loop_expr = self.arena.alloc(hir::Expr {
hir_id: self.lower_node_id(e.id),
kind,
span: self.lower_span(e.span),
});
let loop_expr =
self.arena.alloc(hir::Expr { hir_id: self.lower_node_id(e.id), kind, span: for_span });
// `mut iter => { ... }`
let iter_arm = self.arm(iter_pat, loop_expr);
let into_iter_span = self.mark_span_with_reason(
DesugaringKind::ForLoop(ForLoopLoc::IntoIter),
head.span,
None,
);
// `match ::std::iter::IntoIterator::into_iter(<head>) { ... }`
let into_iter_expr = {
self.expr_call_lang_item_fn(
into_iter_span,
head_span,
hir::LangItem::IntoIterIntoIter,
arena_vec![self; head],
)
};
// #82462: to correctly diagnose borrow errors, the block that contains
// the iter expr needs to have a span that covers the loop body.
let desugared_full_span =
self.mark_span_with_reason(DesugaringKind::ForLoop(ForLoopLoc::Head), e_span, None);
let match_expr = self.arena.alloc(self.expr_match(
desugared_full_span,
for_span,
into_iter_expr,
arena_vec![self; iter_arm],
hir::MatchSource::ForLoopDesugar,
@ -1472,7 +1411,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
// surrounding scope of the `match` since the `match` is not a terminating scope.
//
// Also, add the attributes to the outer returned expr node.
self.expr_drop_temps_mut(desugared_full_span, match_expr, attrs.into())
self.expr_drop_temps_mut(for_span, match_expr, attrs.into())
}
/// Desugar `ExprKind::Try` from: `<expr>?` into:

View File

@ -13,7 +13,7 @@ use rustc_middle::mir::{
use rustc_middle::ty::adjustment::PointerCast;
use rustc_middle::ty::{self, RegionVid, TyCtxt};
use rustc_span::symbol::Symbol;
use rustc_span::Span;
use rustc_span::{sym, DesugaringKind, Span};
use crate::region_infer::BlameConstraint;
use crate::{
@ -135,7 +135,16 @@ impl BorrowExplanation {
should_note_order,
} => {
let local_decl = &body.local_decls[dropped_local];
let (dtor_desc, type_desc) = match local_decl.ty.kind() {
let mut ty = local_decl.ty;
if local_decl.source_info.span.desugaring_kind() == Some(DesugaringKind::ForLoop) {
if let ty::Adt(adt, substs) = local_decl.ty.kind() {
if tcx.is_diagnostic_item(sym::Option, adt.did) {
// in for loop desugaring, only look at the `Some(..)` inner type
ty = substs.type_at(0);
}
}
}
let (dtor_desc, type_desc) = match ty.kind() {
// If type is an ADT that implements Drop, then
// simplify output by reporting just the ADT name.
ty::Adt(adt, _substs) if adt.has_dtor(tcx) && !adt.is_box() => {

View File

@ -13,11 +13,7 @@ use rustc_middle::mir::{
use rustc_middle::ty::print::Print;
use rustc_middle::ty::{self, DefIdTree, Instance, Ty, TyCtxt};
use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult};
use rustc_span::{
hygiene::{DesugaringKind, ForLoopLoc},
symbol::sym,
Span,
};
use rustc_span::{hygiene::DesugaringKind, symbol::sym, Span};
use rustc_target::abi::VariantIdx;
use super::borrow_set::BorrowData;
@ -955,10 +951,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let kind = kind.unwrap_or_else(|| {
// This isn't a 'special' use of `self`
debug!("move_spans: method_did={:?}, fn_call_span={:?}", method_did, fn_call_span);
let implicit_into_iter = matches!(
fn_call_span.desugaring_kind(),
Some(DesugaringKind::ForLoop(ForLoopLoc::IntoIter))
);
let implicit_into_iter = Some(method_did) == tcx.lang_items().into_iter_fn()
&& fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop);
let parent_self_ty = parent
.filter(|did| tcx.def_kind(*did) == rustc_hir::def::DefKind::Impl)
.and_then(|did| match tcx.type_of(did).kind() {

View File

@ -445,15 +445,23 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
},
))) => {
// check if the RHS is from desugaring
let locations = self.body.find_assignments(local);
let opt_assignment_rhs_span = locations
.first()
.map(|&location| self.body.source_info(location).span);
let opt_desugaring_kind =
opt_assignment_rhs_span.and_then(|span| span.desugaring_kind());
match opt_desugaring_kind {
let opt_assignment_rhs_span =
self.body.find_assignments(local).first().map(|&location| {
let stmt = &self.body[location.block].statements
[location.statement_index];
match stmt.kind {
mir::StatementKind::Assign(box (
_,
mir::Rvalue::Use(mir::Operand::Copy(place)),
)) => {
self.body.local_decls[place.local].source_info.span
}
_ => self.body.source_info(location).span,
}
});
match opt_assignment_rhs_span.and_then(|s| s.desugaring_kind()) {
// on for loops, RHS points to the iterator part
Some(DesugaringKind::ForLoop(_)) => {
Some(DesugaringKind::ForLoop) => {
self.suggest_similar_mut_method_for_for_loop(&mut err);
Some((
false,

View File

@ -1821,8 +1821,6 @@ impl<'hir> QPath<'hir> {
pub enum LocalSource {
/// A `match _ { .. }`.
Normal,
/// A desugared `for _ in _ { .. }` loop.
ForLoopDesugar,
/// When lowering async functions, we create locals within the `async move` so that
/// all parameters are dropped after the future is polled.
///

View File

@ -2,6 +2,7 @@ use crate::def::{CtorOf, DefKind, Res};
use crate::def_id::DefId;
use crate::hir::{self, HirId, PatKind};
use rustc_data_structures::stable_set::FxHashSet;
use rustc_span::hygiene::DesugaringKind;
use rustc_span::symbol::Ident;
use rustc_span::Span;
@ -143,4 +144,14 @@ impl hir::Pat<'_> {
});
result
}
/// If the pattern is `Some(<pat>)` from a desugared for loop, returns the inner pattern
pub fn for_loop_some(&self) -> Option<&Self> {
if self.span.desugaring_kind() == Some(DesugaringKind::ForLoop) {
if let hir::PatKind::Struct(_, [pat_field], _) = self.kind {
return Some(pat_field.pat);
}
}
None
}
}

View File

@ -5,13 +5,12 @@ use rustc_hir as hir;
use rustc_hir::def::{DefKind, Namespace};
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::{Body, Expr, ExprKind, FnRetTy, HirId, Local, Pat};
use rustc_hir::{Body, Expr, ExprKind, FnRetTy, HirId, Local, MatchSource, Pat};
use rustc_middle::hir::map::Map;
use rustc_middle::infer::unify_key::ConstVariableOriginKind;
use rustc_middle::ty::print::Print;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
use rustc_middle::ty::{self, DefIdTree, InferConst, Ty, TyCtxt};
use rustc_span::source_map::DesugaringKind;
use rustc_span::symbol::kw;
use rustc_span::Span;
use std::borrow::Cow;
@ -26,6 +25,7 @@ struct FindHirNodeVisitor<'a, 'tcx> {
found_closure: Option<&'tcx Expr<'tcx>>,
found_method_call: Option<&'tcx Expr<'tcx>>,
found_exact_method_call: Option<&'tcx Expr<'tcx>>,
found_for_loop_iter: Option<&'tcx Expr<'tcx>>,
found_use_diagnostic: Option<UseDiagnostic<'tcx>>,
}
@ -41,6 +41,7 @@ impl<'a, 'tcx> FindHirNodeVisitor<'a, 'tcx> {
found_closure: None,
found_method_call: None,
found_exact_method_call: None,
found_for_loop_iter: None,
found_use_diagnostic: None,
}
}
@ -111,6 +112,15 @@ impl<'a, 'tcx> Visitor<'tcx> for FindHirNodeVisitor<'a, 'tcx> {
}
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
if let ExprKind::Match(scrutinee, [_, arm], MatchSource::ForLoopDesugar) = expr.kind {
if let Some(pat) = arm.pat.for_loop_some() {
if let Some(ty) = self.node_ty_contains_target(pat.hir_id) {
self.found_for_loop_iter = Some(scrutinee);
self.found_node_ty = Some(ty);
return;
}
}
}
if let ExprKind::MethodCall(_, call_span, exprs, _) = expr.kind {
if call_span == self.target_span
&& Some(self.target)
@ -643,10 +653,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
let msg = if let Some(simple_ident) = pattern.simple_ident() {
match pattern.span.desugaring_kind() {
None => format!("consider giving `{}` {}", simple_ident, suffix),
Some(DesugaringKind::ForLoop(_)) => {
"the element type for this iterator is not specified".to_string()
}
_ => format!("this needs {}", suffix),
Some(_) => format!("this needs {}", suffix),
}
} else {
format!("consider giving this pattern {}", suffix)
@ -719,6 +726,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
// = note: type must be known at this point
self.annotate_method_call(segment, e, &mut err);
}
} else if let Some(scrutinee) = local_visitor.found_for_loop_iter {
err.span_label(
scrutinee.span,
"the element type for this iterator is not specified".to_string(),
);
}
// Instead of the following:
// error[E0282]: type annotations needed

View File

@ -395,7 +395,7 @@ pub fn in_external_macro(sess: &Session, span: Span) -> bool {
match expn_data.kind {
ExpnKind::Inlined
| ExpnKind::Root
| ExpnKind::Desugaring(DesugaringKind::ForLoop(_) | DesugaringKind::WhileLoop) => false,
| ExpnKind::Desugaring(DesugaringKind::ForLoop | DesugaringKind::WhileLoop) => false,
ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) => true, // well, it's "external"
ExpnKind::Macro(MacroKind::Bang, _) => {
// Dummy span for the `def_site` means it's an external macro.

View File

@ -74,19 +74,16 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, '_, 'tcx> {
let (msg, sp) = match loc.source {
hir::LocalSource::Normal => ("local binding", Some(loc.span)),
hir::LocalSource::ForLoopDesugar => ("`for` loop binding", None),
hir::LocalSource::AsyncFn => ("async fn binding", None),
hir::LocalSource::AwaitDesugar => ("`await` future binding", None),
hir::LocalSource::AssignDesugar(_) => ("destructuring assignment binding", None),
};
self.check_irrefutable(&loc.pat, msg, sp);
self.check_patterns(&loc.pat, Irrefutable);
}
fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
intravisit::walk_param(self, param);
self.check_irrefutable(&param.pat, "function argument", None);
self.check_patterns(&param.pat, Irrefutable);
}
}
@ -161,12 +158,12 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
fn check_match(
&mut self,
scrut: &hir::Expr<'_>,
arms: &'tcx [hir::Arm<'tcx>],
hir_arms: &'tcx [hir::Arm<'tcx>],
source: hir::MatchSource,
) {
let mut cx = self.new_cx(scrut.hir_id);
for arm in arms {
for arm in hir_arms {
// Check the arm for some things unrelated to exhaustiveness.
self.check_patterns(&arm.pat, Refutable);
if let Some(hir::Guard::IfLet(ref pat, _)) = arm.guard {
@ -178,7 +175,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
let mut have_errors = false;
let arms: Vec<_> = arms
let arms: Vec<_> = hir_arms
.iter()
.map(|hir::Arm { pat, guard, .. }| MatchArm {
pat: self.lower_pattern(&mut cx, pat, &mut have_errors),
@ -196,6 +193,9 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
let report = compute_match_usefulness(&cx, &arms, scrut.hir_id, scrut_ty);
match source {
// Don't report arm reachability of desugared `match $iter.into_iter() { iter => .. }`
// when the iterator is an uninhabited type. unreachable_code will trigger instead.
hir::MatchSource::ForLoopDesugar if arms.len() == 1 => {}
hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => {
report_arm_reachability(&cx, &report)
}
@ -208,7 +208,13 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
let is_empty_match = arms.is_empty();
let witnesses = report.non_exhaustiveness_witnesses;
if !witnesses.is_empty() {
non_exhaustive_match(&cx, scrut_ty, scrut.span, witnesses, is_empty_match);
if source == hir::MatchSource::ForLoopDesugar && hir_arms.len() == 2 {
// the for loop pattern is not irrefutable
let pat = hir_arms[1].pat.for_loop_some().unwrap();
self.check_irrefutable(pat, "`for` loop binding", None);
} else {
non_exhaustive_match(&cx, scrut_ty, scrut.span, witnesses, is_empty_match);
}
}
}
@ -225,6 +231,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
let witnesses = report.non_exhaustiveness_witnesses;
if witnesses.is_empty() {
// The pattern is irrefutable.
self.check_patterns(pat, Irrefutable);
return;
}

View File

@ -1099,18 +1099,11 @@ pub enum DesugaringKind {
OpaqueTy,
Async,
Await,
ForLoop(ForLoopLoc),
ForLoop,
LetElse,
WhileLoop,
}
/// A location in the desugaring of a `for` loop
#[derive(Clone, Copy, PartialEq, Debug, Encodable, Decodable, HashStable_Generic)]
pub enum ForLoopLoc {
Head,
IntoIter,
}
impl DesugaringKind {
/// The description wording should combine well with "desugaring of {}".
pub fn descr(self) -> &'static str {
@ -1121,7 +1114,7 @@ impl DesugaringKind {
DesugaringKind::QuestionMark => "operator `?`",
DesugaringKind::TryBlock => "`try` block",
DesugaringKind::OpaqueTy => "`impl Trait`",
DesugaringKind::ForLoop(_) => "`for` loop",
DesugaringKind::ForLoop => "`for` loop",
DesugaringKind::LetElse => "`let...else`",
DesugaringKind::WhileLoop => "`while` loop",
}

View File

@ -41,7 +41,7 @@ pub mod edition;
use edition::Edition;
pub mod hygiene;
use hygiene::Transparency;
pub use hygiene::{DesugaringKind, ExpnKind, ForLoopLoc, MacroKind};
pub use hygiene::{DesugaringKind, ExpnKind, MacroKind};
pub use hygiene::{ExpnData, ExpnHash, ExpnId, LocalExpnId, SyntaxContext};
pub mod def_id;
use def_id::{CrateNum, DefId, DefPathHash, LocalDefId, LOCAL_CRATE};

View File

@ -269,7 +269,6 @@ symbols! {
__D,
__H,
__S,
__next,
__try_var,
_args,
_d,

View File

@ -27,7 +27,7 @@ use rustc_middle::ty::{TypeAndMut, TypeckResults};
use rustc_session::Limit;
use rustc_span::def_id::LOCAL_CRATE;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{BytePos, DesugaringKind, ExpnKind, ForLoopLoc, MultiSpan, Span, DUMMY_SP};
use rustc_span::{BytePos, DesugaringKind, ExpnKind, MultiSpan, Span, DUMMY_SP};
use rustc_target::spec::abi;
use std::fmt;
@ -685,7 +685,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
&obligation.cause.code
{
parent_code.clone()
} else if let ExpnKind::Desugaring(DesugaringKind::ForLoop(ForLoopLoc::IntoIter)) =
} else if let ExpnKind::Desugaring(DesugaringKind::ForLoop) =
span.ctxt().outer_expn_data().kind
{
Lrc::new(obligation.cause.code.clone())
@ -765,8 +765,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
// This if is to prevent a special edge-case
if matches!(
span.ctxt().outer_expn_data().kind,
ExpnKind::Root
| ExpnKind::Desugaring(DesugaringKind::ForLoop(ForLoopLoc::IntoIter))
ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop)
) {
// We don't want a borrowing suggestion on the fields in structs,
// ```

View File

@ -549,13 +549,10 @@ mod fn_keyword {}
/// {
/// let result = match IntoIterator::into_iter(iterator) {
/// mut iter => loop {
/// let next;
/// match iter.next() {
/// Some(val) => next = val,
/// None => break,
/// Some(loop_variable) => { code(); },
/// };
/// let loop_variable = next;
/// let () = { code(); };
/// },
/// };
/// result

View File

@ -183,7 +183,7 @@ pub fn add_loop_label_to_break() {
#[cfg(not(any(cfail1,cfail4)))]
#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")]
#[rustc_clean(cfg="cfail3")]
#[rustc_clean(cfg="cfail5", except="hir_owner_nodes")]
#[rustc_clean(cfg="cfail5", except="hir_owner_nodes, optimized_mir")]
#[rustc_clean(cfg="cfail6")]
pub fn add_loop_label_to_break() {
let mut _x = 0;

View File

@ -7,39 +7,29 @@
let mut _2: std::ops::Range<i32>; // in scope 0 at $DIR/remove_storage_markers.rs:8:14: 8:19
let mut _3: std::ops::Range<i32>; // in scope 0 at $DIR/remove_storage_markers.rs:8:14: 8:19
let mut _5: (); // in scope 0 at $DIR/remove_storage_markers.rs:6:1: 11:2
let _7: (); // in scope 0 at $DIR/remove_storage_markers.rs:8:14: 8:19
let mut _8: std::option::Option<i32>; // in scope 0 at $DIR/remove_storage_markers.rs:8:14: 8:19
let _6: (); // in scope 0 at $DIR/remove_storage_markers.rs:8:14: 8:19
let mut _7: std::option::Option<i32>; // in scope 0 at $DIR/remove_storage_markers.rs:8:14: 8:19
let mut _8: &mut std::ops::Range<i32>; // in scope 0 at $DIR/remove_storage_markers.rs:8:14: 8:19
let mut _9: &mut std::ops::Range<i32>; // in scope 0 at $DIR/remove_storage_markers.rs:8:14: 8:19
let mut _10: &mut std::ops::Range<i32>; // in scope 0 at $DIR/remove_storage_markers.rs:8:14: 8:19
let mut _11: isize; // in scope 0 at $DIR/remove_storage_markers.rs:8:9: 8:10
let mut _13: i32; // in scope 0 at $DIR/remove_storage_markers.rs:8:9: 8:10
let mut _14: !; // in scope 0 at $DIR/remove_storage_markers.rs:8:5: 10:6
let _16: (); // in scope 0 at $DIR/remove_storage_markers.rs:8:20: 10:6
let mut _17: i32; // in scope 0 at $DIR/remove_storage_markers.rs:9:16: 9:17
let mut _10: isize; // in scope 0 at $DIR/remove_storage_markers.rs:8:5: 10:6
let mut _11: !; // in scope 0 at $DIR/remove_storage_markers.rs:8:5: 10:6
let mut _13: i32; // in scope 0 at $DIR/remove_storage_markers.rs:9:16: 9:17
scope 1 {
debug sum => _1; // in scope 1 at $DIR/remove_storage_markers.rs:7:9: 7:16
let mut _4: std::ops::Range<i32>; // in scope 1 at $DIR/remove_storage_markers.rs:8:14: 8:19
scope 2 {
debug iter => _4; // in scope 2 at $DIR/remove_storage_markers.rs:8:14: 8:19
let mut _6: i32; // in scope 2 at $DIR/remove_storage_markers.rs:8:14: 8:19
let _12: i32; // in scope 2 at $DIR/remove_storage_markers.rs:8:9: 8:10
scope 3 {
debug __next => _6; // in scope 3 at $DIR/remove_storage_markers.rs:8:14: 8:19
let _12: i32; // in scope 3 at $DIR/remove_storage_markers.rs:8:9: 8:10
let _15: i32; // in scope 3 at $DIR/remove_storage_markers.rs:8:9: 8:10
scope 4 {
debug val => _12; // in scope 4 at $DIR/remove_storage_markers.rs:8:9: 8:10
}
scope 5 {
debug i => _15; // in scope 5 at $DIR/remove_storage_markers.rs:8:9: 8:10
}
scope 7 (inlined iter::range::<impl Iterator for std::ops::Range<i32>>::next) { // at $DIR/remove_storage_markers.rs:8:14: 8:19
debug self => _9; // in scope 7 at $DIR/remove_storage_markers.rs:8:14: 8:19
let mut _18: &mut std::ops::Range<i32>; // in scope 7 at $DIR/remove_storage_markers.rs:8:14: 8:19
}
debug i => _12; // in scope 3 at $DIR/remove_storage_markers.rs:8:9: 8:10
}
scope 5 (inlined iter::range::<impl Iterator for std::ops::Range<i32>>::next) { // at $DIR/remove_storage_markers.rs:8:14: 8:19
debug self => _8; // in scope 5 at $DIR/remove_storage_markers.rs:8:14: 8:19
let mut _14: &mut std::ops::Range<i32>; // in scope 5 at $DIR/remove_storage_markers.rs:8:14: 8:19
}
}
scope 6 (inlined <std::ops::Range<i32> as IntoIterator>::into_iter) { // at $DIR/remove_storage_markers.rs:8:14: 8:19
debug self => _3; // in scope 6 at $DIR/remove_storage_markers.rs:8:14: 8:19
scope 4 (inlined <std::ops::Range<i32> as IntoIterator>::into_iter) { // at $DIR/remove_storage_markers.rs:8:14: 8:19
debug self => _3; // in scope 4 at $DIR/remove_storage_markers.rs:8:14: 8:19
}
}
@ -50,7 +40,7 @@
- StorageLive(_3); // scope 1 at $DIR/remove_storage_markers.rs:8:14: 8:19
(_3.0: i32) = const 0_i32; // scope 1 at $DIR/remove_storage_markers.rs:8:14: 8:19
(_3.1: i32) = const 10_i32; // scope 1 at $DIR/remove_storage_markers.rs:8:14: 8:19
_2 = move _3; // scope 6 at $DIR/remove_storage_markers.rs:8:14: 8:19
_2 = move _3; // scope 4 at $DIR/remove_storage_markers.rs:8:14: 8:19
- StorageDead(_3); // scope 1 at $DIR/remove_storage_markers.rs:8:18: 8:19
- StorageLive(_4); // scope 1 at $DIR/remove_storage_markers.rs:8:14: 8:19
_4 = move _2; // scope 1 at $DIR/remove_storage_markers.rs:8:14: 8:19
@ -59,25 +49,39 @@
bb1: {
- StorageLive(_6); // scope 2 at $DIR/remove_storage_markers.rs:8:14: 8:19
- StorageLive(_7); // scope 3 at $DIR/remove_storage_markers.rs:8:14: 8:19
- StorageLive(_8); // scope 3 at $DIR/remove_storage_markers.rs:8:14: 8:19
- StorageLive(_9); // scope 3 at $DIR/remove_storage_markers.rs:8:14: 8:19
- StorageLive(_10); // scope 3 at $DIR/remove_storage_markers.rs:8:14: 8:19
_10 = &mut _4; // scope 3 at $DIR/remove_storage_markers.rs:8:14: 8:19
_9 = &mut (*_10); // scope 3 at $DIR/remove_storage_markers.rs:8:14: 8:19
- StorageLive(_18); // scope 7 at $DIR/remove_storage_markers.rs:8:14: 8:19
_18 = &mut (*_9); // scope 7 at $DIR/remove_storage_markers.rs:8:14: 8:19
_8 = <std::ops::Range<i32> as iter::range::RangeIteratorImpl>::spec_next(move _18) -> bb4; // scope 7 at $DIR/remove_storage_markers.rs:8:14: 8:19
- StorageLive(_7); // scope 2 at $DIR/remove_storage_markers.rs:8:14: 8:19
- StorageLive(_8); // scope 2 at $DIR/remove_storage_markers.rs:8:14: 8:19
- StorageLive(_9); // scope 2 at $DIR/remove_storage_markers.rs:8:14: 8:19
_9 = &mut _4; // scope 2 at $DIR/remove_storage_markers.rs:8:14: 8:19
_8 = &mut (*_9); // scope 2 at $DIR/remove_storage_markers.rs:8:14: 8:19
- StorageLive(_14); // scope 5 at $DIR/remove_storage_markers.rs:8:14: 8:19
_14 = &mut (*_8); // scope 5 at $DIR/remove_storage_markers.rs:8:14: 8:19
_7 = <std::ops::Range<i32> as iter::range::RangeIteratorImpl>::spec_next(move _14) -> bb4; // scope 5 at $DIR/remove_storage_markers.rs:8:14: 8:19
// mir::Constant
// + span: $DIR/remove_storage_markers.rs:8:14: 8:19
// + literal: Const { ty: for<'r> fn(&'r mut std::ops::Range<i32>) -> std::option::Option<<std::ops::Range<i32> as std::iter::range::RangeIteratorImpl>::Item> {<std::ops::Range<i32> as std::iter::range::RangeIteratorImpl>::spec_next}, val: Value(Scalar(<ZST>)) }
}
bb2: {
_0 = const (); // scope 3 at $DIR/remove_storage_markers.rs:8:5: 10:6
- StorageDead(_10); // scope 3 at $DIR/remove_storage_markers.rs:8:18: 8:19
- StorageDead(_8); // scope 3 at $DIR/remove_storage_markers.rs:8:18: 8:19
- StorageDead(_7); // scope 3 at $DIR/remove_storage_markers.rs:8:18: 8:19
- StorageLive(_12); // scope 2 at $DIR/remove_storage_markers.rs:8:9: 8:10
_12 = ((_7 as Some).0: i32); // scope 2 at $DIR/remove_storage_markers.rs:8:9: 8:10
- StorageLive(_13); // scope 3 at $DIR/remove_storage_markers.rs:9:16: 9:17
_13 = _12; // scope 3 at $DIR/remove_storage_markers.rs:9:16: 9:17
_1 = Add(_1, move _13); // scope 3 at $DIR/remove_storage_markers.rs:9:9: 9:17
- StorageDead(_13); // scope 3 at $DIR/remove_storage_markers.rs:9:16: 9:17
_6 = const (); // scope 3 at $DIR/remove_storage_markers.rs:8:20: 10:6
- StorageDead(_12); // scope 2 at $DIR/remove_storage_markers.rs:10:5: 10:6
- StorageDead(_9); // scope 2 at $DIR/remove_storage_markers.rs:10:5: 10:6
- StorageDead(_7); // scope 2 at $DIR/remove_storage_markers.rs:10:5: 10:6
- StorageDead(_6); // scope 2 at $DIR/remove_storage_markers.rs:10:5: 10:6
_5 = const (); // scope 2 at $DIR/remove_storage_markers.rs:8:5: 10:6
goto -> bb1; // scope 2 at $DIR/remove_storage_markers.rs:8:5: 10:6
}
bb3: {
_0 = const (); // scope 2 at $DIR/remove_storage_markers.rs:8:5: 10:6
- StorageDead(_9); // scope 2 at $DIR/remove_storage_markers.rs:10:5: 10:6
- StorageDead(_7); // scope 2 at $DIR/remove_storage_markers.rs:10:5: 10:6
- StorageDead(_6); // scope 2 at $DIR/remove_storage_markers.rs:10:5: 10:6
- StorageDead(_4); // scope 1 at $DIR/remove_storage_markers.rs:10:5: 10:6
- StorageDead(_2); // scope 1 at $DIR/remove_storage_markers.rs:10:5: 10:6
@ -85,38 +89,11 @@
return; // scope 0 at $DIR/remove_storage_markers.rs:11:2: 11:2
}
bb3: {
- StorageLive(_12); // scope 3 at $DIR/remove_storage_markers.rs:8:9: 8:10
_12 = ((_8 as Some).0: i32); // scope 3 at $DIR/remove_storage_markers.rs:8:9: 8:10
- StorageLive(_13); // scope 4 at $DIR/remove_storage_markers.rs:8:9: 8:10
_13 = _12; // scope 4 at $DIR/remove_storage_markers.rs:8:9: 8:10
_6 = move _13; // scope 4 at $DIR/remove_storage_markers.rs:8:9: 8:10
_7 = const (); // scope 4 at $DIR/remove_storage_markers.rs:8:9: 8:10
- StorageDead(_13); // scope 4 at $DIR/remove_storage_markers.rs:8:9: 8:10
- StorageDead(_12); // scope 3 at $DIR/remove_storage_markers.rs:8:9: 8:10
- StorageDead(_10); // scope 3 at $DIR/remove_storage_markers.rs:8:18: 8:19
- StorageDead(_8); // scope 3 at $DIR/remove_storage_markers.rs:8:18: 8:19
- StorageDead(_7); // scope 3 at $DIR/remove_storage_markers.rs:8:18: 8:19
- StorageLive(_15); // scope 3 at $DIR/remove_storage_markers.rs:8:9: 8:10
_15 = _6; // scope 3 at $DIR/remove_storage_markers.rs:8:14: 8:19
- StorageLive(_16); // scope 5 at $DIR/remove_storage_markers.rs:8:20: 10:6
- StorageLive(_17); // scope 5 at $DIR/remove_storage_markers.rs:9:16: 9:17
_17 = _15; // scope 5 at $DIR/remove_storage_markers.rs:9:16: 9:17
_1 = Add(_1, move _17); // scope 5 at $DIR/remove_storage_markers.rs:9:9: 9:17
- StorageDead(_17); // scope 5 at $DIR/remove_storage_markers.rs:9:16: 9:17
_16 = const (); // scope 5 at $DIR/remove_storage_markers.rs:8:20: 10:6
- StorageDead(_16); // scope 5 at $DIR/remove_storage_markers.rs:10:5: 10:6
_5 = const (); // scope 2 at $DIR/remove_storage_markers.rs:8:5: 10:6
- StorageDead(_15); // scope 3 at $DIR/remove_storage_markers.rs:10:5: 10:6
- StorageDead(_6); // scope 2 at $DIR/remove_storage_markers.rs:10:5: 10:6
goto -> bb1; // scope 2 at $DIR/remove_storage_markers.rs:8:5: 10:6
}
bb4: {
- StorageDead(_18); // scope 7 at $DIR/remove_storage_markers.rs:8:14: 8:19
- StorageDead(_9); // scope 3 at $DIR/remove_storage_markers.rs:8:18: 8:19
_11 = discriminant(_8); // scope 3 at $DIR/remove_storage_markers.rs:8:14: 8:19
switchInt(move _11) -> [0_isize: bb2, otherwise: bb3]; // scope 3 at $DIR/remove_storage_markers.rs:8:14: 8:19
- StorageDead(_14); // scope 5 at $DIR/remove_storage_markers.rs:8:14: 8:19
- StorageDead(_8); // scope 2 at $DIR/remove_storage_markers.rs:8:18: 8:19
_10 = discriminant(_7); // scope 2 at $DIR/remove_storage_markers.rs:8:14: 8:19
switchInt(move _10) -> [0_isize: bb3, otherwise: bb2]; // scope 2 at $DIR/remove_storage_markers.rs:8:14: 8:19
}
}

View File

@ -5,7 +5,6 @@
5| 1|
6| 1| let mut x = 0;
7| 11| for _ in 0..10 {
^10
8| 10| match is_true {
9| | true => {
10| 10| continue;
@ -17,7 +16,6 @@
16| 0| x = 3;
17| | }
18| 11| for _ in 0..10 {
^10
19| 10| match is_true {
20| 0| false => {
21| 0| x = 1;
@ -29,7 +27,6 @@
27| 0| x = 3;
28| | }
29| 11| for _ in 0..10 {
^10
30| 10| match is_true {
31| 10| true => {
32| 10| x = 1;
@ -41,14 +38,12 @@
38| 10| x = 3;
39| | }
40| 11| for _ in 0..10 {
^10
41| 10| if is_true {
42| 10| continue;
43| 0| }
44| 0| x = 3;
45| | }
46| 11| for _ in 0..10 {
^10
47| 10| match is_true {
48| 0| false => {
49| 0| x = 1;

View File

@ -40,7 +40,8 @@
39| 30|}
40| |
41| 6|fn display<T: Display>(xs: &[T]) {
42| 18| for x in xs {
42| 24| for x in xs {
^18
43| 18| print!("{}", x);
44| 18| }
45| 6| println!();

View File

@ -17,7 +17,8 @@
16| 0| } else {
17| 0| }
18| |
19| 10| for i in 0..10 {
19| 11| for i in 0..10 {
^10
20| 10| if true {
21| 10| if false {
22| 0| while true {}
@ -43,7 +44,8 @@
41| 1| write!(f, "cool")?;
^0
42| | }
43| 10| for i in 0..10 {
43| 11| for i in 0..10 {
^10
44| 10| if false {
45| 0| } else {
46| 10| if false {

View File

@ -13,7 +13,7 @@
^0
13| |
14| | for
15| 2| _
15| | _
16| | in
17| 3| 0..2
18| | {

View File

@ -14,7 +14,7 @@
14| 1| countdown = 10
15| | ;
16| | for
17| 6| _
17| | _
18| | in
19| 6| 0..10
20| | {
@ -64,7 +64,7 @@
63| 1| countdown = 10
64| | ;
65| | for
66| 6| _
66| | _
67| | in
68| 6| 0..10
69| | {

View File

@ -27,7 +27,7 @@ where
let key = f(x);
result.entry(key).or_insert(Vec::new()).push(x);
}
result //~ ERROR cannot return value referencing local binding
result //~ ERROR cannot return value referencing temporary value
}
fn main() {}

View File

@ -1,8 +1,8 @@
error[E0515]: cannot return value referencing local binding
error[E0515]: cannot return value referencing temporary value
--> $DIR/return-local-binding-from-desugaring.rs:30:5
|
LL | for ref x in xs {
| -- local binding introduced here
| -- temporary value created here
...
LL | result
| ^^^^^^ returns a value referencing data owned by the current function

View File

@ -1,8 +1,10 @@
error[E0282]: type annotations needed
--> $DIR/for-loop-unconstrained-element-type.rs:8:14
--> $DIR/for-loop-unconstrained-element-type.rs:8:9
|
LL | for i in Vec::new() { }
| ^^^^^^^^^^ the element type for this iterator is not specified
| ^ ---------- the element type for this iterator is not specified
| |
| cannot infer type
error: aborting due to previous error

View File

@ -1,8 +1,8 @@
error[E0282]: type annotations needed for `&(_,)`
error[E0282]: type annotations needed for `(_,)`
--> $DIR/issue-20261.rs:4:11
|
LL | for (ref i,) in [].iter() {
| --------- the element type for this iterator is not specified
| --------- this method call resolves to `std::slice::Iter<'_, T>`
LL | i.clone();
| ^^^^^ cannot infer type
|

View File

@ -3,5 +3,4 @@ use std::collections::HashMap;
fn main() {
for _ in HashMap::new().iter().cloned() {} //~ ERROR type mismatch
//~^ ERROR type mismatch
//~| ERROR type mismatch
}

View File

@ -23,16 +23,6 @@ LL | for _ in HashMap::new().iter().cloned() {}
= note: required because of the requirements on the impl of `Iterator` for `Cloned<std::collections::hash_map::Iter<'_, _, _>>`
= note: required because of the requirements on the impl of `IntoIterator` for `Cloned<std::collections::hash_map::Iter<'_, _, _>>`
error[E0271]: type mismatch resolving `<std::collections::hash_map::Iter<'_, _, _> as Iterator>::Item == &_`
--> $DIR/issue-33941.rs:4:14
|
LL | for _ in HashMap::new().iter().cloned() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected reference, found tuple
|
= note: expected reference `&_`
found tuple `(&_, &_)`
= note: required because of the requirements on the impl of `Iterator` for `Cloned<std::collections::hash_map::Iter<'_, _, _>>`
error: aborting due to 3 previous errors
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0271`.

View File

@ -2,7 +2,7 @@ error[E0596]: cannot borrow data in a `&` reference as mutable
--> $DIR/dont-print-desugared.rs:4:10
|
LL | for &ref mut x in s {}
| ^^^^^^^^^ cannot borrow as mutable through `&` reference
| ^^^^^^^^^ cannot borrow as mutable
error[E0597]: `y` does not live long enough
--> $DIR/dont-print-desugared.rs:17:16

View File

@ -2,12 +2,10 @@ error[E0713]: borrow may still be in use when destructor runs
--> $DIR/issue-53773.rs:41:22
|
LL | members.push(child.raw);
| ^^^^^^^^^
| -------------^^^^^^^^^- borrow later used here
LL |
LL | }
| - here, drop of `child` needs exclusive access to `*child.raw`, because the type `C<'_>` implements the `Drop` trait
LL | members.len();
| ------------- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value

View File

@ -17,5 +17,4 @@ impl Iterator for Void {
fn main() {
for _ in unimplemented!() as Void {}
//~^ ERROR unreachable pattern
//~^^ ERROR unreachable pattern
}

View File

@ -10,11 +10,5 @@ note: the lint level is defined here
LL | #![deny(unreachable_patterns)]
| ^^^^^^^^^^^^^^^^^^^^
error: unreachable pattern
--> $DIR/unreachable-loop-patterns.rs:18:14
|
LL | for _ in unimplemented!() as Void {}
| ^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 2 previous errors
error: aborting due to previous error

View File

@ -1,6 +1,4 @@
use super::{
get_span_of_entire_for_loop, make_iterator_snippet, IncrementVisitor, InitializeVisitor, EXPLICIT_COUNTER_LOOP,
};
use super::{make_iterator_snippet, IncrementVisitor, InitializeVisitor, EXPLICIT_COUNTER_LOOP};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{get_enclosing_block, is_integer_const};
@ -37,12 +35,10 @@ pub(super) fn check<'tcx>(
then {
let mut applicability = Applicability::MachineApplicable;
let for_span = get_span_of_entire_for_loop(expr);
span_lint_and_sugg(
cx,
EXPLICIT_COUNTER_LOOP,
for_span.with_hi(arg.span.hi()),
expr.span.with_hi(arg.span.hi()),
&format!("the variable `{}` is used as a loop counter", name),
"consider using",
format!(

View File

@ -1,4 +1,4 @@
use super::{get_span_of_entire_for_loop, IncrementVisitor, InitializeVisitor, MANUAL_MEMCPY};
use super::{IncrementVisitor, InitializeVisitor, MANUAL_MEMCPY};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
use clippy_utils::sugg::Sugg;
@ -86,7 +86,7 @@ pub(super) fn check<'tcx>(
span_lint_and_sugg(
cx,
MANUAL_MEMCPY,
get_span_of_entire_for_loop(expr),
expr.span,
"it looks like you're manually copying between slices",
"try replacing the loop by",
big_sugg,

View File

@ -23,7 +23,7 @@ use rustc_hir::{Expr, ExprKind, LoopSource, Pat};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
use utils::{get_span_of_entire_for_loop, make_iterator_snippet, IncrementVisitor, InitializeVisitor};
use utils::{make_iterator_snippet, IncrementVisitor, InitializeVisitor};
declare_clippy_lint! {
/// ### What it does
@ -566,7 +566,15 @@ declare_lint_pass!(Loops => [
impl<'tcx> LateLintPass<'tcx> for Loops {
#[allow(clippy::too_many_lines)]
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let Some(higher::ForLoop { pat, arg, body, span }) = higher::ForLoop::hir(expr) {
let for_loop = higher::ForLoop::hir(expr);
if let Some(higher::ForLoop {
pat,
arg,
body,
loop_id,
span,
}) = for_loop
{
// we don't want to check expanded macros
// this check is not at the top of the function
// since higher::for_loop expressions are marked as expansions
@ -574,6 +582,9 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
return;
}
check_for_loop(cx, pat, arg, body, expr, span);
if let ExprKind::Block(block, _) = body.kind {
never_loop::check(cx, block, loop_id, span, for_loop.as_ref());
}
}
// we don't want to check expanded macros
@ -582,7 +593,9 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
}
// check for never_loop
never_loop::check(cx, expr);
if let ExprKind::Loop(block, ..) = expr.kind {
never_loop::check(cx, block, expr.hir_id, expr.span, None);
}
// check for `loop { if let {} else break }` that could be `while let`
// (also matches an explicit "match" instead of "if let")

View File

@ -4,35 +4,41 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::higher::ForLoop;
use clippy_utils::source::snippet;
use rustc_errors::Applicability;
use rustc_hir::{Block, Expr, ExprKind, HirId, InlineAsmOperand, LoopSource, Node, Pat, Stmt, StmtKind};
use rustc_hir::{Block, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind};
use rustc_lint::LateContext;
use rustc_span::Span;
use std::iter::{once, Iterator};
pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::Loop(block, _, source, _) = expr.kind {
match never_loop_block(block, expr.hir_id) {
NeverLoopResult::AlwaysBreak => {
span_lint_and_then(cx, NEVER_LOOP, expr.span, "this loop never actually loops", |diag| {
if_chain! {
if source == LoopSource::ForLoop;
if let Some((_, Node::Expr(parent_match))) = cx.tcx.hir().parent_iter(expr.hir_id).nth(1);
if let Some(ForLoop { arg: iterator, pat, span: for_span, .. }) = ForLoop::hir(parent_match);
then {
// Suggests using an `if let` instead. This is `Unspecified` because the
// loop may (probably) contain `break` statements which would be invalid
// in an `if let`.
diag.span_suggestion_verbose(
for_span.with_hi(iterator.span.hi()),
"if you need the first element of the iterator, try writing",
for_to_if_let_sugg(cx, iterator, pat),
Applicability::Unspecified,
);
}
};
});
},
NeverLoopResult::MayContinueMainLoop | NeverLoopResult::Otherwise => (),
}
pub(super) fn check(
cx: &LateContext<'tcx>,
block: &'tcx Block<'_>,
loop_id: HirId,
span: Span,
for_loop: Option<&ForLoop<'_>>,
) {
match never_loop_block(block, loop_id) {
NeverLoopResult::AlwaysBreak => {
span_lint_and_then(cx, NEVER_LOOP, span, "this loop never actually loops", |diag| {
if let Some(ForLoop {
arg: iterator,
pat,
span: for_span,
..
}) = for_loop
{
// Suggests using an `if let` instead. This is `Unspecified` because the
// loop may (probably) contain `break` statements which would be invalid
// in an `if let`.
diag.span_suggestion_verbose(
for_span.with_hi(iterator.span.hi()),
"if you need the first element of the iterator, try writing",
for_to_if_let_sugg(cx, iterator, pat),
Applicability::Unspecified,
);
}
});
},
NeverLoopResult::MayContinueMainLoop | NeverLoopResult::Otherwise => (),
}
}

View File

@ -1,4 +1,4 @@
use super::{get_span_of_entire_for_loop, SINGLE_ELEMENT_LOOP};
use super::SINGLE_ELEMENT_LOOP;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::single_segment_path;
use clippy_utils::source::{indent_of, snippet};
@ -30,7 +30,6 @@ pub(super) fn check<'tcx>(
if !block.stmts.is_empty();
then {
let for_span = get_span_of_entire_for_loop(expr);
let mut block_str = snippet(cx, block.span, "..").into_owned();
block_str.remove(0);
block_str.pop();
@ -39,7 +38,7 @@ pub(super) fn check<'tcx>(
span_lint_and_sugg(
cx,
SINGLE_ELEMENT_LOOP,
for_span,
expr.span,
"for loop over a single element",
"try",
format!("{{\n{}let {} = &{};{}}}", " ".repeat(indent_of(cx, block.stmts[0].span).unwrap_or(0)), target.name, list_item_name, block_str),

View File

@ -7,7 +7,6 @@ use rustc_hir::HirIdMap;
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Stmt, StmtKind};
use rustc_lint::LateContext;
use rustc_middle::hir::map::Map;
use rustc_span::source_map::Span;
use rustc_span::symbol::{sym, Symbol};
use std::iter::Iterator;
@ -300,17 +299,6 @@ impl<'tcx> Visitor<'tcx> for LoopNestVisitor {
}
}
// this function assumes the given expression is a `for` loop.
pub(super) fn get_span_of_entire_for_loop(expr: &Expr<'_>) -> Span {
// for some reason this is the only way to get the `Span`
// of the entire `for` loop
if let ExprKind::Match(_, arms, _) = &expr.kind {
arms[0].body.span
} else {
unreachable!()
}
}
/// If `arg` was the argument to a `for` loop, return the "cleanest" way of writing the
/// actual `Iterator` that the loop uses.
pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic_ref: &mut Applicability) -> String {

View File

@ -20,8 +20,8 @@ use rustc_span::symbol::sym;
use clippy_utils::consts::{constant, Constant};
use clippy_utils::sugg::Sugg;
use clippy_utils::{
expr_path_res, get_item_name, get_parent_expr, higher, in_constant, is_diag_trait_item, is_integer_const,
iter_input_pats, last_path_segment, match_any_def_paths, paths, unsext, SpanlessEq,
expr_path_res, get_item_name, get_parent_expr, in_constant, is_diag_trait_item, is_integer_const, iter_input_pats,
last_path_segment, match_any_def_paths, paths, unsext, SpanlessEq,
};
declare_clippy_lint! {
@ -312,7 +312,6 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
if let StmtKind::Local(local) = stmt.kind;
if let PatKind::Binding(an, .., name, None) = local.pat.kind;
if let Some(init) = local.init;
if !higher::is_from_for_desugar(local);
if an == BindingAnnotation::Ref || an == BindingAnnotation::RefMut;
then {
// use the macro callsite when the init span (but not the whole local span)

View File

@ -1,16 +1,12 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::last_path_segment;
use rustc_hir::{
intravisit, Body, Expr, ExprKind, FnDecl, HirId, LocalSource, MatchSource, Mutability, Pat, PatField, PatKind,
QPath, Stmt, StmtKind,
intravisit, Body, Expr, ExprKind, FnDecl, HirId, LocalSource, Mutability, Pat, PatKind, Stmt, StmtKind,
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{AdtDef, FieldDef, Ty, TyKind, VariantDef};
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
use std::iter;
declare_clippy_lint! {
/// ### What it does
@ -87,43 +83,28 @@ declare_lint_pass!(PatternTypeMismatch => [PATTERN_TYPE_MISMATCH]);
impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch {
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
if let StmtKind::Local(local) = stmt.kind {
if let Some(init) = &local.init {
if let Some(init_ty) = cx.typeck_results().node_type_opt(init.hir_id) {
let pat = &local.pat;
if in_external_macro(cx.sess(), pat.span) {
return;
}
let deref_possible = match local.source {
LocalSource::Normal => DerefPossible::Possible,
_ => DerefPossible::Impossible,
};
apply_lint(cx, pat, init_ty, deref_possible);
}
if in_external_macro(cx.sess(), local.pat.span) {
return;
}
let deref_possible = match local.source {
LocalSource::Normal => DerefPossible::Possible,
_ => DerefPossible::Impossible,
};
apply_lint(cx, local.pat, deref_possible);
}
}
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::Match(scrutinee, arms, MatchSource::Normal) = expr.kind {
if let Some(expr_ty) = cx.typeck_results().node_type_opt(scrutinee.hir_id) {
'pattern_checks: for arm in arms {
let pat = &arm.pat;
if in_external_macro(cx.sess(), pat.span) {
continue 'pattern_checks;
}
if apply_lint(cx, pat, expr_ty, DerefPossible::Possible) {
break 'pattern_checks;
}
if let ExprKind::Match(_, arms, _) = expr.kind {
for arm in arms {
let pat = &arm.pat;
if apply_lint(cx, pat, DerefPossible::Possible) {
break;
}
}
}
if let ExprKind::Let(let_pat, let_expr, _) = expr.kind {
if let Some(expr_ty) = cx.typeck_results().node_type_opt(let_expr.hir_id) {
if in_external_macro(cx.sess(), let_pat.span) {
return;
}
apply_lint(cx, let_pat, expr_ty, DerefPossible::Possible);
}
if let ExprKind::Let(let_pat, ..) = expr.kind {
apply_lint(cx, let_pat, DerefPossible::Possible);
}
}
@ -134,12 +115,10 @@ impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch {
_: &'tcx FnDecl<'_>,
body: &'tcx Body<'_>,
_: Span,
hir_id: HirId,
_: HirId,
) {
if let Some(fn_sig) = cx.typeck_results().liberated_fn_sigs().get(hir_id) {
for (param, ty) in iter::zip(body.params, fn_sig.inputs()) {
apply_lint(cx, param.pat, ty, DerefPossible::Impossible);
}
for param in body.params {
apply_lint(cx, param.pat, DerefPossible::Impossible);
}
}
}
@ -150,8 +129,8 @@ enum DerefPossible {
Impossible,
}
fn apply_lint<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, expr_ty: Ty<'tcx>, deref_possible: DerefPossible) -> bool {
let maybe_mismatch = find_first_mismatch(cx, pat, expr_ty, Level::Top);
fn apply_lint<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, deref_possible: DerefPossible) -> bool {
let maybe_mismatch = find_first_mismatch(cx, pat);
if let Some((span, mutability, level)) = maybe_mismatch {
span_lint_and_help(
cx,
@ -184,132 +163,32 @@ enum Level {
}
#[allow(rustc::usage_of_ty_tykind)]
fn find_first_mismatch<'tcx>(
cx: &LateContext<'tcx>,
pat: &Pat<'_>,
ty: Ty<'tcx>,
level: Level,
) -> Option<(Span, Mutability, Level)> {
if let PatKind::Ref(sub_pat, _) = pat.kind {
if let TyKind::Ref(_, sub_ty, _) = ty.kind() {
return find_first_mismatch(cx, sub_pat, sub_ty, Level::Lower);
fn find_first_mismatch<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>) -> Option<(Span, Mutability, Level)> {
let mut result = None;
pat.walk(|p| {
if result.is_some() {
return false;
}
}
if let TyKind::Ref(_, _, mutability) = *ty.kind() {
if is_non_ref_pattern(&pat.kind) {
return Some((pat.span, mutability, level));
if in_external_macro(cx.sess(), p.span) {
return true;
}
}
if let PatKind::Struct(ref qpath, field_pats, _) = pat.kind {
if let TyKind::Adt(adt_def, substs_ref) = ty.kind() {
if let Some(variant) = get_variant(adt_def, qpath) {
let field_defs = &variant.fields;
return find_first_mismatch_in_struct(cx, field_pats, field_defs, substs_ref);
}
}
}
if let PatKind::TupleStruct(ref qpath, pats, _) = pat.kind {
if let TyKind::Adt(adt_def, substs_ref) = ty.kind() {
if let Some(variant) = get_variant(adt_def, qpath) {
let field_defs = &variant.fields;
let ty_iter = field_defs.iter().map(|field_def| field_def.ty(cx.tcx, substs_ref));
return find_first_mismatch_in_tuple(cx, pats, ty_iter);
}
}
}
if let PatKind::Tuple(pats, _) = pat.kind {
if let TyKind::Tuple(..) = ty.kind() {
return find_first_mismatch_in_tuple(cx, pats, ty.tuple_fields());
}
}
if let PatKind::Or(sub_pats) = pat.kind {
for pat in sub_pats {
let maybe_mismatch = find_first_mismatch(cx, pat, ty, level);
if let Some(mismatch) = maybe_mismatch {
return Some(mismatch);
}
}
}
None
}
fn get_variant<'a>(adt_def: &'a AdtDef, qpath: &QPath<'_>) -> Option<&'a VariantDef> {
if adt_def.is_struct() {
if let Some(variant) = adt_def.variants.iter().next() {
return Some(variant);
}
}
if adt_def.is_enum() {
let pat_ident = last_path_segment(qpath).ident;
for variant in &adt_def.variants {
if variant.ident == pat_ident {
return Some(variant);
}
}
}
None
}
fn find_first_mismatch_in_tuple<'tcx, I>(
cx: &LateContext<'tcx>,
pats: &[Pat<'_>],
ty_iter_src: I,
) -> Option<(Span, Mutability, Level)>
where
I: IntoIterator<Item = Ty<'tcx>>,
{
let mut field_tys = ty_iter_src.into_iter();
'fields: for pat in pats {
let field_ty = if let Some(ty) = field_tys.next() {
ty
} else {
break 'fields;
let adjust_pat = match p.kind {
PatKind::Or([p, ..]) => p,
_ => p,
};
let maybe_mismatch = find_first_mismatch(cx, pat, field_ty, Level::Lower);
if let Some(mismatch) = maybe_mismatch {
return Some(mismatch);
}
}
None
}
fn find_first_mismatch_in_struct<'tcx>(
cx: &LateContext<'tcx>,
field_pats: &[PatField<'_>],
field_defs: &[FieldDef],
substs_ref: SubstsRef<'tcx>,
) -> Option<(Span, Mutability, Level)> {
for field_pat in field_pats {
'definitions: for field_def in field_defs {
if field_pat.ident == field_def.ident {
let field_ty = field_def.ty(cx.tcx, substs_ref);
let pat = &field_pat.pat;
let maybe_mismatch = find_first_mismatch(cx, pat, field_ty, Level::Lower);
if let Some(mismatch) = maybe_mismatch {
return Some(mismatch);
if let Some(adjustments) = cx.typeck_results().pat_adjustments().get(adjust_pat.hir_id) {
if let [first, ..] = **adjustments {
if let ty::Ref(.., mutability) = *first.kind() {
let level = if p.hir_id == pat.hir_id {
Level::Top
} else {
Level::Lower
};
result = Some((p.span, mutability, level));
}
break 'definitions;
}
}
}
None
}
fn is_non_ref_pattern(pat_kind: &PatKind<'_>) -> bool {
match pat_kind {
PatKind::Struct(..) | PatKind::Tuple(..) | PatKind::TupleStruct(..) | PatKind::Path(..) => true,
PatKind::Or(sub_pats) => sub_pats.iter().any(|pat| is_non_ref_pattern(&pat.kind)),
_ => false,
}
result.is_none()
});
result
}

View File

@ -1,5 +1,4 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::higher;
use clippy_utils::source::snippet_with_macro_callsite;
use rustc_errors::Applicability;
use rustc_hir::{Stmt, StmtKind};
@ -14,9 +13,6 @@ pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
if in_external_macro(cx.sess(), stmt.span) || local.pat.span.from_expansion() {
return;
}
if higher::is_from_for_desugar(local) {
return;
}
span_lint_and_then(
cx,
LET_UNIT_VALUE,

View File

@ -22,31 +22,31 @@ pub struct ForLoop<'tcx> {
pub arg: &'tcx hir::Expr<'tcx>,
/// `for` loop body
pub body: &'tcx hir::Expr<'tcx>,
/// Compare this against `hir::Destination.target`
pub loop_id: HirId,
/// entire `for` loop span
pub span: Span,
}
impl<'tcx> ForLoop<'tcx> {
#[inline]
/// Parses a desugared `for` loop
pub fn hir(expr: &Expr<'tcx>) -> Option<Self> {
if_chain! {
if let hir::ExprKind::Match(iterexpr, arms, hir::MatchSource::ForLoopDesugar) = expr.kind;
if let Some(first_arm) = arms.get(0);
if let hir::ExprKind::Call(_, iterargs) = iterexpr.kind;
if let Some(first_arg) = iterargs.get(0);
if iterargs.len() == 1 && arms.len() == 1 && first_arm.guard.is_none();
if let hir::ExprKind::Loop(block, ..) = first_arm.body.kind;
if block.expr.is_none();
if let [ _, _, ref let_stmt, ref body ] = *block.stmts;
if let hir::StmtKind::Local(local) = let_stmt.kind;
if let hir::StmtKind::Expr(body_expr) = body.kind;
if let hir::ExprKind::DropTemps(e) = expr.kind;
if let hir::ExprKind::Match(iterexpr, [arm], hir::MatchSource::ForLoopDesugar) = e.kind;
if let hir::ExprKind::Call(_, [arg]) = iterexpr.kind;
if let hir::ExprKind::Loop(block, ..) = arm.body.kind;
if let [stmt] = &*block.stmts;
if let hir::StmtKind::Expr(e) = stmt.kind;
if let hir::ExprKind::Match(_, [_, some_arm], _) = e.kind;
if let hir::PatKind::Struct(_, [field], _) = some_arm.pat.kind;
then {
return Some(Self {
pat: &*local.pat,
arg: first_arg,
body: body_expr,
span: first_arm.span
pat: field.pat,
arg,
body: some_arm.body,
loop_id: arm.body.hir_id,
span: expr.span.ctxt().outer_expn_data().call_site,
});
}
}
@ -678,38 +678,6 @@ impl<'tcx> FormatArgsArg<'tcx> {
}
}
/// Checks if a `let` statement is from a `for` loop desugaring.
pub fn is_from_for_desugar(local: &hir::Local<'_>) -> bool {
// This will detect plain for-loops without an actual variable binding:
//
// ```
// for x in some_vec {
// // do stuff
// }
// ```
if_chain! {
if let Some(expr) = local.init;
if let hir::ExprKind::Match(_, _, hir::MatchSource::ForLoopDesugar) = expr.kind;
then {
return true;
}
}
// This detects a variable binding in for loop to avoid `let_unit_value`
// lint (see issue #1964).
//
// ```
// for _ in vec![()] {
// // anything
// }
// ```
if let hir::LocalSource::ForLoopDesugar = local.source {
return true;
}
false
}
/// A parsed `panic!` expansion
pub struct PanicExpn<'tcx> {
/// Span of `panic!(..)`

View File

@ -11,11 +11,8 @@ if_chain! {
// unimplemented: field checks
if arms.len() == 1;
if let ExprKind::Loop(ref body, ref label, LoopSource::ForLoop) = arms[0].body.kind;
if body.stmts.len() == 4;
if let StmtKind::Local(ref local) = body.stmts[0].kind;
if let PatKind::Binding(BindingAnnotation::Mutable, _, name, None) = local.pat.kind;
if name.as_str() == "__next";
if let StmtKind::Expr(ref e, _) = body.stmts[1].kind
if body.stmts.len() == 1;
if let StmtKind::Expr(ref e, _) = body.stmts[0].kind
if let ExprKind::Match(ref expr2, ref arms1, MatchSource::ForLoopDesugar) = e.kind;
if let ExprKind::Call(ref func1, ref args1) = expr2.kind;
if let ExprKind::Path(ref path2) = func1.kind;
@ -25,39 +22,27 @@ if_chain! {
if let ExprKind::Path(ref path3) = inner.kind;
if match_qpath(path3, &["iter"]);
if arms1.len() == 2;
if let ExprKind::Assign(ref target, ref value, ref _span) = arms1[0].body.kind;
if let ExprKind::Path(ref path4) = target.kind;
if match_qpath(path4, &["__next"]);
if let ExprKind::Path(ref path5) = value.kind;
if match_qpath(path5, &["val"]);
if let PatKind::Struct(ref path6, ref fields1, false) = arms1[0].pat.kind;
if matches!(path6, QPath::LangItem(LangItem::OptionSome, _));
if fields1.len() == 1;
if let ExprKind::Break(ref destination, None) = arms1[0].body.kind;
if let PatKind::Struct(ref path4, ref fields1, false) = arms1[0].pat.kind;
if matches!(path4, QPath::LangItem(LangItem::OptionNone, _));
if fields1.len() == 0;
// unimplemented: field checks
if let ExprKind::Break(ref destination, None) = arms1[1].body.kind;
if let PatKind::Struct(ref path7, ref fields2, false) = arms1[1].pat.kind;
if matches!(path7, QPath::LangItem(LangItem::OptionNone, _));
if fields2.len() == 0;
// unimplemented: field checks
if let StmtKind::Local(ref local1) = body.stmts[2].kind;
if let Some(ref init) = local1.init;
if let ExprKind::Path(ref path8) = init.kind;
if match_qpath(path8, &["__next"]);
if let PatKind::Binding(BindingAnnotation::Unannotated, _, name1, None) = local1.pat.kind;
if name1.as_str() == "y";
if let StmtKind::Expr(ref e1, _) = body.stmts[3].kind
if let ExprKind::Block(ref block) = e1.kind;
if let ExprKind::Block(ref block) = arms1[1].body.kind;
if block.stmts.len() == 1;
if let StmtKind::Local(ref local2) = block.stmts[0].kind;
if let Some(ref init1) = local2.init;
if let ExprKind::Path(ref path9) = init1.kind;
if match_qpath(path9, &["y"]);
if let PatKind::Binding(BindingAnnotation::Unannotated, _, name2, None) = local2.pat.kind;
if name2.as_str() == "z";
if let StmtKind::Local(ref local) = block.stmts[0].kind;
if let Some(ref init) = local.init;
if let ExprKind::Path(ref path5) = init.kind;
if match_qpath(path5, &["y"]);
if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local.pat.kind;
if name.as_str() == "z";
if block.expr.is_none();
if let PatKind::Struct(ref path6, ref fields2, false) = arms1[1].pat.kind;
if matches!(path6, QPath::LangItem(LangItem::OptionSome, _));
if fields2.len() == 1;
// unimplemented: field checks
if body.expr.is_none();
if let PatKind::Binding(BindingAnnotation::Mutable, _, name3, None) = arms[0].pat.kind;
if name3.as_str() == "iter";
if let PatKind::Binding(BindingAnnotation::Mutable, _, name1, None) = arms[0].pat.kind;
if name1.as_str() == "iter";
then {
// report your lint here
}