Merge commit '62589a21d3' into clippy-subtree-update
This commit is contained in:
parent
a200679d96
commit
44731205b8
138 changed files with 4899 additions and 3517 deletions
6
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
6
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
|
@ -20,15 +20,15 @@ body:
|
|||
label: Reproducer
|
||||
description: Please provide the code and steps to reproduce the bug
|
||||
value: |
|
||||
I tried this code:
|
||||
Code:
|
||||
|
||||
```rust
|
||||
<code>
|
||||
```
|
||||
|
||||
I expected to see this happen:
|
||||
Current output:
|
||||
|
||||
Instead, this happened:
|
||||
Desired output:
|
||||
- type: textarea
|
||||
id: version
|
||||
attributes:
|
||||
|
|
|
|||
2
.github/workflows/clippy_mq.yml
vendored
2
.github/workflows/clippy_mq.yml
vendored
|
|
@ -25,8 +25,6 @@ jobs:
|
|||
host: i686-unknown-linux-gnu
|
||||
- os: windows-latest
|
||||
host: x86_64-pc-windows-msvc
|
||||
- os: macos-13
|
||||
host: x86_64-apple-darwin
|
||||
- os: macos-latest
|
||||
host: aarch64-apple-darwin
|
||||
|
||||
|
|
|
|||
|
|
@ -157,18 +157,19 @@ fn add_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> {
|
|||
let path = "clippy_lints/src/lib.rs";
|
||||
let mut lib_rs = fs::read_to_string(path).context("reading")?;
|
||||
|
||||
let comment_start = lib_rs.find("// add lints here,").expect("Couldn't find comment");
|
||||
let ctor_arg = if lint.pass == Pass::Late { "_" } else { "" };
|
||||
let lint_pass = lint.pass;
|
||||
let (comment, ctor_arg) = if lint.pass == Pass::Late {
|
||||
("// add late passes here", "_")
|
||||
} else {
|
||||
("// add early passes here", "")
|
||||
};
|
||||
let comment_start = lib_rs.find(comment).expect("Couldn't find comment");
|
||||
let module_name = lint.name;
|
||||
let camel_name = to_camel_case(lint.name);
|
||||
|
||||
let new_lint = if enable_msrv {
|
||||
format!(
|
||||
"store.register_{lint_pass}_pass(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(conf)));\n ",
|
||||
)
|
||||
format!("Box::new(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(conf))),\n ",)
|
||||
} else {
|
||||
format!("store.register_{lint_pass}_pass(|{ctor_arg}| Box::new({module_name}::{camel_name}));\n ",)
|
||||
format!("Box::new(|{ctor_arg}| Box::new({module_name}::{camel_name})),\n ",)
|
||||
};
|
||||
|
||||
lib_rs.insert_str(comment_start, &new_lint);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
|
|||
use clippy_utils::higher::has_let_expr;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::res::MaybeDef;
|
||||
use clippy_utils::source::SpanRangeExt;
|
||||
use clippy_utils::source::{SpanRangeExt, snippet_with_context};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::{eq_expr_value, sym};
|
||||
|
|
@ -415,19 +415,20 @@ fn simplify_not(cx: &LateContext<'_>, curr_msrv: Msrv, expr: &Expr<'_>) -> Optio
|
|||
BinOpKind::Ge => Some(" < "),
|
||||
_ => None,
|
||||
}
|
||||
.and_then(|op| {
|
||||
let lhs_snippet = lhs.span.get_source_text(cx)?;
|
||||
let rhs_snippet = rhs.span.get_source_text(cx)?;
|
||||
.map(|op| {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let (lhs_snippet, _) = snippet_with_context(cx, lhs.span, SyntaxContext::root(), "", &mut app);
|
||||
let (rhs_snippet, _) = snippet_with_context(cx, rhs.span, SyntaxContext::root(), "", &mut app);
|
||||
|
||||
if !(lhs_snippet.starts_with('(') && lhs_snippet.ends_with(')'))
|
||||
&& let (ExprKind::Cast(..), BinOpKind::Ge) = (&lhs.kind, binop.node)
|
||||
{
|
||||
// e.g. `(a as u64) < b`. Without the parens the `<` is
|
||||
// interpreted as a start of generic arguments for `u64`
|
||||
return Some(format!("({lhs_snippet}){op}{rhs_snippet}"));
|
||||
return format!("({lhs_snippet}){op}{rhs_snippet}");
|
||||
}
|
||||
|
||||
Some(format!("{lhs_snippet}{op}{rhs_snippet}"))
|
||||
format!("{lhs_snippet}{op}{rhs_snippet}")
|
||||
})
|
||||
},
|
||||
ExprKind::MethodCall(path, receiver, args, _) => {
|
||||
|
|
|
|||
|
|
@ -2,14 +2,13 @@ use clippy_config::Conf;
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::res::{MaybeDef, MaybeQPath};
|
||||
use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{
|
||||
contains_return, expr_adjustment_requires_coercion, higher, is_else_clause, is_in_const_context, peel_blocks, sym,
|
||||
as_some_expr, contains_return, expr_adjustment_requires_coercion, higher, is_else_clause, is_in_const_context,
|
||||
is_none_expr, peel_blocks, sym,
|
||||
};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
|
@ -70,11 +69,10 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
|
|||
}) = higher::If::hir(expr)
|
||||
&& let ExprKind::Block(then_block, _) = then.kind
|
||||
&& let Some(then_expr) = then_block.expr
|
||||
&& let ExprKind::Call(then_call, [then_arg]) = then_expr.kind
|
||||
&& let Some(then_arg) = as_some_expr(cx, then_expr)
|
||||
&& !expr.span.from_expansion()
|
||||
&& !then_expr.span.from_expansion()
|
||||
&& then_call.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome)
|
||||
&& peel_blocks(els).res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone)
|
||||
&& is_none_expr(cx, peel_blocks(els))
|
||||
&& !is_else_clause(cx.tcx, expr)
|
||||
&& !is_in_const_context(cx)
|
||||
&& self.msrv.meets(cx, msrvs::BOOL_THEN)
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::msrvs::Msrv;
|
||||
use clippy_utils::{is_in_const_context, is_in_test};
|
||||
use clippy_utils::{is_in_const_context, is_in_test, sym};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, HirId, RustcVersion, StabilityLevel, StableSince};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::def_id::{CrateNum, DefId};
|
||||
use rustc_span::{ExpnKind, Span, sym};
|
||||
use rustc_span::{ExpnKind, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
@ -77,11 +77,36 @@ enum Availability {
|
|||
Since(RustcVersion),
|
||||
}
|
||||
|
||||
/// All known std crates containing a stability attribute.
|
||||
struct StdCrates([Option<CrateNum>; 6]);
|
||||
impl StdCrates {
|
||||
fn new(tcx: TyCtxt<'_>) -> Self {
|
||||
let mut res = Self([None; _]);
|
||||
for &krate in tcx.crates(()) {
|
||||
// FIXME(@Jarcho): We should have an internal lint to detect when this list is out of date.
|
||||
match tcx.crate_name(krate) {
|
||||
sym::alloc => res.0[0] = Some(krate),
|
||||
sym::core => res.0[1] = Some(krate),
|
||||
sym::core_arch => res.0[2] = Some(krate),
|
||||
sym::proc_macro => res.0[3] = Some(krate),
|
||||
sym::std => res.0[4] = Some(krate),
|
||||
sym::std_detect => res.0[5] = Some(krate),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
fn contains(&self, krate: CrateNum) -> bool {
|
||||
self.0.contains(&Some(krate))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IncompatibleMsrv {
|
||||
msrv: Msrv,
|
||||
availability_cache: FxHashMap<(DefId, bool), Availability>,
|
||||
check_in_tests: bool,
|
||||
core_crate: Option<CrateNum>,
|
||||
std_crates: StdCrates,
|
||||
|
||||
// The most recently called path. Used to skip checking the path after it's
|
||||
// been checked when visiting the call expression.
|
||||
|
|
@ -96,11 +121,7 @@ impl IncompatibleMsrv {
|
|||
msrv: conf.msrv,
|
||||
availability_cache: FxHashMap::default(),
|
||||
check_in_tests: conf.check_incompatible_msrv_in_tests,
|
||||
core_crate: tcx
|
||||
.crates(())
|
||||
.iter()
|
||||
.find(|krate| tcx.crate_name(**krate) == sym::core)
|
||||
.copied(),
|
||||
std_crates: StdCrates::new(tcx),
|
||||
called_path: None,
|
||||
}
|
||||
}
|
||||
|
|
@ -152,21 +173,24 @@ impl IncompatibleMsrv {
|
|||
node: HirId,
|
||||
span: Span,
|
||||
) {
|
||||
if def_id.is_local() {
|
||||
// We don't check local items since their MSRV is supposed to always be valid.
|
||||
if !self.std_crates.contains(def_id.krate) {
|
||||
// No stability attributes to lookup for these items.
|
||||
return;
|
||||
}
|
||||
let expn_data = span.ctxt().outer_expn_data();
|
||||
if let ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) = expn_data.kind {
|
||||
// Desugared expressions get to cheat and stability is ignored.
|
||||
// Intentionally not using `.from_expansion()`, since we do still care about macro expansions
|
||||
return;
|
||||
}
|
||||
// Functions coming from `core` while expanding a macro such as `assert*!()` get to cheat too: the
|
||||
// macros may have existed prior to the checked MSRV, but their expansion with a recent compiler
|
||||
// might use recent functions or methods. Compiling with an older compiler would not use those.
|
||||
if Some(def_id.krate) == self.core_crate && expn_data.macro_def_id.map(|did| did.krate) == self.core_crate {
|
||||
return;
|
||||
// Use `from_expansion` to fast-path the common case.
|
||||
if span.from_expansion() {
|
||||
let expn = span.ctxt().outer_expn_data();
|
||||
match expn.kind {
|
||||
// FIXME(@Jarcho): Check that the actual desugaring or std macro is supported by the
|
||||
// current MSRV. Note that nested expansions need to be handled as well.
|
||||
ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) => return,
|
||||
ExpnKind::Macro(..) if expn.macro_def_id.is_some_and(|did| self.std_crates.contains(did.krate)) => {
|
||||
return;
|
||||
},
|
||||
// All other expansions share the target's MSRV.
|
||||
// FIXME(@Jarcho): What should we do about version dependant macros from external crates?
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
if (self.check_in_tests || !is_in_test(cx.tcx, node))
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_config::types::InherentImplLintScope;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::fulfill_or_allowed;
|
||||
use clippy_utils::{fulfill_or_allowed, is_cfg_test, is_in_cfg_test};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir::def_id::{LocalDefId, LocalModDefId};
|
||||
use rustc_hir::{Item, ItemKind, Node};
|
||||
|
|
@ -100,7 +100,8 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl {
|
|||
},
|
||||
InherentImplLintScope::Crate => Criterion::Crate,
|
||||
};
|
||||
match type_map.entry((impl_ty, criterion)) {
|
||||
let is_test = is_cfg_test(cx.tcx, hir_id) || is_in_cfg_test(cx.tcx, hir_id);
|
||||
match type_map.entry((impl_ty, criterion, is_test)) {
|
||||
Entry::Vacant(e) => {
|
||||
// Store the id for the first impl block of this type. The span is retrieved lazily.
|
||||
e.insert(IdOrSpan::Id(impl_id));
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_hir_and_then;
|
||||
use clippy_utils::res::MaybeResPath;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
|
|
@ -59,80 +59,88 @@ declare_lint_pass!(LetIfSeq => [USELESS_LET_IF_SEQ]);
|
|||
impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
|
||||
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
|
||||
for [stmt, next] in block.stmts.array_windows::<2>() {
|
||||
if let hir::StmtKind::Let(local) = stmt.kind
|
||||
&& let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind
|
||||
&& let hir::StmtKind::Expr(if_) = next.kind
|
||||
&& let hir::ExprKind::If(cond, then, else_) = if_.kind
|
||||
&& !is_local_used(cx, cond, canonical_id)
|
||||
&& let hir::ExprKind::Block(then, _) = then.kind
|
||||
&& let Some(value) = check_assign(cx, canonical_id, then)
|
||||
&& !is_local_used(cx, value, canonical_id)
|
||||
{
|
||||
let span = stmt.span.to(if_.span);
|
||||
|
||||
let has_interior_mutability = !cx
|
||||
.typeck_results()
|
||||
.node_type(canonical_id)
|
||||
.is_freeze(cx.tcx, cx.typing_env());
|
||||
if has_interior_mutability {
|
||||
return;
|
||||
}
|
||||
|
||||
let (default_multi_stmts, default) = if let Some(else_) = else_ {
|
||||
if let hir::ExprKind::Block(else_, _) = else_.kind {
|
||||
if let Some(default) = check_assign(cx, canonical_id, else_) {
|
||||
(else_.stmts.len() > 1, default)
|
||||
} else if let Some(default) = local.init {
|
||||
(true, default)
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} else if let Some(default) = local.init {
|
||||
(false, default)
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let mutability = match mode {
|
||||
BindingMode(_, Mutability::Mut) => "<mut> ",
|
||||
_ => "",
|
||||
};
|
||||
|
||||
// FIXME: this should not suggest `mut` if we can detect that the variable is not
|
||||
// use mutably after the `if`
|
||||
|
||||
let sug = format!(
|
||||
"let {mutability}{name} = if {cond} {{{then} {value} }} else {{{else} {default} }};",
|
||||
name=ident.name,
|
||||
cond=snippet(cx, cond.span, "_"),
|
||||
then=if then.stmts.len() > 1 { " ..;" } else { "" },
|
||||
else=if default_multi_stmts { " ..;" } else { "" },
|
||||
value=snippet(cx, value.span, "<value>"),
|
||||
default=snippet(cx, default.span, "<default>"),
|
||||
);
|
||||
span_lint_hir_and_then(
|
||||
cx,
|
||||
USELESS_LET_IF_SEQ,
|
||||
local.hir_id,
|
||||
span,
|
||||
"`if _ { .. } else { .. }` is an expression",
|
||||
|diag| {
|
||||
diag.span_suggestion(
|
||||
span,
|
||||
"it is more idiomatic to write",
|
||||
sug,
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
if !mutability.is_empty() {
|
||||
diag.note("you might not need `mut` at all");
|
||||
}
|
||||
},
|
||||
);
|
||||
if let hir::StmtKind::Expr(if_) = next.kind {
|
||||
check_block_inner(cx, stmt, if_);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(expr) = block.expr
|
||||
&& let Some(stmt) = block.stmts.last()
|
||||
{
|
||||
check_block_inner(cx, stmt, expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_block_inner<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'tcx>, if_: &'tcx hir::Expr<'tcx>) {
|
||||
if let hir::StmtKind::Let(local) = stmt.kind
|
||||
&& let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind
|
||||
&& let hir::ExprKind::If(cond, then, else_) = if_.kind
|
||||
&& !is_local_used(cx, cond, canonical_id)
|
||||
&& let hir::ExprKind::Block(then, _) = then.kind
|
||||
&& let Some(value) = check_assign(cx, canonical_id, then)
|
||||
&& !is_local_used(cx, value, canonical_id)
|
||||
{
|
||||
let span = stmt.span.to(if_.span);
|
||||
|
||||
let has_interior_mutability = !cx
|
||||
.typeck_results()
|
||||
.node_type(canonical_id)
|
||||
.is_freeze(cx.tcx, cx.typing_env());
|
||||
if has_interior_mutability {
|
||||
return;
|
||||
}
|
||||
|
||||
let (default_multi_stmts, default) = if let Some(else_) = else_ {
|
||||
if let hir::ExprKind::Block(else_, _) = else_.kind {
|
||||
if let Some(default) = check_assign(cx, canonical_id, else_) {
|
||||
(else_.stmts.len() > 1, default)
|
||||
} else if let Some(default) = local.init {
|
||||
(true, default)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else if let Some(default) = local.init {
|
||||
(false, default)
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mutability = match mode {
|
||||
BindingMode(_, Mutability::Mut) => "<mut> ",
|
||||
_ => "",
|
||||
};
|
||||
|
||||
// FIXME: this should not suggest `mut` if we can detect that the variable is not
|
||||
// use mutably after the `if`
|
||||
|
||||
let mut applicability = Applicability::HasPlaceholders;
|
||||
let (cond_snip, _) = snippet_with_context(cx, cond.span, if_.span.ctxt(), "_", &mut applicability);
|
||||
let (value_snip, _) = snippet_with_context(cx, value.span, if_.span.ctxt(), "<value>", &mut applicability);
|
||||
let (default_snip, _) =
|
||||
snippet_with_context(cx, default.span, if_.span.ctxt(), "<default>", &mut applicability);
|
||||
let sug = format!(
|
||||
"let {mutability}{name} = if {cond_snip} {{{then} {value_snip} }} else {{{else} {default_snip} }};",
|
||||
name=ident.name,
|
||||
then=if then.stmts.len() > 1 { " ..;" } else { "" },
|
||||
else=if default_multi_stmts { " ..;" } else { "" },
|
||||
);
|
||||
span_lint_hir_and_then(
|
||||
cx,
|
||||
USELESS_LET_IF_SEQ,
|
||||
local.hir_id,
|
||||
span,
|
||||
"`if _ { .. } else { .. }` is an expression",
|
||||
|diag| {
|
||||
diag.span_suggestion(span, "it is more idiomatic to write", sug, applicability);
|
||||
if !mutability.is_empty() {
|
||||
diag.note("you might not need `mut` at all");
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -401,7 +401,9 @@ mod zombie_processes;
|
|||
use clippy_config::{Conf, get_configuration_metadata, sanitize_explanation};
|
||||
use clippy_utils::macros::FormatArgsStorage;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_lint::Lint;
|
||||
use rustc_data_structures::sync;
|
||||
use rustc_lint::{EarlyLintPass, LateLintPass, Lint};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use utils::attr_collector::{AttrCollector, AttrStorage};
|
||||
|
||||
pub fn explain(name: &str) -> i32 {
|
||||
|
|
@ -443,381 +445,410 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
|
|||
// level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass.
|
||||
store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes::new(conf)));
|
||||
|
||||
store.register_early_pass(move || Box::new(attrs::PostExpansionEarlyAttributes::new(conf)));
|
||||
|
||||
let format_args_storage = FormatArgsStorage::default();
|
||||
let format_args = format_args_storage.clone();
|
||||
store.register_early_pass(move || {
|
||||
Box::new(utils::format_args_collector::FormatArgsCollector::new(
|
||||
format_args.clone(),
|
||||
))
|
||||
});
|
||||
|
||||
let attr_storage = AttrStorage::default();
|
||||
let attrs = attr_storage.clone();
|
||||
store.register_early_pass(move || Box::new(AttrCollector::new(attrs.clone())));
|
||||
|
||||
store.register_late_pass(move |_| Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir));
|
||||
store.register_late_pass(|_| Box::new(utils::author::Author));
|
||||
store.register_late_pass(move |tcx| Box::new(await_holding_invalid::AwaitHolding::new(tcx, conf)));
|
||||
store.register_late_pass(|_| Box::new(serde_api::SerdeApi));
|
||||
store.register_late_pass(move |_| Box::new(types::Types::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(booleans::NonminimalBool::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(enum_clike::UnportableVariant));
|
||||
store.register_late_pass(move |_| Box::new(float_literal::FloatLiteral::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(ptr::Ptr));
|
||||
store.register_late_pass(|_| Box::new(needless_bool::NeedlessBool));
|
||||
store.register_late_pass(|_| Box::new(bool_comparison::BoolComparison));
|
||||
store.register_late_pass(|_| Box::new(needless_for_each::NeedlessForEach));
|
||||
store.register_late_pass(|_| Box::new(misc::LintPass));
|
||||
store.register_late_pass(|_| Box::new(eta_reduction::EtaReduction));
|
||||
store.register_late_pass(|_| Box::new(mut_mut::MutMut::default()));
|
||||
store.register_late_pass(|_| Box::new(unnecessary_mut_passed::UnnecessaryMutPassed));
|
||||
store.register_late_pass(|_| Box::<significant_drop_tightening::SignificantDropTightening<'_>>::default());
|
||||
store.register_late_pass(move |_| Box::new(len_zero::LenZero::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(attrs::Attributes::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(blocks_in_conditions::BlocksInConditions));
|
||||
store.register_late_pass(|_| Box::new(unicode::Unicode));
|
||||
store.register_late_pass(|_| Box::new(uninit_vec::UninitVec));
|
||||
store.register_late_pass(|_| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd));
|
||||
store.register_late_pass(|_| Box::new(strings::StringAdd));
|
||||
store.register_late_pass(|_| Box::new(implicit_return::ImplicitReturn));
|
||||
store.register_late_pass(move |_| Box::new(implicit_saturating_sub::ImplicitSaturatingSub::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(default_numeric_fallback::DefaultNumericFallback));
|
||||
store.register_late_pass(move |_| {
|
||||
Box::new(inconsistent_struct_constructor::InconsistentStructConstructor::new(
|
||||
conf,
|
||||
))
|
||||
});
|
||||
store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions));
|
||||
store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports));
|
||||
store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(conf)));
|
||||
let format_args = format_args_storage.clone();
|
||||
store.register_late_pass(move |_| Box::new(methods::Methods::new(conf, format_args.clone())));
|
||||
store.register_late_pass(move |_| Box::new(matches::Matches::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustive::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(conf)));
|
||||
store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(conf)));
|
||||
store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(checked_conversions::CheckedConversions::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(mem_replace::MemReplace::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(ranges::Ranges::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(from_over_into::FromOverInto::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(use_self::UseSelf::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(missing_const_for_fn::MissingConstForFn::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(needless_question_mark::NeedlessQuestionMark));
|
||||
store.register_late_pass(move |_| Box::new(casts::Casts::new(conf)));
|
||||
store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(size_of_in_element_count::SizeOfInElementCount));
|
||||
store.register_late_pass(|_| Box::new(same_name_method::SameNameMethod));
|
||||
store.register_late_pass(move |_| Box::new(index_refutable_slice::IndexRefutableSlice::new(conf)));
|
||||
store.register_late_pass(|_| Box::<shadow::Shadow>::default());
|
||||
let format_args = format_args_storage.clone();
|
||||
store.register_late_pass(move |_| Box::new(unit_types::UnitTypes::new(format_args.clone())));
|
||||
store.register_late_pass(move |_| Box::new(loops::Loops::new(conf)));
|
||||
store.register_late_pass(|_| Box::<main_recursion::MainRecursion>::default());
|
||||
store.register_late_pass(move |_| Box::new(lifetimes::Lifetimes::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(entry::HashMapPass));
|
||||
store.register_late_pass(|_| Box::new(minmax::MinMaxPass));
|
||||
store.register_late_pass(|_| Box::new(zero_div_zero::ZeroDiv));
|
||||
store.register_late_pass(|_| Box::new(mutex_atomic::Mutex));
|
||||
store.register_late_pass(|_| Box::new(needless_update::NeedlessUpdate));
|
||||
store.register_late_pass(|_| Box::new(needless_borrowed_ref::NeedlessBorrowedRef));
|
||||
store.register_late_pass(|_| Box::new(borrow_deref_ref::BorrowDerefRef));
|
||||
store.register_late_pass(|_| Box::<no_effect::NoEffect>::default());
|
||||
store.register_late_pass(|_| Box::new(temporary_assignment::TemporaryAssignment));
|
||||
store.register_late_pass(move |_| Box::new(transmute::Transmute::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(cognitive_complexity::CognitiveComplexity::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(escape::BoxedLocal::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(vec::UselessVec::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(panic_unimplemented::PanicUnimplemented::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(strings::StringLitAsBytes));
|
||||
store.register_late_pass(|_| Box::new(derive::Derive));
|
||||
store.register_late_pass(move |_| Box::new(derivable_impls::DerivableImpls::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(drop_forget_ref::DropForgetRef));
|
||||
store.register_late_pass(|_| Box::new(empty_enums::EmptyEnums));
|
||||
store.register_late_pass(|_| Box::<regex::Regex>::default());
|
||||
store.register_late_pass(move |tcx| Box::new(ifs::CopyAndPaste::new(tcx, conf)));
|
||||
store.register_late_pass(|_| Box::new(copy_iterator::CopyIterator));
|
||||
let format_args = format_args_storage.clone();
|
||||
store.register_late_pass(move |_| Box::new(format::UselessFormat::new(format_args.clone())));
|
||||
store.register_late_pass(|_| Box::new(swap::Swap));
|
||||
store.register_late_pass(|_| Box::new(panicking_overflow_checks::PanickingOverflowChecks));
|
||||
store.register_late_pass(|_| Box::<new_without_default::NewWithoutDefault>::default());
|
||||
store.register_late_pass(move |_| Box::new(disallowed_names::DisallowedNames::new(conf)));
|
||||
store.register_early_pass(|| Box::new(functions::EarlyFunctions));
|
||||
store.register_late_pass(move |tcx| Box::new(functions::Functions::new(tcx, conf)));
|
||||
store.register_late_pass(move |_| Box::new(doc::Documentation::new(conf)));
|
||||
store.register_early_pass(move || Box::new(doc::Documentation::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(neg_multiply::NegMultiply));
|
||||
store.register_late_pass(|_| Box::new(let_if_seq::LetIfSeq));
|
||||
store.register_late_pass(|_| Box::new(mixed_read_write_in_expression::EvalOrderDependence));
|
||||
store.register_late_pass(move |_| Box::new(missing_doc::MissingDoc::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(missing_inline::MissingInline));
|
||||
store.register_late_pass(move |_| Box::new(exhaustive_items::ExhaustiveItems));
|
||||
store.register_late_pass(|_| Box::new(unused_result_ok::UnusedResultOk));
|
||||
store.register_late_pass(|_| Box::new(match_result_ok::MatchResultOk));
|
||||
store.register_late_pass(|_| Box::new(partialeq_ne_impl::PartialEqNeImpl));
|
||||
store.register_late_pass(|_| Box::new(unused_io_amount::UnusedIoAmount));
|
||||
store.register_late_pass(move |_| Box::new(large_enum_variant::LargeEnumVariant::new(conf)));
|
||||
let format_args = format_args_storage.clone();
|
||||
store.register_late_pass(move |_| Box::new(explicit_write::ExplicitWrite::new(format_args.clone())));
|
||||
store.register_late_pass(|_| Box::new(needless_pass_by_value::NeedlessPassByValue));
|
||||
store.register_late_pass(move |tcx| Box::new(pass_by_ref_or_value::PassByRefOrValue::new(tcx, conf)));
|
||||
store.register_late_pass(|_| Box::new(ref_option_ref::RefOptionRef));
|
||||
store.register_late_pass(|_| Box::new(infinite_iter::InfiniteIter));
|
||||
store.register_late_pass(|_| Box::new(inline_fn_without_body::InlineFnWithoutBody));
|
||||
store.register_late_pass(|_| Box::<useless_conversion::UselessConversion>::default());
|
||||
store.register_late_pass(|_| Box::new(implicit_hasher::ImplicitHasher));
|
||||
store.register_late_pass(|_| Box::new(fallible_impl_from::FallibleImplFrom));
|
||||
store.register_late_pass(move |_| Box::new(question_mark::QuestionMark::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(question_mark_used::QuestionMarkUsed));
|
||||
store.register_early_pass(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings));
|
||||
store.register_late_pass(|_| Box::new(suspicious_trait_impl::SuspiciousImpl));
|
||||
store.register_late_pass(|_| Box::new(map_unit_fn::MapUnit));
|
||||
store.register_late_pass(move |_| Box::new(inherent_impl::MultipleInherentImpl::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd));
|
||||
store.register_late_pass(move |_| Box::new(unwrap::Unwrap::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(indexing_slicing::IndexingSlicing::new(conf)));
|
||||
store.register_late_pass(move |tcx| Box::new(non_copy_const::NonCopyConst::new(tcx, conf)));
|
||||
store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone));
|
||||
store.register_late_pass(|_| Box::new(slow_vector_initialization::SlowVectorInit));
|
||||
store.register_late_pass(move |_| Box::new(unnecessary_wraps::UnnecessaryWraps::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(assertions_on_constants::AssertionsOnConstants::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(assertions_on_result_states::AssertionsOnResultStates));
|
||||
store.register_late_pass(|_| Box::new(inherent_to_string::InherentToString));
|
||||
store.register_late_pass(move |_| Box::new(trait_bounds::TraitBounds::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(comparison_chain::ComparisonChain));
|
||||
store.register_late_pass(move |tcx| Box::new(mut_key::MutableKeyType::new(tcx, conf)));
|
||||
store.register_late_pass(|_| Box::new(reference::DerefAddrOf));
|
||||
store.register_early_pass(|| Box::new(double_parens::DoubleParens));
|
||||
let format_args = format_args_storage.clone();
|
||||
store.register_late_pass(move |_| Box::new(format_impl::FormatImpl::new(format_args.clone())));
|
||||
store.register_early_pass(|| Box::new(unsafe_removed_from_name::UnsafeNameRemoval));
|
||||
store.register_early_pass(|| Box::new(else_if_without_else::ElseIfWithoutElse));
|
||||
store.register_early_pass(|| Box::new(int_plus_one::IntPlusOne));
|
||||
store.register_early_pass(|| Box::new(formatting::Formatting));
|
||||
store.register_early_pass(|| Box::new(misc_early::MiscEarlyLints));
|
||||
store.register_late_pass(|_| Box::new(redundant_closure_call::RedundantClosureCall));
|
||||
store.register_early_pass(|| Box::new(unused_unit::UnusedUnit));
|
||||
store.register_late_pass(|_| Box::new(unused_unit::UnusedUnit));
|
||||
store.register_late_pass(|_| Box::new(returns::Return));
|
||||
store.register_late_pass(move |_| Box::new(collapsible_if::CollapsibleIf::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(items_after_statements::ItemsAfterStatements));
|
||||
store.register_early_pass(|| Box::new(precedence::Precedence));
|
||||
store.register_late_pass(|_| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals));
|
||||
store.register_late_pass(|_| Box::new(needless_continue::NeedlessContinue));
|
||||
store.register_early_pass(|| Box::new(redundant_else::RedundantElse));
|
||||
store.register_late_pass(|_| Box::new(create_dir::CreateDir));
|
||||
store.register_early_pass(|| Box::new(needless_arbitrary_self_type::NeedlessArbitrarySelfType));
|
||||
store.register_early_pass(move || Box::new(literal_representation::LiteralDigitGrouping::new(conf)));
|
||||
store.register_early_pass(move || Box::new(literal_representation::DecimalLiteralRepresentation::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(item_name_repetitions::ItemNameRepetitions::new(conf)));
|
||||
store.register_early_pass(|| Box::new(tabs_in_doc_comments::TabsInDocComments));
|
||||
store.register_late_pass(move |_| Box::new(upper_case_acronyms::UpperCaseAcronyms::new(conf)));
|
||||
store.register_late_pass(|_| Box::<default::Default>::default());
|
||||
store.register_late_pass(move |_| Box::new(unused_self::UnusedSelf::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(mutable_debug_assertion::DebugAssertWithMutCall));
|
||||
store.register_late_pass(|_| Box::new(exit::Exit));
|
||||
store.register_late_pass(move |_| Box::new(to_digit_is_some::ToDigitIsSome::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(large_stack_arrays::LargeStackArrays::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(large_const_arrays::LargeConstArrays::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(floating_point_arithmetic::FloatingPointArithmetic));
|
||||
store.register_late_pass(|_| Box::new(as_conversions::AsConversions));
|
||||
store.register_late_pass(|_| Box::new(let_underscore::LetUnderscore));
|
||||
store.register_early_pass(|| Box::<single_component_path_imports::SingleComponentPathImports>::default());
|
||||
store.register_late_pass(move |_| Box::new(excessive_bools::ExcessiveBools::new(conf)));
|
||||
store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap));
|
||||
store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(conf)));
|
||||
store.register_late_pass(|_| Box::<redundant_pub_crate::RedundantPubCrate>::default());
|
||||
store.register_late_pass(|_| Box::<dereference::Dereferencing<'_>>::default());
|
||||
store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse));
|
||||
store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend));
|
||||
store.register_late_pass(move |_| Box::new(large_futures::LargeFuture::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(if_let_mutex::IfLetMutex));
|
||||
store.register_late_pass(|_| Box::new(if_not_else::IfNotElse));
|
||||
store.register_late_pass(|_| Box::new(equatable_if_let::PatternEquality));
|
||||
store.register_late_pass(|_| Box::new(manual_async_fn::ManualAsyncFn));
|
||||
store.register_late_pass(|_| Box::new(panic_in_result_fn::PanicInResultFn));
|
||||
store.register_early_pass(move || Box::new(non_expressive_names::NonExpressiveNames::new(conf)));
|
||||
store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(conf)));
|
||||
store.register_late_pass(|_| Box::<macro_use::MacroUseImports>::default());
|
||||
store.register_late_pass(|_| Box::new(pattern_type_mismatch::PatternTypeMismatch));
|
||||
store.register_late_pass(|_| Box::<unwrap_in_result::UnwrapInResult>::default());
|
||||
store.register_late_pass(|_| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
|
||||
store.register_late_pass(|_| Box::new(async_yields_async::AsyncYieldsAsync));
|
||||
let attrs = attr_storage.clone();
|
||||
store.register_late_pass(move |tcx| Box::new(disallowed_macros::DisallowedMacros::new(tcx, conf, attrs.clone())));
|
||||
store.register_late_pass(move |tcx| Box::new(disallowed_methods::DisallowedMethods::new(tcx, conf)));
|
||||
store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax));
|
||||
store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax));
|
||||
store.register_late_pass(|_| Box::new(empty_drop::EmptyDrop));
|
||||
store.register_late_pass(|_| Box::new(strings::StrToString));
|
||||
store.register_late_pass(|_| Box::new(zero_sized_map_values::ZeroSizedMapValues));
|
||||
store.register_late_pass(|_| Box::<vec_init_then_push::VecInitThenPush>::default());
|
||||
store.register_late_pass(|_| Box::new(redundant_slicing::RedundantSlicing));
|
||||
store.register_late_pass(|_| Box::new(from_str_radix_10::FromStrRadix10));
|
||||
store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(bool_assert_comparison::BoolAssertComparison));
|
||||
store.register_early_pass(move || Box::new(module_style::ModStyle::default()));
|
||||
store.register_late_pass(|_| Box::<unused_async::UnusedAsync>::default());
|
||||
store.register_late_pass(move |tcx| Box::new(disallowed_types::DisallowedTypes::new(tcx, conf)));
|
||||
store.register_late_pass(move |tcx| Box::new(missing_enforced_import_rename::ImportRename::new(tcx, conf)));
|
||||
store.register_early_pass(move || Box::new(disallowed_script_idents::DisallowedScriptIdents::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(strlen_on_c_strings::StrlenOnCStrings));
|
||||
store.register_late_pass(move |_| Box::new(self_named_constructors::SelfNamedConstructors));
|
||||
store.register_late_pass(move |_| Box::new(iter_not_returning_iterator::IterNotReturningIterator));
|
||||
store.register_late_pass(move |_| Box::new(manual_assert::ManualAssert));
|
||||
store.register_late_pass(move |_| Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::new(conf)));
|
||||
let format_args = format_args_storage.clone();
|
||||
store.register_late_pass(move |tcx| Box::new(format_args::FormatArgs::new(tcx, conf, format_args.clone())));
|
||||
store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray));
|
||||
store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes));
|
||||
store.register_late_pass(|_| Box::new(needless_late_init::NeedlessLateInit));
|
||||
store.register_late_pass(|_| Box::new(return_self_not_must_use::ReturnSelfNotMustUse));
|
||||
store.register_late_pass(|_| Box::new(init_numbered_fields::NumberedFields));
|
||||
store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames));
|
||||
store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(default_union_representation::DefaultUnionRepresentation));
|
||||
store.register_late_pass(|_| Box::<only_used_in_recursion::OnlyUsedInRecursion>::default());
|
||||
store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(conf)));
|
||||
let format_args = format_args_storage.clone();
|
||||
store.register_late_pass(move |_| Box::new(write::Write::new(conf, format_args.clone())));
|
||||
store.register_late_pass(move |_| Box::new(cargo::Cargo::new(conf)));
|
||||
store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef));
|
||||
store.register_late_pass(|_| Box::new(empty_with_brackets::EmptyWithBrackets::default()));
|
||||
store.register_late_pass(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings));
|
||||
store.register_early_pass(|| Box::new(pub_use::PubUse));
|
||||
store.register_late_pass(|_| Box::new(format_push_string::FormatPushString));
|
||||
store.register_late_pass(move |_| Box::new(large_include_file::LargeIncludeFile::new(conf)));
|
||||
store.register_early_pass(move || Box::new(large_include_file::LargeIncludeFile::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(strings::TrimSplitWhitespace));
|
||||
store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit));
|
||||
store.register_early_pass(|| Box::<duplicate_mod::DuplicateMod>::default());
|
||||
store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding));
|
||||
store.register_early_pass(move || Box::new(almost_complete_range::AlmostCompleteRange::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(swap_ptr_to_ref::SwapPtrToRef));
|
||||
store.register_late_pass(|_| Box::new(mismatching_type_param_order::TypeParamMismatch));
|
||||
store.register_late_pass(|_| Box::new(read_zero_byte_vec::ReadZeroByteVec));
|
||||
store.register_late_pass(|_| Box::new(default_instead_of_iter_empty::DefaultIterEmpty));
|
||||
store.register_late_pass(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(manual_rotate::ManualRotate));
|
||||
store.register_late_pass(move |_| Box::new(operators::Operators::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(std_instead_of_core::StdReexports::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(time_subtraction::UncheckedTimeSubtraction::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone));
|
||||
store.register_late_pass(move |_| Box::new(manual_abs_diff::ManualAbsDiff::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew));
|
||||
store.register_late_pass(|_| Box::new(unused_peekable::UnusedPeekable));
|
||||
store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments));
|
||||
store.register_late_pass(|_| Box::new(bool_to_int_with_if::BoolToIntWithIf));
|
||||
store.register_late_pass(|_| Box::new(box_default::BoxDefault));
|
||||
store.register_late_pass(|_| Box::new(implicit_saturating_add::ImplicitSaturatingAdd));
|
||||
store.register_early_pass(|| Box::new(partial_pub_fields::PartialPubFields));
|
||||
store.register_late_pass(|_| Box::new(missing_trait_methods::MissingTraitMethods));
|
||||
store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr));
|
||||
store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow));
|
||||
store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(semicolon_block::SemicolonBlock::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(permissions_set_readonly_false::PermissionsSetReadonlyFalse));
|
||||
store.register_late_pass(|_| Box::new(size_of_ref::SizeOfRef));
|
||||
store.register_late_pass(|_| Box::new(multiple_unsafe_ops_per_block::MultipleUnsafeOpsPerBlock));
|
||||
store.register_late_pass(move |_| Box::new(extra_unused_type_parameters::ExtraUnusedTypeParameters::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi));
|
||||
store.register_late_pass(|_| Box::new(collection_is_never_read::CollectionIsNeverRead));
|
||||
store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage));
|
||||
store.register_late_pass(|_| Box::new(needless_maybe_sized::NeedlessMaybeSized));
|
||||
store.register_late_pass(|_| Box::new(redundant_async_block::RedundantAsyncBlock));
|
||||
store.register_early_pass(|| Box::new(let_with_type_underscore::UnderscoreTyped));
|
||||
store.register_late_pass(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(unnecessary_struct_initialization::UnnecessaryStruct));
|
||||
store.register_late_pass(move |_| Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(tests_outside_test_module::TestsOutsideTestModule));
|
||||
store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation::new(conf)));
|
||||
store.register_early_pass(move || Box::new(excessive_nesting::ExcessiveNesting::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(items_after_test_module::ItemsAfterTestModule));
|
||||
store.register_early_pass(|| Box::new(ref_patterns::RefPatterns));
|
||||
store.register_late_pass(|_| Box::new(default_constructed_unit_structs::DefaultConstructedUnitStructs));
|
||||
store.register_early_pass(|| Box::new(needless_else::NeedlessElse));
|
||||
store.register_late_pass(|_| Box::new(missing_fields_in_debug::MissingFieldsInDebug));
|
||||
store.register_late_pass(|_| Box::new(endian_bytes::EndianBytes));
|
||||
store.register_late_pass(|_| Box::new(redundant_type_annotations::RedundantTypeAnnotations));
|
||||
store.register_late_pass(|_| Box::new(arc_with_non_send_sync::ArcWithNonSendSync));
|
||||
store.register_late_pass(|_| Box::new(needless_ifs::NeedlessIfs));
|
||||
store.register_late_pass(move |_| Box::new(min_ident_chars::MinIdentChars::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(large_stack_frames::LargeStackFrames::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(single_range_in_vec_init::SingleRangeInVecInit));
|
||||
store.register_late_pass(move |_| Box::new(needless_pass_by_ref_mut::NeedlessPassByRefMut::new(conf)));
|
||||
store.register_late_pass(|tcx| Box::new(non_canonical_impls::NonCanonicalImpls::new(tcx)));
|
||||
store.register_late_pass(move |_| Box::new(single_call_fn::SingleCallFn::new(conf)));
|
||||
store.register_early_pass(move || Box::new(raw_strings::RawStrings::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(legacy_numeric_constants::LegacyNumericConstants::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(manual_range_patterns::ManualRangePatterns));
|
||||
store.register_early_pass(|| Box::new(visibility::Visibility));
|
||||
store.register_late_pass(move |_| Box::new(tuple_array_conversions::TupleArrayConversions::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(manual_float_methods::ManualFloatMethods::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(four_forward_slashes::FourForwardSlashes));
|
||||
store.register_late_pass(|_| Box::new(error_impl_error::ErrorImplError));
|
||||
store.register_late_pass(move |_| Box::new(absolute_paths::AbsolutePaths::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(redundant_locals::RedundantLocals));
|
||||
store.register_late_pass(|_| Box::new(ignored_unit_patterns::IgnoredUnitPatterns));
|
||||
store.register_late_pass(|_| Box::<reserve_after_initialization::ReserveAfterInitialization>::default());
|
||||
store.register_late_pass(|_| Box::new(implied_bounds_in_impls::ImpliedBoundsInImpls));
|
||||
store.register_late_pass(|_| Box::new(missing_asserts_for_indexing::MissingAssertsForIndexing));
|
||||
store.register_late_pass(|_| Box::new(unnecessary_map_on_constructor::UnnecessaryMapOnConstructor));
|
||||
store.register_late_pass(move |_| {
|
||||
Box::new(needless_borrows_for_generic_args::NeedlessBorrowsForGenericArgs::new(
|
||||
conf,
|
||||
))
|
||||
});
|
||||
store.register_late_pass(move |_| Box::new(manual_hash_one::ManualHashOne::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(iter_without_into_iter::IterWithoutIntoIter));
|
||||
store.register_late_pass(|_| Box::<pathbuf_init_then_push::PathbufThenPush<'_>>::default());
|
||||
store.register_late_pass(|_| Box::new(iter_over_hash_type::IterOverHashType));
|
||||
store.register_late_pass(|_| Box::new(impl_hash_with_borrow_str_and_bytes::ImplHashWithBorrowStrBytes));
|
||||
store.register_late_pass(move |_| Box::new(repeat_vec_with_capacity::RepeatVecWithCapacity::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(uninhabited_references::UninhabitedReferences));
|
||||
store.register_late_pass(|_| Box::new(ineffective_open_options::IneffectiveOpenOptions));
|
||||
store.register_late_pass(|_| Box::<unconditional_recursion::UnconditionalRecursion>::default());
|
||||
store.register_late_pass(move |_| Box::new(pub_underscore_fields::PubUnderscoreFields::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(missing_const_for_thread_local::MissingConstForThreadLocal::new(conf)));
|
||||
store.register_late_pass(move |tcx| Box::new(incompatible_msrv::IncompatibleMsrv::new(tcx, conf)));
|
||||
store.register_late_pass(|_| Box::new(to_string_trait_impl::ToStringTraitImpl));
|
||||
store.register_early_pass(|| Box::new(multiple_bound_locations::MultipleBoundLocations));
|
||||
store.register_late_pass(move |_| Box::new(assigning_clones::AssigningClones::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(zero_repeat_side_effects::ZeroRepeatSideEffects));
|
||||
store.register_late_pass(move |_| Box::new(macro_metavars_in_unsafe::ExprMetavarsInUnsafe::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(string_patterns::StringPatterns::new(conf)));
|
||||
store.register_early_pass(|| Box::new(field_scoped_visibility_modifiers::FieldScopedVisibilityModifiers));
|
||||
store.register_late_pass(|_| Box::new(set_contains_or_insert::SetContainsOrInsert));
|
||||
store.register_early_pass(|| Box::new(byte_char_slices::ByteCharSlice));
|
||||
store.register_early_pass(|| Box::new(cfg_not_test::CfgNotTest));
|
||||
store.register_late_pass(|_| Box::new(zombie_processes::ZombieProcesses));
|
||||
store.register_late_pass(|_| Box::new(pointers_in_nomem_asm_block::PointersInNomemAsmBlock));
|
||||
store.register_late_pass(move |_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(non_zero_suggestions::NonZeroSuggestions));
|
||||
store.register_late_pass(|_| Box::new(literal_string_with_formatting_args::LiteralStringWithFormattingArg));
|
||||
store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp));
|
||||
store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound));
|
||||
store.register_early_pass(|| Box::new(empty_line_after::EmptyLineAfter::new()));
|
||||
store.register_late_pass(move |_| Box::new(arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(useless_concat::UselessConcat));
|
||||
store.register_late_pass(|_| Box::new(unneeded_struct_pattern::UnneededStructPattern));
|
||||
store.register_late_pass(|_| Box::<unnecessary_semicolon::UnnecessarySemicolon>::default());
|
||||
store.register_late_pass(move |_| Box::new(non_std_lazy_statics::NonStdLazyStatic::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(manual_option_as_slice::ManualOptionAsSlice::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(single_option_map::SingleOptionMap));
|
||||
store.register_late_pass(move |_| Box::new(redundant_test_prefix::RedundantTestPrefix));
|
||||
store.register_late_pass(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(infallible_try_from::InfallibleTryFrom));
|
||||
store.register_late_pass(|_| Box::new(coerce_container_to_any::CoerceContainerToAny));
|
||||
store.register_late_pass(|_| Box::new(toplevel_ref_arg::ToplevelRefArg));
|
||||
store.register_late_pass(|_| Box::new(volatile_composites::VolatileComposites));
|
||||
store.register_late_pass(|_| Box::new(replace_box::ReplaceBox));
|
||||
// add lints here, do not remove this comment, it's used in `new_lint`
|
||||
let early_lints: [Box<dyn Fn() -> Box<dyn EarlyLintPass + 'static> + sync::DynSend + sync::DynSync>; _] = [
|
||||
{
|
||||
let format_args = format_args_storage.clone();
|
||||
Box::new(move || {
|
||||
Box::new(utils::format_args_collector::FormatArgsCollector::new(
|
||||
format_args.clone(),
|
||||
))
|
||||
})
|
||||
},
|
||||
{
|
||||
let attrs = attr_storage.clone();
|
||||
Box::new(move || Box::new(AttrCollector::new(attrs.clone())))
|
||||
},
|
||||
Box::new(move || Box::new(attrs::PostExpansionEarlyAttributes::new(conf))),
|
||||
Box::new(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports)),
|
||||
Box::new(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(conf))),
|
||||
Box::new(move || Box::new(redundant_field_names::RedundantFieldNames::new(conf))),
|
||||
Box::new(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(conf))),
|
||||
Box::new(|| Box::new(functions::EarlyFunctions)),
|
||||
Box::new(move || Box::new(doc::Documentation::new(conf))),
|
||||
Box::new(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings)),
|
||||
Box::new(|| Box::new(double_parens::DoubleParens)),
|
||||
Box::new(|| Box::new(unsafe_removed_from_name::UnsafeNameRemoval)),
|
||||
Box::new(|| Box::new(else_if_without_else::ElseIfWithoutElse)),
|
||||
Box::new(|| Box::new(int_plus_one::IntPlusOne)),
|
||||
Box::new(|| Box::new(formatting::Formatting)),
|
||||
Box::new(|| Box::new(misc_early::MiscEarlyLints)),
|
||||
Box::new(|| Box::new(unused_unit::UnusedUnit)),
|
||||
Box::new(|| Box::new(precedence::Precedence)),
|
||||
Box::new(|| Box::new(redundant_else::RedundantElse)),
|
||||
Box::new(|| Box::new(needless_arbitrary_self_type::NeedlessArbitrarySelfType)),
|
||||
Box::new(move || Box::new(literal_representation::LiteralDigitGrouping::new(conf))),
|
||||
Box::new(move || Box::new(literal_representation::DecimalLiteralRepresentation::new(conf))),
|
||||
Box::new(|| Box::new(tabs_in_doc_comments::TabsInDocComments)),
|
||||
Box::new(|| Box::<single_component_path_imports::SingleComponentPathImports>::default()),
|
||||
Box::new(|| Box::new(option_env_unwrap::OptionEnvUnwrap)),
|
||||
Box::new(move || Box::new(non_expressive_names::NonExpressiveNames::new(conf))),
|
||||
Box::new(move || Box::new(nonstandard_macro_braces::MacroBraces::new(conf))),
|
||||
Box::new(|| Box::new(asm_syntax::InlineAsmX86AttSyntax)),
|
||||
Box::new(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax)),
|
||||
Box::new(move || Box::new(module_style::ModStyle::default())),
|
||||
Box::new(move || Box::new(disallowed_script_idents::DisallowedScriptIdents::new(conf))),
|
||||
Box::new(|| Box::new(octal_escapes::OctalEscapes)),
|
||||
Box::new(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames)),
|
||||
Box::new(|| Box::new(crate_in_macro_def::CrateInMacroDef)),
|
||||
Box::new(|| Box::new(pub_use::PubUse)),
|
||||
Box::new(move || Box::new(large_include_file::LargeIncludeFile::new(conf))),
|
||||
Box::new(|| Box::<duplicate_mod::DuplicateMod>::default()),
|
||||
Box::new(|| Box::new(unused_rounding::UnusedRounding)),
|
||||
Box::new(move || Box::new(almost_complete_range::AlmostCompleteRange::new(conf))),
|
||||
Box::new(|| Box::new(multi_assignments::MultiAssignments)),
|
||||
Box::new(|| Box::new(partial_pub_fields::PartialPubFields)),
|
||||
Box::new(|| Box::new(let_with_type_underscore::UnderscoreTyped)),
|
||||
Box::new(move || Box::new(excessive_nesting::ExcessiveNesting::new(conf))),
|
||||
Box::new(|| Box::new(ref_patterns::RefPatterns)),
|
||||
Box::new(|| Box::new(needless_else::NeedlessElse)),
|
||||
Box::new(move || Box::new(raw_strings::RawStrings::new(conf))),
|
||||
Box::new(|| Box::new(visibility::Visibility)),
|
||||
Box::new(|| Box::new(multiple_bound_locations::MultipleBoundLocations)),
|
||||
Box::new(|| Box::new(field_scoped_visibility_modifiers::FieldScopedVisibilityModifiers)),
|
||||
Box::new(|| Box::new(byte_char_slices::ByteCharSlice)),
|
||||
Box::new(|| Box::new(cfg_not_test::CfgNotTest)),
|
||||
Box::new(|| Box::new(empty_line_after::EmptyLineAfter::new())),
|
||||
// add early passes here, used by `cargo dev new_lint`
|
||||
];
|
||||
store.early_passes.extend(early_lints);
|
||||
|
||||
#[expect(clippy::type_complexity)]
|
||||
let late_lints: [Box<
|
||||
dyn for<'tcx> Fn(TyCtxt<'tcx>) -> Box<dyn LateLintPass<'tcx> + 'tcx> + sync::DynSend + sync::DynSync,
|
||||
>; _] = [
|
||||
Box::new(move |_| Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new(conf))),
|
||||
Box::new(|_| Box::new(utils::dump_hir::DumpHir)),
|
||||
Box::new(|_| Box::new(utils::author::Author)),
|
||||
Box::new(move |tcx| Box::new(await_holding_invalid::AwaitHolding::new(tcx, conf))),
|
||||
Box::new(|_| Box::new(serde_api::SerdeApi)),
|
||||
Box::new(move |_| Box::new(types::Types::new(conf))),
|
||||
Box::new(move |_| Box::new(booleans::NonminimalBool::new(conf))),
|
||||
Box::new(|_| Box::new(enum_clike::UnportableVariant)),
|
||||
Box::new(move |_| Box::new(float_literal::FloatLiteral::new(conf))),
|
||||
Box::new(|_| Box::new(ptr::Ptr)),
|
||||
Box::new(|_| Box::new(needless_bool::NeedlessBool)),
|
||||
Box::new(|_| Box::new(bool_comparison::BoolComparison)),
|
||||
Box::new(|_| Box::new(needless_for_each::NeedlessForEach)),
|
||||
Box::new(|_| Box::new(misc::LintPass)),
|
||||
Box::new(|_| Box::new(eta_reduction::EtaReduction)),
|
||||
Box::new(|_| Box::new(mut_mut::MutMut::default())),
|
||||
Box::new(|_| Box::new(unnecessary_mut_passed::UnnecessaryMutPassed)),
|
||||
Box::new(|_| Box::<significant_drop_tightening::SignificantDropTightening<'_>>::default()),
|
||||
Box::new(move |_| Box::new(len_zero::LenZero::new(conf))),
|
||||
Box::new(move |_| Box::new(attrs::Attributes::new(conf))),
|
||||
Box::new(|_| Box::new(blocks_in_conditions::BlocksInConditions)),
|
||||
Box::new(|_| Box::new(unicode::Unicode)),
|
||||
Box::new(|_| Box::new(uninit_vec::UninitVec)),
|
||||
Box::new(|_| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd)),
|
||||
Box::new(|_| Box::new(strings::StringAdd)),
|
||||
Box::new(|_| Box::new(implicit_return::ImplicitReturn)),
|
||||
Box::new(move |_| Box::new(implicit_saturating_sub::ImplicitSaturatingSub::new(conf))),
|
||||
Box::new(|_| Box::new(default_numeric_fallback::DefaultNumericFallback)),
|
||||
Box::new(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions)),
|
||||
Box::new(move |_| Box::new(approx_const::ApproxConstant::new(conf))),
|
||||
Box::new(move |_| Box::new(matches::Matches::new(conf))),
|
||||
Box::new(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustive::new(conf))),
|
||||
Box::new(move |_| Box::new(manual_strip::ManualStrip::new(conf))),
|
||||
Box::new(move |_| Box::new(checked_conversions::CheckedConversions::new(conf))),
|
||||
Box::new(move |_| Box::new(mem_replace::MemReplace::new(conf))),
|
||||
Box::new(move |_| Box::new(ranges::Ranges::new(conf))),
|
||||
Box::new(move |_| Box::new(from_over_into::FromOverInto::new(conf))),
|
||||
Box::new(move |_| Box::new(use_self::UseSelf::new(conf))),
|
||||
Box::new(move |_| Box::new(missing_const_for_fn::MissingConstForFn::new(conf))),
|
||||
Box::new(move |_| Box::new(needless_question_mark::NeedlessQuestionMark)),
|
||||
Box::new(move |_| Box::new(casts::Casts::new(conf))),
|
||||
Box::new(|_| Box::new(size_of_in_element_count::SizeOfInElementCount)),
|
||||
Box::new(|_| Box::new(same_name_method::SameNameMethod)),
|
||||
Box::new(move |_| Box::new(index_refutable_slice::IndexRefutableSlice::new(conf))),
|
||||
Box::new(|_| Box::<shadow::Shadow>::default()),
|
||||
Box::new(move |_| {
|
||||
Box::new(inconsistent_struct_constructor::InconsistentStructConstructor::new(
|
||||
conf,
|
||||
))
|
||||
}),
|
||||
{
|
||||
let format_args = format_args_storage.clone();
|
||||
Box::new(move |_| Box::new(methods::Methods::new(conf, format_args.clone())))
|
||||
},
|
||||
{
|
||||
let format_args = format_args_storage.clone();
|
||||
Box::new(move |_| Box::new(unit_types::UnitTypes::new(format_args.clone())))
|
||||
},
|
||||
Box::new(move |_| Box::new(loops::Loops::new(conf))),
|
||||
Box::new(|_| Box::<main_recursion::MainRecursion>::default()),
|
||||
Box::new(move |_| Box::new(lifetimes::Lifetimes::new(conf))),
|
||||
Box::new(|_| Box::new(entry::HashMapPass)),
|
||||
Box::new(|_| Box::new(minmax::MinMaxPass)),
|
||||
Box::new(|_| Box::new(zero_div_zero::ZeroDiv)),
|
||||
Box::new(|_| Box::new(mutex_atomic::Mutex)),
|
||||
Box::new(|_| Box::new(needless_update::NeedlessUpdate)),
|
||||
Box::new(|_| Box::new(needless_borrowed_ref::NeedlessBorrowedRef)),
|
||||
Box::new(|_| Box::new(borrow_deref_ref::BorrowDerefRef)),
|
||||
Box::new(|_| Box::<no_effect::NoEffect>::default()),
|
||||
Box::new(|_| Box::new(temporary_assignment::TemporaryAssignment)),
|
||||
Box::new(move |_| Box::new(transmute::Transmute::new(conf))),
|
||||
Box::new(move |_| Box::new(cognitive_complexity::CognitiveComplexity::new(conf))),
|
||||
Box::new(move |_| Box::new(escape::BoxedLocal::new(conf))),
|
||||
Box::new(move |_| Box::new(vec::UselessVec::new(conf))),
|
||||
Box::new(move |_| Box::new(panic_unimplemented::PanicUnimplemented::new(conf))),
|
||||
Box::new(|_| Box::new(strings::StringLitAsBytes)),
|
||||
Box::new(|_| Box::new(derive::Derive)),
|
||||
Box::new(move |_| Box::new(derivable_impls::DerivableImpls::new(conf))),
|
||||
Box::new(|_| Box::new(drop_forget_ref::DropForgetRef)),
|
||||
Box::new(|_| Box::new(empty_enums::EmptyEnums)),
|
||||
Box::new(|_| Box::<regex::Regex>::default()),
|
||||
Box::new(move |tcx| Box::new(ifs::CopyAndPaste::new(tcx, conf))),
|
||||
Box::new(|_| Box::new(copy_iterator::CopyIterator)),
|
||||
{
|
||||
let format_args = format_args_storage.clone();
|
||||
Box::new(move |_| Box::new(format::UselessFormat::new(format_args.clone())))
|
||||
},
|
||||
Box::new(|_| Box::new(swap::Swap)),
|
||||
Box::new(|_| Box::new(panicking_overflow_checks::PanickingOverflowChecks)),
|
||||
Box::new(|_| Box::<new_without_default::NewWithoutDefault>::default()),
|
||||
Box::new(move |_| Box::new(disallowed_names::DisallowedNames::new(conf))),
|
||||
Box::new(move |tcx| Box::new(functions::Functions::new(tcx, conf))),
|
||||
Box::new(move |_| Box::new(doc::Documentation::new(conf))),
|
||||
Box::new(|_| Box::new(neg_multiply::NegMultiply)),
|
||||
Box::new(|_| Box::new(let_if_seq::LetIfSeq)),
|
||||
Box::new(|_| Box::new(mixed_read_write_in_expression::EvalOrderDependence)),
|
||||
Box::new(move |_| Box::new(missing_doc::MissingDoc::new(conf))),
|
||||
Box::new(|_| Box::new(missing_inline::MissingInline)),
|
||||
Box::new(move |_| Box::new(exhaustive_items::ExhaustiveItems)),
|
||||
Box::new(|_| Box::new(unused_result_ok::UnusedResultOk)),
|
||||
Box::new(|_| Box::new(match_result_ok::MatchResultOk)),
|
||||
Box::new(|_| Box::new(partialeq_ne_impl::PartialEqNeImpl)),
|
||||
Box::new(|_| Box::new(unused_io_amount::UnusedIoAmount)),
|
||||
Box::new(move |_| Box::new(large_enum_variant::LargeEnumVariant::new(conf))),
|
||||
{
|
||||
let format_args = format_args_storage.clone();
|
||||
Box::new(move |_| Box::new(explicit_write::ExplicitWrite::new(format_args.clone())))
|
||||
},
|
||||
Box::new(|_| Box::new(needless_pass_by_value::NeedlessPassByValue)),
|
||||
Box::new(move |tcx| Box::new(pass_by_ref_or_value::PassByRefOrValue::new(tcx, conf))),
|
||||
Box::new(|_| Box::new(ref_option_ref::RefOptionRef)),
|
||||
Box::new(|_| Box::new(infinite_iter::InfiniteIter)),
|
||||
Box::new(|_| Box::new(inline_fn_without_body::InlineFnWithoutBody)),
|
||||
Box::new(|_| Box::<useless_conversion::UselessConversion>::default()),
|
||||
Box::new(|_| Box::new(implicit_hasher::ImplicitHasher)),
|
||||
Box::new(|_| Box::new(fallible_impl_from::FallibleImplFrom)),
|
||||
Box::new(move |_| Box::new(question_mark::QuestionMark::new(conf))),
|
||||
Box::new(|_| Box::new(question_mark_used::QuestionMarkUsed)),
|
||||
Box::new(|_| Box::new(suspicious_trait_impl::SuspiciousImpl)),
|
||||
Box::new(|_| Box::new(map_unit_fn::MapUnit)),
|
||||
Box::new(move |_| Box::new(inherent_impl::MultipleInherentImpl::new(conf))),
|
||||
Box::new(|_| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd)),
|
||||
Box::new(move |_| Box::new(unwrap::Unwrap::new(conf))),
|
||||
Box::new(move |_| Box::new(indexing_slicing::IndexingSlicing::new(conf))),
|
||||
Box::new(move |tcx| Box::new(non_copy_const::NonCopyConst::new(tcx, conf))),
|
||||
Box::new(|_| Box::new(redundant_clone::RedundantClone)),
|
||||
Box::new(|_| Box::new(slow_vector_initialization::SlowVectorInit)),
|
||||
Box::new(move |_| Box::new(unnecessary_wraps::UnnecessaryWraps::new(conf))),
|
||||
Box::new(|_| Box::new(assertions_on_constants::AssertionsOnConstants::new(conf))),
|
||||
Box::new(|_| Box::new(assertions_on_result_states::AssertionsOnResultStates)),
|
||||
Box::new(|_| Box::new(inherent_to_string::InherentToString)),
|
||||
Box::new(move |_| Box::new(trait_bounds::TraitBounds::new(conf))),
|
||||
Box::new(|_| Box::new(comparison_chain::ComparisonChain)),
|
||||
Box::new(move |tcx| Box::new(mut_key::MutableKeyType::new(tcx, conf))),
|
||||
Box::new(|_| Box::new(reference::DerefAddrOf)),
|
||||
{
|
||||
let format_args = format_args_storage.clone();
|
||||
Box::new(move |_| Box::new(format_impl::FormatImpl::new(format_args.clone())))
|
||||
},
|
||||
Box::new(|_| Box::new(redundant_closure_call::RedundantClosureCall)),
|
||||
Box::new(|_| Box::new(unused_unit::UnusedUnit)),
|
||||
Box::new(|_| Box::new(returns::Return)),
|
||||
Box::new(move |_| Box::new(collapsible_if::CollapsibleIf::new(conf))),
|
||||
Box::new(|_| Box::new(items_after_statements::ItemsAfterStatements)),
|
||||
Box::new(|_| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals)),
|
||||
Box::new(|_| Box::new(needless_continue::NeedlessContinue)),
|
||||
Box::new(|_| Box::new(create_dir::CreateDir)),
|
||||
Box::new(move |_| Box::new(item_name_repetitions::ItemNameRepetitions::new(conf))),
|
||||
Box::new(move |_| Box::new(upper_case_acronyms::UpperCaseAcronyms::new(conf))),
|
||||
Box::new(|_| Box::<default::Default>::default()),
|
||||
Box::new(move |_| Box::new(unused_self::UnusedSelf::new(conf))),
|
||||
Box::new(|_| Box::new(mutable_debug_assertion::DebugAssertWithMutCall)),
|
||||
Box::new(|_| Box::new(exit::Exit)),
|
||||
Box::new(move |_| Box::new(to_digit_is_some::ToDigitIsSome::new(conf))),
|
||||
Box::new(move |_| Box::new(large_stack_arrays::LargeStackArrays::new(conf))),
|
||||
Box::new(move |_| Box::new(large_const_arrays::LargeConstArrays::new(conf))),
|
||||
Box::new(|_| Box::new(floating_point_arithmetic::FloatingPointArithmetic)),
|
||||
Box::new(|_| Box::new(as_conversions::AsConversions)),
|
||||
Box::new(|_| Box::new(let_underscore::LetUnderscore)),
|
||||
Box::new(move |_| Box::new(excessive_bools::ExcessiveBools::new(conf))),
|
||||
Box::new(move |_| Box::new(wildcard_imports::WildcardImports::new(conf))),
|
||||
Box::new(|_| Box::<redundant_pub_crate::RedundantPubCrate>::default()),
|
||||
Box::new(|_| Box::<dereference::Dereferencing<'_>>::default()),
|
||||
Box::new(|_| Box::new(option_if_let_else::OptionIfLetElse)),
|
||||
Box::new(|_| Box::new(future_not_send::FutureNotSend)),
|
||||
Box::new(move |_| Box::new(large_futures::LargeFuture::new(conf))),
|
||||
Box::new(|_| Box::new(if_let_mutex::IfLetMutex)),
|
||||
Box::new(|_| Box::new(if_not_else::IfNotElse)),
|
||||
Box::new(|_| Box::new(equatable_if_let::PatternEquality)),
|
||||
Box::new(|_| Box::new(manual_async_fn::ManualAsyncFn)),
|
||||
Box::new(|_| Box::new(panic_in_result_fn::PanicInResultFn)),
|
||||
Box::new(|_| Box::<macro_use::MacroUseImports>::default()),
|
||||
Box::new(|_| Box::new(pattern_type_mismatch::PatternTypeMismatch)),
|
||||
Box::new(|_| Box::<unwrap_in_result::UnwrapInResult>::default()),
|
||||
Box::new(|_| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned)),
|
||||
Box::new(|_| Box::new(async_yields_async::AsyncYieldsAsync)),
|
||||
{
|
||||
let attrs = attr_storage.clone();
|
||||
Box::new(move |tcx| Box::new(disallowed_macros::DisallowedMacros::new(tcx, conf, attrs.clone())))
|
||||
},
|
||||
Box::new(move |tcx| Box::new(disallowed_methods::DisallowedMethods::new(tcx, conf))),
|
||||
Box::new(|_| Box::new(empty_drop::EmptyDrop)),
|
||||
Box::new(|_| Box::new(strings::StrToString)),
|
||||
Box::new(|_| Box::new(zero_sized_map_values::ZeroSizedMapValues)),
|
||||
Box::new(|_| Box::<vec_init_then_push::VecInitThenPush>::default()),
|
||||
Box::new(|_| Box::new(redundant_slicing::RedundantSlicing)),
|
||||
Box::new(|_| Box::new(from_str_radix_10::FromStrRadix10)),
|
||||
Box::new(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(conf))),
|
||||
Box::new(|_| Box::new(bool_assert_comparison::BoolAssertComparison)),
|
||||
Box::new(|_| Box::<unused_async::UnusedAsync>::default()),
|
||||
Box::new(move |tcx| Box::new(disallowed_types::DisallowedTypes::new(tcx, conf))),
|
||||
Box::new(move |tcx| Box::new(missing_enforced_import_rename::ImportRename::new(tcx, conf))),
|
||||
Box::new(|_| Box::new(strlen_on_c_strings::StrlenOnCStrings)),
|
||||
Box::new(move |_| Box::new(self_named_constructors::SelfNamedConstructors)),
|
||||
Box::new(move |_| Box::new(iter_not_returning_iterator::IterNotReturningIterator)),
|
||||
Box::new(move |_| Box::new(manual_assert::ManualAssert)),
|
||||
Box::new(move |_| Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new(conf))),
|
||||
Box::new(move |_| Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::new(conf))),
|
||||
{
|
||||
let format_args = format_args_storage.clone();
|
||||
Box::new(move |tcx| Box::new(format_args::FormatArgs::new(tcx, conf, format_args.clone())))
|
||||
},
|
||||
Box::new(|_| Box::new(trailing_empty_array::TrailingEmptyArray)),
|
||||
Box::new(|_| Box::new(needless_late_init::NeedlessLateInit)),
|
||||
Box::new(|_| Box::new(return_self_not_must_use::ReturnSelfNotMustUse)),
|
||||
Box::new(|_| Box::new(init_numbered_fields::NumberedFields)),
|
||||
Box::new(move |_| Box::new(manual_bits::ManualBits::new(conf))),
|
||||
Box::new(|_| Box::new(default_union_representation::DefaultUnionRepresentation)),
|
||||
Box::new(|_| Box::<only_used_in_recursion::OnlyUsedInRecursion>::default()),
|
||||
Box::new(move |_| Box::new(dbg_macro::DbgMacro::new(conf))),
|
||||
{
|
||||
let format_args = format_args_storage.clone();
|
||||
Box::new(move |_| Box::new(write::Write::new(conf, format_args.clone())))
|
||||
},
|
||||
Box::new(move |_| Box::new(cargo::Cargo::new(conf))),
|
||||
Box::new(|_| Box::new(empty_with_brackets::EmptyWithBrackets::default())),
|
||||
Box::new(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings)),
|
||||
Box::new(|_| Box::new(format_push_string::FormatPushString)),
|
||||
Box::new(move |_| Box::new(large_include_file::LargeIncludeFile::new(conf))),
|
||||
Box::new(|_| Box::new(strings::TrimSplitWhitespace)),
|
||||
Box::new(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)),
|
||||
Box::new(|_| Box::new(swap_ptr_to_ref::SwapPtrToRef)),
|
||||
Box::new(|_| Box::new(mismatching_type_param_order::TypeParamMismatch)),
|
||||
Box::new(|_| Box::new(read_zero_byte_vec::ReadZeroByteVec)),
|
||||
Box::new(|_| Box::new(default_instead_of_iter_empty::DefaultIterEmpty)),
|
||||
Box::new(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(conf))),
|
||||
Box::new(move |_| Box::new(manual_retain::ManualRetain::new(conf))),
|
||||
Box::new(move |_| Box::new(manual_rotate::ManualRotate)),
|
||||
Box::new(move |_| Box::new(operators::Operators::new(conf))),
|
||||
Box::new(move |_| Box::new(std_instead_of_core::StdReexports::new(conf))),
|
||||
Box::new(move |_| Box::new(time_subtraction::UncheckedTimeSubtraction::new(conf))),
|
||||
Box::new(|_| Box::new(partialeq_to_none::PartialeqToNone)),
|
||||
Box::new(move |_| Box::new(manual_abs_diff::ManualAbsDiff::new(conf))),
|
||||
Box::new(move |_| Box::new(manual_clamp::ManualClamp::new(conf))),
|
||||
Box::new(|_| Box::new(manual_string_new::ManualStringNew)),
|
||||
Box::new(|_| Box::new(unused_peekable::UnusedPeekable)),
|
||||
Box::new(|_| Box::new(bool_to_int_with_if::BoolToIntWithIf)),
|
||||
Box::new(|_| Box::new(box_default::BoxDefault)),
|
||||
Box::new(|_| Box::new(implicit_saturating_add::ImplicitSaturatingAdd)),
|
||||
Box::new(|_| Box::new(missing_trait_methods::MissingTraitMethods)),
|
||||
Box::new(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr)),
|
||||
Box::new(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow)),
|
||||
Box::new(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(conf))),
|
||||
Box::new(move |_| Box::new(semicolon_block::SemicolonBlock::new(conf))),
|
||||
Box::new(|_| Box::new(permissions_set_readonly_false::PermissionsSetReadonlyFalse)),
|
||||
Box::new(|_| Box::new(size_of_ref::SizeOfRef)),
|
||||
Box::new(|_| Box::new(multiple_unsafe_ops_per_block::MultipleUnsafeOpsPerBlock)),
|
||||
Box::new(move |_| Box::new(extra_unused_type_parameters::ExtraUnusedTypeParameters::new(conf))),
|
||||
Box::new(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi)),
|
||||
Box::new(|_| Box::new(collection_is_never_read::CollectionIsNeverRead)),
|
||||
Box::new(|_| Box::new(missing_assert_message::MissingAssertMessage)),
|
||||
Box::new(|_| Box::new(needless_maybe_sized::NeedlessMaybeSized)),
|
||||
Box::new(|_| Box::new(redundant_async_block::RedundantAsyncBlock)),
|
||||
Box::new(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(conf))),
|
||||
Box::new(|_| Box::new(unnecessary_struct_initialization::UnnecessaryStruct)),
|
||||
Box::new(move |_| Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new(conf))),
|
||||
Box::new(|_| Box::new(tests_outside_test_module::TestsOutsideTestModule)),
|
||||
Box::new(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation::new(conf))),
|
||||
Box::new(|_| Box::new(items_after_test_module::ItemsAfterTestModule)),
|
||||
Box::new(|_| Box::new(default_constructed_unit_structs::DefaultConstructedUnitStructs)),
|
||||
Box::new(|_| Box::new(missing_fields_in_debug::MissingFieldsInDebug)),
|
||||
Box::new(|_| Box::new(endian_bytes::EndianBytes)),
|
||||
Box::new(|_| Box::new(redundant_type_annotations::RedundantTypeAnnotations)),
|
||||
Box::new(|_| Box::new(arc_with_non_send_sync::ArcWithNonSendSync)),
|
||||
Box::new(|_| Box::new(needless_ifs::NeedlessIfs)),
|
||||
Box::new(move |_| Box::new(min_ident_chars::MinIdentChars::new(conf))),
|
||||
Box::new(move |_| Box::new(large_stack_frames::LargeStackFrames::new(conf))),
|
||||
Box::new(|_| Box::new(single_range_in_vec_init::SingleRangeInVecInit)),
|
||||
Box::new(move |_| Box::new(needless_pass_by_ref_mut::NeedlessPassByRefMut::new(conf))),
|
||||
Box::new(|tcx| Box::new(non_canonical_impls::NonCanonicalImpls::new(tcx))),
|
||||
Box::new(move |_| Box::new(single_call_fn::SingleCallFn::new(conf))),
|
||||
Box::new(move |_| Box::new(legacy_numeric_constants::LegacyNumericConstants::new(conf))),
|
||||
Box::new(|_| Box::new(manual_range_patterns::ManualRangePatterns)),
|
||||
Box::new(move |_| Box::new(tuple_array_conversions::TupleArrayConversions::new(conf))),
|
||||
Box::new(move |_| Box::new(manual_float_methods::ManualFloatMethods::new(conf))),
|
||||
Box::new(|_| Box::new(four_forward_slashes::FourForwardSlashes)),
|
||||
Box::new(|_| Box::new(error_impl_error::ErrorImplError)),
|
||||
Box::new(move |_| Box::new(absolute_paths::AbsolutePaths::new(conf))),
|
||||
Box::new(|_| Box::new(redundant_locals::RedundantLocals)),
|
||||
Box::new(|_| Box::new(ignored_unit_patterns::IgnoredUnitPatterns)),
|
||||
Box::new(|_| Box::<reserve_after_initialization::ReserveAfterInitialization>::default()),
|
||||
Box::new(|_| Box::new(implied_bounds_in_impls::ImpliedBoundsInImpls)),
|
||||
Box::new(|_| Box::new(missing_asserts_for_indexing::MissingAssertsForIndexing)),
|
||||
Box::new(|_| Box::new(unnecessary_map_on_constructor::UnnecessaryMapOnConstructor)),
|
||||
Box::new(move |_| {
|
||||
Box::new(needless_borrows_for_generic_args::NeedlessBorrowsForGenericArgs::new(
|
||||
conf,
|
||||
))
|
||||
}),
|
||||
Box::new(move |_| Box::new(manual_hash_one::ManualHashOne::new(conf))),
|
||||
Box::new(|_| Box::new(iter_without_into_iter::IterWithoutIntoIter)),
|
||||
Box::new(|_| Box::<pathbuf_init_then_push::PathbufThenPush<'_>>::default()),
|
||||
Box::new(|_| Box::new(iter_over_hash_type::IterOverHashType)),
|
||||
Box::new(|_| Box::new(impl_hash_with_borrow_str_and_bytes::ImplHashWithBorrowStrBytes)),
|
||||
Box::new(move |_| Box::new(repeat_vec_with_capacity::RepeatVecWithCapacity::new(conf))),
|
||||
Box::new(|_| Box::new(uninhabited_references::UninhabitedReferences)),
|
||||
Box::new(|_| Box::new(ineffective_open_options::IneffectiveOpenOptions)),
|
||||
Box::new(|_| Box::<unconditional_recursion::UnconditionalRecursion>::default()),
|
||||
Box::new(move |_| Box::new(pub_underscore_fields::PubUnderscoreFields::new(conf))),
|
||||
Box::new(move |_| Box::new(missing_const_for_thread_local::MissingConstForThreadLocal::new(conf))),
|
||||
Box::new(move |tcx| Box::new(incompatible_msrv::IncompatibleMsrv::new(tcx, conf))),
|
||||
Box::new(|_| Box::new(to_string_trait_impl::ToStringTraitImpl)),
|
||||
Box::new(move |_| Box::new(assigning_clones::AssigningClones::new(conf))),
|
||||
Box::new(|_| Box::new(zero_repeat_side_effects::ZeroRepeatSideEffects)),
|
||||
Box::new(move |_| Box::new(macro_metavars_in_unsafe::ExprMetavarsInUnsafe::new(conf))),
|
||||
Box::new(move |_| Box::new(string_patterns::StringPatterns::new(conf))),
|
||||
Box::new(|_| Box::new(set_contains_or_insert::SetContainsOrInsert)),
|
||||
Box::new(|_| Box::new(zombie_processes::ZombieProcesses)),
|
||||
Box::new(|_| Box::new(pointers_in_nomem_asm_block::PointersInNomemAsmBlock)),
|
||||
Box::new(move |_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo::new(conf))),
|
||||
Box::new(|_| Box::new(non_zero_suggestions::NonZeroSuggestions)),
|
||||
Box::new(|_| Box::new(literal_string_with_formatting_args::LiteralStringWithFormattingArg)),
|
||||
Box::new(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf))),
|
||||
Box::new(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp)),
|
||||
Box::new(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound)),
|
||||
Box::new(move |_| Box::new(arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new(conf))),
|
||||
Box::new(|_| Box::new(useless_concat::UselessConcat)),
|
||||
Box::new(|_| Box::new(unneeded_struct_pattern::UnneededStructPattern)),
|
||||
Box::new(|_| Box::<unnecessary_semicolon::UnnecessarySemicolon>::default()),
|
||||
Box::new(move |_| Box::new(non_std_lazy_statics::NonStdLazyStatic::new(conf))),
|
||||
Box::new(|_| Box::new(manual_option_as_slice::ManualOptionAsSlice::new(conf))),
|
||||
Box::new(|_| Box::new(single_option_map::SingleOptionMap)),
|
||||
Box::new(move |_| Box::new(redundant_test_prefix::RedundantTestPrefix)),
|
||||
Box::new(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf))),
|
||||
Box::new(|_| Box::new(infallible_try_from::InfallibleTryFrom)),
|
||||
Box::new(|_| Box::new(coerce_container_to_any::CoerceContainerToAny)),
|
||||
Box::new(|_| Box::new(toplevel_ref_arg::ToplevelRefArg)),
|
||||
Box::new(|_| Box::new(volatile_composites::VolatileComposites)),
|
||||
Box::new(|_| Box::<replace_box::ReplaceBox>::default()),
|
||||
// add late passes here, used by `cargo dev new_lint`
|
||||
];
|
||||
store.late_passes.extend(late_lints);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath};
|
|||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::usage::contains_return_break_continue_macro;
|
||||
use clippy_utils::{higher, peel_blocks_with_stmt};
|
||||
use clippy_utils::{as_some_expr, higher, peel_blocks_with_stmt};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, Node, Pat, PatKind, Stmt, StmtKind};
|
||||
|
|
@ -33,8 +33,7 @@ pub(super) fn check<'tcx>(
|
|||
&& let [stmt] = block.stmts
|
||||
&& let StmtKind::Semi(semi) = stmt.kind
|
||||
&& let ExprKind::Ret(Some(ret_value)) = semi.kind
|
||||
&& let ExprKind::Call(ctor, [inner_ret]) = ret_value.kind
|
||||
&& ctor.res(cx).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome)
|
||||
&& let Some(inner_ret) = as_some_expr(cx, ret_value)
|
||||
&& inner_ret.res_local_id() == Some(binding_id)
|
||||
&& !contains_return_break_continue_macro(cond)
|
||||
&& let Some((last_stmt, last_ret)) = last_stmt_and_ret(cx, expr)
|
||||
|
|
|
|||
|
|
@ -880,6 +880,15 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
|
|||
missing_spin_loop::check(cx, condition, body);
|
||||
manual_while_let_some::check(cx, condition, body, span);
|
||||
}
|
||||
|
||||
if let ExprKind::MethodCall(path, recv, [arg], _) = expr.kind
|
||||
&& matches!(
|
||||
path.ident.name,
|
||||
sym::all | sym::any | sym::filter_map | sym::find_map | sym::flat_map | sym::for_each | sym::map
|
||||
)
|
||||
{
|
||||
unused_enumerate_index::check_method(cx, expr, recv, arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -908,7 +917,7 @@ impl Loops {
|
|||
same_item_push::check(cx, pat, arg, body, expr, self.msrv);
|
||||
manual_flatten::check(cx, pat, arg, body, span, self.msrv);
|
||||
manual_find::check(cx, pat, arg, body, span, expr);
|
||||
unused_enumerate_index::check(cx, pat, arg, body);
|
||||
unused_enumerate_index::check(cx, arg, pat, None, body);
|
||||
char_indices_as_byte_indices::check(cx, pat, arg, body);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,42 +1,87 @@
|
|||
use super::UNUSED_ENUMERATE_INDEX;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::res::MaybeDef;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::{pat_is_wild, sugg};
|
||||
use clippy_utils::diagnostics::span_lint_hir_and_then;
|
||||
use clippy_utils::res::{MaybeDef, MaybeTypeckRes};
|
||||
use clippy_utils::source::{SpanRangeExt, walk_span_to_context};
|
||||
use clippy_utils::{expr_or_init, pat_is_wild};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::{Expr, ExprKind, Pat, PatKind};
|
||||
use rustc_hir::{Expr, ExprKind, Pat, PatKind, TyKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
use rustc_span::{Span, SyntaxContext, sym};
|
||||
|
||||
/// Checks for the `UNUSED_ENUMERATE_INDEX` lint.
|
||||
///
|
||||
/// The lint is also partially implemented in `clippy_lints/src/methods/unused_enumerate_index.rs`.
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>, arg: &Expr<'_>, body: &'tcx Expr<'tcx>) {
|
||||
if let PatKind::Tuple([index, elem], _) = pat.kind
|
||||
&& let ExprKind::MethodCall(_method, self_arg, [], _) = arg.kind
|
||||
&& let ty = cx.typeck_results().expr_ty(arg)
|
||||
&& pat_is_wild(cx, &index.kind, body)
|
||||
&& ty.is_diag_item(cx, sym::Enumerate)
|
||||
&& let Some((DefKind::AssocFn, call_id)) = cx.typeck_results().type_dependent_def(arg.hir_id)
|
||||
&& cx.tcx.is_diagnostic_item(sym::enumerate_method, call_id)
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
iter_expr: &'tcx Expr<'tcx>,
|
||||
pat: &Pat<'tcx>,
|
||||
ty_spans: Option<(Span, Span)>,
|
||||
body: &'tcx Expr<'tcx>,
|
||||
) {
|
||||
if let PatKind::Tuple([idx_pat, inner_pat], _) = pat.kind
|
||||
&& cx.typeck_results().expr_ty(iter_expr).is_diag_item(cx, sym::Enumerate)
|
||||
&& pat_is_wild(cx, &idx_pat.kind, body)
|
||||
&& let enumerate_call = expr_or_init(cx, iter_expr)
|
||||
&& let ExprKind::MethodCall(_, _, [], enumerate_span) = enumerate_call.kind
|
||||
&& let Some(enumerate_id) = cx.typeck_results().type_dependent_def_id(enumerate_call.hir_id)
|
||||
&& cx.tcx.is_diagnostic_item(sym::enumerate_method, enumerate_id)
|
||||
&& !enumerate_call.span.from_expansion()
|
||||
&& !pat.span.from_expansion()
|
||||
&& !idx_pat.span.from_expansion()
|
||||
&& !inner_pat.span.from_expansion()
|
||||
&& let Some(enumerate_range) = enumerate_span.map_range(cx, |_, text, range| {
|
||||
text.get(..range.start)?
|
||||
.ends_with('.')
|
||||
.then_some(range.start - 1..range.end)
|
||||
})
|
||||
{
|
||||
span_lint_and_then(
|
||||
let enumerate_span = Span::new(enumerate_range.start, enumerate_range.end, SyntaxContext::root(), None);
|
||||
span_lint_hir_and_then(
|
||||
cx,
|
||||
UNUSED_ENUMERATE_INDEX,
|
||||
arg.span,
|
||||
enumerate_call.hir_id,
|
||||
enumerate_span,
|
||||
"you seem to use `.enumerate()` and immediately discard the index",
|
||||
|diag| {
|
||||
let base_iter = sugg::Sugg::hir(cx, self_arg, "base iter");
|
||||
let mut spans = Vec::with_capacity(5);
|
||||
spans.push((enumerate_span, String::new()));
|
||||
spans.push((pat.span.with_hi(inner_pat.span.lo()), String::new()));
|
||||
spans.push((pat.span.with_lo(inner_pat.span.hi()), String::new()));
|
||||
if let Some((outer, inner)) = ty_spans {
|
||||
spans.push((outer.with_hi(inner.lo()), String::new()));
|
||||
spans.push((outer.with_lo(inner.hi()), String::new()));
|
||||
}
|
||||
diag.multipart_suggestion(
|
||||
"remove the `.enumerate()` call",
|
||||
vec![
|
||||
(pat.span, snippet(cx, elem.span, "..").into_owned()),
|
||||
(arg.span, base_iter.to_string()),
|
||||
],
|
||||
spans,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn check_method<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
e: &'tcx Expr<'tcx>,
|
||||
recv: &'tcx Expr<'tcx>,
|
||||
arg: &'tcx Expr<'tcx>,
|
||||
) {
|
||||
if let ExprKind::Closure(closure) = arg.kind
|
||||
&& let body = cx.tcx.hir_body(closure.body)
|
||||
&& let [param] = body.params
|
||||
&& cx.ty_based_def(e).opt_parent(cx).is_diag_item(cx, sym::Iterator)
|
||||
&& let [input] = closure.fn_decl.inputs
|
||||
&& !arg.span.from_expansion()
|
||||
&& !input.span.from_expansion()
|
||||
&& !recv.span.from_expansion()
|
||||
&& !param.span.from_expansion()
|
||||
{
|
||||
let ty_spans = if let TyKind::Tup([_, inner]) = input.kind {
|
||||
let Some(inner) = walk_span_to_context(inner.span, SyntaxContext::root()) else {
|
||||
return;
|
||||
};
|
||||
Some((input.span, inner))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
check(cx, recv, param.pat, ty_spans, body.value);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
|
|||
use clippy_utils::res::{MaybeDef, MaybeTypeckRes};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::visitors::is_res_used;
|
||||
use clippy_utils::{get_enclosing_loop_or_multi_call_closure, higher, is_refutable};
|
||||
use clippy_utils::{as_some_pattern, get_enclosing_loop_or_multi_call_closure, higher, is_refutable};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::intravisit::{Visitor, walk_expr};
|
||||
use rustc_hir::{Closure, Expr, ExprKind, HirId, LangItem, LetStmt, Mutability, PatKind, UnOp};
|
||||
use rustc_hir::{Closure, Expr, ExprKind, HirId, LetStmt, Mutability, UnOp};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::hir::nested_filter::OnlyBodies;
|
||||
use rustc_middle::ty::adjustment::Adjust;
|
||||
|
|
@ -19,8 +19,7 @@ use rustc_span::symbol::sym;
|
|||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let Some(higher::WhileLet { if_then, let_pat, let_expr, label, .. }) = higher::WhileLet::hir(expr)
|
||||
// check for `Some(..)` pattern
|
||||
&& let PatKind::TupleStruct(ref pat_path, some_pat, _) = let_pat.kind
|
||||
&& cx.qpath_res(pat_path, let_pat.hir_id).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome)
|
||||
&& let Some(some_pat) = as_some_pattern(cx, let_pat)
|
||||
// check for call to `Iterator::next`
|
||||
&& let ExprKind::MethodCall(method_name, iter_expr, [], _) = let_expr.kind
|
||||
&& method_name.ident.name == sym::next
|
||||
|
|
|
|||
|
|
@ -70,12 +70,12 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsPowerOfTwo {
|
|||
if !expr.span.from_expansion()
|
||||
&& let Some((lhs, rhs)) = unexpanded_binop_operands(expr, BinOpKind::Eq)
|
||||
{
|
||||
if let Some(a) = count_ones_receiver(cx, lhs)
|
||||
&& is_integer_literal(rhs, 1)
|
||||
if is_integer_literal(rhs, 1)
|
||||
&& let Some(a) = count_ones_receiver(cx, lhs)
|
||||
{
|
||||
self.build_sugg(cx, expr, a);
|
||||
} else if let Some(a) = count_ones_receiver(cx, rhs)
|
||||
&& is_integer_literal(lhs, 1)
|
||||
} else if is_integer_literal(lhs, 1)
|
||||
&& let Some(a) = count_ones_receiver(cx, rhs)
|
||||
{
|
||||
self.build_sugg(cx, expr, a);
|
||||
} else if is_integer_literal(rhs, 0)
|
||||
|
|
|
|||
|
|
@ -3,10 +3,9 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
|||
use clippy_utils::msrvs::Msrv;
|
||||
use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath};
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::{is_none_pattern, msrvs, peel_hir_expr_refs, sym};
|
||||
use clippy_utils::{as_some_pattern, is_none_pattern, msrvs, peel_hir_expr_refs, sym};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, LangItem, Pat, PatKind, QPath, is_range_literal};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, Pat, PatKind, QPath, is_range_literal};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{Span, Symbol};
|
||||
|
|
@ -154,10 +153,8 @@ fn check_as_ref(cx: &LateContext<'_>, expr: &Expr<'_>, span: Span, msrv: Msrv) {
|
|||
}
|
||||
|
||||
fn extract_ident_from_some_pat(cx: &LateContext<'_>, pat: &Pat<'_>) -> Option<Symbol> {
|
||||
if let PatKind::TupleStruct(QPath::Resolved(None, path), [binding], _) = pat.kind
|
||||
&& let Res::Def(DefKind::Ctor(..), def_id) = path.res
|
||||
if let Some([binding]) = as_some_pattern(cx, pat)
|
||||
&& let PatKind::Binding(_mode, _hir_id, ident, _inner_pat) = binding.kind
|
||||
&& clippy_utils::is_lang_item_or_ctor(cx, def_id, LangItem::OptionSome)
|
||||
{
|
||||
Some(ident.name)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::res::MaybeDef;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::{higher, sym};
|
||||
use clippy_utils::{as_some_pattern, higher, sym};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, LangItem, PatKind};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
||||
|
|
@ -55,10 +55,9 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk {
|
|||
};
|
||||
|
||||
if let ExprKind::MethodCall(ok_path, recv, [], ..) = let_expr.kind //check is expr.ok() has type Result<T,E>.ok(, _)
|
||||
&& let PatKind::TupleStruct(ref pat_path, [ok_pat], _) = let_pat.kind //get operation
|
||||
&& ok_path.ident.name == sym::ok
|
||||
&& cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result)
|
||||
&& cx.qpath_res(pat_path, let_pat.hir_id).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome)
|
||||
&& let Some([ok_pat]) = as_some_pattern(cx, let_pat) //get operation
|
||||
&& let ctxt = expr.span.ctxt()
|
||||
&& let_expr.span.ctxt() == ctxt
|
||||
&& let_pat.span.ctxt() == ctxt
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
use clippy_utils::as_some_expr;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath};
|
||||
use clippy_utils::visitors::contains_unsafe_block;
|
||||
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_hir::LangItem::OptionNone;
|
||||
use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::{SyntaxContext, sym};
|
||||
|
|
@ -52,21 +53,19 @@ fn peels_blocks_incl_unsafe<'a>(expr: &'a Expr<'a>) -> &'a Expr<'a> {
|
|||
peels_blocks_incl_unsafe_opt(expr).unwrap_or(expr)
|
||||
}
|
||||
|
||||
// function called for each <expr> expression:
|
||||
/// Checks whether <expr> resolves to `Some(target)`
|
||||
// NOTE: called for each <expr> expression:
|
||||
// Some(x) => if <cond> {
|
||||
// <expr>
|
||||
// } else {
|
||||
// <expr>
|
||||
// }
|
||||
// Returns true if <expr> resolves to `Some(x)`, `false` otherwise
|
||||
fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: &Expr<'_>) -> bool {
|
||||
if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr)
|
||||
// there can be not statements in the block as they would be removed when switching to `.filter`
|
||||
&& let ExprKind::Call(callee, [arg]) = inner_expr.kind
|
||||
&& let Some(arg) = as_some_expr(cx, inner_expr)
|
||||
{
|
||||
return ctxt == expr.span.ctxt()
|
||||
&& callee.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome)
|
||||
&& arg.res_local_id() == Some(target);
|
||||
return ctxt == expr.span.ctxt() && arg.res_local_id() == Some(target);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::res::{MaybeDef, MaybeQPath};
|
||||
use clippy_utils::res::MaybeDef;
|
||||
use clippy_utils::source::{indent_of, reindent_multiline};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::{option_arg_ty, peel_and_count_ty_refs};
|
||||
use clippy_utils::{get_parent_expr, peel_blocks, span_contains_comment};
|
||||
use clippy_utils::{as_some_expr, get_parent_expr, is_none_expr, peel_blocks, span_contains_comment};
|
||||
use rustc_ast::{BindingMode, Mutability};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr};
|
||||
use rustc_hir::LangItem::ResultErr;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, Pat, PatExpr, PatExprKind, PatKind, Path, QPath};
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
|
|
@ -106,8 +106,7 @@ fn is_ok_or_err<'hir>(cx: &LateContext<'_>, pat: &Pat<'hir>) -> Option<(bool, &'
|
|||
|
||||
/// Check if `expr` contains `Some(ident)`, possibly as a block
|
||||
fn is_some_ident<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, ident: &Ident, ty: Ty<'tcx>) -> bool {
|
||||
if let ExprKind::Call(body_callee, [body_arg]) = peel_blocks(expr).kind
|
||||
&& body_callee.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome)
|
||||
if let Some(body_arg) = as_some_expr(cx, peel_blocks(expr))
|
||||
&& cx.typeck_results().expr_ty(body_arg) == ty
|
||||
&& let ExprKind::Path(QPath::Resolved(
|
||||
_,
|
||||
|
|
@ -124,7 +123,7 @@ fn is_some_ident<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, ident: &Ident, t
|
|||
|
||||
/// Check if `expr` is `None`, possibly as a block
|
||||
fn is_none(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
peel_blocks(expr).res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone)
|
||||
is_none_expr(cx, peel_blocks(expr))
|
||||
}
|
||||
|
||||
/// Suggest replacing `expr` by `scrutinee.METHOD()`, where `METHOD` is either `ok` or
|
||||
|
|
|
|||
|
|
@ -1,18 +1,17 @@
|
|||
use crate::map_unit_fn::OPTION_MAP_UNIT_FN;
|
||||
use crate::matches::MATCH_AS_REF;
|
||||
use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath};
|
||||
use clippy_utils::res::{MaybeDef, MaybeResPath};
|
||||
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::{is_copy, is_unsafe_fn, peel_and_count_ty_refs};
|
||||
use clippy_utils::{
|
||||
CaptureKind, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed, peel_blocks,
|
||||
peel_hir_expr_refs, peel_hir_expr_while,
|
||||
CaptureKind, as_some_pattern, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed,
|
||||
is_none_expr, is_none_pattern, peel_blocks, peel_hir_expr_refs, peel_hir_expr_while,
|
||||
};
|
||||
use rustc_ast::util::parser::ExprPrecedence;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::{BindingMode, Expr, ExprKind, HirId, Mutability, Pat, PatExpr, PatExprKind, PatKind, Path, QPath};
|
||||
use rustc_hir::{BindingMode, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::{SyntaxContext, sym};
|
||||
|
||||
|
|
@ -44,16 +43,16 @@ where
|
|||
try_parse_pattern(cx, then_pat, expr_ctxt),
|
||||
else_pat.map_or(Some(OptionPat::Wild), |p| try_parse_pattern(cx, p, expr_ctxt)),
|
||||
) {
|
||||
(Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => {
|
||||
(Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_arm_body(cx, then_body) => {
|
||||
(else_body, pattern, ref_count, true)
|
||||
},
|
||||
(Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => {
|
||||
(Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_arm_body(cx, then_body) => {
|
||||
(else_body, pattern, ref_count, false)
|
||||
},
|
||||
(Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_body) => {
|
||||
(Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_arm_body(cx, else_body) => {
|
||||
(then_body, pattern, ref_count, true)
|
||||
},
|
||||
(Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_body) => {
|
||||
(Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_arm_body(cx, else_body) => {
|
||||
(then_body, pattern, ref_count, false)
|
||||
},
|
||||
_ => return None,
|
||||
|
|
@ -255,23 +254,9 @@ pub(super) fn try_parse_pattern<'tcx>(
|
|||
match pat.kind {
|
||||
PatKind::Wild => Some(OptionPat::Wild),
|
||||
PatKind::Ref(pat, _, _) => f(cx, pat, ref_count + 1, ctxt),
|
||||
PatKind::Expr(PatExpr {
|
||||
kind: PatExprKind::Path(qpath),
|
||||
hir_id,
|
||||
..
|
||||
}) if cx
|
||||
.qpath_res(qpath, *hir_id)
|
||||
.ctor_parent(cx)
|
||||
.is_lang_item(cx, OptionNone) =>
|
||||
{
|
||||
Some(OptionPat::None)
|
||||
},
|
||||
PatKind::TupleStruct(ref qpath, [pattern], _)
|
||||
if cx
|
||||
.qpath_res(qpath, pat.hir_id)
|
||||
.ctor_parent(cx)
|
||||
.is_lang_item(cx, OptionSome)
|
||||
&& pat.span.ctxt() == ctxt =>
|
||||
_ if is_none_pattern(cx, pat) => Some(OptionPat::None),
|
||||
_ if let Some([pattern]) = as_some_pattern(cx, pat)
|
||||
&& pat.span.ctxt() == ctxt =>
|
||||
{
|
||||
Some(OptionPat::Some { pattern, ref_count })
|
||||
},
|
||||
|
|
@ -281,7 +266,7 @@ pub(super) fn try_parse_pattern<'tcx>(
|
|||
f(cx, pat, 0, ctxt)
|
||||
}
|
||||
|
||||
// Checks for the `None` value.
|
||||
fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
peel_blocks(expr).res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone)
|
||||
/// Checks for the `None` value, possibly in a block.
|
||||
fn is_none_arm_body(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
is_none_expr(cx, peel_blocks(expr))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::res::{MaybeDef, MaybeQPath};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::option_arg_ty;
|
||||
use clippy_utils::{is_none_arm, peel_blocks};
|
||||
use clippy_utils::{as_some_expr, as_some_pattern, is_none_arm, peel_blocks};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, LangItem, Mutability, PatKind, QPath};
|
||||
use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, Mutability, PatKind, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
|
||||
|
|
@ -82,14 +81,9 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr:
|
|||
|
||||
// Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`)
|
||||
fn as_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<Mutability> {
|
||||
if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind
|
||||
&& cx
|
||||
.qpath_res(qpath, arm.pat.hir_id)
|
||||
.ctor_parent(cx)
|
||||
.is_lang_item(cx, LangItem::OptionSome)
|
||||
if let Some([first_pat, ..]) = as_some_pattern(cx, arm.pat)
|
||||
&& let PatKind::Binding(BindingMode(ByRef::Yes(_, mutabl), _), .., ident, _) = first_pat.kind
|
||||
&& let ExprKind::Call(e, [arg]) = peel_blocks(arm.body).kind
|
||||
&& e.res(cx).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome)
|
||||
&& let Some(arg) = as_some_expr(cx, peel_blocks(arm.body))
|
||||
&& let ExprKind::Path(QPath::Resolved(_, path2)) = arg.kind
|
||||
&& path2.segments.len() == 1
|
||||
&& ident.name == path2.segments[0].ident.name
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use rustc_data_structures::fx::FxHashSet;
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_path, walk_stmt};
|
||||
use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, Node, PatKind, Path, Stmt, StmtKind};
|
||||
use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, Item, ItemKind, Node, PatKind, Path, Stmt, StmtKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::{Span, Symbol};
|
||||
|
||||
|
|
@ -307,26 +307,6 @@ fn expr_in_nested_block(cx: &LateContext<'_>, match_expr: &Expr<'_>) -> bool {
|
|||
false
|
||||
}
|
||||
|
||||
fn expr_must_have_curlies(cx: &LateContext<'_>, match_expr: &Expr<'_>) -> bool {
|
||||
let parent = cx.tcx.parent_hir_node(match_expr.hir_id);
|
||||
if let Node::Expr(Expr {
|
||||
kind: ExprKind::Closure(..) | ExprKind::Binary(..),
|
||||
..
|
||||
})
|
||||
| Node::AnonConst(..) = parent
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if let Node::Arm(arm) = &cx.tcx.parent_hir_node(match_expr.hir_id)
|
||||
&& let ExprKind::Match(..) = arm.body.kind
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn indent_of_nth_line(snippet: &str, nth: usize) -> Option<usize> {
|
||||
snippet
|
||||
.lines()
|
||||
|
|
@ -379,14 +359,47 @@ fn sugg_with_curlies<'a>(
|
|||
|
||||
let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0));
|
||||
let (mut cbrace_start, mut cbrace_end) = (String::new(), String::new());
|
||||
if !expr_in_nested_block(cx, match_expr)
|
||||
&& ((needs_var_binding && is_var_binding_used_later) || expr_must_have_curlies(cx, match_expr))
|
||||
{
|
||||
let mut add_curlies = || {
|
||||
cbrace_end = format!("\n{indent}}}");
|
||||
// Fix body indent due to the closure
|
||||
indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
|
||||
cbrace_start = format!("{{\n{indent}");
|
||||
snippet_body = reindent_snippet_if_in_block(&snippet_body, !assignment_str.is_empty());
|
||||
};
|
||||
|
||||
if !expr_in_nested_block(cx, match_expr) {
|
||||
let mut parent = cx.tcx.parent_hir_node(match_expr.hir_id);
|
||||
if let Node::Expr(Expr {
|
||||
kind: ExprKind::Assign(..),
|
||||
hir_id,
|
||||
..
|
||||
}) = parent
|
||||
{
|
||||
parent = cx.tcx.parent_hir_node(*hir_id);
|
||||
}
|
||||
if let Node::Stmt(stmt) = parent {
|
||||
parent = cx.tcx.parent_hir_node(stmt.hir_id);
|
||||
}
|
||||
|
||||
match parent {
|
||||
Node::Block(..)
|
||||
| Node::Expr(Expr {
|
||||
kind: ExprKind::Block(..) | ExprKind::ConstBlock(..),
|
||||
..
|
||||
}) => {
|
||||
if needs_var_binding && is_var_binding_used_later {
|
||||
add_curlies();
|
||||
}
|
||||
},
|
||||
Node::Expr(..)
|
||||
| Node::AnonConst(..)
|
||||
| Node::Item(Item {
|
||||
kind: ItemKind::Const(..),
|
||||
..
|
||||
}) => add_curlies(),
|
||||
Node::Arm(arm) if let ExprKind::Match(..) = arm.body.kind => add_curlies(),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
format!("{cbrace_start}{scrutinee};\n{indent}{assignment_str}{snippet_body}{cbrace_end}")
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use crate::FxHashSet;
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::{first_line_of_span, indent_of, snippet};
|
||||
use clippy_utils::ty::{for_each_top_level_late_bound_region, is_copy};
|
||||
use clippy_utils::{get_attr, is_lint_allowed, sym};
|
||||
use clippy_utils::{get_builtin_attr, is_lint_allowed, sym};
|
||||
use itertools::Itertools;
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
|
|
@ -183,7 +183,7 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> {
|
|||
|
||||
fn has_sig_drop_attr_impl(&mut self, ty: Ty<'tcx>) -> bool {
|
||||
if let Some(adt) = ty.ty_adt_def()
|
||||
&& get_attr(
|
||||
&& get_builtin_attr(
|
||||
self.cx.sess(),
|
||||
self.cx.tcx.get_all_attrs(adt.did()),
|
||||
sym::has_significant_drop,
|
||||
|
|
|
|||
|
|
@ -373,7 +373,10 @@ impl<'a> PatState<'a> {
|
|||
},
|
||||
|
||||
// Patterns for things which can only contain a single sub-pattern.
|
||||
PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _, _) | PatKind::Box(pat) | PatKind::Deref(pat) => {
|
||||
PatKind::Binding(_, _, _, Some(pat))
|
||||
| PatKind::Ref(pat, _, _)
|
||||
| PatKind::Box(pat)
|
||||
| PatKind::Deref(pat) => {
|
||||
self.add_pat(cx, pat)
|
||||
},
|
||||
PatKind::Tuple([sub_pat], pos)
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::res::{MaybeDef, MaybeQPath};
|
||||
use clippy_utils::res::MaybeDef;
|
||||
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::is_non_aggregate_primitive_type;
|
||||
use clippy_utils::{is_default_equivalent, is_expr_used_or_unified, peel_ref_operators, std_or_core};
|
||||
use clippy_utils::{
|
||||
as_some_expr, is_default_equivalent, is_expr_used_or_unified, is_none_expr, peel_ref_operators, std_or_core,
|
||||
};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
|
@ -128,7 +129,7 @@ impl_lint_pass!(MemReplace =>
|
|||
[MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_OPTION_WITH_SOME, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]);
|
||||
|
||||
fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) -> bool {
|
||||
if src.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) {
|
||||
if is_none_expr(cx, src) {
|
||||
// Since this is a late pass (already type-checked),
|
||||
// and we already know that the second argument is an
|
||||
// `Option`, we do not need to check the first
|
||||
|
|
@ -161,8 +162,7 @@ fn check_replace_option_with_some(
|
|||
expr_span: Span,
|
||||
msrv: Msrv,
|
||||
) -> bool {
|
||||
if let ExprKind::Call(src_func, [src_arg]) = src.kind
|
||||
&& src_func.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome)
|
||||
if let Some(src_arg) = as_some_expr(cx, src)
|
||||
&& msrv.meets(cx, msrvs::OPTION_REPLACE)
|
||||
{
|
||||
// We do not have to check for a `const` context here, because `core::mem::replace()` and
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
use super::ERR_EXPECT;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::res::MaybeDef;
|
||||
use clippy_utils::ty::has_debug_impl;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::LateContext;
|
||||
|
|
@ -17,12 +16,10 @@ pub(super) fn check(
|
|||
err_span: Span,
|
||||
msrv: Msrv,
|
||||
) {
|
||||
if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result)
|
||||
// Grabs the `Result<T, E>` type
|
||||
&& let result_type = cx.typeck_results().expr_ty(recv)
|
||||
// Tests if the T type in a `Result<T, E>` is not None
|
||||
&& let Some(data_type) = get_data_type(cx, result_type)
|
||||
// Tests if the T type in a `Result<T, E>` implements debug
|
||||
let result_ty = cx.typeck_results().expr_ty(recv);
|
||||
// Grabs the `Result<T, E>` type
|
||||
if let Some(data_type) = get_data_type(cx, result_ty)
|
||||
// Tests if the T type in a `Result<T, E>` implements Debug
|
||||
&& has_debug_impl(cx, data_type)
|
||||
&& msrv.meets(cx, msrvs::EXPECT_ERR)
|
||||
{
|
||||
|
|
@ -41,7 +38,7 @@ pub(super) fn check(
|
|||
/// Given a `Result<T, E>` type, return its data (`T`).
|
||||
fn get_data_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option<Ty<'a>> {
|
||||
match ty.kind() {
|
||||
ty::Adt(_, args) if ty.is_diag_item(cx, sym::Result) => args.types().next(),
|
||||
ty::Adt(adt, args) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => args.types().next(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
use std::iter::once;
|
||||
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::res::{MaybeDef, MaybeQPath};
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::{ExprFnSig, expr_sig, ty_sig};
|
||||
use clippy_utils::{get_expr_use_or_unification_node, std_or_core, sym};
|
||||
use clippy_utils::{as_some_expr, get_expr_use_or_unification_node, is_none_expr, std_or_core, sym};
|
||||
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_hir::hir_id::HirId;
|
||||
use rustc_hir::{Expr, ExprKind, Node};
|
||||
use rustc_lint::LateContext;
|
||||
|
|
@ -68,15 +66,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method
|
|||
let item = match recv.kind {
|
||||
ExprKind::Array([]) => None,
|
||||
ExprKind::Array([e]) => Some(e),
|
||||
ExprKind::Path(ref p)
|
||||
if cx
|
||||
.qpath_res(p, recv.hir_id)
|
||||
.ctor_parent(cx)
|
||||
.is_lang_item(cx, OptionNone) =>
|
||||
{
|
||||
None
|
||||
},
|
||||
ExprKind::Call(f, [arg]) if f.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) => Some(arg),
|
||||
_ if is_none_expr(cx, recv) => None,
|
||||
_ if let Some(arg) = as_some_expr(cx, recv) => Some(arg),
|
||||
_ => return,
|
||||
};
|
||||
let iter_type = match method_name {
|
||||
|
|
|
|||
|
|
@ -81,7 +81,8 @@ pub(super) fn check<'tcx>(
|
|||
}
|
||||
|
||||
match it.kind {
|
||||
PatKind::Binding(BindingMode(_, Mutability::Mut), _, _, _) | PatKind::Ref(_, _, Mutability::Mut) => {
|
||||
PatKind::Binding(BindingMode(_, Mutability::Mut), _, _, _)
|
||||
| PatKind::Ref(_, _, Mutability::Mut) => {
|
||||
to_be_discarded = true;
|
||||
false
|
||||
},
|
||||
|
|
|
|||
|
|
@ -138,7 +138,6 @@ mod unnecessary_option_map_or_else;
|
|||
mod unnecessary_result_map_or_else;
|
||||
mod unnecessary_sort_by;
|
||||
mod unnecessary_to_owned;
|
||||
mod unused_enumerate_index;
|
||||
mod unwrap_expect_used;
|
||||
mod useless_asref;
|
||||
mod useless_nonzero_new_unchecked;
|
||||
|
|
@ -1084,7 +1083,7 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Why is this bad?
|
||||
/// In versions of the compiler before Rust 1.82.0, this bypasses the specialized
|
||||
/// implementation of`ToString` and instead goes through the more expensive string
|
||||
/// implementation of `ToString` and instead goes through the more expensive string
|
||||
/// formatting facilities.
|
||||
///
|
||||
/// ### Example
|
||||
|
|
@ -5026,7 +5025,6 @@ impl Methods {
|
|||
zst_offset::check(cx, expr, recv);
|
||||
},
|
||||
(sym::all, [arg]) => {
|
||||
unused_enumerate_index::check(cx, expr, recv, arg);
|
||||
needless_character_iteration::check(cx, expr, recv, arg, true);
|
||||
match method_call(recv) {
|
||||
Some((sym::cloned, recv2, [], _, _)) => {
|
||||
|
|
@ -5056,7 +5054,6 @@ impl Methods {
|
|||
}
|
||||
},
|
||||
(sym::any, [arg]) => {
|
||||
unused_enumerate_index::check(cx, expr, recv, arg);
|
||||
needless_character_iteration::check(cx, expr, recv, arg, false);
|
||||
match method_call(recv) {
|
||||
Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check(
|
||||
|
|
@ -5170,7 +5167,7 @@ impl Methods {
|
|||
},
|
||||
(sym::expect, [_]) => {
|
||||
match method_call(recv) {
|
||||
Some((sym::ok, recv, [], _, _)) => ok_expect::check(cx, expr, recv),
|
||||
Some((sym::ok, recv_inner, [], _, _)) => ok_expect::check(cx, expr, recv, recv_inner),
|
||||
Some((sym::err, recv, [], err_span, _)) => {
|
||||
err_expect::check(cx, expr, recv, span, err_span, self.msrv);
|
||||
},
|
||||
|
|
@ -5216,7 +5213,6 @@ impl Methods {
|
|||
}
|
||||
},
|
||||
(sym::filter_map, [arg]) => {
|
||||
unused_enumerate_index::check(cx, expr, recv, arg);
|
||||
unnecessary_filter_map::check(cx, expr, arg, call_span, unnecessary_filter_map::Kind::FilterMap);
|
||||
filter_map_bool_then::check(cx, expr, arg, call_span);
|
||||
filter_map_identity::check(cx, expr, arg, span);
|
||||
|
|
@ -5231,11 +5227,9 @@ impl Methods {
|
|||
);
|
||||
},
|
||||
(sym::find_map, [arg]) => {
|
||||
unused_enumerate_index::check(cx, expr, recv, arg);
|
||||
unnecessary_filter_map::check(cx, expr, arg, call_span, unnecessary_filter_map::Kind::FindMap);
|
||||
},
|
||||
(sym::flat_map, [arg]) => {
|
||||
unused_enumerate_index::check(cx, expr, recv, arg);
|
||||
flat_map_identity::check(cx, expr, arg, span);
|
||||
flat_map_option::check(cx, expr, arg, span);
|
||||
lines_filter_map_ok::check_filter_or_flat_map(
|
||||
|
|
@ -5263,20 +5257,17 @@ impl Methods {
|
|||
manual_try_fold::check(cx, expr, init, acc, call_span, self.msrv);
|
||||
unnecessary_fold::check(cx, expr, init, acc, span);
|
||||
},
|
||||
(sym::for_each, [arg]) => {
|
||||
unused_enumerate_index::check(cx, expr, recv, arg);
|
||||
match method_call(recv) {
|
||||
Some((sym::inspect, _, [_], span2, _)) => inspect_for_each::check(cx, expr, span2),
|
||||
Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check(
|
||||
cx,
|
||||
expr,
|
||||
recv,
|
||||
recv2,
|
||||
iter_overeager_cloned::Op::NeedlessMove(arg),
|
||||
false,
|
||||
),
|
||||
_ => {},
|
||||
}
|
||||
(sym::for_each, [arg]) => match method_call(recv) {
|
||||
Some((sym::inspect, _, [_], span2, _)) => inspect_for_each::check(cx, expr, span2),
|
||||
Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check(
|
||||
cx,
|
||||
expr,
|
||||
recv,
|
||||
recv2,
|
||||
iter_overeager_cloned::Op::NeedlessMove(arg),
|
||||
false,
|
||||
),
|
||||
_ => {},
|
||||
},
|
||||
(sym::get, [arg]) => {
|
||||
get_first::check(cx, expr, recv, arg);
|
||||
|
|
@ -5337,7 +5328,6 @@ impl Methods {
|
|||
},
|
||||
(name @ (sym::map | sym::map_err), [m_arg]) => {
|
||||
if name == sym::map {
|
||||
unused_enumerate_index::check(cx, expr, recv, m_arg);
|
||||
map_clone::check(cx, expr, recv, m_arg, self.msrv);
|
||||
map_with_unused_argument_over_ranges::check(cx, expr, recv, m_arg, self.msrv, span);
|
||||
manual_is_variant_and::check_map(cx, expr);
|
||||
|
|
|
|||
|
|
@ -38,11 +38,14 @@ pub(super) fn check<'tcx>(
|
|||
Node::Expr(parent) => {
|
||||
check_collect_into_intoiterator(cx, parent, collect_expr, call_span, iter_expr);
|
||||
|
||||
let sugg: String;
|
||||
let mut app;
|
||||
|
||||
if let ExprKind::MethodCall(name, _, args @ ([] | [_]), _) = parent.kind {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
app = Applicability::MachineApplicable;
|
||||
let collect_ty = cx.typeck_results().expr_ty(collect_expr);
|
||||
|
||||
let sugg: String = match name.ident.name {
|
||||
sugg = match name.ident.name {
|
||||
sym::len => {
|
||||
if let Some(adt) = collect_ty.ty_adt_def()
|
||||
&& matches!(
|
||||
|
|
@ -78,17 +81,23 @@ pub(super) fn check<'tcx>(
|
|||
},
|
||||
_ => return,
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
NEEDLESS_COLLECT,
|
||||
call_span.with_hi(parent.span.hi()),
|
||||
NEEDLESS_COLLECT_MSG,
|
||||
"replace with",
|
||||
sugg,
|
||||
app,
|
||||
);
|
||||
} else if let ExprKind::Index(_, index, _) = parent.kind {
|
||||
app = Applicability::MaybeIncorrect;
|
||||
let snip = snippet_with_applicability(cx, index.span, "_", &mut app);
|
||||
sugg = format!("nth({snip}).unwrap()");
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
NEEDLESS_COLLECT,
|
||||
call_span.with_hi(parent.span.hi()),
|
||||
NEEDLESS_COLLECT_MSG,
|
||||
"replace with",
|
||||
sugg,
|
||||
app,
|
||||
);
|
||||
},
|
||||
Node::LetStmt(l) => {
|
||||
if let PatKind::Binding(BindingMode::NONE | BindingMode::MUT, id, _, None) = l.pat.kind
|
||||
|
|
|
|||
|
|
@ -1,28 +1,35 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::res::MaybeDef;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::ty::has_debug_impl;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::OK_EXPECT;
|
||||
|
||||
/// lint use of `ok().expect()` for `Result`s
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) {
|
||||
if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result)
|
||||
// lint if the caller of `ok()` is a `Result`
|
||||
&& let result_type = cx.typeck_results().expr_ty(recv)
|
||||
&& let Some(error_type) = get_error_type(cx, result_type)
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, recv_inner: &hir::Expr<'_>) {
|
||||
let result_ty = cx.typeck_results().expr_ty(recv_inner);
|
||||
// lint if the caller of `ok()` is a `Result`
|
||||
if let Some(error_type) = get_error_type(cx, result_ty)
|
||||
&& has_debug_impl(cx, error_type)
|
||||
&& let Some(span) = recv.span.trim_start(recv_inner.span)
|
||||
{
|
||||
span_lint_and_help(
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
OK_EXPECT,
|
||||
expr.span,
|
||||
"called `ok().expect()` on a `Result` value",
|
||||
None,
|
||||
"you can call `expect()` directly on the `Result`",
|
||||
|diag| {
|
||||
let span = cx.sess().source_map().span_extend_while_whitespace(span);
|
||||
diag.span_suggestion_verbose(
|
||||
span,
|
||||
"call `expect()` directly on the `Result`",
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -30,7 +37,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
|
|||
/// Given a `Result<T, E>` type, return its error type (`E`).
|
||||
fn get_error_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option<Ty<'a>> {
|
||||
match ty.kind() {
|
||||
ty::Adt(_, args) if ty.is_diag_item(cx, sym::Result) => args.types().nth(1),
|
||||
ty::Adt(adt, args) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => args.types().nth(1),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_none_expr;
|
||||
use clippy_utils::res::{MaybeDef, MaybeQPath};
|
||||
use clippy_utils::source::snippet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_hir::LangItem::OptionSome;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
|
|
@ -48,7 +49,7 @@ pub(super) fn check<'tcx>(
|
|||
return;
|
||||
}
|
||||
|
||||
if !def_arg.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) {
|
||||
if !is_none_expr(cx, def_arg) {
|
||||
// nothing to lint!
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::peel_blocks;
|
||||
use clippy_utils::res::{MaybeDef, MaybeQPath};
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::{is_none_expr, peel_blocks};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_hir::LangItem::OptionSome;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
|
|
@ -25,7 +25,7 @@ pub(super) fn check<'tcx>(
|
|||
&& let hir::ExprKind::Closure(&hir::Closure { body, .. }) = def_arg.kind
|
||||
&& let body = cx.tcx.hir_body(body)
|
||||
// And finally we check that we return a `None` in the "else case".
|
||||
&& peel_blocks(body.value).res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone)
|
||||
&& is_none_expr(cx, peel_blocks(body.value))
|
||||
{
|
||||
let msg = "called `map_or_else(|_| None, Some)` on a `Result` value";
|
||||
let self_snippet = snippet(cx, recv.span, "..");
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
use super::utils::clone_or_copy_needed;
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath, MaybeTypeckRes};
|
||||
use clippy_utils::sym;
|
||||
use clippy_utils::ty::{is_copy, option_arg_ty};
|
||||
use clippy_utils::usage::mutated_variables;
|
||||
use clippy_utils::visitors::{Descend, for_each_expr_without_closures};
|
||||
use clippy_utils::{as_some_expr, sym};
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
|
|
@ -47,8 +47,7 @@ pub(super) fn check<'tcx>(
|
|||
let sugg = if !found_filtering {
|
||||
// Check if the closure is .filter_map(|x| Some(x))
|
||||
if kind.is_filter_map()
|
||||
&& let hir::ExprKind::Call(expr, [arg]) = body.value.kind
|
||||
&& expr.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome)
|
||||
&& let Some(arg) = as_some_expr(cx, body.value)
|
||||
&& let hir::ExprKind::Path(_) = arg.kind
|
||||
{
|
||||
span_lint(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::res::{MaybeDef, MaybeQPath};
|
||||
use clippy_utils::{last_path_segment, sym};
|
||||
use clippy_utils::{is_none_expr, last_path_segment, sym};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{self as hir, AmbigArg};
|
||||
use rustc_lint::LateContext;
|
||||
|
|
@ -23,6 +23,7 @@ fn get_ty_from_args<'a>(args: Option<&'a [hir::GenericArg<'a>]>, index: usize) -
|
|||
}
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_lines)]
|
||||
pub(super) fn check(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
|
|
@ -38,23 +39,24 @@ pub(super) fn check(
|
|||
}
|
||||
|
||||
let (constructor, call_args, ty) = if let hir::ExprKind::Call(call, call_args) = init.kind {
|
||||
let Some((qpath, hir_id)) = call.opt_qpath() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let args = last_path_segment(qpath).args.map(|args| args.args);
|
||||
let res = cx.qpath_res(qpath, hir_id);
|
||||
|
||||
if res.ctor_parent(cx).is_lang_item(cx, hir::LangItem::OptionSome) {
|
||||
(sym::Some, call_args, get_ty_from_args(args, 0))
|
||||
} else if res.ctor_parent(cx).is_lang_item(cx, hir::LangItem::ResultOk) {
|
||||
(sym::Ok, call_args, get_ty_from_args(args, 0))
|
||||
} else if res.ctor_parent(cx).is_lang_item(cx, hir::LangItem::ResultErr) {
|
||||
(sym::Err, call_args, get_ty_from_args(args, 1))
|
||||
if let Some((qpath, hir_id)) = call.opt_qpath()
|
||||
&& let args = last_path_segment(qpath).args.map(|args| args.args)
|
||||
&& let Some(did) = cx.qpath_res(qpath, hir_id).ctor_parent(cx).opt_def_id()
|
||||
{
|
||||
let lang_items = cx.tcx.lang_items();
|
||||
if Some(did) == lang_items.option_some_variant() {
|
||||
(sym::Some, call_args, get_ty_from_args(args, 0))
|
||||
} else if Some(did) == lang_items.result_ok_variant() {
|
||||
(sym::Ok, call_args, get_ty_from_args(args, 0))
|
||||
} else if Some(did) == lang_items.result_err_variant() {
|
||||
(sym::Err, call_args, get_ty_from_args(args, 1))
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else if init.res(cx).ctor_parent(cx).is_lang_item(cx, hir::LangItem::OptionNone) {
|
||||
} else if is_none_expr(cx, init) {
|
||||
let call_args: &[hir::Expr<'_>] = &[];
|
||||
(sym::None, call_args, None)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,138 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_hir_and_then;
|
||||
use clippy_utils::res::{MaybeDef, MaybeTypeckRes};
|
||||
use clippy_utils::source::{SpanRangeExt, snippet};
|
||||
use clippy_utils::{expr_or_init, pat_is_wild};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, FnDecl, PatKind, TyKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::{Span, sym};
|
||||
|
||||
use crate::loops::UNUSED_ENUMERATE_INDEX;
|
||||
|
||||
/// Check for the `UNUSED_ENUMERATE_INDEX` lint outside of loops.
|
||||
///
|
||||
/// The lint is declared in `clippy_lints/src/loops/mod.rs`. There, the following pattern is
|
||||
/// checked:
|
||||
/// ```ignore
|
||||
/// for (_, x) in some_iter.enumerate() {
|
||||
/// // Index is ignored
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This `check` function checks for chained method calls constructs where we can detect that the
|
||||
/// index is unused. Currently, this checks only for the following patterns:
|
||||
/// ```ignore
|
||||
/// some_iter.enumerate().map_function(|(_, x)| ..)
|
||||
/// let x = some_iter.enumerate();
|
||||
/// x.map_function(|(_, x)| ..)
|
||||
/// ```
|
||||
/// where `map_function` is one of `all`, `any`, `filter_map`, `find_map`, `flat_map`, `for_each` or
|
||||
/// `map`.
|
||||
///
|
||||
/// # Preconditions
|
||||
/// This function must be called not on the `enumerate` call expression itself, but on any of the
|
||||
/// map functions listed above. It will ensure that `recv` is a `std::iter::Enumerate` instance and
|
||||
/// that the method call is one of the `std::iter::Iterator` trait.
|
||||
///
|
||||
/// * `call_expr`: The map function call expression
|
||||
/// * `recv`: The receiver of the call
|
||||
/// * `closure_arg`: The argument to the map function call containing the closure/function to apply
|
||||
pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, closure_arg: &Expr<'_>) {
|
||||
let recv_ty = cx.typeck_results().expr_ty(recv);
|
||||
// If we call a method on a `std::iter::Enumerate` instance
|
||||
if recv_ty.is_diag_item(cx, sym::Enumerate)
|
||||
// If we are calling a method of the `Iterator` trait
|
||||
&& cx.ty_based_def(call_expr).opt_parent(cx).is_diag_item(cx, sym::Iterator)
|
||||
// And the map argument is a closure
|
||||
&& let ExprKind::Closure(closure) = closure_arg.kind
|
||||
&& let closure_body = cx.tcx.hir_body(closure.body)
|
||||
// And that closure has one argument ...
|
||||
&& let [closure_param] = closure_body.params
|
||||
// .. which is a tuple of 2 elements
|
||||
&& let PatKind::Tuple([index, elem], ..) = closure_param.pat.kind
|
||||
// And that the first element (the index) is either `_` or unused in the body
|
||||
&& pat_is_wild(cx, &index.kind, closure_body)
|
||||
// Try to find the initializer for `recv`. This is needed in case `recv` is a local_binding. In the
|
||||
// first example below, `expr_or_init` would return `recv`.
|
||||
// ```
|
||||
// iter.enumerate().map(|(_, x)| x)
|
||||
// ^^^^^^^^^^^^^^^^ `recv`, a call to `std::iter::Iterator::enumerate`
|
||||
//
|
||||
// let binding = iter.enumerate();
|
||||
// ^^^^^^^^^^^^^^^^ `recv_init_expr`
|
||||
// binding.map(|(_, x)| x)
|
||||
// ^^^^^^^ `recv`, not a call to `std::iter::Iterator::enumerate`
|
||||
// ```
|
||||
&& let recv_init_expr = expr_or_init(cx, recv)
|
||||
// Make sure the initializer is a method call. It may be that the `Enumerate` comes from something
|
||||
// that we cannot control.
|
||||
// This would for instance happen with:
|
||||
// ```
|
||||
// external_lib::some_function_returning_enumerate().map(|(_, x)| x)
|
||||
// ```
|
||||
&& let ExprKind::MethodCall(_, enumerate_recv, _, enumerate_span) = recv_init_expr.kind
|
||||
&& let Some(enumerate_defid) = cx.typeck_results().type_dependent_def_id(recv_init_expr.hir_id)
|
||||
// Make sure the method call is `std::iter::Iterator::enumerate`.
|
||||
&& cx.tcx.is_diagnostic_item(sym::enumerate_method, enumerate_defid)
|
||||
{
|
||||
// Check if the tuple type was explicit. It may be the type system _needs_ the type of the element
|
||||
// that would be explicitly in the closure.
|
||||
let new_closure_param = match find_elem_explicit_type_span(closure.fn_decl) {
|
||||
// We have an explicit type. Get its snippet, that of the binding name, and do `binding: ty`.
|
||||
// Fallback to `..` if we fail getting either snippet.
|
||||
Some(ty_span) => elem
|
||||
.span
|
||||
.get_source_text(cx)
|
||||
.and_then(|binding_name| {
|
||||
ty_span
|
||||
.get_source_text(cx)
|
||||
.map(|ty_name| format!("{binding_name}: {ty_name}"))
|
||||
})
|
||||
.unwrap_or_else(|| "..".to_string()),
|
||||
// Otherwise, we have no explicit type. We can replace with the binding name of the element.
|
||||
None => snippet(cx, elem.span, "..").into_owned(),
|
||||
};
|
||||
|
||||
// Suggest removing the tuple from the closure and the preceding call to `enumerate`, whose span we
|
||||
// can get from the `MethodCall`.
|
||||
span_lint_hir_and_then(
|
||||
cx,
|
||||
UNUSED_ENUMERATE_INDEX,
|
||||
recv_init_expr.hir_id,
|
||||
enumerate_span,
|
||||
"you seem to use `.enumerate()` and immediately discard the index",
|
||||
|diag| {
|
||||
diag.multipart_suggestion(
|
||||
"remove the `.enumerate()` call",
|
||||
vec![
|
||||
(closure_param.span, new_closure_param),
|
||||
(
|
||||
enumerate_span.with_lo(enumerate_recv.span.source_callsite().hi()),
|
||||
String::new(),
|
||||
),
|
||||
],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Find the span of the explicit type of the element.
|
||||
///
|
||||
/// # Returns
|
||||
/// If the tuple argument:
|
||||
/// * Has no explicit type, returns `None`
|
||||
/// * Has an explicit tuple type with an implicit element type (`(usize, _)`), returns `None`
|
||||
/// * Has an explicit tuple type with an explicit element type (`(_, i32)`), returns the span for
|
||||
/// the element type.
|
||||
fn find_elem_explicit_type_span(fn_decl: &FnDecl<'_>) -> Option<Span> {
|
||||
if let [tuple_ty] = fn_decl.inputs
|
||||
&& let TyKind::Tup([_idx_ty, elem_ty]) = tuple_ty.kind
|
||||
&& !matches!(elem_ty.kind, TyKind::Err(..) | TyKind::Infer(()))
|
||||
{
|
||||
Some(elem_ty.span)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
@ -3,10 +3,11 @@ use std::ops::ControlFlow;
|
|||
|
||||
use clippy_utils::comparisons::{Rel, normalize_comparison};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace};
|
||||
use clippy_utils::higher::{If, Range};
|
||||
use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace, root_macro_call};
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::visitors::for_each_expr_without_closures;
|
||||
use clippy_utils::{eq_expr_value, hash_expr, higher};
|
||||
use clippy_utils::{eq_expr_value, hash_expr};
|
||||
use rustc_ast::{BinOpKind, LitKind, RangeLimits};
|
||||
use rustc_data_structures::packed::Pu128;
|
||||
use rustc_data_structures::unhash::UnindexMap;
|
||||
|
|
@ -15,7 +16,7 @@ use rustc_hir::{Block, Body, Expr, ExprKind, UnOp};
|
|||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::{Span, sym};
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
@ -134,15 +135,15 @@ fn len_comparison<'hir>(
|
|||
fn assert_len_expr<'hir>(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &'hir Expr<'hir>,
|
||||
) -> Option<(LengthComparison, usize, &'hir Expr<'hir>)> {
|
||||
let (cmp, asserted_len, slice_len) = if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr)
|
||||
) -> Option<(LengthComparison, usize, &'hir Expr<'hir>, Symbol)> {
|
||||
let ((cmp, asserted_len, slice_len), macro_call) = if let Some(If { cond, then, .. }) = If::hir(expr)
|
||||
&& let ExprKind::Unary(UnOp::Not, condition) = &cond.kind
|
||||
&& let ExprKind::Binary(bin_op, left, right) = &condition.kind
|
||||
// check if `then` block has a never type expression
|
||||
&& let ExprKind::Block(Block { expr: Some(then_expr), .. }, _) = then.kind
|
||||
&& cx.typeck_results().expr_ty(then_expr).is_never()
|
||||
{
|
||||
len_comparison(bin_op.node, left, right)?
|
||||
(len_comparison(bin_op.node, left, right)?, sym::assert_macro)
|
||||
} else if let Some((macro_call, bin_op)) = first_node_macro_backtrace(cx, expr).find_map(|macro_call| {
|
||||
match cx.tcx.get_diagnostic_name(macro_call.def_id) {
|
||||
Some(sym::assert_eq_macro) => Some((macro_call, BinOpKind::Eq)),
|
||||
|
|
@ -151,7 +152,12 @@ fn assert_len_expr<'hir>(
|
|||
}
|
||||
}) && let Some((left, right, _)) = find_assert_eq_args(cx, expr, macro_call.expn)
|
||||
{
|
||||
len_comparison(bin_op, left, right)?
|
||||
(
|
||||
len_comparison(bin_op, left, right)?,
|
||||
root_macro_call(expr.span)
|
||||
.and_then(|macro_call| cx.tcx.get_diagnostic_name(macro_call.def_id))
|
||||
.unwrap_or(sym::assert_macro),
|
||||
)
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
|
|
@ -160,7 +166,7 @@ fn assert_len_expr<'hir>(
|
|||
&& cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice()
|
||||
&& method.ident.name == sym::len
|
||||
{
|
||||
Some((cmp, asserted_len, recv))
|
||||
Some((cmp, asserted_len, recv, macro_call))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
@ -174,6 +180,7 @@ enum IndexEntry<'hir> {
|
|||
comparison: LengthComparison,
|
||||
assert_span: Span,
|
||||
slice: &'hir Expr<'hir>,
|
||||
macro_call: Symbol,
|
||||
},
|
||||
/// `assert!` with indexing
|
||||
///
|
||||
|
|
@ -187,6 +194,7 @@ enum IndexEntry<'hir> {
|
|||
slice: &'hir Expr<'hir>,
|
||||
indexes: Vec<Span>,
|
||||
comparison: LengthComparison,
|
||||
macro_call: Symbol,
|
||||
},
|
||||
/// Indexing without an `assert!`
|
||||
IndexWithoutAssert {
|
||||
|
|
@ -225,9 +233,9 @@ fn upper_index_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<usize> {
|
|||
&& let LitKind::Int(Pu128(index), _) = lit.node
|
||||
{
|
||||
Some(index as usize)
|
||||
} else if let Some(higher::Range {
|
||||
} else if let Some(Range {
|
||||
end: Some(end), limits, ..
|
||||
}) = higher::Range::hir(cx, expr)
|
||||
}) = Range::hir(cx, expr)
|
||||
&& let ExprKind::Lit(lit) = &end.kind
|
||||
&& let LitKind::Int(Pu128(index @ 1..), _) = lit.node
|
||||
{
|
||||
|
|
@ -258,6 +266,7 @@ fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Uni
|
|||
comparison,
|
||||
assert_span,
|
||||
slice,
|
||||
macro_call,
|
||||
} => {
|
||||
if slice.span.lo() > assert_span.lo() {
|
||||
*entry = IndexEntry::AssertWithIndex {
|
||||
|
|
@ -268,6 +277,7 @@ fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Uni
|
|||
slice,
|
||||
indexes: vec![expr.span],
|
||||
comparison: *comparison,
|
||||
macro_call: *macro_call,
|
||||
};
|
||||
}
|
||||
},
|
||||
|
|
@ -303,7 +313,7 @@ fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Uni
|
|||
|
||||
/// Checks if the expression is an `assert!` expression and adds it to `asserts`
|
||||
fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut UnindexMap<u64, Vec<IndexEntry<'hir>>>) {
|
||||
if let Some((comparison, asserted_len, slice)) = assert_len_expr(cx, expr) {
|
||||
if let Some((comparison, asserted_len, slice, macro_call)) = assert_len_expr(cx, expr) {
|
||||
let hash = hash_expr(cx, slice);
|
||||
let indexes = map.entry(hash).or_default();
|
||||
|
||||
|
|
@ -326,6 +336,7 @@ fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Un
|
|||
assert_span: expr.span.source_callsite(),
|
||||
comparison,
|
||||
asserted_len,
|
||||
macro_call,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
|
|
@ -334,6 +345,7 @@ fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Un
|
|||
comparison,
|
||||
assert_span: expr.span.source_callsite(),
|
||||
slice,
|
||||
macro_call,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -362,6 +374,7 @@ fn report_indexes(cx: &LateContext<'_>, map: &UnindexMap<u64, Vec<IndexEntry<'_>
|
|||
comparison,
|
||||
assert_span,
|
||||
slice,
|
||||
macro_call,
|
||||
} if indexes.len() > 1 && !is_first_highest => {
|
||||
// if we have found an `assert!`, let's also check that it's actually right
|
||||
// and if it covers the highest index and if not, suggest the correct length
|
||||
|
|
@ -382,11 +395,23 @@ fn report_indexes(cx: &LateContext<'_>, map: &UnindexMap<u64, Vec<IndexEntry<'_>
|
|||
snippet(cx, slice.span, "..")
|
||||
)),
|
||||
// `highest_index` here is rather a length, so we need to add 1 to it
|
||||
LengthComparison::LengthEqualInt if asserted_len < highest_index + 1 => Some(format!(
|
||||
"assert!({}.len() == {})",
|
||||
snippet(cx, slice.span, ".."),
|
||||
highest_index + 1
|
||||
)),
|
||||
LengthComparison::LengthEqualInt if asserted_len < highest_index + 1 => match macro_call {
|
||||
sym::assert_eq_macro => Some(format!(
|
||||
"assert_eq!({}.len(), {})",
|
||||
snippet(cx, slice.span, ".."),
|
||||
highest_index + 1
|
||||
)),
|
||||
sym::debug_assert_eq_macro => Some(format!(
|
||||
"debug_assert_eq!({}.len(), {})",
|
||||
snippet(cx, slice.span, ".."),
|
||||
highest_index + 1
|
||||
)),
|
||||
_ => Some(format!(
|
||||
"assert!({}.len() == {})",
|
||||
snippet(cx, slice.span, ".."),
|
||||
highest_index + 1
|
||||
)),
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use rustc_hir::def_id::DefId;
|
|||
use rustc_hir::{self as hir, Attribute, find_attr};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::ty::AssocContainer;
|
||||
use rustc_session::config::CrateType;
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::Span;
|
||||
|
||||
|
|
@ -81,20 +82,20 @@ fn check_missing_inline_attrs(
|
|||
}
|
||||
}
|
||||
|
||||
fn is_executable_or_proc_macro(cx: &LateContext<'_>) -> bool {
|
||||
use rustc_session::config::CrateType;
|
||||
|
||||
cx.tcx
|
||||
.crate_types()
|
||||
.iter()
|
||||
.any(|t: &CrateType| matches!(t, CrateType::Executable | CrateType::ProcMacro))
|
||||
}
|
||||
|
||||
declare_lint_pass!(MissingInline => [MISSING_INLINE_IN_PUBLIC_ITEMS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for MissingInline {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) {
|
||||
if it.span.in_external_macro(cx.sess().source_map()) || is_executable_or_proc_macro(cx) {
|
||||
if it.span.in_external_macro(cx.sess().source_map()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if cx
|
||||
.tcx
|
||||
.crate_types()
|
||||
.iter()
|
||||
.any(|t: &CrateType| matches!(t, CrateType::ProcMacro))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -149,7 +150,13 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline {
|
|||
}
|
||||
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
|
||||
if impl_item.span.in_external_macro(cx.sess().source_map()) || is_executable_or_proc_macro(cx) {
|
||||
if impl_item.span.in_external_macro(cx.sess().source_map())
|
||||
|| cx
|
||||
.tcx
|
||||
.crate_types()
|
||||
.iter()
|
||||
.any(|t: &CrateType| matches!(t, CrateType::ProcMacro))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext};
|
|||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::def_id::LOCAL_CRATE;
|
||||
use rustc_span::{FileName, SourceFile, Span, SyntaxContext, sym};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
@ -150,7 +150,13 @@ fn check_self_named_module(cx: &EarlyContext<'_>, path: &Path, file: &SourceFile
|
|||
/// Using `mod.rs` in integration tests is a [common pattern](https://doc.rust-lang.org/book/ch11-03-test-organization.html#submodules-in-integration-test)
|
||||
/// for code-sharing between tests.
|
||||
fn check_mod_module(cx: &EarlyContext<'_>, path: &Path, file: &SourceFile) {
|
||||
if path.ends_with("mod.rs") && !path.starts_with("tests") {
|
||||
if path.ends_with("mod.rs")
|
||||
&& !path
|
||||
.components()
|
||||
.filter_map(|c| if let Component::Normal(d) = c { Some(d) } else { None })
|
||||
.take_while(|&c| c != "src")
|
||||
.any(|c| c == "tests")
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MOD_MODULE_FILES,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use rustc_ast::ast::{BindingMode, ByRef, Lifetime, Mutability, Param, PatKind, Path, TyKind};
|
||||
use rustc_ast::ast::{BindingMode, ByRef, Lifetime, Param, PatKind, TyKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::symbol::kw;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
@ -65,52 +64,6 @@ enum Mode {
|
|||
Value,
|
||||
}
|
||||
|
||||
fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mode: &Mode, mutbl: Mutability) {
|
||||
if let [segment] = &path.segments[..]
|
||||
&& segment.ident.name == kw::SelfUpper
|
||||
{
|
||||
// In case we have a named lifetime, we check if the name comes from expansion.
|
||||
// If it does, at this point we know the rest of the parameter was written by the user,
|
||||
// so let them decide what the name of the lifetime should be.
|
||||
// See #6089 for more details.
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let self_param = match (binding_mode, mutbl) {
|
||||
(Mode::Ref(None), Mutability::Mut) => "&mut self".to_string(),
|
||||
(Mode::Ref(Some(lifetime)), Mutability::Mut) => {
|
||||
if lifetime.ident.span.from_expansion() {
|
||||
applicability = Applicability::HasPlaceholders;
|
||||
"&'_ mut self".to_string()
|
||||
} else {
|
||||
let lt_name = snippet_with_applicability(cx, lifetime.ident.span, "..", &mut applicability);
|
||||
format!("&{lt_name} mut self")
|
||||
}
|
||||
},
|
||||
(Mode::Ref(None), Mutability::Not) => "&self".to_string(),
|
||||
(Mode::Ref(Some(lifetime)), Mutability::Not) => {
|
||||
if lifetime.ident.span.from_expansion() {
|
||||
applicability = Applicability::HasPlaceholders;
|
||||
"&'_ self".to_string()
|
||||
} else {
|
||||
let lt_name = snippet_with_applicability(cx, lifetime.ident.span, "..", &mut applicability);
|
||||
format!("&{lt_name} self")
|
||||
}
|
||||
},
|
||||
(Mode::Value, Mutability::Mut) => "mut self".to_string(),
|
||||
(Mode::Value, Mutability::Not) => "self".to_string(),
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
NEEDLESS_ARBITRARY_SELF_TYPE,
|
||||
span,
|
||||
"the type of the `self` parameter does not need to be arbitrary",
|
||||
"consider to change this parameter to",
|
||||
self_param,
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl EarlyLintPass for NeedlessArbitrarySelfType {
|
||||
fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) {
|
||||
// Bail out if the parameter it's not a receiver or was not written by the user
|
||||
|
|
@ -118,20 +71,55 @@ impl EarlyLintPass for NeedlessArbitrarySelfType {
|
|||
return;
|
||||
}
|
||||
|
||||
match &p.ty.kind {
|
||||
TyKind::Path(None, path) => {
|
||||
if let PatKind::Ident(BindingMode(ByRef::No, mutbl), _, _) = p.pat.kind {
|
||||
check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Value, mutbl);
|
||||
}
|
||||
let (path, binding_mode, mutbl) = match &p.ty.kind {
|
||||
TyKind::Path(None, path) if let PatKind::Ident(BindingMode(ByRef::No, mutbl), _, _) = p.pat.kind => {
|
||||
(path, Mode::Value, mutbl)
|
||||
},
|
||||
TyKind::Ref(lifetime, mut_ty) => {
|
||||
TyKind::Ref(lifetime, mut_ty)
|
||||
if let TyKind::Path(None, path) = &mut_ty.ty.kind
|
||||
&& let PatKind::Ident(BindingMode::NONE, _, _) = p.pat.kind
|
||||
{
|
||||
check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Ref(*lifetime), mut_ty.mutbl);
|
||||
}
|
||||
&& let PatKind::Ident(BindingMode::NONE, _, _) = p.pat.kind =>
|
||||
{
|
||||
(path, Mode::Ref(*lifetime), mut_ty.mutbl)
|
||||
},
|
||||
_ => {},
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let span = p.span.to(p.ty.span);
|
||||
if let [segment] = &path.segments[..]
|
||||
&& segment.ident.name == kw::SelfUpper
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
NEEDLESS_ARBITRARY_SELF_TYPE,
|
||||
span,
|
||||
"the type of the `self` parameter does not need to be arbitrary",
|
||||
|diag| {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let add = match binding_mode {
|
||||
Mode::Value => String::new(),
|
||||
Mode::Ref(None) => mutbl.ref_prefix_str().to_string(),
|
||||
Mode::Ref(Some(lifetime)) => {
|
||||
// In case we have a named lifetime, we check if the name comes from expansion.
|
||||
// If it does, at this point we know the rest of the parameter was written by the user,
|
||||
// so let them decide what the name of the lifetime should be.
|
||||
// See #6089 for more details.
|
||||
let lt_name = if lifetime.ident.span.from_expansion() {
|
||||
applicability = Applicability::HasPlaceholders;
|
||||
"'_".into()
|
||||
} else {
|
||||
snippet_with_applicability(cx, lifetime.ident.span, "'_", &mut applicability)
|
||||
};
|
||||
format!("&{lt_name} {mut_}", mut_ = mutbl.prefix_str())
|
||||
},
|
||||
};
|
||||
|
||||
let mut sugg = vec![(p.ty.span.with_lo(p.span.hi()), String::new())];
|
||||
if !add.is_empty() {
|
||||
sugg.push((p.span.shrink_to_lo(), add));
|
||||
}
|
||||
diag.multipart_suggestion_verbose("remove the type", sugg, applicability);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -354,10 +354,7 @@ fn self_cmp_call<'tcx>(
|
|||
needs_fully_qualified: &mut bool,
|
||||
) -> bool {
|
||||
match cmp_expr.kind {
|
||||
ExprKind::Call(path, [_, _]) => path
|
||||
.res(typeck)
|
||||
.opt_def_id()
|
||||
.is_some_and(|def_id| cx.tcx.is_diagnostic_item(sym::ord_cmp_method, def_id)),
|
||||
ExprKind::Call(path, [_, _]) => path.res(typeck).is_diag_item(cx, sym::ord_cmp_method),
|
||||
ExprKind::MethodCall(_, recv, [_], ..) => {
|
||||
let ExprKind::Path(path) = recv.kind else {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -706,7 +706,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
|
|||
IsFreeze::Maybe => match cx.tcx.const_eval_poly(item.owner_id.to_def_id()) {
|
||||
Ok(val) if let Ok(is_freeze) = self.is_value_freeze(cx.tcx, cx.typing_env(), ty, val) => !is_freeze,
|
||||
// FIXME: we just assume mgca rhs's are freeze
|
||||
_ => const_item_rhs_to_expr(cx.tcx, ct_rhs).map_or(false, |e| !self.is_init_expr_freeze(
|
||||
_ => const_item_rhs_to_expr(cx.tcx, ct_rhs).is_some_and(|e| !self.is_init_expr_freeze(
|
||||
cx.tcx,
|
||||
cx.typing_env(),
|
||||
cx.tcx.typeck(item.owner_id),
|
||||
|
|
@ -749,7 +749,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
|
|||
!is_freeze
|
||||
},
|
||||
// FIXME: we just assume mgca rhs's are freeze
|
||||
_ => const_item_rhs_to_expr(cx.tcx, ct_rhs).map_or(false, |e| {
|
||||
_ => const_item_rhs_to_expr(cx.tcx, ct_rhs).is_some_and(|e| {
|
||||
!self.is_init_expr_freeze(
|
||||
cx.tcx,
|
||||
cx.typing_env(),
|
||||
|
|
@ -806,7 +806,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
|
|||
IsFreeze::Maybe => match cx.tcx.const_eval_poly(item.owner_id.to_def_id()) {
|
||||
Ok(val) if let Ok(is_freeze) = self.is_value_freeze(cx.tcx, cx.typing_env(), ty, val) => !is_freeze,
|
||||
// FIXME: we just assume mgca rhs's are freeze
|
||||
_ => const_item_rhs_to_expr(cx.tcx, ct_rhs).map_or(false, |e| {
|
||||
_ => const_item_rhs_to_expr(cx.tcx, ct_rhs).is_some_and(|e| {
|
||||
!self.is_init_expr_freeze(
|
||||
cx.tcx,
|
||||
cx.typing_env(),
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use super::ARITHMETIC_SIDE_EFFECTS;
|
||||
use crate::clippy_utils::res::MaybeQPath as _;
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::consts::{ConstEvalCtxt, Constant};
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
|
|
@ -6,7 +7,7 @@ use clippy_utils::res::MaybeDef;
|
|||
use clippy_utils::{expr_or_init, is_from_proc_macro, is_lint_allowed, peel_hir_expr_refs, peel_hir_expr_unary, sym};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_middle::ty::{self, Ty, UintTy};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{Span, Symbol};
|
||||
use {rustc_ast as ast, rustc_hir as hir};
|
||||
|
|
@ -88,74 +89,16 @@ impl ArithmeticSideEffects {
|
|||
self.allowed_unary.contains(ty_string_elem)
|
||||
}
|
||||
|
||||
fn is_non_zero_u(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
|
||||
if let ty::Adt(adt, substs) = ty.kind()
|
||||
&& cx.tcx.is_diagnostic_item(sym::NonZero, adt.did())
|
||||
&& let int_type = substs.type_at(0)
|
||||
&& matches!(int_type.kind(), ty::Uint(_))
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Verifies built-in types that have specific allowed operations
|
||||
fn has_specific_allowed_type_and_operation<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
lhs_ty: Ty<'tcx>,
|
||||
op: hir::BinOpKind,
|
||||
rhs_ty: Ty<'tcx>,
|
||||
) -> bool {
|
||||
let is_div_or_rem = matches!(op, hir::BinOpKind::Div | hir::BinOpKind::Rem);
|
||||
let is_sat_or_wrap = |ty: Ty<'_>| ty.is_diag_item(cx, sym::Saturating) || ty.is_diag_item(cx, sym::Wrapping);
|
||||
|
||||
// If the RHS is `NonZero<u*>`, then division or module by zero will never occur.
|
||||
if Self::is_non_zero_u(cx, rhs_ty) && is_div_or_rem {
|
||||
return true;
|
||||
}
|
||||
|
||||
// `Saturation` and `Wrapping` can overflow if the RHS is zero in a division or module.
|
||||
if is_sat_or_wrap(lhs_ty) {
|
||||
return !is_div_or_rem;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
// For example, 8i32 or &i64::MAX.
|
||||
fn is_integral(ty: Ty<'_>) -> bool {
|
||||
ty.peel_refs().is_integral()
|
||||
}
|
||||
|
||||
// Common entry-point to avoid code duplication.
|
||||
fn issue_lint<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if is_from_proc_macro(cx, expr) {
|
||||
return;
|
||||
}
|
||||
|
||||
let msg = "arithmetic operation that can potentially result in unexpected side-effects";
|
||||
span_lint(cx, ARITHMETIC_SIDE_EFFECTS, expr.span, msg);
|
||||
self.expr_span = Some(expr.span);
|
||||
}
|
||||
|
||||
/// Returns the numeric value of a literal integer originated from `expr`, if any.
|
||||
///
|
||||
/// Literal integers can be originated from adhoc declarations like `1`, associated constants
|
||||
/// like `i32::MAX` or constant references like `N` from `const N: i32 = 1;`,
|
||||
fn literal_integer(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<u128> {
|
||||
let actual = peel_hir_expr_unary(expr).0;
|
||||
if let hir::ExprKind::Lit(lit) = actual.kind
|
||||
&& let ast::LitKind::Int(n, _) = lit.node
|
||||
{
|
||||
return Some(n.get());
|
||||
}
|
||||
if let Some(Constant::Int(n)) = ConstEvalCtxt::new(cx).eval(expr) {
|
||||
return Some(n);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Methods like `add_assign` are send to their `BinOps` references.
|
||||
fn manage_sugar_methods<'tcx>(
|
||||
&mut self,
|
||||
|
|
@ -213,59 +156,53 @@ impl ArithmeticSideEffects {
|
|||
&& let hir::ExprKind::MethodCall(method, receiver, [], _) = actual_lhs.kind
|
||||
&& method.ident.name == sym::get
|
||||
&& let receiver_ty = cx.typeck_results().expr_ty(receiver).peel_refs()
|
||||
&& Self::is_non_zero_u(cx, receiver_ty)
|
||||
&& let Some(1) = Self::literal_integer(cx, actual_rhs)
|
||||
&& is_non_zero_u(cx, receiver_ty)
|
||||
&& literal_integer(cx, actual_rhs) == Some(1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let lhs_ty = cx.typeck_results().expr_ty(actual_lhs).peel_refs();
|
||||
let rhs_ty = cx.typeck_results().expr_ty_adjusted(actual_rhs).peel_refs();
|
||||
if self.has_allowed_binary(lhs_ty, rhs_ty) {
|
||||
if self.has_allowed_binary(lhs_ty, rhs_ty)
|
||||
| has_specific_allowed_type_and_operation(cx, lhs_ty, op, rhs_ty)
|
||||
| is_safe_due_to_smaller_source_type(cx, op, (actual_lhs, lhs_ty), actual_rhs)
|
||||
| is_safe_due_to_smaller_source_type(cx, op, (actual_rhs, rhs_ty), actual_lhs)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if Self::has_specific_allowed_type_and_operation(cx, lhs_ty, op, rhs_ty) {
|
||||
return;
|
||||
}
|
||||
|
||||
let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) {
|
||||
if is_integer(lhs_ty) && is_integer(rhs_ty) {
|
||||
if let hir::BinOpKind::Shl | hir::BinOpKind::Shr = op {
|
||||
// At least for integers, shifts are already handled by the CTFE
|
||||
return;
|
||||
}
|
||||
match (
|
||||
Self::literal_integer(cx, actual_lhs),
|
||||
Self::literal_integer(cx, actual_rhs),
|
||||
) {
|
||||
(None, None) => false,
|
||||
match (literal_integer(cx, actual_lhs), literal_integer(cx, actual_rhs)) {
|
||||
(None, Some(n)) => match (&op, n) {
|
||||
// Division and module are always valid if applied to non-zero integers
|
||||
(hir::BinOpKind::Div | hir::BinOpKind::Rem, local_n) if local_n != 0 => true,
|
||||
(hir::BinOpKind::Div | hir::BinOpKind::Rem, local_n) if local_n != 0 => return,
|
||||
// Adding or subtracting zeros is always a no-op
|
||||
(hir::BinOpKind::Add | hir::BinOpKind::Sub, 0)
|
||||
// Multiplication by 1 or 0 will never overflow
|
||||
| (hir::BinOpKind::Mul, 0 | 1)
|
||||
=> true,
|
||||
_ => false,
|
||||
=> return,
|
||||
_ => {},
|
||||
},
|
||||
(Some(n), None) => match (&op, n) {
|
||||
// Adding or subtracting zeros is always a no-op
|
||||
(hir::BinOpKind::Add | hir::BinOpKind::Sub, 0)
|
||||
// Multiplication by 1 or 0 will never overflow
|
||||
| (hir::BinOpKind::Mul, 0 | 1)
|
||||
=> true,
|
||||
_ => false,
|
||||
},
|
||||
(Some(_), Some(_)) => {
|
||||
matches!((lhs_ref_counter, rhs_ref_counter), (0, 0))
|
||||
(Some(n), None)
|
||||
if matches!(
|
||||
(&op, n),
|
||||
// Adding or subtracting zeros is always a no-op
|
||||
(hir::BinOpKind::Add | hir::BinOpKind::Sub, 0)
|
||||
// Multiplication by 1 or 0 will never overflow
|
||||
| (hir::BinOpKind::Mul, 0 | 1)
|
||||
) =>
|
||||
{
|
||||
return;
|
||||
},
|
||||
(Some(_), Some(_)) if matches!((lhs_ref_counter, rhs_ref_counter), (0, 0)) => return,
|
||||
_ => {},
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if !has_valid_op {
|
||||
self.issue_lint(cx, expr);
|
||||
}
|
||||
self.issue_lint(cx, expr);
|
||||
}
|
||||
|
||||
/// There are some integer methods like `wrapping_div` that will panic depending on the
|
||||
|
|
@ -285,7 +222,7 @@ impl ArithmeticSideEffects {
|
|||
return;
|
||||
}
|
||||
let instance_ty = cx.typeck_results().expr_ty_adjusted(receiver);
|
||||
if !Self::is_integral(instance_ty) {
|
||||
if !is_integer(instance_ty) {
|
||||
return;
|
||||
}
|
||||
self.manage_sugar_methods(cx, expr, receiver, ps, arg);
|
||||
|
|
@ -293,7 +230,7 @@ impl ArithmeticSideEffects {
|
|||
return;
|
||||
}
|
||||
let (actual_arg, _) = peel_hir_expr_refs(arg);
|
||||
match Self::literal_integer(cx, actual_arg) {
|
||||
match literal_integer(cx, actual_arg) {
|
||||
None | Some(0) => self.issue_lint(cx, arg),
|
||||
Some(_) => {},
|
||||
}
|
||||
|
|
@ -317,7 +254,7 @@ impl ArithmeticSideEffects {
|
|||
return;
|
||||
}
|
||||
let actual_un_expr = peel_hir_expr_refs(un_expr).0;
|
||||
if Self::literal_integer(cx, actual_un_expr).is_some() {
|
||||
if literal_integer(cx, actual_un_expr).is_some() {
|
||||
return;
|
||||
}
|
||||
self.issue_lint(cx, expr);
|
||||
|
|
@ -385,3 +322,120 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Detects a type-casting conversion and returns the type of the original expression. For
|
||||
/// example, `let foo = u64::from(bar)`.
|
||||
fn find_original_primitive_ty<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) -> Option<Ty<'tcx>> {
|
||||
if let hir::ExprKind::Call(path, [arg]) = &expr.kind
|
||||
&& path.res(cx).opt_def_id().is_diag_item(&cx.tcx, sym::from_fn)
|
||||
{
|
||||
Some(cx.typeck_results().expr_ty(arg))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Verifies built-in types that have specific allowed operations
|
||||
fn has_specific_allowed_type_and_operation<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
lhs_ty: Ty<'tcx>,
|
||||
op: hir::BinOpKind,
|
||||
rhs_ty: Ty<'tcx>,
|
||||
) -> bool {
|
||||
let is_div_or_rem = matches!(op, hir::BinOpKind::Div | hir::BinOpKind::Rem);
|
||||
let is_sat_or_wrap = |ty: Ty<'_>| matches!(ty.opt_diag_name(cx), Some(sym::Saturating | sym::Wrapping));
|
||||
|
||||
// If the RHS is `NonZero<u*>`, then division or module by zero will never occur.
|
||||
if is_non_zero_u(cx, rhs_ty) && is_div_or_rem {
|
||||
return true;
|
||||
}
|
||||
|
||||
// `Saturation` and `Wrapping` can overflow if the RHS is zero in a division or module.
|
||||
if is_sat_or_wrap(lhs_ty) {
|
||||
return !is_div_or_rem;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
// For example, `i8` or `u128` and possible associated references like `&&u16`.
|
||||
fn is_integer(ty: Ty<'_>) -> bool {
|
||||
ty.peel_refs().is_integral()
|
||||
}
|
||||
|
||||
fn is_non_zero_u(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
|
||||
if let ty::Adt(adt, substs) = ty.kind()
|
||||
&& cx.tcx.is_diagnostic_item(sym::NonZero, adt.did())
|
||||
&& let int_type = substs.type_at(0)
|
||||
&& matches!(int_type.kind(), ty::Uint(_))
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// If one side is a literal it is possible to evaluate overflows as long as the other side has a
|
||||
/// smaller type. `0` and `1` suffixes indicate different sides.
|
||||
///
|
||||
/// For example, `1000u64 + u64::from(some_runtime_variable_of_type_u8)`.
|
||||
fn is_safe_due_to_smaller_source_type(
|
||||
cx: &LateContext<'_>,
|
||||
op: hir::BinOpKind,
|
||||
(expr0, ty0): (&hir::Expr<'_>, Ty<'_>),
|
||||
expr1: &hir::Expr<'_>,
|
||||
) -> bool {
|
||||
let Some(num0) = literal_integer(cx, expr0) else {
|
||||
return false;
|
||||
};
|
||||
let Some(orig_ty1) = find_original_primitive_ty(cx, expr1) else {
|
||||
return false;
|
||||
};
|
||||
let Some(num1) = max_int_num(orig_ty1) else {
|
||||
return false;
|
||||
};
|
||||
let Some(rslt) = (match op {
|
||||
hir::BinOpKind::Add => num0.checked_add(num1),
|
||||
hir::BinOpKind::Mul => num0.checked_mul(num1),
|
||||
_ => None,
|
||||
}) else {
|
||||
return false;
|
||||
};
|
||||
match ty0.peel_refs().kind() {
|
||||
ty::Uint(UintTy::U16) => u16::try_from(rslt).is_ok(),
|
||||
ty::Uint(UintTy::U32) => u32::try_from(rslt).is_ok(),
|
||||
ty::Uint(UintTy::U64) => u64::try_from(rslt).is_ok(),
|
||||
ty::Uint(UintTy::U128) => true,
|
||||
ty::Uint(UintTy::Usize) => usize::try_from(rslt).is_ok(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the numeric value of a literal integer originated from `expr`, if any.
|
||||
///
|
||||
/// Literal integers can be originated from adhoc declarations like `1`, associated constants
|
||||
/// like `i32::MAX` or constant references like `N` from `const N: i32 = 1;`,
|
||||
fn literal_integer(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<u128> {
|
||||
let actual = peel_hir_expr_unary(expr).0;
|
||||
if let hir::ExprKind::Lit(lit) = actual.kind
|
||||
&& let ast::LitKind::Int(n, _) = lit.node
|
||||
{
|
||||
return Some(n.get());
|
||||
}
|
||||
if let Some(Constant::Int(n)) = ConstEvalCtxt::new(cx).eval(expr) {
|
||||
return Some(n);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn max_int_num(ty: Ty<'_>) -> Option<u128> {
|
||||
match ty.peel_refs().kind() {
|
||||
ty::Uint(UintTy::U8) => Some(u8::MAX.into()),
|
||||
ty::Uint(UintTy::U16) => Some(u16::MAX.into()),
|
||||
ty::Uint(UintTy::U32) => Some(u32::MAX.into()),
|
||||
ty::Uint(UintTy::U64) => Some(u64::MAX.into()),
|
||||
ty::Uint(UintTy::U128) => Some(u128::MAX),
|
||||
ty::Uint(UintTy::Usize) => usize::MAX.try_into().ok(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,16 +6,15 @@ use clippy_utils::sugg::Sugg;
|
|||
use clippy_utils::ty::is_copy;
|
||||
use clippy_utils::{
|
||||
CaptureKind, can_move_expr_to_closure, eager_or_lazy, expr_requires_coercion, higher, is_else_clause,
|
||||
is_in_const_context, peel_blocks, peel_hir_expr_while,
|
||||
is_in_const_context, is_none_pattern, peel_blocks, peel_hir_expr_while,
|
||||
};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
|
||||
use rustc_hir::LangItem::ResultErr;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::intravisit::{Visitor, walk_expr, walk_path};
|
||||
use rustc_hir::{
|
||||
Arm, BindingMode, Expr, ExprKind, HirId, MatchSource, Mutability, Node, Pat, PatExpr, PatExprKind, PatKind, Path,
|
||||
QPath, UnOp,
|
||||
Arm, BindingMode, Expr, ExprKind, HirId, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, UnOp,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::nested_filter;
|
||||
|
|
@ -313,11 +312,14 @@ impl<'tcx> Visitor<'tcx> for ReferenceVisitor<'_, 'tcx> {
|
|||
}
|
||||
|
||||
fn try_get_inner_pat_and_is_result<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<(&'tcx Pat<'tcx>, bool)> {
|
||||
if let PatKind::TupleStruct(ref qpath, [inner_pat], ..) = pat.kind {
|
||||
let res = cx.qpath_res(qpath, pat.hir_id);
|
||||
if res.ctor_parent(cx).is_lang_item(cx, OptionSome) {
|
||||
if let PatKind::TupleStruct(ref qpath, [inner_pat], ..) = pat.kind
|
||||
&& let res = cx.qpath_res(qpath, pat.hir_id)
|
||||
&& let Some(did) = res.ctor_parent(cx).opt_def_id()
|
||||
{
|
||||
let lang_items = cx.tcx.lang_items();
|
||||
if Some(did) == lang_items.option_some_variant() {
|
||||
return Some((inner_pat, false));
|
||||
} else if res.ctor_parent(cx).is_lang_item(cx, ResultOk) {
|
||||
} else if Some(did) == lang_items.result_ok_variant() {
|
||||
return Some((inner_pat, true));
|
||||
}
|
||||
}
|
||||
|
|
@ -376,14 +378,7 @@ fn try_convert_match<'tcx>(
|
|||
|
||||
fn is_none_or_err_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
|
||||
match arm.pat.kind {
|
||||
PatKind::Expr(PatExpr {
|
||||
kind: PatExprKind::Path(qpath),
|
||||
hir_id,
|
||||
..
|
||||
}) => cx
|
||||
.qpath_res(qpath, *hir_id)
|
||||
.ctor_parent(cx)
|
||||
.is_lang_item(cx, OptionNone),
|
||||
_ if is_none_pattern(cx, arm.pat) => true,
|
||||
PatKind::TupleStruct(ref qpath, [first_pat], _) => {
|
||||
cx.qpath_res(qpath, arm.pat.hir_id)
|
||||
.ctor_parent(cx)
|
||||
|
|
|
|||
49
clippy_lints/src/ptr/cmp_null.rs
Normal file
49
clippy_lints/src/ptr/cmp_null.rs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
use super::CMP_NULL;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::res::{MaybeDef, MaybeResPath};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{is_lint_allowed, sym};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
op: BinOpKind,
|
||||
l: &Expr<'_>,
|
||||
r: &Expr<'_>,
|
||||
) -> bool {
|
||||
let non_null_path_snippet = match (
|
||||
is_lint_allowed(cx, CMP_NULL, expr.hir_id),
|
||||
is_null_path(cx, l),
|
||||
is_null_path(cx, r),
|
||||
) {
|
||||
(false, true, false) if let Some(sugg) = Sugg::hir_opt(cx, r) => sugg.maybe_paren(),
|
||||
(false, false, true) if let Some(sugg) = Sugg::hir_opt(cx, l) => sugg.maybe_paren(),
|
||||
_ => return false,
|
||||
};
|
||||
let invert = if op == BinOpKind::Eq { "" } else { "!" };
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
CMP_NULL,
|
||||
expr.span,
|
||||
"comparing with null is better expressed by the `.is_null()` method",
|
||||
"try",
|
||||
format!("{invert}{non_null_path_snippet}.is_null()",),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
true
|
||||
}
|
||||
|
||||
fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
if let ExprKind::Call(pathexp, []) = expr.kind {
|
||||
matches!(
|
||||
pathexp.basic_res().opt_diag_name(cx),
|
||||
Some(sym::ptr_null | sym::ptr_null_mut)
|
||||
)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
202
clippy_lints/src/ptr/mod.rs
Normal file
202
clippy_lints/src/ptr/mod.rs
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
use rustc_hir::{BinOpKind, Body, Expr, ExprKind, ImplItemKind, ItemKind, Node, TraitFn, TraitItem, TraitItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
||||
mod cmp_null;
|
||||
mod mut_from_ref;
|
||||
mod ptr_arg;
|
||||
mod ptr_eq;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// This lint checks for function arguments of type `&String`, `&Vec`,
|
||||
/// `&PathBuf`, and `Cow<_>`. It will also suggest you replace `.clone()` calls
|
||||
/// with the appropriate `.to_owned()`/`to_string()` calls.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Requiring the argument to be of the specific type
|
||||
/// makes the function less useful for no benefit; slices in the form of `&[T]`
|
||||
/// or `&str` usually suffice and can be obtained from other types, too.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// There may be `fn(&Vec)`-typed references pointing to your function.
|
||||
/// If you have them, you will get a compiler error after applying this lint's
|
||||
/// suggestions. You then have the choice to undo your changes or change the
|
||||
/// type of the reference.
|
||||
///
|
||||
/// Note that if the function is part of your public interface, there may be
|
||||
/// other crates referencing it, of which you may not be aware. Carefully
|
||||
/// deprecate the function before applying the lint suggestions in this case.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// fn foo(&Vec<u32>) { .. }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```ignore
|
||||
/// fn foo(&[u32]) { .. }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub PTR_ARG,
|
||||
style,
|
||||
"fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// This lint checks for equality comparisons with `ptr::null`
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It's easier and more readable to use the inherent
|
||||
/// `.is_null()`
|
||||
/// method instead
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// use std::ptr;
|
||||
///
|
||||
/// if x == ptr::null {
|
||||
/// // ..
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// if x.is_null() {
|
||||
/// // ..
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub CMP_NULL,
|
||||
style,
|
||||
"comparing a pointer to a null pointer, suggesting to use `.is_null()` instead"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// This lint checks for functions that take immutable references and return
|
||||
/// mutable ones. This will not trigger if no unsafe code exists as there
|
||||
/// are multiple safe functions which will do this transformation
|
||||
///
|
||||
/// To be on the conservative side, if there's at least one mutable
|
||||
/// reference with the output lifetime, this lint will not trigger.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Creating a mutable reference which can be repeatably derived from an
|
||||
/// immutable reference is unsound as it allows creating multiple live
|
||||
/// mutable references to the same object.
|
||||
///
|
||||
/// This [error](https://github.com/rust-lang/rust/issues/39465) actually
|
||||
/// lead to an interim Rust release 1.15.1.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// This pattern is used by memory allocators to allow allocating multiple
|
||||
/// objects while returning mutable references to each one. So long as
|
||||
/// different mutable references are returned each time such a function may
|
||||
/// be safe.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// fn foo(&Foo) -> &mut Bar { .. }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub MUT_FROM_REF,
|
||||
correctness,
|
||||
"fns that create mutable refs from immutable ref args"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Use `std::ptr::eq` when applicable
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `ptr::eq` can be used to compare `&T` references
|
||||
/// (which coerce to `*const T` implicitly) by their address rather than
|
||||
/// comparing the values they point to.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let a = &[1, 2, 3];
|
||||
/// let b = &[1, 2, 3];
|
||||
///
|
||||
/// assert!(a as *const _ as usize == b as *const _ as usize);
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let a = &[1, 2, 3];
|
||||
/// let b = &[1, 2, 3];
|
||||
///
|
||||
/// assert!(std::ptr::eq(a, b));
|
||||
/// ```
|
||||
#[clippy::version = "1.49.0"]
|
||||
pub PTR_EQ,
|
||||
style,
|
||||
"use `std::ptr::eq` when comparing raw pointers"
|
||||
}
|
||||
|
||||
declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, PTR_EQ]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Ptr {
|
||||
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
|
||||
if let TraitItemKind::Fn(sig, trait_method) = &item.kind {
|
||||
if matches!(trait_method, TraitFn::Provided(_)) {
|
||||
// Handled by `check_body`.
|
||||
return;
|
||||
}
|
||||
|
||||
mut_from_ref::check(cx, sig, None);
|
||||
ptr_arg::check_trait_item(cx, item.owner_id, sig);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_body(&mut self, cx: &LateContext<'tcx>, body: &Body<'tcx>) {
|
||||
let mut parents = cx.tcx.hir_parent_iter(body.value.hir_id);
|
||||
let (item_id, sig, is_trait_item) = match parents.next() {
|
||||
Some((_, Node::Item(i))) => {
|
||||
if let ItemKind::Fn { sig, .. } = &i.kind {
|
||||
(i.owner_id, sig, false)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
Some((_, Node::ImplItem(i))) => {
|
||||
if !matches!(parents.next(),
|
||||
Some((_, Node::Item(i))) if matches!(&i.kind, ItemKind::Impl(i) if i.of_trait.is_none())
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if let ImplItemKind::Fn(sig, _) = &i.kind {
|
||||
(i.owner_id, sig, false)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
Some((_, Node::TraitItem(i))) => {
|
||||
if let TraitItemKind::Fn(sig, _) = &i.kind {
|
||||
(i.owner_id, sig, true)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
|
||||
mut_from_ref::check(cx, sig, Some(body));
|
||||
ptr_arg::check_body(cx, body, item_id, sig, is_trait_item);
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Binary(op, l, r) = expr.kind
|
||||
&& (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne)
|
||||
{
|
||||
#[expect(
|
||||
clippy::collapsible_if,
|
||||
reason = "the outer `if`s check the HIR, the inner ones run lints"
|
||||
)]
|
||||
if !cmp_null::check(cx, expr, op.node, l, r) {
|
||||
ptr_eq::check(cx, op.node, l, r, expr.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
75
clippy_lints/src/ptr/mut_from_ref.rs
Normal file
75
clippy_lints/src/ptr/mut_from_ref.rs
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
use super::MUT_FROM_REF;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::visitors::contains_unsafe_block;
|
||||
use rustc_errors::MultiSpan;
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::{self as hir, Body, FnRetTy, FnSig, GenericArg, Lifetime, Mutability, TyKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::Span;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Option<&Body<'tcx>>) {
|
||||
let FnRetTy::Return(ty) = sig.decl.output else { return };
|
||||
for (out, mutability, out_span) in get_lifetimes(ty) {
|
||||
if mutability != Some(Mutability::Mut) {
|
||||
continue;
|
||||
}
|
||||
let out_region = cx.tcx.named_bound_var(out.hir_id);
|
||||
// `None` if one of the types contains `&'a mut T` or `T<'a>`.
|
||||
// Else, contains all the locations of `&'a T` types.
|
||||
let args_immut_refs: Option<Vec<Span>> = sig
|
||||
.decl
|
||||
.inputs
|
||||
.iter()
|
||||
.flat_map(get_lifetimes)
|
||||
.filter(|&(lt, _, _)| cx.tcx.named_bound_var(lt.hir_id) == out_region)
|
||||
.map(|(_, mutability, span)| (mutability == Some(Mutability::Not)).then_some(span))
|
||||
.collect();
|
||||
if let Some(args_immut_refs) = args_immut_refs
|
||||
&& !args_immut_refs.is_empty()
|
||||
&& body.is_none_or(|body| sig.header.is_unsafe() || contains_unsafe_block(cx, body.value))
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MUT_FROM_REF,
|
||||
out_span,
|
||||
"mutable borrow from immutable input(s)",
|
||||
|diag| {
|
||||
let ms = MultiSpan::from_spans(args_immut_refs);
|
||||
diag.span_note(ms, "immutable borrow here");
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct LifetimeVisitor<'tcx> {
|
||||
result: Vec<(&'tcx Lifetime, Option<Mutability>, Span)>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for LifetimeVisitor<'tcx> {
|
||||
fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, hir::AmbigArg>) {
|
||||
if let TyKind::Ref(lt, ref m) = ty.kind {
|
||||
self.result.push((lt, Some(m.mutbl), ty.span));
|
||||
}
|
||||
hir::intravisit::walk_ty(self, ty);
|
||||
}
|
||||
|
||||
fn visit_generic_arg(&mut self, generic_arg: &'tcx GenericArg<'tcx>) {
|
||||
if let GenericArg::Lifetime(lt) = generic_arg {
|
||||
self.result.push((lt, None, generic_arg.span()));
|
||||
}
|
||||
hir::intravisit::walk_generic_arg(self, generic_arg);
|
||||
}
|
||||
}
|
||||
|
||||
/// Visit `ty` and collect the all the lifetimes appearing in it, implicit or not.
|
||||
///
|
||||
/// The second field of the vector's elements indicate if the lifetime is attached to a
|
||||
/// shared reference, a mutable reference, or neither.
|
||||
fn get_lifetimes<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Vec<(&'tcx Lifetime, Option<Mutability>, Span)> {
|
||||
use hir::intravisit::VisitorExt as _;
|
||||
|
||||
let mut visitor = LifetimeVisitor { result: Vec::new() };
|
||||
visitor.visit_ty_unambig(ty);
|
||||
visitor.result
|
||||
}
|
||||
|
|
@ -1,24 +1,22 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
|
||||
use clippy_utils::res::{MaybeDef, MaybeResPath};
|
||||
use super::PTR_ARG;
|
||||
use clippy_utils::diagnostics::span_lint_hir_and_then;
|
||||
use clippy_utils::res::MaybeResPath;
|
||||
use clippy_utils::source::SpanRangeExt;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::visitors::contains_unsafe_block;
|
||||
use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, std_or_core, sym};
|
||||
use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, sym};
|
||||
use hir::LifetimeKind;
|
||||
use rustc_abi::ExternAbi;
|
||||
use rustc_errors::{Applicability, MultiSpan};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::hir_id::{HirId, HirIdMap};
|
||||
use rustc_hir::intravisit::{Visitor, walk_expr};
|
||||
use rustc_hir::{
|
||||
self as hir, AnonConst, BinOpKind, BindingMode, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg, ImplItemKind,
|
||||
ItemKind, Lifetime, Mutability, Node, Param, PatKind, QPath, TraitFn, TraitItem, TraitItemKind, TyKind,
|
||||
self as hir, AnonConst, BindingMode, Body, Expr, ExprKind, FnSig, GenericArg, Lifetime, Mutability, Node, OwnerId,
|
||||
Param, PatKind, QPath, TyKind,
|
||||
};
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_infer::traits::{Obligation, ObligationCause};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::ty::{self, Binder, ClauseKind, ExistentialPredicate, List, PredicateKind, Ty};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_trait_selection::infer::InferCtxtExt as _;
|
||||
|
|
@ -27,260 +25,65 @@ use std::{fmt, iter};
|
|||
|
||||
use crate::vec::is_allowed_vec_method;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// This lint checks for function arguments of type `&String`, `&Vec`,
|
||||
/// `&PathBuf`, and `Cow<_>`. It will also suggest you replace `.clone()` calls
|
||||
/// with the appropriate `.to_owned()`/`to_string()` calls.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Requiring the argument to be of the specific type
|
||||
/// makes the function less useful for no benefit; slices in the form of `&[T]`
|
||||
/// or `&str` usually suffice and can be obtained from other types, too.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// There may be `fn(&Vec)`-typed references pointing to your function.
|
||||
/// If you have them, you will get a compiler error after applying this lint's
|
||||
/// suggestions. You then have the choice to undo your changes or change the
|
||||
/// type of the reference.
|
||||
///
|
||||
/// Note that if the function is part of your public interface, there may be
|
||||
/// other crates referencing it, of which you may not be aware. Carefully
|
||||
/// deprecate the function before applying the lint suggestions in this case.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// fn foo(&Vec<u32>) { .. }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```ignore
|
||||
/// fn foo(&[u32]) { .. }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub PTR_ARG,
|
||||
style,
|
||||
"fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// This lint checks for equality comparisons with `ptr::null`
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It's easier and more readable to use the inherent
|
||||
/// `.is_null()`
|
||||
/// method instead
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// use std::ptr;
|
||||
///
|
||||
/// if x == ptr::null {
|
||||
/// // ..
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// if x.is_null() {
|
||||
/// // ..
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub CMP_NULL,
|
||||
style,
|
||||
"comparing a pointer to a null pointer, suggesting to use `.is_null()` instead"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// This lint checks for functions that take immutable references and return
|
||||
/// mutable ones. This will not trigger if no unsafe code exists as there
|
||||
/// are multiple safe functions which will do this transformation
|
||||
///
|
||||
/// To be on the conservative side, if there's at least one mutable
|
||||
/// reference with the output lifetime, this lint will not trigger.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Creating a mutable reference which can be repeatably derived from an
|
||||
/// immutable reference is unsound as it allows creating multiple live
|
||||
/// mutable references to the same object.
|
||||
///
|
||||
/// This [error](https://github.com/rust-lang/rust/issues/39465) actually
|
||||
/// lead to an interim Rust release 1.15.1.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// This pattern is used by memory allocators to allow allocating multiple
|
||||
/// objects while returning mutable references to each one. So long as
|
||||
/// different mutable references are returned each time such a function may
|
||||
/// be safe.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// fn foo(&Foo) -> &mut Bar { .. }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub MUT_FROM_REF,
|
||||
correctness,
|
||||
"fns that create mutable refs from immutable ref args"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Use `std::ptr::eq` when applicable
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `ptr::eq` can be used to compare `&T` references
|
||||
/// (which coerce to `*const T` implicitly) by their address rather than
|
||||
/// comparing the values they point to.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let a = &[1, 2, 3];
|
||||
/// let b = &[1, 2, 3];
|
||||
///
|
||||
/// assert!(a as *const _ as usize == b as *const _ as usize);
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let a = &[1, 2, 3];
|
||||
/// let b = &[1, 2, 3];
|
||||
///
|
||||
/// assert!(std::ptr::eq(a, b));
|
||||
/// ```
|
||||
#[clippy::version = "1.49.0"]
|
||||
pub PTR_EQ,
|
||||
style,
|
||||
"use `std::ptr::eq` when comparing raw pointers"
|
||||
}
|
||||
|
||||
declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, PTR_EQ]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Ptr {
|
||||
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
|
||||
if let TraitItemKind::Fn(sig, trait_method) = &item.kind {
|
||||
if matches!(trait_method, TraitFn::Provided(_)) {
|
||||
// Handled by check body.
|
||||
return;
|
||||
}
|
||||
|
||||
check_mut_from_ref(cx, sig, None);
|
||||
|
||||
if !matches!(sig.header.abi, ExternAbi::Rust) {
|
||||
// Ignore `extern` functions with non-Rust calling conventions
|
||||
return;
|
||||
}
|
||||
|
||||
for arg in check_fn_args(
|
||||
cx,
|
||||
cx.tcx.fn_sig(item.owner_id).instantiate_identity().skip_binder(),
|
||||
sig.decl.inputs,
|
||||
&[],
|
||||
)
|
||||
.filter(|arg| arg.mutability() == Mutability::Not)
|
||||
{
|
||||
span_lint_hir_and_then(cx, PTR_ARG, arg.emission_id, arg.span, arg.build_msg(), |diag| {
|
||||
diag.span_suggestion(
|
||||
arg.span,
|
||||
"change this to",
|
||||
format!("{}{}", arg.ref_prefix, arg.deref_ty.display(cx)),
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
pub(super) fn check_body<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
item_id: OwnerId,
|
||||
sig: &FnSig<'tcx>,
|
||||
is_trait_item: bool,
|
||||
) {
|
||||
if !matches!(sig.header.abi, ExternAbi::Rust) {
|
||||
// Ignore `extern` functions with non-Rust calling conventions
|
||||
return;
|
||||
}
|
||||
|
||||
fn check_body(&mut self, cx: &LateContext<'tcx>, body: &Body<'tcx>) {
|
||||
let mut parents = cx.tcx.hir_parent_iter(body.value.hir_id);
|
||||
let (item_id, sig, is_trait_item) = match parents.next() {
|
||||
Some((_, Node::Item(i))) => {
|
||||
if let ItemKind::Fn { sig, .. } = &i.kind {
|
||||
(i.owner_id, sig, false)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
Some((_, Node::ImplItem(i))) => {
|
||||
if !matches!(parents.next(),
|
||||
Some((_, Node::Item(i))) if matches!(&i.kind, ItemKind::Impl(i) if i.of_trait.is_none())
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if let ImplItemKind::Fn(sig, _) = &i.kind {
|
||||
(i.owner_id, sig, false)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
Some((_, Node::TraitItem(i))) => {
|
||||
if let TraitItemKind::Fn(sig, _) = &i.kind {
|
||||
(i.owner_id, sig, true)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
let decl = sig.decl;
|
||||
let sig = cx.tcx.fn_sig(item_id).instantiate_identity().skip_binder();
|
||||
let lint_args: Vec<_> = check_fn_args(cx, sig, decl.inputs, body.params)
|
||||
.filter(|arg| !is_trait_item || arg.mutability() == Mutability::Not)
|
||||
.collect();
|
||||
let results = check_ptr_arg_usage(cx, body, &lint_args);
|
||||
|
||||
check_mut_from_ref(cx, sig, Some(body));
|
||||
|
||||
if !matches!(sig.header.abi, ExternAbi::Rust) {
|
||||
// Ignore `extern` functions with non-Rust calling conventions
|
||||
return;
|
||||
}
|
||||
|
||||
let decl = sig.decl;
|
||||
let sig = cx.tcx.fn_sig(item_id).instantiate_identity().skip_binder();
|
||||
let lint_args: Vec<_> = check_fn_args(cx, sig, decl.inputs, body.params)
|
||||
.filter(|arg| !is_trait_item || arg.mutability() == Mutability::Not)
|
||||
.collect();
|
||||
let results = check_ptr_arg_usage(cx, body, &lint_args);
|
||||
|
||||
for (result, args) in iter::zip(&results, &lint_args).filter(|(r, _)| !r.skip) {
|
||||
span_lint_hir_and_then(cx, PTR_ARG, args.emission_id, args.span, args.build_msg(), |diag| {
|
||||
diag.multipart_suggestion(
|
||||
"change this to",
|
||||
iter::once((args.span, format!("{}{}", args.ref_prefix, args.deref_ty.display(cx))))
|
||||
.chain(result.replacements.iter().map(|r| {
|
||||
(
|
||||
r.expr_span,
|
||||
format!("{}{}", r.self_span.get_source_text(cx).unwrap(), r.replacement),
|
||||
)
|
||||
}))
|
||||
.collect(),
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Binary(op, l, r) = expr.kind
|
||||
&& (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne)
|
||||
{
|
||||
let non_null_path_snippet = match (
|
||||
is_lint_allowed(cx, CMP_NULL, expr.hir_id),
|
||||
is_null_path(cx, l),
|
||||
is_null_path(cx, r),
|
||||
) {
|
||||
(false, true, false) if let Some(sugg) = Sugg::hir_opt(cx, r) => sugg.maybe_paren(),
|
||||
(false, false, true) if let Some(sugg) = Sugg::hir_opt(cx, l) => sugg.maybe_paren(),
|
||||
_ => return check_ptr_eq(cx, expr, op.node, l, r),
|
||||
};
|
||||
let invert = if op.node == BinOpKind::Eq { "" } else { "!" };
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
CMP_NULL,
|
||||
expr.span,
|
||||
"comparing with null is better expressed by the `.is_null()` method",
|
||||
"try",
|
||||
format!("{invert}{non_null_path_snippet}.is_null()",),
|
||||
Applicability::MachineApplicable,
|
||||
for (result, args) in iter::zip(&results, &lint_args).filter(|(r, _)| !r.skip) {
|
||||
span_lint_hir_and_then(cx, PTR_ARG, args.emission_id, args.span, args.build_msg(), |diag| {
|
||||
diag.multipart_suggestion(
|
||||
"change this to",
|
||||
iter::once((args.span, format!("{}{}", args.ref_prefix, args.deref_ty.display(cx))))
|
||||
.chain(result.replacements.iter().map(|r| {
|
||||
(
|
||||
r.expr_span,
|
||||
format!("{}{}", r.self_span.get_source_text(cx).unwrap(), r.replacement),
|
||||
)
|
||||
}))
|
||||
.collect(),
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item_id: OwnerId, sig: &FnSig<'tcx>) {
|
||||
if !matches!(sig.header.abi, ExternAbi::Rust) {
|
||||
// Ignore `extern` functions with non-Rust calling conventions
|
||||
return;
|
||||
}
|
||||
|
||||
for arg in check_fn_args(
|
||||
cx,
|
||||
cx.tcx.fn_sig(item_id).instantiate_identity().skip_binder(),
|
||||
sig.decl.inputs,
|
||||
&[],
|
||||
)
|
||||
.filter(|arg| arg.mutability() == Mutability::Not)
|
||||
{
|
||||
span_lint_hir_and_then(cx, PTR_ARG, arg.emission_id, arg.span, arg.build_msg(), |diag| {
|
||||
diag.span_suggestion(
|
||||
arg.span,
|
||||
"change this to",
|
||||
format!("{}{}", arg.ref_prefix, arg.deref_ty.display(cx)),
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -393,10 +196,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
|
|||
hir_tys: &'tcx [hir::Ty<'tcx>],
|
||||
params: &'tcx [Param<'tcx>],
|
||||
) -> impl Iterator<Item = PtrArg<'tcx>> + 'cx {
|
||||
fn_sig
|
||||
.inputs()
|
||||
.iter()
|
||||
.zip(hir_tys.iter())
|
||||
iter::zip(fn_sig.inputs(), hir_tys)
|
||||
.enumerate()
|
||||
.filter_map(move |(i, (ty, hir_ty))| {
|
||||
if let ty::Ref(_, ty, mutability) = *ty.kind()
|
||||
|
|
@ -499,41 +299,6 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
|
|||
})
|
||||
}
|
||||
|
||||
fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Option<&Body<'tcx>>) {
|
||||
let FnRetTy::Return(ty) = sig.decl.output else { return };
|
||||
for (out, mutability, out_span) in get_lifetimes(ty) {
|
||||
if mutability != Some(Mutability::Mut) {
|
||||
continue;
|
||||
}
|
||||
let out_region = cx.tcx.named_bound_var(out.hir_id);
|
||||
// `None` if one of the types contains `&'a mut T` or `T<'a>`.
|
||||
// Else, contains all the locations of `&'a T` types.
|
||||
let args_immut_refs: Option<Vec<Span>> = sig
|
||||
.decl
|
||||
.inputs
|
||||
.iter()
|
||||
.flat_map(get_lifetimes)
|
||||
.filter(|&(lt, _, _)| cx.tcx.named_bound_var(lt.hir_id) == out_region)
|
||||
.map(|(_, mutability, span)| (mutability == Some(Mutability::Not)).then_some(span))
|
||||
.collect();
|
||||
if let Some(args_immut_refs) = args_immut_refs
|
||||
&& !args_immut_refs.is_empty()
|
||||
&& body.is_none_or(|body| sig.header.is_unsafe() || contains_unsafe_block(cx, body.value))
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MUT_FROM_REF,
|
||||
out_span,
|
||||
"mutable borrow from immutable input(s)",
|
||||
|diag| {
|
||||
let ms = MultiSpan::from_spans(args_immut_refs);
|
||||
diag.span_note(ms, "immutable borrow here");
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_lines)]
|
||||
fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &Body<'tcx>, args: &[PtrArg<'tcx>]) -> Vec<PtrArgResult> {
|
||||
struct V<'cx, 'tcx> {
|
||||
|
|
@ -658,11 +423,11 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &Body<'tcx>, args: &[
|
|||
match param.pat.kind {
|
||||
PatKind::Binding(BindingMode::NONE, id, ident, None)
|
||||
if !is_lint_allowed(cx, PTR_ARG, param.hir_id)
|
||||
// Let's not lint for the current parameter. The user may still intend to mutate
|
||||
// (or, if not mutate, then perhaps call a method that's not otherwise available
|
||||
// for) the referenced value behind the parameter with the underscore being only
|
||||
// temporary.
|
||||
&& !ident.name.as_str().starts_with('_') =>
|
||||
// Let's not lint for the current parameter. The user may still intend to mutate
|
||||
// (or, if not mutate, then perhaps call a method that's not otherwise available
|
||||
// for) the referenced value behind the parameter with the underscore being only
|
||||
// temporary.
|
||||
&& !ident.name.as_str().starts_with('_') =>
|
||||
{
|
||||
Some((id, i))
|
||||
},
|
||||
|
|
@ -708,123 +473,3 @@ fn matches_preds<'tcx>(
|
|||
.must_apply_modulo_regions(),
|
||||
})
|
||||
}
|
||||
|
||||
struct LifetimeVisitor<'tcx> {
|
||||
result: Vec<(&'tcx Lifetime, Option<Mutability>, Span)>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for LifetimeVisitor<'tcx> {
|
||||
fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, hir::AmbigArg>) {
|
||||
if let TyKind::Ref(lt, ref m) = ty.kind {
|
||||
self.result.push((lt, Some(m.mutbl), ty.span));
|
||||
}
|
||||
hir::intravisit::walk_ty(self, ty);
|
||||
}
|
||||
|
||||
fn visit_generic_arg(&mut self, generic_arg: &'tcx GenericArg<'tcx>) {
|
||||
if let GenericArg::Lifetime(lt) = generic_arg {
|
||||
self.result.push((lt, None, generic_arg.span()));
|
||||
}
|
||||
hir::intravisit::walk_generic_arg(self, generic_arg);
|
||||
}
|
||||
}
|
||||
|
||||
/// Visit `ty` and collect the all the lifetimes appearing in it, implicit or not.
|
||||
///
|
||||
/// The second field of the vector's elements indicate if the lifetime is attached to a
|
||||
/// shared reference, a mutable reference, or neither.
|
||||
fn get_lifetimes<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Vec<(&'tcx Lifetime, Option<Mutability>, Span)> {
|
||||
use hir::intravisit::VisitorExt as _;
|
||||
|
||||
let mut visitor = LifetimeVisitor { result: Vec::new() };
|
||||
visitor.visit_ty_unambig(ty);
|
||||
visitor.result
|
||||
}
|
||||
|
||||
fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
if let ExprKind::Call(pathexp, []) = expr.kind {
|
||||
matches!(
|
||||
pathexp.basic_res().opt_diag_name(cx),
|
||||
Some(sym::ptr_null | sym::ptr_null_mut)
|
||||
)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn check_ptr_eq<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
op: BinOpKind,
|
||||
left: &'tcx Expr<'_>,
|
||||
right: &'tcx Expr<'_>,
|
||||
) {
|
||||
if expr.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove one level of usize conversion if any
|
||||
let (left, right, usize_peeled) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) {
|
||||
(Some(lhs), Some(rhs)) => (lhs, rhs, true),
|
||||
_ => (left, right, false),
|
||||
};
|
||||
|
||||
// This lint concerns raw pointers
|
||||
let (left_ty, right_ty) = (cx.typeck_results().expr_ty(left), cx.typeck_results().expr_ty(right));
|
||||
if !left_ty.is_raw_ptr() || !right_ty.is_raw_ptr() {
|
||||
return;
|
||||
}
|
||||
|
||||
let ((left_var, left_casts_peeled), (right_var, right_casts_peeled)) =
|
||||
(peel_raw_casts(cx, left, left_ty), peel_raw_casts(cx, right, right_ty));
|
||||
|
||||
if !(usize_peeled || left_casts_peeled || right_casts_peeled) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let left_snip = Sugg::hir_with_context(cx, left_var, expr.span.ctxt(), "_", &mut app);
|
||||
let right_snip = Sugg::hir_with_context(cx, right_var, expr.span.ctxt(), "_", &mut app);
|
||||
{
|
||||
let Some(top_crate) = std_or_core(cx) else { return };
|
||||
let invert = if op == BinOpKind::Eq { "" } else { "!" };
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
PTR_EQ,
|
||||
expr.span,
|
||||
format!("use `{top_crate}::ptr::eq` when comparing raw pointers"),
|
||||
"try",
|
||||
format!("{invert}{top_crate}::ptr::eq({left_snip}, {right_snip})"),
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// If the given expression is a cast to a usize, return the lhs of the cast
|
||||
// E.g., `foo as *const _ as usize` returns `foo as *const _`.
|
||||
fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
|
||||
if !cast_expr.span.from_expansion()
|
||||
&& cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize
|
||||
&& let ExprKind::Cast(expr, _) = cast_expr.kind
|
||||
{
|
||||
Some(expr)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// Peel raw casts if the remaining expression can be coerced to it, and whether casts have been
|
||||
// peeled or not.
|
||||
fn peel_raw_casts<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expr_ty: Ty<'tcx>) -> (&'tcx Expr<'tcx>, bool) {
|
||||
if !expr.span.from_expansion()
|
||||
&& let ExprKind::Cast(inner, _) = expr.kind
|
||||
&& let ty::RawPtr(target_ty, _) = expr_ty.kind()
|
||||
&& let inner_ty = cx.typeck_results().expr_ty(inner)
|
||||
&& let ty::RawPtr(inner_target_ty, _) | ty::Ref(_, inner_target_ty, _) = inner_ty.kind()
|
||||
&& target_ty == inner_target_ty
|
||||
{
|
||||
(peel_raw_casts(cx, inner, inner_ty).0, true)
|
||||
} else {
|
||||
(expr, false)
|
||||
}
|
||||
}
|
||||
87
clippy_lints/src/ptr/ptr_eq.rs
Normal file
87
clippy_lints/src/ptr/ptr_eq.rs
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
use super::PTR_EQ;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::std_or_core;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::Span;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
op: BinOpKind,
|
||||
left: &'tcx Expr<'_>,
|
||||
right: &'tcx Expr<'_>,
|
||||
span: Span,
|
||||
) {
|
||||
if span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove one level of usize conversion if any
|
||||
let (left, right, usize_peeled) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) {
|
||||
(Some(lhs), Some(rhs)) => (lhs, rhs, true),
|
||||
_ => (left, right, false),
|
||||
};
|
||||
|
||||
// This lint concerns raw pointers
|
||||
let (left_ty, right_ty) = (cx.typeck_results().expr_ty(left), cx.typeck_results().expr_ty(right));
|
||||
if !left_ty.is_raw_ptr() || !right_ty.is_raw_ptr() {
|
||||
return;
|
||||
}
|
||||
|
||||
let ((left_var, left_casts_peeled), (right_var, right_casts_peeled)) =
|
||||
(peel_raw_casts(cx, left, left_ty), peel_raw_casts(cx, right, right_ty));
|
||||
|
||||
if !(usize_peeled || left_casts_peeled || right_casts_peeled) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let ctxt = span.ctxt();
|
||||
let left_snip = Sugg::hir_with_context(cx, left_var, ctxt, "_", &mut app);
|
||||
let right_snip = Sugg::hir_with_context(cx, right_var, ctxt, "_", &mut app);
|
||||
{
|
||||
let Some(top_crate) = std_or_core(cx) else { return };
|
||||
let invert = if op == BinOpKind::Eq { "" } else { "!" };
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
PTR_EQ,
|
||||
span,
|
||||
format!("use `{top_crate}::ptr::eq` when comparing raw pointers"),
|
||||
"try",
|
||||
format!("{invert}{top_crate}::ptr::eq({left_snip}, {right_snip})"),
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// If the given expression is a cast to a usize, return the lhs of the cast
|
||||
// E.g., `foo as *const _ as usize` returns `foo as *const _`.
|
||||
fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
|
||||
if !cast_expr.span.from_expansion()
|
||||
&& cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize
|
||||
&& let ExprKind::Cast(expr, _) = cast_expr.kind
|
||||
{
|
||||
Some(expr)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// Peel raw casts if the remaining expression can be coerced to it, and whether casts have been
|
||||
// peeled or not.
|
||||
fn peel_raw_casts<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expr_ty: Ty<'tcx>) -> (&'tcx Expr<'tcx>, bool) {
|
||||
if !expr.span.from_expansion()
|
||||
&& let ExprKind::Cast(inner, _) = expr.kind
|
||||
&& let ty::RawPtr(target_ty, _) = expr_ty.kind()
|
||||
&& let inner_ty = cx.typeck_results().expr_ty(inner)
|
||||
&& let ty::RawPtr(inner_target_ty, _) | ty::Ref(_, inner_target_ty, _) = inner_ty.kind()
|
||||
&& target_ty == inner_target_ty
|
||||
{
|
||||
(peel_raw_casts(cx, inner, inner_ty).0, true)
|
||||
} else {
|
||||
(expr, false)
|
||||
}
|
||||
}
|
||||
|
|
@ -150,7 +150,7 @@ fn check_let_some_else_return_none(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
|
|||
let init_expr_str = Sugg::hir_with_applicability(cx, init_expr, "..", &mut applicability).maybe_paren();
|
||||
// Take care when binding is `ref`
|
||||
let sugg = if let PatKind::Binding(
|
||||
BindingMode(ByRef::Yes(_,ref_mutability), binding_mutability),
|
||||
BindingMode(ByRef::Yes(_, ref_mutability), binding_mutability),
|
||||
_hir_id,
|
||||
ident,
|
||||
subpattern,
|
||||
|
|
|
|||
|
|
@ -3,11 +3,17 @@ use clippy_utils::res::{MaybeDef, MaybeResPath};
|
|||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::{is_default_equivalent_call, local_is_initialized};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_data_structures::smallvec::SmallVec;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, LangItem, QPath};
|
||||
use rustc_hir::{Body, BodyId, Expr, ExprKind, HirId, LangItem, QPath};
|
||||
use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::sym;
|
||||
use rustc_middle::hir::place::ProjectionKind;
|
||||
use rustc_middle::mir::FakeReadCause;
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{Symbol, sym};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
@ -33,17 +39,57 @@ declare_clippy_lint! {
|
|||
perf,
|
||||
"assigning a newly created box to `Box<T>` is inefficient"
|
||||
}
|
||||
declare_lint_pass!(ReplaceBox => [REPLACE_BOX]);
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ReplaceBox {
|
||||
consumed_locals: FxHashSet<HirId>,
|
||||
loaded_bodies: SmallVec<[BodyId; 2]>,
|
||||
}
|
||||
|
||||
impl ReplaceBox {
|
||||
fn get_consumed_locals(&mut self, cx: &LateContext<'_>) -> &FxHashSet<HirId> {
|
||||
if let Some(body_id) = cx.enclosing_body
|
||||
&& !self.loaded_bodies.contains(&body_id)
|
||||
{
|
||||
self.loaded_bodies.push(body_id);
|
||||
ExprUseVisitor::for_clippy(
|
||||
cx,
|
||||
cx.tcx.hir_body_owner_def_id(body_id),
|
||||
MovedVariablesCtxt {
|
||||
consumed_locals: &mut self.consumed_locals,
|
||||
},
|
||||
)
|
||||
.consume_body(cx.tcx.hir_body(body_id))
|
||||
.into_ok();
|
||||
}
|
||||
|
||||
&self.consumed_locals
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(ReplaceBox => [REPLACE_BOX]);
|
||||
|
||||
impl LateLintPass<'_> for ReplaceBox {
|
||||
fn check_body_post(&mut self, _: &LateContext<'_>, body: &Body<'_>) {
|
||||
if self.loaded_bodies.first().is_some_and(|&x| x == body.id()) {
|
||||
self.consumed_locals.clear();
|
||||
self.loaded_bodies.clear();
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
|
||||
if let ExprKind::Assign(lhs, rhs, _) = &expr.kind
|
||||
&& !lhs.span.from_expansion()
|
||||
&& !rhs.span.from_expansion()
|
||||
&& let lhs_ty = cx.typeck_results().expr_ty(lhs)
|
||||
&& let Some(inner_ty) = lhs_ty.boxed_ty()
|
||||
// No diagnostic for late-initialized locals
|
||||
&& lhs.res_local_id().is_none_or(|local| local_is_initialized(cx, local))
|
||||
&& let Some(inner_ty) = lhs_ty.boxed_ty()
|
||||
// No diagnostic if this is a local that has been moved, or the field
|
||||
// of a local that has been moved, or several chained field accesses of a local
|
||||
&& local_base(lhs).is_none_or(|(base_id, _)| {
|
||||
!self.get_consumed_locals(cx).contains(&base_id)
|
||||
})
|
||||
{
|
||||
if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default)
|
||||
&& implements_trait(cx, inner_ty, default_trait_id, &[])
|
||||
|
|
@ -109,3 +155,46 @@ fn get_box_new_payload<'tcx>(cx: &LateContext<'_>, expr: &Expr<'tcx>) -> Option<
|
|||
None
|
||||
}
|
||||
}
|
||||
|
||||
struct MovedVariablesCtxt<'a> {
|
||||
consumed_locals: &'a mut FxHashSet<HirId>,
|
||||
}
|
||||
|
||||
impl<'tcx> Delegate<'tcx> for MovedVariablesCtxt<'_> {
|
||||
fn consume(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) {
|
||||
if let PlaceBase::Local(id) = cmt.place.base
|
||||
&& let mut projections = cmt
|
||||
.place
|
||||
.projections
|
||||
.iter()
|
||||
.filter(|x| matches!(x.kind, ProjectionKind::Deref))
|
||||
// Either no deref or multiple derefs
|
||||
&& (projections.next().is_none() || projections.next().is_some())
|
||||
{
|
||||
self.consumed_locals.insert(id);
|
||||
}
|
||||
}
|
||||
|
||||
fn use_cloned(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
|
||||
|
||||
fn borrow(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) {}
|
||||
|
||||
fn mutate(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
|
||||
|
||||
fn fake_read(&mut self, _: &PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {}
|
||||
}
|
||||
|
||||
/// A local place followed by optional fields
|
||||
type IdFields = (HirId, Vec<Symbol>);
|
||||
|
||||
/// If `expr` is a local variable with optional field accesses, return it.
|
||||
fn local_base(expr: &Expr<'_>) -> Option<IdFields> {
|
||||
match expr.kind {
|
||||
ExprKind::Path(qpath) => qpath.res_local_id().map(|id| (id, Vec::new())),
|
||||
ExprKind::Field(expr, field) => local_base(expr).map(|(id, mut fields)| {
|
||||
fields.push(field.name);
|
||||
(id, fields)
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use clippy_utils::res::MaybeResPath;
|
|||
use clippy_utils::source::SpanRangeExt;
|
||||
use clippy_utils::sugg::has_enclosing_paren;
|
||||
use clippy_utils::visitors::for_each_expr;
|
||||
use clippy_utils::{binary_expr_needs_parentheses, fn_def_id, span_contains_cfg};
|
||||
use clippy_utils::{binary_expr_needs_parentheses, fn_def_id, span_contains_non_whitespace};
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Block, Expr, PatKind, StmtKind};
|
||||
|
|
@ -27,7 +27,7 @@ pub(super) fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>)
|
|||
&& !initexpr.span.in_external_macro(cx.sess().source_map())
|
||||
&& !retexpr.span.in_external_macro(cx.sess().source_map())
|
||||
&& !local.span.from_expansion()
|
||||
&& !span_contains_cfg(cx, stmt.span.between(retexpr.span))
|
||||
&& !span_contains_non_whitespace(cx, stmt.span.between(retexpr.span), true)
|
||||
{
|
||||
span_lint_hir_and_then(
|
||||
cx,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::res::MaybeResPath;
|
||||
use clippy_utils::source::{indent_of, snippet};
|
||||
use clippy_utils::{expr_or_init, get_attr, peel_hir_expr_unary, sym};
|
||||
use clippy_utils::{expr_or_init, get_builtin_attr, peel_hir_expr_unary, sym};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
|
|
@ -167,7 +167,7 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> {
|
|||
|
||||
fn has_sig_drop_attr_uncached(&mut self, ty: Ty<'tcx>, depth: usize) -> bool {
|
||||
if let Some(adt) = ty.ty_adt_def() {
|
||||
let mut iter = get_attr(
|
||||
let mut iter = get_builtin_attr(
|
||||
self.cx.sess(),
|
||||
self.cx.tcx.get_all_attrs(adt.did()),
|
||||
sym::has_significant_drop,
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::higher::VecArgs;
|
||||
use clippy_utils::macros::root_macro_call_first_node;
|
||||
use clippy_utils::source::SpanRangeExt;
|
||||
use clippy_utils::source::{SpanRangeExt, snippet_with_context};
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::{is_no_std_crate, sym};
|
||||
use rustc_ast::{LitIntType, LitKind, UintTy};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, LangItem, StructTailExpr};
|
||||
use rustc_hir::{Expr, ExprKind, StructTailExpr};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::DesugaringKind;
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
@ -86,19 +87,21 @@ impl LateLintPass<'_> for SingleRangeInVecInit {
|
|||
return;
|
||||
};
|
||||
|
||||
let ExprKind::Struct(&qpath, [start, end], StructTailExpr::None) = inner_expr.kind else {
|
||||
let ExprKind::Struct(_, [start, end], StructTailExpr::None) = inner_expr.kind else {
|
||||
return;
|
||||
};
|
||||
|
||||
if cx.tcx.qpath_is_lang_item(qpath, LangItem::Range)
|
||||
if inner_expr.span.is_desugaring(DesugaringKind::RangeExpr)
|
||||
&& let ty = cx.typeck_results().expr_ty(start.expr)
|
||||
&& let Some(snippet) = span.get_source_text(cx)
|
||||
// `is_from_proc_macro` will skip any `vec![]`. Let's not!
|
||||
&& snippet.starts_with(suggested_type.starts_with())
|
||||
&& snippet.ends_with(suggested_type.ends_with())
|
||||
&& let Some(start_snippet) = start.span.get_source_text(cx)
|
||||
&& let Some(end_snippet) = end.span.get_source_text(cx)
|
||||
{
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let (start_snippet, _) = snippet_with_context(cx, start.expr.span, span.ctxt(), "..", &mut applicability);
|
||||
let (end_snippet, _) = snippet_with_context(cx, end.expr.span, span.ctxt(), "..", &mut applicability);
|
||||
|
||||
let should_emit_every_value = if let Some(step_def_id) = cx.tcx.get_diagnostic_item(sym::range_step)
|
||||
&& implements_trait(cx, ty, step_def_id, &[])
|
||||
{
|
||||
|
|
@ -129,7 +132,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit {
|
|||
span,
|
||||
"if you wanted a `Vec` that contains the entire range, try",
|
||||
format!("({start_snippet}..{end_snippet}).collect::<std::vec::Vec<{ty}>>()"),
|
||||
Applicability::MaybeIncorrect,
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -138,7 +141,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit {
|
|||
inner_expr.span,
|
||||
format!("if you wanted {suggested_type} of len {end_snippet}, try"),
|
||||
format!("{start_snippet}; {end_snippet}"),
|
||||
Applicability::MaybeIncorrect,
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -435,10 +435,11 @@ declare_clippy_lint! {
|
|||
/// to infer a technically correct yet unexpected type.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// ```
|
||||
/// # unsafe {
|
||||
/// let mut x: i32 = 0;
|
||||
/// // Avoid "naked" calls to `transmute()`!
|
||||
/// let x: i32 = std::mem::transmute([1u16, 2u16]);
|
||||
/// x = std::mem::transmute([1u16, 2u16]);
|
||||
///
|
||||
/// // `first_answers` is intended to transmute a slice of bool to a slice of u8.
|
||||
/// // But the programmer forgot to index the first element of the outer slice,
|
||||
|
|
@ -449,7 +450,7 @@ declare_clippy_lint! {
|
|||
/// # }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// ```
|
||||
/// # unsafe {
|
||||
/// let x = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]);
|
||||
///
|
||||
|
|
|
|||
|
|
@ -1,115 +1,59 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::qpath_generic_tys;
|
||||
use clippy_utils::res::{MaybeDef, MaybeResPath};
|
||||
use clippy_utils::res::MaybeResPath;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{self as hir, QPath, TyKind};
|
||||
use rustc_hir::{QPath, Ty, TyKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::symbol::sym;
|
||||
use std::borrow::Cow;
|
||||
|
||||
use super::RC_BUFFER;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
|
||||
let app = Applicability::Unspecified;
|
||||
let name = cx.tcx.get_diagnostic_name(def_id);
|
||||
if name == Some(sym::Rc) {
|
||||
if let Some(alternate) = match_buffer_type(cx, qpath) {
|
||||
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
RC_BUFFER,
|
||||
hir_ty.span,
|
||||
"usage of `Rc<T>` when T is a buffer type",
|
||||
|diag| {
|
||||
diag.span_suggestion(hir_ty.span, "try", format!("Rc<{alternate}>"), app);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
let Some(ty) = qpath_generic_tys(qpath).next() else {
|
||||
return false;
|
||||
};
|
||||
if !ty.basic_res().is_diag_item(cx, sym::Vec) {
|
||||
return false;
|
||||
}
|
||||
let TyKind::Path(qpath) = &ty.kind else { return false };
|
||||
let inner_span = match qpath_generic_tys(qpath).next() {
|
||||
Some(ty) => ty.span,
|
||||
None => return false,
|
||||
};
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
RC_BUFFER,
|
||||
hir_ty.span,
|
||||
"usage of `Rc<T>` when T is a buffer type",
|
||||
|diag| {
|
||||
let mut applicability = app;
|
||||
diag.span_suggestion(
|
||||
hir_ty.span,
|
||||
"try",
|
||||
format!(
|
||||
"Rc<[{}]>",
|
||||
snippet_with_applicability(cx, inner_span, "..", &mut applicability)
|
||||
),
|
||||
app,
|
||||
);
|
||||
},
|
||||
);
|
||||
return true;
|
||||
}
|
||||
} else if name == Some(sym::Arc) {
|
||||
if let Some(alternate) = match_buffer_type(cx, qpath) {
|
||||
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
RC_BUFFER,
|
||||
hir_ty.span,
|
||||
"usage of `Arc<T>` when T is a buffer type",
|
||||
|diag| {
|
||||
diag.span_suggestion(hir_ty.span, "try", format!("Arc<{alternate}>"), app);
|
||||
},
|
||||
);
|
||||
} else if let Some(ty) = qpath_generic_tys(qpath).next() {
|
||||
if !ty.basic_res().is_diag_item(cx, sym::Vec) {
|
||||
return false;
|
||||
}
|
||||
let TyKind::Path(qpath) = &ty.kind else { return false };
|
||||
let inner_span = match qpath_generic_tys(qpath).next() {
|
||||
Some(ty) => ty.span,
|
||||
None => return false,
|
||||
};
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
RC_BUFFER,
|
||||
hir_ty.span,
|
||||
"usage of `Arc<T>` when T is a buffer type",
|
||||
|diag| {
|
||||
let mut applicability = app;
|
||||
diag.span_suggestion(
|
||||
hir_ty.span,
|
||||
"try",
|
||||
format!(
|
||||
"Arc<[{}]>",
|
||||
snippet_with_applicability(cx, inner_span, "..", &mut applicability)
|
||||
),
|
||||
app,
|
||||
);
|
||||
},
|
||||
);
|
||||
return true;
|
||||
}
|
||||
pub(super) fn check(cx: &LateContext<'_>, hir_ty: &Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
|
||||
let mut app = Applicability::Unspecified;
|
||||
let rc = match cx.tcx.get_diagnostic_name(def_id) {
|
||||
Some(sym::Rc) => "Rc",
|
||||
Some(sym::Arc) => "Arc",
|
||||
_ => return false,
|
||||
};
|
||||
if let Some(ty) = qpath_generic_tys(qpath).next()
|
||||
&& let Some(alternate) = match_buffer_type(cx, ty, &mut app)
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
RC_BUFFER,
|
||||
hir_ty.span,
|
||||
format!("usage of `{rc}<T>` when `T` is a buffer type"),
|
||||
|diag| {
|
||||
diag.span_suggestion_verbose(ty.span, "try", alternate, app);
|
||||
},
|
||||
);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn match_buffer_type(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> {
|
||||
let ty = qpath_generic_tys(qpath).next()?;
|
||||
fn match_buffer_type(
|
||||
cx: &LateContext<'_>,
|
||||
ty: &Ty<'_>,
|
||||
applicability: &mut Applicability,
|
||||
) -> Option<Cow<'static, str>> {
|
||||
let id = ty.basic_res().opt_def_id()?;
|
||||
let path = match cx.tcx.get_diagnostic_name(id) {
|
||||
Some(sym::OsString) => "std::ffi::OsStr",
|
||||
Some(sym::PathBuf) => "std::path::Path",
|
||||
_ if Some(id) == cx.tcx.lang_items().string() => "str",
|
||||
Some(sym::OsString) => "std::ffi::OsStr".into(),
|
||||
Some(sym::PathBuf) => "std::path::Path".into(),
|
||||
Some(sym::Vec) => {
|
||||
let TyKind::Path(vec_qpath) = &ty.kind else {
|
||||
return None;
|
||||
};
|
||||
let vec_generic_ty = qpath_generic_tys(vec_qpath).next()?;
|
||||
let snippet = snippet_with_applicability(cx, vec_generic_ty.span, "_", applicability);
|
||||
format!("[{snippet}]").into()
|
||||
},
|
||||
_ if Some(id) == cx.tcx.lang_items().string() => "str".into(),
|
||||
_ => return None,
|
||||
};
|
||||
Some(path)
|
||||
|
|
|
|||
|
|
@ -215,7 +215,13 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_has_safety_comment<'tcx>(cx: &LateContext<'tcx>, item: &hir::Item<'tcx>, (span, help_span): (Span, Span), is_doc: bool) {
|
||||
#[expect(clippy::too_many_lines)]
|
||||
fn check_has_safety_comment<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
item: &hir::Item<'tcx>,
|
||||
(span, help_span): (Span, Span),
|
||||
is_doc: bool,
|
||||
) {
|
||||
match &item.kind {
|
||||
ItemKind::Impl(Impl {
|
||||
of_trait: Some(of_trait),
|
||||
|
|
@ -236,12 +242,14 @@ fn check_has_safety_comment<'tcx>(cx: &LateContext<'tcx>, item: &hir::Item<'tcx>
|
|||
ItemKind::Impl(_) => {},
|
||||
// const and static items only need a safety comment if their body is an unsafe block, lint otherwise
|
||||
&ItemKind::Const(.., ct_rhs) => {
|
||||
if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, ct_rhs.hir_id()) {
|
||||
if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, ct_rhs.hir_id()) {
|
||||
let expr = const_item_rhs_to_expr(cx.tcx, ct_rhs);
|
||||
if let Some(expr) = expr && !matches!(
|
||||
expr.kind, hir::ExprKind::Block(block, _)
|
||||
if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
|
||||
) {
|
||||
if let Some(expr) = expr
|
||||
&& !matches!(
|
||||
expr.kind, hir::ExprKind::Block(block, _)
|
||||
if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
|
||||
)
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
UNNECESSARY_SAFETY_COMMENT,
|
||||
|
|
@ -256,8 +264,8 @@ fn check_has_safety_comment<'tcx>(cx: &LateContext<'tcx>, item: &hir::Item<'tcx>
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
&ItemKind::Static(.., body) => {
|
||||
},
|
||||
&ItemKind::Static(.., body) => {
|
||||
if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, body.hir_id) {
|
||||
let body = cx.tcx.hir_body(body);
|
||||
if !matches!(
|
||||
|
|
|
|||
|
|
@ -35,18 +35,17 @@ declare_lint_pass!(UnnecessaryMapOnConstructor => [UNNECESSARY_MAP_ON_CONSTRUCTO
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for UnnecessaryMapOnConstructor {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
|
||||
if expr.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
if let hir::ExprKind::MethodCall(path, recv, [map_arg], ..) = expr.kind
|
||||
if !expr.span.from_expansion()
|
||||
&& let hir::ExprKind::MethodCall(path, recv, [map_arg], ..) = expr.kind
|
||||
&& !map_arg.span.from_expansion()
|
||||
&& let hir::ExprKind::Path(fun) = map_arg.kind
|
||||
&& let Some(sym::Option | sym::Result) = cx.typeck_results().expr_ty(recv).opt_diag_name(cx)
|
||||
{
|
||||
let (constructor_path, constructor_item) = if let hir::ExprKind::Call(constructor, [arg, ..]) = recv.kind
|
||||
&& let hir::ExprKind::Path(constructor_path) = constructor.kind
|
||||
&& !constructor.span.from_expansion()
|
||||
&& !arg.span.from_expansion()
|
||||
{
|
||||
if constructor.span.from_expansion() || arg.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
(constructor_path, arg)
|
||||
} else {
|
||||
return;
|
||||
|
|
@ -67,29 +66,22 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMapOnConstructor {
|
|||
_ => return,
|
||||
}
|
||||
|
||||
if let hir::ExprKind::Path(fun) = map_arg.kind {
|
||||
if map_arg.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let fun_snippet = snippet_with_applicability(cx, fun.span(), "_", &mut applicability);
|
||||
let constructor_snippet =
|
||||
snippet_with_applicability(cx, constructor_path.span(), "_", &mut applicability);
|
||||
let constructor_arg_snippet =
|
||||
snippet_with_applicability(cx, constructor_item.span, "_", &mut applicability);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_MAP_ON_CONSTRUCTOR,
|
||||
expr.span,
|
||||
format!(
|
||||
"unnecessary {} on constructor {constructor_snippet}(_)",
|
||||
path.ident.name
|
||||
),
|
||||
"try",
|
||||
format!("{constructor_snippet}({fun_snippet}({constructor_arg_snippet}))"),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let fun_snippet = snippet_with_applicability(cx, fun.span(), "_", &mut app);
|
||||
let constructor_snippet = snippet_with_applicability(cx, constructor_path.span(), "_", &mut app);
|
||||
let constructor_arg_snippet = snippet_with_applicability(cx, constructor_item.span, "_", &mut app);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_MAP_ON_CONSTRUCTOR,
|
||||
expr.span,
|
||||
format!(
|
||||
"unnecessary `{}` on constructor `{constructor_snippet}(_)`",
|
||||
path.ident.name
|
||||
),
|
||||
"try",
|
||||
format!("{constructor_snippet}({fun_snippet}({constructor_arg_snippet}))"),
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use rustc_ast::mut_visit::*;
|
|||
use rustc_ast::{self as ast, DUMMY_NODE_ID, Mutability, Pat, PatKind, Pinnedness};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_data_structures::thin_vec::{ThinVec, thin_vec};
|
||||
use rustc_data_structures::thinvec::ExtractIf;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
|
@ -98,7 +99,7 @@ fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) {
|
|||
return;
|
||||
}
|
||||
|
||||
let mut pat = Box::new(pat.clone());
|
||||
let mut pat = pat.clone();
|
||||
|
||||
// Nix all the paren patterns everywhere so that they aren't in our way.
|
||||
remove_all_parens(&mut pat);
|
||||
|
|
@ -120,7 +121,7 @@ fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) {
|
|||
}
|
||||
|
||||
/// Remove all `(p)` patterns in `pat`.
|
||||
fn remove_all_parens(pat: &mut Box<Pat>) {
|
||||
fn remove_all_parens(pat: &mut Pat) {
|
||||
#[derive(Default)]
|
||||
struct Visitor {
|
||||
/// If is not in the outer most pattern. This is needed to avoid removing the outermost
|
||||
|
|
@ -143,7 +144,7 @@ fn remove_all_parens(pat: &mut Box<Pat>) {
|
|||
}
|
||||
|
||||
/// Insert parens where necessary according to Rust's precedence rules for patterns.
|
||||
fn insert_necessary_parens(pat: &mut Box<Pat>) {
|
||||
fn insert_necessary_parens(pat: &mut Pat) {
|
||||
struct Visitor;
|
||||
impl MutVisitor for Visitor {
|
||||
fn visit_pat(&mut self, pat: &mut Pat) {
|
||||
|
|
@ -152,7 +153,8 @@ fn insert_necessary_parens(pat: &mut Box<Pat>) {
|
|||
let target = match &mut pat.kind {
|
||||
// `i @ a | b`, `box a | b`, and `& mut? a | b`.
|
||||
Ident(.., Some(p)) | Box(p) | Ref(p, _, _) if matches!(&p.kind, Or(ps) if ps.len() > 1) => p,
|
||||
Ref(p, Pinnedness::Not, Mutability::Not) if matches!(p.kind, Ident(BindingMode::MUT, ..)) => p, // `&(mut x)`
|
||||
// `&(mut x)`
|
||||
Ref(p, Pinnedness::Not, Mutability::Not) if matches!(p.kind, Ident(BindingMode::MUT, ..)) => p,
|
||||
_ => return,
|
||||
};
|
||||
target.kind = Paren(Box::new(take_pat(target)));
|
||||
|
|
@ -163,7 +165,7 @@ fn insert_necessary_parens(pat: &mut Box<Pat>) {
|
|||
|
||||
/// Unnest or-patterns `p0 | ... | p1` in the pattern `pat`.
|
||||
/// For example, this would transform `Some(0) | FOO | Some(2)` into `Some(0 | 2) | FOO`.
|
||||
fn unnest_or_patterns(pat: &mut Box<Pat>) -> bool {
|
||||
fn unnest_or_patterns(pat: &mut Pat) -> bool {
|
||||
struct Visitor {
|
||||
changed: bool,
|
||||
}
|
||||
|
|
@ -385,15 +387,14 @@ fn take_pat(from: &mut Pat) -> Pat {
|
|||
/// in `tail_or` if there are any and return if there were.
|
||||
fn extend_with_tail_or(target: &mut Pat, tail_or: ThinVec<Pat>) -> bool {
|
||||
fn extend(target: &mut Pat, mut tail_or: ThinVec<Pat>) {
|
||||
match target {
|
||||
// On an existing or-pattern in the target, append to it.
|
||||
Pat { kind: Or(ps), .. } => ps.append(&mut tail_or),
|
||||
// Otherwise convert the target to an or-pattern.
|
||||
target => {
|
||||
let mut init_or = thin_vec![take_pat(target)];
|
||||
init_or.append(&mut tail_or);
|
||||
target.kind = Or(init_or);
|
||||
},
|
||||
// On an existing or-pattern in the target, append to it,
|
||||
// otherwise convert the target to an or-pattern.
|
||||
if let Or(ps) = &mut target.kind {
|
||||
ps.append(&mut tail_or);
|
||||
} else {
|
||||
let mut init_or = thin_vec![take_pat(target)];
|
||||
init_or.append(&mut tail_or);
|
||||
target.kind = Or(init_or);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -416,26 +417,14 @@ fn drain_matching(
|
|||
let mut tail_or = ThinVec::new();
|
||||
let mut idx = 0;
|
||||
|
||||
// If `ThinVec` had the `drain_filter` method, this loop could be rewritten
|
||||
// like so:
|
||||
//
|
||||
// for pat in alternatives.drain_filter(|p| {
|
||||
// // Check if we should extract, but only if `idx >= start`.
|
||||
// idx += 1;
|
||||
// idx > start && predicate(&p.kind)
|
||||
// }) {
|
||||
// tail_or.push(extract(pat.into_inner().kind));
|
||||
// }
|
||||
let mut i = 0;
|
||||
while i < alternatives.len() {
|
||||
idx += 1;
|
||||
// FIXME: once `thin-vec` releases a new version, change this to `alternatives.extract_if()`
|
||||
// See https://github.com/mozilla/thin-vec/issues/77
|
||||
for pat in ExtractIf::new(alternatives, |p| {
|
||||
// Check if we should extract, but only if `idx >= start`.
|
||||
if idx > start && predicate(&alternatives[i].kind) {
|
||||
let pat = alternatives.remove(i);
|
||||
tail_or.push(extract(pat.kind));
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
idx += 1;
|
||||
idx > start && predicate(&p.kind)
|
||||
}) {
|
||||
tail_or.push(extract(pat.kind));
|
||||
}
|
||||
|
||||
tail_or
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::res::MaybeQPath;
|
||||
use clippy_utils::{get_attr, higher, sym};
|
||||
use clippy_utils::{get_builtin_attr, higher, sym};
|
||||
use itertools::Itertools;
|
||||
use rustc_ast::LitIntType;
|
||||
use rustc_ast::ast::{LitFloatType, LitKind};
|
||||
|
|
@ -859,5 +859,5 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
|
|||
|
||||
fn has_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
|
||||
let attrs = cx.tcx.hir_attrs(hir_id);
|
||||
get_attr(cx.sess(), attrs, sym::author).count() > 0
|
||||
get_builtin_attr(cx.sess(), attrs, sym::author).count() > 0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use clippy_utils::{get_attr, sym};
|
||||
use clippy_utils::{get_builtin_attr, sym};
|
||||
use hir::TraitItem;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
|
|
@ -60,5 +60,5 @@ impl<'tcx> LateLintPass<'tcx> for DumpHir {
|
|||
|
||||
fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool {
|
||||
let attrs = cx.tcx.hir_attrs(hir_id);
|
||||
get_attr(cx.sess(), attrs, sym::dump).count() > 0
|
||||
get_builtin_attr(cx.sess(), attrs, sym::dump).count() > 0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,734 +0,0 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
|
||||
use clippy_utils::macros::{FormatArgsStorage, MacroCall, format_arg_removal_span, root_macro_call_first_node};
|
||||
use clippy_utils::source::{SpanRangeExt, expand_past_previous_comma};
|
||||
use clippy_utils::{is_in_test, sym};
|
||||
use rustc_ast::token::LitKind;
|
||||
use rustc_ast::{
|
||||
FormatArgPosition, FormatArgPositionKind, FormatArgs, FormatArgsPiece, FormatCount, FormatOptions,
|
||||
FormatPlaceholder, FormatTrait,
|
||||
};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, Impl, Item, ItemKind, OwnerId};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{BytePos, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// This lint warns when you use `println!("")` to
|
||||
/// print a newline.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// You should use `println!()`, which is simpler.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// println!("");
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// println!();
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub PRINTLN_EMPTY_STRING,
|
||||
style,
|
||||
"using `println!(\"\")` with an empty string"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// This lint warns when you use `print!()` with a format
|
||||
/// string that ends in a newline.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// You should use `println!()` instead, which appends the
|
||||
/// newline.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # let name = "World";
|
||||
/// print!("Hello {}!\n", name);
|
||||
/// ```
|
||||
/// use println!() instead
|
||||
/// ```no_run
|
||||
/// # let name = "World";
|
||||
/// println!("Hello {}!", name);
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub PRINT_WITH_NEWLINE,
|
||||
style,
|
||||
"using `print!()` with a format string that ends in a single newline"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for printing on *stdout*. The purpose of this lint
|
||||
/// is to catch debugging remnants.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// People often print on *stdout* while debugging an
|
||||
/// application and might forget to remove those prints afterward.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// Only catches `print!` and `println!` calls.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// println!("Hello world!");
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub PRINT_STDOUT,
|
||||
restriction,
|
||||
"printing on stdout"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for printing on *stderr*. The purpose of this lint
|
||||
/// is to catch debugging remnants.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// People often print on *stderr* while debugging an
|
||||
/// application and might forget to remove those prints afterward.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// Only catches `eprint!` and `eprintln!` calls.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// eprintln!("Hello world!");
|
||||
/// ```
|
||||
#[clippy::version = "1.50.0"]
|
||||
pub PRINT_STDERR,
|
||||
restriction,
|
||||
"printing on stderr"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of `Debug` formatting. The purpose of this
|
||||
/// lint is to catch debugging remnants.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// The purpose of the `Debug` trait is to facilitate debugging Rust code,
|
||||
/// and [no guarantees are made about its output][stability].
|
||||
/// It should not be used in user-facing output.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # let foo = "bar";
|
||||
/// println!("{:?}", foo);
|
||||
/// ```
|
||||
///
|
||||
/// [stability]: https://doc.rust-lang.org/stable/std/fmt/trait.Debug.html#stability
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub USE_DEBUG,
|
||||
restriction,
|
||||
"use of `Debug`-based formatting"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// This lint warns about the use of literals as `print!`/`println!` args.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Using literals as `println!` args is inefficient
|
||||
/// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
|
||||
/// (i.e., just put the literal in the format string)
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// println!("{}", "foo");
|
||||
/// ```
|
||||
/// use the literal without formatting:
|
||||
/// ```no_run
|
||||
/// println!("foo");
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub PRINT_LITERAL,
|
||||
style,
|
||||
"printing a literal with a format string"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// This lint warns when you use `writeln!(buf, "")` to
|
||||
/// print a newline.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// You should use `writeln!(buf)`, which is simpler.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # use std::fmt::Write;
|
||||
/// # let mut buf = String::new();
|
||||
/// writeln!(buf, "");
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # use std::fmt::Write;
|
||||
/// # let mut buf = String::new();
|
||||
/// writeln!(buf);
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub WRITELN_EMPTY_STRING,
|
||||
style,
|
||||
"using `writeln!(buf, \"\")` with an empty string"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// This lint warns when you use `write!()` with a format
|
||||
/// string that
|
||||
/// ends in a newline.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// You should use `writeln!()` instead, which appends the
|
||||
/// newline.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # use std::fmt::Write;
|
||||
/// # let mut buf = String::new();
|
||||
/// # let name = "World";
|
||||
/// write!(buf, "Hello {}!\n", name);
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # use std::fmt::Write;
|
||||
/// # let mut buf = String::new();
|
||||
/// # let name = "World";
|
||||
/// writeln!(buf, "Hello {}!", name);
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub WRITE_WITH_NEWLINE,
|
||||
style,
|
||||
"using `write!()` with a format string that ends in a single newline"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// This lint warns about the use of literals as `write!`/`writeln!` args.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Using literals as `writeln!` args is inefficient
|
||||
/// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
|
||||
/// (i.e., just put the literal in the format string)
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # use std::fmt::Write;
|
||||
/// # let mut buf = String::new();
|
||||
/// writeln!(buf, "{}", "foo");
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # use std::fmt::Write;
|
||||
/// # let mut buf = String::new();
|
||||
/// writeln!(buf, "foo");
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub WRITE_LITERAL,
|
||||
style,
|
||||
"writing a literal with a format string"
|
||||
}
|
||||
|
||||
pub struct Write {
|
||||
format_args: FormatArgsStorage,
|
||||
// The outermost `impl Debug` we're currently in. While we're in one, `USE_DEBUG` is deactivated
|
||||
outermost_debug_impl: Option<OwnerId>,
|
||||
allow_print_in_tests: bool,
|
||||
}
|
||||
|
||||
impl Write {
|
||||
pub fn new(conf: &'static Conf, format_args: FormatArgsStorage) -> Self {
|
||||
Self {
|
||||
format_args,
|
||||
outermost_debug_impl: None,
|
||||
allow_print_in_tests: conf.allow_print_in_tests,
|
||||
}
|
||||
}
|
||||
|
||||
fn in_debug_impl(&self) -> bool {
|
||||
self.outermost_debug_impl.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(Write => [
|
||||
PRINT_WITH_NEWLINE,
|
||||
PRINTLN_EMPTY_STRING,
|
||||
PRINT_STDOUT,
|
||||
PRINT_STDERR,
|
||||
USE_DEBUG,
|
||||
PRINT_LITERAL,
|
||||
WRITE_WITH_NEWLINE,
|
||||
WRITELN_EMPTY_STRING,
|
||||
WRITE_LITERAL,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Write {
|
||||
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
|
||||
// Only check for `impl Debug`s if we're not already in one
|
||||
if self.outermost_debug_impl.is_none() && is_debug_impl(cx, item) {
|
||||
self.outermost_debug_impl = Some(item.owner_id);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_item_post(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) {
|
||||
// Only clear `self.outermost_debug_impl` if we're escaping the _outermost_ debug impl
|
||||
if self.outermost_debug_impl == Some(item.owner_id) {
|
||||
self.outermost_debug_impl = None;
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
let Some(macro_call) = root_macro_call_first_node(cx, expr) else {
|
||||
return;
|
||||
};
|
||||
let Some(diag_name) = cx.tcx.get_diagnostic_name(macro_call.def_id) else {
|
||||
return;
|
||||
};
|
||||
let Some(name) = diag_name.as_str().strip_suffix("_macro") else {
|
||||
return;
|
||||
};
|
||||
|
||||
let is_build_script = cx
|
||||
.sess()
|
||||
.opts
|
||||
.crate_name
|
||||
.as_ref()
|
||||
.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 {
|
||||
sym::print_macro | sym::println_macro if !allowed_in_tests => {
|
||||
if !is_build_script {
|
||||
span_lint(cx, PRINT_STDOUT, macro_call.span, format!("use of `{name}!`"));
|
||||
}
|
||||
},
|
||||
sym::eprint_macro | sym::eprintln_macro if !allowed_in_tests => {
|
||||
span_lint(cx, PRINT_STDERR, macro_call.span, format!("use of `{name}!`"));
|
||||
},
|
||||
sym::write_macro | sym::writeln_macro => {},
|
||||
_ => return,
|
||||
}
|
||||
|
||||
if let Some(format_args) = self.format_args.get(cx, expr, macro_call.expn) {
|
||||
// ignore `writeln!(w)` and `write!(v, some_macro!())`
|
||||
if format_args.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
match diag_name {
|
||||
sym::print_macro | sym::eprint_macro | sym::write_macro => {
|
||||
check_newline(cx, format_args, ¯o_call, name);
|
||||
},
|
||||
sym::println_macro | sym::eprintln_macro | sym::writeln_macro => {
|
||||
check_empty_string(cx, format_args, ¯o_call, name);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
check_literal(cx, format_args, name);
|
||||
|
||||
if !self.in_debug_impl() {
|
||||
for piece in &format_args.template {
|
||||
if let &FormatArgsPiece::Placeholder(FormatPlaceholder {
|
||||
span: Some(span),
|
||||
format_trait: FormatTrait::Debug,
|
||||
..
|
||||
}) = piece
|
||||
{
|
||||
span_lint(cx, USE_DEBUG, span, "use of `Debug`-based formatting");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
|
||||
if let ItemKind::Impl(Impl {
|
||||
of_trait: Some(of_trait),
|
||||
..
|
||||
}) = &item.kind
|
||||
&& let Some(trait_id) = of_trait.trait_ref.trait_def_id()
|
||||
{
|
||||
cx.tcx.is_diagnostic_item(sym::Debug, trait_id)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) {
|
||||
let Some(&FormatArgsPiece::Literal(last)) = format_args.template.last() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let count_vertical_whitespace = || {
|
||||
format_args
|
||||
.template
|
||||
.iter()
|
||||
.filter_map(|piece| match piece {
|
||||
FormatArgsPiece::Literal(literal) => Some(literal),
|
||||
FormatArgsPiece::Placeholder(_) => None,
|
||||
})
|
||||
.flat_map(|literal| literal.as_str().chars())
|
||||
.filter(|ch| matches!(ch, '\r' | '\n'))
|
||||
.count()
|
||||
};
|
||||
|
||||
if last.as_str().ends_with('\n')
|
||||
// ignore format strings with other internal vertical whitespace
|
||||
&& count_vertical_whitespace() == 1
|
||||
{
|
||||
let mut format_string_span = format_args.span;
|
||||
|
||||
let lint = if name == "write" {
|
||||
format_string_span = expand_past_previous_comma(cx, format_string_span);
|
||||
|
||||
WRITE_WITH_NEWLINE
|
||||
} else {
|
||||
PRINT_WITH_NEWLINE
|
||||
};
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
lint,
|
||||
macro_call.span,
|
||||
format!("using `{name}!()` with a format string that ends in a single newline"),
|
||||
|diag| {
|
||||
let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!');
|
||||
let Some(format_snippet) = format_string_span.get_source_text(cx) else {
|
||||
return;
|
||||
};
|
||||
|
||||
if format_args.template.len() == 1 && last == sym::LF {
|
||||
// print!("\n"), write!(f, "\n")
|
||||
|
||||
diag.multipart_suggestion(
|
||||
format!("use `{name}ln!` instead"),
|
||||
vec![(name_span, format!("{name}ln")), (format_string_span, String::new())],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else if format_snippet.ends_with("\\n\"") {
|
||||
// print!("...\n"), write!(f, "...\n")
|
||||
|
||||
let hi = format_string_span.hi();
|
||||
let newline_span = format_string_span.with_lo(hi - BytePos(3)).with_hi(hi - BytePos(1));
|
||||
|
||||
diag.multipart_suggestion(
|
||||
format!("use `{name}ln!` instead"),
|
||||
vec![(name_span, format!("{name}ln")), (newline_span, String::new())],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) {
|
||||
if let [FormatArgsPiece::Literal(sym::LF)] = &format_args.template[..] {
|
||||
let mut span = format_args.span;
|
||||
|
||||
let lint = if name == "writeln" {
|
||||
span = expand_past_previous_comma(cx, span);
|
||||
|
||||
WRITELN_EMPTY_STRING
|
||||
} else {
|
||||
PRINTLN_EMPTY_STRING
|
||||
};
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
lint,
|
||||
macro_call.span,
|
||||
format!("empty string literal in `{name}!`"),
|
||||
|diag| {
|
||||
diag.span_suggestion(
|
||||
span,
|
||||
"remove the empty string",
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
|
||||
let arg_index = |argument: &FormatArgPosition| argument.index.unwrap_or_else(|pos| pos);
|
||||
|
||||
let lint_name = if name.starts_with("write") {
|
||||
WRITE_LITERAL
|
||||
} else {
|
||||
PRINT_LITERAL
|
||||
};
|
||||
|
||||
let mut counts = vec![0u32; format_args.arguments.all_args().len()];
|
||||
for piece in &format_args.template {
|
||||
if let FormatArgsPiece::Placeholder(placeholder) = piece {
|
||||
counts[arg_index(&placeholder.argument)] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let mut suggestion: Vec<(Span, String)> = vec![];
|
||||
// holds index of replaced positional arguments; used to decrement the index of the remaining
|
||||
// positional arguments.
|
||||
let mut replaced_position: Vec<usize> = vec![];
|
||||
let mut sug_span: Option<Span> = None;
|
||||
|
||||
for piece in &format_args.template {
|
||||
if let FormatArgsPiece::Placeholder(FormatPlaceholder {
|
||||
argument,
|
||||
span: Some(placeholder_span),
|
||||
format_trait: FormatTrait::Display,
|
||||
format_options,
|
||||
}) = piece
|
||||
&& *format_options == FormatOptions::default()
|
||||
&& let index = arg_index(argument)
|
||||
&& counts[index] == 1
|
||||
&& let Some(arg) = format_args.arguments.by_index(index)
|
||||
&& let rustc_ast::ExprKind::Lit(lit) = &arg.expr.kind
|
||||
&& !arg.expr.span.from_expansion()
|
||||
&& let Some(value_string) = arg.expr.span.get_source_text(cx)
|
||||
{
|
||||
let (replacement, replace_raw) = match lit.kind {
|
||||
LitKind::Str | LitKind::StrRaw(_) => match extract_str_literal(&value_string) {
|
||||
Some(extracted) => extracted,
|
||||
None => return,
|
||||
},
|
||||
LitKind::Char => (
|
||||
match lit.symbol {
|
||||
sym::DOUBLE_QUOTE => "\\\"",
|
||||
sym::BACKSLASH_SINGLE_QUOTE => "'",
|
||||
_ => match value_string.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) {
|
||||
Some(stripped) => stripped,
|
||||
None => return,
|
||||
},
|
||||
}
|
||||
.to_string(),
|
||||
false,
|
||||
),
|
||||
LitKind::Bool => (lit.symbol.to_string(), false),
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
let Some(format_string_snippet) = format_args.span.get_source_text(cx) else {
|
||||
continue;
|
||||
};
|
||||
let format_string_is_raw = format_string_snippet.starts_with('r');
|
||||
|
||||
let replacement = match (format_string_is_raw, replace_raw) {
|
||||
(false, false) => Some(replacement),
|
||||
(false, true) => Some(replacement.replace('\\', "\\\\").replace('"', "\\\"")),
|
||||
(true, false) => match conservative_unescape(&replacement) {
|
||||
Ok(unescaped) => Some(unescaped),
|
||||
Err(UnescapeErr::Lint) => None,
|
||||
Err(UnescapeErr::Ignore) => continue,
|
||||
},
|
||||
(true, true) => {
|
||||
if replacement.contains(['#', '"']) {
|
||||
None
|
||||
} else {
|
||||
Some(replacement)
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
sug_span = Some(sug_span.unwrap_or(arg.expr.span).to(arg.expr.span));
|
||||
|
||||
if let Some((_, index)) = format_arg_piece_span(piece) {
|
||||
replaced_position.push(index);
|
||||
}
|
||||
|
||||
if let Some(replacement) = replacement
|
||||
// `format!("{}", "a")`, `format!("{named}", named = "b")
|
||||
// ~~~~~ ~~~~~~~~~~~~~
|
||||
&& let Some(removal_span) = format_arg_removal_span(format_args, index)
|
||||
{
|
||||
let replacement = escape_braces(&replacement, !format_string_is_raw && !replace_raw);
|
||||
suggestion.push((*placeholder_span, replacement));
|
||||
suggestion.push((removal_span, String::new()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Decrement the index of the remaining by the number of replaced positional arguments
|
||||
if !suggestion.is_empty() {
|
||||
for piece in &format_args.template {
|
||||
relocalize_format_args_indexes(piece, &mut suggestion, &replaced_position);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(span) = sug_span {
|
||||
span_lint_and_then(cx, lint_name, span, "literal with an empty format string", |diag| {
|
||||
if !suggestion.is_empty() {
|
||||
diag.multipart_suggestion("try", suggestion, Applicability::MachineApplicable);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract Span and its index from the given `piece`
|
||||
fn format_arg_piece_span(piece: &FormatArgsPiece) -> Option<(Span, usize)> {
|
||||
match piece {
|
||||
FormatArgsPiece::Placeholder(FormatPlaceholder {
|
||||
argument: FormatArgPosition { index: Ok(index), .. },
|
||||
span: Some(span),
|
||||
..
|
||||
}) => Some((*span, *index)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Relocalizes the indexes of positional arguments in the format string
|
||||
fn relocalize_format_args_indexes(
|
||||
piece: &FormatArgsPiece,
|
||||
suggestion: &mut Vec<(Span, String)>,
|
||||
replaced_position: &[usize],
|
||||
) {
|
||||
if let FormatArgsPiece::Placeholder(FormatPlaceholder {
|
||||
argument:
|
||||
FormatArgPosition {
|
||||
index: Ok(index),
|
||||
// Only consider positional arguments
|
||||
kind: FormatArgPositionKind::Number,
|
||||
span: Some(span),
|
||||
},
|
||||
format_options,
|
||||
..
|
||||
}) = piece
|
||||
{
|
||||
if suggestion.iter().any(|(s, _)| s.overlaps(*span)) {
|
||||
// If the span is already in the suggestion, we don't need to process it again
|
||||
return;
|
||||
}
|
||||
|
||||
// lambda to get the decremented index based on the replaced positions
|
||||
let decremented_index = |index: usize| -> usize {
|
||||
let decrement = replaced_position.iter().filter(|&&i| i < index).count();
|
||||
index - decrement
|
||||
};
|
||||
|
||||
suggestion.push((*span, decremented_index(*index).to_string()));
|
||||
|
||||
// If there are format options, we need to handle them as well
|
||||
if *format_options != FormatOptions::default() {
|
||||
// lambda to process width and precision format counts and add them to the suggestion
|
||||
let mut process_format_count = |count: &Option<FormatCount>, formatter: &dyn Fn(usize) -> String| {
|
||||
if let Some(FormatCount::Argument(FormatArgPosition {
|
||||
index: Ok(format_arg_index),
|
||||
kind: FormatArgPositionKind::Number,
|
||||
span: Some(format_arg_span),
|
||||
})) = count
|
||||
{
|
||||
suggestion.push((*format_arg_span, formatter(decremented_index(*format_arg_index))));
|
||||
}
|
||||
};
|
||||
|
||||
process_format_count(&format_options.width, &|index: usize| format!("{index}$"));
|
||||
process_format_count(&format_options.precision, &|index: usize| format!(".{index}$"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes the raw marker, `#`s and quotes from a str, and returns if the literal is raw
|
||||
///
|
||||
/// `r#"a"#` -> (`a`, true)
|
||||
///
|
||||
/// `"b"` -> (`b`, false)
|
||||
fn extract_str_literal(literal: &str) -> Option<(String, bool)> {
|
||||
let (literal, raw) = match literal.strip_prefix('r') {
|
||||
Some(stripped) => (stripped.trim_matches('#'), true),
|
||||
None => (literal, false),
|
||||
};
|
||||
|
||||
Some((literal.strip_prefix('"')?.strip_suffix('"')?.to_string(), raw))
|
||||
}
|
||||
|
||||
enum UnescapeErr {
|
||||
/// Should still be linted, can be manually resolved by author, e.g.
|
||||
///
|
||||
/// ```ignore
|
||||
/// print!(r"{}", '"');
|
||||
/// ```
|
||||
Lint,
|
||||
/// Should not be linted, e.g.
|
||||
///
|
||||
/// ```ignore
|
||||
/// print!(r"{}", '\r');
|
||||
/// ```
|
||||
Ignore,
|
||||
}
|
||||
|
||||
/// Unescape a normal string into a raw string
|
||||
fn conservative_unescape(literal: &str) -> Result<String, UnescapeErr> {
|
||||
let mut unescaped = String::with_capacity(literal.len());
|
||||
let mut chars = literal.chars();
|
||||
let mut err = false;
|
||||
|
||||
while let Some(ch) = chars.next() {
|
||||
match ch {
|
||||
'#' => err = true,
|
||||
'\\' => match chars.next() {
|
||||
Some('\\') => unescaped.push('\\'),
|
||||
Some('"') => err = true,
|
||||
_ => return Err(UnescapeErr::Ignore),
|
||||
},
|
||||
_ => unescaped.push(ch),
|
||||
}
|
||||
}
|
||||
|
||||
if err { Err(UnescapeErr::Lint) } else { Ok(unescaped) }
|
||||
}
|
||||
|
||||
/// Replaces `{` with `{{` and `}` with `}}`. If `preserve_unicode_escapes` is `true` the braces in
|
||||
/// `\u{xxxx}` are left unmodified
|
||||
#[expect(clippy::match_same_arms)]
|
||||
fn escape_braces(literal: &str, preserve_unicode_escapes: bool) -> String {
|
||||
#[derive(Clone, Copy)]
|
||||
enum State {
|
||||
Normal,
|
||||
Backslash,
|
||||
UnicodeEscape,
|
||||
}
|
||||
|
||||
let mut escaped = String::with_capacity(literal.len());
|
||||
let mut state = State::Normal;
|
||||
|
||||
for ch in literal.chars() {
|
||||
state = match (ch, state) {
|
||||
// Escape braces outside of unicode escapes by doubling them up
|
||||
('{' | '}', State::Normal) => {
|
||||
escaped.push(ch);
|
||||
State::Normal
|
||||
},
|
||||
// If `preserve_unicode_escapes` isn't enabled stay in `State::Normal`, otherwise:
|
||||
//
|
||||
// \u{aaaa} \\ \x01
|
||||
// ^ ^ ^
|
||||
('\\', State::Normal) if preserve_unicode_escapes => State::Backslash,
|
||||
// \u{aaaa}
|
||||
// ^
|
||||
('u', State::Backslash) => State::UnicodeEscape,
|
||||
// \xAA \\
|
||||
// ^ ^
|
||||
(_, State::Backslash) => State::Normal,
|
||||
// \u{aaaa}
|
||||
// ^
|
||||
('}', State::UnicodeEscape) => State::Normal,
|
||||
_ => state,
|
||||
};
|
||||
|
||||
escaped.push(ch);
|
||||
}
|
||||
|
||||
escaped
|
||||
}
|
||||
38
clippy_lints/src/write/empty_string.rs
Normal file
38
clippy_lints/src/write/empty_string.rs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::macros::MacroCall;
|
||||
use clippy_utils::source::expand_past_previous_comma;
|
||||
use clippy_utils::sym;
|
||||
use rustc_ast::{FormatArgs, FormatArgsPiece};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
use super::{PRINTLN_EMPTY_STRING, WRITELN_EMPTY_STRING};
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) {
|
||||
if let [FormatArgsPiece::Literal(sym::LF)] = &format_args.template[..] {
|
||||
let mut span = format_args.span;
|
||||
|
||||
let lint = if name == "writeln" {
|
||||
span = expand_past_previous_comma(cx, span);
|
||||
|
||||
WRITELN_EMPTY_STRING
|
||||
} else {
|
||||
PRINTLN_EMPTY_STRING
|
||||
};
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
lint,
|
||||
macro_call.span,
|
||||
format!("empty string literal in `{name}!`"),
|
||||
|diag| {
|
||||
diag.span_suggestion(
|
||||
span,
|
||||
"remove the empty string",
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
285
clippy_lints/src/write/literal.rs
Normal file
285
clippy_lints/src/write/literal.rs
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::macros::format_arg_removal_span;
|
||||
use clippy_utils::source::SpanRangeExt;
|
||||
use clippy_utils::sym;
|
||||
use rustc_ast::token::LitKind;
|
||||
use rustc_ast::{
|
||||
FormatArgPosition, FormatArgPositionKind, FormatArgs, FormatArgsPiece, FormatCount, FormatOptions,
|
||||
FormatPlaceholder, FormatTrait,
|
||||
};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::Span;
|
||||
|
||||
use super::{PRINT_LITERAL, WRITE_LITERAL};
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
|
||||
let arg_index = |argument: &FormatArgPosition| argument.index.unwrap_or_else(|pos| pos);
|
||||
|
||||
let lint_name = if name.starts_with("write") {
|
||||
WRITE_LITERAL
|
||||
} else {
|
||||
PRINT_LITERAL
|
||||
};
|
||||
|
||||
let mut counts = vec![0u32; format_args.arguments.all_args().len()];
|
||||
for piece in &format_args.template {
|
||||
if let FormatArgsPiece::Placeholder(placeholder) = piece {
|
||||
counts[arg_index(&placeholder.argument)] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let mut suggestion: Vec<(Span, String)> = vec![];
|
||||
// holds index of replaced positional arguments; used to decrement the index of the remaining
|
||||
// positional arguments.
|
||||
let mut replaced_position: Vec<usize> = vec![];
|
||||
let mut sug_span: Option<Span> = None;
|
||||
|
||||
for piece in &format_args.template {
|
||||
if let FormatArgsPiece::Placeholder(FormatPlaceholder {
|
||||
argument,
|
||||
span: Some(placeholder_span),
|
||||
format_trait: FormatTrait::Display,
|
||||
format_options,
|
||||
}) = piece
|
||||
&& *format_options == FormatOptions::default()
|
||||
&& let index = arg_index(argument)
|
||||
&& counts[index] == 1
|
||||
&& let Some(arg) = format_args.arguments.by_index(index)
|
||||
&& let rustc_ast::ExprKind::Lit(lit) = &arg.expr.kind
|
||||
&& !arg.expr.span.from_expansion()
|
||||
&& let Some(value_string) = arg.expr.span.get_source_text(cx)
|
||||
{
|
||||
let (replacement, replace_raw) = match lit.kind {
|
||||
LitKind::Str | LitKind::StrRaw(_) => match extract_str_literal(&value_string) {
|
||||
Some(extracted) => extracted,
|
||||
None => return,
|
||||
},
|
||||
LitKind::Char => (
|
||||
match lit.symbol {
|
||||
sym::DOUBLE_QUOTE => "\\\"",
|
||||
sym::BACKSLASH_SINGLE_QUOTE => "'",
|
||||
_ => match value_string.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) {
|
||||
Some(stripped) => stripped,
|
||||
None => return,
|
||||
},
|
||||
}
|
||||
.to_string(),
|
||||
false,
|
||||
),
|
||||
LitKind::Bool => (lit.symbol.to_string(), false),
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
let Some(format_string_snippet) = format_args.span.get_source_text(cx) else {
|
||||
continue;
|
||||
};
|
||||
let format_string_is_raw = format_string_snippet.starts_with('r');
|
||||
|
||||
let replacement = match (format_string_is_raw, replace_raw) {
|
||||
(false, false) => Some(replacement),
|
||||
(false, true) => Some(replacement.replace('\\', "\\\\").replace('"', "\\\"")),
|
||||
(true, false) => match conservative_unescape(&replacement) {
|
||||
Ok(unescaped) => Some(unescaped),
|
||||
Err(UnescapeErr::Lint) => None,
|
||||
Err(UnescapeErr::Ignore) => continue,
|
||||
},
|
||||
(true, true) => {
|
||||
if replacement.contains(['#', '"']) {
|
||||
None
|
||||
} else {
|
||||
Some(replacement)
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
sug_span = Some(sug_span.unwrap_or(arg.expr.span).to(arg.expr.span));
|
||||
|
||||
if let Some((_, index)) = format_arg_piece_span(piece) {
|
||||
replaced_position.push(index);
|
||||
}
|
||||
|
||||
if let Some(replacement) = replacement
|
||||
// `format!("{}", "a")`, `format!("{named}", named = "b")
|
||||
// ~~~~~ ~~~~~~~~~~~~~
|
||||
&& let Some(removal_span) = format_arg_removal_span(format_args, index)
|
||||
{
|
||||
let replacement = escape_braces(&replacement, !format_string_is_raw && !replace_raw);
|
||||
suggestion.push((*placeholder_span, replacement));
|
||||
suggestion.push((removal_span, String::new()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Decrement the index of the remaining by the number of replaced positional arguments
|
||||
if !suggestion.is_empty() {
|
||||
for piece in &format_args.template {
|
||||
relocalize_format_args_indexes(piece, &mut suggestion, &replaced_position);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(span) = sug_span {
|
||||
span_lint_and_then(cx, lint_name, span, "literal with an empty format string", |diag| {
|
||||
if !suggestion.is_empty() {
|
||||
diag.multipart_suggestion("try", suggestion, Applicability::MachineApplicable);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract Span and its index from the given `piece`
|
||||
fn format_arg_piece_span(piece: &FormatArgsPiece) -> Option<(Span, usize)> {
|
||||
match piece {
|
||||
FormatArgsPiece::Placeholder(FormatPlaceholder {
|
||||
argument: FormatArgPosition { index: Ok(index), .. },
|
||||
span: Some(span),
|
||||
..
|
||||
}) => Some((*span, *index)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Relocalizes the indexes of positional arguments in the format string
|
||||
fn relocalize_format_args_indexes(
|
||||
piece: &FormatArgsPiece,
|
||||
suggestion: &mut Vec<(Span, String)>,
|
||||
replaced_position: &[usize],
|
||||
) {
|
||||
if let FormatArgsPiece::Placeholder(FormatPlaceholder {
|
||||
argument:
|
||||
FormatArgPosition {
|
||||
index: Ok(index),
|
||||
// Only consider positional arguments
|
||||
kind: FormatArgPositionKind::Number,
|
||||
span: Some(span),
|
||||
},
|
||||
format_options,
|
||||
..
|
||||
}) = piece
|
||||
{
|
||||
if suggestion.iter().any(|(s, _)| s.overlaps(*span)) {
|
||||
// If the span is already in the suggestion, we don't need to process it again
|
||||
return;
|
||||
}
|
||||
|
||||
// lambda to get the decremented index based on the replaced positions
|
||||
let decremented_index = |index: usize| -> usize {
|
||||
let decrement = replaced_position.iter().filter(|&&i| i < index).count();
|
||||
index - decrement
|
||||
};
|
||||
|
||||
suggestion.push((*span, decremented_index(*index).to_string()));
|
||||
|
||||
// If there are format options, we need to handle them as well
|
||||
if *format_options != FormatOptions::default() {
|
||||
// lambda to process width and precision format counts and add them to the suggestion
|
||||
let mut process_format_count = |count: &Option<FormatCount>, formatter: &dyn Fn(usize) -> String| {
|
||||
if let Some(FormatCount::Argument(FormatArgPosition {
|
||||
index: Ok(format_arg_index),
|
||||
kind: FormatArgPositionKind::Number,
|
||||
span: Some(format_arg_span),
|
||||
})) = count
|
||||
{
|
||||
suggestion.push((*format_arg_span, formatter(decremented_index(*format_arg_index))));
|
||||
}
|
||||
};
|
||||
|
||||
process_format_count(&format_options.width, &|index: usize| format!("{index}$"));
|
||||
process_format_count(&format_options.precision, &|index: usize| format!(".{index}$"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes the raw marker, `#`s and quotes from a str, and returns if the literal is raw
|
||||
///
|
||||
/// `r#"a"#` -> (`a`, true)
|
||||
///
|
||||
/// `"b"` -> (`b`, false)
|
||||
fn extract_str_literal(literal: &str) -> Option<(String, bool)> {
|
||||
let (literal, raw) = match literal.strip_prefix('r') {
|
||||
Some(stripped) => (stripped.trim_matches('#'), true),
|
||||
None => (literal, false),
|
||||
};
|
||||
|
||||
Some((literal.strip_prefix('"')?.strip_suffix('"')?.to_string(), raw))
|
||||
}
|
||||
|
||||
enum UnescapeErr {
|
||||
/// Should still be linted, can be manually resolved by author, e.g.
|
||||
///
|
||||
/// ```ignore
|
||||
/// print!(r"{}", '"');
|
||||
/// ```
|
||||
Lint,
|
||||
/// Should not be linted, e.g.
|
||||
///
|
||||
/// ```ignore
|
||||
/// print!(r"{}", '\r');
|
||||
/// ```
|
||||
Ignore,
|
||||
}
|
||||
|
||||
/// Unescape a normal string into a raw string
|
||||
fn conservative_unescape(literal: &str) -> Result<String, UnescapeErr> {
|
||||
let mut unescaped = String::with_capacity(literal.len());
|
||||
let mut chars = literal.chars();
|
||||
let mut err = false;
|
||||
|
||||
while let Some(ch) = chars.next() {
|
||||
match ch {
|
||||
'#' => err = true,
|
||||
'\\' => match chars.next() {
|
||||
Some('\\') => unescaped.push('\\'),
|
||||
Some('"') => err = true,
|
||||
_ => return Err(UnescapeErr::Ignore),
|
||||
},
|
||||
_ => unescaped.push(ch),
|
||||
}
|
||||
}
|
||||
|
||||
if err { Err(UnescapeErr::Lint) } else { Ok(unescaped) }
|
||||
}
|
||||
|
||||
/// Replaces `{` with `{{` and `}` with `}}`. If `preserve_unicode_escapes` is `true` the braces
|
||||
/// in `\u{xxxx}` are left unmodified
|
||||
#[expect(clippy::match_same_arms)]
|
||||
fn escape_braces(literal: &str, preserve_unicode_escapes: bool) -> String {
|
||||
#[derive(Clone, Copy)]
|
||||
enum State {
|
||||
Normal,
|
||||
Backslash,
|
||||
UnicodeEscape,
|
||||
}
|
||||
|
||||
let mut escaped = String::with_capacity(literal.len());
|
||||
let mut state = State::Normal;
|
||||
|
||||
for ch in literal.chars() {
|
||||
state = match (ch, state) {
|
||||
// Escape braces outside of unicode escapes by doubling them up
|
||||
('{' | '}', State::Normal) => {
|
||||
escaped.push(ch);
|
||||
State::Normal
|
||||
},
|
||||
// If `preserve_unicode_escapes` isn't enabled stay in `State::Normal`, otherwise:
|
||||
//
|
||||
// \u{aaaa} \\ \x01
|
||||
// ^ ^ ^
|
||||
('\\', State::Normal) if preserve_unicode_escapes => State::Backslash,
|
||||
// \u{aaaa}
|
||||
// ^
|
||||
('u', State::Backslash) => State::UnicodeEscape,
|
||||
// \xAA \\
|
||||
// ^ ^
|
||||
(_, State::Backslash) => State::Normal,
|
||||
// \u{aaaa}
|
||||
// ^
|
||||
('}', State::UnicodeEscape) => State::Normal,
|
||||
_ => state,
|
||||
};
|
||||
|
||||
escaped.push(ch);
|
||||
}
|
||||
|
||||
escaped
|
||||
}
|
||||
354
clippy_lints/src/write/mod.rs
Normal file
354
clippy_lints/src/write/mod.rs
Normal file
|
|
@ -0,0 +1,354 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::macros::{FormatArgsStorage, root_macro_call_first_node};
|
||||
use clippy_utils::{is_in_test, sym};
|
||||
use rustc_hir::{Expr, Impl, Item, ItemKind, OwnerId};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
||||
mod empty_string;
|
||||
mod literal;
|
||||
mod use_debug;
|
||||
mod with_newline;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// This lint warns when you use `println!("")` to
|
||||
/// print a newline.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// You should use `println!()`, which is simpler.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// println!("");
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// println!();
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub PRINTLN_EMPTY_STRING,
|
||||
style,
|
||||
"using `println!(\"\")` with an empty string"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// This lint warns when you use `print!()` with a format
|
||||
/// string that ends in a newline.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// You should use `println!()` instead, which appends the
|
||||
/// newline.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # let name = "World";
|
||||
/// print!("Hello {}!\n", name);
|
||||
/// ```
|
||||
/// use println!() instead
|
||||
/// ```no_run
|
||||
/// # let name = "World";
|
||||
/// println!("Hello {}!", name);
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub PRINT_WITH_NEWLINE,
|
||||
style,
|
||||
"using `print!()` with a format string that ends in a single newline"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for printing on *stdout*. The purpose of this lint
|
||||
/// is to catch debugging remnants.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// People often print on *stdout* while debugging an
|
||||
/// application and might forget to remove those prints afterward.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// Only catches `print!` and `println!` calls.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// println!("Hello world!");
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub PRINT_STDOUT,
|
||||
restriction,
|
||||
"printing on stdout"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for printing on *stderr*. The purpose of this lint
|
||||
/// is to catch debugging remnants.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// People often print on *stderr* while debugging an
|
||||
/// application and might forget to remove those prints afterward.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// Only catches `eprint!` and `eprintln!` calls.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// eprintln!("Hello world!");
|
||||
/// ```
|
||||
#[clippy::version = "1.50.0"]
|
||||
pub PRINT_STDERR,
|
||||
restriction,
|
||||
"printing on stderr"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of `Debug` formatting. The purpose of this
|
||||
/// lint is to catch debugging remnants.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// The purpose of the `Debug` trait is to facilitate debugging Rust code,
|
||||
/// and [no guarantees are made about its output][stability].
|
||||
/// It should not be used in user-facing output.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # let foo = "bar";
|
||||
/// println!("{:?}", foo);
|
||||
/// ```
|
||||
///
|
||||
/// [stability]: https://doc.rust-lang.org/stable/std/fmt/trait.Debug.html#stability
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub USE_DEBUG,
|
||||
restriction,
|
||||
"use of `Debug`-based formatting"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// This lint warns about the use of literals as `print!`/`println!` args.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Using literals as `println!` args is inefficient
|
||||
/// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
|
||||
/// (i.e., just put the literal in the format string)
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// println!("{}", "foo");
|
||||
/// ```
|
||||
/// use the literal without formatting:
|
||||
/// ```no_run
|
||||
/// println!("foo");
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub PRINT_LITERAL,
|
||||
style,
|
||||
"printing a literal with a format string"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// This lint warns when you use `writeln!(buf, "")` to
|
||||
/// print a newline.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// You should use `writeln!(buf)`, which is simpler.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # use std::fmt::Write;
|
||||
/// # let mut buf = String::new();
|
||||
/// writeln!(buf, "");
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # use std::fmt::Write;
|
||||
/// # let mut buf = String::new();
|
||||
/// writeln!(buf);
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub WRITELN_EMPTY_STRING,
|
||||
style,
|
||||
"using `writeln!(buf, \"\")` with an empty string"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// This lint warns when you use `write!()` with a format
|
||||
/// string that
|
||||
/// ends in a newline.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// You should use `writeln!()` instead, which appends the
|
||||
/// newline.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # use std::fmt::Write;
|
||||
/// # let mut buf = String::new();
|
||||
/// # let name = "World";
|
||||
/// write!(buf, "Hello {}!\n", name);
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # use std::fmt::Write;
|
||||
/// # let mut buf = String::new();
|
||||
/// # let name = "World";
|
||||
/// writeln!(buf, "Hello {}!", name);
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub WRITE_WITH_NEWLINE,
|
||||
style,
|
||||
"using `write!()` with a format string that ends in a single newline"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// This lint warns about the use of literals as `write!`/`writeln!` args.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Using literals as `writeln!` args is inefficient
|
||||
/// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
|
||||
/// (i.e., just put the literal in the format string)
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # use std::fmt::Write;
|
||||
/// # let mut buf = String::new();
|
||||
/// writeln!(buf, "{}", "foo");
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # use std::fmt::Write;
|
||||
/// # let mut buf = String::new();
|
||||
/// writeln!(buf, "foo");
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub WRITE_LITERAL,
|
||||
style,
|
||||
"writing a literal with a format string"
|
||||
}
|
||||
|
||||
pub struct Write {
|
||||
format_args: FormatArgsStorage,
|
||||
// The outermost `impl Debug` we're currently in. While we're in one, `USE_DEBUG` is deactivated
|
||||
outermost_debug_impl: Option<OwnerId>,
|
||||
allow_print_in_tests: bool,
|
||||
}
|
||||
|
||||
impl Write {
|
||||
pub fn new(conf: &'static Conf, format_args: FormatArgsStorage) -> Self {
|
||||
Self {
|
||||
format_args,
|
||||
outermost_debug_impl: None,
|
||||
allow_print_in_tests: conf.allow_print_in_tests,
|
||||
}
|
||||
}
|
||||
|
||||
fn in_debug_impl(&self) -> bool {
|
||||
self.outermost_debug_impl.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(Write => [
|
||||
PRINT_WITH_NEWLINE,
|
||||
PRINTLN_EMPTY_STRING,
|
||||
PRINT_STDOUT,
|
||||
PRINT_STDERR,
|
||||
USE_DEBUG,
|
||||
PRINT_LITERAL,
|
||||
WRITE_WITH_NEWLINE,
|
||||
WRITELN_EMPTY_STRING,
|
||||
WRITE_LITERAL,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Write {
|
||||
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
|
||||
// Only check for `impl Debug`s if we're not already in one
|
||||
if self.outermost_debug_impl.is_none() && is_debug_impl(cx, item) {
|
||||
self.outermost_debug_impl = Some(item.owner_id);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_item_post(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) {
|
||||
// Only clear `self.outermost_debug_impl` if we're escaping the _outermost_ debug impl
|
||||
if self.outermost_debug_impl == Some(item.owner_id) {
|
||||
self.outermost_debug_impl = None;
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
let Some(macro_call) = root_macro_call_first_node(cx, expr) else {
|
||||
return;
|
||||
};
|
||||
let Some(diag_name) = cx.tcx.get_diagnostic_name(macro_call.def_id) else {
|
||||
return;
|
||||
};
|
||||
let Some(name) = diag_name.as_str().strip_suffix("_macro") else {
|
||||
return;
|
||||
};
|
||||
|
||||
let is_build_script = cx
|
||||
.sess()
|
||||
.opts
|
||||
.crate_name
|
||||
.as_ref()
|
||||
.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 {
|
||||
sym::print_macro | sym::println_macro if !allowed_in_tests => {
|
||||
if !is_build_script {
|
||||
span_lint(cx, PRINT_STDOUT, macro_call.span, format!("use of `{name}!`"));
|
||||
}
|
||||
},
|
||||
sym::eprint_macro | sym::eprintln_macro if !allowed_in_tests => {
|
||||
span_lint(cx, PRINT_STDERR, macro_call.span, format!("use of `{name}!`"));
|
||||
},
|
||||
sym::write_macro | sym::writeln_macro => {},
|
||||
_ => return,
|
||||
}
|
||||
|
||||
if let Some(format_args) = self.format_args.get(cx, expr, macro_call.expn) {
|
||||
// ignore `writeln!(w)` and `write!(v, some_macro!())`
|
||||
if format_args.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
match diag_name {
|
||||
sym::print_macro | sym::eprint_macro | sym::write_macro => {
|
||||
with_newline::check(cx, format_args, ¯o_call, name);
|
||||
},
|
||||
sym::println_macro | sym::eprintln_macro | sym::writeln_macro => {
|
||||
empty_string::check(cx, format_args, ¯o_call, name);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
literal::check(cx, format_args, name);
|
||||
|
||||
if !self.in_debug_impl() {
|
||||
use_debug::check(cx, format_args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
|
||||
if let ItemKind::Impl(Impl {
|
||||
of_trait: Some(of_trait),
|
||||
..
|
||||
}) = &item.kind
|
||||
&& let Some(trait_id) = of_trait.trait_ref.trait_def_id()
|
||||
{
|
||||
cx.tcx.is_diagnostic_item(sym::Debug, trait_id)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
18
clippy_lints/src/write/use_debug.rs
Normal file
18
clippy_lints/src/write/use_debug.rs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use rustc_ast::{FormatArgs, FormatArgsPiece, FormatPlaceholder, FormatTrait};
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
use super::USE_DEBUG;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, format_args: &FormatArgs) {
|
||||
for piece in &format_args.template {
|
||||
if let &FormatArgsPiece::Placeholder(FormatPlaceholder {
|
||||
span: Some(span),
|
||||
format_trait: FormatTrait::Debug,
|
||||
..
|
||||
}) = piece
|
||||
{
|
||||
span_lint(cx, USE_DEBUG, span, "use of `Debug`-based formatting");
|
||||
}
|
||||
}
|
||||
}
|
||||
78
clippy_lints/src/write/with_newline.rs
Normal file
78
clippy_lints/src/write/with_newline.rs
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::macros::MacroCall;
|
||||
use clippy_utils::source::{SpanRangeExt, expand_past_previous_comma};
|
||||
use clippy_utils::sym;
|
||||
use rustc_ast::{FormatArgs, FormatArgsPiece};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_span::BytePos;
|
||||
|
||||
use super::{PRINT_WITH_NEWLINE, WRITE_WITH_NEWLINE};
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) {
|
||||
let Some(&FormatArgsPiece::Literal(last)) = format_args.template.last() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let count_vertical_whitespace = || {
|
||||
format_args
|
||||
.template
|
||||
.iter()
|
||||
.filter_map(|piece| match piece {
|
||||
FormatArgsPiece::Literal(literal) => Some(literal),
|
||||
FormatArgsPiece::Placeholder(_) => None,
|
||||
})
|
||||
.flat_map(|literal| literal.as_str().chars())
|
||||
.filter(|ch| matches!(ch, '\r' | '\n'))
|
||||
.count()
|
||||
};
|
||||
|
||||
if last.as_str().ends_with('\n')
|
||||
// ignore format strings with other internal vertical whitespace
|
||||
&& count_vertical_whitespace() == 1
|
||||
{
|
||||
let mut format_string_span = format_args.span;
|
||||
|
||||
let lint = if name == "write" {
|
||||
format_string_span = expand_past_previous_comma(cx, format_string_span);
|
||||
|
||||
WRITE_WITH_NEWLINE
|
||||
} else {
|
||||
PRINT_WITH_NEWLINE
|
||||
};
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
lint,
|
||||
macro_call.span,
|
||||
format!("using `{name}!()` with a format string that ends in a single newline"),
|
||||
|diag| {
|
||||
let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!');
|
||||
let Some(format_snippet) = format_string_span.get_source_text(cx) else {
|
||||
return;
|
||||
};
|
||||
|
||||
if format_args.template.len() == 1 && last == sym::LF {
|
||||
// print!("\n"), write!(f, "\n")
|
||||
|
||||
diag.multipart_suggestion(
|
||||
format!("use `{name}ln!` instead"),
|
||||
vec![(name_span, format!("{name}ln")), (format_string_span, String::new())],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else if format_snippet.ends_with("\\n\"") {
|
||||
// print!("...\n"), write!(f, "...\n")
|
||||
|
||||
let hi = format_string_span.hi();
|
||||
let newline_span = format_string_span.with_lo(hi - BytePos(3)).with_hi(hi - BytePos(1));
|
||||
|
||||
diag.multipart_suggestion(
|
||||
format!("use `{name}ln!` instead"),
|
||||
vec![(name_span, format!("{name}ln")), (newline_span, String::new())],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain:
|
|||
|
||||
<!-- begin autogenerated nightly -->
|
||||
```
|
||||
nightly-2025-10-31
|
||||
nightly-2025-11-14
|
||||
```
|
||||
<!-- end autogenerated nightly -->
|
||||
|
||||
|
|
|
|||
|
|
@ -371,7 +371,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
|
|||
&& eq_id(*li, *ri)
|
||||
&& eq_generics(lg, rg)
|
||||
&& eq_ty(lt, rt)
|
||||
&& both(lb.as_ref(), rb.as_ref(), |l, r| eq_const_item_rhs(l, r))
|
||||
&& both(lb.as_ref(), rb.as_ref(), eq_const_item_rhs)
|
||||
},
|
||||
(
|
||||
Fn(box ast::Fn {
|
||||
|
|
@ -625,7 +625,7 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool {
|
|||
&& eq_id(*li, *ri)
|
||||
&& eq_generics(lg, rg)
|
||||
&& eq_ty(lt, rt)
|
||||
&& both(lb.as_ref(), rb.as_ref(), |l, r| eq_const_item_rhs(l, r))
|
||||
&& both(lb.as_ref(), rb.as_ref(), eq_const_item_rhs)
|
||||
},
|
||||
(
|
||||
Fn(box ast::Fn {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
//! Utility functions for attributes, including Clippy's built-in ones
|
||||
|
||||
use crate::source::SpanRangeExt;
|
||||
use crate::{sym, tokenize_with_text};
|
||||
use rustc_ast::attr;
|
||||
|
|
@ -12,131 +14,59 @@ use rustc_session::Session;
|
|||
use rustc_span::{Span, Symbol};
|
||||
use std::str::FromStr;
|
||||
|
||||
/// Deprecation status of attributes known by Clippy.
|
||||
pub enum DeprecationStatus {
|
||||
/// Attribute is deprecated
|
||||
Deprecated,
|
||||
/// Attribute is deprecated and was replaced by the named attribute
|
||||
Replaced(&'static str),
|
||||
None,
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub const BUILTIN_ATTRIBUTES: &[(Symbol, DeprecationStatus)] = &[
|
||||
(sym::author, DeprecationStatus::None),
|
||||
(sym::version, DeprecationStatus::None),
|
||||
(sym::cognitive_complexity, DeprecationStatus::None),
|
||||
(sym::cyclomatic_complexity, DeprecationStatus::Replaced("cognitive_complexity")),
|
||||
(sym::dump, DeprecationStatus::None),
|
||||
(sym::msrv, DeprecationStatus::None),
|
||||
// The following attributes are for the 3rd party crate authors.
|
||||
// See book/src/attribs.md
|
||||
(sym::has_significant_drop, DeprecationStatus::None),
|
||||
(sym::format_args, DeprecationStatus::None),
|
||||
];
|
||||
|
||||
pub struct LimitStack {
|
||||
stack: Vec<u64>,
|
||||
}
|
||||
|
||||
impl Drop for LimitStack {
|
||||
fn drop(&mut self) {
|
||||
assert_eq!(self.stack.len(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
impl LimitStack {
|
||||
#[must_use]
|
||||
pub fn new(limit: u64) -> Self {
|
||||
Self { stack: vec![limit] }
|
||||
}
|
||||
pub fn limit(&self) -> u64 {
|
||||
*self.stack.last().expect("there should always be a value in the stack")
|
||||
}
|
||||
pub fn push_attrs(&mut self, sess: &Session, attrs: &[impl AttributeExt], name: Symbol) {
|
||||
let stack = &mut self.stack;
|
||||
parse_attrs(sess, attrs, name, |val| stack.push(val));
|
||||
}
|
||||
pub fn pop_attrs(&mut self, sess: &Session, attrs: &[impl AttributeExt], name: Symbol) {
|
||||
let stack = &mut self.stack;
|
||||
parse_attrs(sess, attrs, name, |val| assert_eq!(stack.pop(), Some(val)));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_attr<'a, A: AttributeExt + 'a>(
|
||||
/// Given `attrs`, extract all the instances of a built-in Clippy attribute called `name`
|
||||
pub fn get_builtin_attr<'a, A: AttributeExt + 'a>(
|
||||
sess: &'a Session,
|
||||
attrs: &'a [A],
|
||||
name: Symbol,
|
||||
) -> impl Iterator<Item = &'a A> {
|
||||
attrs.iter().filter(move |attr| {
|
||||
let Some(attr_segments) = attr.ident_path() else {
|
||||
return false;
|
||||
};
|
||||
if let Some([clippy, segment2]) = attr.ident_path().as_deref()
|
||||
&& clippy.name == sym::clippy
|
||||
{
|
||||
let new_name = match segment2.name {
|
||||
sym::cyclomatic_complexity => Some("cognitive_complexity"),
|
||||
sym::author
|
||||
| sym::version
|
||||
| sym::cognitive_complexity
|
||||
| sym::dump
|
||||
| sym::msrv
|
||||
// The following attributes are for the 3rd party crate authors.
|
||||
// See book/src/attribs.md
|
||||
| sym::has_significant_drop
|
||||
| sym::format_args => None,
|
||||
_ => {
|
||||
sess.dcx().span_err(segment2.span, "usage of unknown attribute");
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
||||
if attr_segments.len() == 2 && attr_segments[0].name == sym::clippy {
|
||||
BUILTIN_ATTRIBUTES
|
||||
.iter()
|
||||
.find_map(|(builtin_name, deprecation_status)| {
|
||||
if attr_segments[1].name == *builtin_name {
|
||||
Some(deprecation_status)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.map_or_else(
|
||||
|| {
|
||||
sess.dcx().span_err(attr_segments[1].span, "usage of unknown attribute");
|
||||
false
|
||||
},
|
||||
|deprecation_status| {
|
||||
let mut diag = sess
|
||||
.dcx()
|
||||
.struct_span_err(attr_segments[1].span, "usage of deprecated attribute");
|
||||
match *deprecation_status {
|
||||
DeprecationStatus::Deprecated => {
|
||||
diag.emit();
|
||||
false
|
||||
},
|
||||
DeprecationStatus::Replaced(new_name) => {
|
||||
diag.span_suggestion(
|
||||
attr_segments[1].span,
|
||||
"consider using",
|
||||
new_name,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
diag.emit();
|
||||
false
|
||||
},
|
||||
DeprecationStatus::None => {
|
||||
diag.cancel();
|
||||
attr_segments[1].name == name
|
||||
},
|
||||
}
|
||||
},
|
||||
)
|
||||
match new_name {
|
||||
Some(new_name) => {
|
||||
sess.dcx()
|
||||
.struct_span_err(segment2.span, "usage of deprecated attribute")
|
||||
.with_span_suggestion(
|
||||
segment2.span,
|
||||
"consider using",
|
||||
new_name,
|
||||
Applicability::MachineApplicable,
|
||||
)
|
||||
.emit();
|
||||
false
|
||||
},
|
||||
None => segment2.name == name,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[impl AttributeExt], name: Symbol, mut f: F) {
|
||||
for attr in get_attr(sess, attrs, name) {
|
||||
if let Some(value) = attr.value_str() {
|
||||
if let Ok(value) = FromStr::from_str(value.as_str()) {
|
||||
f(value);
|
||||
} else {
|
||||
sess.dcx().span_err(attr.span(), "not a number");
|
||||
}
|
||||
} else {
|
||||
sess.dcx().span_err(attr.span(), "bad clippy attribute");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_unique_attr<'a, A: AttributeExt>(sess: &'a Session, attrs: &'a [A], name: Symbol) -> Option<&'a A> {
|
||||
/// If `attrs` contain exactly one instance of a built-in Clippy attribute called `name`,
|
||||
/// returns that attribute, and `None` otherwise
|
||||
pub fn get_unique_builtin_attr<'a, A: AttributeExt>(sess: &'a Session, attrs: &'a [A], name: Symbol) -> Option<&'a A> {
|
||||
let mut unique_attr: Option<&A> = None;
|
||||
for attr in get_attr(sess, attrs, name) {
|
||||
for attr in get_builtin_attr(sess, attrs, name) {
|
||||
if let Some(duplicate) = unique_attr {
|
||||
sess.dcx()
|
||||
.struct_span_err(attr.span(), format!("`{name}` is defined multiple times"))
|
||||
|
|
@ -149,13 +79,13 @@ pub fn get_unique_attr<'a, A: AttributeExt>(sess: &'a Session, attrs: &'a [A], n
|
|||
unique_attr
|
||||
}
|
||||
|
||||
/// Returns true if the attributes contain any of `proc_macro`,
|
||||
/// `proc_macro_derive` or `proc_macro_attribute`, false otherwise
|
||||
/// Checks whether `attrs` contain any of `proc_macro`, `proc_macro_derive` or
|
||||
/// `proc_macro_attribute`
|
||||
pub fn is_proc_macro(attrs: &[impl AttributeExt]) -> bool {
|
||||
attrs.iter().any(AttributeExt::is_proc_macro_attr)
|
||||
}
|
||||
|
||||
/// Returns true if the attributes contain `#[doc(hidden)]`
|
||||
/// Checks whether `attrs` contain `#[doc(hidden)]`
|
||||
pub fn is_doc_hidden(attrs: &[impl AttributeExt]) -> bool {
|
||||
attrs
|
||||
.iter()
|
||||
|
|
@ -164,6 +94,7 @@ pub fn is_doc_hidden(attrs: &[impl AttributeExt]) -> bool {
|
|||
.any(|l| attr::list_contains_name(&l, sym::hidden))
|
||||
}
|
||||
|
||||
/// Checks whether the given ADT, or any of its fields/variants, are marked as `#[non_exhaustive]`
|
||||
pub fn has_non_exhaustive_attr(tcx: TyCtxt<'_>, adt: AdtDef<'_>) -> bool {
|
||||
adt.is_variant_list_non_exhaustive()
|
||||
|| find_attr!(tcx.get_all_attrs(adt.did()), AttributeKind::NonExhaustive(..))
|
||||
|
|
@ -176,7 +107,7 @@ pub fn has_non_exhaustive_attr(tcx: TyCtxt<'_>, adt: AdtDef<'_>) -> bool {
|
|||
.any(|field_def| find_attr!(tcx.get_all_attrs(field_def.did), AttributeKind::NonExhaustive(..)))
|
||||
}
|
||||
|
||||
/// Checks if the given span contains a `#[cfg(..)]` attribute
|
||||
/// Checks whether the given span contains a `#[cfg(..)]` attribute
|
||||
pub fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool {
|
||||
s.check_source_text(cx, |src| {
|
||||
let mut iter = tokenize_with_text(src);
|
||||
|
|
@ -198,3 +129,52 @@ pub fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool {
|
|||
false
|
||||
})
|
||||
}
|
||||
|
||||
/// Currently used to keep track of the current value of `#[clippy::cognitive_complexity(N)]`
|
||||
pub struct LimitStack {
|
||||
default: u64,
|
||||
stack: Vec<u64>,
|
||||
}
|
||||
|
||||
impl Drop for LimitStack {
|
||||
fn drop(&mut self) {
|
||||
debug_assert_eq!(self.stack, Vec::<u64>::new()); // avoid `.is_empty()`, for a nicer error message
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(missing_docs, reason = "they're all trivial...")]
|
||||
impl LimitStack {
|
||||
#[must_use]
|
||||
/// Initialize the stack starting with a default value, which usually comes from configuration
|
||||
pub fn new(limit: u64) -> Self {
|
||||
Self {
|
||||
default: limit,
|
||||
stack: vec![],
|
||||
}
|
||||
}
|
||||
pub fn limit(&self) -> u64 {
|
||||
self.stack.last().copied().unwrap_or(self.default)
|
||||
}
|
||||
pub fn push_attrs(&mut self, sess: &Session, attrs: &[impl AttributeExt], name: Symbol) {
|
||||
let stack = &mut self.stack;
|
||||
parse_attrs(sess, attrs, name, |val| stack.push(val));
|
||||
}
|
||||
pub fn pop_attrs(&mut self, sess: &Session, attrs: &[impl AttributeExt], name: Symbol) {
|
||||
let stack = &mut self.stack;
|
||||
parse_attrs(sess, attrs, name, |val| debug_assert_eq!(stack.pop(), Some(val)));
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[impl AttributeExt], name: Symbol, mut f: F) {
|
||||
for attr in get_builtin_attr(sess, attrs, name) {
|
||||
let Some(value) = attr.value_str() else {
|
||||
sess.dcx().span_err(attr.span(), "bad clippy attribute");
|
||||
continue;
|
||||
};
|
||||
let Ok(value) = u64::from_str(value.as_str()) else {
|
||||
sess.dcx().span_err(attr.span(), "not a number");
|
||||
continue;
|
||||
};
|
||||
f(value);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1138,9 +1138,8 @@ pub fn const_item_rhs_to_expr<'tcx>(tcx: TyCtxt<'tcx>, ct_rhs: ConstItemRhs<'tcx
|
|||
match ct_rhs {
|
||||
ConstItemRhs::Body(body_id) => Some(tcx.hir_body(body_id).value),
|
||||
ConstItemRhs::TypeConst(const_arg) => match const_arg.kind {
|
||||
ConstArgKind::Path(_) => None,
|
||||
ConstArgKind::Anon(anon) => Some(tcx.hir_body(anon.body).value),
|
||||
ConstArgKind::Error(..) | ConstArgKind::Infer(..) => None,
|
||||
ConstArgKind::Path(_) | ConstArgKind::Error(..) | ConstArgKind::Infer(..) => None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -480,10 +480,8 @@ impl HirEqInterExpr<'_, '_, '_> {
|
|||
// Use explicit match for now since ConstArg is undergoing flux.
|
||||
(ConstArgKind::Path(..), ConstArgKind::Anon(..))
|
||||
| (ConstArgKind::Anon(..), ConstArgKind::Path(..))
|
||||
| (ConstArgKind::Infer(..), _)
|
||||
| (_, ConstArgKind::Infer(..))
|
||||
| (ConstArgKind::Error(..), _)
|
||||
| (_, ConstArgKind::Error(..)) => false,
|
||||
| (ConstArgKind::Infer(..) | ConstArgKind::Error(..), _)
|
||||
| (_, ConstArgKind::Infer(..) | ConstArgKind::Error(..)) => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ extern crate rustc_span;
|
|||
extern crate rustc_trait_selection;
|
||||
|
||||
pub mod ast_utils;
|
||||
#[deny(missing_docs)]
|
||||
pub mod attrs;
|
||||
mod check_proc_macro;
|
||||
pub mod comparisons;
|
||||
|
|
@ -131,7 +132,7 @@ use crate::ast_utils::unordered_over;
|
|||
use crate::consts::{ConstEvalCtxt, Constant};
|
||||
use crate::higher::Range;
|
||||
use crate::msrvs::Msrv;
|
||||
use crate::res::{MaybeDef, MaybeResPath};
|
||||
use crate::res::{MaybeDef, MaybeQPath, MaybeResPath};
|
||||
use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
|
||||
use crate::visitors::for_each_expr_without_closures;
|
||||
|
||||
|
|
@ -300,6 +301,22 @@ pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) ->
|
|||
cx.tcx.lang_items().get(item) == Some(did)
|
||||
}
|
||||
|
||||
/// Checks is `expr` is `None`
|
||||
pub fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
expr.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone)
|
||||
}
|
||||
|
||||
/// If `expr` is `Some(inner)`, returns `inner`
|
||||
pub fn as_some_expr<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
|
||||
if let ExprKind::Call(e, [arg]) = expr.kind
|
||||
&& e.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome)
|
||||
{
|
||||
Some(arg)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if `expr` is an empty block or an empty tuple.
|
||||
pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
|
||||
matches!(
|
||||
|
|
@ -320,6 +337,25 @@ pub fn is_wild(pat: &Pat<'_>) -> bool {
|
|||
matches!(pat.kind, PatKind::Wild)
|
||||
}
|
||||
|
||||
/// If `pat` is:
|
||||
/// - `Some(inner)`, returns `inner`
|
||||
/// - it will _usually_ contain just one element, but could have two, given patterns like
|
||||
/// `Some(inner, ..)` or `Some(.., inner)`
|
||||
/// - `Some`, returns `[]`
|
||||
/// - otherwise, returns `None`
|
||||
pub fn as_some_pattern<'a, 'hir>(cx: &LateContext<'_>, pat: &'a Pat<'hir>) -> Option<&'a [Pat<'hir>]> {
|
||||
if let PatKind::TupleStruct(ref qpath, inner, _) = pat.kind
|
||||
&& cx
|
||||
.qpath_res(qpath, pat.hir_id)
|
||||
.ctor_parent(cx)
|
||||
.is_lang_item(cx, OptionSome)
|
||||
{
|
||||
Some(inner)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the `pat` is `None`.
|
||||
pub fn is_none_pattern(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
|
||||
matches!(pat.kind,
|
||||
|
|
@ -2782,11 +2818,7 @@ pub fn pat_and_expr_can_be_question_mark<'a, 'hir>(
|
|||
pat: &'a Pat<'hir>,
|
||||
else_body: &Expr<'_>,
|
||||
) -> Option<&'a Pat<'hir>> {
|
||||
if let PatKind::TupleStruct(pat_path, [inner_pat], _) = pat.kind
|
||||
&& cx
|
||||
.qpath_res(&pat_path, pat.hir_id)
|
||||
.ctor_parent(cx)
|
||||
.is_lang_item(cx, OptionSome)
|
||||
if let Some([inner_pat]) = as_some_pattern(cx, pat)
|
||||
&& !is_refutable(cx, inner_pat)
|
||||
&& let else_body = peel_blocks(else_body)
|
||||
&& let ExprKind::Ret(Some(ret_val)) = else_body.kind
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
use std::sync::{Arc, OnceLock};
|
||||
|
||||
use crate::visitors::{Descend, for_each_expr_without_closures};
|
||||
use crate::{get_unique_attr, sym};
|
||||
use crate::{get_unique_builtin_attr, sym};
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
use rustc_ast::{FormatArgs, FormatArgument, FormatPlaceholder};
|
||||
|
|
@ -42,7 +42,7 @@ pub fn is_format_macro(cx: &LateContext<'_>, macro_def_id: DefId) -> bool {
|
|||
} else {
|
||||
// Allow users to tag any macro as being format!-like
|
||||
// TODO: consider deleting FORMAT_MACRO_DIAG_ITEMS and using just this method
|
||||
get_unique_attr(cx.sess(), cx.tcx.get_all_attrs(macro_def_id), sym::format_args).is_some()
|
||||
get_unique_builtin_attr(cx.sess(), cx.tcx.get_all_attrs(macro_def_id), sym::format_args).is_some()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,16 @@
|
|||
use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
|
||||
use std::iter;
|
||||
|
||||
/// Represents the base of a numeric literal, used for parsing and formatting.
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
pub enum Radix {
|
||||
/// A binary literal (e.g., `0b1010`)
|
||||
Binary,
|
||||
/// An octal literal (e.g., `0o670`)
|
||||
Octal,
|
||||
/// A decimal literal (e.g., `123`)
|
||||
Decimal,
|
||||
/// A hexadecimal literal (e.g., `0xFF`)
|
||||
Hexadecimal,
|
||||
}
|
||||
|
||||
|
|
@ -46,6 +51,7 @@ pub struct NumericLiteral<'a> {
|
|||
}
|
||||
|
||||
impl<'a> NumericLiteral<'a> {
|
||||
/// Attempts to parse a `NumericLiteral` from the source string of an `ast::LitKind`.
|
||||
pub fn from_lit_kind(src: &'a str, lit_kind: &LitKind) -> Option<NumericLiteral<'a>> {
|
||||
let unsigned_src = src.strip_prefix('-').map_or(src, |s| s);
|
||||
if lit_kind.is_numeric()
|
||||
|
|
@ -63,6 +69,7 @@ impl<'a> NumericLiteral<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Parses a raw numeric literal string into its structured `NumericLiteral` parts.
|
||||
#[must_use]
|
||||
pub fn new(lit: &'a str, suffix: Option<&'a str>, float: bool) -> Self {
|
||||
let unsigned_lit = lit.trim_start_matches('-');
|
||||
|
|
@ -102,11 +109,12 @@ impl<'a> NumericLiteral<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Checks if the literal's radix is `Radix::Decimal`
|
||||
pub fn is_decimal(&self) -> bool {
|
||||
self.radix == Radix::Decimal
|
||||
}
|
||||
|
||||
pub fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(&str, &str)>) {
|
||||
fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(&str, &str)>) {
|
||||
let mut integer = digits;
|
||||
let mut fraction = None;
|
||||
let mut exponent = None;
|
||||
|
|
@ -180,7 +188,7 @@ impl<'a> NumericLiteral<'a> {
|
|||
output
|
||||
}
|
||||
|
||||
pub fn group_digits(output: &mut String, input: &str, group_size: usize, partial_group_first: bool, pad: bool) {
|
||||
fn group_digits(output: &mut String, input: &str, group_size: usize, partial_group_first: bool, zero_pad: bool) {
|
||||
debug_assert!(group_size > 0);
|
||||
|
||||
let mut digits = input.chars().filter(|&c| c != '_');
|
||||
|
|
@ -196,7 +204,7 @@ impl<'a> NumericLiteral<'a> {
|
|||
|
||||
if partial_group_first {
|
||||
first_group_size = (digits.clone().count() - 1) % group_size + 1;
|
||||
if pad {
|
||||
if zero_pad {
|
||||
for _ in 0..group_size - first_group_size {
|
||||
output.push('0');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -194,8 +194,7 @@ fn check_rvalue<'tcx>(
|
|||
))
|
||||
}
|
||||
},
|
||||
Rvalue::NullaryOp(NullOp::OffsetOf(_) | NullOp::RuntimeChecks(_), _)
|
||||
| Rvalue::ShallowInitBox(_, _) => Ok(()),
|
||||
Rvalue::NullaryOp(NullOp::OffsetOf(_) | NullOp::RuntimeChecks(_), _) | Rvalue::ShallowInitBox(_, _) => Ok(()),
|
||||
Rvalue::UnaryOp(_, operand) => {
|
||||
let ty = operand.ty(body, cx.tcx);
|
||||
if ty.is_integral() || ty.is_bool() {
|
||||
|
|
|
|||
|
|
@ -126,6 +126,7 @@ generate! {
|
|||
copy_from_nonoverlapping,
|
||||
copy_to,
|
||||
copy_to_nonoverlapping,
|
||||
core_arch,
|
||||
count_ones,
|
||||
create,
|
||||
create_new,
|
||||
|
|
@ -330,6 +331,7 @@ generate! {
|
|||
splitn_mut,
|
||||
sqrt,
|
||||
starts_with,
|
||||
std_detect,
|
||||
step_by,
|
||||
strlen,
|
||||
style,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[toolchain]
|
||||
# begin autogenerated nightly
|
||||
channel = "nightly-2025-10-31"
|
||||
channel = "nightly-2025-11-14"
|
||||
# end autogenerated nightly
|
||||
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
|
||||
profile = "minimal"
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
extern crate proc_macro_derive;
|
||||
|
||||
use core::num::{NonZero, Saturating, Wrapping};
|
||||
use core::time::Duration;
|
||||
|
||||
const ONE: i32 = 1;
|
||||
const ZERO: i32 = 0;
|
||||
|
|
@ -687,4 +688,59 @@ pub fn explicit_methods() {
|
|||
//~^ arithmetic_side_effects
|
||||
}
|
||||
|
||||
pub fn issue_15943(days: u8) -> Duration {
|
||||
Duration::from_secs(86400 * u64::from(days))
|
||||
}
|
||||
|
||||
pub fn type_conversion_add() {
|
||||
let _ = u128::MAX + u128::from(1u8);
|
||||
//~^ arithmetic_side_effects
|
||||
let _ = 1u128 + u128::from(1u16);
|
||||
let _ = 1u128 + u128::from(1u32);
|
||||
let _ = 1u128 + u128::from(1u64);
|
||||
|
||||
let _ = 1u64 + u64::from(1u8);
|
||||
let _ = 1u64 + u64::from(1u16);
|
||||
let _ = 1u64 + u64::from(1u32);
|
||||
|
||||
let _ = 1u32 + u32::from(1u8);
|
||||
let _ = 1u32 + u32::from(1u16);
|
||||
|
||||
let _ = 1u16 + u16::from(1u8);
|
||||
}
|
||||
|
||||
pub fn type_conversion_mul() {
|
||||
let _ = u128::MAX * u128::from(1u8);
|
||||
//~^ arithmetic_side_effects
|
||||
let _ = 1u128 * u128::from(1u16);
|
||||
let _ = 1u128 * u128::from(1u32);
|
||||
let _ = 1u128 * u128::from(1u64);
|
||||
|
||||
let _ = 1u64 * u64::from(1u8);
|
||||
let _ = 1u64 * u64::from(1u16);
|
||||
let _ = 1u64 * u64::from(1u32);
|
||||
|
||||
let _ = 1u32 * u32::from(1u8);
|
||||
let _ = 1u32 * u32::from(1u16);
|
||||
|
||||
let _ = 1u16 * u16::from(1u8);
|
||||
}
|
||||
|
||||
pub fn type_conversion_does_not_escape_its_context() {
|
||||
struct Foo;
|
||||
impl Foo {
|
||||
fn from(n: u8) -> u64 {
|
||||
u64::from(n)
|
||||
}
|
||||
}
|
||||
let _ = Duration::from_secs(86400 * Foo::from(1));
|
||||
//~^ arithmetic_side_effects
|
||||
|
||||
fn shift(x: u8) -> u64 {
|
||||
1 << u64::from(x)
|
||||
}
|
||||
let _ = Duration::from_secs(86400 * shift(1));
|
||||
//~^ arithmetic_side_effects
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:166:13
|
||||
--> tests/ui/arithmetic_side_effects.rs:167:13
|
||||
|
|
||||
LL | let _ = 1f16 + 1f16;
|
||||
| ^^^^^^^^^^^
|
||||
|
|
@ -8,766 +8,790 @@ LL | let _ = 1f16 + 1f16;
|
|||
= help: to override `-D warnings` add `#[allow(clippy::arithmetic_side_effects)]`
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:170:13
|
||||
--> tests/ui/arithmetic_side_effects.rs:171:13
|
||||
|
|
||||
LL | let _ = 1f128 + 1f128;
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:175:13
|
||||
--> tests/ui/arithmetic_side_effects.rs:176:13
|
||||
|
|
||||
LL | let _ = String::new() + &String::new();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:311:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:312:5
|
||||
|
|
||||
LL | _n += 1;
|
||||
| ^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:313:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:314:5
|
||||
|
|
||||
LL | _n += &1;
|
||||
| ^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:315:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:316:5
|
||||
|
|
||||
LL | _n -= 1;
|
||||
| ^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:317:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:318:5
|
||||
|
|
||||
LL | _n -= &1;
|
||||
| ^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:319:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:320:5
|
||||
|
|
||||
LL | _n /= 0;
|
||||
| ^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:321:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:322:5
|
||||
|
|
||||
LL | _n /= &0;
|
||||
| ^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:323:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:324:5
|
||||
|
|
||||
LL | _n %= 0;
|
||||
| ^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:325:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:326:5
|
||||
|
|
||||
LL | _n %= &0;
|
||||
| ^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:327:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:328:5
|
||||
|
|
||||
LL | _n *= 2;
|
||||
| ^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:329:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:330:5
|
||||
|
|
||||
LL | _n *= &2;
|
||||
| ^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:331:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:332:5
|
||||
|
|
||||
LL | _n += -1;
|
||||
| ^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:333:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:334:5
|
||||
|
|
||||
LL | _n += &-1;
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:335:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:336:5
|
||||
|
|
||||
LL | _n -= -1;
|
||||
| ^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:337:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:338:5
|
||||
|
|
||||
LL | _n -= &-1;
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:339:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:340:5
|
||||
|
|
||||
LL | _n /= -0;
|
||||
| ^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:341:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:342:5
|
||||
|
|
||||
LL | _n /= &-0;
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:343:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:344:5
|
||||
|
|
||||
LL | _n %= -0;
|
||||
| ^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:345:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:346:5
|
||||
|
|
||||
LL | _n %= &-0;
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:347:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:348:5
|
||||
|
|
||||
LL | _n *= -2;
|
||||
| ^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:349:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:350:5
|
||||
|
|
||||
LL | _n *= &-2;
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:351:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:352:5
|
||||
|
|
||||
LL | _custom += Custom;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:353:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:354:5
|
||||
|
|
||||
LL | _custom += &Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:355:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:356:5
|
||||
|
|
||||
LL | _custom -= Custom;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:357:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:358:5
|
||||
|
|
||||
LL | _custom -= &Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:359:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:360:5
|
||||
|
|
||||
LL | _custom /= Custom;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:361:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:362:5
|
||||
|
|
||||
LL | _custom /= &Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:363:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:364:5
|
||||
|
|
||||
LL | _custom %= Custom;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:365:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:366:5
|
||||
|
|
||||
LL | _custom %= &Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:367:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:368:5
|
||||
|
|
||||
LL | _custom *= Custom;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:369:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:370:5
|
||||
|
|
||||
LL | _custom *= &Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:371:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:372:5
|
||||
|
|
||||
LL | _custom >>= Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:373:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:374:5
|
||||
|
|
||||
LL | _custom >>= &Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:375:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:376:5
|
||||
|
|
||||
LL | _custom <<= Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:377:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:378:5
|
||||
|
|
||||
LL | _custom <<= &Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:379:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:380:5
|
||||
|
|
||||
LL | _custom += -Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:381:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:382:5
|
||||
|
|
||||
LL | _custom += &-Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:383:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:384:5
|
||||
|
|
||||
LL | _custom -= -Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:385:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:386:5
|
||||
|
|
||||
LL | _custom -= &-Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:387:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:388:5
|
||||
|
|
||||
LL | _custom /= -Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:389:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:390:5
|
||||
|
|
||||
LL | _custom /= &-Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:391:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:392:5
|
||||
|
|
||||
LL | _custom %= -Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:393:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:394:5
|
||||
|
|
||||
LL | _custom %= &-Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:395:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:396:5
|
||||
|
|
||||
LL | _custom *= -Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:397:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:398:5
|
||||
|
|
||||
LL | _custom *= &-Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:399:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:400:5
|
||||
|
|
||||
LL | _custom >>= -Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:401:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:402:5
|
||||
|
|
||||
LL | _custom >>= &-Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:403:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:404:5
|
||||
|
|
||||
LL | _custom <<= -Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:405:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:406:5
|
||||
|
|
||||
LL | _custom <<= &-Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:409:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:410:10
|
||||
|
|
||||
LL | _n = _n + 1;
|
||||
| ^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:411:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:412:10
|
||||
|
|
||||
LL | _n = _n + &1;
|
||||
| ^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:413:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:414:10
|
||||
|
|
||||
LL | _n = 1 + _n;
|
||||
| ^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:415:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:416:10
|
||||
|
|
||||
LL | _n = &1 + _n;
|
||||
| ^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:417:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:418:10
|
||||
|
|
||||
LL | _n = _n - 1;
|
||||
| ^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:419:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:420:10
|
||||
|
|
||||
LL | _n = _n - &1;
|
||||
| ^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:421:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:422:10
|
||||
|
|
||||
LL | _n = 1 - _n;
|
||||
| ^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:423:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:424:10
|
||||
|
|
||||
LL | _n = &1 - _n;
|
||||
| ^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:425:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:426:10
|
||||
|
|
||||
LL | _n = _n / 0;
|
||||
| ^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:427:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:428:10
|
||||
|
|
||||
LL | _n = _n / &0;
|
||||
| ^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:429:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:430:10
|
||||
|
|
||||
LL | _n = _n % 0;
|
||||
| ^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:431:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:432:10
|
||||
|
|
||||
LL | _n = _n % &0;
|
||||
| ^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:433:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:434:10
|
||||
|
|
||||
LL | _n = _n * 2;
|
||||
| ^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:435:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:436:10
|
||||
|
|
||||
LL | _n = _n * &2;
|
||||
| ^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:437:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:438:10
|
||||
|
|
||||
LL | _n = 2 * _n;
|
||||
| ^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:439:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:440:10
|
||||
|
|
||||
LL | _n = &2 * _n;
|
||||
| ^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:441:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:442:10
|
||||
|
|
||||
LL | _n = 23 + &85;
|
||||
| ^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:443:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:444:10
|
||||
|
|
||||
LL | _n = &23 + 85;
|
||||
| ^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:445:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:446:10
|
||||
|
|
||||
LL | _n = &23 + &85;
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:447:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:448:15
|
||||
|
|
||||
LL | _custom = _custom + _custom;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:449:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:450:15
|
||||
|
|
||||
LL | _custom = _custom + &_custom;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:451:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:452:15
|
||||
|
|
||||
LL | _custom = Custom + _custom;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:453:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:454:15
|
||||
|
|
||||
LL | _custom = &Custom + _custom;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:455:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:456:15
|
||||
|
|
||||
LL | _custom = _custom - Custom;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:457:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:458:15
|
||||
|
|
||||
LL | _custom = _custom - &Custom;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:459:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:460:15
|
||||
|
|
||||
LL | _custom = Custom - _custom;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:461:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:462:15
|
||||
|
|
||||
LL | _custom = &Custom - _custom;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:463:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:464:15
|
||||
|
|
||||
LL | _custom = _custom / Custom;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:465:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:466:15
|
||||
|
|
||||
LL | _custom = _custom / &Custom;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:467:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:468:15
|
||||
|
|
||||
LL | _custom = _custom % Custom;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:469:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:470:15
|
||||
|
|
||||
LL | _custom = _custom % &Custom;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:471:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:472:15
|
||||
|
|
||||
LL | _custom = _custom * Custom;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:473:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:474:15
|
||||
|
|
||||
LL | _custom = _custom * &Custom;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:475:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:476:15
|
||||
|
|
||||
LL | _custom = Custom * _custom;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:477:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:478:15
|
||||
|
|
||||
LL | _custom = &Custom * _custom;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:479:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:480:15
|
||||
|
|
||||
LL | _custom = Custom + &Custom;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:481:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:482:15
|
||||
|
|
||||
LL | _custom = &Custom + Custom;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:483:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:484:15
|
||||
|
|
||||
LL | _custom = &Custom + &Custom;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:485:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:486:15
|
||||
|
|
||||
LL | _custom = _custom >> _custom;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:487:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:488:15
|
||||
|
|
||||
LL | _custom = _custom >> &_custom;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:489:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:490:15
|
||||
|
|
||||
LL | _custom = Custom << _custom;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:491:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:492:15
|
||||
|
|
||||
LL | _custom = &Custom << _custom;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:495:23
|
||||
--> tests/ui/arithmetic_side_effects.rs:496:23
|
||||
|
|
||||
LL | _n.saturating_div(0);
|
||||
| ^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:497:21
|
||||
--> tests/ui/arithmetic_side_effects.rs:498:21
|
||||
|
|
||||
LL | _n.wrapping_div(0);
|
||||
| ^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:499:21
|
||||
--> tests/ui/arithmetic_side_effects.rs:500:21
|
||||
|
|
||||
LL | _n.wrapping_rem(0);
|
||||
| ^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:501:28
|
||||
--> tests/ui/arithmetic_side_effects.rs:502:28
|
||||
|
|
||||
LL | _n.wrapping_rem_euclid(0);
|
||||
| ^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:504:23
|
||||
--> tests/ui/arithmetic_side_effects.rs:505:23
|
||||
|
|
||||
LL | _n.saturating_div(_n);
|
||||
| ^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:506:21
|
||||
--> tests/ui/arithmetic_side_effects.rs:507:21
|
||||
|
|
||||
LL | _n.wrapping_div(_n);
|
||||
| ^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:508:21
|
||||
--> tests/ui/arithmetic_side_effects.rs:509:21
|
||||
|
|
||||
LL | _n.wrapping_rem(_n);
|
||||
| ^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:510:28
|
||||
--> tests/ui/arithmetic_side_effects.rs:511:28
|
||||
|
|
||||
LL | _n.wrapping_rem_euclid(_n);
|
||||
| ^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:513:23
|
||||
--> tests/ui/arithmetic_side_effects.rs:514:23
|
||||
|
|
||||
LL | _n.saturating_div(*Box::new(_n));
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:517:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:518:10
|
||||
|
|
||||
LL | _n = -_n;
|
||||
| ^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:519:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:520:10
|
||||
|
|
||||
LL | _n = -&_n;
|
||||
| ^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:521:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:522:15
|
||||
|
|
||||
LL | _custom = -_custom;
|
||||
| ^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:523:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:524:15
|
||||
|
|
||||
LL | _custom = -&_custom;
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:525:9
|
||||
--> tests/ui/arithmetic_side_effects.rs:526:9
|
||||
|
|
||||
LL | _ = -*Box::new(_n);
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:535:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:536:5
|
||||
|
|
||||
LL | 1 + i;
|
||||
| ^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:537:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:538:5
|
||||
|
|
||||
LL | i * 2;
|
||||
| ^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:539:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:540:5
|
||||
|
|
||||
LL | 1 % i / 2;
|
||||
| ^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:541:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:542:5
|
||||
|
|
||||
LL | i - 2 + 2 - i;
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:543:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:544:5
|
||||
|
|
||||
LL | -i;
|
||||
| ^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:555:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:556:5
|
||||
|
|
||||
LL | i += 1;
|
||||
| ^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:557:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:558:5
|
||||
|
|
||||
LL | i -= 1;
|
||||
| ^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:559:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:560:5
|
||||
|
|
||||
LL | i *= 2;
|
||||
| ^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:562:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:563:5
|
||||
|
|
||||
LL | i /= 0;
|
||||
| ^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:565:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:566:5
|
||||
|
|
||||
LL | i /= var1;
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:567:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:568:5
|
||||
|
|
||||
LL | i /= var2;
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:570:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:571:5
|
||||
|
|
||||
LL | i %= 0;
|
||||
| ^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:573:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:574:5
|
||||
|
|
||||
LL | i %= var1;
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:575:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:576:5
|
||||
|
|
||||
LL | i %= var2;
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:586:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:587:5
|
||||
|
|
||||
LL | 10 / a
|
||||
| ^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:641:9
|
||||
--> tests/ui/arithmetic_side_effects.rs:642:9
|
||||
|
|
||||
LL | x / maybe_zero
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:646:9
|
||||
--> tests/ui/arithmetic_side_effects.rs:647:9
|
||||
|
|
||||
LL | x % maybe_zero
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:658:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:659:5
|
||||
|
|
||||
LL | one.add_assign(1);
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:663:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:664:5
|
||||
|
|
||||
LL | one.sub_assign(1);
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:684:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:685:5
|
||||
|
|
||||
LL | one.add(&one);
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:686:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:687:5
|
||||
|
|
||||
LL | Box::new(one).add(one);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 128 previous errors
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:696:13
|
||||
|
|
||||
LL | let _ = u128::MAX + u128::from(1u8);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:713:13
|
||||
|
|
||||
LL | let _ = u128::MAX * u128::from(1u8);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:736:33
|
||||
|
|
||||
LL | let _ = Duration::from_secs(86400 * Foo::from(1));
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:742:33
|
||||
|
|
||||
LL | let _ = Duration::from_secs(86400 * shift(1));
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 132 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
#![warn(clippy::cmp_null)]
|
||||
#![allow(unused_mut)]
|
||||
|
||||
use std::ptr;
|
||||
|
||||
|
|
@ -18,7 +17,7 @@ fn main() {
|
|||
}
|
||||
|
||||
let mut y = 0;
|
||||
let mut m: *mut usize = &mut y;
|
||||
let m: *mut usize = &mut y;
|
||||
if m.is_null() {
|
||||
//~^ cmp_null
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
#![warn(clippy::cmp_null)]
|
||||
#![allow(unused_mut)]
|
||||
|
||||
use std::ptr;
|
||||
|
||||
|
|
@ -18,7 +17,7 @@ fn main() {
|
|||
}
|
||||
|
||||
let mut y = 0;
|
||||
let mut m: *mut usize = &mut y;
|
||||
let m: *mut usize = &mut y;
|
||||
if m == ptr::null_mut() {
|
||||
//~^ cmp_null
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
error: comparing with null is better expressed by the `.is_null()` method
|
||||
--> tests/ui/cmp_null.rs:9:8
|
||||
--> tests/ui/cmp_null.rs:8:8
|
||||
|
|
||||
LL | if p == ptr::null() {
|
||||
| ^^^^^^^^^^^^^^^^ help: try: `p.is_null()`
|
||||
|
|
@ -8,31 +8,31 @@ LL | if p == ptr::null() {
|
|||
= help: to override `-D warnings` add `#[allow(clippy::cmp_null)]`
|
||||
|
||||
error: comparing with null is better expressed by the `.is_null()` method
|
||||
--> tests/ui/cmp_null.rs:14:8
|
||||
--> tests/ui/cmp_null.rs:13:8
|
||||
|
|
||||
LL | if ptr::null() == p {
|
||||
| ^^^^^^^^^^^^^^^^ help: try: `p.is_null()`
|
||||
|
||||
error: comparing with null is better expressed by the `.is_null()` method
|
||||
--> tests/ui/cmp_null.rs:22:8
|
||||
--> tests/ui/cmp_null.rs:21:8
|
||||
|
|
||||
LL | if m == ptr::null_mut() {
|
||||
| ^^^^^^^^^^^^^^^^^^^^ help: try: `m.is_null()`
|
||||
|
||||
error: comparing with null is better expressed by the `.is_null()` method
|
||||
--> tests/ui/cmp_null.rs:27:8
|
||||
--> tests/ui/cmp_null.rs:26:8
|
||||
|
|
||||
LL | if ptr::null_mut() == m {
|
||||
| ^^^^^^^^^^^^^^^^^^^^ help: try: `m.is_null()`
|
||||
|
||||
error: comparing with null is better expressed by the `.is_null()` method
|
||||
--> tests/ui/cmp_null.rs:33:13
|
||||
--> tests/ui/cmp_null.rs:32:13
|
||||
|
|
||||
LL | let _ = x as *const () == ptr::null();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(x as *const ()).is_null()`
|
||||
|
||||
error: comparing with null is better expressed by the `.is_null()` method
|
||||
--> tests/ui/cmp_null.rs:39:19
|
||||
--> tests/ui/cmp_null.rs:38:19
|
||||
|
|
||||
LL | debug_assert!(f != std::ptr::null_mut());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!f.is_null()`
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
error: casting function pointer `foo` to `i8`, which truncates the value
|
||||
--> tests/ui/fn_to_numeric_cast.rs:12:13
|
||||
--> tests/ui/fn_to_numeric_cast.rs:13:13
|
||||
|
|
||||
LL | let _ = foo as i8;
|
||||
| ^^^^^^^^^ help: try: `foo as usize`
|
||||
|
|
@ -8,13 +8,13 @@ LL | let _ = foo as i8;
|
|||
= help: to override `-D warnings` add `#[allow(clippy::fn_to_numeric_cast_with_truncation)]`
|
||||
|
||||
error: casting function pointer `foo` to `i16`, which truncates the value
|
||||
--> tests/ui/fn_to_numeric_cast.rs:14:13
|
||||
--> tests/ui/fn_to_numeric_cast.rs:15:13
|
||||
|
|
||||
LL | let _ = foo as i16;
|
||||
| ^^^^^^^^^^ help: try: `foo as usize`
|
||||
|
||||
error: casting function pointer `foo` to `i32`
|
||||
--> tests/ui/fn_to_numeric_cast.rs:16:13
|
||||
--> tests/ui/fn_to_numeric_cast.rs:17:13
|
||||
|
|
||||
LL | let _ = foo as i32;
|
||||
| ^^^^^^^^^^ help: try: `foo as usize`
|
||||
|
|
@ -23,121 +23,121 @@ LL | let _ = foo as i32;
|
|||
= help: to override `-D warnings` add `#[allow(clippy::fn_to_numeric_cast)]`
|
||||
|
||||
error: casting function pointer `foo` to `i64`
|
||||
--> tests/ui/fn_to_numeric_cast.rs:19:13
|
||||
--> tests/ui/fn_to_numeric_cast.rs:20:13
|
||||
|
|
||||
LL | let _ = foo as i64;
|
||||
| ^^^^^^^^^^ help: try: `foo as usize`
|
||||
|
||||
error: casting function pointer `foo` to `i128`
|
||||
--> tests/ui/fn_to_numeric_cast.rs:21:13
|
||||
--> tests/ui/fn_to_numeric_cast.rs:22:13
|
||||
|
|
||||
LL | let _ = foo as i128;
|
||||
| ^^^^^^^^^^^ help: try: `foo as usize`
|
||||
|
||||
error: casting function pointer `foo` to `isize`
|
||||
--> tests/ui/fn_to_numeric_cast.rs:23:13
|
||||
--> tests/ui/fn_to_numeric_cast.rs:24:13
|
||||
|
|
||||
LL | let _ = foo as isize;
|
||||
| ^^^^^^^^^^^^ help: try: `foo as usize`
|
||||
|
||||
error: casting function pointer `foo` to `u8`, which truncates the value
|
||||
--> tests/ui/fn_to_numeric_cast.rs:26:13
|
||||
--> tests/ui/fn_to_numeric_cast.rs:27:13
|
||||
|
|
||||
LL | let _ = foo as u8;
|
||||
| ^^^^^^^^^ help: try: `foo as usize`
|
||||
|
||||
error: casting function pointer `foo` to `u16`, which truncates the value
|
||||
--> tests/ui/fn_to_numeric_cast.rs:28:13
|
||||
--> tests/ui/fn_to_numeric_cast.rs:29:13
|
||||
|
|
||||
LL | let _ = foo as u16;
|
||||
| ^^^^^^^^^^ help: try: `foo as usize`
|
||||
|
||||
error: casting function pointer `foo` to `u32`
|
||||
--> tests/ui/fn_to_numeric_cast.rs:30:13
|
||||
--> tests/ui/fn_to_numeric_cast.rs:31:13
|
||||
|
|
||||
LL | let _ = foo as u32;
|
||||
| ^^^^^^^^^^ help: try: `foo as usize`
|
||||
|
||||
error: casting function pointer `foo` to `u64`
|
||||
--> tests/ui/fn_to_numeric_cast.rs:33:13
|
||||
--> tests/ui/fn_to_numeric_cast.rs:34:13
|
||||
|
|
||||
LL | let _ = foo as u64;
|
||||
| ^^^^^^^^^^ help: try: `foo as usize`
|
||||
|
||||
error: casting function pointer `foo` to `u128`
|
||||
--> tests/ui/fn_to_numeric_cast.rs:35:13
|
||||
--> tests/ui/fn_to_numeric_cast.rs:36:13
|
||||
|
|
||||
LL | let _ = foo as u128;
|
||||
| ^^^^^^^^^^^ help: try: `foo as usize`
|
||||
|
||||
error: casting function pointer `abc` to `i8`, which truncates the value
|
||||
--> tests/ui/fn_to_numeric_cast.rs:49:13
|
||||
--> tests/ui/fn_to_numeric_cast.rs:50:13
|
||||
|
|
||||
LL | let _ = abc as i8;
|
||||
| ^^^^^^^^^ help: try: `abc as usize`
|
||||
|
||||
error: casting function pointer `abc` to `i16`, which truncates the value
|
||||
--> tests/ui/fn_to_numeric_cast.rs:51:13
|
||||
--> tests/ui/fn_to_numeric_cast.rs:52:13
|
||||
|
|
||||
LL | let _ = abc as i16;
|
||||
| ^^^^^^^^^^ help: try: `abc as usize`
|
||||
|
||||
error: casting function pointer `abc` to `i32`
|
||||
--> tests/ui/fn_to_numeric_cast.rs:53:13
|
||||
--> tests/ui/fn_to_numeric_cast.rs:54:13
|
||||
|
|
||||
LL | let _ = abc as i32;
|
||||
| ^^^^^^^^^^ help: try: `abc as usize`
|
||||
|
||||
error: casting function pointer `abc` to `i64`
|
||||
--> tests/ui/fn_to_numeric_cast.rs:56:13
|
||||
--> tests/ui/fn_to_numeric_cast.rs:57:13
|
||||
|
|
||||
LL | let _ = abc as i64;
|
||||
| ^^^^^^^^^^ help: try: `abc as usize`
|
||||
|
||||
error: casting function pointer `abc` to `i128`
|
||||
--> tests/ui/fn_to_numeric_cast.rs:58:13
|
||||
--> tests/ui/fn_to_numeric_cast.rs:59:13
|
||||
|
|
||||
LL | let _ = abc as i128;
|
||||
| ^^^^^^^^^^^ help: try: `abc as usize`
|
||||
|
||||
error: casting function pointer `abc` to `isize`
|
||||
--> tests/ui/fn_to_numeric_cast.rs:60:13
|
||||
--> tests/ui/fn_to_numeric_cast.rs:61:13
|
||||
|
|
||||
LL | let _ = abc as isize;
|
||||
| ^^^^^^^^^^^^ help: try: `abc as usize`
|
||||
|
||||
error: casting function pointer `abc` to `u8`, which truncates the value
|
||||
--> tests/ui/fn_to_numeric_cast.rs:63:13
|
||||
--> tests/ui/fn_to_numeric_cast.rs:64:13
|
||||
|
|
||||
LL | let _ = abc as u8;
|
||||
| ^^^^^^^^^ help: try: `abc as usize`
|
||||
|
||||
error: casting function pointer `abc` to `u16`, which truncates the value
|
||||
--> tests/ui/fn_to_numeric_cast.rs:65:13
|
||||
--> tests/ui/fn_to_numeric_cast.rs:66:13
|
||||
|
|
||||
LL | let _ = abc as u16;
|
||||
| ^^^^^^^^^^ help: try: `abc as usize`
|
||||
|
||||
error: casting function pointer `abc` to `u32`
|
||||
--> tests/ui/fn_to_numeric_cast.rs:67:13
|
||||
--> tests/ui/fn_to_numeric_cast.rs:68:13
|
||||
|
|
||||
LL | let _ = abc as u32;
|
||||
| ^^^^^^^^^^ help: try: `abc as usize`
|
||||
|
||||
error: casting function pointer `abc` to `u64`
|
||||
--> tests/ui/fn_to_numeric_cast.rs:70:13
|
||||
--> tests/ui/fn_to_numeric_cast.rs:71:13
|
||||
|
|
||||
LL | let _ = abc as u64;
|
||||
| ^^^^^^^^^^ help: try: `abc as usize`
|
||||
|
||||
error: casting function pointer `abc` to `u128`
|
||||
--> tests/ui/fn_to_numeric_cast.rs:72:13
|
||||
--> tests/ui/fn_to_numeric_cast.rs:73:13
|
||||
|
|
||||
LL | let _ = abc as u128;
|
||||
| ^^^^^^^^^^^ help: try: `abc as usize`
|
||||
|
||||
error: casting function pointer `f` to `i32`
|
||||
--> tests/ui/fn_to_numeric_cast.rs:80:5
|
||||
--> tests/ui/fn_to_numeric_cast.rs:81:5
|
||||
|
|
||||
LL | f as i32
|
||||
| ^^^^^^^^ help: try: `f as usize`
|
||||
|
|
|
|||
|
|
@ -178,4 +178,11 @@ const fn uncalled_len() {
|
|||
//~^ incompatible_msrv
|
||||
}
|
||||
|
||||
#[clippy::msrv = "1.0.0"]
|
||||
fn vec_macro() {
|
||||
let _: Vec<u32> = vec![];
|
||||
let _: Vec<u32> = vec![1; 3];
|
||||
let _: Vec<u32> = vec![1, 2];
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
|||
|
|
@ -261,4 +261,14 @@ fn issue14164() -> Result<u32, ()> {
|
|||
//~[edition2024]^ let_and_return
|
||||
}
|
||||
|
||||
fn issue15987() -> i32 {
|
||||
macro_rules! sample {
|
||||
( $( $args:expr ),+ ) => {};
|
||||
}
|
||||
|
||||
let r = 5;
|
||||
sample!(r);
|
||||
r
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
|||
|
|
@ -261,4 +261,14 @@ fn issue14164() -> Result<u32, ()> {
|
|||
//~[edition2024]^ let_and_return
|
||||
}
|
||||
|
||||
fn issue15987() -> i32 {
|
||||
macro_rules! sample {
|
||||
( $( $args:expr ),+ ) => {};
|
||||
}
|
||||
|
||||
let r = 5;
|
||||
sample!(r);
|
||||
r
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
|||
|
|
@ -261,4 +261,14 @@ fn issue14164() -> Result<u32, ()> {
|
|||
//~[edition2024]^ let_and_return
|
||||
}
|
||||
|
||||
fn issue15987() -> i32 {
|
||||
macro_rules! sample {
|
||||
( $( $args:expr ),+ ) => {};
|
||||
}
|
||||
|
||||
let r = 5;
|
||||
sample!(r);
|
||||
r
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
|||
|
|
@ -139,3 +139,34 @@ fn main() {
|
|||
}
|
||||
println!("{}", val.get());
|
||||
}
|
||||
|
||||
fn issue16062(bar: fn() -> bool) {
|
||||
let foo;
|
||||
//~^ useless_let_if_seq
|
||||
if bar() {
|
||||
foo = 42;
|
||||
} else {
|
||||
foo = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fn issue16064(bar: fn() -> bool) {
|
||||
macro_rules! mac {
|
||||
($e:expr) => {
|
||||
$e()
|
||||
};
|
||||
($base:expr, $lit:expr) => {
|
||||
$lit * $base + 2
|
||||
};
|
||||
}
|
||||
|
||||
let foo;
|
||||
//~^ useless_let_if_seq
|
||||
if mac!(bar) {
|
||||
foo = mac!(10, 4);
|
||||
} else {
|
||||
foo = 0;
|
||||
}
|
||||
|
||||
let bar = 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,5 +52,29 @@ LL | | }
|
|||
|
|
||||
= note: you might not need `mut` at all
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
error: `if _ { .. } else { .. }` is an expression
|
||||
--> tests/ui/let_if_seq.rs:144:5
|
||||
|
|
||||
LL | / let foo;
|
||||
LL | |
|
||||
LL | | if bar() {
|
||||
LL | | foo = 42;
|
||||
LL | | } else {
|
||||
LL | | foo = 0;
|
||||
LL | | }
|
||||
| |_____^ help: it is more idiomatic to write: `let foo = if bar() { 42 } else { 0 };`
|
||||
|
||||
error: `if _ { .. } else { .. }` is an expression
|
||||
--> tests/ui/let_if_seq.rs:163:5
|
||||
|
|
||||
LL | / let foo;
|
||||
LL | |
|
||||
LL | | if mac!(bar) {
|
||||
LL | | foo = mac!(10, 4);
|
||||
LL | | } else {
|
||||
LL | | foo = 0;
|
||||
LL | | }
|
||||
| |_____^ help: it is more idiomatic to write: `let foo = if mac!(bar) { mac!(10, 4) } else { 0 };`
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -265,3 +265,82 @@ fn issue15269(a: usize, b: usize, c: usize) -> bool {
|
|||
a < b
|
||||
&& b < c
|
||||
}
|
||||
|
||||
#[allow(
|
||||
irrefutable_let_patterns,
|
||||
clippy::blocks_in_conditions,
|
||||
clippy::unused_unit,
|
||||
clippy::let_unit_value,
|
||||
clippy::unit_arg,
|
||||
clippy::unnecessary_operation
|
||||
)]
|
||||
fn issue15537(a: i32) -> ((), (), ()) {
|
||||
let y = (
|
||||
{ todo!() },
|
||||
{
|
||||
{ a };
|
||||
()
|
||||
},
|
||||
(),
|
||||
);
|
||||
|
||||
let y = [
|
||||
{ todo!() },
|
||||
{
|
||||
{ a };
|
||||
()
|
||||
},
|
||||
(),
|
||||
];
|
||||
|
||||
fn call(x: (), y: (), z: ()) {}
|
||||
let y = call(
|
||||
{ todo!() },
|
||||
{
|
||||
{ a };
|
||||
()
|
||||
},
|
||||
(),
|
||||
);
|
||||
|
||||
struct Foo;
|
||||
impl Foo {
|
||||
fn method(&self, x: (), y: (), z: ()) {}
|
||||
}
|
||||
let x = Foo;
|
||||
x.method(
|
||||
{ todo!() },
|
||||
{
|
||||
{ a };
|
||||
()
|
||||
},
|
||||
(),
|
||||
);
|
||||
|
||||
-{
|
||||
{ a };
|
||||
1
|
||||
};
|
||||
|
||||
_ = { a };
|
||||
1;
|
||||
|
||||
if let x = {
|
||||
{ a };
|
||||
1
|
||||
} {}
|
||||
|
||||
if {
|
||||
{ a };
|
||||
true
|
||||
} {
|
||||
todo!()
|
||||
}
|
||||
|
||||
[1, 2, 3][{
|
||||
{ a };
|
||||
1usize
|
||||
}];
|
||||
|
||||
todo!()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -342,3 +342,84 @@ fn issue15269(a: usize, b: usize, c: usize) -> bool {
|
|||
(a, b) => b < c,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(
|
||||
irrefutable_let_patterns,
|
||||
clippy::blocks_in_conditions,
|
||||
clippy::unused_unit,
|
||||
clippy::let_unit_value,
|
||||
clippy::unit_arg,
|
||||
clippy::unnecessary_operation
|
||||
)]
|
||||
fn issue15537(a: i32) -> ((), (), ()) {
|
||||
let y = (
|
||||
{ todo!() },
|
||||
match { a } {
|
||||
//~^ match_single_binding
|
||||
_ => (),
|
||||
},
|
||||
(),
|
||||
);
|
||||
|
||||
let y = [
|
||||
{ todo!() },
|
||||
match { a } {
|
||||
//~^ match_single_binding
|
||||
_ => (),
|
||||
},
|
||||
(),
|
||||
];
|
||||
|
||||
fn call(x: (), y: (), z: ()) {}
|
||||
let y = call(
|
||||
{ todo!() },
|
||||
match { a } {
|
||||
//~^ match_single_binding
|
||||
_ => (),
|
||||
},
|
||||
(),
|
||||
);
|
||||
|
||||
struct Foo;
|
||||
impl Foo {
|
||||
fn method(&self, x: (), y: (), z: ()) {}
|
||||
}
|
||||
let x = Foo;
|
||||
x.method(
|
||||
{ todo!() },
|
||||
match { a } {
|
||||
//~^ match_single_binding
|
||||
_ => (),
|
||||
},
|
||||
(),
|
||||
);
|
||||
|
||||
-match { a } {
|
||||
//~^ match_single_binding
|
||||
_ => 1,
|
||||
};
|
||||
|
||||
_ = match { a } {
|
||||
//~^ match_single_binding
|
||||
_ => 1,
|
||||
};
|
||||
|
||||
if let x = match { a } {
|
||||
//~^ match_single_binding
|
||||
_ => 1,
|
||||
} {}
|
||||
|
||||
if match { a } {
|
||||
//~^ match_single_binding
|
||||
_ => true,
|
||||
} {
|
||||
todo!()
|
||||
}
|
||||
|
||||
[1, 2, 3][match { a } {
|
||||
//~^ match_single_binding
|
||||
_ => 1usize,
|
||||
}];
|
||||
|
||||
todo!()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -525,5 +525,161 @@ LL | | (a, b) => b < c,
|
|||
LL | | }
|
||||
| |_________^ help: consider using the match body instead: `b < c`
|
||||
|
||||
error: aborting due to 37 previous errors
|
||||
error: this match could be replaced by its scrutinee and body
|
||||
--> tests/ui/match_single_binding.rs:357:9
|
||||
|
|
||||
LL | / match { a } {
|
||||
LL | |
|
||||
LL | | _ => (),
|
||||
LL | | },
|
||||
| |_________^
|
||||
|
|
||||
help: consider using the scrutinee and body instead
|
||||
|
|
||||
LL ~ {
|
||||
LL + { a };
|
||||
LL + ()
|
||||
LL ~ },
|
||||
|
|
||||
|
||||
error: this match could be replaced by its scrutinee and body
|
||||
--> tests/ui/match_single_binding.rs:366:9
|
||||
|
|
||||
LL | / match { a } {
|
||||
LL | |
|
||||
LL | | _ => (),
|
||||
LL | | },
|
||||
| |_________^
|
||||
|
|
||||
help: consider using the scrutinee and body instead
|
||||
|
|
||||
LL ~ {
|
||||
LL + { a };
|
||||
LL + ()
|
||||
LL ~ },
|
||||
|
|
||||
|
||||
error: this match could be replaced by its scrutinee and body
|
||||
--> tests/ui/match_single_binding.rs:376:9
|
||||
|
|
||||
LL | / match { a } {
|
||||
LL | |
|
||||
LL | | _ => (),
|
||||
LL | | },
|
||||
| |_________^
|
||||
|
|
||||
help: consider using the scrutinee and body instead
|
||||
|
|
||||
LL ~ {
|
||||
LL + { a };
|
||||
LL + ()
|
||||
LL ~ },
|
||||
|
|
||||
|
||||
error: this match could be replaced by its scrutinee and body
|
||||
--> tests/ui/match_single_binding.rs:390:9
|
||||
|
|
||||
LL | / match { a } {
|
||||
LL | |
|
||||
LL | | _ => (),
|
||||
LL | | },
|
||||
| |_________^
|
||||
|
|
||||
help: consider using the scrutinee and body instead
|
||||
|
|
||||
LL ~ {
|
||||
LL + { a };
|
||||
LL + ()
|
||||
LL ~ },
|
||||
|
|
||||
|
||||
error: this match could be replaced by its scrutinee and body
|
||||
--> tests/ui/match_single_binding.rs:397:6
|
||||
|
|
||||
LL | -match { a } {
|
||||
| ______^
|
||||
LL | |
|
||||
LL | | _ => 1,
|
||||
LL | | };
|
||||
| |_____^
|
||||
|
|
||||
help: consider using the scrutinee and body instead
|
||||
|
|
||||
LL ~ -{
|
||||
LL + { a };
|
||||
LL + 1
|
||||
LL ~ };
|
||||
|
|
||||
|
||||
error: this match could be replaced by its scrutinee and body
|
||||
--> tests/ui/match_single_binding.rs:402:9
|
||||
|
|
||||
LL | _ = match { a } {
|
||||
| _________^
|
||||
LL | |
|
||||
LL | | _ => 1,
|
||||
LL | | };
|
||||
| |_____^
|
||||
|
|
||||
help: consider using the scrutinee and body instead
|
||||
|
|
||||
LL ~ _ = { a };
|
||||
LL ~ 1;
|
||||
|
|
||||
|
||||
error: this match could be replaced by its scrutinee and body
|
||||
--> tests/ui/match_single_binding.rs:407:16
|
||||
|
|
||||
LL | if let x = match { a } {
|
||||
| ________________^
|
||||
LL | |
|
||||
LL | | _ => 1,
|
||||
LL | | } {}
|
||||
| |_____^
|
||||
|
|
||||
help: consider using the scrutinee and body instead
|
||||
|
|
||||
LL ~ if let x = {
|
||||
LL + { a };
|
||||
LL + 1
|
||||
LL ~ } {}
|
||||
|
|
||||
|
||||
error: this match could be replaced by its scrutinee and body
|
||||
--> tests/ui/match_single_binding.rs:412:8
|
||||
|
|
||||
LL | if match { a } {
|
||||
| ________^
|
||||
LL | |
|
||||
LL | | _ => true,
|
||||
LL | | } {
|
||||
| |_____^
|
||||
|
|
||||
help: consider using the scrutinee and body instead
|
||||
|
|
||||
LL ~ if {
|
||||
LL + { a };
|
||||
LL + true
|
||||
LL ~ } {
|
||||
|
|
||||
|
||||
error: this match could be replaced by its scrutinee and body
|
||||
--> tests/ui/match_single_binding.rs:419:15
|
||||
|
|
||||
LL | [1, 2, 3][match { a } {
|
||||
| _______________^
|
||||
LL | |
|
||||
LL | | _ => 1usize,
|
||||
LL | | }];
|
||||
| |_____^
|
||||
|
|
||||
help: consider using the scrutinee and body instead
|
||||
|
|
||||
LL ~ [1, 2, 3][{
|
||||
LL + { a };
|
||||
LL + 1usize
|
||||
LL ~ }];
|
||||
|
|
||||
|
||||
error: aborting due to 46 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -150,9 +150,9 @@ fn highest_index_first(v1: &[u8]) {
|
|||
}
|
||||
|
||||
fn issue14255(v1: &[u8], v2: &[u8], v3: &[u8], v4: &[u8]) {
|
||||
assert!(v1.len() == 3);
|
||||
assert_eq!(v1.len(), 3);
|
||||
assert_eq!(v2.len(), 4);
|
||||
assert!(v3.len() == 3);
|
||||
assert_eq!(v3.len(), 3);
|
||||
assert_eq!(4, v4.len());
|
||||
|
||||
let _ = v1[0] + v1[1] + v1[2];
|
||||
|
|
@ -166,4 +166,18 @@ fn issue14255(v1: &[u8], v2: &[u8], v3: &[u8], v4: &[u8]) {
|
|||
let _ = v4[0] + v4[1] + v4[2];
|
||||
}
|
||||
|
||||
mod issue15988 {
|
||||
fn assert_eq_len(v: &[i32]) {
|
||||
assert_eq!(v.len(), 3);
|
||||
let _ = v[0] + v[1] + v[2];
|
||||
//~^ missing_asserts_for_indexing
|
||||
}
|
||||
|
||||
fn debug_assert_eq_len(v: &[i32]) {
|
||||
debug_assert_eq!(v.len(), 3);
|
||||
let _ = v[0] + v[1] + v[2];
|
||||
//~^ missing_asserts_for_indexing
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
|||
|
|
@ -166,4 +166,18 @@ fn issue14255(v1: &[u8], v2: &[u8], v3: &[u8], v4: &[u8]) {
|
|||
let _ = v4[0] + v4[1] + v4[2];
|
||||
}
|
||||
|
||||
mod issue15988 {
|
||||
fn assert_eq_len(v: &[i32]) {
|
||||
assert_eq!(v.len(), 2);
|
||||
let _ = v[0] + v[1] + v[2];
|
||||
//~^ missing_asserts_for_indexing
|
||||
}
|
||||
|
||||
fn debug_assert_eq_len(v: &[i32]) {
|
||||
debug_assert_eq!(v.len(), 2);
|
||||
let _ = v[0] + v[1] + v[2];
|
||||
//~^ missing_asserts_for_indexing
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
|||
|
|
@ -305,7 +305,7 @@ error: indexing into a slice multiple times with an `assert` that does not cover
|
|||
--> tests/ui/missing_asserts_for_indexing.rs:158:13
|
||||
|
|
||||
LL | assert_eq!(v1.len(), 2);
|
||||
| ----------------------- help: provide the highest index that is indexed with: `assert!(v1.len() == 3)`
|
||||
| ----------------------- help: provide the highest index that is indexed with: `assert_eq!(v1.len(), 3)`
|
||||
...
|
||||
LL | let _ = v1[0] + v1[1] + v1[2];
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -331,7 +331,7 @@ error: indexing into a slice multiple times with an `assert` that does not cover
|
|||
--> tests/ui/missing_asserts_for_indexing.rs:163:13
|
||||
|
|
||||
LL | assert_eq!(2, v3.len());
|
||||
| ----------------------- help: provide the highest index that is indexed with: `assert!(v3.len() == 3)`
|
||||
| ----------------------- help: provide the highest index that is indexed with: `assert_eq!(v3.len(), 3)`
|
||||
...
|
||||
LL | let _ = v3[0] + v3[1] + v3[2];
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -353,5 +353,55 @@ LL | let _ = v3[0] + v3[1] + v3[2];
|
|||
| ^^^^^
|
||||
= note: asserting the length before indexing will elide bounds checks
|
||||
|
||||
error: aborting due to 13 previous errors
|
||||
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
|
||||
--> tests/ui/missing_asserts_for_indexing.rs:172:17
|
||||
|
|
||||
LL | assert_eq!(v.len(), 2);
|
||||
| ---------------------- help: provide the highest index that is indexed with: `assert_eq!(v.len(), 3)`
|
||||
LL | let _ = v[0] + v[1] + v[2];
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: slice indexed here
|
||||
--> tests/ui/missing_asserts_for_indexing.rs:172:17
|
||||
|
|
||||
LL | let _ = v[0] + v[1] + v[2];
|
||||
| ^^^^
|
||||
note: slice indexed here
|
||||
--> tests/ui/missing_asserts_for_indexing.rs:172:24
|
||||
|
|
||||
LL | let _ = v[0] + v[1] + v[2];
|
||||
| ^^^^
|
||||
note: slice indexed here
|
||||
--> tests/ui/missing_asserts_for_indexing.rs:172:31
|
||||
|
|
||||
LL | let _ = v[0] + v[1] + v[2];
|
||||
| ^^^^
|
||||
= note: asserting the length before indexing will elide bounds checks
|
||||
|
||||
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
|
||||
--> tests/ui/missing_asserts_for_indexing.rs:178:17
|
||||
|
|
||||
LL | debug_assert_eq!(v.len(), 2);
|
||||
| ---------------------------- help: provide the highest index that is indexed with: `debug_assert_eq!(v.len(), 3)`
|
||||
LL | let _ = v[0] + v[1] + v[2];
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: slice indexed here
|
||||
--> tests/ui/missing_asserts_for_indexing.rs:178:17
|
||||
|
|
||||
LL | let _ = v[0] + v[1] + v[2];
|
||||
| ^^^^
|
||||
note: slice indexed here
|
||||
--> tests/ui/missing_asserts_for_indexing.rs:178:24
|
||||
|
|
||||
LL | let _ = v[0] + v[1] + v[2];
|
||||
| ^^^^
|
||||
note: slice indexed here
|
||||
--> tests/ui/missing_asserts_for_indexing.rs:178:31
|
||||
|
|
||||
LL | let _ = v[0] + v[1] + v[2];
|
||||
| ^^^^
|
||||
= note: asserting the length before indexing will elide bounds checks
|
||||
|
||||
error: aborting due to 15 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
//@ check-pass
|
||||
|
||||
#![warn(clippy::missing_inline_in_public_items)]
|
||||
|
||||
pub fn foo() {}
|
||||
//~^ missing_inline_in_public_items
|
||||
|
||||
fn main() {}
|
||||
|
|
|
|||
11
tests/ui/missing_inline_executable.stderr
Normal file
11
tests/ui/missing_inline_executable.stderr
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
error: missing `#[inline]` for a function
|
||||
--> tests/ui/missing_inline_executable.rs:3:1
|
||||
|
|
||||
LL | pub fn foo() {}
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::missing-inline-in-public-items` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::missing_inline_in_public_items)]`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
10
tests/ui/missing_inline_test_crate.rs
Normal file
10
tests/ui/missing_inline_test_crate.rs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
//@compile-flags: --test
|
||||
//@check-pass
|
||||
#![warn(clippy::missing_inline_in_public_items)]
|
||||
|
||||
#[expect(clippy::missing_inline_in_public_items)]
|
||||
pub fn foo() -> u32 {
|
||||
0
|
||||
}
|
||||
|
||||
fn private_function() {}
|
||||
|
|
@ -13,8 +13,7 @@ impl A {}
|
|||
//~^ multiple_inherent_impl
|
||||
|
||||
#[cfg(test)]
|
||||
impl A {} // false positive
|
||||
//~^ multiple_inherent_impl
|
||||
impl A {}
|
||||
|
||||
#[cfg(test)]
|
||||
impl A {}
|
||||
|
|
@ -25,8 +24,7 @@ struct B;
|
|||
impl B {}
|
||||
|
||||
#[cfg(test)]
|
||||
impl B {} // false positive
|
||||
//~^ multiple_inherent_impl
|
||||
impl B {}
|
||||
|
||||
impl B {}
|
||||
//~^ multiple_inherent_impl
|
||||
|
|
|
|||
|
|
@ -16,76 +16,52 @@ LL | #![deny(clippy::multiple_inherent_impl)]
|
|||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: multiple implementations of this structure
|
||||
--> tests/ui/multiple_inherent_impl_cfg.rs:19:1
|
||||
|
|
||||
LL | impl A {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
note: first implementation here
|
||||
--> tests/ui/multiple_inherent_impl_cfg.rs:16:1
|
||||
|
|
||||
LL | impl A {} // false positive
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
note: first implementation here
|
||||
--> tests/ui/multiple_inherent_impl_cfg.rs:10:1
|
||||
|
|
||||
LL | impl A {}
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: multiple implementations of this structure
|
||||
--> tests/ui/multiple_inherent_impl_cfg.rs:20:1
|
||||
|
|
||||
LL | impl A {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
note: first implementation here
|
||||
--> tests/ui/multiple_inherent_impl_cfg.rs:10:1
|
||||
|
|
||||
LL | impl A {}
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: multiple implementations of this structure
|
||||
--> tests/ui/multiple_inherent_impl_cfg.rs:28:1
|
||||
|
|
||||
LL | impl B {} // false positive
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
note: first implementation here
|
||||
--> tests/ui/multiple_inherent_impl_cfg.rs:25:1
|
||||
|
|
||||
LL | impl B {}
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: multiple implementations of this structure
|
||||
--> tests/ui/multiple_inherent_impl_cfg.rs:31:1
|
||||
--> tests/ui/multiple_inherent_impl_cfg.rs:29:1
|
||||
|
|
||||
LL | impl B {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
note: first implementation here
|
||||
--> tests/ui/multiple_inherent_impl_cfg.rs:25:1
|
||||
--> tests/ui/multiple_inherent_impl_cfg.rs:24:1
|
||||
|
|
||||
LL | impl B {}
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: multiple implementations of this structure
|
||||
--> tests/ui/multiple_inherent_impl_cfg.rs:35:1
|
||||
--> tests/ui/multiple_inherent_impl_cfg.rs:33:1
|
||||
|
|
||||
LL | impl B {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
note: first implementation here
|
||||
--> tests/ui/multiple_inherent_impl_cfg.rs:25:1
|
||||
--> tests/ui/multiple_inherent_impl_cfg.rs:27:1
|
||||
|
|
||||
LL | impl B {}
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: multiple implementations of this structure
|
||||
--> tests/ui/multiple_inherent_impl_cfg.rs:45:1
|
||||
--> tests/ui/multiple_inherent_impl_cfg.rs:43:1
|
||||
|
|
||||
LL | impl C {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
note: first implementation here
|
||||
--> tests/ui/multiple_inherent_impl_cfg.rs:42:1
|
||||
--> tests/ui/multiple_inherent_impl_cfg.rs:40:1
|
||||
|
|
||||
LL | impl C {}
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: aborting due to 7 previous errors
|
||||
error: aborting due to 5 previous errors
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue