Avoid to suggest using label

This commit is contained in:
Yoshitomo Nakanishi 2021-02-11 12:50:20 +09:00
parent 5bb0f16552
commit 90cbbb2da3
3 changed files with 25 additions and 51 deletions

View file

@ -31,26 +31,20 @@ pub(super) fn lint(cx: &LateContext<'_>, expr: &'tcx Expr<'_>, args: &[&[Expr<'_
let body = cx.tcx.hir().body(body_id);
if let ExprKind::Block(..) = body.value.kind;
then {
let mut ret_span_collector = RetSpanCollector::new();
ret_span_collector.visit_expr(&body.value);
let mut ret_collector = RetCollector::new();
ret_collector.visit_expr(&body.value);
// Skip the lint if `return` is used in `Loop` to avoid a suggest using `'label`.
if ret_collector.ret_in_loop {
return;
}
let label = "'outer";
let loop_label = if ret_span_collector.need_label {
format!("{}: ", label)
} else {
"".to_string()
};
let sugg =
format!("{}for {} in {} {{ .. }}", loop_label, snippet(cx, body.params[0].pat.span, ""), snippet(cx, for_each_receiver.span, ""));
format!("for {} in {} {{ .. }}", snippet(cx, body.params[0].pat.span, ""), snippet(cx, for_each_receiver.span, ""));
let mut notes = vec![];
for (span, need_label) in ret_span_collector.spans {
let cont_label = if need_label {
format!(" {}", label)
} else {
"".to_string()
};
let note = format!("change `return` to `continue{}` in the loop body", cont_label);
for span in ret_collector.spans {
let note = format!("change `return` to `continue` in the loop body");
notes.push((span, note));
}
@ -100,34 +94,37 @@ fn is_target_ty(cx: &LateContext<'_>, expr_ty: Ty<'_>) -> bool {
false
}
/// Collect spans of `return` in the closure body.
struct RetSpanCollector {
spans: Vec<(Span, bool)>,
/// This type plays two roles.
/// 1. Collect spans of `return` in the closure body.
/// 2. Detect use of `return` in `Loop` in the closure body.
struct RetCollector {
spans: Vec<Span>,
ret_in_loop: bool,
loop_depth: u16,
need_label: bool,
}
impl RetSpanCollector {
impl RetCollector {
fn new() -> Self {
Self {
spans: Vec::new(),
ret_in_loop: false,
loop_depth: 0,
need_label: false,
}
}
}
impl<'tcx> Visitor<'tcx> for RetSpanCollector {
impl<'tcx> Visitor<'tcx> for RetCollector {
type Map = Map<'tcx>;
fn visit_expr(&mut self, expr: &Expr<'_>) {
match expr.kind {
ExprKind::Ret(..) => {
if self.loop_depth > 0 && !self.need_label {
self.need_label = true
if self.loop_depth > 0 && !self.ret_in_loop {
self.ret_in_loop = true
}
self.spans.push((expr.span, self.loop_depth > 0))
self.spans.push(expr.span)
},
ExprKind::Loop(..) => {

View file

@ -82,7 +82,7 @@ fn main() {
}
});
// Should trigger this lint with notes that say "change `return` to `continue 'outer`".
// Should NOT trigger this lint in case `return` is used in `Loop` of the closure.
vec.iter().for_each(|v| {
for i in 0..*v {
if i == 10 {

View file

@ -122,28 +122,5 @@ note: change `return` to `continue` in the loop body
LL | return;
| ^^^^^^
error: excessive use of `for_each`
--> $DIR/excessive_for_each.rs:86:5
|
LL | / vec.iter().for_each(|v| {
LL | | for i in 0..*v {
LL | | if i == 10 {
LL | | return;
... |
LL | | }
LL | | });
| |______^ help: try: `'outer: for v in vec.iter() { .. }`
|
note: change `return` to `continue 'outer` in the loop body
--> $DIR/excessive_for_each.rs:89:17
|
LL | return;
| ^^^^^^
note: change `return` to `continue` in the loop body
--> $DIR/excessive_for_each.rs:95:13
|
LL | return;
| ^^^^^^
error: aborting due to 15 previous errors
error: aborting due to 14 previous errors