mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 08:13:41 +00:00
Auto merge of #133047 - matthiaskrgr:rollup-9se1vth, r=matthiaskrgr
Rollup of 4 pull requests Successful merges: - #128197 (Skip locking span interner for some syntax context checks) - #133040 ([rustdoc] Fix handling of footnote reference in footnote definition) - #133043 (rustdoc-search: case-sensitive only when capitals are used) - #133046 (Clippy subtree update) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
e84902d35a
@ -559,12 +559,6 @@ impl Span {
|
||||
!self.is_dummy() && sm.is_span_accessible(self)
|
||||
}
|
||||
|
||||
/// Returns `true` if this span comes from any kind of macro, desugaring or inlining.
|
||||
#[inline]
|
||||
pub fn from_expansion(self) -> bool {
|
||||
!self.ctxt().is_root()
|
||||
}
|
||||
|
||||
/// Returns `true` if `span` originates in a derive-macro's expansion.
|
||||
pub fn in_derive_expansion(self) -> bool {
|
||||
matches!(self.ctxt().outer_expn_data().kind, ExpnKind::Macro(MacroKind::Derive, _))
|
||||
|
@ -303,6 +303,13 @@ impl Span {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if this span comes from any kind of macro, desugaring or inlining.
|
||||
#[inline]
|
||||
pub fn from_expansion(self) -> bool {
|
||||
// If the span is fully inferred then ctxt > MAX_CTXT
|
||||
self.inline_ctxt().map_or(true, |ctxt| !ctxt.is_root())
|
||||
}
|
||||
|
||||
/// Returns `true` if this is a dummy span with any hygienic context.
|
||||
#[inline]
|
||||
pub fn is_dummy(self) -> bool {
|
||||
@ -370,9 +377,10 @@ impl Span {
|
||||
pub fn eq_ctxt(self, other: Span) -> bool {
|
||||
match (self.inline_ctxt(), other.inline_ctxt()) {
|
||||
(Ok(ctxt1), Ok(ctxt2)) => ctxt1 == ctxt2,
|
||||
(Ok(ctxt), Err(index)) | (Err(index), Ok(ctxt)) => {
|
||||
with_span_interner(|interner| ctxt == interner.spans[index].ctxt)
|
||||
}
|
||||
// If `inline_ctxt` returns `Ok` the context is <= MAX_CTXT.
|
||||
// If it returns `Err` the span is fully interned and the context is > MAX_CTXT.
|
||||
// As these do not overlap an `Ok` and `Err` result cannot have an equal context.
|
||||
(Ok(_), Err(_)) | (Err(_), Ok(_)) => false,
|
||||
(Err(index1), Err(index2)) => with_span_interner(|interner| {
|
||||
interner.spans[index1].ctxt == interner.spans[index2].ctxt
|
||||
}),
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! Markdown footnote handling.
|
||||
use std::fmt::Write as _;
|
||||
|
||||
use pulldown_cmark::{Event, Tag, TagEnd, html};
|
||||
use pulldown_cmark::{CowStr, Event, Tag, TagEnd, html};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
|
||||
use super::SpannedEvent;
|
||||
@ -21,7 +21,7 @@ struct FootnoteDef<'a> {
|
||||
id: usize,
|
||||
}
|
||||
|
||||
impl<'a, 'b, I> Footnotes<'a, 'b, I> {
|
||||
impl<'a, 'b, I: Iterator<Item = SpannedEvent<'a>>> Footnotes<'a, 'b, I> {
|
||||
pub(super) fn new(iter: I, existing_footnotes: &'b mut usize) -> Self {
|
||||
Footnotes { inner: iter, footnotes: FxIndexMap::default(), existing_footnotes }
|
||||
}
|
||||
@ -34,15 +34,8 @@ impl<'a, 'b, I> Footnotes<'a, 'b, I> {
|
||||
// Don't allow changing the ID of existing entrys, but allow changing the contents.
|
||||
(content, *id)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, 'b, I> {
|
||||
type Item = SpannedEvent<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
match self.inner.next() {
|
||||
Some((Event::FootnoteReference(ref reference), range)) => {
|
||||
fn handle_footnote_reference(&mut self, reference: &CowStr<'a>) -> Event<'a> {
|
||||
// When we see a reference (to a footnote we may not know) the definition of,
|
||||
// reserve a number for it, and emit a link to that number.
|
||||
let (_, id) = self.get_entry(reference);
|
||||
@ -53,12 +46,38 @@ impl<'a, 'b, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, 'b
|
||||
// are local to the item so we make this ID "local" when displayed.
|
||||
id - *self.existing_footnotes
|
||||
);
|
||||
return Some((Event::Html(reference.into()), range));
|
||||
Event::Html(reference.into())
|
||||
}
|
||||
|
||||
fn collect_footnote_def(&mut self) -> Vec<Event<'a>> {
|
||||
let mut content = Vec::new();
|
||||
while let Some((event, _)) = self.inner.next() {
|
||||
match event {
|
||||
Event::End(TagEnd::FootnoteDefinition) => break,
|
||||
Event::FootnoteReference(ref reference) => {
|
||||
content.push(self.handle_footnote_reference(reference));
|
||||
}
|
||||
event => content.push(event),
|
||||
}
|
||||
}
|
||||
content
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, 'b, I> {
|
||||
type Item = SpannedEvent<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
let next = self.inner.next();
|
||||
match next {
|
||||
Some((Event::FootnoteReference(ref reference), range)) => {
|
||||
return Some((self.handle_footnote_reference(reference), range));
|
||||
}
|
||||
Some((Event::Start(Tag::FootnoteDefinition(def)), _)) => {
|
||||
// When we see a footnote definition, collect the assocated content, and store
|
||||
// that for rendering later.
|
||||
let content = collect_footnote_def(&mut self.inner);
|
||||
let content = self.collect_footnote_def();
|
||||
let (entry_content, _) = self.get_entry(&def);
|
||||
*entry_content = content;
|
||||
}
|
||||
@ -80,17 +99,6 @@ impl<'a, 'b, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, 'b
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_footnote_def<'a>(events: impl Iterator<Item = SpannedEvent<'a>>) -> Vec<Event<'a>> {
|
||||
let mut content = Vec::new();
|
||||
for (event, _) in events {
|
||||
if let Event::End(TagEnd::FootnoteDefinition) = event {
|
||||
break;
|
||||
}
|
||||
content.push(event);
|
||||
}
|
||||
content
|
||||
}
|
||||
|
||||
fn render_footnotes_defs(mut footnotes: Vec<FootnoteDef<'_>>) -> String {
|
||||
let mut ret = String::from("<div class=\"footnotes\"><hr><ol>");
|
||||
|
||||
|
@ -2667,6 +2667,7 @@ class DocSearch {
|
||||
const sortResults = async(results, typeInfo, preferredCrate) => {
|
||||
const userQuery = parsedQuery.userQuery;
|
||||
const normalizedUserQuery = parsedQuery.userQuery.toLowerCase();
|
||||
const isMixedCase = normalizedUserQuery !== userQuery;
|
||||
const result_list = [];
|
||||
for (const result of results.values()) {
|
||||
result.item = this.searchIndex[result.id];
|
||||
@ -2678,11 +2679,13 @@ class DocSearch {
|
||||
let a, b;
|
||||
|
||||
// sort by exact case-sensitive match
|
||||
if (isMixedCase) {
|
||||
a = (aaa.item.name !== userQuery);
|
||||
b = (bbb.item.name !== userQuery);
|
||||
if (a !== b) {
|
||||
return a - b;
|
||||
}
|
||||
}
|
||||
|
||||
// sort by exact match with regard to the last word (mismatch goes later)
|
||||
a = (aaa.word !== normalizedUserQuery);
|
||||
|
@ -6071,6 +6071,7 @@ Released 2018-09-13
|
||||
[`unnecessary_literal_bound`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_literal_bound
|
||||
[`unnecessary_literal_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_literal_unwrap
|
||||
[`unnecessary_map_on_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_map_on_constructor
|
||||
[`unnecessary_map_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_map_or
|
||||
[`unnecessary_min_or_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_min_or_max
|
||||
[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
|
||||
[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
|
||||
|
@ -84,7 +84,7 @@ fn delete_vs_task_file(path: &Path) -> bool {
|
||||
/// It may fail silently.
|
||||
fn try_delete_vs_directory_if_empty() {
|
||||
let path = Path::new(VSCODE_DIR);
|
||||
if path.read_dir().map_or(false, |mut iter| iter.next().is_none()) {
|
||||
if path.read_dir().is_ok_and(|mut iter| iter.next().is_none()) {
|
||||
// The directory is empty. We just try to delete it but allow a silence
|
||||
// fail as an empty `.vscode` directory is still valid
|
||||
let _silence_result = fs::remove_dir(path);
|
||||
|
@ -16,7 +16,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) {
|
||||
return;
|
||||
}
|
||||
if let Some(lint_list) = &attr.meta_item_list() {
|
||||
if attr.ident().map_or(false, |ident| is_lint_level(ident.name, attr.id)) {
|
||||
if attr.ident().is_some_and(|ident| is_lint_level(ident.name, attr.id)) {
|
||||
for lint in lint_list {
|
||||
match item.kind {
|
||||
ItemKind::Use(..) => {
|
||||
|
@ -50,7 +50,7 @@ fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_
|
||||
block
|
||||
.expr
|
||||
.as_ref()
|
||||
.map_or(false, |e| is_relevant_expr(cx, typeck_results, e)),
|
||||
.is_some_and(|e| is_relevant_expr(cx, typeck_results, e)),
|
||||
|stmt| match &stmt.kind {
|
||||
StmtKind::Let(_) => true,
|
||||
StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, typeck_results, expr),
|
||||
@ -60,7 +60,7 @@ fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_
|
||||
}
|
||||
|
||||
fn is_relevant_expr(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, expr: &Expr<'_>) -> bool {
|
||||
if macro_backtrace(expr.span).last().map_or(false, |macro_call| {
|
||||
if macro_backtrace(expr.span).last().is_some_and(|macro_call| {
|
||||
is_panic(cx, macro_call.def_id) || cx.tcx.item_name(macro_call.def_id) == sym::unreachable
|
||||
}) {
|
||||
return false;
|
||||
|
@ -60,8 +60,8 @@ fn is_impl_not_trait_with_bool_out<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -
|
||||
trait_id,
|
||||
)
|
||||
})
|
||||
.map_or(false, |assoc_item| {
|
||||
let proj = Ty::new_projection_from_args(cx.tcx, assoc_item.def_id, cx.tcx.mk_args_trait(ty, []));
|
||||
.is_some_and(|assoc_item| {
|
||||
let proj = Ty::new_projection(cx.tcx, assoc_item.def_id, cx.tcx.mk_args_trait(ty, []));
|
||||
let nty = cx.tcx.normalize_erasing_regions(cx.param_env, proj);
|
||||
|
||||
nty.is_bool()
|
||||
|
@ -667,5 +667,5 @@ fn implements_ord(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
cx.tcx
|
||||
.get_diagnostic_item(sym::Ord)
|
||||
.map_or(false, |id| implements_trait(cx, ty, id, &[]))
|
||||
.is_some_and(|id| implements_trait(cx, ty, id, &[]))
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ impl LateLintPass<'_> for BoxDefault {
|
||||
// And that method is `new`
|
||||
&& seg.ident.name == sym::new
|
||||
// And the call is that of a `Box` method
|
||||
&& path_def_id(cx, ty).map_or(false, |id| Some(id) == cx.tcx.lang_items().owned_box())
|
||||
&& path_def_id(cx, ty).is_some_and(|id| Some(id) == cx.tcx.lang_items().owned_box())
|
||||
// And the single argument to the call is another function call
|
||||
// This is the `T::default()` (or default equivalent) of `Box::new(T::default())`
|
||||
&& let ExprKind::Call(arg_path, _) = arg.kind
|
||||
@ -83,9 +83,9 @@ fn is_plain_default(cx: &LateContext<'_>, arg_path: &Expr<'_>) -> bool {
|
||||
}
|
||||
|
||||
fn is_local_vec_expn(cx: &LateContext<'_>, expr: &Expr<'_>, ref_expr: &Expr<'_>) -> bool {
|
||||
macro_backtrace(expr.span).next().map_or(false, |call| {
|
||||
cx.tcx.is_diagnostic_item(sym::vec_macro, call.def_id) && call.span.eq_ctxt(ref_expr.span)
|
||||
})
|
||||
macro_backtrace(expr.span)
|
||||
.next()
|
||||
.is_some_and(|call| cx.tcx.is_diagnostic_item(sym::vec_macro, call.def_id) && call.span.eq_ctxt(ref_expr.span))
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -159,8 +159,7 @@ pub(super) fn check<'tcx>(
|
||||
// The same is true if the expression encompassing the cast expression is a unary
|
||||
// expression or an addressof expression.
|
||||
let needs_block = matches!(cast_expr.kind, ExprKind::Unary(..) | ExprKind::AddrOf(..))
|
||||
|| get_parent_expr(cx, expr)
|
||||
.map_or(false, |e| matches!(e.kind, ExprKind::Unary(..) | ExprKind::AddrOf(..)));
|
||||
|| get_parent_expr(cx, expr).is_some_and(|e| matches!(e.kind, ExprKind::Unary(..) | ExprKind::AddrOf(..)));
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
@ -110,7 +110,7 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain {
|
||||
let is_ord = cx
|
||||
.tcx
|
||||
.get_diagnostic_item(sym::Ord)
|
||||
.map_or(false, |id| implements_trait(cx, ty, id, &[]));
|
||||
.is_some_and(|id| implements_trait(cx, ty, id, &[]));
|
||||
|
||||
if !is_ord {
|
||||
return;
|
||||
|
@ -345,7 +345,7 @@ fn eq_binding_names(s: &Stmt<'_>, names: &[(HirId, Symbol)]) -> bool {
|
||||
let mut i = 0usize;
|
||||
let mut res = true;
|
||||
l.pat.each_binding_or_first(&mut |_, _, _, name| {
|
||||
if names.get(i).map_or(false, |&(_, n)| n == name.name) {
|
||||
if names.get(i).is_some_and(|&(_, n)| n == name.name) {
|
||||
i += 1;
|
||||
} else {
|
||||
res = false;
|
||||
@ -389,12 +389,10 @@ fn eq_stmts(
|
||||
let new_bindings = &moved_bindings[old_count..];
|
||||
blocks
|
||||
.iter()
|
||||
.all(|b| get_stmt(b).map_or(false, |s| eq_binding_names(s, new_bindings)))
|
||||
.all(|b| get_stmt(b).is_some_and(|s| eq_binding_names(s, new_bindings)))
|
||||
} else {
|
||||
true
|
||||
}) && blocks
|
||||
.iter()
|
||||
.all(|b| get_stmt(b).map_or(false, |s| eq.eq_stmt(s, stmt)))
|
||||
}) && blocks.iter().all(|b| get_stmt(b).is_some_and(|s| eq.eq_stmt(s, stmt)))
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_lines)]
|
||||
@ -451,9 +449,7 @@ fn scan_block_for_eq<'tcx>(
|
||||
// x + 50
|
||||
let expr_hash_eq = if let Some(e) = block.expr {
|
||||
let hash = hash_expr(cx, e);
|
||||
blocks
|
||||
.iter()
|
||||
.all(|b| b.expr.map_or(false, |e| hash_expr(cx, e) == hash))
|
||||
blocks.iter().all(|b| b.expr.is_some_and(|e| hash_expr(cx, e) == hash))
|
||||
} else {
|
||||
blocks.iter().all(|b| b.expr.is_none())
|
||||
};
|
||||
@ -514,7 +510,7 @@ fn scan_block_for_eq<'tcx>(
|
||||
});
|
||||
if let Some(e) = block.expr {
|
||||
for block in blocks {
|
||||
if block.expr.map_or(false, |expr| !eq.eq_expr(expr, e)) {
|
||||
if block.expr.is_some_and(|expr| !eq.eq_expr(expr, e)) {
|
||||
moved_locals.truncate(moved_locals_at_start);
|
||||
return BlockEq {
|
||||
start_end_eq,
|
||||
@ -533,7 +529,7 @@ fn scan_block_for_eq<'tcx>(
|
||||
}
|
||||
|
||||
fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbol)], if_expr: &Expr<'_>) -> bool {
|
||||
get_enclosing_block(cx, if_expr.hir_id).map_or(false, |block| {
|
||||
get_enclosing_block(cx, if_expr.hir_id).is_some_and(|block| {
|
||||
let ignore_span = block.span.shrink_to_lo().to(if_expr.span);
|
||||
|
||||
symbols
|
||||
|
@ -481,6 +481,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
||||
crate::methods::UNNECESSARY_JOIN_INFO,
|
||||
crate::methods::UNNECESSARY_LAZY_EVALUATIONS_INFO,
|
||||
crate::methods::UNNECESSARY_LITERAL_UNWRAP_INFO,
|
||||
crate::methods::UNNECESSARY_MAP_OR_INFO,
|
||||
crate::methods::UNNECESSARY_MIN_OR_MAX_INFO,
|
||||
crate::methods::UNNECESSARY_RESULT_MAP_OR_ELSE_INFO,
|
||||
crate::methods::UNNECESSARY_SORT_BY_INFO,
|
||||
|
@ -253,6 +253,6 @@ impl<'tcx> From<Ty<'tcx>> for ExplicitTyBound {
|
||||
|
||||
impl<'tcx> From<Option<Ty<'tcx>>> for ExplicitTyBound {
|
||||
fn from(v: Option<Ty<'tcx>>) -> Self {
|
||||
Self(v.map_or(false, Ty::is_numeric))
|
||||
Self(v.is_some_and(Ty::is_numeric))
|
||||
}
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ fn is_path_self(e: &Expr<'_>) -> bool {
|
||||
fn contains_trait_object(ty: Ty<'_>) -> bool {
|
||||
match ty.kind() {
|
||||
ty::Ref(_, ty, _) => contains_trait_object(*ty),
|
||||
ty::Adt(def, args) => def.is_box() && args[0].as_type().map_or(false, contains_trait_object),
|
||||
ty::Adt(def, args) => def.is_box() && args[0].as_type().is_some_and(contains_trait_object),
|
||||
ty::Dynamic(..) => true,
|
||||
_ => false,
|
||||
}
|
||||
|
@ -327,7 +327,7 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h
|
||||
// there's a Copy impl for any instance of the adt.
|
||||
if !is_copy(cx, ty) {
|
||||
if ty_subs.non_erasable_generics().next().is_some() {
|
||||
let has_copy_impl = cx.tcx.all_local_trait_impls(()).get(©_id).map_or(false, |impls| {
|
||||
let has_copy_impl = cx.tcx.all_local_trait_impls(()).get(©_id).is_some_and(|impls| {
|
||||
impls.iter().any(|&id| {
|
||||
matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _)
|
||||
if ty_adt.did() == adt.did())
|
||||
|
@ -47,7 +47,7 @@ pub fn check(
|
||||
),
|
||||
_ => (),
|
||||
}
|
||||
if !headers.panics && panic_info.map_or(false, |el| !el.1) {
|
||||
if !headers.panics && panic_info.is_some_and(|el| !el.1) {
|
||||
span_lint_and_note(
|
||||
cx,
|
||||
MISSING_PANICS_DOC,
|
||||
|
@ -112,7 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
|
||||
MEM_FORGET,
|
||||
Cow::Owned(format!(
|
||||
"usage of `mem::forget` on {}",
|
||||
if arg_ty.ty_adt_def().map_or(false, |def| def.has_dtor(cx.tcx)) {
|
||||
if arg_ty.ty_adt_def().is_some_and(|def| def.has_dtor(cx.tcx)) {
|
||||
"`Drop` type"
|
||||
} else {
|
||||
"type with `Drop` fields"
|
||||
|
@ -196,7 +196,7 @@ fn check_clousure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tc
|
||||
{
|
||||
span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
|
||||
if let Some(mut snippet) = snippet_opt(cx, callee.span) {
|
||||
if path_to_local(callee).map_or(false, |l| {
|
||||
if path_to_local(callee).is_some_and(|l| {
|
||||
// FIXME: Do we really need this `local_used_in` check?
|
||||
// Isn't it checking something like... `callee(callee)`?
|
||||
// If somehow this check is needed, add some test for it,
|
||||
|
@ -87,7 +87,7 @@ fn raw_ptr_arg(cx: &LateContext<'_>, arg: &hir::Param<'_>) -> Option<HirId> {
|
||||
}
|
||||
|
||||
fn check_arg(cx: &LateContext<'_>, raw_ptrs: &HirIdSet, arg: &hir::Expr<'_>) {
|
||||
if path_to_local(arg).map_or(false, |id| raw_ptrs.contains(&id)) {
|
||||
if path_to_local(arg).is_some_and(|id| raw_ptrs.contains(&id)) {
|
||||
span_lint(
|
||||
cx,
|
||||
NOT_UNSAFE_PTR_ARG_DEREF,
|
||||
|
@ -171,13 +171,13 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
|
||||
if let ExprKind::Path(ref qpath) = path.kind {
|
||||
cx.qpath_res(qpath, path.hir_id)
|
||||
.opt_def_id()
|
||||
.map_or(false, |id| cx.tcx.is_diagnostic_item(sym::iter_repeat, id))
|
||||
.is_some_and(|id| cx.tcx.is_diagnostic_item(sym::iter_repeat, id))
|
||||
.into()
|
||||
} else {
|
||||
Finite
|
||||
}
|
||||
},
|
||||
ExprKind::Struct(..) => higher::Range::hir(expr).map_or(false, |r| r.end.is_none()).into(),
|
||||
ExprKind::Struct(..) => higher::Range::hir(expr).is_some_and(|r| r.end.is_none()).into(),
|
||||
_ => Finite,
|
||||
}
|
||||
}
|
||||
@ -228,9 +228,7 @@ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
|
||||
let not_double_ended = cx
|
||||
.tcx
|
||||
.get_diagnostic_item(sym::DoubleEndedIterator)
|
||||
.map_or(false, |id| {
|
||||
!implements_trait(cx, cx.typeck_results().expr_ty(receiver), id, &[])
|
||||
});
|
||||
.is_some_and(|id| !implements_trait(cx, cx.typeck_results().expr_ty(receiver), id, &[]));
|
||||
if not_double_ended {
|
||||
return is_infinite(cx, receiver);
|
||||
}
|
||||
|
@ -309,8 +309,8 @@ fn check_enum_start(cx: &LateContext<'_>, item_name: &str, variant: &Variant<'_>
|
||||
let item_name_chars = item_name.chars().count();
|
||||
|
||||
if count_match_start(item_name, name).char_count == item_name_chars
|
||||
&& name.chars().nth(item_name_chars).map_or(false, |c| !c.is_lowercase())
|
||||
&& name.chars().nth(item_name_chars + 1).map_or(false, |c| !c.is_numeric())
|
||||
&& name.chars().nth(item_name_chars).is_some_and(|c| !c.is_lowercase())
|
||||
&& name.chars().nth(item_name_chars + 1).is_some_and(|c| !c.is_numeric())
|
||||
{
|
||||
span_lint_hir(
|
||||
cx,
|
||||
|
@ -75,7 +75,7 @@ fn check_sig(cx: &LateContext<'_>, name: Symbol, sig: &FnSig<'_>, fn_id: LocalDe
|
||||
if cx
|
||||
.tcx
|
||||
.get_diagnostic_item(sym::Iterator)
|
||||
.map_or(false, |iter_id| !implements_trait(cx, ret_ty, iter_id, &[]))
|
||||
.is_some_and(|iter_id| !implements_trait(cx, ret_ty, iter_id, &[]))
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
|
@ -56,7 +56,8 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays {
|
||||
&& !item.span.from_expansion()
|
||||
&& let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
|
||||
&& let ty::Array(element_type, cst) = ty.kind()
|
||||
&& let Some((ty::ValTree::Leaf(element_count), _)) = cx.tcx.try_normalize_erasing_regions(ParamEnv::empty(), *cst).unwrap_or(*cst).try_to_valtree()
|
||||
&& let Some((ty::ValTree::Leaf(element_count), _)) = cx.tcx
|
||||
.try_normalize_erasing_regions(ParamEnv::empty(), *cst).unwrap_or(*cst).try_to_valtree()
|
||||
&& let element_count = element_count.to_target_usize(cx.tcx)
|
||||
&& let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes())
|
||||
&& u128::from(self.maximum_allowed_size) < u128::from(element_count) * u128::from(element_size)
|
||||
|
@ -94,6 +94,8 @@ impl LateLintPass<'_> for LargeIncludeFile {
|
||||
// Currently, rustc limits the usage of macro at the top-level of attributes,
|
||||
// so we don't need to recurse into each level.
|
||||
&& let AttrKind::Normal(ref normal) = attr.kind
|
||||
&& let Some(doc) = attr.doc_str()
|
||||
&& doc.as_str().len() as u64 > self.max_file_size
|
||||
&& let AttrArgs::Eq(_, AttrArgsEq::Hir(ref meta)) = normal.item.args
|
||||
&& !attr.span.contains(meta.span)
|
||||
// Since the `include_str` is already expanded at this point, we can only take the
|
||||
|
@ -622,7 +622,7 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
|
||||
let ty = &cx.typeck_results().expr_ty(expr).peel_refs();
|
||||
match ty.kind() {
|
||||
ty::Dynamic(tt, ..) => tt.principal().map_or(false, |principal| {
|
||||
ty::Dynamic(tt, ..) => tt.principal().is_some_and(|principal| {
|
||||
let is_empty = sym!(is_empty);
|
||||
cx.tcx
|
||||
.associated_items(principal.def_id())
|
||||
|
@ -737,7 +737,7 @@ fn elision_suggestions(
|
||||
suggestions.extend(
|
||||
usages
|
||||
.iter()
|
||||
.filter(|usage| named_lifetime(usage).map_or(false, |id| elidable_lts.contains(&id)))
|
||||
.filter(|usage| named_lifetime(usage).is_some_and(|id| elidable_lts.contains(&id)))
|
||||
.map(|usage| {
|
||||
match cx.tcx.parent_hir_node(usage.hir_id) {
|
||||
Node::Ty(Ty {
|
||||
|
@ -58,7 +58,7 @@ pub(super) fn check<'tcx>(
|
||||
.tcx
|
||||
.lang_items()
|
||||
.copy_trait()
|
||||
.map_or(false, |id| implements_trait(cx, ty, id, &[]))
|
||||
.is_some_and(|id| implements_trait(cx, ty, id, &[]))
|
||||
{
|
||||
snippet.push_str(
|
||||
&format!(
|
||||
|
@ -416,7 +416,7 @@ fn get_assignments<'a, 'tcx>(
|
||||
.chain(*expr)
|
||||
.filter(move |e| {
|
||||
if let ExprKind::AssignOp(_, place, _) = e.kind {
|
||||
path_to_local(place).map_or(false, |id| {
|
||||
path_to_local(place).is_some_and(|id| {
|
||||
!loop_counters
|
||||
.iter()
|
||||
// skip the first item which should be `StartKind::Range`
|
||||
|
@ -126,7 +126,7 @@ impl BreakAfterExprVisitor {
|
||||
break_after_expr: false,
|
||||
};
|
||||
|
||||
get_enclosing_block(cx, hir_id).map_or(false, |block| {
|
||||
get_enclosing_block(cx, hir_id).is_some_and(|block| {
|
||||
visitor.visit_block(block);
|
||||
visitor.break_after_expr
|
||||
})
|
||||
|
@ -50,7 +50,7 @@ pub(super) fn check<'tcx>(
|
||||
.tcx
|
||||
.lang_items()
|
||||
.clone_trait()
|
||||
.map_or(false, |id| implements_trait(cx, ty, id, &[]))
|
||||
.is_some_and(|id| implements_trait(cx, ty, id, &[]))
|
||||
{
|
||||
// Make sure that the push does not involve possibly mutating values
|
||||
match pushed_item.kind {
|
||||
|
@ -256,9 +256,10 @@ fn is_conditional(expr: &Expr<'_>) -> bool {
|
||||
/// If `arg` was the argument to a `for` loop, return the "cleanest" way of writing the
|
||||
/// actual `Iterator` that the loop uses.
|
||||
pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic_ref: &mut Applicability) -> String {
|
||||
let impls_iterator = cx.tcx.get_diagnostic_item(sym::Iterator).map_or(false, |id| {
|
||||
implements_trait(cx, cx.typeck_results().expr_ty(arg), id, &[])
|
||||
});
|
||||
let impls_iterator = cx
|
||||
.tcx
|
||||
.get_diagnostic_item(sym::Iterator)
|
||||
.is_some_and(|id| implements_trait(cx, cx.typeck_results().expr_ty(arg), id, &[]));
|
||||
if impls_iterator {
|
||||
format!(
|
||||
"{}",
|
||||
|
@ -226,7 +226,7 @@ impl TypeClampability {
|
||||
} else if cx
|
||||
.tcx
|
||||
.get_diagnostic_item(sym::Ord)
|
||||
.map_or(false, |id| implements_trait(cx, ty, id, &[]))
|
||||
.is_some_and(|id| implements_trait(cx, ty, id, &[]))
|
||||
{
|
||||
Some(TypeClampability::Ord)
|
||||
} else {
|
||||
|
@ -162,9 +162,9 @@ fn eq_pattern_length<'tcx>(cx: &LateContext<'tcx>, pattern: &Expr<'_>, expr: &'t
|
||||
..
|
||||
}) = expr.kind
|
||||
{
|
||||
constant_length(cx, pattern).map_or(false, |length| *n == length)
|
||||
constant_length(cx, pattern).is_some_and(|length| *n == length)
|
||||
} else {
|
||||
len_arg(cx, expr).map_or(false, |arg| eq_expr_value(cx, pattern, arg))
|
||||
len_arg(cx, expr).is_some_and(|arg| eq_expr_value(cx, pattern, arg))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ where
|
||||
&& b0 != b1
|
||||
&& (first_guard.is_none() || iter.len() == 0)
|
||||
&& first_attrs.is_empty()
|
||||
&& iter.all(|arm| find_bool_lit(&arm.2.kind).map_or(false, |b| b == b0) && arm.3.is_none() && arm.0.is_empty())
|
||||
&& iter.all(|arm| find_bool_lit(&arm.2.kind).is_some_and(|b| b == b0) && arm.3.is_none() && arm.0.is_empty())
|
||||
{
|
||||
if let Some(last_pat) = last_pat_opt {
|
||||
if !is_wild(last_pat) {
|
||||
|
@ -447,7 +447,7 @@ fn is_pat_variant(cx: &LateContext<'_>, pat: &Pat<'_>, path: &QPath<'_>, expecte
|
||||
.tcx
|
||||
.lang_items()
|
||||
.get(expected_lang_item)
|
||||
.map_or(false, |expected_id| cx.tcx.parent(id) == expected_id),
|
||||
.is_some_and(|expected_id| cx.tcx.parent(id) == expected_id),
|
||||
Item::Diag(expected_ty, expected_variant) => {
|
||||
let ty = cx.typeck_results().pat_ty(pat);
|
||||
|
||||
|
@ -175,7 +175,7 @@ impl<'tcx> Visitor<'tcx> for PatVisitor<'tcx> {
|
||||
if matches!(pat.kind, PatKind::Binding(..)) {
|
||||
ControlFlow::Break(())
|
||||
} else {
|
||||
self.has_enum |= self.typeck.pat_ty(pat).ty_adt_def().map_or(false, AdtDef::is_enum);
|
||||
self.has_enum |= self.typeck.pat_ty(pat).ty_adt_def().is_some_and(AdtDef::is_enum);
|
||||
walk_pat(self, pat)
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine
|
||||
let span = hygiene::walk_chain(err_arg.span, try_arg.span.ctxt());
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let origin_snippet = snippet_with_applicability(cx, span, "_", &mut applicability);
|
||||
let ret_prefix = if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::Ret(_))) {
|
||||
let ret_prefix = if get_parent_expr(cx, expr).is_some_and(|e| matches!(e.kind, ExprKind::Ret(_))) {
|
||||
"" // already returns
|
||||
} else {
|
||||
"return "
|
||||
|
@ -83,7 +83,7 @@ pub(super) fn check<'tcx>(
|
||||
hir::ExprKind::MethodCall(..) => {
|
||||
cx.typeck_results()
|
||||
.type_dependent_def_id(arg.hir_id)
|
||||
.map_or(false, |method_id| {
|
||||
.is_some_and(|method_id| {
|
||||
matches!(
|
||||
cx.tcx.fn_sig(method_id).instantiate_identity().output().skip_binder().kind(),
|
||||
ty::Ref(re, ..) if re.is_static()
|
||||
|
@ -32,9 +32,10 @@ pub(super) fn check<'tcx>(
|
||||
filter_arg: &'tcx hir::Expr<'_>,
|
||||
) {
|
||||
// lint if caller of `.filter().next()` is an Iterator
|
||||
let recv_impls_iterator = cx.tcx.get_diagnostic_item(sym::Iterator).map_or(false, |id| {
|
||||
implements_trait(cx, cx.typeck_results().expr_ty(recv), id, &[])
|
||||
});
|
||||
let recv_impls_iterator = cx
|
||||
.tcx
|
||||
.get_diagnostic_item(sym::Iterator)
|
||||
.is_some_and(|id| implements_trait(cx, cx.typeck_results().expr_ty(recv), id, &[]));
|
||||
if recv_impls_iterator {
|
||||
let msg = "called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling \
|
||||
`.find(..)` instead";
|
||||
|
@ -19,9 +19,7 @@ pub(super) fn check<'tcx>(
|
||||
if cx
|
||||
.tcx
|
||||
.get_diagnostic_item(sym::DoubleEndedIterator)
|
||||
.map_or(false, |double_ended_iterator| {
|
||||
implements_trait(cx, rev_recv_ty, double_ended_iterator, &[])
|
||||
})
|
||||
.is_some_and(|double_ended_iterator| implements_trait(cx, rev_recv_ty, double_ended_iterator, &[]))
|
||||
&& is_trait_method(cx, rev_call, sym::Iterator)
|
||||
&& is_trait_method(cx, expr, sym::Iterator)
|
||||
{
|
||||
|
@ -36,8 +36,8 @@ fn parse_repeat_arg(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<RepeatKind> {
|
||||
} else {
|
||||
let ty = cx.typeck_results().expr_ty(e);
|
||||
if is_type_lang_item(cx, ty, LangItem::String)
|
||||
|| (is_type_lang_item(cx, ty, LangItem::OwnedBox) && get_ty_param(ty).map_or(false, Ty::is_str))
|
||||
|| (is_type_diagnostic_item(cx, ty, sym::Cow) && get_ty_param(ty).map_or(false, Ty::is_str))
|
||||
|| (is_type_lang_item(cx, ty, LangItem::OwnedBox) && get_ty_param(ty).is_some_and(Ty::is_str))
|
||||
|| (is_type_diagnostic_item(cx, ty, sym::Cow) && get_ty_param(ty).is_some_and(Ty::is_str))
|
||||
{
|
||||
Some(RepeatKind::String)
|
||||
} else {
|
||||
|
@ -70,7 +70,7 @@ pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_
|
||||
if ident_eq(name, obj) && method.ident.name == sym::clone
|
||||
&& let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id)
|
||||
&& let Some(trait_id) = cx.tcx.trait_of_item(fn_id)
|
||||
&& cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id)
|
||||
&& cx.tcx.lang_items().clone_trait() == Some(trait_id)
|
||||
// no autoderefs
|
||||
&& !cx.typeck_results().expr_adjustments(obj).iter()
|
||||
.any(|a| matches!(a.kind, Adjust::Deref(Some(..))))
|
||||
|
@ -121,6 +121,7 @@ mod unnecessary_iter_cloned;
|
||||
mod unnecessary_join;
|
||||
mod unnecessary_lazy_eval;
|
||||
mod unnecessary_literal_unwrap;
|
||||
mod unnecessary_map_or;
|
||||
mod unnecessary_min_or_max;
|
||||
mod unnecessary_result_map_or_else;
|
||||
mod unnecessary_sort_by;
|
||||
@ -4099,6 +4100,33 @@ declare_clippy_lint! {
|
||||
"is_empty() called on strings known at compile time"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Converts some constructs mapping an Enum value for equality comparison.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Calls such as `opt.map_or(false, |val| val == 5)` are needlessly long and cumbersome,
|
||||
/// and can be reduced to, for example, `opt == Some(5)` assuming `opt` implements `PartialEq`.
|
||||
/// This lint offers readability and conciseness improvements.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// pub fn a(x: Option<i32>) -> bool {
|
||||
/// x.map_or(false, |n| n == 5)
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// pub fn a(x: Option<i32>) -> bool {
|
||||
/// x == Some(5)
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.75.0"]
|
||||
pub UNNECESSARY_MAP_OR,
|
||||
style,
|
||||
"reduce unnecessary pattern matching for constructs that implement `PartialEq`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks if an iterator is used to check if a string is ascii.
|
||||
@ -4417,6 +4445,7 @@ impl_lint_pass!(Methods => [
|
||||
NEEDLESS_AS_BYTES,
|
||||
MAP_ALL_ANY_IDENTITY,
|
||||
MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES,
|
||||
UNNECESSARY_MAP_OR,
|
||||
]);
|
||||
|
||||
/// Extracts a method call name, args, and `Span` of the method name.
|
||||
@ -4950,6 +4979,7 @@ impl Methods {
|
||||
option_map_or_none::check(cx, expr, recv, def, map);
|
||||
manual_ok_or::check(cx, expr, recv, def, map);
|
||||
option_map_or_err_ok::check(cx, expr, recv, def, map);
|
||||
unnecessary_map_or::check(cx, expr, recv, def, map, &self.msrv);
|
||||
},
|
||||
("map_or_else", [def, map]) => {
|
||||
result_map_or_else_none::check(cx, expr, recv, def, map);
|
||||
@ -5343,7 +5373,7 @@ impl SelfKind {
|
||||
boxed_ty == parent_ty
|
||||
} else if is_type_diagnostic_item(cx, ty, sym::Rc) || is_type_diagnostic_item(cx, ty, sym::Arc) {
|
||||
if let ty::Adt(_, args) = ty.kind() {
|
||||
args.types().next().map_or(false, |t| t == parent_ty)
|
||||
args.types().next() == Some(parent_ty)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
@ -193,7 +193,7 @@ fn check_collect_into_intoiterator<'tcx>(
|
||||
|
||||
/// Checks if the given method call matches the expected signature of `([&[mut]] self) -> bool`
|
||||
fn is_is_empty_sig(cx: &LateContext<'_>, call_id: HirId) -> bool {
|
||||
cx.typeck_results().type_dependent_def_id(call_id).map_or(false, |id| {
|
||||
cx.typeck_results().type_dependent_def_id(call_id).is_some_and(|id| {
|
||||
let sig = cx.tcx.fn_sig(id).instantiate_identity().skip_binder();
|
||||
sig.inputs().len() == 1 && sig.output().is_bool()
|
||||
})
|
||||
|
@ -45,7 +45,7 @@ pub(super) fn check(
|
||||
hir::ExprKind::Path(ref expr_qpath) => {
|
||||
cx.qpath_res(expr_qpath, map_arg.hir_id)
|
||||
.opt_def_id()
|
||||
.map_or(false, |fun_def_id| {
|
||||
.is_some_and(|fun_def_id| {
|
||||
cx.tcx.is_diagnostic_item(sym::deref_method, fun_def_id)
|
||||
|| cx.tcx.is_diagnostic_item(sym::deref_mut_method, fun_def_id)
|
||||
|| deref_aliases
|
||||
|
@ -59,9 +59,7 @@ pub(super) fn check<'tcx>(
|
||||
let output_ty = cx.tcx.fn_sig(def_id).instantiate(cx.tcx, args).skip_binder().output();
|
||||
cx.tcx
|
||||
.get_diagnostic_item(sym::Default)
|
||||
.map_or(false, |default_trait_id| {
|
||||
implements_trait(cx, output_ty, default_trait_id, &[])
|
||||
})
|
||||
.is_some_and(|default_trait_id| implements_trait(cx, output_ty, default_trait_id, &[]))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
@ -348,7 +348,7 @@ fn parse_iter_usage<'tcx>(
|
||||
&& cx
|
||||
.typeck_results()
|
||||
.type_dependent_def_id(e.hir_id)
|
||||
.map_or(false, |id| is_diag_item_method(cx, id, sym::Option)) =>
|
||||
.is_some_and(|id| is_diag_item_method(cx, id, sym::Option)) =>
|
||||
{
|
||||
(Some(UnwrapKind::Unwrap), e.span)
|
||||
},
|
||||
|
131
src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs
Normal file
131
src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs
Normal file
@ -0,0 +1,131 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::sugg::{Sugg, make_binop};
|
||||
use clippy_utils::ty::{get_type_diagnostic_name, implements_trait};
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
use clippy_utils::{is_from_proc_macro, path_to_local_id};
|
||||
use rustc_ast::LitKind::Bool;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::UNNECESSARY_MAP_OR;
|
||||
|
||||
pub(super) enum Variant {
|
||||
Ok,
|
||||
Some,
|
||||
}
|
||||
impl Variant {
|
||||
pub fn variant_name(&self) -> &'static str {
|
||||
match self {
|
||||
Variant::Ok => "Ok",
|
||||
Variant::Some => "Some",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn method_name(&self) -> &'static str {
|
||||
match self {
|
||||
Variant::Ok => "is_ok_and",
|
||||
Variant::Some => "is_some_and",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn check<'a>(
|
||||
cx: &LateContext<'a>,
|
||||
expr: &Expr<'a>,
|
||||
recv: &Expr<'_>,
|
||||
def: &Expr<'_>,
|
||||
map: &Expr<'_>,
|
||||
msrv: &Msrv,
|
||||
) {
|
||||
let ExprKind::Lit(def_kind) = def.kind else {
|
||||
return;
|
||||
};
|
||||
|
||||
let recv_ty = cx.typeck_results().expr_ty(recv);
|
||||
|
||||
let Bool(def_bool) = def_kind.node else {
|
||||
return;
|
||||
};
|
||||
|
||||
let variant = match get_type_diagnostic_name(cx, recv_ty) {
|
||||
Some(sym::Option) => Variant::Some,
|
||||
Some(sym::Result) => Variant::Ok,
|
||||
Some(_) | None => return,
|
||||
};
|
||||
|
||||
let (sugg, method) = if let ExprKind::Closure(map_closure) = map.kind
|
||||
&& let closure_body = cx.tcx.hir().body(map_closure.body)
|
||||
&& let closure_body_value = closure_body.value.peel_blocks()
|
||||
&& let ExprKind::Binary(op, l, r) = closure_body_value.kind
|
||||
&& let Some(param) = closure_body.params.first()
|
||||
&& let PatKind::Binding(_, hir_id, _, _) = param.pat.kind
|
||||
// checking that map_or is one of the following:
|
||||
// .map_or(false, |x| x == y)
|
||||
// .map_or(false, |x| y == x) - swapped comparison
|
||||
// .map_or(true, |x| x != y)
|
||||
// .map_or(true, |x| y != x) - swapped comparison
|
||||
&& ((BinOpKind::Eq == op.node && !def_bool) || (BinOpKind::Ne == op.node && def_bool))
|
||||
&& let non_binding_location = if path_to_local_id(l, hir_id) { r } else { l }
|
||||
&& switch_to_eager_eval(cx, non_binding_location)
|
||||
// xor, because if its both then thats a strange edge case and
|
||||
// we can just ignore it, since by default clippy will error on this
|
||||
&& (path_to_local_id(l, hir_id) ^ path_to_local_id(r, hir_id))
|
||||
&& !is_local_used(cx, non_binding_location, hir_id)
|
||||
&& let typeck_results = cx.typeck_results()
|
||||
&& typeck_results.expr_ty(l) == typeck_results.expr_ty(r)
|
||||
&& let Some(partial_eq) = cx.tcx.get_diagnostic_item(sym::PartialEq)
|
||||
&& implements_trait(cx, recv_ty, partial_eq, &[recv_ty.into()])
|
||||
{
|
||||
let wrap = variant.variant_name();
|
||||
|
||||
// we may need to add parens around the suggestion
|
||||
// in case the parent expression has additional method calls,
|
||||
// since for example `Some(5).map_or(false, |x| x == 5).then(|| 1)`
|
||||
// being converted to `Some(5) == Some(5).then(|| 1)` isnt
|
||||
// the same thing
|
||||
|
||||
let inner_non_binding = Sugg::NonParen(Cow::Owned(format!(
|
||||
"{wrap}({})",
|
||||
Sugg::hir(cx, non_binding_location, "")
|
||||
)));
|
||||
|
||||
let binop = make_binop(op.node, &Sugg::hir(cx, recv, ".."), &inner_non_binding)
|
||||
.maybe_par()
|
||||
.into_string();
|
||||
|
||||
(binop, "a standard comparison")
|
||||
} else if !def_bool
|
||||
&& msrv.meets(msrvs::OPTION_RESULT_IS_VARIANT_AND)
|
||||
&& let Some(recv_callsite) = snippet_opt(cx, recv.span.source_callsite())
|
||||
&& let Some(span_callsite) = snippet_opt(cx, map.span.source_callsite())
|
||||
{
|
||||
let suggested_name = variant.method_name();
|
||||
(
|
||||
format!("{recv_callsite}.{suggested_name}({span_callsite})",),
|
||||
suggested_name,
|
||||
)
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
if is_from_proc_macro(cx, expr) {
|
||||
return;
|
||||
}
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_MAP_OR,
|
||||
expr.span,
|
||||
"this `map_or` is redundant",
|
||||
format!("use {method} instead"),
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
@ -166,9 +166,10 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Exp
|
||||
},
|
||||
)) = &left_expr.kind
|
||||
&& left_name == left_ident
|
||||
&& cx.tcx.get_diagnostic_item(sym::Ord).map_or(false, |id| {
|
||||
implements_trait(cx, cx.typeck_results().expr_ty(left_expr), id, &[])
|
||||
})
|
||||
&& cx
|
||||
.tcx
|
||||
.get_diagnostic_item(sym::Ord)
|
||||
.is_some_and(|id| implements_trait(cx, cx.typeck_results().expr_ty(left_expr), id, &[]))
|
||||
{
|
||||
return Some(LintTrigger::Sort(SortDetection { vec_name }));
|
||||
}
|
||||
|
@ -124,7 +124,7 @@ fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool {
|
||||
&& let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id)
|
||||
&& let Some(trait_id) = cx.tcx.trait_of_item(fn_id)
|
||||
// We check it's the `Clone` trait.
|
||||
&& cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id)
|
||||
&& cx.tcx.lang_items().clone_trait().is_some_and(|id| id == trait_id)
|
||||
// no autoderefs
|
||||
&& !cx.typeck_results().expr_adjustments(obj).iter()
|
||||
.any(|a| matches!(a.kind, Adjust::Deref(Some(..))))
|
||||
|
@ -427,7 +427,7 @@ impl MiscEarlyLints {
|
||||
// See <https://github.com/rust-lang/rust-clippy/issues/4507> for a regression.
|
||||
// FIXME: Find a better way to detect those cases.
|
||||
let lit_snip = match snippet_opt(cx, span) {
|
||||
Some(snip) if snip.chars().next().map_or(false, |c| c.is_ascii_digit()) => snip,
|
||||
Some(snip) if snip.chars().next().is_some_and(|c| c.is_ascii_digit()) => snip,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
|
@ -134,9 +134,11 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowsForGenericArgs<'tcx> {
|
||||
}
|
||||
|
||||
fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &Body<'_>) {
|
||||
if self.possible_borrowers.last().map_or(false, |&(local_def_id, _)| {
|
||||
local_def_id == cx.tcx.hir().body_owner_def_id(body.id())
|
||||
}) {
|
||||
if self
|
||||
.possible_borrowers
|
||||
.last()
|
||||
.is_some_and(|&(local_def_id, _)| local_def_id == cx.tcx.hir().body_owner_def_id(body.id()))
|
||||
{
|
||||
self.possible_borrowers.pop();
|
||||
}
|
||||
}
|
||||
@ -232,7 +234,7 @@ fn needless_borrow_count<'tcx>(
|
||||
let mut check_reference_and_referent = |reference: &Expr<'tcx>, referent: &Expr<'tcx>| {
|
||||
if let ExprKind::Field(base, _) = &referent.kind {
|
||||
let base_ty = cx.typeck_results().expr_ty(base);
|
||||
if drop_trait_def_id.map_or(false, |id| implements_trait(cx, base_ty, id, &[])) {
|
||||
if drop_trait_def_id.is_some_and(|id| implements_trait(cx, base_ty, id, &[])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -153,7 +153,7 @@ fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&ast::Label>)
|
||||
}
|
||||
|
||||
fn is_first_block_stmt_continue(block: &ast::Block, label: Option<&ast::Label>) -> bool {
|
||||
block.stmts.first().map_or(false, |stmt| match stmt.kind {
|
||||
block.stmts.first().is_some_and(|stmt| match stmt.kind {
|
||||
ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => {
|
||||
if let ast::ExprKind::Continue(ref l) = e.kind {
|
||||
compare_labels(label, l.as_ref())
|
||||
@ -390,7 +390,7 @@ fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) {
|
||||
#[must_use]
|
||||
fn erode_from_back(s: &str) -> String {
|
||||
let mut ret = s.to_string();
|
||||
while ret.pop().map_or(false, |c| c != '}') {}
|
||||
while ret.pop().is_some_and(|c| c != '}') {}
|
||||
while let Some(c) = ret.pop() {
|
||||
if !c.is_whitespace() {
|
||||
ret.push(c);
|
||||
|
@ -88,7 +88,7 @@ fn is_offending_macro(cx: &EarlyContext<'_>, span: Span, mac_braces: &MacroBrace
|
||||
|| span
|
||||
.macro_backtrace()
|
||||
.last()
|
||||
.map_or(false, |e| e.macro_def_id.map_or(false, DefId::is_local))
|
||||
.is_some_and(|e| e.macro_def_id.is_some_and(DefId::is_local))
|
||||
};
|
||||
let span_call_site = span.ctxt().outer_expn_data().call_site;
|
||||
if let ExpnKind::Macro(MacroKind::Bang, mac_name) = span.ctxt().outer_expn_data().kind
|
||||
|
@ -290,7 +290,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
|
||||
Some((Node::Expr(parent), child_id)) => match parent.kind {
|
||||
// Recursive call. Track which index the parameter is used in.
|
||||
ExprKind::Call(callee, args)
|
||||
if path_def_id(cx, callee).map_or(false, |id| {
|
||||
if path_def_id(cx, callee).is_some_and(|id| {
|
||||
id == param.fn_id && has_matching_args(param.fn_kind, typeck.node_args(callee.hir_id))
|
||||
}) =>
|
||||
{
|
||||
@ -300,7 +300,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
|
||||
return;
|
||||
},
|
||||
ExprKind::MethodCall(_, receiver, args, _)
|
||||
if typeck.type_dependent_def_id(parent.hir_id).map_or(false, |id| {
|
||||
if typeck.type_dependent_def_id(parent.hir_id).is_some_and(|id| {
|
||||
id == param.fn_id && has_matching_args(param.fn_kind, typeck.node_args(parent.hir_id))
|
||||
}) =>
|
||||
{
|
||||
|
@ -325,7 +325,7 @@ impl ArithmeticSideEffects {
|
||||
fn should_skip_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'tcx>) -> bool {
|
||||
is_lint_allowed(cx, ARITHMETIC_SIDE_EFFECTS, expr.hir_id)
|
||||
|| self.expr_span.is_some()
|
||||
|| self.const_span.map_or(false, |sp| sp.contains(expr.span))
|
||||
|| self.const_span.is_some_and(|sp| sp.contains(expr.span))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,14 +42,12 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool)
|
||||
if typeck
|
||||
.type_dependent_def_id(expr.hir_id)
|
||||
.and_then(|id| cx.tcx.trait_of_item(id))
|
||||
.map_or(false, |id| {
|
||||
matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ToString | sym::ToOwned))
|
||||
}) =>
|
||||
.is_some_and(|id| matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ToString | sym::ToOwned))) =>
|
||||
{
|
||||
(arg, arg.span)
|
||||
},
|
||||
ExprKind::Call(path, [arg])
|
||||
if path_def_id(cx, path).map_or(false, |did| {
|
||||
if path_def_id(cx, path).is_some_and(|did| {
|
||||
if cx.tcx.is_diagnostic_item(sym::from_str_method, did) {
|
||||
true
|
||||
} else if cx.tcx.is_diagnostic_item(sym::from_fn, did) {
|
||||
|
@ -42,83 +42,43 @@ pub(crate) fn check<'tcx>(
|
||||
|
||||
match op {
|
||||
BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
|
||||
let _ = check_op(
|
||||
cx,
|
||||
left,
|
||||
0,
|
||||
expr.span,
|
||||
peeled_right_span,
|
||||
needs_parenthesis(cx, expr, right),
|
||||
right_is_coerced_to_value,
|
||||
) || check_op(
|
||||
cx,
|
||||
right,
|
||||
0,
|
||||
expr.span,
|
||||
peeled_left_span,
|
||||
Parens::Unneeded,
|
||||
left_is_coerced_to_value,
|
||||
);
|
||||
if is_redundant_op(cx, left, 0) {
|
||||
let paren = needs_parenthesis(cx, expr, right);
|
||||
span_ineffective_operation(cx, expr.span, peeled_right_span, paren, right_is_coerced_to_value);
|
||||
} else if is_redundant_op(cx, right, 0) {
|
||||
let paren = needs_parenthesis(cx, expr, left);
|
||||
span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value);
|
||||
}
|
||||
},
|
||||
BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => {
|
||||
let _ = check_op(
|
||||
cx,
|
||||
right,
|
||||
0,
|
||||
expr.span,
|
||||
peeled_left_span,
|
||||
Parens::Unneeded,
|
||||
left_is_coerced_to_value,
|
||||
);
|
||||
if is_redundant_op(cx, right, 0) {
|
||||
let paren = needs_parenthesis(cx, expr, left);
|
||||
span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value);
|
||||
}
|
||||
},
|
||||
BinOpKind::Mul => {
|
||||
let _ = check_op(
|
||||
cx,
|
||||
left,
|
||||
1,
|
||||
expr.span,
|
||||
peeled_right_span,
|
||||
needs_parenthesis(cx, expr, right),
|
||||
right_is_coerced_to_value,
|
||||
) || check_op(
|
||||
cx,
|
||||
right,
|
||||
1,
|
||||
expr.span,
|
||||
peeled_left_span,
|
||||
Parens::Unneeded,
|
||||
left_is_coerced_to_value,
|
||||
);
|
||||
if is_redundant_op(cx, left, 1) {
|
||||
let paren = needs_parenthesis(cx, expr, right);
|
||||
span_ineffective_operation(cx, expr.span, peeled_right_span, paren, right_is_coerced_to_value);
|
||||
} else if is_redundant_op(cx, right, 1) {
|
||||
let paren = needs_parenthesis(cx, expr, left);
|
||||
span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value);
|
||||
}
|
||||
},
|
||||
BinOpKind::Div => {
|
||||
let _ = check_op(
|
||||
cx,
|
||||
right,
|
||||
1,
|
||||
expr.span,
|
||||
peeled_left_span,
|
||||
Parens::Unneeded,
|
||||
left_is_coerced_to_value,
|
||||
);
|
||||
if is_redundant_op(cx, right, 1) {
|
||||
let paren = needs_parenthesis(cx, expr, left);
|
||||
span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value);
|
||||
}
|
||||
},
|
||||
BinOpKind::BitAnd => {
|
||||
let _ = check_op(
|
||||
cx,
|
||||
left,
|
||||
-1,
|
||||
expr.span,
|
||||
peeled_right_span,
|
||||
needs_parenthesis(cx, expr, right),
|
||||
right_is_coerced_to_value,
|
||||
) || check_op(
|
||||
cx,
|
||||
right,
|
||||
-1,
|
||||
expr.span,
|
||||
peeled_left_span,
|
||||
Parens::Unneeded,
|
||||
left_is_coerced_to_value,
|
||||
);
|
||||
if is_redundant_op(cx, left, -1) {
|
||||
let paren = needs_parenthesis(cx, expr, right);
|
||||
span_ineffective_operation(cx, expr.span, peeled_right_span, paren, right_is_coerced_to_value);
|
||||
} else if is_redundant_op(cx, right, -1) {
|
||||
let paren = needs_parenthesis(cx, expr, left);
|
||||
span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value);
|
||||
}
|
||||
},
|
||||
BinOpKind::Rem => check_remainder(cx, left, right, expr.span, left.span),
|
||||
_ => (),
|
||||
@ -138,30 +98,53 @@ enum Parens {
|
||||
Unneeded,
|
||||
}
|
||||
|
||||
/// Checks if `left op right` needs parenthesis when reduced to `right`
|
||||
/// Checks if a binary expression needs parenthesis when reduced to just its
|
||||
/// right or left child.
|
||||
///
|
||||
/// e.g. `-(x + y + 0)` cannot be reduced to `-x + y`, as the behavior changes silently.
|
||||
/// e.g. `1u64 + ((x + y + 0i32) as u64)` cannot be reduced to `1u64 + x + y as u64`, since
|
||||
/// the the cast expression will not apply to the same expression.
|
||||
/// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` cannot be reduced
|
||||
/// to `if b { 1 } else { 2 } + if b { 3 } else { 4 }` where the `if` could be
|
||||
/// interpreted as a statement
|
||||
/// interpreted as a statement. The same behavior happens for `match`, `loop`,
|
||||
/// and blocks.
|
||||
/// e.g. `2 * (0 + { a })` can be reduced to `2 * { a }` without the need for parenthesis,
|
||||
/// but `1 * ({ a } + 4)` cannot be reduced to `{ a } + 4`, as a block at the start of a line
|
||||
/// will be interpreted as a statement instead of an expression.
|
||||
///
|
||||
/// See #8724
|
||||
fn needs_parenthesis(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>) -> Parens {
|
||||
match right.kind {
|
||||
/// See #8724, #13470
|
||||
fn needs_parenthesis(cx: &LateContext<'_>, binary: &Expr<'_>, child: &Expr<'_>) -> Parens {
|
||||
match child.kind {
|
||||
ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => {
|
||||
// ensure we're checking against the leftmost expression of `right`
|
||||
// For casts and binary expressions, we want to add parenthesis if
|
||||
// the parent HIR node is an expression, or if the parent HIR node
|
||||
// is a Block or Stmt, and the new left hand side would need
|
||||
// parenthesis be treated as a statement rather than an expression.
|
||||
if let Some((_, parent)) = cx.tcx.hir().parent_iter(binary.hir_id).next() {
|
||||
match parent {
|
||||
Node::Expr(_) => return Parens::Needed,
|
||||
Node::Block(_) | Node::Stmt(_) => {
|
||||
// ensure we're checking against the leftmost expression of `child`
|
||||
//
|
||||
// ~~~~~~~~~~~ `binary`
|
||||
// ~~~ `lhs`
|
||||
// 0 + {4} * 2
|
||||
// ~~~~~~~ `right`
|
||||
// ~~~~~~~ `child`
|
||||
return needs_parenthesis(cx, binary, lhs);
|
||||
},
|
||||
ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Block(..) | ExprKind::Loop(..) => {},
|
||||
_ => return Parens::Unneeded,
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Block(..) | ExprKind::Loop(..) => {
|
||||
// For if, match, block, and loop expressions, we want to add parenthesis if
|
||||
// the closest ancestor node that is not an expression is a block or statement.
|
||||
// This would mean that the rustfix suggestion will appear at the start of a line, which causes
|
||||
// these expressions to be interpreted as statements if they do not have parenthesis.
|
||||
let mut prev_id = binary.hir_id;
|
||||
for (_, node) in cx.tcx.hir().parent_iter(binary.hir_id) {
|
||||
if let Node::Expr(expr) = node
|
||||
&& let ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) = expr.kind
|
||||
for (_, parent) in cx.tcx.hir().parent_iter(binary.hir_id) {
|
||||
if let Node::Expr(expr) = parent
|
||||
&& let ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) | ExprKind::Unary(_, lhs) = expr.kind
|
||||
&& lhs.hir_id == prev_id
|
||||
{
|
||||
// keep going until we find a node that encompasses left of `binary`
|
||||
@ -169,12 +152,16 @@ fn needs_parenthesis(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>)
|
||||
continue;
|
||||
}
|
||||
|
||||
match node {
|
||||
Node::Block(_) | Node::Stmt(_) => break,
|
||||
match parent {
|
||||
Node::Block(_) | Node::Stmt(_) => return Parens::Needed,
|
||||
_ => return Parens::Unneeded,
|
||||
};
|
||||
}
|
||||
|
||||
},
|
||||
_ => {
|
||||
return Parens::Unneeded;
|
||||
},
|
||||
}
|
||||
Parens::Needed
|
||||
}
|
||||
|
||||
@ -199,7 +186,7 @@ fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span
|
||||
}
|
||||
}
|
||||
|
||||
fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, parens: Parens, is_erased: bool) -> bool {
|
||||
fn is_redundant_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8) -> bool {
|
||||
if let Some(Constant::Int(v)) = ConstEvalCtxt::new(cx).eval_simple(e).map(Constant::peel_refs) {
|
||||
let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() {
|
||||
ty::Int(ity) => unsext(cx.tcx, -1_i128, ity),
|
||||
@ -212,7 +199,6 @@ fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, pa
|
||||
1 => v == 1,
|
||||
_ => unreachable!(),
|
||||
} {
|
||||
span_ineffective_operation(cx, span, arg, parens, is_erased);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ pub struct Context {
|
||||
}
|
||||
impl Context {
|
||||
fn skip_expr(&mut self, e: &hir::Expr<'_>) -> bool {
|
||||
self.expr_id.is_some() || self.const_span.map_or(false, |span| span.contains(e.span))
|
||||
self.expr_id.is_some() || self.const_span.is_some_and(|span| span.contains(e.span))
|
||||
}
|
||||
|
||||
pub fn check_binary<'tcx>(
|
||||
|
@ -192,7 +192,7 @@ impl PassByRefOrValue {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let value_type = if fn_body.and_then(|body| body.params.get(index)).map_or(false, is_self) {
|
||||
let value_type = if fn_body.and_then(|body| body.params.get(index)).is_some_and(is_self) {
|
||||
"self".into()
|
||||
} else {
|
||||
snippet(cx, decl_ty.span, "_").into()
|
||||
|
@ -727,9 +727,8 @@ fn get_ref_lm<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutabili
|
||||
|
||||
fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
if let ExprKind::Call(pathexp, []) = expr.kind {
|
||||
path_def_id(cx, pathexp).map_or(false, |id| {
|
||||
matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ptr_null | sym::ptr_null_mut))
|
||||
})
|
||||
path_def_id(cx, pathexp)
|
||||
.is_some_and(|id| matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ptr_null | sym::ptr_null_mut)))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
@ -8,18 +8,18 @@ use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
|
||||
use clippy_utils::{
|
||||
eq_expr_value, higher, is_else_clause, is_in_const_context, is_lint_allowed, is_path_lang_item, is_res_lang_ctor,
|
||||
pat_and_expr_can_be_question_mark, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt,
|
||||
span_contains_comment,
|
||||
pat_and_expr_can_be_question_mark, path_res, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt,
|
||||
span_contains_cfg, span_contains_comment,
|
||||
};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk};
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::{
|
||||
BindingMode, Block, Body, ByRef, Expr, ExprKind, LetStmt, Mutability, Node, PatKind, PathSegment, QPath, Stmt,
|
||||
StmtKind,
|
||||
Arm, BindingMode, Block, Body, ByRef, Expr, ExprKind, FnRetTy, HirId, LetStmt, MatchSource, Mutability, Node, Pat,
|
||||
PatKind, PathSegment, QPath, Stmt, StmtKind,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::sym;
|
||||
use rustc_span::symbol::Symbol;
|
||||
@ -58,6 +58,9 @@ pub struct QuestionMark {
|
||||
/// if it is greater than zero.
|
||||
/// As for why we need this in the first place: <https://github.com/rust-lang/rust-clippy/issues/8628>
|
||||
try_block_depth_stack: Vec<u32>,
|
||||
/// Keeps track of the number of inferred return type closures we are inside, to avoid problems
|
||||
/// with the `Err(x.into())` expansion being ambiguious.
|
||||
inferred_ret_closure_stack: u16,
|
||||
}
|
||||
|
||||
impl_lint_pass!(QuestionMark => [QUESTION_MARK, MANUAL_LET_ELSE]);
|
||||
@ -68,6 +71,7 @@ impl QuestionMark {
|
||||
msrv: conf.msrv.clone(),
|
||||
matches_behaviour: conf.matches_for_let_else,
|
||||
try_block_depth_stack: Vec::new(),
|
||||
inferred_ret_closure_stack: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -125,7 +129,7 @@ fn check_let_some_else_return_none(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
|
||||
cx.tcx
|
||||
.lang_items()
|
||||
.try_trait()
|
||||
.map_or(false, |did| implements_trait(cx, init_ty, did, &[]))
|
||||
.is_some_and(|did| implements_trait(cx, init_ty, did, &[]))
|
||||
}
|
||||
|
||||
if let StmtKind::Let(LetStmt {
|
||||
@ -271,6 +275,135 @@ fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Ex
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum TryMode {
|
||||
Result,
|
||||
Option,
|
||||
}
|
||||
|
||||
fn find_try_mode<'tcx>(cx: &LateContext<'tcx>, scrutinee: &Expr<'tcx>) -> Option<TryMode> {
|
||||
let scrutinee_ty = cx.typeck_results().expr_ty_adjusted(scrutinee);
|
||||
let ty::Adt(scrutinee_adt_def, _) = scrutinee_ty.kind() else {
|
||||
return None;
|
||||
};
|
||||
|
||||
match cx.tcx.get_diagnostic_name(scrutinee_adt_def.did())? {
|
||||
sym::Result => Some(TryMode::Result),
|
||||
sym::Option => Some(TryMode::Option),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// Check that `pat` is `{ctor_lang_item}(val)`, returning `val`.
|
||||
fn extract_ctor_call<'a, 'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expected_ctor: LangItem,
|
||||
pat: &'a Pat<'tcx>,
|
||||
) -> Option<&'a Pat<'tcx>> {
|
||||
if let PatKind::TupleStruct(variant_path, [val_binding], _) = &pat.kind
|
||||
&& is_res_lang_ctor(cx, cx.qpath_res(variant_path, pat.hir_id), expected_ctor)
|
||||
{
|
||||
Some(val_binding)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// Extracts the local ID of a plain `val` pattern.
|
||||
fn extract_binding_pat(pat: &Pat<'_>) -> Option<HirId> {
|
||||
if let PatKind::Binding(BindingMode::NONE, binding, _, None) = pat.kind {
|
||||
Some(binding)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn check_arm_is_some_or_ok<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &Arm<'tcx>) -> bool {
|
||||
let happy_ctor = match mode {
|
||||
TryMode::Result => ResultOk,
|
||||
TryMode::Option => OptionSome,
|
||||
};
|
||||
|
||||
// Check for `Ok(val)` or `Some(val)`
|
||||
if arm.guard.is_none()
|
||||
&& let Some(val_binding) = extract_ctor_call(cx, happy_ctor, arm.pat)
|
||||
// Extract out `val`
|
||||
&& let Some(binding) = extract_binding_pat(val_binding)
|
||||
// Check body is just `=> val`
|
||||
&& path_to_local_id(peel_blocks(arm.body), binding)
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn check_arm_is_none_or_err<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &Arm<'tcx>) -> bool {
|
||||
if arm.guard.is_some() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let arm_body = peel_blocks(arm.body);
|
||||
match mode {
|
||||
TryMode::Result => {
|
||||
// Check that pat is Err(val)
|
||||
if let Some(ok_pat) = extract_ctor_call(cx, ResultErr, arm.pat)
|
||||
&& let Some(ok_val) = extract_binding_pat(ok_pat)
|
||||
// check `=> return Err(...)`
|
||||
&& let ExprKind::Ret(Some(wrapped_ret_expr)) = arm_body.kind
|
||||
&& let ExprKind::Call(ok_ctor, [ret_expr]) = wrapped_ret_expr.kind
|
||||
&& is_res_lang_ctor(cx, path_res(cx, ok_ctor), ResultErr)
|
||||
// check `...` is `val` from binding
|
||||
&& path_to_local_id(ret_expr, ok_val)
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
},
|
||||
TryMode::Option => {
|
||||
// Check the pat is `None`
|
||||
if is_res_lang_ctor(cx, path_res(cx, arm.pat), OptionNone)
|
||||
// Check `=> return None`
|
||||
&& let ExprKind::Ret(Some(ret_expr)) = arm_body.kind
|
||||
&& is_res_lang_ctor(cx, path_res(cx, ret_expr), OptionNone)
|
||||
&& !ret_expr.span.from_expansion()
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn check_arms_are_try<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm1: &Arm<'tcx>, arm2: &Arm<'tcx>) -> bool {
|
||||
(check_arm_is_some_or_ok(cx, mode, arm1) && check_arm_is_none_or_err(cx, mode, arm2))
|
||||
|| (check_arm_is_some_or_ok(cx, mode, arm2) && check_arm_is_none_or_err(cx, mode, arm1))
|
||||
}
|
||||
|
||||
fn check_if_try_match<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
|
||||
if let ExprKind::Match(scrutinee, [arm1, arm2], MatchSource::Normal | MatchSource::Postfix) = expr.kind
|
||||
&& !expr.span.from_expansion()
|
||||
&& let Some(mode) = find_try_mode(cx, scrutinee)
|
||||
&& !span_contains_cfg(cx, expr.span)
|
||||
&& check_arms_are_try(cx, mode, arm1, arm2)
|
||||
{
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let snippet = snippet_with_applicability(cx, scrutinee.span.source_callsite(), "..", &mut applicability);
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
QUESTION_MARK,
|
||||
expr.span,
|
||||
"this `match` expression can be replaced with `?`",
|
||||
"try instead",
|
||||
snippet.into_owned() + "?",
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
|
||||
if let Some(higher::IfLet {
|
||||
let_pat,
|
||||
@ -339,6 +472,17 @@ fn is_try_block(cx: &LateContext<'_>, bl: &Block<'_>) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_inferred_ret_closure(expr: &Expr<'_>) -> bool {
|
||||
let ExprKind::Closure(closure) = expr.kind else {
|
||||
return false;
|
||||
};
|
||||
|
||||
match closure.fn_decl.output {
|
||||
FnRetTy::Return(ret_ty) => ret_ty.is_suggestable_infer_ty(),
|
||||
FnRetTy::DefaultReturn(_) => true,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for QuestionMark {
|
||||
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
|
||||
if !is_lint_allowed(cx, QUESTION_MARK_USED, stmt.hir_id) {
|
||||
@ -350,11 +494,27 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMark {
|
||||
}
|
||||
self.check_manual_let_else(cx, stmt);
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if is_inferred_ret_closure(expr) {
|
||||
self.inferred_ret_closure_stack += 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if !self.inside_try_block() && !is_in_const_context(cx) && is_lint_allowed(cx, QUESTION_MARK_USED, expr.hir_id)
|
||||
{
|
||||
check_is_none_or_err_and_early_return(cx, expr);
|
||||
check_if_let_some_or_err_and_early_return(cx, expr);
|
||||
|
||||
if self.inferred_ret_closure_stack == 0 {
|
||||
check_if_try_match(cx, expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr_post(&mut self, _: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if is_inferred_ret_closure(expr) {
|
||||
self.inferred_ret_closure_stack -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing {
|
||||
let (expr_ty, expr_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(expr));
|
||||
let (indexed_ty, indexed_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(indexed));
|
||||
let parent_expr = get_parent_expr(cx, expr);
|
||||
let needs_parens_for_prefix = parent_expr.map_or(false, |parent| parent.precedence().order() > PREC_PREFIX);
|
||||
let needs_parens_for_prefix = parent_expr.is_some_and(|parent| parent.precedence().order() > PREC_PREFIX);
|
||||
|
||||
if expr_ty == indexed_ty {
|
||||
if expr_ref_count > indexed_ref_count {
|
||||
@ -107,7 +107,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing {
|
||||
kind: ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _),
|
||||
..
|
||||
})
|
||||
) || cx.typeck_results().expr_adjustments(expr).first().map_or(false, |a| {
|
||||
) || cx.typeck_results().expr_adjustments(expr).first().is_some_and(|a| {
|
||||
matches!(
|
||||
a.kind,
|
||||
Adjust::Borrow(AutoBorrow::Ref(AutoBorrowMutability::Mut { .. }))
|
||||
|
@ -412,9 +412,7 @@ impl<'tcx> IndexBinding<'_, 'tcx> {
|
||||
}
|
||||
Self::is_used_slice_indexed(lhs, idx_ident) || Self::is_used_slice_indexed(rhs, idx_ident)
|
||||
},
|
||||
ExprKind::Path(QPath::Resolved(_, path)) => {
|
||||
path.segments.first().map_or(false, |idx| idx.ident == idx_ident)
|
||||
},
|
||||
ExprKind::Path(QPath::Resolved(_, path)) => path.segments.first().is_some_and(|idx| idx.ident == idx_ident),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -51,8 +51,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m
|
||||
format!("&{ltopt}({inner_snippet})")
|
||||
},
|
||||
TyKind::Path(qpath)
|
||||
if get_bounds_if_impl_trait(cx, qpath, inner.hir_id)
|
||||
.map_or(false, |bounds| bounds.len() > 1) =>
|
||||
if get_bounds_if_impl_trait(cx, qpath, inner.hir_id).is_some_and(|bounds| bounds.len() > 1) =>
|
||||
{
|
||||
format!("&{ltopt}({inner_snippet})")
|
||||
},
|
||||
|
@ -38,7 +38,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx LetStmt<'_>) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (local.ty.map_or(false, |ty| !matches!(ty.kind, TyKind::Infer))
|
||||
if (local.ty.is_some_and(|ty| !matches!(ty.kind, TyKind::Infer))
|
||||
|| matches!(local.pat.kind, PatKind::Tuple([], ddpos) if ddpos.as_opt_usize().is_none()))
|
||||
&& expr_needs_inferred_result(cx, init)
|
||||
{
|
||||
|
@ -98,7 +98,7 @@ impl VecPushSearcher {
|
||||
needs_mut |= cx.typeck_results().expr_ty_adjusted(last_place).ref_mutability()
|
||||
== Some(Mutability::Mut)
|
||||
|| get_parent_expr(cx, last_place)
|
||||
.map_or(false, |e| matches!(e.kind, ExprKind::AddrOf(_, Mutability::Mut, _)));
|
||||
.is_some_and(|e| matches!(e.kind, ExprKind::AddrOf(_, Mutability::Mut, _)));
|
||||
},
|
||||
ExprKind::MethodCall(_, recv, ..)
|
||||
if recv.hir_id == e.hir_id
|
||||
|
@ -295,7 +295,7 @@ impl<'tcx> LateLintPass<'tcx> for Write {
|
||||
.opts
|
||||
.crate_name
|
||||
.as_ref()
|
||||
.map_or(false, |crate_name| crate_name == "build_script_build");
|
||||
.is_some_and(|crate_name| crate_name == "build_script_build");
|
||||
|
||||
let allowed_in_tests = self.allow_print_in_tests && is_in_test(cx.tcx, expr.hir_id);
|
||||
match diag_name {
|
||||
|
@ -51,7 +51,7 @@ fn span_matches_pat(sess: &Session, span: Span, start_pat: Pat, end_pat: Pat) ->
|
||||
return false;
|
||||
};
|
||||
let end = span.hi() - pos.sf.start_pos;
|
||||
src.get(pos.pos.0 as usize..end.0 as usize).map_or(false, |s| {
|
||||
src.get(pos.pos.0 as usize..end.0 as usize).is_some_and(|s| {
|
||||
// Spans can be wrapped in a mixture or parenthesis, whitespace, and trailing commas.
|
||||
let start_str = s.trim_start_matches(|c: char| c.is_whitespace() || c == '(');
|
||||
let end_str = s.trim_end_matches(|c: char| c.is_whitespace() || c == ')' || c == ',');
|
||||
@ -60,13 +60,13 @@ fn span_matches_pat(sess: &Session, span: Span, start_pat: Pat, end_pat: Pat) ->
|
||||
Pat::MultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)),
|
||||
Pat::OwnedMultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)),
|
||||
Pat::Sym(sym) => start_str.starts_with(sym.as_str()),
|
||||
Pat::Num => start_str.as_bytes().first().map_or(false, u8::is_ascii_digit),
|
||||
Pat::Num => start_str.as_bytes().first().is_some_and(u8::is_ascii_digit),
|
||||
} && match end_pat {
|
||||
Pat::Str(text) => end_str.ends_with(text),
|
||||
Pat::MultiStr(texts) => texts.iter().any(|s| end_str.ends_with(s)),
|
||||
Pat::OwnedMultiStr(texts) => texts.iter().any(|s| end_str.ends_with(s)),
|
||||
Pat::Sym(sym) => end_str.ends_with(sym.as_str()),
|
||||
Pat::Num => end_str.as_bytes().last().map_or(false, u8::is_ascii_hexdigit),
|
||||
Pat::Num => end_str.as_bytes().last().is_some_and(u8::is_ascii_hexdigit),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -258,7 +258,7 @@ impl HirEqInterExpr<'_, '_, '_> {
|
||||
}
|
||||
|
||||
fn should_ignore(&mut self, expr: &Expr<'_>) -> bool {
|
||||
macro_backtrace(expr.span).last().map_or(false, |macro_call| {
|
||||
macro_backtrace(expr.span).last().is_some_and(|macro_call| {
|
||||
matches!(
|
||||
&self.inner.cx.tcx.get_diagnostic_name(macro_call.def_id),
|
||||
Some(sym::todo_macro | sym::unimplemented_macro)
|
||||
@ -322,7 +322,7 @@ impl HirEqInterExpr<'_, '_, '_> {
|
||||
(&ExprKind::Block(l, _), &ExprKind::Block(r, _)) => self.eq_block(l, r),
|
||||
(&ExprKind::Binary(l_op, ll, lr), &ExprKind::Binary(r_op, rl, rr)) => {
|
||||
l_op.node == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
|
||||
|| swap_binop(l_op.node, ll, lr).map_or(false, |(l_op, ll, lr)| {
|
||||
|| swap_binop(l_op.node, ll, lr).is_some_and(|(l_op, ll, lr)| {
|
||||
l_op == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
|
||||
})
|
||||
},
|
||||
@ -444,7 +444,7 @@ impl HirEqInterExpr<'_, '_, '_> {
|
||||
) => false,
|
||||
};
|
||||
(is_eq && (!self.should_ignore(left) || !self.should_ignore(right)))
|
||||
|| self.inner.expr_fallback.as_mut().map_or(false, |f| f(left, right))
|
||||
|| self.inner.expr_fallback.as_mut().is_some_and(|f| f(left, right))
|
||||
}
|
||||
|
||||
fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool {
|
||||
@ -724,7 +724,7 @@ fn swap_binop<'a>(
|
||||
/// `eq_fn`.
|
||||
pub fn both<X>(l: Option<&X>, r: Option<&X>, mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
|
||||
l.as_ref()
|
||||
.map_or_else(|| r.is_none(), |x| r.as_ref().map_or(false, |y| eq_fn(x, y)))
|
||||
.map_or_else(|| r.is_none(), |x| r.as_ref().is_some_and(|y| eq_fn(x, y)))
|
||||
}
|
||||
|
||||
/// Checks if two slices are equal as per `eq_fn`.
|
||||
|
@ -342,10 +342,9 @@ pub fn is_ty_alias(qpath: &QPath<'_>) -> bool {
|
||||
/// Checks if the method call given in `expr` belongs to the given trait.
|
||||
/// This is a deprecated function, consider using [`is_trait_method`].
|
||||
pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool {
|
||||
cx.typeck_results()
|
||||
.type_dependent_def_id(expr.hir_id)
|
||||
.and_then(|defid| cx.tcx.trait_of_item(defid))
|
||||
.map_or(false, |trt_id| match_def_path(cx, trt_id, path))
|
||||
let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
|
||||
let trt_id = cx.tcx.trait_of_item(def_id);
|
||||
trt_id.is_some_and(|trt_id| match_def_path(cx, trt_id, path))
|
||||
}
|
||||
|
||||
/// Checks if the given method call expression calls an inherent method.
|
||||
@ -379,7 +378,7 @@ pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol
|
||||
pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
|
||||
cx.typeck_results()
|
||||
.type_dependent_def_id(expr.hir_id)
|
||||
.map_or(false, |did| is_diag_trait_item(cx, did, diag_item))
|
||||
.is_some_and(|did| is_diag_trait_item(cx, did, diag_item))
|
||||
}
|
||||
|
||||
/// Checks if the `def_id` belongs to a function that is part of a trait impl.
|
||||
@ -406,7 +405,7 @@ pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -
|
||||
if let ExprKind::Path(ref qpath) = expr.kind {
|
||||
cx.qpath_res(qpath, expr.hir_id)
|
||||
.opt_def_id()
|
||||
.map_or(false, |def_id| is_diag_trait_item(cx, def_id, diag_item))
|
||||
.is_some_and(|def_id| is_diag_trait_item(cx, def_id, diag_item))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
@ -466,13 +465,13 @@ pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
|
||||
///
|
||||
/// Please use `is_path_diagnostic_item` if the target is a diagnostic item.
|
||||
pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool {
|
||||
path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, segments))
|
||||
path_def_id(cx, expr).is_some_and(|id| match_def_path(cx, id, segments))
|
||||
}
|
||||
|
||||
/// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if
|
||||
/// it matches the given lang item.
|
||||
pub fn is_path_lang_item<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>, lang_item: LangItem) -> bool {
|
||||
path_def_id(cx, maybe_path).map_or(false, |id| cx.tcx.lang_items().get(lang_item) == Some(id))
|
||||
path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.lang_items().get(lang_item) == Some(id))
|
||||
}
|
||||
|
||||
/// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if
|
||||
@ -482,7 +481,7 @@ pub fn is_path_diagnostic_item<'tcx>(
|
||||
maybe_path: &impl MaybePath<'tcx>,
|
||||
diag_item: Symbol,
|
||||
) -> bool {
|
||||
path_def_id(cx, maybe_path).map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id))
|
||||
path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.is_diagnostic_item(diag_item, id))
|
||||
}
|
||||
|
||||
/// THIS METHOD IS DEPRECATED. Matches a `Path` against a slice of segment string literals.
|
||||
@ -1315,7 +1314,7 @@ pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option<Vec
|
||||
pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
|
||||
cx.tcx
|
||||
.entry_fn(())
|
||||
.map_or(false, |(entry_fn_def_id, _)| def_id == entry_fn_def_id)
|
||||
.is_some_and(|(entry_fn_def_id, _)| def_id == entry_fn_def_id)
|
||||
}
|
||||
|
||||
/// Returns `true` if the expression is in the program's `#[panic_handler]`.
|
||||
@ -1753,8 +1752,8 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
|
||||
|
||||
match pat.kind {
|
||||
PatKind::Wild | PatKind::Never => false, // If `!` typechecked then the type is empty, so not refutable.
|
||||
PatKind::Binding(_, _, _, pat) => pat.map_or(false, |pat| is_refutable(cx, pat)),
|
||||
PatKind::Box(pat) | PatKind::Deref(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
|
||||
PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)),
|
||||
PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
|
||||
PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id),
|
||||
PatKind::Or(pats) => {
|
||||
// TODO: should be the honest check, that pats is exhaustive set
|
||||
@ -1778,7 +1777,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
|
||||
},
|
||||
}
|
||||
},
|
||||
PatKind::Lit(..) | PatKind::Range(..) | PatKind::Err(_) => true,
|
||||
PatKind::Lit(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) => true,
|
||||
}
|
||||
}
|
||||
|
||||
@ -2021,7 +2020,7 @@ pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool {
|
||||
let path = cx.get_def_path(did);
|
||||
// libc is meant to be used as a flat list of names, but they're all actually defined in different
|
||||
// modules based on the target platform. Ignore everything but crate name and the item name.
|
||||
path.first().map_or(false, |s| s.as_str() == "libc") && path.last().map_or(false, |s| s.as_str() == name)
|
||||
path.first().is_some_and(|s| s.as_str() == "libc") && path.last().is_some_and(|s| s.as_str() == name)
|
||||
}
|
||||
|
||||
/// Returns the list of condition expressions and the list of blocks in a
|
||||
@ -2104,7 +2103,7 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
_ => None,
|
||||
};
|
||||
|
||||
did.map_or(false, |did| cx.tcx.has_attr(did, sym::must_use))
|
||||
did.is_some_and(|did| cx.tcx.has_attr(did, sym::must_use))
|
||||
}
|
||||
|
||||
/// Checks if a function's body represents the identity function. Looks for bodies of the form:
|
||||
@ -2211,7 +2210,7 @@ pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>)
|
||||
pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
match expr.kind {
|
||||
ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir().body(body)),
|
||||
_ => path_def_id(cx, expr).map_or(false, |id| cx.tcx.is_diagnostic_item(sym::convert_identity, id)),
|
||||
_ => path_def_id(cx, expr).is_some_and(|id| cx.tcx.is_diagnostic_item(sym::convert_identity, id)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ impl<'a> NumericLiteral<'a> {
|
||||
.trim_start()
|
||||
.chars()
|
||||
.next()
|
||||
.map_or(false, |c| c.is_ascii_digit())
|
||||
.is_some_and(|c| c.is_ascii_digit())
|
||||
{
|
||||
let (unsuffixed, suffix) = split_suffix(src, lit_kind);
|
||||
let float = matches!(lit_kind, LitKind::Float(..));
|
||||
|
@ -905,7 +905,7 @@ impl<'tcx> DerefDelegate<'_, 'tcx> {
|
||||
_ => return false,
|
||||
};
|
||||
|
||||
ty.map_or(false, |ty| matches!(ty.kind(), ty::Ref(_, inner, _) if inner.is_ref()))
|
||||
ty.is_some_and(|ty| matches!(ty.kind(), ty::Ref(_, inner, _) if inner.is_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@ pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
pub fn has_debug_impl<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
cx.tcx
|
||||
.get_diagnostic_item(sym::Debug)
|
||||
.map_or(false, |debug| implements_trait(cx, ty, debug, &[]))
|
||||
.is_some_and(|debug| implements_trait(cx, ty, debug, &[]))
|
||||
}
|
||||
|
||||
/// Checks whether a type can be partially moved.
|
||||
@ -487,7 +487,7 @@ pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
.tcx
|
||||
.lang_items()
|
||||
.drop_trait()
|
||||
.map_or(false, |id| implements_trait(cx, ty, id, &[]))
|
||||
.is_some_and(|id| implements_trait(cx, ty, id, &[]))
|
||||
{
|
||||
// This type doesn't implement drop, so no side effects here.
|
||||
// Check if any component type has any.
|
||||
@ -718,7 +718,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'t
|
||||
{
|
||||
let output = bounds
|
||||
.projection_bounds()
|
||||
.find(|p| lang_items.fn_once_output().map_or(false, |id| id == p.item_def_id()))
|
||||
.find(|p| lang_items.fn_once_output().is_some_and(|id| id == p.item_def_id()))
|
||||
.map(|p| p.map_bound(|p| p.term.expect_type()));
|
||||
Some(ExprFnSig::Trait(bound.map_bound(|b| b.args.type_at(0)), output, None))
|
||||
},
|
||||
@ -753,7 +753,7 @@ fn sig_from_bounds<'tcx>(
|
||||
&& p.self_ty() == ty =>
|
||||
{
|
||||
let i = pred.kind().rebind(p.trait_ref.args.type_at(1));
|
||||
if inputs.map_or(false, |inputs| i != inputs) {
|
||||
if inputs.is_some_and(|inputs| i != inputs) {
|
||||
// Multiple different fn trait impls. Is this even allowed?
|
||||
return None;
|
||||
}
|
||||
@ -794,7 +794,7 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: AliasTy<'tcx>) -> Option
|
||||
{
|
||||
let i = pred.kind().rebind(p.trait_ref.args.type_at(1));
|
||||
|
||||
if inputs.map_or(false, |inputs| inputs != i) {
|
||||
if inputs.is_some_and(|inputs| inputs != i) {
|
||||
// Multiple different fn trait impls. Is this even allowed?
|
||||
return None;
|
||||
}
|
||||
@ -1291,7 +1291,7 @@ pub fn normalize_with_regions<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>
|
||||
|
||||
/// Checks if the type is `core::mem::ManuallyDrop<_>`
|
||||
pub fn is_manually_drop(ty: Ty<'_>) -> bool {
|
||||
ty.ty_adt_def().map_or(false, AdtDef::is_manually_drop)
|
||||
ty.ty_adt_def().is_some_and(AdtDef::is_manually_drop)
|
||||
}
|
||||
|
||||
/// Returns the deref chain of a type, starting with the type itself.
|
||||
|
@ -344,13 +344,13 @@ pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) ->
|
||||
.cx
|
||||
.qpath_res(p, hir_id)
|
||||
.opt_def_id()
|
||||
.map_or(false, |id| self.cx.tcx.is_const_fn(id)) => {},
|
||||
.is_some_and(|id| self.cx.tcx.is_const_fn(id)) => {},
|
||||
ExprKind::MethodCall(..)
|
||||
if self
|
||||
.cx
|
||||
.typeck_results()
|
||||
.type_dependent_def_id(e.hir_id)
|
||||
.map_or(false, |id| self.cx.tcx.is_const_fn(id)) => {},
|
||||
.is_some_and(|id| self.cx.tcx.is_const_fn(id)) => {},
|
||||
ExprKind::Binary(_, lhs, rhs)
|
||||
if self.cx.typeck_results().expr_ty(lhs).peel_refs().is_primitive_ty()
|
||||
&& self.cx.typeck_results().expr_ty(rhs).peel_refs().is_primitive_ty() => {},
|
||||
@ -426,9 +426,7 @@ pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
|
||||
.cx
|
||||
.typeck_results()
|
||||
.type_dependent_def_id(e.hir_id)
|
||||
.map_or(false, |id| {
|
||||
self.cx.tcx.fn_sig(id).skip_binder().safety() == Safety::Unsafe
|
||||
}) =>
|
||||
.is_some_and(|id| self.cx.tcx.fn_sig(id).skip_binder().safety() == Safety::Unsafe) =>
|
||||
{
|
||||
ControlFlow::Break(())
|
||||
},
|
||||
@ -444,7 +442,7 @@ pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
|
||||
.cx
|
||||
.qpath_res(p, e.hir_id)
|
||||
.opt_def_id()
|
||||
.map_or(false, |id| self.cx.tcx.is_mutable_static(id)) =>
|
||||
.is_some_and(|id| self.cx.tcx.is_mutable_static(id)) =>
|
||||
{
|
||||
ControlFlow::Break(())
|
||||
},
|
||||
|
@ -1,4 +1,4 @@
|
||||
[toolchain]
|
||||
channel = "nightly-2024-11-07"
|
||||
channel = "nightly-2024-11-14"
|
||||
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
|
||||
profile = "minimal"
|
||||
|
@ -1,6 +1,7 @@
|
||||
//@aux-build:paths.rs
|
||||
#![deny(clippy::internal)]
|
||||
#![feature(rustc_private)]
|
||||
#![allow(clippy::unnecessary_map_or)]
|
||||
|
||||
extern crate clippy_utils;
|
||||
extern crate paths;
|
||||
|
@ -1,6 +1,7 @@
|
||||
//@aux-build:paths.rs
|
||||
#![deny(clippy::internal)]
|
||||
#![feature(rustc_private)]
|
||||
#![allow(clippy::unnecessary_map_or)]
|
||||
|
||||
extern crate clippy_utils;
|
||||
extern crate paths;
|
||||
|
@ -1,5 +1,5 @@
|
||||
error: use of a def path to a diagnostic item
|
||||
--> tests/ui-internal/unnecessary_def_path.rs:36:13
|
||||
--> tests/ui-internal/unnecessary_def_path.rs:37:13
|
||||
|
|
||||
LL | let _ = match_type(cx, ty, &OPTION);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)`
|
||||
@ -12,61 +12,61 @@ LL | #![deny(clippy::internal)]
|
||||
= note: `#[deny(clippy::unnecessary_def_path)]` implied by `#[deny(clippy::internal)]`
|
||||
|
||||
error: use of a def path to a diagnostic item
|
||||
--> tests/ui-internal/unnecessary_def_path.rs:37:13
|
||||
--> tests/ui-internal/unnecessary_def_path.rs:38:13
|
||||
|
|
||||
LL | let _ = match_type(cx, ty, RESULT);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)`
|
||||
|
||||
error: use of a def path to a diagnostic item
|
||||
--> tests/ui-internal/unnecessary_def_path.rs:38:13
|
||||
--> tests/ui-internal/unnecessary_def_path.rs:39:13
|
||||
|
|
||||
LL | let _ = match_type(cx, ty, &["core", "result", "Result"]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)`
|
||||
|
||||
error: use of a def path to a diagnostic item
|
||||
--> tests/ui-internal/unnecessary_def_path.rs:42:13
|
||||
--> tests/ui-internal/unnecessary_def_path.rs:43:13
|
||||
|
|
||||
LL | let _ = clippy_utils::ty::match_type(cx, ty, rc_path);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Rc)`
|
||||
|
||||
error: use of a def path to a diagnostic item
|
||||
--> tests/ui-internal/unnecessary_def_path.rs:44:13
|
||||
--> tests/ui-internal/unnecessary_def_path.rs:45:13
|
||||
|
|
||||
LL | let _ = match_type(cx, ty, &paths::OPTION);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)`
|
||||
|
||||
error: use of a def path to a diagnostic item
|
||||
--> tests/ui-internal/unnecessary_def_path.rs:45:13
|
||||
--> tests/ui-internal/unnecessary_def_path.rs:46:13
|
||||
|
|
||||
LL | let _ = match_type(cx, ty, paths::RESULT);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)`
|
||||
|
||||
error: use of a def path to a `LangItem`
|
||||
--> tests/ui-internal/unnecessary_def_path.rs:47:13
|
||||
--> tests/ui-internal/unnecessary_def_path.rs:48:13
|
||||
|
|
||||
LL | let _ = match_type(cx, ty, &["alloc", "boxed", "Box"]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_lang_item(cx, ty, LangItem::OwnedBox)`
|
||||
|
||||
error: use of a def path to a diagnostic item
|
||||
--> tests/ui-internal/unnecessary_def_path.rs:48:13
|
||||
--> tests/ui-internal/unnecessary_def_path.rs:49:13
|
||||
|
|
||||
LL | let _ = match_type(cx, ty, &["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit)`
|
||||
|
||||
error: use of a def path to a `LangItem`
|
||||
--> tests/ui-internal/unnecessary_def_path.rs:50:13
|
||||
--> tests/ui-internal/unnecessary_def_path.rs:51:13
|
||||
|
|
||||
LL | let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OwnedBox) == Some(did)`
|
||||
|
||||
error: use of a def path to a diagnostic item
|
||||
--> tests/ui-internal/unnecessary_def_path.rs:51:13
|
||||
--> tests/ui-internal/unnecessary_def_path.rs:52:13
|
||||
|
|
||||
LL | let _ = match_def_path(cx, did, &["core", "option", "Option"]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.is_diagnostic_item(sym::Option, did)`
|
||||
|
||||
error: use of a def path to a `LangItem`
|
||||
--> tests/ui-internal/unnecessary_def_path.rs:52:13
|
||||
--> tests/ui-internal/unnecessary_def_path.rs:53:13
|
||||
|
|
||||
LL | let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OptionSome) == Some(did)`
|
||||
@ -74,25 +74,25 @@ LL | let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]);
|
||||
= help: if this `DefId` came from a constructor expression or pattern then the parent `DefId` should be used instead
|
||||
|
||||
error: use of a def path to a diagnostic item
|
||||
--> tests/ui-internal/unnecessary_def_path.rs:54:13
|
||||
--> tests/ui-internal/unnecessary_def_path.rs:55:13
|
||||
|
|
||||
LL | let _ = match_trait_method(cx, expr, &["core", "convert", "AsRef"]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_trait_method(cx, expr, sym::AsRef)`
|
||||
|
||||
error: use of a def path to a diagnostic item
|
||||
--> tests/ui-internal/unnecessary_def_path.rs:56:13
|
||||
--> tests/ui-internal/unnecessary_def_path.rs:57:13
|
||||
|
|
||||
LL | let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option"]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_path_diagnostic_item(cx, expr, sym::Option)`
|
||||
|
||||
error: use of a def path to a `LangItem`
|
||||
--> tests/ui-internal/unnecessary_def_path.rs:57:13
|
||||
--> tests/ui-internal/unnecessary_def_path.rs:58:13
|
||||
|
|
||||
LL | let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().get(LangItem::IteratorNext) == Some(id))`
|
||||
|
||||
error: use of a def path to a `LangItem`
|
||||
--> tests/ui-internal/unnecessary_def_path.rs:58:13
|
||||
--> tests/ui-internal/unnecessary_def_path.rs:59:13
|
||||
|
|
||||
LL | let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option", "Some"]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome)`
|
||||
|
@ -15,5 +15,9 @@ const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt");
|
||||
const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt");
|
||||
//~^ large_include_file
|
||||
|
||||
#[doc = include_str!("too_big.txt")] //~ large_include_file
|
||||
#[doc = include_str!("too_big.txt")]
|
||||
//~^ large_include_file
|
||||
// Should not lint!
|
||||
// Regression test for <https://github.com/rust-lang/rust-clippy/issues/13670>.
|
||||
#[doc = include_str!("empty.txt")]
|
||||
fn main() {}
|
||||
|
@ -1,4 +1,5 @@
|
||||
#![warn(clippy::case_sensitive_file_extension_comparisons)]
|
||||
#![allow(clippy::unnecessary_map_or)]
|
||||
|
||||
use std::string::String;
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
#![warn(clippy::case_sensitive_file_extension_comparisons)]
|
||||
#![allow(clippy::unnecessary_map_or)]
|
||||
|
||||
use std::string::String;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
error: case-sensitive file extension comparison
|
||||
--> tests/ui/case_sensitive_file_extension_comparisons.rs:13:5
|
||||
--> tests/ui/case_sensitive_file_extension_comparisons.rs:14:5
|
||||
|
|
||||
LL | filename.ends_with(".rs")
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -15,7 +15,7 @@ LL + .map_or(false, |ext| ext.eq_ignore_ascii_case("rs"))
|
||||
|
|
||||
|
||||
error: case-sensitive file extension comparison
|
||||
--> tests/ui/case_sensitive_file_extension_comparisons.rs:18:13
|
||||
--> tests/ui/case_sensitive_file_extension_comparisons.rs:19:13
|
||||
|
|
||||
LL | let _ = String::new().ends_with(".ext12");
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -29,7 +29,7 @@ LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12"));
|
||||
|
|
||||
|
||||
error: case-sensitive file extension comparison
|
||||
--> tests/ui/case_sensitive_file_extension_comparisons.rs:19:13
|
||||
--> tests/ui/case_sensitive_file_extension_comparisons.rs:20:13
|
||||
|
|
||||
LL | let _ = "str".ends_with(".ext12");
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -43,7 +43,7 @@ LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12"));
|
||||
|
|
||||
|
||||
error: case-sensitive file extension comparison
|
||||
--> tests/ui/case_sensitive_file_extension_comparisons.rs:23:17
|
||||
--> tests/ui/case_sensitive_file_extension_comparisons.rs:24:17
|
||||
|
|
||||
LL | let _ = "str".ends_with(".ext12");
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -57,7 +57,7 @@ LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12"));
|
||||
|
|
||||
|
||||
error: case-sensitive file extension comparison
|
||||
--> tests/ui/case_sensitive_file_extension_comparisons.rs:30:13
|
||||
--> tests/ui/case_sensitive_file_extension_comparisons.rs:31:13
|
||||
|
|
||||
LL | let _ = String::new().ends_with(".EXT12");
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -71,7 +71,7 @@ LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12"));
|
||||
|
|
||||
|
||||
error: case-sensitive file extension comparison
|
||||
--> tests/ui/case_sensitive_file_extension_comparisons.rs:31:13
|
||||
--> tests/ui/case_sensitive_file_extension_comparisons.rs:32:13
|
||||
|
|
||||
LL | let _ = "str".ends_with(".EXT12");
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -116,7 +116,7 @@ fn main() {
|
||||
//~^ ERROR: this operation has no effect
|
||||
(match a { 0 => 10, _ => 20 }) + if b { 3 } else { 4 };
|
||||
//~^ ERROR: this operation has no effect
|
||||
(if b { 1 } else { 2 });
|
||||
((if b { 1 } else { 2 }));
|
||||
//~^ ERROR: this operation has no effect
|
||||
|
||||
({ a }) + 3;
|
||||
@ -212,3 +212,47 @@ fn issue_12050() {
|
||||
//~^ ERROR: this operation has no effect
|
||||
}
|
||||
}
|
||||
|
||||
fn issue_13470() {
|
||||
let x = 1i32;
|
||||
let y = 1i32;
|
||||
// Removes the + 0i32 while keeping the parentheses around x + y so the cast operation works
|
||||
let _: u64 = (x + y) as u64;
|
||||
//~^ ERROR: this operation has no effect
|
||||
// both of the next two lines should look the same after rustfix
|
||||
let _: u64 = 1u64 & (x + y) as u64;
|
||||
//~^ ERROR: this operation has no effect
|
||||
// Same as above, but with extra redundant parenthesis
|
||||
let _: u64 = 1u64 & ((x + y)) as u64;
|
||||
//~^ ERROR: this operation has no effect
|
||||
// Should maintain parenthesis even if the surrounding expr has the same precedence
|
||||
let _: u64 = 5u64 + ((x + y)) as u64;
|
||||
//~^ ERROR: this operation has no effect
|
||||
|
||||
// If we don't maintain the parens here, the behavior changes
|
||||
let _ = -(x + y);
|
||||
//~^ ERROR: this operation has no effect
|
||||
// Similarly, we need to maintain parens here
|
||||
let _ = -(x / y);
|
||||
//~^ ERROR: this operation has no effect
|
||||
// Maintain parenthesis if the parent expr is of higher precedence
|
||||
let _ = 2i32 * (x + y);
|
||||
//~^ ERROR: this operation has no effect
|
||||
// Maintain parenthesis if the parent expr is the same precedence
|
||||
// as not all operations are associative
|
||||
let _ = 2i32 - (x - y);
|
||||
//~^ ERROR: this operation has no effect
|
||||
// But make sure that inner parens still exist
|
||||
let z = 1i32;
|
||||
let _ = 2 + (x + (y * z));
|
||||
//~^ ERROR: this operation has no effect
|
||||
// Maintain parenthesis if the parent expr is of lower precedence
|
||||
// This is for clarity, and clippy will not warn on these being unnecessary
|
||||
let _ = 2i32 + (x * y);
|
||||
//~^ ERROR: this operation has no effect
|
||||
|
||||
let x = 1i16;
|
||||
let y = 1i16;
|
||||
let _: u64 = 1u64 + ((x as i32 + y as i32) as u64);
|
||||
//~^ ERROR: this operation has no effect
|
||||
}
|
||||
|
@ -212,3 +212,47 @@ fn issue_12050() {
|
||||
//~^ ERROR: this operation has no effect
|
||||
}
|
||||
}
|
||||
|
||||
fn issue_13470() {
|
||||
let x = 1i32;
|
||||
let y = 1i32;
|
||||
// Removes the + 0i32 while keeping the parentheses around x + y so the cast operation works
|
||||
let _: u64 = (x + y + 0i32) as u64;
|
||||
//~^ ERROR: this operation has no effect
|
||||
// both of the next two lines should look the same after rustfix
|
||||
let _: u64 = 1u64 & (x + y + 0i32) as u64;
|
||||
//~^ ERROR: this operation has no effect
|
||||
// Same as above, but with extra redundant parenthesis
|
||||
let _: u64 = 1u64 & ((x + y) + 0i32) as u64;
|
||||
//~^ ERROR: this operation has no effect
|
||||
// Should maintain parenthesis even if the surrounding expr has the same precedence
|
||||
let _: u64 = 5u64 + ((x + y) + 0i32) as u64;
|
||||
//~^ ERROR: this operation has no effect
|
||||
|
||||
// If we don't maintain the parens here, the behavior changes
|
||||
let _ = -(x + y + 0i32);
|
||||
//~^ ERROR: this operation has no effect
|
||||
// Similarly, we need to maintain parens here
|
||||
let _ = -(x / y / 1i32);
|
||||
//~^ ERROR: this operation has no effect
|
||||
// Maintain parenthesis if the parent expr is of higher precedence
|
||||
let _ = 2i32 * (x + y + 0i32);
|
||||
//~^ ERROR: this operation has no effect
|
||||
// Maintain parenthesis if the parent expr is the same precedence
|
||||
// as not all operations are associative
|
||||
let _ = 2i32 - (x - y - 0i32);
|
||||
//~^ ERROR: this operation has no effect
|
||||
// But make sure that inner parens still exist
|
||||
let z = 1i32;
|
||||
let _ = 2 + (x + (y * z) + 0);
|
||||
//~^ ERROR: this operation has no effect
|
||||
// Maintain parenthesis if the parent expr is of lower precedence
|
||||
// This is for clarity, and clippy will not warn on these being unnecessary
|
||||
let _ = 2i32 + (x * y * 1i32);
|
||||
//~^ ERROR: this operation has no effect
|
||||
|
||||
let x = 1i16;
|
||||
let y = 1i16;
|
||||
let _: u64 = 1u64 + ((x as i32 + y as i32) as u64 + 0u64);
|
||||
//~^ ERROR: this operation has no effect
|
||||
}
|
||||
|
@ -149,7 +149,7 @@ error: this operation has no effect
|
||||
--> tests/ui/identity_op.rs:119:5
|
||||
|
|
||||
LL | (if b { 1 } else { 2 }) + 0;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `((if b { 1 } else { 2 }))`
|
||||
|
||||
error: this operation has no effect
|
||||
--> tests/ui/identity_op.rs:122:5
|
||||
@ -313,5 +313,71 @@ error: this operation has no effect
|
||||
LL | let _: i32 = **&&*&x + 0;
|
||||
| ^^^^^^^^^^^ help: consider reducing it to: `***&&*&x`
|
||||
|
||||
error: aborting due to 52 previous errors
|
||||
error: this operation has no effect
|
||||
--> tests/ui/identity_op.rs:220:18
|
||||
|
|
||||
LL | let _: u64 = (x + y + 0i32) as u64;
|
||||
| ^^^^^^^^^^^^^^ help: consider reducing it to: `(x + y)`
|
||||
|
||||
error: this operation has no effect
|
||||
--> tests/ui/identity_op.rs:223:25
|
||||
|
|
||||
LL | let _: u64 = 1u64 & (x + y + 0i32) as u64;
|
||||
| ^^^^^^^^^^^^^^ help: consider reducing it to: `(x + y)`
|
||||
|
||||
error: this operation has no effect
|
||||
--> tests/ui/identity_op.rs:226:25
|
||||
|
|
||||
LL | let _: u64 = 1u64 & ((x + y) + 0i32) as u64;
|
||||
| ^^^^^^^^^^^^^^^^ help: consider reducing it to: `((x + y))`
|
||||
|
||||
error: this operation has no effect
|
||||
--> tests/ui/identity_op.rs:229:25
|
||||
|
|
||||
LL | let _: u64 = 5u64 + ((x + y) + 0i32) as u64;
|
||||
| ^^^^^^^^^^^^^^^^ help: consider reducing it to: `((x + y))`
|
||||
|
||||
error: this operation has no effect
|
||||
--> tests/ui/identity_op.rs:233:14
|
||||
|
|
||||
LL | let _ = -(x + y + 0i32);
|
||||
| ^^^^^^^^^^^^^^ help: consider reducing it to: `(x + y)`
|
||||
|
||||
error: this operation has no effect
|
||||
--> tests/ui/identity_op.rs:236:14
|
||||
|
|
||||
LL | let _ = -(x / y / 1i32);
|
||||
| ^^^^^^^^^^^^^^ help: consider reducing it to: `(x / y)`
|
||||
|
||||
error: this operation has no effect
|
||||
--> tests/ui/identity_op.rs:239:20
|
||||
|
|
||||
LL | let _ = 2i32 * (x + y + 0i32);
|
||||
| ^^^^^^^^^^^^^^ help: consider reducing it to: `(x + y)`
|
||||
|
||||
error: this operation has no effect
|
||||
--> tests/ui/identity_op.rs:243:20
|
||||
|
|
||||
LL | let _ = 2i32 - (x - y - 0i32);
|
||||
| ^^^^^^^^^^^^^^ help: consider reducing it to: `(x - y)`
|
||||
|
||||
error: this operation has no effect
|
||||
--> tests/ui/identity_op.rs:247:17
|
||||
|
|
||||
LL | let _ = 2 + (x + (y * z) + 0);
|
||||
| ^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(x + (y * z))`
|
||||
|
||||
error: this operation has no effect
|
||||
--> tests/ui/identity_op.rs:251:20
|
||||
|
|
||||
LL | let _ = 2i32 + (x * y * 1i32);
|
||||
| ^^^^^^^^^^^^^^ help: consider reducing it to: `(x * y)`
|
||||
|
||||
error: this operation has no effect
|
||||
--> tests/ui/identity_op.rs:256:25
|
||||
|
|
||||
LL | let _: u64 = 1u64 + ((x as i32 + y as i32) as u64 + 0u64);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `((x as i32 + y as i32) as u64)`
|
||||
|
||||
error: aborting due to 63 previous errors
|
||||
|
||||
|
@ -96,12 +96,42 @@ impl MoveStruct {
|
||||
}
|
||||
|
||||
fn func() -> Option<i32> {
|
||||
macro_rules! opt_none {
|
||||
() => {
|
||||
None
|
||||
};
|
||||
}
|
||||
|
||||
fn f() -> Option<String> {
|
||||
Some(String::new())
|
||||
}
|
||||
|
||||
f()?;
|
||||
|
||||
let _val = f()?;
|
||||
|
||||
let s: &str = match &Some(String::new()) {
|
||||
Some(v) => v,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
f()?;
|
||||
|
||||
opt_none!()?;
|
||||
|
||||
match f() {
|
||||
Some(x) => x,
|
||||
None => return opt_none!(),
|
||||
};
|
||||
|
||||
match f() {
|
||||
Some(val) => {
|
||||
println!("{val}");
|
||||
val
|
||||
},
|
||||
None => return None,
|
||||
};
|
||||
|
||||
Some(0)
|
||||
}
|
||||
|
||||
@ -114,6 +144,10 @@ fn result_func(x: Result<i32, i32>) -> Result<i32, i32> {
|
||||
|
||||
x?;
|
||||
|
||||
let _val = func_returning_result()?;
|
||||
|
||||
func_returning_result()?;
|
||||
|
||||
// No warning
|
||||
let y = if let Ok(x) = x {
|
||||
x
|
||||
@ -157,6 +191,28 @@ fn result_func(x: Result<i32, i32>) -> Result<i32, i32> {
|
||||
Ok(y)
|
||||
}
|
||||
|
||||
fn infer_check() {
|
||||
let closure = |x: Result<u8, ()>| {
|
||||
// `?` would fail here, as it expands to `Err(val.into())` which is not constrained.
|
||||
let _val = match x {
|
||||
Ok(val) => val,
|
||||
Err(val) => return Err(val),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
||||
let closure = |x: Result<u8, ()>| -> Result<(), _> {
|
||||
// `?` would fail here, as it expands to `Err(val.into())` which is not constrained.
|
||||
let _val = match x {
|
||||
Ok(val) => val,
|
||||
Err(val) => return Err(val),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
};
|
||||
}
|
||||
|
||||
// see issue #8019
|
||||
pub enum NotOption {
|
||||
None,
|
||||
|
@ -124,6 +124,12 @@ impl MoveStruct {
|
||||
}
|
||||
|
||||
fn func() -> Option<i32> {
|
||||
macro_rules! opt_none {
|
||||
() => {
|
||||
None
|
||||
};
|
||||
}
|
||||
|
||||
fn f() -> Option<String> {
|
||||
Some(String::new())
|
||||
}
|
||||
@ -132,6 +138,39 @@ fn func() -> Option<i32> {
|
||||
return None;
|
||||
}
|
||||
|
||||
let _val = match f() {
|
||||
Some(val) => val,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
let s: &str = match &Some(String::new()) {
|
||||
Some(v) => v,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
match f() {
|
||||
Some(val) => val,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
match opt_none!() {
|
||||
Some(x) => x,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
match f() {
|
||||
Some(x) => x,
|
||||
None => return opt_none!(),
|
||||
};
|
||||
|
||||
match f() {
|
||||
Some(val) => {
|
||||
println!("{val}");
|
||||
val
|
||||
},
|
||||
None => return None,
|
||||
};
|
||||
|
||||
Some(0)
|
||||
}
|
||||
|
||||
@ -146,6 +185,16 @@ fn result_func(x: Result<i32, i32>) -> Result<i32, i32> {
|
||||
return x;
|
||||
}
|
||||
|
||||
let _val = match func_returning_result() {
|
||||
Ok(val) => val,
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
|
||||
match func_returning_result() {
|
||||
Ok(val) => val,
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
|
||||
// No warning
|
||||
let y = if let Ok(x) = x {
|
||||
x
|
||||
@ -189,6 +238,28 @@ fn result_func(x: Result<i32, i32>) -> Result<i32, i32> {
|
||||
Ok(y)
|
||||
}
|
||||
|
||||
fn infer_check() {
|
||||
let closure = |x: Result<u8, ()>| {
|
||||
// `?` would fail here, as it expands to `Err(val.into())` which is not constrained.
|
||||
let _val = match x {
|
||||
Ok(val) => val,
|
||||
Err(val) => return Err(val),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
||||
let closure = |x: Result<u8, ()>| -> Result<(), _> {
|
||||
// `?` would fail here, as it expands to `Err(val.into())` which is not constrained.
|
||||
let _val = match x {
|
||||
Ok(val) => val,
|
||||
Err(val) => return Err(val),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
};
|
||||
}
|
||||
|
||||
// see issue #8019
|
||||
pub enum NotOption {
|
||||
None,
|
||||
|
@ -94,29 +94,76 @@ LL | | };
|
||||
| |_________^ help: replace it with: `self.opt?`
|
||||
|
||||
error: this block may be rewritten with the `?` operator
|
||||
--> tests/ui/question_mark.rs:131:5
|
||||
--> tests/ui/question_mark.rs:137:5
|
||||
|
|
||||
LL | / if f().is_none() {
|
||||
LL | | return None;
|
||||
LL | | }
|
||||
| |_____^ help: replace it with: `f()?;`
|
||||
|
||||
error: this `match` expression can be replaced with `?`
|
||||
--> tests/ui/question_mark.rs:141:16
|
||||
|
|
||||
LL | let _val = match f() {
|
||||
| ________________^
|
||||
LL | | Some(val) => val,
|
||||
LL | | None => return None,
|
||||
LL | | };
|
||||
| |_____^ help: try instead: `f()?`
|
||||
|
||||
error: this `match` expression can be replaced with `?`
|
||||
--> tests/ui/question_mark.rs:151:5
|
||||
|
|
||||
LL | / match f() {
|
||||
LL | | Some(val) => val,
|
||||
LL | | None => return None,
|
||||
LL | | };
|
||||
| |_____^ help: try instead: `f()?`
|
||||
|
||||
error: this `match` expression can be replaced with `?`
|
||||
--> tests/ui/question_mark.rs:156:5
|
||||
|
|
||||
LL | / match opt_none!() {
|
||||
LL | | Some(x) => x,
|
||||
LL | | None => return None,
|
||||
LL | | };
|
||||
| |_____^ help: try instead: `opt_none!()?`
|
||||
|
||||
error: this block may be rewritten with the `?` operator
|
||||
--> tests/ui/question_mark.rs:143:13
|
||||
--> tests/ui/question_mark.rs:182:13
|
||||
|
|
||||
LL | let _ = if let Ok(x) = x { x } else { return x };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x?`
|
||||
|
||||
error: this block may be rewritten with the `?` operator
|
||||
--> tests/ui/question_mark.rs:145:5
|
||||
--> tests/ui/question_mark.rs:184:5
|
||||
|
|
||||
LL | / if x.is_err() {
|
||||
LL | | return x;
|
||||
LL | | }
|
||||
| |_____^ help: replace it with: `x?;`
|
||||
|
||||
error: this `match` expression can be replaced with `?`
|
||||
--> tests/ui/question_mark.rs:188:16
|
||||
|
|
||||
LL | let _val = match func_returning_result() {
|
||||
| ________________^
|
||||
LL | | Ok(val) => val,
|
||||
LL | | Err(err) => return Err(err),
|
||||
LL | | };
|
||||
| |_____^ help: try instead: `func_returning_result()?`
|
||||
|
||||
error: this `match` expression can be replaced with `?`
|
||||
--> tests/ui/question_mark.rs:193:5
|
||||
|
|
||||
LL | / match func_returning_result() {
|
||||
LL | | Ok(val) => val,
|
||||
LL | | Err(err) => return Err(err),
|
||||
LL | | };
|
||||
| |_____^ help: try instead: `func_returning_result()?`
|
||||
|
||||
error: this block may be rewritten with the `?` operator
|
||||
--> tests/ui/question_mark.rs:213:5
|
||||
--> tests/ui/question_mark.rs:284:5
|
||||
|
|
||||
LL | / if let Err(err) = func_returning_result() {
|
||||
LL | | return Err(err);
|
||||
@ -124,7 +171,7 @@ LL | | }
|
||||
| |_____^ help: replace it with: `func_returning_result()?;`
|
||||
|
||||
error: this block may be rewritten with the `?` operator
|
||||
--> tests/ui/question_mark.rs:220:5
|
||||
--> tests/ui/question_mark.rs:291:5
|
||||
|
|
||||
LL | / if let Err(err) = func_returning_result() {
|
||||
LL | | return Err(err);
|
||||
@ -132,7 +179,7 @@ LL | | }
|
||||
| |_____^ help: replace it with: `func_returning_result()?;`
|
||||
|
||||
error: this block may be rewritten with the `?` operator
|
||||
--> tests/ui/question_mark.rs:297:13
|
||||
--> tests/ui/question_mark.rs:368:13
|
||||
|
|
||||
LL | / if a.is_none() {
|
||||
LL | | return None;
|
||||
@ -142,12 +189,12 @@ LL | | }
|
||||
| |_____________^ help: replace it with: `a?;`
|
||||
|
||||
error: this `let...else` may be rewritten with the `?` operator
|
||||
--> tests/ui/question_mark.rs:357:5
|
||||
--> tests/ui/question_mark.rs:428:5
|
||||
|
|
||||
LL | / let Some(v) = bar.foo.owned.clone() else {
|
||||
LL | | return None;
|
||||
LL | | };
|
||||
| |______^ help: replace it with: `let v = bar.foo.owned.clone()?;`
|
||||
|
||||
error: aborting due to 17 previous errors
|
||||
error: aborting due to 22 previous errors
|
||||
|
||||
|
64
src/tools/clippy/tests/ui/unnecessary_map_or.fixed
Normal file
64
src/tools/clippy/tests/ui/unnecessary_map_or.fixed
Normal file
@ -0,0 +1,64 @@
|
||||
//@aux-build:proc_macros.rs
|
||||
#![warn(clippy::unnecessary_map_or)]
|
||||
#![allow(clippy::no_effect)]
|
||||
#![allow(clippy::eq_op)]
|
||||
#![allow(clippy::unnecessary_lazy_evaluations)]
|
||||
#[clippy::msrv = "1.70.0"]
|
||||
#[macro_use]
|
||||
extern crate proc_macros;
|
||||
|
||||
fn main() {
|
||||
// should trigger
|
||||
let _ = (Some(5) == Some(5));
|
||||
let _ = (Some(5) != Some(5));
|
||||
let _ = (Some(5) == Some(5));
|
||||
let _ = Some(5).is_some_and(|n| {
|
||||
let _ = n;
|
||||
6 >= 5
|
||||
});
|
||||
let _ = Some(vec![5]).is_some_and(|n| n == [5]);
|
||||
let _ = Some(vec![1]).is_some_and(|n| vec![2] == n);
|
||||
let _ = Some(5).is_some_and(|n| n == n);
|
||||
let _ = Some(5).is_some_and(|n| n == if 2 > 1 { n } else { 0 });
|
||||
let _ = Ok::<Vec<i32>, i32>(vec![5]).is_ok_and(|n| n == [5]);
|
||||
let _ = (Ok::<i32, i32>(5) == Ok(5));
|
||||
let _ = (Some(5) == Some(5)).then(|| 1);
|
||||
|
||||
// shouldnt trigger
|
||||
let _ = Some(5).map_or(true, |n| n == 5);
|
||||
let _ = Some(5).map_or(true, |n| 5 == n);
|
||||
macro_rules! x {
|
||||
() => {
|
||||
Some(1)
|
||||
};
|
||||
}
|
||||
// methods lints dont fire on macros
|
||||
let _ = x!().map_or(false, |n| n == 1);
|
||||
let _ = x!().map_or(false, |n| n == vec![1][0]);
|
||||
|
||||
msrv_1_69();
|
||||
|
||||
external! {
|
||||
let _ = Some(5).map_or(false, |n| n == 5);
|
||||
}
|
||||
|
||||
with_span! {
|
||||
let _ = Some(5).map_or(false, |n| n == 5);
|
||||
}
|
||||
|
||||
// check for presence of PartialEq, and alter suggestion to use `is_ok_and` if absent
|
||||
struct S;
|
||||
let r: Result<i32, S> = Ok(3);
|
||||
let _ = r.is_ok_and(|x| x == 7);
|
||||
|
||||
#[derive(PartialEq)]
|
||||
struct S2;
|
||||
let r: Result<i32, S2> = Ok(4);
|
||||
let _ = (r == Ok(8));
|
||||
}
|
||||
|
||||
#[clippy::msrv = "1.69.0"]
|
||||
fn msrv_1_69() {
|
||||
// is_some_and added in 1.70.0
|
||||
let _ = Some(5).map_or(false, |n| n == if 2 > 1 { n } else { 0 });
|
||||
}
|
67
src/tools/clippy/tests/ui/unnecessary_map_or.rs
Normal file
67
src/tools/clippy/tests/ui/unnecessary_map_or.rs
Normal file
@ -0,0 +1,67 @@
|
||||
//@aux-build:proc_macros.rs
|
||||
#![warn(clippy::unnecessary_map_or)]
|
||||
#![allow(clippy::no_effect)]
|
||||
#![allow(clippy::eq_op)]
|
||||
#![allow(clippy::unnecessary_lazy_evaluations)]
|
||||
#[clippy::msrv = "1.70.0"]
|
||||
#[macro_use]
|
||||
extern crate proc_macros;
|
||||
|
||||
fn main() {
|
||||
// should trigger
|
||||
let _ = Some(5).map_or(false, |n| n == 5);
|
||||
let _ = Some(5).map_or(true, |n| n != 5);
|
||||
let _ = Some(5).map_or(false, |n| {
|
||||
let _ = 1;
|
||||
n == 5
|
||||
});
|
||||
let _ = Some(5).map_or(false, |n| {
|
||||
let _ = n;
|
||||
6 >= 5
|
||||
});
|
||||
let _ = Some(vec![5]).map_or(false, |n| n == [5]);
|
||||
let _ = Some(vec![1]).map_or(false, |n| vec![2] == n);
|
||||
let _ = Some(5).map_or(false, |n| n == n);
|
||||
let _ = Some(5).map_or(false, |n| n == if 2 > 1 { n } else { 0 });
|
||||
let _ = Ok::<Vec<i32>, i32>(vec![5]).map_or(false, |n| n == [5]);
|
||||
let _ = Ok::<i32, i32>(5).map_or(false, |n| n == 5);
|
||||
let _ = Some(5).map_or(false, |n| n == 5).then(|| 1);
|
||||
|
||||
// shouldnt trigger
|
||||
let _ = Some(5).map_or(true, |n| n == 5);
|
||||
let _ = Some(5).map_or(true, |n| 5 == n);
|
||||
macro_rules! x {
|
||||
() => {
|
||||
Some(1)
|
||||
};
|
||||
}
|
||||
// methods lints dont fire on macros
|
||||
let _ = x!().map_or(false, |n| n == 1);
|
||||
let _ = x!().map_or(false, |n| n == vec![1][0]);
|
||||
|
||||
msrv_1_69();
|
||||
|
||||
external! {
|
||||
let _ = Some(5).map_or(false, |n| n == 5);
|
||||
}
|
||||
|
||||
with_span! {
|
||||
let _ = Some(5).map_or(false, |n| n == 5);
|
||||
}
|
||||
|
||||
// check for presence of PartialEq, and alter suggestion to use `is_ok_and` if absent
|
||||
struct S;
|
||||
let r: Result<i32, S> = Ok(3);
|
||||
let _ = r.map_or(false, |x| x == 7);
|
||||
|
||||
#[derive(PartialEq)]
|
||||
struct S2;
|
||||
let r: Result<i32, S2> = Ok(4);
|
||||
let _ = r.map_or(false, |x| x == 8);
|
||||
}
|
||||
|
||||
#[clippy::msrv = "1.69.0"]
|
||||
fn msrv_1_69() {
|
||||
// is_some_and added in 1.70.0
|
||||
let _ = Some(5).map_or(false, |n| n == if 2 > 1 { n } else { 0 });
|
||||
}
|
99
src/tools/clippy/tests/ui/unnecessary_map_or.stderr
Normal file
99
src/tools/clippy/tests/ui/unnecessary_map_or.stderr
Normal file
@ -0,0 +1,99 @@
|
||||
error: this `map_or` is redundant
|
||||
--> tests/ui/unnecessary_map_or.rs:12:13
|
||||
|
|
||||
LL | let _ = Some(5).map_or(false, |n| n == 5);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))`
|
||||
|
|
||||
= note: `-D clippy::unnecessary-map-or` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::unnecessary_map_or)]`
|
||||
|
||||
error: this `map_or` is redundant
|
||||
--> tests/ui/unnecessary_map_or.rs:13:13
|
||||
|
|
||||
LL | let _ = Some(5).map_or(true, |n| n != 5);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) != Some(5))`
|
||||
|
||||
error: this `map_or` is redundant
|
||||
--> tests/ui/unnecessary_map_or.rs:14:13
|
||||
|
|
||||
LL | let _ = Some(5).map_or(false, |n| {
|
||||
| _____________^
|
||||
LL | | let _ = 1;
|
||||
LL | | n == 5
|
||||
LL | | });
|
||||
| |______^ help: use a standard comparison instead: `(Some(5) == Some(5))`
|
||||
|
||||
error: this `map_or` is redundant
|
||||
--> tests/ui/unnecessary_map_or.rs:18:13
|
||||
|
|
||||
LL | let _ = Some(5).map_or(false, |n| {
|
||||
| _____________^
|
||||
LL | | let _ = n;
|
||||
LL | | 6 >= 5
|
||||
LL | | });
|
||||
| |______^
|
||||
|
|
||||
help: use is_some_and instead
|
||||
|
|
||||
LL ~ let _ = Some(5).is_some_and(|n| {
|
||||
LL + let _ = n;
|
||||
LL + 6 >= 5
|
||||
LL ~ });
|
||||
|
|
||||
|
||||
error: this `map_or` is redundant
|
||||
--> tests/ui/unnecessary_map_or.rs:22:13
|
||||
|
|
||||
LL | let _ = Some(vec![5]).map_or(false, |n| n == [5]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(vec![5]).is_some_and(|n| n == [5])`
|
||||
|
||||
error: this `map_or` is redundant
|
||||
--> tests/ui/unnecessary_map_or.rs:23:13
|
||||
|
|
||||
LL | let _ = Some(vec![1]).map_or(false, |n| vec![2] == n);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(vec![1]).is_some_and(|n| vec![2] == n)`
|
||||
|
||||
error: this `map_or` is redundant
|
||||
--> tests/ui/unnecessary_map_or.rs:24:13
|
||||
|
|
||||
LL | let _ = Some(5).map_or(false, |n| n == n);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(5).is_some_and(|n| n == n)`
|
||||
|
||||
error: this `map_or` is redundant
|
||||
--> tests/ui/unnecessary_map_or.rs:25:13
|
||||
|
|
||||
LL | let _ = Some(5).map_or(false, |n| n == if 2 > 1 { n } else { 0 });
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(5).is_some_and(|n| n == if 2 > 1 { n } else { 0 })`
|
||||
|
||||
error: this `map_or` is redundant
|
||||
--> tests/ui/unnecessary_map_or.rs:26:13
|
||||
|
|
||||
LL | let _ = Ok::<Vec<i32>, i32>(vec![5]).map_or(false, |n| n == [5]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_ok_and instead: `Ok::<Vec<i32>, i32>(vec![5]).is_ok_and(|n| n == [5])`
|
||||
|
||||
error: this `map_or` is redundant
|
||||
--> tests/ui/unnecessary_map_or.rs:27:13
|
||||
|
|
||||
LL | let _ = Ok::<i32, i32>(5).map_or(false, |n| n == 5);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Ok::<i32, i32>(5) == Ok(5))`
|
||||
|
||||
error: this `map_or` is redundant
|
||||
--> tests/ui/unnecessary_map_or.rs:28:13
|
||||
|
|
||||
LL | let _ = Some(5).map_or(false, |n| n == 5).then(|| 1);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))`
|
||||
|
||||
error: this `map_or` is redundant
|
||||
--> tests/ui/unnecessary_map_or.rs:55:13
|
||||
|
|
||||
LL | let _ = r.map_or(false, |x| x == 7);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_ok_and instead: `r.is_ok_and(|x| x == 7)`
|
||||
|
||||
error: this `map_or` is redundant
|
||||
--> tests/ui/unnecessary_map_or.rs:60:13
|
||||
|
|
||||
LL | let _ = r.map_or(false, |x| x == 8);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(r == Ok(8))`
|
||||
|
||||
error: aborting due to 13 previous errors
|
||||
|
24
tests/rustdoc-js-std/write.js
Normal file
24
tests/rustdoc-js-std/write.js
Normal file
@ -0,0 +1,24 @@
|
||||
const EXPECTED = [
|
||||
{
|
||||
'query': 'write',
|
||||
'others': [
|
||||
{ 'path': 'std::fmt', 'name': 'write' },
|
||||
{ 'path': 'std::fs', 'name': 'write' },
|
||||
{ 'path': 'std::ptr', 'name': 'write' },
|
||||
{ 'path': 'std::fmt', 'name': 'Write' },
|
||||
{ 'path': 'std::io', 'name': 'Write' },
|
||||
{ 'path': 'std::hash::Hasher', 'name': 'write' },
|
||||
],
|
||||
},
|
||||
{
|
||||
'query': 'Write',
|
||||
'others': [
|
||||
{ 'path': 'std::fmt', 'name': 'Write' },
|
||||
{ 'path': 'std::io', 'name': 'Write' },
|
||||
{ 'path': 'std::fmt', 'name': 'write' },
|
||||
{ 'path': 'std::fs', 'name': 'write' },
|
||||
{ 'path': 'std::ptr', 'name': 'write' },
|
||||
{ 'path': 'std::hash::Hasher', 'name': 'write' },
|
||||
],
|
||||
},
|
||||
];
|
17
tests/rustdoc-js/case.js
Normal file
17
tests/rustdoc-js/case.js
Normal file
@ -0,0 +1,17 @@
|
||||
const EXPECTED = [
|
||||
{
|
||||
'query': 'Foo',
|
||||
'others': [
|
||||
{ 'path': 'case', 'name': 'Foo', 'desc': 'Docs for Foo' },
|
||||
{ 'path': 'case', 'name': 'foo', 'desc': 'Docs for foo' },
|
||||
],
|
||||
},
|
||||
{
|
||||
'query': 'foo',
|
||||
'others': [
|
||||
// https://github.com/rust-lang/rust/issues/133017
|
||||
{ 'path': 'case', 'name': 'Foo', 'desc': 'Docs for Foo' },
|
||||
{ 'path': 'case', 'name': 'foo', 'desc': 'Docs for foo' },
|
||||
],
|
||||
},
|
||||
];
|
7
tests/rustdoc-js/case.rs
Normal file
7
tests/rustdoc-js/case.rs
Normal file
@ -0,0 +1,7 @@
|
||||
#![allow(nonstandard_style)]
|
||||
|
||||
/// Docs for Foo
|
||||
pub struct Foo;
|
||||
|
||||
/// Docs for foo
|
||||
pub struct foo;
|
20
tests/rustdoc/footnote-reference-in-footnote-def.rs
Normal file
20
tests/rustdoc/footnote-reference-in-footnote-def.rs
Normal file
@ -0,0 +1,20 @@
|
||||
// Checks that footnote references in footnote definitions are correctly generated.
|
||||
// Regression test for <https://github.com/rust-lang/rust/issues/131946>.
|
||||
|
||||
#![crate_name = "foo"]
|
||||
|
||||
//@ has 'foo/index.html'
|
||||
//@ has - '//*[@class="docblock"]/p/sup[@id="fnref1"]/a[@href="#fn1"]' '1'
|
||||
//@ has - '//li[@id="fn1"]/p' 'meow'
|
||||
//@ has - '//li[@id="fn1"]/p/sup[@id="fnref2"]/a[@href="#fn2"]' '2'
|
||||
//@ has - '//li[@id="fn1"]//a[@href="#fn2"]' '2'
|
||||
//@ has - '//li[@id="fn2"]/p' 'uwu'
|
||||
//@ has - '//li[@id="fn2"]/p/sup[@id="fnref1"]/a[@href="#fn1"]' '1'
|
||||
//@ has - '//li[@id="fn2"]//a[@href="#fn1"]' '1'
|
||||
|
||||
//! # footnote-hell
|
||||
//!
|
||||
//! Hello [^a].
|
||||
//!
|
||||
//! [^a]: meow [^b]
|
||||
//! [^b]: uwu [^a]
|
Loading…
Reference in New Issue
Block a user