Apply collapsible_if to Clippy itself
Since Clippy uses the `let_chains` feature, there are many occasions to collapse `if` and `if let` statements.
This commit is contained in:
parent
cd70152470
commit
79c69112dc
135 changed files with 1870 additions and 1913 deletions
|
|
@ -402,53 +402,53 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io
|
|||
}
|
||||
}
|
||||
|
||||
if path.exists() {
|
||||
if let Some(lint) = lints.iter().find(|l| l.name == name) {
|
||||
if lint.module == name {
|
||||
// The lint name is the same as the file, we can just delete the entire file
|
||||
fs::remove_file(path)?;
|
||||
} else {
|
||||
// We can't delete the entire file, just remove the declaration
|
||||
if path.exists()
|
||||
&& let Some(lint) = lints.iter().find(|l| l.name == name)
|
||||
{
|
||||
if lint.module == name {
|
||||
// The lint name is the same as the file, we can just delete the entire file
|
||||
fs::remove_file(path)?;
|
||||
} else {
|
||||
// We can't delete the entire file, just remove the declaration
|
||||
|
||||
if let Some(Some("mod.rs")) = path.file_name().map(OsStr::to_str) {
|
||||
// Remove clippy_lints/src/some_mod/some_lint.rs
|
||||
let mut lint_mod_path = path.to_path_buf();
|
||||
lint_mod_path.set_file_name(name);
|
||||
lint_mod_path.set_extension("rs");
|
||||
if let Some(Some("mod.rs")) = path.file_name().map(OsStr::to_str) {
|
||||
// Remove clippy_lints/src/some_mod/some_lint.rs
|
||||
let mut lint_mod_path = path.to_path_buf();
|
||||
lint_mod_path.set_file_name(name);
|
||||
lint_mod_path.set_extension("rs");
|
||||
|
||||
let _ = fs::remove_file(lint_mod_path);
|
||||
}
|
||||
|
||||
let mut content =
|
||||
fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy()));
|
||||
|
||||
eprintln!(
|
||||
"warn: you will have to manually remove any code related to `{name}` from `{}`",
|
||||
path.display()
|
||||
);
|
||||
|
||||
assert!(
|
||||
content[lint.declaration_range.clone()].contains(&name.to_uppercase()),
|
||||
"error: `{}` does not contain lint `{}`'s declaration",
|
||||
path.display(),
|
||||
lint.name
|
||||
);
|
||||
|
||||
// Remove lint declaration (declare_clippy_lint!)
|
||||
content.replace_range(lint.declaration_range.clone(), "");
|
||||
|
||||
// Remove the module declaration (mod xyz;)
|
||||
let mod_decl = format!("\nmod {name};");
|
||||
content = content.replacen(&mod_decl, "", 1);
|
||||
|
||||
remove_impl_lint_pass(&lint.name.to_uppercase(), &mut content);
|
||||
fs::write(path, content).unwrap_or_else(|_| panic!("failed to write to `{}`", path.to_string_lossy()));
|
||||
let _ = fs::remove_file(lint_mod_path);
|
||||
}
|
||||
|
||||
remove_test_assets(name);
|
||||
remove_lint(name, lints);
|
||||
return Ok(true);
|
||||
let mut content =
|
||||
fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy()));
|
||||
|
||||
eprintln!(
|
||||
"warn: you will have to manually remove any code related to `{name}` from `{}`",
|
||||
path.display()
|
||||
);
|
||||
|
||||
assert!(
|
||||
content[lint.declaration_range.clone()].contains(&name.to_uppercase()),
|
||||
"error: `{}` does not contain lint `{}`'s declaration",
|
||||
path.display(),
|
||||
lint.name
|
||||
);
|
||||
|
||||
// Remove lint declaration (declare_clippy_lint!)
|
||||
content.replace_range(lint.declaration_range.clone(), "");
|
||||
|
||||
// Remove the module declaration (mod xyz;)
|
||||
let mod_decl = format!("\nmod {name};");
|
||||
content = content.replacen(&mod_decl, "", 1);
|
||||
|
||||
remove_impl_lint_pass(&lint.name.to_uppercase(), &mut content);
|
||||
fs::write(path, content).unwrap_or_else(|_| panic!("failed to write to `{}`", path.to_string_lossy()));
|
||||
}
|
||||
|
||||
remove_test_assets(name);
|
||||
remove_lint(name, lints);
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
|
|
|
|||
|
|
@ -30,10 +30,10 @@ pub fn clippy_project_root() -> PathBuf {
|
|||
let current_dir = std::env::current_dir().unwrap();
|
||||
for path in current_dir.ancestors() {
|
||||
let result = fs::read_to_string(path.join("Cargo.toml"));
|
||||
if let Err(err) = &result {
|
||||
if err.kind() == io::ErrorKind::NotFound {
|
||||
continue;
|
||||
}
|
||||
if let Err(err) = &result
|
||||
&& err.kind() == io::ErrorKind::NotFound
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let content = result.unwrap();
|
||||
|
|
|
|||
|
|
@ -263,10 +263,11 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering {
|
|||
continue;
|
||||
}
|
||||
|
||||
if let Some(cur_v) = cur_v {
|
||||
if cur_v.ident.name.as_str() > variant.ident.name.as_str() && cur_v.span != variant.span {
|
||||
Self::lint_member_name(cx, &variant.ident, &cur_v.ident);
|
||||
}
|
||||
if let Some(cur_v) = cur_v
|
||||
&& cur_v.ident.name.as_str() > variant.ident.name.as_str()
|
||||
&& cur_v.span != variant.span
|
||||
{
|
||||
Self::lint_member_name(cx, &variant.ident, &cur_v.ident);
|
||||
}
|
||||
cur_v = Some(variant);
|
||||
}
|
||||
|
|
@ -278,10 +279,11 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering {
|
|||
continue;
|
||||
}
|
||||
|
||||
if let Some(cur_f) = cur_f {
|
||||
if cur_f.ident.name.as_str() > field.ident.name.as_str() && cur_f.span != field.span {
|
||||
Self::lint_member_name(cx, &field.ident, &cur_f.ident);
|
||||
}
|
||||
if let Some(cur_f) = cur_f
|
||||
&& cur_f.ident.name.as_str() > field.ident.name.as_str()
|
||||
&& cur_f.span != field.span
|
||||
{
|
||||
Self::lint_member_name(cx, &field.ident, &cur_f.ident);
|
||||
}
|
||||
cur_f = Some(field);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,17 +8,18 @@ use rustc_span::{DUMMY_SP, sym};
|
|||
|
||||
pub(super) fn check(cx: &EarlyContext<'_>, name: Symbol, items: &[MetaItemInner]) {
|
||||
for lint in items {
|
||||
if let Some(lint_name) = extract_clippy_lint(lint) {
|
||||
if lint_name.as_str() == "restriction" && name != sym::allow {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
BLANKET_CLIPPY_RESTRICTION_LINTS,
|
||||
lint.span(),
|
||||
"`clippy::restriction` is not meant to be enabled as a group",
|
||||
None,
|
||||
"enable the restriction lints you need individually",
|
||||
);
|
||||
}
|
||||
if let Some(lint_name) = extract_clippy_lint(lint)
|
||||
&& lint_name.as_str() == "restriction"
|
||||
&& name != sym::allow
|
||||
{
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
BLANKET_CLIPPY_RESTRICTION_LINTS,
|
||||
lint.span(),
|
||||
"`clippy::restriction` is not meant to be enabled as a group",
|
||||
None,
|
||||
"enable the restriction lints you need individually",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ use rustc_span::Span;
|
|||
use semver::Version;
|
||||
|
||||
pub(super) fn check(cx: &EarlyContext<'_>, span: Span, lit: &MetaItemLit) {
|
||||
if let LitKind::Str(is, _) = lit.kind {
|
||||
if is.as_str() == "TBD" || Version::parse(is.as_str()).is_ok() {
|
||||
return;
|
||||
}
|
||||
if let LitKind::Str(is, _) = lit.kind
|
||||
&& (is.as_str() == "TBD" || Version::parse(is.as_str()).is_ok())
|
||||
{
|
||||
return;
|
||||
}
|
||||
span_lint(
|
||||
cx,
|
||||
|
|
|
|||
|
|
@ -573,28 +573,27 @@ impl EarlyLintPass for PostExpansionEarlyAttributes {
|
|||
}
|
||||
|
||||
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
|
||||
if let Some(items) = &attr.meta_item_list() {
|
||||
if let Some(ident) = attr.ident() {
|
||||
if matches!(ident.name, sym::allow) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) {
|
||||
allow_attributes::check(cx, attr);
|
||||
}
|
||||
if matches!(ident.name, sym::allow | sym::expect) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION)
|
||||
if let Some(items) = &attr.meta_item_list()
|
||||
&& let Some(ident) = attr.ident()
|
||||
{
|
||||
if matches!(ident.name, sym::allow) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) {
|
||||
allow_attributes::check(cx, attr);
|
||||
}
|
||||
if matches!(ident.name, sym::allow | sym::expect) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) {
|
||||
allow_attributes_without_reason::check(cx, ident.name, items, attr);
|
||||
}
|
||||
if is_lint_level(ident.name, attr.id) {
|
||||
blanket_clippy_restriction_lints::check(cx, ident.name, items);
|
||||
}
|
||||
if items.is_empty() || !attr.has_name(sym::deprecated) {
|
||||
return;
|
||||
}
|
||||
for item in items {
|
||||
if let MetaItemInner::MetaItem(mi) = &item
|
||||
&& let MetaItemKind::NameValue(lit) = &mi.kind
|
||||
&& mi.has_name(sym::since)
|
||||
{
|
||||
allow_attributes_without_reason::check(cx, ident.name, items, attr);
|
||||
}
|
||||
if is_lint_level(ident.name, attr.id) {
|
||||
blanket_clippy_restriction_lints::check(cx, ident.name, items);
|
||||
}
|
||||
if items.is_empty() || !attr.has_name(sym::deprecated) {
|
||||
return;
|
||||
}
|
||||
for item in items {
|
||||
if let MetaItemInner::MetaItem(mi) = &item
|
||||
&& let MetaItemKind::NameValue(lit) = &mi.kind
|
||||
&& mi.has_name(sym::since)
|
||||
{
|
||||
deprecated_semver::check(cx, item.span(), lit);
|
||||
}
|
||||
deprecated_semver::check(cx, item.span(), lit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,75 +14,75 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) {
|
|||
if attr.span.in_external_macro(cx.sess().source_map()) {
|
||||
return;
|
||||
}
|
||||
if let Some(lint_list) = &attr.meta_item_list() {
|
||||
if attr.ident().is_some_and(|ident| is_lint_level(ident.name, attr.id)) {
|
||||
for lint in lint_list {
|
||||
match item.kind {
|
||||
ItemKind::Use(..) => {
|
||||
let (namespace @ (Some(sym::clippy) | None), Some(name)) = namespace_and_lint(lint) else {
|
||||
return;
|
||||
};
|
||||
if let Some(lint_list) = &attr.meta_item_list()
|
||||
&& attr.ident().is_some_and(|ident| is_lint_level(ident.name, attr.id))
|
||||
{
|
||||
for lint in lint_list {
|
||||
match item.kind {
|
||||
ItemKind::Use(..) => {
|
||||
let (namespace @ (Some(sym::clippy) | None), Some(name)) = namespace_and_lint(lint) else {
|
||||
return;
|
||||
};
|
||||
|
||||
if namespace.is_none()
|
||||
&& matches!(
|
||||
name.as_str(),
|
||||
"ambiguous_glob_reexports"
|
||||
| "dead_code"
|
||||
| "deprecated"
|
||||
| "hidden_glob_reexports"
|
||||
| "unreachable_pub"
|
||||
| "unused"
|
||||
| "unused_braces"
|
||||
| "unused_import_braces"
|
||||
| "unused_imports"
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if namespace.is_none()
|
||||
&& matches!(
|
||||
name.as_str(),
|
||||
"ambiguous_glob_reexports"
|
||||
| "dead_code"
|
||||
| "deprecated"
|
||||
| "hidden_glob_reexports"
|
||||
| "unreachable_pub"
|
||||
| "unused"
|
||||
| "unused_braces"
|
||||
| "unused_import_braces"
|
||||
| "unused_imports"
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if namespace == Some(sym::clippy)
|
||||
&& matches!(
|
||||
name.as_str(),
|
||||
"wildcard_imports"
|
||||
| "enum_glob_use"
|
||||
| "redundant_pub_crate"
|
||||
| "macro_use_imports"
|
||||
| "unsafe_removed_from_name"
|
||||
| "module_name_repetitions"
|
||||
| "single_component_path_imports"
|
||||
| "disallowed_types"
|
||||
| "unused_trait_names"
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
},
|
||||
ItemKind::ExternCrate(..) => {
|
||||
if is_word(lint, sym::unused_imports) && skip_unused_imports {
|
||||
return;
|
||||
}
|
||||
if is_word(lint, sym!(unused_extern_crates)) {
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
if namespace == Some(sym::clippy)
|
||||
&& matches!(
|
||||
name.as_str(),
|
||||
"wildcard_imports"
|
||||
| "enum_glob_use"
|
||||
| "redundant_pub_crate"
|
||||
| "macro_use_imports"
|
||||
| "unsafe_removed_from_name"
|
||||
| "module_name_repetitions"
|
||||
| "single_component_path_imports"
|
||||
| "disallowed_types"
|
||||
| "unused_trait_names"
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
},
|
||||
ItemKind::ExternCrate(..) => {
|
||||
if is_word(lint, sym::unused_imports) && skip_unused_imports {
|
||||
return;
|
||||
}
|
||||
if is_word(lint, sym!(unused_extern_crates)) {
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
let line_span = first_line_of_span(cx, attr.span);
|
||||
}
|
||||
let line_span = first_line_of_span(cx, attr.span);
|
||||
|
||||
if let Some(src) = line_span.get_source_text(cx) {
|
||||
if src.contains("#[") {
|
||||
#[expect(clippy::collapsible_span_lint_calls)]
|
||||
span_lint_and_then(cx, USELESS_ATTRIBUTE, line_span, "useless lint attribute", |diag| {
|
||||
diag.span_suggestion(
|
||||
line_span,
|
||||
"if you just forgot a `!`, use",
|
||||
src.replacen("#[", "#![", 1),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
if let Some(src) = line_span.get_source_text(cx)
|
||||
&& src.contains("#[")
|
||||
{
|
||||
#[expect(clippy::collapsible_span_lint_calls)]
|
||||
span_lint_and_then(cx, USELESS_ATTRIBUTE, line_span, "useless lint attribute", |diag| {
|
||||
diag.span_suggestion(
|
||||
line_span,
|
||||
"if you just forgot a `!`, use",
|
||||
src.replacen("#[", "#![", 1),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -192,10 +192,9 @@ impl<'tcx> LateLintPass<'tcx> for AwaitHolding {
|
|||
def_id,
|
||||
..
|
||||
}) = expr.kind
|
||||
&& let Some(coroutine_layout) = cx.tcx.mir_coroutine_witnesses(*def_id)
|
||||
{
|
||||
if let Some(coroutine_layout) = cx.tcx.mir_coroutine_witnesses(*def_id) {
|
||||
self.check_interior_types(cx, coroutine_layout);
|
||||
}
|
||||
self.check_interior_types(cx, coroutine_layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -242,11 +242,11 @@ struct Hir2Qmm<'a, 'tcx, 'v> {
|
|||
impl<'v> Hir2Qmm<'_, '_, 'v> {
|
||||
fn extract(&mut self, op: BinOpKind, a: &[&'v Expr<'_>], mut v: Vec<Bool>) -> Result<Vec<Bool>, String> {
|
||||
for a in a {
|
||||
if let ExprKind::Binary(binop, lhs, rhs) = &a.kind {
|
||||
if binop.node == op {
|
||||
v = self.extract(op, &[lhs, rhs], v)?;
|
||||
continue;
|
||||
}
|
||||
if let ExprKind::Binary(binop, lhs, rhs) = &a.kind
|
||||
&& binop.node == op
|
||||
{
|
||||
v = self.extract(op, &[lhs, rhs], v)?;
|
||||
continue;
|
||||
}
|
||||
v.push(self.run(a)?);
|
||||
}
|
||||
|
|
@ -418,12 +418,12 @@ fn simplify_not(cx: &LateContext<'_>, curr_msrv: Msrv, expr: &Expr<'_>) -> Optio
|
|||
let lhs_snippet = lhs.span.get_source_text(cx)?;
|
||||
let rhs_snippet = rhs.span.get_source_text(cx)?;
|
||||
|
||||
if !(lhs_snippet.starts_with('(') && lhs_snippet.ends_with(')')) {
|
||||
if 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}"));
|
||||
}
|
||||
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}"));
|
||||
}
|
||||
|
||||
Some(format!("{lhs_snippet}{op}{rhs_snippet}"))
|
||||
|
|
|
|||
|
|
@ -93,10 +93,10 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef {
|
|||
|
||||
// has deref trait -> give 2 help
|
||||
// doesn't have deref trait -> give 1 help
|
||||
if let Some(deref_trait_id) = cx.tcx.lang_items().deref_trait() {
|
||||
if !implements_trait(cx, *inner_ty, deref_trait_id, &[]) {
|
||||
return;
|
||||
}
|
||||
if let Some(deref_trait_id) = cx.tcx.lang_items().deref_trait()
|
||||
&& !implements_trait(cx, *inner_ty, deref_trait_id, &[])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
diag.span_suggestion(
|
||||
|
|
|
|||
|
|
@ -64,11 +64,11 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b
|
|||
apply_reductions(cx, nbits, left, signed).min(max_bits.unwrap_or(u64::MAX))
|
||||
},
|
||||
ExprKind::MethodCall(method, _, [lo, hi], _) => {
|
||||
if method.ident.as_str() == "clamp" {
|
||||
if method.ident.as_str() == "clamp"
|
||||
//FIXME: make this a diagnostic item
|
||||
if let (Some(lo_bits), Some(hi_bits)) = (get_constant_bits(cx, lo), get_constant_bits(cx, hi)) {
|
||||
return lo_bits.max(hi_bits);
|
||||
}
|
||||
&& let (Some(lo_bits), Some(hi_bits)) = (get_constant_bits(cx, lo), get_constant_bits(cx, hi))
|
||||
{
|
||||
return lo_bits.max(hi_bits);
|
||||
}
|
||||
nbits
|
||||
},
|
||||
|
|
|
|||
|
|
@ -19,16 +19,15 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||
cx.typeck_results().expr_ty(expr),
|
||||
);
|
||||
lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
|
||||
} else if let ExprKind::MethodCall(method_path, self_arg, [], _) = &expr.kind {
|
||||
if method_path.ident.name.as_str() == "cast"
|
||||
&& let Some(generic_args) = method_path.args
|
||||
&& let [GenericArg::Type(cast_to)] = generic_args.args
|
||||
// There probably is no obvious reason to do this, just to be consistent with `as` cases.
|
||||
&& !is_hir_ty_cfg_dependant(cx, cast_to.as_unambig_ty())
|
||||
{
|
||||
let (cast_from, cast_to) = (cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr));
|
||||
lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
|
||||
}
|
||||
} else if let ExprKind::MethodCall(method_path, self_arg, [], _) = &expr.kind
|
||||
&& method_path.ident.name.as_str() == "cast"
|
||||
&& let Some(generic_args) = method_path.args
|
||||
&& let [GenericArg::Type(cast_to)] = generic_args.args
|
||||
// There probably is no obvious reason to do this, just to be consistent with `as` cases.
|
||||
&& !is_hir_ty_cfg_dependant(cx, cast_to.as_unambig_ty())
|
||||
{
|
||||
let (cast_from, cast_to) = (cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr));
|
||||
lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,42 +21,41 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Msrv)
|
|||
start_ty,
|
||||
end_ty,
|
||||
}) = expr_cast_chain_tys(cx, expr)
|
||||
&& let (Ok(from_layout), Ok(to_layout)) = (cx.layout_of(start_ty.ty), cx.layout_of(end_ty.ty))
|
||||
{
|
||||
if let (Ok(from_layout), Ok(to_layout)) = (cx.layout_of(start_ty.ty), cx.layout_of(end_ty.ty)) {
|
||||
let from_size = from_layout.size.bytes();
|
||||
let to_size = to_layout.size.bytes();
|
||||
if from_size != to_size && from_size != 0 && to_size != 0 && msrv.meets(cx, msrvs::PTR_SLICE_RAW_PARTS) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
CAST_SLICE_DIFFERENT_SIZES,
|
||||
expr.span,
|
||||
format!(
|
||||
"casting between raw pointers to `[{}]` (element size {from_size}) and `[{}]` (element size {to_size}) does not adjust the count",
|
||||
start_ty.ty, end_ty.ty,
|
||||
),
|
||||
|diag| {
|
||||
let ptr_snippet = source::snippet(cx, left_cast.span, "..");
|
||||
let from_size = from_layout.size.bytes();
|
||||
let to_size = to_layout.size.bytes();
|
||||
if from_size != to_size && from_size != 0 && to_size != 0 && msrv.meets(cx, msrvs::PTR_SLICE_RAW_PARTS) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
CAST_SLICE_DIFFERENT_SIZES,
|
||||
expr.span,
|
||||
format!(
|
||||
"casting between raw pointers to `[{}]` (element size {from_size}) and `[{}]` (element size {to_size}) does not adjust the count",
|
||||
start_ty.ty, end_ty.ty,
|
||||
),
|
||||
|diag| {
|
||||
let ptr_snippet = source::snippet(cx, left_cast.span, "..");
|
||||
|
||||
let (mutbl_fn_str, mutbl_ptr_str) = match end_ty.mutbl {
|
||||
Mutability::Mut => ("_mut", "mut"),
|
||||
Mutability::Not => ("", "const"),
|
||||
};
|
||||
let sugg = format!(
|
||||
"core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {}, ..)",
|
||||
// get just the ty from the TypeAndMut so that the printed type isn't something like `mut
|
||||
// T`, extract just the `T`
|
||||
end_ty.ty
|
||||
);
|
||||
let (mutbl_fn_str, mutbl_ptr_str) = match end_ty.mutbl {
|
||||
Mutability::Mut => ("_mut", "mut"),
|
||||
Mutability::Not => ("", "const"),
|
||||
};
|
||||
let sugg = format!(
|
||||
"core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {}, ..)",
|
||||
// get just the ty from the TypeAndMut so that the printed type isn't something like `mut
|
||||
// T`, extract just the `T`
|
||||
end_ty.ty
|
||||
);
|
||||
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"),
|
||||
sugg,
|
||||
rustc_errors::Applicability::HasPlaceholders,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"),
|
||||
sugg,
|
||||
rustc_errors::Applicability::HasPlaceholders,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,11 +130,11 @@ pub(super) fn check<'tcx>(
|
|||
| LitKind::Float(_, LitFloatType::Suffixed(_))
|
||||
if cast_from.kind() == cast_to.kind() =>
|
||||
{
|
||||
if let Some(src) = cast_expr.span.get_source_text(cx) {
|
||||
if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) {
|
||||
lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to);
|
||||
return true;
|
||||
}
|
||||
if let Some(src) = cast_expr.span.get_source_text(cx)
|
||||
&& let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node)
|
||||
{
|
||||
lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
|
|
|
|||
|
|
@ -253,11 +253,11 @@ fn get_types_from_cast<'a>(
|
|||
match limit.kind {
|
||||
// `from_type::from(_)`
|
||||
ExprKind::Call(path, _) => {
|
||||
if let ExprKind::Path(ref path) = path.kind {
|
||||
if let ExprKind::Path(ref path) = path.kind
|
||||
// `to_type`
|
||||
if let Some(to_type) = get_implementing_type(path, types, func) {
|
||||
return Some((from_type, to_type));
|
||||
}
|
||||
&& let Some(to_type) = get_implementing_type(path, types, func)
|
||||
{
|
||||
return Some((from_type, to_type));
|
||||
}
|
||||
},
|
||||
// `to_type::MAX`
|
||||
|
|
|
|||
|
|
@ -539,10 +539,10 @@ fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbo
|
|||
.filter(|stmt| !ignore_span.overlaps(stmt.span))
|
||||
.try_for_each(|stmt| intravisit::walk_stmt(&mut walker, stmt));
|
||||
|
||||
if let Some(expr) = block.expr {
|
||||
if res.is_continue() {
|
||||
res = intravisit::walk_expr(&mut walker, expr);
|
||||
}
|
||||
if let Some(expr) = block.expr
|
||||
&& res.is_continue()
|
||||
{
|
||||
res = intravisit::walk_expr(&mut walker, expr);
|
||||
}
|
||||
|
||||
res.is_break()
|
||||
|
|
|
|||
|
|
@ -1133,61 +1133,60 @@ fn report<'tcx>(
|
|||
|
||||
impl<'tcx> Dereferencing<'tcx> {
|
||||
fn check_local_usage(&mut self, cx: &LateContext<'tcx>, e: &Expr<'tcx>, local: HirId) {
|
||||
if let Some(outer_pat) = self.ref_locals.get_mut(&local) {
|
||||
if let Some(pat) = outer_pat {
|
||||
// Check for auto-deref
|
||||
if !matches!(
|
||||
cx.typeck_results().expr_adjustments(e),
|
||||
[
|
||||
Adjustment {
|
||||
kind: Adjust::Deref(_),
|
||||
..
|
||||
},
|
||||
Adjustment {
|
||||
kind: Adjust::Deref(_),
|
||||
..
|
||||
},
|
||||
if let Some(outer_pat) = self.ref_locals.get_mut(&local)
|
||||
&& let Some(pat) = outer_pat
|
||||
// Check for auto-deref
|
||||
&& !matches!(
|
||||
cx.typeck_results().expr_adjustments(e),
|
||||
[
|
||||
Adjustment {
|
||||
kind: Adjust::Deref(_),
|
||||
..
|
||||
]
|
||||
) {
|
||||
match get_parent_expr(cx, e) {
|
||||
// Field accesses are the same no matter the number of references.
|
||||
Some(Expr {
|
||||
kind: ExprKind::Field(..),
|
||||
..
|
||||
}) => (),
|
||||
Some(&Expr {
|
||||
span,
|
||||
kind: ExprKind::Unary(UnOp::Deref, _),
|
||||
..
|
||||
}) if !span.from_expansion() => {
|
||||
// Remove explicit deref.
|
||||
let snip = snippet_with_context(cx, e.span, span.ctxt(), "..", &mut pat.app).0;
|
||||
pat.replacements.push((span, snip.into()));
|
||||
},
|
||||
Some(parent) if !parent.span.from_expansion() => {
|
||||
// Double reference might be needed at this point.
|
||||
if parent.precedence() == ExprPrecedence::Unambiguous {
|
||||
// Parentheses would be needed here, don't lint.
|
||||
*outer_pat = None;
|
||||
} else {
|
||||
pat.always_deref = false;
|
||||
let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0;
|
||||
pat.replacements.push((e.span, format!("&{snip}")));
|
||||
}
|
||||
},
|
||||
_ if !e.span.from_expansion() => {
|
||||
// Double reference might be needed at this point.
|
||||
pat.always_deref = false;
|
||||
let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app);
|
||||
pat.replacements.push((e.span, format!("&{snip}")));
|
||||
},
|
||||
// Edge case for macros. The span of the identifier will usually match the context of the
|
||||
// binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc
|
||||
// macros
|
||||
_ => *outer_pat = None,
|
||||
},
|
||||
Adjustment {
|
||||
kind: Adjust::Deref(_),
|
||||
..
|
||||
},
|
||||
..
|
||||
]
|
||||
)
|
||||
{
|
||||
match get_parent_expr(cx, e) {
|
||||
// Field accesses are the same no matter the number of references.
|
||||
Some(Expr {
|
||||
kind: ExprKind::Field(..),
|
||||
..
|
||||
}) => (),
|
||||
Some(&Expr {
|
||||
span,
|
||||
kind: ExprKind::Unary(UnOp::Deref, _),
|
||||
..
|
||||
}) if !span.from_expansion() => {
|
||||
// Remove explicit deref.
|
||||
let snip = snippet_with_context(cx, e.span, span.ctxt(), "..", &mut pat.app).0;
|
||||
pat.replacements.push((span, snip.into()));
|
||||
},
|
||||
Some(parent) if !parent.span.from_expansion() => {
|
||||
// Double reference might be needed at this point.
|
||||
if parent.precedence() == ExprPrecedence::Unambiguous {
|
||||
// Parentheses would be needed here, don't lint.
|
||||
*outer_pat = None;
|
||||
} else {
|
||||
pat.always_deref = false;
|
||||
let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0;
|
||||
pat.replacements.push((e.span, format!("&{snip}")));
|
||||
}
|
||||
}
|
||||
},
|
||||
_ if !e.span.from_expansion() => {
|
||||
// Double reference might be needed at this point.
|
||||
pat.always_deref = false;
|
||||
let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app);
|
||||
pat.replacements.push((e.span, format!("&{snip}")));
|
||||
},
|
||||
// Edge case for macros. The span of the identifier will usually match the context of the
|
||||
// binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc
|
||||
// macros
|
||||
_ => *outer_pat = None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,18 +94,18 @@ fn check_struct<'tcx>(
|
|||
ty_args: GenericArgsRef<'_>,
|
||||
typeck_results: &'tcx TypeckResults<'tcx>,
|
||||
) {
|
||||
if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind {
|
||||
if let Some(PathSegment { args, .. }) = p.segments.last() {
|
||||
let args = args.map(|a| a.args).unwrap_or(&[]);
|
||||
if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind
|
||||
&& let Some(PathSegment { args, .. }) = p.segments.last()
|
||||
{
|
||||
let args = args.map(|a| a.args).unwrap_or(&[]);
|
||||
|
||||
// ty_args contains the generic parameters of the type declaration, while args contains the
|
||||
// arguments used at instantiation time. If both len are not equal, it means that some
|
||||
// parameters were not provided (which means that the default values were used); in this
|
||||
// case we will not risk suggesting too broad a rewrite. We won't either if any argument
|
||||
// is a type or a const.
|
||||
if ty_args.len() != args.len() || args.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_))) {
|
||||
return;
|
||||
}
|
||||
// ty_args contains the generic parameters of the type declaration, while args contains the
|
||||
// arguments used at instantiation time. If both len are not equal, it means that some
|
||||
// parameters were not provided (which means that the default values were used); in this
|
||||
// case we will not risk suggesting too broad a rewrite. We won't either if any argument
|
||||
// is a type or a const.
|
||||
if ty_args.len() != args.len() || args.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -428,10 +428,10 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
|
|||
}
|
||||
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) -> Self::Result {
|
||||
if let ExprKind::Block(block, _) = expr.kind {
|
||||
if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
if let ExprKind::Block(block, _) = expr.kind
|
||||
&& block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
|
||||
{
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
|
||||
walk_expr(self, expr)
|
||||
|
|
|
|||
|
|
@ -113,20 +113,20 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize, b
|
|||
s != "-" && s.contains('-')
|
||||
}
|
||||
|
||||
if let Ok(url) = Url::parse(word) {
|
||||
if let Ok(url) = Url::parse(word)
|
||||
// try to get around the fact that `foo::bar` parses as a valid URL
|
||||
if !url.cannot_be_a_base() {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
DOC_MARKDOWN,
|
||||
span,
|
||||
"you should put bare URLs between `<`/`>` or make a proper Markdown link",
|
||||
"try",
|
||||
format!("<{word}>"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
return;
|
||||
}
|
||||
&& !url.cannot_be_a_base()
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
DOC_MARKDOWN,
|
||||
span,
|
||||
"you should put bare URLs between `<`/`>` or make a proper Markdown link",
|
||||
"try",
|
||||
format!("<{word}>"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// We assume that mixed-case words are not meant to be put inside backticks. (Issue #2343)
|
||||
|
|
|
|||
|
|
@ -880,19 +880,18 @@ fn check_for_code_clusters<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a
|
|||
if let Some(start) = code_starts_at
|
||||
&& let Some(end) = code_ends_at
|
||||
&& code_includes_link
|
||||
&& let Some(span) = fragments.span(cx, start..end)
|
||||
{
|
||||
if let Some(span) = fragments.span(cx, start..end) {
|
||||
span_lint_and_then(cx, DOC_LINK_CODE, span, "code link adjacent to code text", |diag| {
|
||||
let sugg = format!("<code>{}</code>", doc[start..end].replace('`', ""));
|
||||
diag.span_suggestion_verbose(
|
||||
span,
|
||||
"wrap the entire group in `<code>` tags",
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
diag.help("separate code snippets will be shown with a gap");
|
||||
});
|
||||
}
|
||||
span_lint_and_then(cx, DOC_LINK_CODE, span, "code link adjacent to code text", |diag| {
|
||||
let sugg = format!("<code>{}</code>", doc[start..end].replace('`', ""));
|
||||
diag.span_suggestion_verbose(
|
||||
span,
|
||||
"wrap the entire group in `<code>` tags",
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
diag.help("separate code snippets will be shown with a gap");
|
||||
});
|
||||
}
|
||||
code_includes_link = false;
|
||||
code_starts_at = None;
|
||||
|
|
@ -1201,16 +1200,15 @@ impl<'tcx> Visitor<'tcx> for FindPanicUnwrap<'_, 'tcx> {
|
|||
return;
|
||||
}
|
||||
|
||||
if let Some(macro_call) = root_macro_call_first_node(self.cx, expr) {
|
||||
if is_panic(self.cx, macro_call.def_id)
|
||||
if let Some(macro_call) = root_macro_call_first_node(self.cx, expr)
|
||||
&& (is_panic(self.cx, macro_call.def_id)
|
||||
|| matches!(
|
||||
self.cx.tcx.item_name(macro_call.def_id).as_str(),
|
||||
"assert" | "assert_eq" | "assert_ne"
|
||||
)
|
||||
{
|
||||
self.is_const = self.cx.tcx.hir_is_inside_const_context(expr.hir_id);
|
||||
self.panic_span = Some(macro_call.span);
|
||||
}
|
||||
))
|
||||
{
|
||||
self.is_const = self.cx.tcx.hir_is_inside_const_context(expr.hir_id);
|
||||
self.panic_span = Some(macro_call.span);
|
||||
}
|
||||
|
||||
// check for `unwrap` and `expect` for both `Option` and `Result`
|
||||
|
|
|
|||
|
|
@ -144,10 +144,10 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
|
|||
// ..
|
||||
// }
|
||||
fn is_single_call_in_arm<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'_>, drop_expr: &'tcx Expr<'_>) -> bool {
|
||||
if matches!(arg.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)) {
|
||||
if let Node::Arm(Arm { body, .. }) = cx.tcx.parent_hir_node(drop_expr.hir_id) {
|
||||
return body.hir_id == drop_expr.hir_id;
|
||||
}
|
||||
if matches!(arg.kind, ExprKind::Call(..) | ExprKind::MethodCall(..))
|
||||
&& let Node::Arm(Arm { body, .. }) = cx.tcx.parent_hir_node(drop_expr.hir_id)
|
||||
{
|
||||
return body.hir_id == drop_expr.hir_id;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,10 +49,10 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant {
|
|||
.ok()
|
||||
.map(|val| rustc_middle::mir::Const::from_value(val, ty));
|
||||
if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx.tcx, c)) {
|
||||
if let ty::Adt(adt, _) = ty.kind() {
|
||||
if adt.is_enum() {
|
||||
ty = adt.repr().discr_type().to_ty(cx.tcx);
|
||||
}
|
||||
if let ty::Adt(adt, _) = ty.kind()
|
||||
&& adt.is_enum()
|
||||
{
|
||||
ty = adt.repr().discr_type().to_ty(cx.tcx);
|
||||
}
|
||||
match ty.kind() {
|
||||
ty::Int(IntTy::Isize) => {
|
||||
|
|
|
|||
|
|
@ -72,10 +72,10 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal {
|
|||
_: Span,
|
||||
fn_def_id: LocalDefId,
|
||||
) {
|
||||
if let Some(header) = fn_kind.header() {
|
||||
if header.abi != ExternAbi::Rust {
|
||||
return;
|
||||
}
|
||||
if let Some(header) = fn_kind.header()
|
||||
&& header.abi != ExternAbi::Rust
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let parent_id = cx
|
||||
|
|
@ -141,22 +141,22 @@ fn is_argument(tcx: TyCtxt<'_>, id: HirId) -> bool {
|
|||
|
||||
impl<'tcx> Delegate<'tcx> for EscapeDelegate<'_, 'tcx> {
|
||||
fn consume(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) {
|
||||
if cmt.place.projections.is_empty() {
|
||||
if let PlaceBase::Local(lid) = cmt.place.base {
|
||||
// FIXME(rust/#120456) - is `swap_remove` correct?
|
||||
self.set.swap_remove(&lid);
|
||||
}
|
||||
if cmt.place.projections.is_empty()
|
||||
&& let PlaceBase::Local(lid) = cmt.place.base
|
||||
{
|
||||
// FIXME(rust/#120456) - is `swap_remove` correct?
|
||||
self.set.swap_remove(&lid);
|
||||
}
|
||||
}
|
||||
|
||||
fn use_cloned(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
|
||||
|
||||
fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) {
|
||||
if cmt.place.projections.is_empty() {
|
||||
if let PlaceBase::Local(lid) = cmt.place.base {
|
||||
// FIXME(rust/#120456) - is `swap_remove` correct?
|
||||
self.set.swap_remove(&lid);
|
||||
}
|
||||
if cmt.place.projections.is_empty()
|
||||
&& let PlaceBase::Local(lid) = cmt.place.base
|
||||
{
|
||||
// FIXME(rust/#120456) - is `swap_remove` correct?
|
||||
self.set.swap_remove(&lid);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -170,10 +170,11 @@ impl<'tcx> Delegate<'tcx> for EscapeDelegate<'_, 'tcx> {
|
|||
|
||||
// skip if there is a `self` parameter binding to a type
|
||||
// that contains `Self` (i.e.: `self: Box<Self>`), see #4804
|
||||
if let Some(trait_self_ty) = self.trait_self_ty {
|
||||
if self.cx.tcx.hir_name(cmt.hir_id) == kw::SelfLower && cmt.place.ty().contains(trait_self_ty) {
|
||||
return;
|
||||
}
|
||||
if let Some(trait_self_ty) = self.trait_self_ty
|
||||
&& self.cx.tcx.hir_name(cmt.hir_id) == kw::SelfLower
|
||||
&& cmt.place.ty().contains(trait_self_ty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if is_non_trait_box(cmt.place.ty()) && !self.is_large_box(cmt.place.ty()) {
|
||||
|
|
|
|||
|
|
@ -75,10 +75,10 @@ fn lint_impl_body(cx: &LateContext<'_>, impl_span: Span, impl_items: &[hir::Impl
|
|||
|
||||
impl<'tcx> Visitor<'tcx> for FindPanicUnwrap<'_, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
||||
if let Some(macro_call) = root_macro_call_first_node(self.lcx, expr) {
|
||||
if is_panic(self.lcx, macro_call.def_id) {
|
||||
self.result.push(expr.span);
|
||||
}
|
||||
if let Some(macro_call) = root_macro_call_first_node(self.lcx, expr)
|
||||
&& is_panic(self.lcx, macro_call.def_id)
|
||||
{
|
||||
self.result.push(expr.span);
|
||||
}
|
||||
|
||||
// check for `unwrap`
|
||||
|
|
|
|||
|
|
@ -228,24 +228,24 @@ fn get_integer_from_float_constant(value: &Constant<'_>) -> Option<i32> {
|
|||
|
||||
fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) {
|
||||
// Check receiver
|
||||
if let Some(value) = ConstEvalCtxt::new(cx).eval(receiver) {
|
||||
if let Some(method) = if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
|
||||
if let Some(value) = ConstEvalCtxt::new(cx).eval(receiver)
|
||||
&& let Some(method) = if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
|
||||
Some("exp")
|
||||
} else if F32(2.0) == value || F64(2.0) == value {
|
||||
Some("exp2")
|
||||
} else {
|
||||
None
|
||||
} {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
expr.span,
|
||||
"exponent for bases 2 and e can be computed more accurately",
|
||||
"consider using",
|
||||
format!("{}.{method}()", prepare_receiver_sugg(cx, &args[0])),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
expr.span,
|
||||
"exponent for bases 2 and e can be computed more accurately",
|
||||
"consider using",
|
||||
format!("{}.{method}()", prepare_receiver_sugg(cx, &args[0])),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
||||
// Check argument
|
||||
|
|
@ -289,55 +289,53 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args:
|
|||
}
|
||||
|
||||
fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) {
|
||||
if let Some(value) = ConstEvalCtxt::new(cx).eval(&args[0]) {
|
||||
if value == Int(2) {
|
||||
if let Some(parent) = get_parent_expr(cx, expr) {
|
||||
if let Some(grandparent) = get_parent_expr(cx, parent) {
|
||||
if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, receiver, ..) = grandparent.kind
|
||||
{
|
||||
if method_name.as_str() == "sqrt" && detect_hypot(cx, receiver).is_some() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if let Some(value) = ConstEvalCtxt::new(cx).eval(&args[0])
|
||||
&& value == Int(2)
|
||||
&& let Some(parent) = get_parent_expr(cx, expr)
|
||||
{
|
||||
if let Some(grandparent) = get_parent_expr(cx, parent)
|
||||
&& let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, receiver, ..) = grandparent.kind
|
||||
&& method_name.as_str() == "sqrt"
|
||||
&& detect_hypot(cx, receiver).is_some()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: op @ (BinOpKind::Add | BinOpKind::Sub),
|
||||
..
|
||||
},
|
||||
lhs,
|
||||
rhs,
|
||||
) = parent.kind
|
||||
{
|
||||
let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs };
|
||||
|
||||
// Negate expr if original code has subtraction and expr is on the right side
|
||||
let maybe_neg_sugg = |expr, hir_id| {
|
||||
let sugg = Sugg::hir(cx, expr, "..");
|
||||
if matches!(op, BinOpKind::Sub) && hir_id == rhs.hir_id {
|
||||
-sugg
|
||||
} else {
|
||||
sugg
|
||||
}
|
||||
};
|
||||
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: op @ (BinOpKind::Add | BinOpKind::Sub),
|
||||
..
|
||||
},
|
||||
lhs,
|
||||
rhs,
|
||||
) = parent.kind
|
||||
{
|
||||
let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs };
|
||||
|
||||
// Negate expr if original code has subtraction and expr is on the right side
|
||||
let maybe_neg_sugg = |expr, hir_id| {
|
||||
let sugg = Sugg::hir(cx, expr, "..");
|
||||
if matches!(op, BinOpKind::Sub) && hir_id == rhs.hir_id {
|
||||
-sugg
|
||||
} else {
|
||||
sugg
|
||||
}
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
parent.span,
|
||||
"multiply and add expressions can be calculated more efficiently and accurately",
|
||||
"consider using",
|
||||
format!(
|
||||
"{}.mul_add({}, {})",
|
||||
Sugg::hir(cx, receiver, "..").maybe_paren(),
|
||||
maybe_neg_sugg(receiver, expr.hir_id),
|
||||
maybe_neg_sugg(other_addend, other_addend.hir_id),
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
parent.span,
|
||||
"multiply and add expressions can be calculated more efficiently and accurately",
|
||||
"consider using",
|
||||
format!(
|
||||
"{}.mul_add({}, {})",
|
||||
Sugg::hir(cx, receiver, "..").maybe_paren(),
|
||||
maybe_neg_sugg(receiver, expr.hir_id),
|
||||
maybe_neg_sugg(other_addend, other_addend.hir_id),
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -483,12 +481,12 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||
rhs,
|
||||
) = &expr.kind
|
||||
{
|
||||
if let Some(parent) = get_parent_expr(cx, expr) {
|
||||
if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, receiver, ..) = parent.kind {
|
||||
if method_name.as_str() == "sqrt" && detect_hypot(cx, receiver).is_some() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if let Some(parent) = get_parent_expr(cx, expr)
|
||||
&& let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, receiver, ..) = parent.kind
|
||||
&& method_name.as_str() == "sqrt"
|
||||
&& detect_hypot(cx, receiver).is_some()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let maybe_neg_sugg = |expr| {
|
||||
|
|
@ -566,15 +564,15 @@ fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
|||
/// If the two expressions are not negations of each other, then it
|
||||
/// returns None.
|
||||
fn are_negated<'a>(cx: &LateContext<'_>, expr1: &'a Expr<'a>, expr2: &'a Expr<'a>) -> Option<(bool, &'a Expr<'a>)> {
|
||||
if let ExprKind::Unary(UnOp::Neg, expr1_negated) = &expr1.kind {
|
||||
if eq_expr_value(cx, expr1_negated, expr2) {
|
||||
return Some((false, expr2));
|
||||
}
|
||||
if let ExprKind::Unary(UnOp::Neg, expr1_negated) = &expr1.kind
|
||||
&& eq_expr_value(cx, expr1_negated, expr2)
|
||||
{
|
||||
return Some((false, expr2));
|
||||
}
|
||||
if let ExprKind::Unary(UnOp::Neg, expr2_negated) = &expr2.kind {
|
||||
if eq_expr_value(cx, expr1, expr2_negated) {
|
||||
return Some((true, expr1));
|
||||
}
|
||||
if let ExprKind::Unary(UnOp::Neg, expr2_negated) = &expr2.kind
|
||||
&& eq_expr_value(cx, expr1, expr2_negated)
|
||||
{
|
||||
return Some((true, expr1));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,27 +138,28 @@ impl EarlyLintPass for Formatting {
|
|||
|
||||
/// Implementation of the `SUSPICIOUS_ASSIGNMENT_FORMATTING` lint.
|
||||
fn check_assign(cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
if let ExprKind::Assign(ref lhs, ref rhs, _) = expr.kind {
|
||||
if !lhs.span.from_expansion() && !rhs.span.from_expansion() {
|
||||
let eq_span = lhs.span.between(rhs.span);
|
||||
if let ExprKind::Unary(op, ref sub_rhs) = rhs.kind {
|
||||
if let Some(eq_snippet) = snippet_opt(cx, eq_span) {
|
||||
let op = op.as_str();
|
||||
let eqop_span = lhs.span.between(sub_rhs.span);
|
||||
if eq_snippet.ends_with('=') {
|
||||
span_lint_and_note(
|
||||
cx,
|
||||
SUSPICIOUS_ASSIGNMENT_FORMATTING,
|
||||
eqop_span,
|
||||
format!(
|
||||
"this looks like you are trying to use `.. {op}= ..`, but you \
|
||||
if let ExprKind::Assign(ref lhs, ref rhs, _) = expr.kind
|
||||
&& !lhs.span.from_expansion()
|
||||
&& !rhs.span.from_expansion()
|
||||
{
|
||||
let eq_span = lhs.span.between(rhs.span);
|
||||
if let ExprKind::Unary(op, ref sub_rhs) = rhs.kind
|
||||
&& let Some(eq_snippet) = snippet_opt(cx, eq_span)
|
||||
{
|
||||
let op = op.as_str();
|
||||
let eqop_span = lhs.span.between(sub_rhs.span);
|
||||
if eq_snippet.ends_with('=') {
|
||||
span_lint_and_note(
|
||||
cx,
|
||||
SUSPICIOUS_ASSIGNMENT_FORMATTING,
|
||||
eqop_span,
|
||||
format!(
|
||||
"this looks like you are trying to use `.. {op}= ..`, but you \
|
||||
really are doing `.. = ({op} ..)`"
|
||||
),
|
||||
None,
|
||||
format!("to remove this lint, use either `{op}=` or `= {op}`"),
|
||||
);
|
||||
}
|
||||
}
|
||||
),
|
||||
None,
|
||||
format!("to remove this lint, use either `{op}=` or `= {op}`"),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,16 +47,16 @@ pub(super) fn check_fn(
|
|||
}
|
||||
|
||||
pub(super) fn check_trait_item(cx: &LateContext<'_>, item: &hir::TraitItem<'_>, too_many_arguments_threshold: u64) {
|
||||
if let hir::TraitItemKind::Fn(ref sig, _) = item.kind {
|
||||
if let hir::TraitItemKind::Fn(ref sig, _) = item.kind
|
||||
// don't lint extern functions decls, it's not their fault
|
||||
if sig.header.abi == ExternAbi::Rust {
|
||||
check_arg_number(
|
||||
cx,
|
||||
sig.decl,
|
||||
item.span.with_hi(sig.decl.output.span().hi()),
|
||||
too_many_arguments_threshold,
|
||||
);
|
||||
}
|
||||
&& sig.header.abi == ExternAbi::Rust
|
||||
{
|
||||
check_arg_number(
|
||||
cx,
|
||||
sig.decl,
|
||||
item.span.with_hi(sig.decl.output.span().hi()),
|
||||
too_many_arguments_threshold,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -153,18 +153,18 @@ fn lint_implicit_returns(
|
|||
ExprKind::Loop(block, ..) => {
|
||||
let mut add_return = false;
|
||||
let _: Option<!> = for_each_expr_without_closures(block, |e| {
|
||||
if let ExprKind::Break(dest, sub_expr) = e.kind {
|
||||
if dest.target_id.ok() == Some(expr.hir_id) {
|
||||
if call_site_span.is_none() && e.span.ctxt() == ctxt {
|
||||
// At this point sub_expr can be `None` in async functions which either diverge, or return
|
||||
// the unit type.
|
||||
if let Some(sub_expr) = sub_expr {
|
||||
lint_break(cx, e.hir_id, e.span, sub_expr.span);
|
||||
}
|
||||
} else {
|
||||
// the break expression is from a macro call, add a return to the loop
|
||||
add_return = true;
|
||||
if let ExprKind::Break(dest, sub_expr) = e.kind
|
||||
&& dest.target_id.ok() == Some(expr.hir_id)
|
||||
{
|
||||
if call_site_span.is_none() && e.span.ctxt() == ctxt {
|
||||
// At this point sub_expr can be `None` in async functions which either diverge, or return
|
||||
// the unit type.
|
||||
if let Some(sub_expr) = sub_expr {
|
||||
lint_break(cx, e.hir_id, e.span, sub_expr.span);
|
||||
}
|
||||
} else {
|
||||
// the break expression is from a macro call, add a return to the loop
|
||||
add_return = true;
|
||||
}
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
|
|
|
|||
|
|
@ -136,28 +136,28 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
|
|||
|
||||
let const_range = to_const_range(cx, range, size);
|
||||
|
||||
if let (Some(start), _) = const_range {
|
||||
if start > size {
|
||||
span_lint(
|
||||
cx,
|
||||
OUT_OF_BOUNDS_INDEXING,
|
||||
range.start.map_or(expr.span, |start| start.span),
|
||||
"range is out of bounds",
|
||||
);
|
||||
return;
|
||||
}
|
||||
if let (Some(start), _) = const_range
|
||||
&& start > size
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
OUT_OF_BOUNDS_INDEXING,
|
||||
range.start.map_or(expr.span, |start| start.span),
|
||||
"range is out of bounds",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if let (_, Some(end)) = const_range {
|
||||
if end > size {
|
||||
span_lint(
|
||||
cx,
|
||||
OUT_OF_BOUNDS_INDEXING,
|
||||
range.end.map_or(expr.span, |end| end.span),
|
||||
"range is out of bounds",
|
||||
);
|
||||
return;
|
||||
}
|
||||
if let (_, Some(end)) = const_range
|
||||
&& end > size
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
OUT_OF_BOUNDS_INDEXING,
|
||||
range.end.map_or(expr.span, |end| end.span),
|
||||
"range is out of bounds",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if let (Some(_), Some(_)) = const_range {
|
||||
|
|
|
|||
|
|
@ -156,11 +156,12 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
|
|||
.and(cap);
|
||||
}
|
||||
}
|
||||
if method.ident.name.as_str() == "flat_map" && args.len() == 1 {
|
||||
if let ExprKind::Closure(&Closure { body, .. }) = args[0].kind {
|
||||
let body = cx.tcx.hir_body(body);
|
||||
return is_infinite(cx, body.value);
|
||||
}
|
||||
if method.ident.name.as_str() == "flat_map"
|
||||
&& args.len() == 1
|
||||
&& let ExprKind::Closure(&Closure { body, .. }) = args[0].kind
|
||||
{
|
||||
let body = cx.tcx.hir_body(body);
|
||||
return is_infinite(cx, body.value);
|
||||
}
|
||||
Finite
|
||||
},
|
||||
|
|
|
|||
|
|
@ -130,14 +130,14 @@ impl IntPlusOne {
|
|||
BinOpKind::Le => "<",
|
||||
_ => return None,
|
||||
};
|
||||
if let Some(snippet) = node.span.get_source_text(cx) {
|
||||
if let Some(other_side_snippet) = other_side.span.get_source_text(cx) {
|
||||
let rec = match side {
|
||||
Side::Lhs => Some(format!("{snippet} {binop_string} {other_side_snippet}")),
|
||||
Side::Rhs => Some(format!("{other_side_snippet} {binop_string} {snippet}")),
|
||||
};
|
||||
return rec;
|
||||
}
|
||||
if let Some(snippet) = node.span.get_source_text(cx)
|
||||
&& let Some(other_side_snippet) = other_side.span.get_source_text(cx)
|
||||
{
|
||||
let rec = match side {
|
||||
Side::Lhs => Some(format!("{snippet} {binop_string} {other_side_snippet}")),
|
||||
Side::Rhs => Some(format!("{other_side_snippet} {binop_string} {snippet}")),
|
||||
};
|
||||
return rec;
|
||||
}
|
||||
None
|
||||
}
|
||||
|
|
@ -157,10 +157,10 @@ impl IntPlusOne {
|
|||
|
||||
impl EarlyLintPass for IntPlusOne {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, item: &Expr) {
|
||||
if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = item.kind {
|
||||
if let Some(rec) = Self::check_binop(cx, kind.node, lhs, rhs) {
|
||||
Self::emit_warning(cx, item, rec);
|
||||
}
|
||||
if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = item.kind
|
||||
&& let Some(rec) = Self::check_binop(cx, kind.node, lhs, rhs)
|
||||
{
|
||||
Self::emit_warning(cx, item, rec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,49 +91,49 @@ fn upcast_comparison_bounds_err<'tcx>(
|
|||
rhs: &'tcx Expr<'_>,
|
||||
invert: bool,
|
||||
) {
|
||||
if let Some((lb, ub)) = lhs_bounds {
|
||||
if let Some(norm_rhs_val) = ConstEvalCtxt::new(cx).eval_full_int(rhs) {
|
||||
if rel == Rel::Eq || rel == Rel::Ne {
|
||||
if norm_rhs_val < lb || norm_rhs_val > ub {
|
||||
err_upcast_comparison(cx, span, lhs, rel == Rel::Ne);
|
||||
}
|
||||
} else if match rel {
|
||||
Rel::Lt => {
|
||||
if invert {
|
||||
norm_rhs_val < lb
|
||||
} else {
|
||||
ub < norm_rhs_val
|
||||
}
|
||||
},
|
||||
Rel::Le => {
|
||||
if invert {
|
||||
norm_rhs_val <= lb
|
||||
} else {
|
||||
ub <= norm_rhs_val
|
||||
}
|
||||
},
|
||||
Rel::Eq | Rel::Ne => unreachable!(),
|
||||
} {
|
||||
err_upcast_comparison(cx, span, lhs, true);
|
||||
} else if match rel {
|
||||
Rel::Lt => {
|
||||
if invert {
|
||||
norm_rhs_val >= ub
|
||||
} else {
|
||||
lb >= norm_rhs_val
|
||||
}
|
||||
},
|
||||
Rel::Le => {
|
||||
if invert {
|
||||
norm_rhs_val > ub
|
||||
} else {
|
||||
lb > norm_rhs_val
|
||||
}
|
||||
},
|
||||
Rel::Eq | Rel::Ne => unreachable!(),
|
||||
} {
|
||||
err_upcast_comparison(cx, span, lhs, false);
|
||||
if let Some((lb, ub)) = lhs_bounds
|
||||
&& let Some(norm_rhs_val) = ConstEvalCtxt::new(cx).eval_full_int(rhs)
|
||||
{
|
||||
if rel == Rel::Eq || rel == Rel::Ne {
|
||||
if norm_rhs_val < lb || norm_rhs_val > ub {
|
||||
err_upcast_comparison(cx, span, lhs, rel == Rel::Ne);
|
||||
}
|
||||
} else if match rel {
|
||||
Rel::Lt => {
|
||||
if invert {
|
||||
norm_rhs_val < lb
|
||||
} else {
|
||||
ub < norm_rhs_val
|
||||
}
|
||||
},
|
||||
Rel::Le => {
|
||||
if invert {
|
||||
norm_rhs_val <= lb
|
||||
} else {
|
||||
ub <= norm_rhs_val
|
||||
}
|
||||
},
|
||||
Rel::Eq | Rel::Ne => unreachable!(),
|
||||
} {
|
||||
err_upcast_comparison(cx, span, lhs, true);
|
||||
} else if match rel {
|
||||
Rel::Lt => {
|
||||
if invert {
|
||||
norm_rhs_val >= ub
|
||||
} else {
|
||||
lb >= norm_rhs_val
|
||||
}
|
||||
},
|
||||
Rel::Le => {
|
||||
if invert {
|
||||
norm_rhs_val > ub
|
||||
} else {
|
||||
lb > norm_rhs_val
|
||||
}
|
||||
},
|
||||
Rel::Eq | Rel::Ne => unreachable!(),
|
||||
} {
|
||||
err_upcast_comparison(cx, span, lhs, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -444,57 +444,56 @@ impl LateLintPass<'_> for ItemNameRepetitions {
|
|||
|
||||
let item_name = ident.name.as_str();
|
||||
let item_camel = to_camel_case(item_name);
|
||||
if !item.span.from_expansion() && is_present_in_source(cx, item.span) {
|
||||
if let [.., (mod_name, mod_camel, mod_owner_id)] = &*self.modules {
|
||||
// constants don't have surrounding modules
|
||||
if !mod_camel.is_empty() {
|
||||
if mod_name == &ident.name
|
||||
&& let ItemKind::Mod(..) = item.kind
|
||||
&& (!self.allow_private_module_inception || cx.tcx.visibility(mod_owner_id.def_id).is_public())
|
||||
{
|
||||
span_lint(
|
||||
if !item.span.from_expansion() && is_present_in_source(cx, item.span)
|
||||
&& let [.., (mod_name, mod_camel, mod_owner_id)] = &*self.modules
|
||||
// constants don't have surrounding modules
|
||||
&& !mod_camel.is_empty()
|
||||
{
|
||||
if mod_name == &ident.name
|
||||
&& let ItemKind::Mod(..) = item.kind
|
||||
&& (!self.allow_private_module_inception || cx.tcx.visibility(mod_owner_id.def_id).is_public())
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
MODULE_INCEPTION,
|
||||
item.span,
|
||||
"module has the same name as its containing module",
|
||||
);
|
||||
}
|
||||
|
||||
// The `module_name_repetitions` lint should only trigger if the item has the module in its
|
||||
// name. Having the same name is accepted.
|
||||
if cx.tcx.visibility(item.owner_id).is_public()
|
||||
&& cx.tcx.visibility(mod_owner_id.def_id).is_public()
|
||||
&& item_camel.len() > mod_camel.len()
|
||||
{
|
||||
let matching = count_match_start(mod_camel, &item_camel);
|
||||
let rmatching = count_match_end(mod_camel, &item_camel);
|
||||
let nchars = mod_camel.chars().count();
|
||||
|
||||
let is_word_beginning = |c: char| c == '_' || c.is_uppercase() || c.is_numeric();
|
||||
|
||||
if matching.char_count == nchars {
|
||||
match item_camel.chars().nth(nchars) {
|
||||
Some(c) if is_word_beginning(c) => span_lint(
|
||||
cx,
|
||||
MODULE_INCEPTION,
|
||||
item.span,
|
||||
"module has the same name as its containing module",
|
||||
);
|
||||
}
|
||||
|
||||
// The `module_name_repetitions` lint should only trigger if the item has the module in its
|
||||
// name. Having the same name is accepted.
|
||||
if cx.tcx.visibility(item.owner_id).is_public()
|
||||
&& cx.tcx.visibility(mod_owner_id.def_id).is_public()
|
||||
&& item_camel.len() > mod_camel.len()
|
||||
{
|
||||
let matching = count_match_start(mod_camel, &item_camel);
|
||||
let rmatching = count_match_end(mod_camel, &item_camel);
|
||||
let nchars = mod_camel.chars().count();
|
||||
|
||||
let is_word_beginning = |c: char| c == '_' || c.is_uppercase() || c.is_numeric();
|
||||
|
||||
if matching.char_count == nchars {
|
||||
match item_camel.chars().nth(nchars) {
|
||||
Some(c) if is_word_beginning(c) => span_lint(
|
||||
cx,
|
||||
MODULE_NAME_REPETITIONS,
|
||||
ident.span,
|
||||
"item name starts with its containing module's name",
|
||||
),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
if rmatching.char_count == nchars
|
||||
&& !self.is_allowed_prefix(&item_camel[..item_camel.len() - rmatching.byte_count])
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
MODULE_NAME_REPETITIONS,
|
||||
ident.span,
|
||||
"item name ends with its containing module's name",
|
||||
);
|
||||
}
|
||||
MODULE_NAME_REPETITIONS,
|
||||
ident.span,
|
||||
"item name starts with its containing module's name",
|
||||
),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
if rmatching.char_count == nchars
|
||||
&& !self.is_allowed_prefix(&item_camel[..item_camel.len() - rmatching.byte_count])
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
MODULE_NAME_REPETITIONS,
|
||||
ident.span,
|
||||
"item name ends with its containing module's name",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -523,10 +523,10 @@ fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>
|
|||
|
||||
if let (&ExprKind::MethodCall(method_path, receiver, [], _), ExprKind::Lit(lit)) = (&method.kind, &lit.kind) {
|
||||
// check if we are in an is_empty() method
|
||||
if let Some(name) = get_item_name(cx, method) {
|
||||
if name.as_str() == "is_empty" {
|
||||
return;
|
||||
}
|
||||
if let Some(name) = get_item_name(cx, method)
|
||||
&& name.as_str() == "is_empty"
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
check_len(cx, span, method_path.ident.name, receiver, &lit.node, op, compare_to);
|
||||
|
|
@ -588,11 +588,11 @@ fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Ex
|
|||
}
|
||||
|
||||
fn is_empty_string(expr: &Expr<'_>) -> bool {
|
||||
if let ExprKind::Lit(lit) = expr.kind {
|
||||
if let LitKind::Str(lit, _) = lit.node {
|
||||
let lit = lit.as_str();
|
||||
return lit.is_empty();
|
||||
}
|
||||
if let ExprKind::Lit(lit) = expr.kind
|
||||
&& let LitKind::Str(lit, _) = lit.node
|
||||
{
|
||||
let lit = lit.as_str();
|
||||
return lit.is_empty();
|
||||
}
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -150,10 +150,10 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
|
|||
} = item.kind
|
||||
{
|
||||
check_fn_inner(cx, sig, Some(id), None, generics, item.span, true, self.msrv);
|
||||
} else if let ItemKind::Impl(impl_) = item.kind {
|
||||
if !item.span.from_expansion() {
|
||||
report_extra_impl_lifetimes(cx, impl_);
|
||||
}
|
||||
} else if let ItemKind::Impl(impl_) = item.kind
|
||||
&& !item.span.from_expansion()
|
||||
{
|
||||
report_extra_impl_lifetimes(cx, impl_);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -387,12 +387,11 @@ impl LiteralDigitGrouping {
|
|||
|
||||
let first = groups.next().expect("At least one group");
|
||||
|
||||
if radix == Radix::Binary || radix == Radix::Octal || radix == Radix::Hexadecimal {
|
||||
if let Some(second_size) = groups.next() {
|
||||
if !groups.all(|i| i == second_size) || first > second_size {
|
||||
return Err(WarningType::UnusualByteGroupings);
|
||||
}
|
||||
}
|
||||
if (radix == Radix::Binary || radix == Radix::Octal || radix == Radix::Hexadecimal)
|
||||
&& let Some(second_size) = groups.next()
|
||||
&& (!groups.all(|i| i == second_size) || first > second_size)
|
||||
{
|
||||
return Err(WarningType::UnusualByteGroupings);
|
||||
}
|
||||
|
||||
if let Some(second) = groups.next() {
|
||||
|
|
|
|||
|
|
@ -45,15 +45,14 @@ fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, spans: &[(Span, Option<Strin
|
|||
let spans = spans
|
||||
.iter()
|
||||
.filter_map(|(span, name)| {
|
||||
if let Some(name) = name {
|
||||
if let Some(name) = name
|
||||
// We need to check that the name is a local.
|
||||
if !mir
|
||||
&& !mir
|
||||
.var_debug_info
|
||||
.iter()
|
||||
.any(|local| !local.source_info.span.from_expansion() && local.name.as_str() == name)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
{
|
||||
return None;
|
||||
}
|
||||
Some(*span)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -13,45 +13,45 @@ use rustc_span::sym;
|
|||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx Expr<'_>, body: &'tcx Expr<'_>) {
|
||||
let pat_span = pat.span;
|
||||
|
||||
if let PatKind::Tuple(pat, _) = pat.kind {
|
||||
if pat.len() == 2 {
|
||||
let arg_span = arg.span;
|
||||
let (new_pat_span, kind, ty, mutbl) = match *cx.typeck_results().expr_ty(arg).kind() {
|
||||
ty::Ref(_, ty, mutbl) => match (&pat[0].kind, &pat[1].kind) {
|
||||
(key, _) if pat_is_wild(cx, key, body) => (pat[1].span, "value", ty, mutbl),
|
||||
(_, value) if pat_is_wild(cx, value, body) => (pat[0].span, "key", ty, Mutability::Not),
|
||||
_ => return,
|
||||
},
|
||||
if let PatKind::Tuple(pat, _) = pat.kind
|
||||
&& pat.len() == 2
|
||||
{
|
||||
let arg_span = arg.span;
|
||||
let (new_pat_span, kind, ty, mutbl) = match *cx.typeck_results().expr_ty(arg).kind() {
|
||||
ty::Ref(_, ty, mutbl) => match (&pat[0].kind, &pat[1].kind) {
|
||||
(key, _) if pat_is_wild(cx, key, body) => (pat[1].span, "value", ty, mutbl),
|
||||
(_, value) if pat_is_wild(cx, value, body) => (pat[0].span, "key", ty, Mutability::Not),
|
||||
_ => return,
|
||||
};
|
||||
let mutbl = match mutbl {
|
||||
Mutability::Not => "",
|
||||
Mutability::Mut => "_mut",
|
||||
};
|
||||
let arg = match arg.kind {
|
||||
ExprKind::AddrOf(BorrowKind::Ref, _, expr) => expr,
|
||||
_ => arg,
|
||||
};
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
let mutbl = match mutbl {
|
||||
Mutability::Not => "",
|
||||
Mutability::Mut => "_mut",
|
||||
};
|
||||
let arg = match arg.kind {
|
||||
ExprKind::AddrOf(BorrowKind::Ref, _, expr) => expr,
|
||||
_ => arg,
|
||||
};
|
||||
|
||||
if is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
FOR_KV_MAP,
|
||||
arg_span,
|
||||
format!("you seem to want to iterate on a map's {kind}s"),
|
||||
|diag| {
|
||||
let map = sugg::Sugg::hir(cx, arg, "map");
|
||||
diag.multipart_suggestion(
|
||||
"use the corresponding method",
|
||||
vec![
|
||||
(pat_span, snippet(cx, new_pat_span, kind).into_owned()),
|
||||
(arg_span, format!("{}.{kind}s{mutbl}()", map.maybe_paren())),
|
||||
],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
if is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
FOR_KV_MAP,
|
||||
arg_span,
|
||||
format!("you seem to want to iterate on a map's {kind}s"),
|
||||
|diag| {
|
||||
let map = sugg::Sugg::hir(cx, arg, "map");
|
||||
diag.multipart_suggestion(
|
||||
"use the corresponding method",
|
||||
vec![
|
||||
(pat_span, snippet(cx, new_pat_span, kind).into_owned()),
|
||||
(arg_span, format!("{}.{kind}s{mutbl}()", map.maybe_paren())),
|
||||
],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,37 +28,37 @@ pub(super) fn check<'tcx>(
|
|||
end: Some(end),
|
||||
limits,
|
||||
}) = higher::Range::hir(arg)
|
||||
{
|
||||
// the var must be a single name
|
||||
if let PatKind::Binding(_, canonical_id, _, _) = pat.kind {
|
||||
let mut starts = vec![Start {
|
||||
id: canonical_id,
|
||||
kind: StartKind::Range,
|
||||
}];
|
||||
&& let PatKind::Binding(_, canonical_id, _, _) = pat.kind
|
||||
{
|
||||
let mut starts = vec![Start {
|
||||
id: canonical_id,
|
||||
kind: StartKind::Range,
|
||||
}];
|
||||
|
||||
// This is one of few ways to return different iterators
|
||||
// derived from: https://stackoverflow.com/questions/29760668/conditionally-iterate-over-one-of-several-possible-iterators/52064434#52064434
|
||||
let mut iter_a = None;
|
||||
let mut iter_b = None;
|
||||
// This is one of few ways to return different iterators
|
||||
// derived from: https://stackoverflow.com/questions/29760668/conditionally-iterate-over-one-of-several-possible-iterators/52064434#52064434
|
||||
let mut iter_a = None;
|
||||
let mut iter_b = None;
|
||||
|
||||
if let ExprKind::Block(block, _) = body.kind {
|
||||
if let Some(loop_counters) = get_loop_counters(cx, block, expr) {
|
||||
starts.extend(loop_counters);
|
||||
}
|
||||
iter_a = Some(get_assignments(block, &starts));
|
||||
} else {
|
||||
iter_b = Some(get_assignment(body));
|
||||
if let ExprKind::Block(block, _) = body.kind {
|
||||
if let Some(loop_counters) = get_loop_counters(cx, block, expr) {
|
||||
starts.extend(loop_counters);
|
||||
}
|
||||
iter_a = Some(get_assignments(block, &starts));
|
||||
} else {
|
||||
iter_b = Some(get_assignment(body));
|
||||
}
|
||||
|
||||
let assignments = iter_a.into_iter().flatten().chain(iter_b);
|
||||
let assignments = iter_a.into_iter().flatten().chain(iter_b);
|
||||
|
||||
let big_sugg = assignments
|
||||
// The only statements in the for loops can be indexed assignments from
|
||||
// indexed retrievals (except increments of loop counters).
|
||||
.map(|o| {
|
||||
o.and_then(|(lhs, rhs)| {
|
||||
let rhs = fetch_cloned_expr(rhs);
|
||||
if let ExprKind::Index(base_left, idx_left, _) = lhs.kind
|
||||
let big_sugg = assignments
|
||||
// The only statements in the for loops can be indexed assignments from
|
||||
// indexed retrievals (except increments of loop counters).
|
||||
.map(|o| {
|
||||
o.and_then(|(lhs, rhs)| {
|
||||
let rhs = fetch_cloned_expr(rhs);
|
||||
if let ExprKind::Index(base_left, idx_left, _) = lhs.kind
|
||||
&& let ExprKind::Index(base_right, idx_right, _) = rhs.kind
|
||||
&& let Some(ty) = get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_left))
|
||||
&& get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_right)).is_some()
|
||||
|
|
@ -68,42 +68,41 @@ pub(super) fn check<'tcx>(
|
|||
&& !local_used_in(cx, canonical_id, base_right)
|
||||
// Source and destination must be different
|
||||
&& path_to_local(base_left) != path_to_local(base_right)
|
||||
{
|
||||
Some((
|
||||
ty,
|
||||
IndexExpr {
|
||||
base: base_left,
|
||||
idx: start_left,
|
||||
idx_offset: offset_left,
|
||||
},
|
||||
IndexExpr {
|
||||
base: base_right,
|
||||
idx: start_right,
|
||||
idx_offset: offset_right,
|
||||
},
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
{
|
||||
Some((
|
||||
ty,
|
||||
IndexExpr {
|
||||
base: base_left,
|
||||
idx: start_left,
|
||||
idx_offset: offset_left,
|
||||
},
|
||||
IndexExpr {
|
||||
base: base_right,
|
||||
idx: start_right,
|
||||
idx_offset: offset_right,
|
||||
},
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.map(|o| o.map(|(ty, dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, ty, &dst, &src)))
|
||||
.collect::<Option<Vec<_>>>()
|
||||
.filter(|v| !v.is_empty())
|
||||
.map(|v| v.join("\n "));
|
||||
})
|
||||
.map(|o| o.map(|(ty, dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, ty, &dst, &src)))
|
||||
.collect::<Option<Vec<_>>>()
|
||||
.filter(|v| !v.is_empty())
|
||||
.map(|v| v.join("\n "));
|
||||
|
||||
if let Some(big_sugg) = big_sugg {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_MEMCPY,
|
||||
expr.span,
|
||||
"it looks like you're manually copying between slices",
|
||||
"try replacing the loop by",
|
||||
big_sugg,
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
if let Some(big_sugg) = big_sugg {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_MEMCPY,
|
||||
expr.span,
|
||||
"it looks like you're manually copying between slices",
|
||||
"try replacing the loop by",
|
||||
big_sugg,
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
|
|
|
|||
|
|
@ -81,15 +81,15 @@ fn check_local(cx: &LateContext<'_>, stmt: &Stmt<'_>, is_empty_recv: &Expr<'_>,
|
|||
}
|
||||
|
||||
fn check_call_arguments(cx: &LateContext<'_>, stmt: &Stmt<'_>, is_empty_recv: &Expr<'_>, loop_span: Span) {
|
||||
if let StmtKind::Semi(expr) | StmtKind::Expr(expr) = stmt.kind {
|
||||
if let ExprKind::MethodCall(.., args, _) | ExprKind::Call(_, args) = expr.kind {
|
||||
let offending_arg = args
|
||||
.iter()
|
||||
.find_map(|arg| is_vec_pop_unwrap(cx, arg, is_empty_recv).then_some(arg.span));
|
||||
if let StmtKind::Semi(expr) | StmtKind::Expr(expr) = stmt.kind
|
||||
&& let ExprKind::MethodCall(.., args, _) | ExprKind::Call(_, args) = expr.kind
|
||||
{
|
||||
let offending_arg = args
|
||||
.iter()
|
||||
.find_map(|arg| is_vec_pop_unwrap(cx, arg, is_empty_recv).then_some(arg.span));
|
||||
|
||||
if let Some(offending_arg) = offending_arg {
|
||||
report_lint(cx, offending_arg, PopStmt::Anonymous, loop_span, is_empty_recv.span);
|
||||
}
|
||||
if let Some(offending_arg) = offending_arg {
|
||||
report_lint(cx, offending_arg, PopStmt::Anonymous, loop_span, is_empty_recv.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,14 +82,14 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> {
|
|||
fn use_cloned(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
|
||||
|
||||
fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId, bk: ty::BorrowKind) {
|
||||
if bk == ty::BorrowKind::Mutable {
|
||||
if let PlaceBase::Local(id) = cmt.place.base {
|
||||
if Some(id) == self.hir_id_low && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) {
|
||||
self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id));
|
||||
}
|
||||
if Some(id) == self.hir_id_high && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) {
|
||||
self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id));
|
||||
}
|
||||
if bk == ty::BorrowKind::Mutable
|
||||
&& let PlaceBase::Local(id) = cmt.place.base
|
||||
{
|
||||
if Some(id) == self.hir_id_low && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) {
|
||||
self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id));
|
||||
}
|
||||
if Some(id) == self.hir_id_high && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) {
|
||||
self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,155 +31,154 @@ pub(super) fn check<'tcx>(
|
|||
ref end,
|
||||
limits,
|
||||
}) = higher::Range::hir(arg)
|
||||
{
|
||||
// the var must be a single name
|
||||
if let PatKind::Binding(_, canonical_id, ident, _) = pat.kind {
|
||||
let mut visitor = VarVisitor {
|
||||
cx,
|
||||
var: canonical_id,
|
||||
indexed_mut: FxHashSet::default(),
|
||||
indexed_indirectly: FxHashMap::default(),
|
||||
indexed_directly: FxIndexMap::default(),
|
||||
referenced: FxHashSet::default(),
|
||||
nonindex: false,
|
||||
prefer_mutable: false,
|
||||
&& let PatKind::Binding(_, canonical_id, ident, _) = pat.kind
|
||||
{
|
||||
let mut visitor = VarVisitor {
|
||||
cx,
|
||||
var: canonical_id,
|
||||
indexed_mut: FxHashSet::default(),
|
||||
indexed_indirectly: FxHashMap::default(),
|
||||
indexed_directly: FxIndexMap::default(),
|
||||
referenced: FxHashSet::default(),
|
||||
nonindex: false,
|
||||
prefer_mutable: false,
|
||||
};
|
||||
walk_expr(&mut visitor, body);
|
||||
|
||||
// linting condition: we only indexed one variable, and indexed it directly
|
||||
if visitor.indexed_indirectly.is_empty() && visitor.indexed_directly.len() == 1 {
|
||||
let (indexed, (indexed_extent, indexed_ty)) = visitor
|
||||
.indexed_directly
|
||||
.into_iter()
|
||||
.next()
|
||||
.expect("already checked that we have exactly 1 element");
|
||||
|
||||
// ensure that the indexed variable was declared before the loop, see #601
|
||||
if let Some(indexed_extent) = indexed_extent {
|
||||
let parent_def_id = cx.tcx.hir_get_parent_item(expr.hir_id);
|
||||
let region_scope_tree = cx.tcx.region_scope_tree(parent_def_id);
|
||||
let pat_extent = region_scope_tree.var_scope(pat.hir_id.local_id).unwrap();
|
||||
if region_scope_tree.is_subscope_of(indexed_extent, pat_extent) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// don't lint if the container that is indexed does not have .iter() method
|
||||
let has_iter = has_iter_method(cx, indexed_ty);
|
||||
if has_iter.is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
// don't lint if the container that is indexed into is also used without
|
||||
// indexing
|
||||
if visitor.referenced.contains(&indexed) {
|
||||
return;
|
||||
}
|
||||
|
||||
let starts_at_zero = is_integer_const(cx, start, 0);
|
||||
|
||||
let skip = if starts_at_zero {
|
||||
String::new()
|
||||
} else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, start, cx) {
|
||||
return;
|
||||
} else {
|
||||
format!(".skip({})", snippet(cx, start.span, ".."))
|
||||
};
|
||||
walk_expr(&mut visitor, body);
|
||||
|
||||
// linting condition: we only indexed one variable, and indexed it directly
|
||||
if visitor.indexed_indirectly.is_empty() && visitor.indexed_directly.len() == 1 {
|
||||
let (indexed, (indexed_extent, indexed_ty)) = visitor
|
||||
.indexed_directly
|
||||
.into_iter()
|
||||
.next()
|
||||
.expect("already checked that we have exactly 1 element");
|
||||
let mut end_is_start_plus_val = false;
|
||||
|
||||
// ensure that the indexed variable was declared before the loop, see #601
|
||||
if let Some(indexed_extent) = indexed_extent {
|
||||
let parent_def_id = cx.tcx.hir_get_parent_item(expr.hir_id);
|
||||
let region_scope_tree = cx.tcx.region_scope_tree(parent_def_id);
|
||||
let pat_extent = region_scope_tree.var_scope(pat.hir_id.local_id).unwrap();
|
||||
if region_scope_tree.is_subscope_of(indexed_extent, pat_extent) {
|
||||
return;
|
||||
let take = if let Some(end) = *end {
|
||||
let mut take_expr = end;
|
||||
|
||||
if let ExprKind::Binary(ref op, left, right) = end.kind
|
||||
&& op.node == BinOpKind::Add
|
||||
{
|
||||
let start_equal_left = SpanlessEq::new(cx).eq_expr(start, left);
|
||||
let start_equal_right = SpanlessEq::new(cx).eq_expr(start, right);
|
||||
|
||||
if start_equal_left {
|
||||
take_expr = right;
|
||||
} else if start_equal_right {
|
||||
take_expr = left;
|
||||
}
|
||||
|
||||
end_is_start_plus_val = start_equal_left | start_equal_right;
|
||||
}
|
||||
|
||||
// don't lint if the container that is indexed does not have .iter() method
|
||||
let has_iter = has_iter_method(cx, indexed_ty);
|
||||
if has_iter.is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
// don't lint if the container that is indexed into is also used without
|
||||
// indexing
|
||||
if visitor.referenced.contains(&indexed) {
|
||||
return;
|
||||
}
|
||||
|
||||
let starts_at_zero = is_integer_const(cx, start, 0);
|
||||
|
||||
let skip = if starts_at_zero {
|
||||
if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) {
|
||||
String::new()
|
||||
} else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, start, cx) {
|
||||
} else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr, cx) {
|
||||
return;
|
||||
} else {
|
||||
format!(".skip({})", snippet(cx, start.span, ".."))
|
||||
};
|
||||
|
||||
let mut end_is_start_plus_val = false;
|
||||
|
||||
let take = if let Some(end) = *end {
|
||||
let mut take_expr = end;
|
||||
|
||||
if let ExprKind::Binary(ref op, left, right) = end.kind {
|
||||
if op.node == BinOpKind::Add {
|
||||
let start_equal_left = SpanlessEq::new(cx).eq_expr(start, left);
|
||||
let start_equal_right = SpanlessEq::new(cx).eq_expr(start, right);
|
||||
|
||||
if start_equal_left {
|
||||
take_expr = right;
|
||||
} else if start_equal_right {
|
||||
take_expr = left;
|
||||
}
|
||||
|
||||
end_is_start_plus_val = start_equal_left | start_equal_right;
|
||||
}
|
||||
}
|
||||
|
||||
if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) {
|
||||
String::new()
|
||||
} else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr, cx) {
|
||||
return;
|
||||
} else {
|
||||
match limits {
|
||||
ast::RangeLimits::Closed => {
|
||||
let take_expr = sugg::Sugg::hir(cx, take_expr, "<count>");
|
||||
format!(".take({})", take_expr + sugg::ONE)
|
||||
},
|
||||
ast::RangeLimits::HalfOpen => {
|
||||
format!(".take({})", snippet(cx, take_expr.span, ".."))
|
||||
},
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
let (ref_mut, method) = if visitor.indexed_mut.contains(&indexed) {
|
||||
("mut ", "iter_mut")
|
||||
} else {
|
||||
("", "iter")
|
||||
};
|
||||
|
||||
let take_is_empty = take.is_empty();
|
||||
let mut method_1 = take;
|
||||
let mut method_2 = skip;
|
||||
|
||||
if end_is_start_plus_val {
|
||||
mem::swap(&mut method_1, &mut method_2);
|
||||
}
|
||||
|
||||
if visitor.nonindex {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
NEEDLESS_RANGE_LOOP,
|
||||
arg.span,
|
||||
format!("the loop variable `{}` is used to index `{indexed}`", ident.name),
|
||||
|diag| {
|
||||
diag.multipart_suggestion(
|
||||
"consider using an iterator and enumerate()",
|
||||
vec![
|
||||
(pat.span, format!("({}, <item>)", ident.name)),
|
||||
(
|
||||
arg.span,
|
||||
format!("{indexed}.{method}().enumerate(){method_1}{method_2}"),
|
||||
),
|
||||
],
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
match limits {
|
||||
ast::RangeLimits::Closed => {
|
||||
let take_expr = sugg::Sugg::hir(cx, take_expr, "<count>");
|
||||
format!(".take({})", take_expr + sugg::ONE)
|
||||
},
|
||||
);
|
||||
} else {
|
||||
let repl = if starts_at_zero && take_is_empty {
|
||||
format!("&{ref_mut}{indexed}")
|
||||
} else {
|
||||
format!("{indexed}.{method}(){method_1}{method_2}")
|
||||
};
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
NEEDLESS_RANGE_LOOP,
|
||||
arg.span,
|
||||
format!("the loop variable `{}` is only used to index `{indexed}`", ident.name),
|
||||
|diag| {
|
||||
diag.multipart_suggestion(
|
||||
"consider using an iterator",
|
||||
vec![(pat.span, "<item>".to_string()), (arg.span, repl)],
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
ast::RangeLimits::HalfOpen => {
|
||||
format!(".take({})", snippet(cx, take_expr.span, ".."))
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
let (ref_mut, method) = if visitor.indexed_mut.contains(&indexed) {
|
||||
("mut ", "iter_mut")
|
||||
} else {
|
||||
("", "iter")
|
||||
};
|
||||
|
||||
let take_is_empty = take.is_empty();
|
||||
let mut method_1 = take;
|
||||
let mut method_2 = skip;
|
||||
|
||||
if end_is_start_plus_val {
|
||||
mem::swap(&mut method_1, &mut method_2);
|
||||
}
|
||||
|
||||
if visitor.nonindex {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
NEEDLESS_RANGE_LOOP,
|
||||
arg.span,
|
||||
format!("the loop variable `{}` is used to index `{indexed}`", ident.name),
|
||||
|diag| {
|
||||
diag.multipart_suggestion(
|
||||
"consider using an iterator and enumerate()",
|
||||
vec![
|
||||
(pat.span, format!("({}, <item>)", ident.name)),
|
||||
(
|
||||
arg.span,
|
||||
format!("{indexed}.{method}().enumerate(){method_1}{method_2}"),
|
||||
),
|
||||
],
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
let repl = if starts_at_zero && take_is_empty {
|
||||
format!("&{ref_mut}{indexed}")
|
||||
} else {
|
||||
format!("{indexed}.{method}(){method_1}{method_2}")
|
||||
};
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
NEEDLESS_RANGE_LOOP,
|
||||
arg.span,
|
||||
format!("the loop variable `{}` is only used to index `{indexed}`", ident.name),
|
||||
|diag| {
|
||||
diag.multipart_suggestion(
|
||||
"consider using an iterator",
|
||||
vec![(pat.span, "<item>".to_string()), (arg.span, repl)],
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -346,10 +345,10 @@ impl<'tcx> Visitor<'tcx> for VarVisitor<'_, 'tcx> {
|
|||
for expr in args {
|
||||
let ty = self.cx.typeck_results().expr_ty_adjusted(expr);
|
||||
self.prefer_mutable = false;
|
||||
if let ty::Ref(_, _, mutbl) = *ty.kind() {
|
||||
if mutbl == Mutability::Mut {
|
||||
self.prefer_mutable = true;
|
||||
}
|
||||
if let ty::Ref(_, _, mutbl) = *ty.kind()
|
||||
&& mutbl == Mutability::Mut
|
||||
{
|
||||
self.prefer_mutable = true;
|
||||
}
|
||||
self.visit_expr(expr);
|
||||
}
|
||||
|
|
@ -361,10 +360,10 @@ impl<'tcx> Visitor<'tcx> for VarVisitor<'_, 'tcx> {
|
|||
iter::once(receiver).chain(args.iter()),
|
||||
) {
|
||||
self.prefer_mutable = false;
|
||||
if let ty::Ref(_, _, mutbl) = *ty.kind() {
|
||||
if mutbl == Mutability::Mut {
|
||||
self.prefer_mutable = true;
|
||||
}
|
||||
if let ty::Ref(_, _, mutbl) = *ty.kind()
|
||||
&& mutbl == Mutability::Mut
|
||||
{
|
||||
self.prefer_mutable = true;
|
||||
}
|
||||
self.visit_expr(expr);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -244,10 +244,10 @@ fn never_loop_expr<'tcx>(
|
|||
});
|
||||
combine_seq(first, || {
|
||||
// checks if break targets a block instead of a loop
|
||||
if let ExprKind::Break(Destination { target_id: Ok(t), .. }, _) = expr.kind {
|
||||
if let Some((_, reachable)) = local_labels.iter_mut().find(|(label, _)| *label == t) {
|
||||
*reachable = true;
|
||||
}
|
||||
if let ExprKind::Break(Destination { target_id: Ok(t), .. }, _) = expr.kind
|
||||
&& let Some((_, reachable)) = local_labels.iter_mut().find(|(label, _)| *label == t)
|
||||
{
|
||||
*reachable = true;
|
||||
}
|
||||
NeverLoopResult::Diverging
|
||||
})
|
||||
|
|
|
|||
|
|
@ -92,10 +92,10 @@ fn check_into_iter(
|
|||
&& let [filter_params] = filter_body.params
|
||||
{
|
||||
if match_map_type(cx, left_expr) {
|
||||
if let hir::PatKind::Tuple([key_pat, value_pat], _) = filter_params.pat.kind {
|
||||
if let Some(sugg) = make_sugg(cx, key_pat, value_pat, left_expr, filter_body) {
|
||||
make_span_lint_and_sugg(cx, parent_expr_span, sugg);
|
||||
}
|
||||
if let hir::PatKind::Tuple([key_pat, value_pat], _) = filter_params.pat.kind
|
||||
&& let Some(sugg) = make_sugg(cx, key_pat, value_pat, left_expr, filter_body)
|
||||
{
|
||||
make_span_lint_and_sugg(cx, parent_expr_span, sugg);
|
||||
}
|
||||
// Cannot lint other cases because `retain` requires two parameters
|
||||
} else {
|
||||
|
|
@ -196,22 +196,21 @@ fn check_to_owned(
|
|||
&& let filter_body = cx.tcx.hir_body(closure.body)
|
||||
&& let [filter_params] = filter_body.params
|
||||
&& msrv.meets(cx, msrvs::STRING_RETAIN)
|
||||
&& let hir::PatKind::Ref(pat, _) = filter_params.pat.kind
|
||||
{
|
||||
if let hir::PatKind::Ref(pat, _) = filter_params.pat.kind {
|
||||
make_span_lint_and_sugg(
|
||||
cx,
|
||||
parent_expr_span,
|
||||
format!(
|
||||
"{}.retain(|{}| {})",
|
||||
snippet(cx, left_expr.span, ".."),
|
||||
snippet(cx, pat.span, ".."),
|
||||
snippet(cx, filter_body.value.span, "..")
|
||||
),
|
||||
);
|
||||
}
|
||||
// Be conservative now. Do nothing for the `Binding` case.
|
||||
// TODO: Ideally, we can rewrite the lambda by stripping one level of reference
|
||||
make_span_lint_and_sugg(
|
||||
cx,
|
||||
parent_expr_span,
|
||||
format!(
|
||||
"{}.retain(|{}| {})",
|
||||
snippet(cx, left_expr.span, ".."),
|
||||
snippet(cx, pat.span, ".."),
|
||||
snippet(cx, filter_body.value.span, "..")
|
||||
),
|
||||
);
|
||||
}
|
||||
// Be conservative now. Do nothing for the `Binding` case.
|
||||
// TODO: Ideally, we can rewrite the lambda by stripping one level of reference
|
||||
}
|
||||
|
||||
fn make_sugg(
|
||||
|
|
|
|||
|
|
@ -113,15 +113,14 @@ fn parse_call(cx: &LateContext<'_>, span: Span, func: &Expr<'_>, arg: &Expr<'_>)
|
|||
&& is_expr_kind_empty_str(&arg.kind)
|
||||
{
|
||||
warn_then_suggest(cx, span);
|
||||
} else if let QPath::Resolved(_, path) = qpath {
|
||||
} else if let QPath::Resolved(_, path) = qpath
|
||||
// From::from(...) or TryFrom::try_from(...)
|
||||
if let [path_seg1, path_seg2] = path.segments
|
||||
&& is_expr_kind_empty_str(&arg.kind)
|
||||
&& ((path_seg1.ident.name == sym::From && path_seg2.ident.name == sym::from)
|
||||
|| (path_seg1.ident.name == sym::TryFrom && path_seg2.ident.name == sym::try_from))
|
||||
{
|
||||
warn_then_suggest(cx, span);
|
||||
}
|
||||
&& let [path_seg1, path_seg2] = path.segments
|
||||
&& is_expr_kind_empty_str(&arg.kind)
|
||||
&& ((path_seg1.ident.name == sym::From && path_seg2.ident.name == sym::from)
|
||||
|| (path_seg1.ident.name == sym::TryFrom && path_seg2.ident.name == sym::try_from))
|
||||
{
|
||||
warn_then_suggest(cx, span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -101,10 +101,10 @@ fn is_unit_type(ty: Ty<'_>) -> bool {
|
|||
fn is_unit_function(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
|
||||
if let ty::FnDef(id, _) = *ty.kind() {
|
||||
if let Some(fn_type) = cx.tcx.fn_sig(id).instantiate_identity().no_bound_vars() {
|
||||
return is_unit_type(fn_type.output());
|
||||
}
|
||||
if let ty::FnDef(id, _) = *ty.kind()
|
||||
&& let Some(fn_type) = cx.tcx.fn_sig(id).instantiate_identity().no_bound_vars()
|
||||
{
|
||||
return is_unit_type(fn_type.output());
|
||||
}
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,10 +41,10 @@ fn get_cond_expr<'tcx>(
|
|||
fn peels_blocks_incl_unsafe_opt<'a>(expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> {
|
||||
// we don't want to use `peel_blocks` here because we don't care if the block is unsafe, it's
|
||||
// checked by `contains_unsafe_block`
|
||||
if let ExprKind::Block(block, None) = expr.kind {
|
||||
if block.stmts.is_empty() {
|
||||
return block.expr;
|
||||
}
|
||||
if let ExprKind::Block(block, None) = expr.kind
|
||||
&& block.stmts.is_empty()
|
||||
{
|
||||
return block.expr;
|
||||
}
|
||||
None
|
||||
}
|
||||
|
|
@ -61,13 +61,13 @@ fn peels_blocks_incl_unsafe<'a>(expr: &'a Expr<'a>) -> &'a Expr<'a> {
|
|||
// }
|
||||
// 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) {
|
||||
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`
|
||||
if let ExprKind::Call(callee, [arg]) = inner_expr.kind {
|
||||
return ctxt == expr.span.ctxt()
|
||||
&& is_res_lang_ctor(cx, path_res(cx, callee), OptionSome)
|
||||
&& path_to_local_id(arg, target);
|
||||
}
|
||||
&& let ExprKind::Call(callee, [arg]) = inner_expr.kind
|
||||
{
|
||||
return ctxt == expr.span.ctxt()
|
||||
&& is_res_lang_ctor(cx, path_res(cx, callee), OptionSome)
|
||||
&& path_to_local_id(arg, target);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,17 +76,18 @@ where
|
|||
&& first_attrs.is_empty()
|
||||
&& iter.all(|arm| find_bool_lit(&arm.2.kind).is_some_and(|b| b == b0) && arm.3.is_none() && arm.0.is_empty())
|
||||
{
|
||||
if let Some(last_pat) = last_pat_opt {
|
||||
if !is_wild(last_pat) {
|
||||
return false;
|
||||
}
|
||||
if let Some(last_pat) = last_pat_opt
|
||||
&& !is_wild(last_pat)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for arm in iter_without_last.clone() {
|
||||
if let Some(pat) = arm.1 {
|
||||
if !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, pat.hir_id) && is_some(pat.kind) {
|
||||
return false;
|
||||
}
|
||||
if let Some(pat) = arm.1
|
||||
&& !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, pat.hir_id)
|
||||
&& is_some(pat.kind)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -113,10 +114,10 @@ where
|
|||
|
||||
// strip potential borrows (#6503), but only if the type is a reference
|
||||
let mut ex_new = ex;
|
||||
if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind {
|
||||
if let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() {
|
||||
ex_new = ex_inner;
|
||||
}
|
||||
if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind
|
||||
&& let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind()
|
||||
{
|
||||
ex_new = ex_inner;
|
||||
}
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
|
|
|||
|
|
@ -178,24 +178,24 @@ 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 let Some(parent_expr) = get_parent_expr(cx, match_expr) {
|
||||
if let ExprKind::Closure { .. } = parent_expr.kind {
|
||||
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}");
|
||||
}
|
||||
if let Some(parent_expr) = get_parent_expr(cx, match_expr)
|
||||
&& let ExprKind::Closure { .. } = parent_expr.kind
|
||||
{
|
||||
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}");
|
||||
}
|
||||
|
||||
// If the parent is already an arm, and the body is another match statement,
|
||||
// we need curly braces around suggestion
|
||||
if let Node::Arm(arm) = &cx.tcx.parent_hir_node(match_expr.hir_id) {
|
||||
if let ExprKind::Match(..) = arm.body.kind {
|
||||
cbrace_end = format!("\n{indent}}}");
|
||||
// Fix body indent due to the match
|
||||
indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
|
||||
cbrace_start = format!("{{\n{indent}");
|
||||
}
|
||||
if let Node::Arm(arm) = &cx.tcx.parent_hir_node(match_expr.hir_id)
|
||||
&& let ExprKind::Match(..) = arm.body.kind
|
||||
{
|
||||
cbrace_end = format!("\n{indent}}}");
|
||||
// Fix body indent due to the match
|
||||
indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
|
||||
cbrace_start = format!("{{\n{indent}");
|
||||
}
|
||||
|
||||
let assignment_str = assignment.map_or_else(String::new, |span| {
|
||||
|
|
|
|||
|
|
@ -26,10 +26,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>, arm
|
|||
&& let ty::Str = ty.kind()
|
||||
{
|
||||
let mut visitor = MatchExprVisitor { cx };
|
||||
if let ControlFlow::Break(case_method) = visitor.visit_expr(scrutinee) {
|
||||
if let Some((bad_case_span, bad_case_sym)) = verify_case(&case_method, arms) {
|
||||
lint(cx, &case_method, bad_case_span, bad_case_sym.as_str());
|
||||
}
|
||||
if let ControlFlow::Break(case_method) = visitor.visit_expr(scrutinee)
|
||||
&& let Some((bad_case_span, bad_case_sym)) = verify_case(&case_method, arms)
|
||||
{
|
||||
lint(cx, &case_method, bad_case_span, bad_case_sym.as_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,18 +80,20 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
|
|||
path
|
||||
},
|
||||
PatKind::TupleStruct(path, patterns, ..) => {
|
||||
if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() {
|
||||
if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p)) {
|
||||
missing_variants.retain(|e| e.ctor_def_id() != Some(id));
|
||||
}
|
||||
if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id()
|
||||
&& arm.guard.is_none()
|
||||
&& patterns.iter().all(|p| !is_refutable(cx, p))
|
||||
{
|
||||
missing_variants.retain(|e| e.ctor_def_id() != Some(id));
|
||||
}
|
||||
path
|
||||
},
|
||||
PatKind::Struct(path, patterns, ..) => {
|
||||
if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() {
|
||||
if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p.pat)) {
|
||||
missing_variants.retain(|e| e.def_id != id);
|
||||
}
|
||||
if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id()
|
||||
&& arm.guard.is_none()
|
||||
&& patterns.iter().all(|p| !is_refutable(cx, p.pat))
|
||||
{
|
||||
missing_variants.retain(|e| e.def_id != id);
|
||||
}
|
||||
path
|
||||
},
|
||||
|
|
|
|||
|
|
@ -26,11 +26,12 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<'
|
|||
if !matching_wild {
|
||||
// Looking for unused bindings (i.e.: `_e`)
|
||||
for pat in inner {
|
||||
if let PatKind::Binding(_, id, ident, None) = pat.kind {
|
||||
if ident.as_str().starts_with('_') && !is_local_used(cx, arm.body, id) {
|
||||
ident_bind_name = ident.name;
|
||||
matching_wild = true;
|
||||
}
|
||||
if let PatKind::Binding(_, id, ident, None) = pat.kind
|
||||
&& ident.as_str().starts_with('_')
|
||||
&& !is_local_used(cx, arm.body, id)
|
||||
{
|
||||
ident_bind_name = ident.name;
|
||||
matching_wild = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,10 +67,10 @@ fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>])
|
|||
for arm in arms {
|
||||
let arm_expr = peel_blocks_with_stmt(arm.body);
|
||||
|
||||
if let Some(guard_expr) = &arm.guard {
|
||||
if guard_expr.can_have_side_effects() {
|
||||
return false;
|
||||
}
|
||||
if let Some(guard_expr) = &arm.guard
|
||||
&& guard_expr.can_have_side_effects()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if let PatKind::Wild = arm.pat.kind {
|
||||
|
|
|
|||
|
|
@ -11,17 +11,17 @@ use super::MATCH_OVERLAPPING_ARM;
|
|||
pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) {
|
||||
if arms.len() >= 2 && cx.typeck_results().expr_ty(ex).is_integral() {
|
||||
let ranges = all_ranges(cx, arms, cx.typeck_results().expr_ty(ex));
|
||||
if !ranges.is_empty() {
|
||||
if let Some((start, end)) = overlapping(&ranges) {
|
||||
span_lint_and_note(
|
||||
cx,
|
||||
MATCH_OVERLAPPING_ARM,
|
||||
start.span,
|
||||
"some ranges overlap",
|
||||
Some(end.span),
|
||||
"overlaps with this",
|
||||
);
|
||||
}
|
||||
if !ranges.is_empty()
|
||||
&& let Some((start, end)) = overlapping(&ranges)
|
||||
{
|
||||
span_lint_and_note(
|
||||
cx,
|
||||
MATCH_OVERLAPPING_ARM,
|
||||
start.span,
|
||||
"some ranges overlap",
|
||||
Some(end.span),
|
||||
"overlaps with this",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -182,17 +182,16 @@ 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() {
|
||||
if get_attr(
|
||||
if let Some(adt) = ty.ty_adt_def()
|
||||
&& get_attr(
|
||||
self.cx.sess(),
|
||||
self.cx.tcx.get_attrs_unchecked(adt.did()),
|
||||
"has_significant_drop",
|
||||
)
|
||||
.count()
|
||||
> 0
|
||||
{
|
||||
return true;
|
||||
}
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if !self.seen_types.insert(ty) {
|
||||
|
|
|
|||
|
|
@ -15,18 +15,18 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arms: &[Arm<'_>]) {
|
|||
return;
|
||||
}
|
||||
for arm in arms {
|
||||
if let PatKind::Or(fields) = arm.pat.kind {
|
||||
if let PatKind::Or(fields) = arm.pat.kind
|
||||
// look for multiple fields in this arm that contains at least one Wild pattern
|
||||
if fields.len() > 1 && fields.iter().any(is_wild) {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
WILDCARD_IN_OR_PATTERNS,
|
||||
arm.pat.span,
|
||||
"wildcard pattern covers any other pattern as it will match anyway",
|
||||
None,
|
||||
"consider handling `_` separately",
|
||||
);
|
||||
}
|
||||
&& fields.len() > 1 && fields.iter().any(is_wild)
|
||||
{
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
WILDCARD_IN_OR_PATTERNS,
|
||||
arm.pat.span,
|
||||
"wildcard pattern covers any other pattern as it will match anyway",
|
||||
None,
|
||||
"consider handling `_` separately",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -192,10 +192,10 @@ impl BindInsteadOfMap {
|
|||
}
|
||||
|
||||
fn is_variant(&self, cx: &LateContext<'_>, res: Res) -> bool {
|
||||
if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res {
|
||||
if let Some(variant_id) = cx.tcx.lang_items().get(self.variant_lang_item) {
|
||||
return cx.tcx.parent(id) == variant_id;
|
||||
}
|
||||
if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res
|
||||
&& let Some(variant_id) = cx.tcx.lang_items().get(self.variant_lang_item)
|
||||
{
|
||||
return cx.tcx.parent(id) == variant_id;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,13 +19,13 @@ pub(super) fn check<'tcx>(
|
|||
arg: &'tcx Expr<'_>,
|
||||
msrv: Msrv,
|
||||
) {
|
||||
if let ExprKind::MethodCall(path_segment, ..) = recv.kind {
|
||||
if matches!(
|
||||
if let ExprKind::MethodCall(path_segment, ..) = recv.kind
|
||||
&& matches!(
|
||||
path_segment.ident.name.as_str(),
|
||||
"to_lowercase" | "to_uppercase" | "to_ascii_lowercase" | "to_ascii_uppercase"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
|
||||
|
|
|
|||
|
|
@ -40,10 +40,10 @@ pub(super) fn check(
|
|||
.map_or_else(|| cx.typeck_results().expr_ty(arg), |a| a.target);
|
||||
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
if let ty::Ref(_, inner, _) = arg_ty.kind() {
|
||||
if let ty::Ref(..) = inner.kind() {
|
||||
return; // don't report clone_on_copy
|
||||
}
|
||||
if let ty::Ref(_, inner, _) = arg_ty.kind()
|
||||
&& let ty::Ref(..) = inner.kind()
|
||||
{
|
||||
return; // don't report clone_on_copy
|
||||
}
|
||||
|
||||
if is_copy(cx, ty) {
|
||||
|
|
|
|||
|
|
@ -54,10 +54,11 @@ pub(super) fn check<'tcx>(
|
|||
if is_type_lang_item(cx, arg_ty, hir::LangItem::String) {
|
||||
return false;
|
||||
}
|
||||
if let ty::Ref(_, ty, ..) = arg_ty.kind() {
|
||||
if ty.is_str() && can_be_static_str(cx, arg) {
|
||||
return false;
|
||||
}
|
||||
if let ty::Ref(_, ty, ..) = arg_ty.kind()
|
||||
&& ty.is_str()
|
||||
&& can_be_static_str(cx, arg)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,15 +14,13 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, receiver: &Expr<'_
|
|||
if expr.span.in_external_macro(cx.sess().source_map()) || !receiver.span.eq_ctxt(expr.span) {
|
||||
return;
|
||||
}
|
||||
if let Some(parent) = get_parent_expr(cx, expr) {
|
||||
if let Some(parent) = get_parent_expr(cx, parent) {
|
||||
if is_inside_always_const_context(cx.tcx, expr.hir_id)
|
||||
&& let Some(macro_call) = root_macro_call(parent.span)
|
||||
&& is_assert_macro(cx, macro_call.def_id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
if let Some(parent) = get_parent_expr(cx, expr)
|
||||
&& let Some(parent) = get_parent_expr(cx, parent)
|
||||
&& is_inside_always_const_context(cx.tcx, expr.hir_id)
|
||||
&& let Some(macro_call) = root_macro_call(parent.span)
|
||||
&& is_assert_macro(cx, macro_call.def_id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
let init_expr = expr_or_init(cx, receiver);
|
||||
if !receiver.span.eq_ctxt(init_expr.span) {
|
||||
|
|
|
|||
|
|
@ -8,14 +8,14 @@ use rustc_span::sym;
|
|||
use super::ITERATOR_STEP_BY_ZERO;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) {
|
||||
if is_trait_method(cx, expr, sym::Iterator) {
|
||||
if let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval(arg) {
|
||||
span_lint(
|
||||
cx,
|
||||
ITERATOR_STEP_BY_ZERO,
|
||||
expr.span,
|
||||
"`Iterator::step_by(0)` will panic at runtime",
|
||||
);
|
||||
}
|
||||
if is_trait_method(cx, expr, sym::Iterator)
|
||||
&& let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval(arg)
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
ITERATOR_STEP_BY_ZERO,
|
||||
expr.span,
|
||||
"`Iterator::step_by(0)` will panic at runtime",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,15 +106,15 @@ fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<MinMax> {
|
|||
};
|
||||
|
||||
let check_lit = |expr: &hir::Expr<'_>, check_min: bool| {
|
||||
if let hir::ExprKind::Lit(lit) = &expr.kind {
|
||||
if let ast::LitKind::Int(value, _) = lit.node {
|
||||
if value == maxval {
|
||||
return Some(MinMax::Max);
|
||||
}
|
||||
if let hir::ExprKind::Lit(lit) = &expr.kind
|
||||
&& let ast::LitKind::Int(value, _) = lit.node
|
||||
{
|
||||
if value == maxval {
|
||||
return Some(MinMax::Max);
|
||||
}
|
||||
|
||||
if check_min && value == minval {
|
||||
return Some(MinMax::Min);
|
||||
}
|
||||
if check_min && value == minval {
|
||||
return Some(MinMax::Min);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -125,10 +125,10 @@ fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<MinMax> {
|
|||
return r;
|
||||
}
|
||||
|
||||
if ty.is_signed() {
|
||||
if let hir::ExprKind::Unary(hir::UnOp::Neg, val) = &expr.kind {
|
||||
return check_lit(val, true);
|
||||
}
|
||||
if ty.is_signed()
|
||||
&& let hir::ExprKind::Unary(hir::UnOp::Neg, val) = &expr.kind
|
||||
{
|
||||
return check_lit(val, true);
|
||||
}
|
||||
|
||||
None
|
||||
|
|
|
|||
|
|
@ -51,19 +51,19 @@ pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_
|
|||
let closure_expr = peel_blocks(closure_body.value);
|
||||
match closure_body.params[0].pat.kind {
|
||||
hir::PatKind::Ref(inner, Mutability::Not) => {
|
||||
if let hir::PatKind::Binding(hir::BindingMode::NONE, .., name, None) = inner.kind {
|
||||
if ident_eq(name, closure_expr) {
|
||||
lint_explicit_closure(cx, e.span, recv.span, true, msrv);
|
||||
}
|
||||
if let hir::PatKind::Binding(hir::BindingMode::NONE, .., name, None) = inner.kind
|
||||
&& ident_eq(name, closure_expr)
|
||||
{
|
||||
lint_explicit_closure(cx, e.span, recv.span, true, msrv);
|
||||
}
|
||||
},
|
||||
hir::PatKind::Binding(hir::BindingMode::NONE, .., name, None) => {
|
||||
match closure_expr.kind {
|
||||
hir::ExprKind::Unary(hir::UnOp::Deref, inner) => {
|
||||
if ident_eq(name, inner) {
|
||||
if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() {
|
||||
lint_explicit_closure(cx, e.span, recv.span, true, msrv);
|
||||
}
|
||||
if ident_eq(name, inner)
|
||||
&& let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind()
|
||||
{
|
||||
lint_explicit_closure(cx, e.span, recv.span, true, msrv);
|
||||
}
|
||||
},
|
||||
hir::ExprKind::MethodCall(method, obj, [], _) => {
|
||||
|
|
|
|||
|
|
@ -4667,11 +4667,12 @@ impl_lint_pass!(Methods => [
|
|||
pub fn method_call<'tcx>(
|
||||
recv: &'tcx Expr<'tcx>,
|
||||
) -> Option<(&'tcx str, &'tcx Expr<'tcx>, &'tcx [Expr<'tcx>], Span, Span)> {
|
||||
if let ExprKind::MethodCall(path, receiver, args, call_span) = recv.kind {
|
||||
if !args.iter().any(|e| e.span.from_expansion()) && !receiver.span.from_expansion() {
|
||||
let name = path.ident.name.as_str();
|
||||
return Some((name, receiver, args, path.ident.span, call_span));
|
||||
}
|
||||
if let ExprKind::MethodCall(path, receiver, args, call_span) = recv.kind
|
||||
&& !args.iter().any(|e| e.span.from_expansion())
|
||||
&& !receiver.span.from_expansion()
|
||||
{
|
||||
let name = path.ident.name.as_str();
|
||||
return Some((name, receiver, args, path.ident.span, call_span));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
|
|
|||
|
|
@ -377,20 +377,20 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> {
|
|||
return;
|
||||
}
|
||||
|
||||
if let Some(hir_id) = path_to_local(recv) {
|
||||
if let Some(index) = self.hir_id_uses_map.remove(&hir_id) {
|
||||
if self
|
||||
.illegal_mutable_capture_ids
|
||||
.intersection(&self.current_mutably_captured_ids)
|
||||
.next()
|
||||
.is_none()
|
||||
{
|
||||
if let Some(hir_id) = self.current_statement_hir_id {
|
||||
self.hir_id_uses_map.insert(hir_id, index);
|
||||
}
|
||||
} else {
|
||||
self.uses[index] = None;
|
||||
if let Some(hir_id) = path_to_local(recv)
|
||||
&& let Some(index) = self.hir_id_uses_map.remove(&hir_id)
|
||||
{
|
||||
if self
|
||||
.illegal_mutable_capture_ids
|
||||
.intersection(&self.current_mutably_captured_ids)
|
||||
.next()
|
||||
.is_none()
|
||||
{
|
||||
if let Some(hir_id) = self.current_statement_hir_id {
|
||||
self.hir_id_uses_map.insert(hir_id, index);
|
||||
}
|
||||
} else {
|
||||
self.uses[index] = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,26 +9,27 @@ use super::NEEDLESS_OPTION_TAKE;
|
|||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) {
|
||||
// Checks if expression type is equal to sym::Option and if the expr is not a syntactic place
|
||||
if !recv.is_syntactic_place_expr() && is_expr_option(cx, recv) {
|
||||
if let Some(function_name) = source_of_temporary_value(recv) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
NEEDLESS_OPTION_TAKE,
|
||||
expr.span,
|
||||
"called `Option::take()` on a temporary value",
|
||||
|diag| {
|
||||
diag.note(format!(
|
||||
"`{function_name}` creates a temporary value, so calling take() has no effect"
|
||||
));
|
||||
diag.span_suggestion(
|
||||
expr.span.with_lo(recv.span.hi()),
|
||||
"remove",
|
||||
"",
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
if !recv.is_syntactic_place_expr()
|
||||
&& is_expr_option(cx, recv)
|
||||
&& let Some(function_name) = source_of_temporary_value(recv)
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
NEEDLESS_OPTION_TAKE,
|
||||
expr.span,
|
||||
"called `Option::take()` on a temporary value",
|
||||
|diag| {
|
||||
diag.note(format!(
|
||||
"`{function_name}` creates a temporary value, so calling take() has no effect"
|
||||
));
|
||||
diag.span_suggestion(
|
||||
expr.span.with_lo(recv.span.hi()),
|
||||
"remove",
|
||||
"",
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -44,10 +45,10 @@ fn is_expr_option(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
|||
fn source_of_temporary_value<'a>(expr: &'a Expr<'_>) -> Option<&'a str> {
|
||||
match expr.peel_borrows().kind {
|
||||
ExprKind::Call(function, _) => {
|
||||
if let ExprKind::Path(QPath::Resolved(_, func_path)) = function.kind {
|
||||
if !func_path.segments.is_empty() {
|
||||
return Some(func_path.segments[0].ident.name.as_str());
|
||||
}
|
||||
if let ExprKind::Path(QPath::Resolved(_, func_path)) = function.kind
|
||||
&& !func_path.segments.is_empty()
|
||||
{
|
||||
return Some(func_path.segments[0].ident.name.as_str());
|
||||
}
|
||||
if let ExprKind::Path(QPath::TypeRelative(_, func_path_segment)) = function.kind {
|
||||
return Some(func_path_segment.ident.name.as_str());
|
||||
|
|
|
|||
|
|
@ -15,21 +15,22 @@ use super::SEEK_FROM_CURRENT;
|
|||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) {
|
||||
let ty = cx.typeck_results().expr_ty(recv);
|
||||
|
||||
if let Some(def_id) = cx.tcx.get_diagnostic_item(sym::IoSeek) {
|
||||
if implements_trait(cx, ty, def_id, &[]) && arg_is_seek_from_current(cx, arg) {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let snip = snippet_with_applicability(cx, recv.span, "..", &mut applicability);
|
||||
if let Some(def_id) = cx.tcx.get_diagnostic_item(sym::IoSeek)
|
||||
&& implements_trait(cx, ty, def_id, &[])
|
||||
&& arg_is_seek_from_current(cx, arg)
|
||||
{
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let snip = snippet_with_applicability(cx, recv.span, "..", &mut applicability);
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SEEK_FROM_CURRENT,
|
||||
expr.span,
|
||||
"using `SeekFrom::Current` to start from current position",
|
||||
"replace with",
|
||||
format!("{snip}.stream_position()"),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SEEK_FROM_CURRENT,
|
||||
expr.span,
|
||||
"using `SeekFrom::Current` to start from current position",
|
||||
"replace with",
|
||||
format!("{snip}.stream_position()"),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -238,15 +238,14 @@ fn indirect_usage<'tcx>(
|
|||
unwrap_kind: Some(unwrap_kind),
|
||||
..
|
||||
} = iter_usage
|
||||
&& parent_id == local_hir_id
|
||||
{
|
||||
if parent_id == local_hir_id {
|
||||
return Some(IndirectUsage {
|
||||
name: ident.name,
|
||||
span: stmt.span,
|
||||
init_expr,
|
||||
unwrap_kind,
|
||||
});
|
||||
}
|
||||
return Some(IndirectUsage {
|
||||
name: ident.name,
|
||||
span: stmt.span,
|
||||
init_expr,
|
||||
unwrap_kind,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,11 +13,11 @@ pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, count_recv: &hir::Expr<
|
|||
&& let closure_body = cx.tcx.hir_body(closure.body)
|
||||
&& !cx.typeck_results().expr_ty(closure_body.value).is_unit()
|
||||
{
|
||||
if let Some(map_mutated_vars) = mutated_variables(closure_body.value, cx) {
|
||||
if let Some(map_mutated_vars) = mutated_variables(closure_body.value, cx)
|
||||
// A variable is used mutably inside of the closure. Suppress the lint.
|
||||
if !map_mutated_vars.is_empty() {
|
||||
return;
|
||||
}
|
||||
&& !map_mutated_vars.is_empty()
|
||||
{
|
||||
return;
|
||||
}
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
|
|
|
|||
|
|
@ -23,56 +23,56 @@ pub(super) fn check<'tcx>(
|
|||
let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
|
||||
let is_bool = cx.typeck_results().expr_ty(recv).is_bool();
|
||||
|
||||
if is_option || is_result || is_bool {
|
||||
if let hir::ExprKind::Closure(&hir::Closure { body, fn_decl, .. }) = arg.kind {
|
||||
let body = cx.tcx.hir_body(body);
|
||||
let body_expr = &body.value;
|
||||
if (is_option || is_result || is_bool)
|
||||
&& let hir::ExprKind::Closure(&hir::Closure { body, fn_decl, .. }) = arg.kind
|
||||
{
|
||||
let body = cx.tcx.hir_body(body);
|
||||
let body_expr = &body.value;
|
||||
|
||||
if usage::BindingUsageFinder::are_params_used(cx, body) || is_from_proc_macro(cx, expr) {
|
||||
return false;
|
||||
}
|
||||
if usage::BindingUsageFinder::are_params_used(cx, body) || is_from_proc_macro(cx, expr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if eager_or_lazy::switch_to_eager_eval(cx, body_expr) {
|
||||
let msg = if is_option {
|
||||
"unnecessary closure used to substitute value for `Option::None`"
|
||||
} else if is_result {
|
||||
"unnecessary closure used to substitute value for `Result::Err`"
|
||||
} else {
|
||||
"unnecessary closure used with `bool::then`"
|
||||
};
|
||||
let applicability = if body
|
||||
.params
|
||||
.iter()
|
||||
// bindings are checked to be unused above
|
||||
.all(|param| matches!(param.pat.kind, hir::PatKind::Binding(..) | hir::PatKind::Wild))
|
||||
&& matches!(
|
||||
fn_decl.output,
|
||||
FnRetTy::DefaultReturn(_)
|
||||
| FnRetTy::Return(hir::Ty {
|
||||
kind: hir::TyKind::Infer(()),
|
||||
..
|
||||
})
|
||||
) {
|
||||
Applicability::MachineApplicable
|
||||
} else {
|
||||
// replacing the lambda may break type inference
|
||||
Applicability::MaybeIncorrect
|
||||
};
|
||||
if eager_or_lazy::switch_to_eager_eval(cx, body_expr) {
|
||||
let msg = if is_option {
|
||||
"unnecessary closure used to substitute value for `Option::None`"
|
||||
} else if is_result {
|
||||
"unnecessary closure used to substitute value for `Result::Err`"
|
||||
} else {
|
||||
"unnecessary closure used with `bool::then`"
|
||||
};
|
||||
let applicability = if body
|
||||
.params
|
||||
.iter()
|
||||
// bindings are checked to be unused above
|
||||
.all(|param| matches!(param.pat.kind, hir::PatKind::Binding(..) | hir::PatKind::Wild))
|
||||
&& matches!(
|
||||
fn_decl.output,
|
||||
FnRetTy::DefaultReturn(_)
|
||||
| FnRetTy::Return(hir::Ty {
|
||||
kind: hir::TyKind::Infer(()),
|
||||
..
|
||||
})
|
||||
) {
|
||||
Applicability::MachineApplicable
|
||||
} else {
|
||||
// replacing the lambda may break type inference
|
||||
Applicability::MaybeIncorrect
|
||||
};
|
||||
|
||||
// This is a duplicate of what's happening in clippy_lints::methods::method_call,
|
||||
// which isn't ideal, We want to get the method call span,
|
||||
// but prefer to avoid changing the signature of the function itself.
|
||||
if let hir::ExprKind::MethodCall(.., span) = expr.kind {
|
||||
span_lint_and_then(cx, UNNECESSARY_LAZY_EVALUATIONS, expr.span, msg, |diag| {
|
||||
diag.span_suggestion_verbose(
|
||||
span,
|
||||
format!("use `{simplify_using}` instead"),
|
||||
format!("{simplify_using}({})", snippet(cx, body_expr.span, "..")),
|
||||
applicability,
|
||||
);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
// This is a duplicate of what's happening in clippy_lints::methods::method_call,
|
||||
// which isn't ideal, We want to get the method call span,
|
||||
// but prefer to avoid changing the signature of the function itself.
|
||||
if let hir::ExprKind::MethodCall(.., span) = expr.kind {
|
||||
span_lint_and_then(cx, UNNECESSARY_LAZY_EVALUATIONS, expr.span, msg, |diag| {
|
||||
diag.span_suggestion_verbose(
|
||||
span,
|
||||
format!("use `{simplify_using}` instead"),
|
||||
format!("{simplify_using}({})", snippet(cx, body_expr.span, "..")),
|
||||
applicability,
|
||||
);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,14 +6,14 @@ use rustc_lint::EarlyContext;
|
|||
use super::BUILTIN_TYPE_SHADOW;
|
||||
|
||||
pub(super) fn check(cx: &EarlyContext<'_>, param: &GenericParam) {
|
||||
if let GenericParamKind::Type { .. } = param.kind {
|
||||
if let Some(prim_ty) = PrimTy::from_name(param.ident.name) {
|
||||
span_lint(
|
||||
cx,
|
||||
BUILTIN_TYPE_SHADOW,
|
||||
param.ident.span,
|
||||
format!("this generic shadows the built-in type `{}`", prim_ty.name()),
|
||||
);
|
||||
}
|
||||
if let GenericParamKind::Type { .. } = param.kind
|
||||
&& let Some(prim_ty) = PrimTy::from_name(param.ident.name)
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
BUILTIN_TYPE_SHADOW,
|
||||
param.ident.span,
|
||||
format!("this generic shadows the built-in type `{}`", prim_ty.name()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,20 +6,20 @@ use rustc_lint::EarlyContext;
|
|||
use super::REDUNDANT_PATTERN;
|
||||
|
||||
pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) {
|
||||
if let PatKind::Ident(ann, ident, Some(ref right)) = pat.kind {
|
||||
if let PatKind::Wild = right.kind {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
REDUNDANT_PATTERN,
|
||||
pat.span,
|
||||
format!(
|
||||
"the `{} @ _` pattern can be written as just `{}`",
|
||||
ident.name, ident.name,
|
||||
),
|
||||
"try",
|
||||
format!("{}{}", ann.prefix_str(), ident.name),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
if let PatKind::Ident(ann, ident, Some(ref right)) = pat.kind
|
||||
&& let PatKind::Wild = right.kind
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
REDUNDANT_PATTERN,
|
||||
pat.span,
|
||||
format!(
|
||||
"the `{} @ _` pattern can be written as just `{}`",
|
||||
ident.name, ident.name,
|
||||
),
|
||||
"try",
|
||||
format!("{}{}", ann.prefix_str(), ident.name),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,30 +7,30 @@ use rustc_span::Span;
|
|||
use super::UNNEEDED_WILDCARD_PATTERN;
|
||||
|
||||
pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) {
|
||||
if let PatKind::TupleStruct(_, _, ref patterns) | PatKind::Tuple(ref patterns) = pat.kind {
|
||||
if let Some(rest_index) = patterns.iter().position(|pat| pat.is_rest()) {
|
||||
if let Some((left_index, left_pat)) = patterns[..rest_index]
|
||||
.iter()
|
||||
.rev()
|
||||
.take_while(|pat| matches!(pat.kind, PatKind::Wild))
|
||||
.enumerate()
|
||||
.last()
|
||||
{
|
||||
span_lint(cx, left_pat.span.until(patterns[rest_index].span), left_index == 0);
|
||||
}
|
||||
if let PatKind::TupleStruct(_, _, ref patterns) | PatKind::Tuple(ref patterns) = pat.kind
|
||||
&& let Some(rest_index) = patterns.iter().position(|pat| pat.is_rest())
|
||||
{
|
||||
if let Some((left_index, left_pat)) = patterns[..rest_index]
|
||||
.iter()
|
||||
.rev()
|
||||
.take_while(|pat| matches!(pat.kind, PatKind::Wild))
|
||||
.enumerate()
|
||||
.last()
|
||||
{
|
||||
span_lint(cx, left_pat.span.until(patterns[rest_index].span), left_index == 0);
|
||||
}
|
||||
|
||||
if let Some((right_index, right_pat)) = patterns[rest_index + 1..]
|
||||
.iter()
|
||||
.take_while(|pat| matches!(pat.kind, PatKind::Wild))
|
||||
.enumerate()
|
||||
.last()
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
patterns[rest_index].span.shrink_to_hi().to(right_pat.span),
|
||||
right_index == 0,
|
||||
);
|
||||
}
|
||||
if let Some((right_index, right_pat)) = patterns[rest_index + 1..]
|
||||
.iter()
|
||||
.take_while(|pat| matches!(pat.kind, PatKind::Wild))
|
||||
.enumerate()
|
||||
.last()
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
patterns[rest_index].span.shrink_to_hi().to(right_pat.span),
|
||||
right_index == 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,10 +111,10 @@ impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch {
|
|||
// Checks if impl_param_name is the same as one of type_param_names,
|
||||
// and is in a different position
|
||||
fn mismatch_param_name(i: usize, impl_param_name: &String, type_param_names: &FxHashMap<&String, usize>) -> bool {
|
||||
if let Some(j) = type_param_names.get(impl_param_name) {
|
||||
if i != *j {
|
||||
return true;
|
||||
}
|
||||
if let Some(j) = type_param_names.get(impl_param_name)
|
||||
&& i != *j
|
||||
{
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -139,12 +139,11 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
|
|||
// Const fns are not allowed as methods in a trait.
|
||||
{
|
||||
let parent = cx.tcx.hir_get_parent_item(hir_id).def_id;
|
||||
if parent != CRATE_DEF_ID {
|
||||
if let hir::Node::Item(item) = cx.tcx.hir_node_by_def_id(parent) {
|
||||
if let hir::ItemKind::Trait(..) = &item.kind {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if parent != CRATE_DEF_ID
|
||||
&& let hir::Node::Item(item) = cx.tcx.hir_node_by_def_id(parent)
|
||||
&& let hir::ItemKind::Trait(..) = &item.kind
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -224,11 +224,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug {
|
|||
// NB: can't call cx.typeck_results() as we are not in a body
|
||||
&& let typeck_results = cx.tcx.typeck_body(*body_id)
|
||||
&& should_lint(cx, typeck_results, block)
|
||||
{
|
||||
// we intentionally only lint structs, see lint description
|
||||
if let ItemKind::Struct(_, data, _) = &self_item.kind {
|
||||
check_struct(cx, typeck_results, block, self_ty, item, data);
|
||||
}
|
||||
&& let ItemKind::Struct(_, data, _) = &self_item.kind
|
||||
{
|
||||
check_struct(cx, typeck_results, block, self_ty, item, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -160,12 +160,13 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline {
|
|||
AssocItemContainer::Impl => cx.tcx.impl_trait_ref(container_id).map(|t| t.skip_binder().def_id),
|
||||
};
|
||||
|
||||
if let Some(trait_def_id) = trait_def_id {
|
||||
if trait_def_id.is_local() && !cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) {
|
||||
// If a trait is being implemented for an item, and the
|
||||
// trait is not exported, we don't need #[inline]
|
||||
return;
|
||||
}
|
||||
if let Some(trait_def_id) = trait_def_id
|
||||
&& trait_def_id.is_local()
|
||||
&& !cx.effective_visibilities.is_exported(impl_item.owner_id.def_id)
|
||||
{
|
||||
// If a trait is being implemented for an item, and the
|
||||
// trait is not exported, we don't need #[inline]
|
||||
return;
|
||||
}
|
||||
|
||||
let attrs = cx.tcx.hir_attrs(impl_item.hir_id());
|
||||
|
|
|
|||
|
|
@ -135,10 +135,10 @@ impl<'tcx> DivergenceVisitor<'_, 'tcx> {
|
|||
}
|
||||
|
||||
fn report_diverging_sub_expr(&mut self, e: &Expr<'_>) {
|
||||
if let Some(macro_call) = root_macro_call_first_node(self.cx, e) {
|
||||
if self.cx.tcx.item_name(macro_call.def_id).as_str() == "todo" {
|
||||
return;
|
||||
}
|
||||
if let Some(macro_call) = root_macro_call_first_node(self.cx, e)
|
||||
&& self.cx.tcx.item_name(macro_call.def_id).as_str() == "todo"
|
||||
{
|
||||
return;
|
||||
}
|
||||
span_lint(self.cx, DIVERGING_SUB_EXPRESSION, e.span, "sub-expression diverges");
|
||||
}
|
||||
|
|
@ -372,10 +372,10 @@ impl<'tcx> Visitor<'tcx> for ReadVisitor<'_, 'tcx> {
|
|||
|
||||
/// Returns `true` if `expr` is the LHS of an assignment, like `expr = ...`.
|
||||
fn is_in_assignment_position(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
if let Some(parent) = get_parent_expr(cx, expr) {
|
||||
if let ExprKind::Assign(lhs, ..) = parent.kind {
|
||||
return lhs.hir_id == expr.hir_id;
|
||||
}
|
||||
if let Some(parent) = get_parent_expr(cx, expr)
|
||||
&& let ExprKind::Assign(lhs, ..) = parent.kind
|
||||
{
|
||||
return lhs.hir_id == expr.hir_id;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,22 +119,22 @@ impl EarlyLintPass for ModStyle {
|
|||
}
|
||||
|
||||
for folder in &folder_segments {
|
||||
if !mod_folders.contains(folder) {
|
||||
if let Some((file, path)) = file_map.get(folder) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
SELF_NAMED_MODULE_FILES,
|
||||
Span::new(file.start_pos, file.start_pos, SyntaxContext::root(), None),
|
||||
format!("`mod.rs` files are required, found `{}`", path.display()),
|
||||
|diag| {
|
||||
let mut correct = path.to_path_buf();
|
||||
correct.pop();
|
||||
correct.push(folder);
|
||||
correct.push("mod.rs");
|
||||
diag.help(format!("move `{}` to `{}`", path.display(), correct.display(),));
|
||||
},
|
||||
);
|
||||
}
|
||||
if !mod_folders.contains(folder)
|
||||
&& let Some((file, path)) = file_map.get(folder)
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
SELF_NAMED_MODULE_FILES,
|
||||
Span::new(file.start_pos, file.start_pos, SyntaxContext::root(), None),
|
||||
format!("`mod.rs` files are required, found `{}`", path.display()),
|
||||
|diag| {
|
||||
let mut correct = path.to_path_buf();
|
||||
correct.pop();
|
||||
correct.push(folder);
|
||||
correct.push("mod.rs");
|
||||
diag.help(format!("move `{}` to `{}`", path.display(), correct.display(),));
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -142,10 +142,9 @@ fn collect_unsafe_exprs<'tcx>(
|
|||
.typeck_results()
|
||||
.type_dependent_def_id(expr.hir_id)
|
||||
.map(|def_id| cx.tcx.fn_sig(def_id))
|
||||
&& sig.skip_binder().safety().is_unsafe()
|
||||
{
|
||||
if sig.skip_binder().safety().is_unsafe() {
|
||||
unsafe_ops.push(("unsafe method call occurs here", expr.span));
|
||||
}
|
||||
unsafe_ops.push(("unsafe method call occurs here", expr.span));
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -82,10 +82,10 @@ impl<'tcx> LateLintPass<'tcx> for MutableKeyType<'tcx> {
|
|||
}
|
||||
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'tcx>) {
|
||||
if let hir::ImplItemKind::Fn(ref sig, ..) = item.kind {
|
||||
if trait_ref_of_method(cx, item.owner_id.def_id).is_none() {
|
||||
self.check_sig(cx, item.owner_id.def_id, sig.decl);
|
||||
}
|
||||
if let hir::ImplItemKind::Fn(ref sig, ..) = item.kind
|
||||
&& trait_ref_of_method(cx, item.owner_id.def_id).is_none()
|
||||
{
|
||||
self.check_sig(cx, item.owner_id.def_id, sig.decl);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -77,16 +77,16 @@ impl<'tcx> intravisit::Visitor<'tcx> for MutVisitor<'_, 'tcx> {
|
|||
expr.span,
|
||||
"generally you want to avoid `&mut &mut _` if possible",
|
||||
);
|
||||
} else if let ty::Ref(_, ty, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() {
|
||||
if ty.peel_refs().is_sized(self.cx.tcx, self.cx.typing_env()) {
|
||||
span_lint_hir(
|
||||
self.cx,
|
||||
MUT_MUT,
|
||||
expr.hir_id,
|
||||
expr.span,
|
||||
"this expression mutably borrows a mutable reference. Consider reborrowing",
|
||||
);
|
||||
}
|
||||
} else if let ty::Ref(_, ty, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind()
|
||||
&& ty.peel_refs().is_sized(self.cx.tcx, self.cx.typing_env())
|
||||
{
|
||||
span_lint_hir(
|
||||
self.cx,
|
||||
MUT_MUT,
|
||||
expr.hir_id,
|
||||
expr.span,
|
||||
"this expression mutably borrows a mutable reference. Consider reborrowing",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,14 +97,13 @@ impl<'tcx> Visitor<'tcx> for MutArgVisitor<'_, 'tcx> {
|
|||
return;
|
||||
},
|
||||
ExprKind::Path(_) => {
|
||||
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
|
||||
if adj
|
||||
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id)
|
||||
&& adj
|
||||
.iter()
|
||||
.any(|a| matches!(a.target.kind(), ty::Ref(_, _, Mutability::Mut)))
|
||||
{
|
||||
self.found = true;
|
||||
return;
|
||||
}
|
||||
{
|
||||
self.found = true;
|
||||
return;
|
||||
}
|
||||
},
|
||||
// Don't check await desugars
|
||||
|
|
|
|||
|
|
@ -91,19 +91,19 @@ declare_lint_pass!(Mutex => [MUTEX_ATOMIC, MUTEX_INTEGER]);
|
|||
impl<'tcx> LateLintPass<'tcx> for Mutex {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
if let ty::Adt(_, subst) = ty.kind() {
|
||||
if is_type_diagnostic_item(cx, ty, sym::Mutex) {
|
||||
let mutex_param = subst.type_at(0);
|
||||
if let Some(atomic_name) = get_atomic_name(mutex_param) {
|
||||
let msg = format!(
|
||||
"consider using an `{atomic_name}` instead of a `Mutex` here; if you just want the locking \
|
||||
if let ty::Adt(_, subst) = ty.kind()
|
||||
&& is_type_diagnostic_item(cx, ty, sym::Mutex)
|
||||
{
|
||||
let mutex_param = subst.type_at(0);
|
||||
if let Some(atomic_name) = get_atomic_name(mutex_param) {
|
||||
let msg = format!(
|
||||
"consider using an `{atomic_name}` instead of a `Mutex` here; if you just want the locking \
|
||||
behavior and not the internal type, consider using `Mutex<()>`"
|
||||
);
|
||||
match *mutex_param.kind() {
|
||||
ty::Uint(t) if t != UintTy::Usize => span_lint(cx, MUTEX_INTEGER, expr.span, msg),
|
||||
ty::Int(t) if t != IntTy::Isize => span_lint(cx, MUTEX_INTEGER, expr.span, msg),
|
||||
_ => span_lint(cx, MUTEX_ATOMIC, expr.span, msg),
|
||||
}
|
||||
);
|
||||
match *mutex_param.kind() {
|
||||
ty::Uint(t) if t != UintTy::Usize => span_lint(cx, MUTEX_INTEGER, expr.span, msg),
|
||||
ty::Int(t) if t != IntTy::Isize => span_lint(cx, MUTEX_INTEGER, expr.span, msg),
|
||||
_ => span_lint(cx, MUTEX_ATOMIC, expr.span, msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -426,10 +426,10 @@ fn fetch_bool_block(expr: &Expr<'_>) -> Option<Expression> {
|
|||
}
|
||||
|
||||
fn fetch_bool_expr(expr: &Expr<'_>) -> Option<bool> {
|
||||
if let ExprKind::Lit(lit_ptr) = peel_blocks(expr).kind {
|
||||
if let LitKind::Bool(value) = lit_ptr.node {
|
||||
return Some(value);
|
||||
}
|
||||
if let ExprKind::Lit(lit_ptr) = peel_blocks(expr).kind
|
||||
&& let LitKind::Bool(value) = lit_ptr.node
|
||||
{
|
||||
return Some(value);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,11 +86,11 @@ fn should_skip<'tcx>(
|
|||
return false;
|
||||
}
|
||||
|
||||
if let PatKind::Binding(.., name, _) = arg.pat.kind {
|
||||
if let PatKind::Binding(.., name, _) = arg.pat.kind
|
||||
// If it's a potentially unused variable, we don't check it.
|
||||
if name.name == kw::Underscore || name.as_str().starts_with('_') {
|
||||
return true;
|
||||
}
|
||||
&& (name.name == kw::Underscore || name.as_str().starts_with('_'))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// All spans generated from a proc-macro invocation are the same...
|
||||
|
|
@ -164,13 +164,13 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
|
|||
};
|
||||
|
||||
// Exclude non-inherent impls
|
||||
if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) {
|
||||
if matches!(
|
||||
if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id)
|
||||
&& matches!(
|
||||
item.kind,
|
||||
ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let fn_sig = cx.tcx.fn_sig(fn_def_id).instantiate_identity();
|
||||
|
|
@ -353,10 +353,10 @@ impl MutablyUsedVariablesCtxt<'_> {
|
|||
for (parent, node) in self.tcx.hir_parent_iter(item) {
|
||||
if let Some(fn_sig) = self.tcx.hir_fn_sig_by_hir_id(parent) {
|
||||
return fn_sig.header.is_unsafe();
|
||||
} else if let Node::Block(block) = node {
|
||||
if matches!(block.rules, BlockCheckMode::UnsafeBlock(_)) {
|
||||
return true;
|
||||
}
|
||||
} else if let Node::Block(block) = node
|
||||
&& matches!(block.rules, BlockCheckMode::UnsafeBlock(_))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
|
|
@ -426,10 +426,10 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> {
|
|||
// upon!
|
||||
self.add_mutably_used_var(*vid);
|
||||
}
|
||||
} else if borrow == ty::BorrowKind::Immutable {
|
||||
} else if borrow == ty::BorrowKind::Immutable
|
||||
// If there is an `async block`, it'll contain a call to a closure which we need to
|
||||
// go into to ensure all "mutate" checks are found.
|
||||
if let Node::Expr(Expr {
|
||||
&& let Node::Expr(Expr {
|
||||
kind:
|
||||
ExprKind::Call(
|
||||
_,
|
||||
|
|
@ -442,9 +442,8 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> {
|
|||
),
|
||||
..
|
||||
}) = self.tcx.hir_node(cmt.hir_id)
|
||||
{
|
||||
self.async_closures.insert(*def_id);
|
||||
}
|
||||
{
|
||||
self.async_closures.insert(*def_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -460,10 +459,9 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> {
|
|||
}),
|
||||
..
|
||||
} = &cmt.place
|
||||
&& !projections.is_empty()
|
||||
{
|
||||
if !projections.is_empty() {
|
||||
self.add_mutably_used_var(*vid);
|
||||
}
|
||||
self.add_mutably_used_var(*vid);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -477,10 +475,9 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> {
|
|||
}),
|
||||
..
|
||||
} = &cmt.place
|
||||
&& self.is_in_unsafe_block(id)
|
||||
{
|
||||
if self.is_in_unsafe_block(id) {
|
||||
self.add_mutably_used_var(*vid);
|
||||
}
|
||||
self.add_mutably_used_var(*vid);
|
||||
}
|
||||
self.prev_bind = None;
|
||||
}
|
||||
|
|
@ -499,15 +496,14 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> {
|
|||
}),
|
||||
..
|
||||
} = &cmt.place
|
||||
&& let FakeReadCause::ForLet(Some(inner)) = cause
|
||||
{
|
||||
if let FakeReadCause::ForLet(Some(inner)) = cause {
|
||||
// Seems like we are inside an async function. We need to store the closure `DefId`
|
||||
// to go through it afterwards.
|
||||
self.async_closures.insert(inner);
|
||||
self.add_alias(cmt.hir_id, *vid);
|
||||
self.prev_move_to_closure.insert(*vid);
|
||||
self.prev_bind = None;
|
||||
}
|
||||
// Seems like we are inside an async function. We need to store the closure `DefId`
|
||||
// to go through it afterwards.
|
||||
self.async_closures.insert(inner);
|
||||
self.add_alias(cmt.hir_id, *vid);
|
||||
self.prev_move_to_closure.insert(*vid);
|
||||
self.prev_bind = None;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -522,10 +518,9 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> {
|
|||
}),
|
||||
..
|
||||
} = &cmt.place
|
||||
&& self.is_in_unsafe_block(id)
|
||||
{
|
||||
if self.is_in_unsafe_block(id) {
|
||||
self.add_mutably_used_var(*vid);
|
||||
}
|
||||
self.add_mutably_used_var(*vid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,13 +98,13 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
|
|||
}
|
||||
|
||||
// Exclude non-inherent impls
|
||||
if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) {
|
||||
if matches!(
|
||||
if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id)
|
||||
&& matches!(
|
||||
item.kind,
|
||||
ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow `Borrow` or functions to be taken by value
|
||||
|
|
@ -197,20 +197,18 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
|
|||
{
|
||||
// Dereference suggestion
|
||||
let sugg = |diag: &mut Diag<'_, ()>| {
|
||||
if let ty::Adt(def, ..) = ty.kind() {
|
||||
if let Some(span) = cx.tcx.hir().span_if_local(def.did()) {
|
||||
if type_allowed_to_implement_copy(
|
||||
cx.tcx,
|
||||
cx.param_env,
|
||||
ty,
|
||||
traits::ObligationCause::dummy_with_span(span),
|
||||
rustc_hir::Safety::Safe,
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
diag.span_help(span, "or consider marking this type as `Copy`");
|
||||
}
|
||||
}
|
||||
if let ty::Adt(def, ..) = ty.kind()
|
||||
&& let Some(span) = cx.tcx.hir().span_if_local(def.did())
|
||||
&& type_allowed_to_implement_copy(
|
||||
cx.tcx,
|
||||
cx.param_env,
|
||||
ty,
|
||||
traits::ObligationCause::dummy_with_span(span),
|
||||
rustc_hir::Safety::Safe,
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
diag.span_help(span, "or consider marking this type as `Copy`");
|
||||
}
|
||||
|
||||
if is_type_diagnostic_item(cx, ty, sym::Vec)
|
||||
|
|
@ -254,29 +252,28 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
|
|||
return;
|
||||
}
|
||||
|
||||
if is_type_lang_item(cx, ty, LangItem::String) {
|
||||
if let Some(clone_spans) =
|
||||
if is_type_lang_item(cx, ty, LangItem::String)
|
||||
&& let Some(clone_spans) =
|
||||
get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")])
|
||||
{
|
||||
{
|
||||
diag.span_suggestion(
|
||||
input.span,
|
||||
"consider changing the type to",
|
||||
"&str",
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
|
||||
for (span, suggestion) in clone_spans {
|
||||
diag.span_suggestion(
|
||||
input.span,
|
||||
"consider changing the type to",
|
||||
"&str",
|
||||
span,
|
||||
span.get_source_text(cx)
|
||||
.map_or("change the call to".to_owned(), |src| format!("change `{src}` to")),
|
||||
suggestion,
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
|
||||
for (span, suggestion) in clone_spans {
|
||||
diag.span_suggestion(
|
||||
span,
|
||||
span.get_source_text(cx)
|
||||
.map_or("change the call to".to_owned(), |src| format!("change `{src}` to")),
|
||||
suggestion,
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
diag.span_suggestion_verbose(
|
||||
|
|
|
|||
|
|
@ -53,17 +53,16 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessUpdate {
|
|||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Struct(_, fields, StructTailExpr::Base(base)) = expr.kind {
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
if let ty::Adt(def, _) = ty.kind() {
|
||||
if fields.len() == def.non_enum_variant().fields.len()
|
||||
&& !def.variant(0_usize.into()).is_field_list_non_exhaustive()
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
NEEDLESS_UPDATE,
|
||||
base.span,
|
||||
"struct update has no effect, all the fields in the struct have already been specified",
|
||||
);
|
||||
}
|
||||
if let ty::Adt(def, _) = ty.kind()
|
||||
&& fields.len() == def.non_enum_variant().fields.len()
|
||||
&& !def.variant(0_usize.into()).is_field_list_non_exhaustive()
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
NEEDLESS_UPDATE,
|
||||
base.span,
|
||||
"struct update has no effect, all the fields in the struct have already been specified",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,14 +35,14 @@ declare_lint_pass!(NegMultiply => [NEG_MULTIPLY]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for NegMultiply {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Binary(ref op, left, right) = e.kind {
|
||||
if BinOpKind::Mul == op.node {
|
||||
match (&left.kind, &right.kind) {
|
||||
(&ExprKind::Unary(..), &ExprKind::Unary(..)) => {},
|
||||
(&ExprKind::Unary(UnOp::Neg, lit), _) => check_mul(cx, e.span, lit, right),
|
||||
(_, &ExprKind::Unary(UnOp::Neg, lit)) => check_mul(cx, e.span, lit, left),
|
||||
_ => {},
|
||||
}
|
||||
if let ExprKind::Binary(ref op, left, right) = e.kind
|
||||
&& BinOpKind::Mul == op.node
|
||||
{
|
||||
match (&left.kind, &right.kind) {
|
||||
(&ExprKind::Unary(..), &ExprKind::Unary(..)) => {},
|
||||
(&ExprKind::Unary(UnOp::Neg, lit), _) => check_mul(cx, e.span, lit, right),
|
||||
(_, &ExprKind::Unary(UnOp::Neg, lit)) => check_mul(cx, e.span, lit, left),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,10 +99,10 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
|
|||
let mut impls = HirIdSet::default();
|
||||
cx.tcx.for_each_impl(default_trait_id, |d| {
|
||||
let ty = cx.tcx.type_of(d).instantiate_identity();
|
||||
if let Some(ty_def) = ty.ty_adt_def() {
|
||||
if let Some(local_def_id) = ty_def.did().as_local() {
|
||||
impls.insert(cx.tcx.local_def_id_to_hir_id(local_def_id));
|
||||
}
|
||||
if let Some(ty_def) = ty.ty_adt_def()
|
||||
&& let Some(local_def_id) = ty_def.did().as_local()
|
||||
{
|
||||
impls.insert(cx.tcx.local_def_id_to_hir_id(local_def_id));
|
||||
}
|
||||
});
|
||||
self.impling_types = Some(impls);
|
||||
|
|
|
|||
|
|
@ -182,23 +182,22 @@ impl NoEffect {
|
|||
);
|
||||
return true;
|
||||
}
|
||||
} else if let StmtKind::Let(local) = stmt.kind {
|
||||
if !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id)
|
||||
&& !matches!(local.source, LocalSource::AsyncFn)
|
||||
&& let Some(init) = local.init
|
||||
&& local.els.is_none()
|
||||
&& !local.pat.span.from_expansion()
|
||||
&& has_no_effect(cx, init)
|
||||
&& let PatKind::Binding(_, hir_id, ident, _) = local.pat.kind
|
||||
&& ident.name.to_ident_string().starts_with('_')
|
||||
&& !in_automatically_derived(cx.tcx, local.hir_id)
|
||||
{
|
||||
if let Some(l) = self.local_bindings.last_mut() {
|
||||
l.push(hir_id);
|
||||
self.underscore_bindings.insert(hir_id, ident.span);
|
||||
}
|
||||
return true;
|
||||
} else if let StmtKind::Let(local) = stmt.kind
|
||||
&& !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id)
|
||||
&& !matches!(local.source, LocalSource::AsyncFn)
|
||||
&& let Some(init) = local.init
|
||||
&& local.els.is_none()
|
||||
&& !local.pat.span.from_expansion()
|
||||
&& has_no_effect(cx, init)
|
||||
&& let PatKind::Binding(_, hir_id, ident, _) = local.pat.kind
|
||||
&& ident.name.to_ident_string().starts_with('_')
|
||||
&& !in_automatically_derived(cx.tcx, local.hir_id)
|
||||
{
|
||||
if let Some(l) = self.local_bindings.last_mut() {
|
||||
l.push(hir_id);
|
||||
self.underscore_bindings.insert(hir_id, ident.span);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,11 +82,10 @@ fn check_non_zero_conversion(cx: &LateContext<'_>, expr: &Expr<'_>, applicabilit
|
|||
if let ty::Adt(adt_def, _) = receiver_ty.kind()
|
||||
&& adt_def.is_struct()
|
||||
&& cx.tcx.get_diagnostic_name(adt_def.did()) == Some(sym::NonZero)
|
||||
&& let Some(target_non_zero_type) = get_target_non_zero_type(target_ty)
|
||||
{
|
||||
if let Some(target_non_zero_type) = get_target_non_zero_type(target_ty) {
|
||||
let arg_snippet = get_arg_snippet(cx, arg, rcv_path);
|
||||
suggest_non_zero_conversion(cx, expr, fn_name, target_non_zero_type, &arg_snippet, applicability);
|
||||
}
|
||||
let arg_snippet = get_arg_snippet(cx, arg, rcv_path);
|
||||
suggest_non_zero_conversion(cx, expr, fn_name, target_non_zero_type, &arg_snippet, applicability);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,15 +12,15 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, op: BinOpKind, right:
|
|||
span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0");
|
||||
}
|
||||
|
||||
if let ty::Int(ity) = cx.typeck_results().expr_ty(right).kind() {
|
||||
if is_integer_const(cx, right, unsext(cx.tcx, -1, *ity)) {
|
||||
span_lint(
|
||||
cx,
|
||||
MODULO_ONE,
|
||||
expr.span,
|
||||
"any number modulo -1 will panic/overflow or result in 0",
|
||||
);
|
||||
}
|
||||
if let ty::Int(ity) = cx.typeck_results().expr_ty(right).kind()
|
||||
&& is_integer_const(cx, right, unsext(cx.tcx, -1, *ity))
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
MODULO_ONE,
|
||||
expr.span,
|
||||
"any number modulo -1 will panic/overflow or result in 0",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,10 +75,10 @@ impl Context {
|
|||
hir::BodyOwnerKind::Static(_) | hir::BodyOwnerKind::Const { .. } => {
|
||||
let body_span = cx.tcx.hir().span_with_body(body_owner);
|
||||
|
||||
if let Some(span) = self.const_span {
|
||||
if span.contains(body_span) {
|
||||
return;
|
||||
}
|
||||
if let Some(span) = self.const_span
|
||||
&& span.contains(body_span)
|
||||
{
|
||||
return;
|
||||
}
|
||||
self.const_span = Some(body_span);
|
||||
},
|
||||
|
|
@ -90,10 +90,10 @@ impl Context {
|
|||
let body_owner = cx.tcx.hir_body_owner(body.id());
|
||||
let body_span = cx.tcx.hir().span_with_body(body_owner);
|
||||
|
||||
if let Some(span) = self.const_span {
|
||||
if span.contains(body_span) {
|
||||
return;
|
||||
}
|
||||
if let Some(span) = self.const_span
|
||||
&& span.contains(body_span)
|
||||
{
|
||||
return;
|
||||
}
|
||||
self.const_span = None;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,12 +47,11 @@ pub(crate) fn check<'tcx>(
|
|||
let rty = cx.typeck_results().expr_ty(r);
|
||||
let lcpy = is_copy(cx, lty);
|
||||
let rcpy = is_copy(cx, rty);
|
||||
if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) {
|
||||
if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty))
|
||||
|| (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty))
|
||||
{
|
||||
return; // Don't lint
|
||||
}
|
||||
if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id)
|
||||
&& ((are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty))
|
||||
|| (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty)))
|
||||
{
|
||||
return; // Don't lint
|
||||
}
|
||||
// either operator autorefs or both args are copyable
|
||||
if (requires_ref || (lcpy && rcpy)) && implements_trait(cx, lty, trait_id, &[rty.into()]) {
|
||||
|
|
|
|||
|
|
@ -178,19 +178,18 @@ impl PassByRefOrValue {
|
|||
&& size <= self.ref_min_size
|
||||
&& let hir::TyKind::Ref(_, MutTy { ty: decl_ty, .. }) = input.kind
|
||||
{
|
||||
if let Some(typeck) = cx.maybe_typeck_results() {
|
||||
if let Some(typeck) = cx.maybe_typeck_results()
|
||||
// Don't lint if a raw pointer is created.
|
||||
// TODO: Limit the check only to raw pointers to the argument (or part of the argument)
|
||||
// which escape the current function.
|
||||
if typeck.node_types().items().any(|(_, &ty)| ty.is_raw_ptr())
|
||||
&& (typeck.node_types().items().any(|(_, &ty)| ty.is_raw_ptr())
|
||||
|| typeck
|
||||
.adjustments()
|
||||
.items()
|
||||
.flat_map(|(_, a)| a)
|
||||
.any(|a| matches!(a.kind, Adjust::Pointer(PointerCoercion::UnsafeFnPointer)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
.any(|a| matches!(a.kind, Adjust::Pointer(PointerCoercion::UnsafeFnPointer))))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let value_type = if fn_body.and_then(|body| body.params.get(index)).is_some_and(is_self) {
|
||||
"self".into()
|
||||
|
|
@ -282,12 +281,11 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue {
|
|||
}
|
||||
let attrs = cx.tcx.hir_attrs(hir_id);
|
||||
for a in attrs {
|
||||
if let Some(meta_items) = a.meta_item_list() {
|
||||
if a.has_name(sym::proc_macro_derive)
|
||||
|| (a.has_name(sym::inline) && attr::list_contains_name(&meta_items, sym::always))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if let Some(meta_items) = a.meta_item_list()
|
||||
&& (a.has_name(sym::proc_macro_derive)
|
||||
|| (a.has_name(sym::inline) && attr::list_contains_name(&meta_items, sym::always)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -296,13 +294,13 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue {
|
|||
}
|
||||
|
||||
// Exclude non-inherent impls
|
||||
if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) {
|
||||
if matches!(
|
||||
if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id)
|
||||
&& matches!(
|
||||
item.kind,
|
||||
ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self.check_poly_fn(cx, def_id, decl, Some(span));
|
||||
|
|
|
|||
|
|
@ -173,16 +173,15 @@ impl<'tcx> LateLintPass<'tcx> for PathbufThenPush<'tcx> {
|
|||
}
|
||||
|
||||
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
|
||||
if let Some(mut searcher) = self.searcher.take() {
|
||||
if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind
|
||||
&& let ExprKind::MethodCall(name, self_arg, [arg_expr], _) = expr.kind
|
||||
&& path_to_local_id(self_arg, searcher.local_id)
|
||||
&& name.ident.as_str() == "push"
|
||||
{
|
||||
searcher.err_span = searcher.err_span.to(stmt.span);
|
||||
searcher.arg = Some(*arg_expr);
|
||||
searcher.display_err(cx);
|
||||
}
|
||||
if let Some(mut searcher) = self.searcher.take()
|
||||
&& let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind
|
||||
&& let ExprKind::MethodCall(name, self_arg, [arg_expr], _) = expr.kind
|
||||
&& path_to_local_id(self_arg, searcher.local_id)
|
||||
&& name.ident.as_str() == "push"
|
||||
{
|
||||
searcher.err_span = searcher.err_span.to(stmt.span);
|
||||
searcher.arg = Some(*arg_expr);
|
||||
searcher.display_err(cx);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -177,17 +177,16 @@ fn find_first_mismatch(cx: &LateContext<'_>, pat: &Pat<'_>) -> Option<(Span, Mut
|
|||
PatKind::Or([p, ..]) => p,
|
||||
_ => p,
|
||||
};
|
||||
if let Some(adjustments) = cx.typeck_results().pat_adjustments().get(adjust_pat.hir_id) {
|
||||
if let [first, ..] = **adjustments {
|
||||
if let ty::Ref(.., mutability) = *first.kind() {
|
||||
let level = if p.hir_id == pat.hir_id {
|
||||
Level::Top
|
||||
} else {
|
||||
Level::Lower
|
||||
};
|
||||
result = Some((p.span, mutability, level));
|
||||
}
|
||||
}
|
||||
if let Some(adjustments) = cx.typeck_results().pat_adjustments().get(adjust_pat.hir_id)
|
||||
&& let [first, ..] = **adjustments
|
||||
&& let ty::Ref(.., mutability) = *first.kind()
|
||||
{
|
||||
let level = if p.hir_id == pat.hir_id {
|
||||
Level::Top
|
||||
} else {
|
||||
Level::Lower
|
||||
};
|
||||
result = Some((p.span, mutability, level));
|
||||
}
|
||||
result.is_none()
|
||||
});
|
||||
|
|
|
|||
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