2021-10-27 14:48:06 +00:00
|
|
|
use super::{make_iterator_snippet, IncrementVisitor, InitializeVisitor, EXPLICIT_COUNTER_LOOP};
|
2021-12-06 11:33:31 +00:00
|
|
|
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
2021-03-25 18:29:11 +00:00
|
|
|
use clippy_utils::source::snippet_with_applicability;
|
|
|
|
use clippy_utils::{get_enclosing_block, is_integer_const};
|
2021-03-12 14:30:50 +00:00
|
|
|
use if_chain::if_chain;
|
|
|
|
use rustc_errors::Applicability;
|
|
|
|
use rustc_hir::intravisit::{walk_block, walk_expr};
|
|
|
|
use rustc_hir::{Expr, Pat};
|
|
|
|
use rustc_lint::LateContext;
|
Overhaul `TyS` and `Ty`.
Specifically, change `Ty` from this:
```
pub type Ty<'tcx> = &'tcx TyS<'tcx>;
```
to this
```
pub struct Ty<'tcx>(Interned<'tcx, TyS<'tcx>>);
```
There are two benefits to this.
- It's now a first class type, so we can define methods on it. This
means we can move a lot of methods away from `TyS`, leaving `TyS` as a
barely-used type, which is appropriate given that it's not meant to
be used directly.
- The uniqueness requirement is now explicit, via the `Interned` type.
E.g. the pointer-based `Eq` and `Hash` comes from `Interned`, rather
than via `TyS`, which wasn't obvious at all.
Much of this commit is boring churn. The interesting changes are in
these files:
- compiler/rustc_middle/src/arena.rs
- compiler/rustc_middle/src/mir/visit.rs
- compiler/rustc_middle/src/ty/context.rs
- compiler/rustc_middle/src/ty/mod.rs
Specifically:
- Most mentions of `TyS` are removed. It's very much a dumb struct now;
`Ty` has all the smarts.
- `TyS` now has `crate` visibility instead of `pub`.
- `TyS::make_for_test` is removed in favour of the static `BOOL_TY`,
which just works better with the new structure.
- The `Eq`/`Ord`/`Hash` impls are removed from `TyS`. `Interned`s impls
of `Eq`/`Hash` now suffice. `Ord` is now partly on `Interned`
(pointer-based, for the `Equal` case) and partly on `TyS`
(contents-based, for the other cases).
- There are many tedious sigil adjustments, i.e. adding or removing `*`
or `&`. They seem to be unavoidable.
2022-01-25 03:13:38 +00:00
|
|
|
use rustc_middle::ty::{self, Ty, UintTy};
|
2021-03-12 14:30:50 +00:00
|
|
|
|
|
|
|
// To trigger the EXPLICIT_COUNTER_LOOP lint, a variable must be
|
|
|
|
// incremented exactly once in the loop body, and initialized to zero
|
|
|
|
// at the start of the loop.
|
|
|
|
pub(super) fn check<'tcx>(
|
|
|
|
cx: &LateContext<'tcx>,
|
|
|
|
pat: &'tcx Pat<'_>,
|
|
|
|
arg: &'tcx Expr<'_>,
|
|
|
|
body: &'tcx Expr<'_>,
|
|
|
|
expr: &'tcx Expr<'_>,
|
|
|
|
) {
|
|
|
|
// Look for variables that are incremented once per loop iteration.
|
|
|
|
let mut increment_visitor = IncrementVisitor::new(cx);
|
|
|
|
walk_expr(&mut increment_visitor, body);
|
|
|
|
|
|
|
|
// For each candidate, check the parent block to see if
|
|
|
|
// it's initialized to zero at the start of the loop.
|
2021-04-08 15:50:13 +00:00
|
|
|
if let Some(block) = get_enclosing_block(cx, expr.hir_id) {
|
2021-03-12 14:30:50 +00:00
|
|
|
for id in increment_visitor.into_results() {
|
|
|
|
let mut initialize_visitor = InitializeVisitor::new(cx, expr, id);
|
|
|
|
walk_block(&mut initialize_visitor, block);
|
|
|
|
|
|
|
|
if_chain! {
|
2021-12-06 11:33:31 +00:00
|
|
|
if let Some((name, ty, initializer)) = initialize_visitor.get_result();
|
2021-03-12 14:30:50 +00:00
|
|
|
if is_integer_const(cx, initializer, 0);
|
|
|
|
then {
|
|
|
|
let mut applicability = Applicability::MachineApplicable;
|
|
|
|
|
Overhaul `TyS` and `Ty`.
Specifically, change `Ty` from this:
```
pub type Ty<'tcx> = &'tcx TyS<'tcx>;
```
to this
```
pub struct Ty<'tcx>(Interned<'tcx, TyS<'tcx>>);
```
There are two benefits to this.
- It's now a first class type, so we can define methods on it. This
means we can move a lot of methods away from `TyS`, leaving `TyS` as a
barely-used type, which is appropriate given that it's not meant to
be used directly.
- The uniqueness requirement is now explicit, via the `Interned` type.
E.g. the pointer-based `Eq` and `Hash` comes from `Interned`, rather
than via `TyS`, which wasn't obvious at all.
Much of this commit is boring churn. The interesting changes are in
these files:
- compiler/rustc_middle/src/arena.rs
- compiler/rustc_middle/src/mir/visit.rs
- compiler/rustc_middle/src/ty/context.rs
- compiler/rustc_middle/src/ty/mod.rs
Specifically:
- Most mentions of `TyS` are removed. It's very much a dumb struct now;
`Ty` has all the smarts.
- `TyS` now has `crate` visibility instead of `pub`.
- `TyS::make_for_test` is removed in favour of the static `BOOL_TY`,
which just works better with the new structure.
- The `Eq`/`Ord`/`Hash` impls are removed from `TyS`. `Interned`s impls
of `Eq`/`Hash` now suffice. `Ord` is now partly on `Interned`
(pointer-based, for the `Equal` case) and partly on `TyS`
(contents-based, for the other cases).
- There are many tedious sigil adjustments, i.e. adding or removing `*`
or `&`. They seem to be unavoidable.
2022-01-25 03:13:38 +00:00
|
|
|
let int_name = match ty.map(Ty::kind) {
|
2021-12-06 11:33:31 +00:00
|
|
|
// usize or inferred
|
|
|
|
Some(ty::Uint(UintTy::Usize)) | None => {
|
|
|
|
span_lint_and_sugg(
|
|
|
|
cx,
|
|
|
|
EXPLICIT_COUNTER_LOOP,
|
|
|
|
expr.span.with_hi(arg.span.hi()),
|
|
|
|
&format!("the variable `{}` is used as a loop counter", name),
|
|
|
|
"consider using",
|
|
|
|
format!(
|
|
|
|
"for ({}, {}) in {}.enumerate()",
|
|
|
|
name,
|
|
|
|
snippet_with_applicability(cx, pat.span, "item", &mut applicability),
|
|
|
|
make_iterator_snippet(cx, arg, &mut applicability),
|
|
|
|
),
|
|
|
|
applicability,
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Some(ty::Int(int_ty)) => int_ty.name_str(),
|
|
|
|
Some(ty::Uint(uint_ty)) => uint_ty.name_str(),
|
|
|
|
_ => return,
|
|
|
|
};
|
|
|
|
|
|
|
|
span_lint_and_then(
|
2021-03-12 14:30:50 +00:00
|
|
|
cx,
|
|
|
|
EXPLICIT_COUNTER_LOOP,
|
2021-10-27 14:48:06 +00:00
|
|
|
expr.span.with_hi(arg.span.hi()),
|
2021-03-12 14:30:50 +00:00
|
|
|
&format!("the variable `{}` is used as a loop counter", name),
|
2021-12-06 11:33:31 +00:00
|
|
|
|diag| {
|
|
|
|
diag.span_suggestion(
|
|
|
|
expr.span.with_hi(arg.span.hi()),
|
|
|
|
"consider using",
|
|
|
|
format!(
|
|
|
|
"for ({}, {}) in (0_{}..).zip({})",
|
|
|
|
name,
|
|
|
|
snippet_with_applicability(cx, pat.span, "item", &mut applicability),
|
|
|
|
int_name,
|
|
|
|
make_iterator_snippet(cx, arg, &mut applicability),
|
|
|
|
),
|
|
|
|
applicability,
|
|
|
|
);
|
|
|
|
|
|
|
|
diag.note(&format!(
|
|
|
|
"`{}` is of type `{}`, making it ineligible for `Iterator::enumerate`",
|
|
|
|
name, int_name
|
|
|
|
));
|
|
|
|
},
|
2021-03-12 14:30:50 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|