New lint: unnecessary_semicolon (#14032)

This lint detects and removes the unnecessary semicolon after a `match`
or `if` statement returning `()`. It seems to be quite a common
"mistake", given the number of hits (88) we had in the Clippy sources
themselves.

The lint doesn't bother about loops, as `rustfmt` already removes the
extra semicolon. It doesn't handle blocks either, as an extra block
level, followed or not by a semicolon, is likely intentional.

I propose to put the lint in `pedantic`, as putting it in `style` seems
quite hazardous given the number of hits.

Note: there exists a `redundant-semicolon` lint in the compiler, but it
is an early lint and cannot check that the expression evaluates to `()`,
so it ignores the cases we're handling here.

----

changelog: [`unnecessary_semicolon`]: new lint
This commit is contained in:
llogiq 2025-01-20 17:39:37 +00:00 committed by GitHub
commit 8f1b4bb87a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
83 changed files with 236 additions and 88 deletions

View file

@ -62,5 +62,5 @@ pub(super) fn check<'tcx>(
),
applicability,
);
};
}
}

View file

@ -32,5 +32,5 @@ pub(super) fn check<'tcx>(
),
applicability,
);
};
}
}

View file

@ -46,5 +46,5 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E
format!("{receiver}.as_bytes().get({n}).copied()"),
applicability,
);
};
}
}

View file

@ -32,7 +32,7 @@ pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span,
// &T where T: Copy
ty::Ref(_, ty, _) if is_copy(cx, *ty) => {},
_ => return,
};
}
span_lint_and_sugg(
cx,
CLONED_INSTEAD_OF_COPIED,

View file

@ -37,7 +37,7 @@ pub(super) fn check(
"expect_err".to_string(),
Applicability::MachineApplicable,
);
};
}
}
/// Given a `Result<T, E>` type, return its data (`T`).

View file

@ -58,7 +58,7 @@ pub(super) fn check<'tcx>(
if ty.is_str() && can_be_static_str(cx, arg) {
return false;
}
};
}
true
}

View file

@ -25,5 +25,5 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span
"into_iter()".to_string(),
Applicability::MaybeIncorrect,
);
};
}
}

View file

@ -37,7 +37,7 @@ pub(super) fn check(
let _ = write!(sugg, r#".extension().is_some_and(|ext| ext == "{path}")"#);
} else {
let _ = write!(sugg, r#".extension().map_or(false, |ext| ext == "{path}")"#);
};
}
span_lint_and_sugg(
cx,

View file

@ -87,7 +87,7 @@ pub fn check_for_loop_iter(
// skip lint
return true;
}
};
}
// the lint should not be executed if no violation happens
let snippet = if let ExprKind::MethodCall(maybe_iter_method_name, collection, [], _) = receiver.kind