Better suggestion for array_into_iter in for loop.

This commit is contained in:
Mara Bos 2021-05-25 19:15:27 +02:00
parent aec2c5b2b6
commit ef152d9b9f
2 changed files with 45 additions and 18 deletions

View file

@ -6,6 +6,7 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment};
use rustc_session::lint::FutureIncompatibilityReason; use rustc_session::lint::FutureIncompatibilityReason;
use rustc_span::edition::Edition; use rustc_span::edition::Edition;
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
use rustc_span::Span;
declare_lint! { declare_lint! {
/// The `array_into_iter` lint detects calling `into_iter` on arrays. /// The `array_into_iter` lint detects calling `into_iter` on arrays.
@ -36,13 +37,29 @@ declare_lint! {
}; };
} }
declare_lint_pass!( #[derive(Copy, Clone, Default)]
/// Checks for instances of calling `into_iter` on arrays. pub struct ArrayIntoIter {
ArrayIntoIter => [ARRAY_INTO_ITER] for_expr_span: Span,
); }
impl_lint_pass!(ArrayIntoIter => [ARRAY_INTO_ITER]);
impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter { impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
// Save the span of expressions in `for _ in expr` syntax,
// so we can give a better suggestion for those later.
if let hir::ExprKind::Match(arg, [_], hir::MatchSource::ForLoopDesugar) = &expr.kind {
if let hir::ExprKind::Call(path, [arg]) = &arg.kind {
if let hir::ExprKind::Path(hir::QPath::LangItem(
hir::LangItem::IntoIterIntoIter,
_,
)) = &path.kind
{
self.for_expr_span = arg.span;
}
}
}
// We only care about method call expressions. // We only care about method call expressions.
if let hir::ExprKind::MethodCall(call, span, args, _) = &expr.kind { if let hir::ExprKind::MethodCall(call, span, args, _) = &expr.kind {
if call.ident.name != sym::into_iter { if call.ident.name != sym::into_iter {
@ -98,27 +115,37 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter {
_ => bug!("array type coerced to something other than array or slice"), _ => bug!("array type coerced to something other than array or slice"),
}; };
cx.struct_span_lint(ARRAY_INTO_ITER, *span, |lint| { cx.struct_span_lint(ARRAY_INTO_ITER, *span, |lint| {
lint.build(&format!( let mut diag = lint.build(&format!(
"this method call resolves to `<&{} as IntoIterator>::into_iter` \ "this method call resolves to `<&{} as IntoIterator>::into_iter` \
(due to backwards compatibility), \ (due to backwards compatibility), \
but will resolve to <{} as IntoIterator>::into_iter in Rust 2021.", but will resolve to <{} as IntoIterator>::into_iter in Rust 2021.",
target, target, target, target,
)) ));
.span_suggestion( diag.span_suggestion(
call.ident.span, call.ident.span,
"use `.iter()` instead of `.into_iter()` to avoid ambiguity", "use `.iter()` instead of `.into_iter()` to avoid ambiguity",
"iter".into(), "iter".into(),
Applicability::MachineApplicable, Applicability::MachineApplicable,
) );
.multipart_suggestion( if self.for_expr_span == expr.span {
"or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value", let expr_span = expr.span.ctxt().outer_expn_data().call_site;
vec![ diag.span_suggestion(
(expr.span.shrink_to_lo(), "IntoIterator::into_iter(".into()), receiver_arg.span.shrink_to_hi().to(expr_span.shrink_to_hi()),
(receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()), ")".into()), "or remove `.into_iter()` to iterate by value",
], String::new(),
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
) );
.emit(); } else {
diag.multipart_suggestion(
"or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value",
vec![
(expr.span.shrink_to_lo(), "IntoIterator::into_iter(".into()),
(receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()), ")".into()),
],
Applicability::MaybeIncorrect,
);
}
diag.emit();
}) })
} }
} }

View file

@ -163,7 +163,7 @@ macro_rules! late_lint_passes {
// FIXME: Turn the computation of types which implement Debug into a query // FIXME: Turn the computation of types which implement Debug into a query
// and change this to a module lint pass // and change this to a module lint pass
MissingDebugImplementations: MissingDebugImplementations::default(), MissingDebugImplementations: MissingDebugImplementations::default(),
ArrayIntoIter: ArrayIntoIter, ArrayIntoIter: ArrayIntoIter::default(),
ClashingExternDeclarations: ClashingExternDeclarations::new(), ClashingExternDeclarations: ClashingExternDeclarations::new(),
DropTraitConstraints: DropTraitConstraints, DropTraitConstraints: DropTraitConstraints,
TemporaryCStringAsPtr: TemporaryCStringAsPtr, TemporaryCStringAsPtr: TemporaryCStringAsPtr,