diff options
| -rw-r--r-- | src/libcore/iter/traits/collect.rs | 1 | ||||
| -rw-r--r-- | src/librustc_lint/array_into_iter.rs | 91 | ||||
| -rw-r--r-- | src/librustc_lint/lib.rs | 4 |
3 files changed, 96 insertions, 0 deletions
diff --git a/src/libcore/iter/traits/collect.rs b/src/libcore/iter/traits/collect.rs index 00a86417058..bbdb169cac0 100644 --- a/src/libcore/iter/traits/collect.rs +++ b/src/libcore/iter/traits/collect.rs @@ -205,6 +205,7 @@ pub trait FromIterator<A>: Sized { /// .collect() /// } /// ``` +#[rustc_diagnostic_item = "IntoIterator"] #[stable(feature = "rust1", since = "1.0.0")] pub trait IntoIterator { /// The type of the elements being iterated over. diff --git a/src/librustc_lint/array_into_iter.rs b/src/librustc_lint/array_into_iter.rs new file mode 100644 index 00000000000..e73414174fb --- /dev/null +++ b/src/librustc_lint/array_into_iter.rs @@ -0,0 +1,91 @@ +use crate::lint::{LateContext, LateLintPass, LintArray, LintContext, LintPass}; +use rustc::{ + lint::FutureIncompatibleInfo, + hir, + ty::{ + self, + adjustment::{Adjust, Adjustment}, + }, +}; +use syntax::{ + errors::Applicability, + symbol::sym, +}; + + +declare_lint! { + pub ARRAY_INTO_ITER, + Warn, + "detects calling `into_iter` on arrays", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #66145 <https://github.com/rust-lang/rust/issues/66145>", + edition: None, + }; +} + +declare_lint_pass!( + /// Checks for instances of calling `into_iter` on arrays. + ArrayIntoIter => [ARRAY_INTO_ITER] +); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ArrayIntoIter { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) { + // We only care about method call expressions. + if let hir::ExprKind::MethodCall(call, span, args) = &expr.kind { + if call.ident.name != sym::into_iter { + return; + } + + // Check if the method call actually calls the libcore + // `IntoIterator::into_iter`. + let def_id = cx.tables.type_dependent_def_id(expr.hir_id).unwrap(); + match cx.tcx.trait_of_item(def_id) { + Some(trait_id) if cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_id) => {}, + _ => return, + }; + + // As this is a method call expression, we have at least one + // argument. + let receiver_arg = &args[0]; + + // Test if the original `self` type is an array type. + match cx.tables.expr_ty(receiver_arg).kind { + ty::Array(..) => {} + _ => return, + } + + // Make sure that the first adjustment is an autoref coercion. + match cx.tables.expr_adjustments(receiver_arg).get(0) { + Some(Adjustment { kind: Adjust::Borrow(_), .. }) => {} + _ => return, + } + + // Emit lint diagnostic. + let target = match cx.tables.expr_ty_adjusted(receiver_arg).kind { + ty::Ref(_, ty::TyS { kind: ty::Array(..), ..}, _) => "[T; N]", + ty::Ref(_, ty::TyS { kind: ty::Slice(..), ..}, _) => "[T]", + + // We know the original first argument type is an array type, + // we know that the first adjustment was an autoref coercion + // and we know that `IntoIterator` is the trait involved. The + // array cannot be coerced to something other than a reference + // to an array or to a slice. + _ => bug!("array type coerced to something other than array or slice"), + }; + let msg = format!( + "this method call currently resolves to `<&{} as IntoIterator>::into_iter` (due \ + to autoref coercions), but that might change in the future when \ + `IntoIterator` impls for arrays are added.", + target, + ); + cx.struct_span_lint(ARRAY_INTO_ITER, *span, &msg) + .span_suggestion( + call.ident.span, + "use `.iter()` instead of `.into_iter()` to avoid ambiguity", + "iter".into(), + Applicability::MachineApplicable, + ) + .emit(); + } + } +} diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index b1beef04c59..473f8d4d3eb 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -21,6 +21,7 @@ #[macro_use] extern crate rustc; +mod array_into_iter; mod error_codes; mod nonstandard_style; mod redundant_semicolon; @@ -56,6 +57,7 @@ use types::*; use unused::*; use non_ascii_idents::*; use rustc::lint::internal::*; +use array_into_iter::ArrayIntoIter; /// Useful for other parts of the compiler. pub use builtin::SoftLints; @@ -130,6 +132,8 @@ macro_rules! late_lint_passes { // FIXME: Turn the computation of types which implement Debug into a query // and change this to a module lint pass MissingDebugImplementations: MissingDebugImplementations::default(), + + ArrayIntoIter: ArrayIntoIter, ]); ) } |
