Merge commit 'ca3b393750' into clippy-subtree-update
This commit is contained in:
parent
876d5f00a0
commit
a5aaf33422
84 changed files with 1067 additions and 427 deletions
|
|
@ -56,7 +56,12 @@ impl<'tcx> LateLintPass<'tcx> for ArcWithNonSendSync {
|
|||
&& let Some(send) = cx.tcx.get_diagnostic_item(sym::Send)
|
||||
&& let Some(sync) = cx.tcx.lang_items().sync_trait()
|
||||
&& let [is_send, is_sync] = [send, sync].map(|id| implements_trait(cx, arg_ty, id, &[]))
|
||||
&& !(is_send && is_sync)
|
||||
&& let reason = match (is_send, is_sync) {
|
||||
(false, false) => "neither `Send` nor `Sync`",
|
||||
(false, true) => "not `Send`",
|
||||
(true, false) => "not `Sync`",
|
||||
_ => return,
|
||||
}
|
||||
&& !is_from_proc_macro(cx, expr)
|
||||
{
|
||||
span_lint_and_then(
|
||||
|
|
@ -66,21 +71,12 @@ impl<'tcx> LateLintPass<'tcx> for ArcWithNonSendSync {
|
|||
"usage of an `Arc` that is not `Send` and `Sync`",
|
||||
|diag| {
|
||||
with_forced_trimmed_paths!({
|
||||
diag.note(format!("`Arc<{arg_ty}>` is not `Send` and `Sync` as:"));
|
||||
|
||||
if !is_send {
|
||||
diag.note(format!("- the trait `Send` is not implemented for `{arg_ty}`"));
|
||||
}
|
||||
if !is_sync {
|
||||
diag.note(format!("- the trait `Sync` is not implemented for `{arg_ty}`"));
|
||||
}
|
||||
|
||||
diag.help("consider using an `Rc` instead. `Arc` does not provide benefits for non `Send` and `Sync` types");
|
||||
|
||||
diag.note("if you intend to use `Arc` with `Send` and `Sync` traits");
|
||||
|
||||
diag.note(format!(
|
||||
"wrap the inner type with a `Mutex` or implement `Send` and `Sync` for `{arg_ty}`"
|
||||
"`Arc<{arg_ty}>` is not `Send` and `Sync` as `{arg_ty}` is {reason}"
|
||||
));
|
||||
diag.help("if the `Arc` will not used be across threads replace it with an `Rc`");
|
||||
diag.help(format!(
|
||||
"otherwise make `{arg_ty}` `Send` and `Sync` or consider a wrapper type such as `Mutex`"
|
||||
));
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@ use super::DUPLICATED_ATTRIBUTES;
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use rustc_ast::{Attribute, MetaItem};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_lint::EarlyContext;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::{sym, Span};
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
fn emit_if_duplicated(
|
||||
cx: &EarlyContext<'_>,
|
||||
cx: &LateContext<'_>,
|
||||
attr: &MetaItem,
|
||||
attr_paths: &mut FxHashMap<String, Span>,
|
||||
complete_path: String,
|
||||
|
|
@ -26,7 +26,7 @@ fn emit_if_duplicated(
|
|||
}
|
||||
|
||||
fn check_duplicated_attr(
|
||||
cx: &EarlyContext<'_>,
|
||||
cx: &LateContext<'_>,
|
||||
attr: &MetaItem,
|
||||
attr_paths: &mut FxHashMap<String, Span>,
|
||||
parent: &mut Vec<String>,
|
||||
|
|
@ -64,7 +64,7 @@ fn check_duplicated_attr(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn check(cx: &EarlyContext<'_>, attrs: &[Attribute]) {
|
||||
pub fn check(cx: &LateContext<'_>, attrs: &[Attribute]) {
|
||||
let mut attr_paths = FxHashMap::default();
|
||||
|
||||
for attr in attrs {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ mod useless_attribute;
|
|||
mod utils;
|
||||
|
||||
use clippy_config::msrvs::Msrv;
|
||||
use rustc_ast::{Attribute, Crate, MetaItemKind, NestedMetaItem};
|
||||
use rustc_ast::{Attribute, MetaItemKind, NestedMetaItem};
|
||||
use rustc_hir::{ImplItem, Item, ItemKind, TraitItem};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, impl_lint_pass};
|
||||
|
|
@ -534,11 +534,13 @@ declare_lint_pass!(Attributes => [
|
|||
BLANKET_CLIPPY_RESTRICTION_LINTS,
|
||||
SHOULD_PANIC_WITHOUT_EXPECT,
|
||||
MIXED_ATTRIBUTES_STYLE,
|
||||
DUPLICATED_ATTRIBUTES,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Attributes {
|
||||
fn check_crate(&mut self, cx: &LateContext<'tcx>) {
|
||||
blanket_clippy_restriction_lints::check_command_line(cx);
|
||||
duplicated_attributes::check(cx, cx.tcx.hir().krate_attrs());
|
||||
}
|
||||
|
||||
fn check_attribute(&mut self, cx: &LateContext<'tcx>, attr: &'tcx Attribute) {
|
||||
|
|
@ -578,6 +580,7 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
|
|||
_ => {},
|
||||
}
|
||||
mixed_attributes_style::check(cx, item.span, attrs);
|
||||
duplicated_attributes::check(cx, attrs);
|
||||
}
|
||||
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
|
||||
|
|
@ -606,17 +609,11 @@ impl_lint_pass!(EarlyAttributes => [
|
|||
MAYBE_MISUSED_CFG,
|
||||
DEPRECATED_CLIPPY_CFG_ATTR,
|
||||
UNNECESSARY_CLIPPY_CFG,
|
||||
DUPLICATED_ATTRIBUTES,
|
||||
]);
|
||||
|
||||
impl EarlyLintPass for EarlyAttributes {
|
||||
fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) {
|
||||
duplicated_attributes::check(cx, &krate.attrs);
|
||||
}
|
||||
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
|
||||
empty_line_after::check(cx, item);
|
||||
duplicated_attributes::check(cx, &item.attrs);
|
||||
}
|
||||
|
||||
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
|
||||
|
|
|
|||
|
|
@ -346,11 +346,18 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
|
|||
_ => None,
|
||||
}
|
||||
.and_then(|op| {
|
||||
Some(format!(
|
||||
"{}{op}{}",
|
||||
snippet_opt(cx, lhs.span)?,
|
||||
snippet_opt(cx, rhs.span)?
|
||||
))
|
||||
let lhs_snippet = snippet_opt(cx, lhs.span)?;
|
||||
let rhs_snippet = snippet_opt(cx, rhs.span)?;
|
||||
|
||||
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}"));
|
||||
}
|
||||
}
|
||||
|
||||
Some(format!("{lhs_snippet}{op}{rhs_snippet}"))
|
||||
})
|
||||
},
|
||||
ExprKind::MethodCall(path, receiver, [], _) => {
|
||||
|
|
|
|||
|
|
@ -754,11 +754,7 @@ impl_lint_pass!(Casts => [
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for Casts {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if !in_external_macro(cx.sess(), expr.span) {
|
||||
ptr_as_ptr::check(cx, expr, &self.msrv);
|
||||
}
|
||||
|
||||
if expr.span.from_expansion() {
|
||||
if in_external_macro(cx.sess(), expr.span) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -771,7 +767,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
|
|||
cx.typeck_results().expr_ty(expr),
|
||||
);
|
||||
|
||||
if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) {
|
||||
if !expr.span.from_expansion() && unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) {
|
||||
return;
|
||||
}
|
||||
cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, &self.msrv);
|
||||
|
|
@ -782,7 +778,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
|
|||
fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
|
||||
zero_ptr::check(cx, expr, cast_expr, cast_to_hir);
|
||||
|
||||
if cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) {
|
||||
if cast_to.is_numeric() {
|
||||
cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to, cast_to_hir.span);
|
||||
if cast_from.is_numeric() {
|
||||
cast_possible_wrap::check(cx, expr, cast_from, cast_to);
|
||||
|
|
|
|||
|
|
@ -1016,9 +1016,18 @@ fn report<'tcx>(
|
|||
},
|
||||
_ => (0, false),
|
||||
};
|
||||
let is_in_tuple = matches!(
|
||||
get_parent_expr(cx, data.first_expr),
|
||||
Some(Expr {
|
||||
kind: ExprKind::Tup(..),
|
||||
..
|
||||
})
|
||||
);
|
||||
|
||||
let sugg = if !snip_is_macro
|
||||
&& (calls_field || expr.precedence().order() < precedence)
|
||||
&& !has_enclosing_paren(&snip)
|
||||
&& !is_in_tuple
|
||||
{
|
||||
format!("({snip})")
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Why is this bad?
|
||||
/// Deriving `serde::Deserialize` will create a constructor
|
||||
/// that may violate invariants hold by another constructor.
|
||||
/// that may violate invariants held by another constructor.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_D
|
|||
pub fn check(
|
||||
cx: &LateContext<'_>,
|
||||
owner_id: OwnerId,
|
||||
sig: &FnSig<'_>,
|
||||
sig: FnSig<'_>,
|
||||
headers: DocHeaders,
|
||||
body_id: Option<BodyId>,
|
||||
panic_span: Option<Span>,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
|
|||
use clippy_utils::macros::{is_panic, root_macro_call_first_node};
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::visitors::Visitable;
|
||||
use clippy_utils::{is_entrypoint_fn, method_chain_args};
|
||||
use clippy_utils::{is_entrypoint_fn, is_trait_impl_item, method_chain_args};
|
||||
use pulldown_cmark::Event::{
|
||||
Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text,
|
||||
};
|
||||
|
|
@ -11,9 +11,8 @@ use pulldown_cmark::Tag::{BlockQuote, CodeBlock, Heading, Item, Link, Paragraph}
|
|||
use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options};
|
||||
use rustc_ast::ast::Attribute;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::{AnonConst, Expr};
|
||||
use rustc_hir::{AnonConst, Expr, ImplItemKind, ItemKind, Node, TraitItemKind, Unsafety};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
|
|
@ -366,7 +365,6 @@ declare_clippy_lint! {
|
|||
#[derive(Clone)]
|
||||
pub struct Documentation {
|
||||
valid_idents: FxHashSet<String>,
|
||||
in_trait_impl: bool,
|
||||
check_private_items: bool,
|
||||
}
|
||||
|
||||
|
|
@ -374,7 +372,6 @@ impl Documentation {
|
|||
pub fn new(valid_idents: &[String], check_private_items: bool) -> Self {
|
||||
Self {
|
||||
valid_idents: valid_idents.iter().cloned().collect(),
|
||||
in_trait_impl: false,
|
||||
check_private_items,
|
||||
}
|
||||
}
|
||||
|
|
@ -394,36 +391,72 @@ impl_lint_pass!(Documentation => [
|
|||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Documentation {
|
||||
fn check_crate(&mut self, cx: &LateContext<'tcx>) {
|
||||
let attrs = cx.tcx.hir().attrs(hir::CRATE_HIR_ID);
|
||||
check_attrs(cx, &self.valid_idents, attrs);
|
||||
}
|
||||
|
||||
fn check_variant(&mut self, cx: &LateContext<'tcx>, variant: &'tcx hir::Variant<'tcx>) {
|
||||
let attrs = cx.tcx.hir().attrs(variant.hir_id);
|
||||
check_attrs(cx, &self.valid_idents, attrs);
|
||||
}
|
||||
|
||||
fn check_field_def(&mut self, cx: &LateContext<'tcx>, variant: &'tcx hir::FieldDef<'tcx>) {
|
||||
let attrs = cx.tcx.hir().attrs(variant.hir_id);
|
||||
check_attrs(cx, &self.valid_idents, attrs);
|
||||
}
|
||||
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
|
||||
let attrs = cx.tcx.hir().attrs(item.hir_id());
|
||||
fn check_attributes(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) {
|
||||
let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else {
|
||||
return;
|
||||
};
|
||||
|
||||
match item.kind {
|
||||
hir::ItemKind::Fn(ref sig, _, body_id) => {
|
||||
if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) {
|
||||
let body = cx.tcx.hir().body(body_id);
|
||||
match cx.tcx.hir_node(cx.last_node_with_lint_attrs) {
|
||||
Node::Item(item) => match item.kind {
|
||||
ItemKind::Fn(sig, _, body_id) => {
|
||||
if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) {
|
||||
let body = cx.tcx.hir().body(body_id);
|
||||
|
||||
let panic_span = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value);
|
||||
let panic_span = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value);
|
||||
missing_headers::check(
|
||||
cx,
|
||||
item.owner_id,
|
||||
sig,
|
||||
headers,
|
||||
Some(body_id),
|
||||
panic_span,
|
||||
self.check_private_items,
|
||||
);
|
||||
}
|
||||
},
|
||||
ItemKind::Trait(_, unsafety, ..) => match (headers.safety, unsafety) {
|
||||
(false, Unsafety::Unsafe) => span_lint(
|
||||
cx,
|
||||
MISSING_SAFETY_DOC,
|
||||
cx.tcx.def_span(item.owner_id),
|
||||
"docs for unsafe trait missing `# Safety` section",
|
||||
),
|
||||
(true, Unsafety::Normal) => span_lint(
|
||||
cx,
|
||||
UNNECESSARY_SAFETY_DOC,
|
||||
cx.tcx.def_span(item.owner_id),
|
||||
"docs for safe trait have unnecessary `# Safety` section",
|
||||
),
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
Node::TraitItem(trait_item) => {
|
||||
if let TraitItemKind::Fn(sig, ..) = trait_item.kind
|
||||
&& !in_external_macro(cx.tcx.sess, trait_item.span)
|
||||
{
|
||||
missing_headers::check(
|
||||
cx,
|
||||
item.owner_id,
|
||||
trait_item.owner_id,
|
||||
sig,
|
||||
headers,
|
||||
None,
|
||||
None,
|
||||
self.check_private_items,
|
||||
);
|
||||
}
|
||||
},
|
||||
Node::ImplItem(impl_item) => {
|
||||
if let ImplItemKind::Fn(sig, body_id) = impl_item.kind
|
||||
&& !in_external_macro(cx.tcx.sess, impl_item.span)
|
||||
&& !is_trait_impl_item(cx, impl_item.hir_id())
|
||||
{
|
||||
let body = cx.tcx.hir().body(body_id);
|
||||
|
||||
let panic_span = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(impl_item.owner_id), body.value);
|
||||
missing_headers::check(
|
||||
cx,
|
||||
impl_item.owner_id,
|
||||
sig,
|
||||
headers,
|
||||
Some(body_id),
|
||||
|
|
@ -432,67 +465,7 @@ impl<'tcx> LateLintPass<'tcx> for Documentation {
|
|||
);
|
||||
}
|
||||
},
|
||||
hir::ItemKind::Impl(impl_) => {
|
||||
self.in_trait_impl = impl_.of_trait.is_some();
|
||||
},
|
||||
hir::ItemKind::Trait(_, unsafety, ..) => match (headers.safety, unsafety) {
|
||||
(false, hir::Unsafety::Unsafe) => span_lint(
|
||||
cx,
|
||||
MISSING_SAFETY_DOC,
|
||||
cx.tcx.def_span(item.owner_id),
|
||||
"docs for unsafe trait missing `# Safety` section",
|
||||
),
|
||||
(true, hir::Unsafety::Normal) => span_lint(
|
||||
cx,
|
||||
UNNECESSARY_SAFETY_DOC,
|
||||
cx.tcx.def_span(item.owner_id),
|
||||
"docs for safe trait have unnecessary `# Safety` section",
|
||||
),
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_item_post(&mut self, _cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
|
||||
if let hir::ItemKind::Impl { .. } = item.kind {
|
||||
self.in_trait_impl = false;
|
||||
}
|
||||
}
|
||||
|
||||
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
|
||||
let attrs = cx.tcx.hir().attrs(item.hir_id());
|
||||
let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else {
|
||||
return;
|
||||
};
|
||||
if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind {
|
||||
if !in_external_macro(cx.tcx.sess, item.span) {
|
||||
missing_headers::check(cx, item.owner_id, sig, headers, None, None, self.check_private_items);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
|
||||
let attrs = cx.tcx.hir().attrs(item.hir_id());
|
||||
let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else {
|
||||
return;
|
||||
};
|
||||
if self.in_trait_impl || in_external_macro(cx.tcx.sess, item.span) {
|
||||
return;
|
||||
}
|
||||
if let hir::ImplItemKind::Fn(ref sig, body_id) = item.kind {
|
||||
let body = cx.tcx.hir().body(body_id);
|
||||
|
||||
let panic_span = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value);
|
||||
missing_headers::check(
|
||||
cx,
|
||||
item.owner_id,
|
||||
sig,
|
||||
headers,
|
||||
Some(body_id),
|
||||
panic_span,
|
||||
self.check_private_items,
|
||||
);
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local};
|
|||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::HirId;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::HirId;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::ty;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use clippy_utils::is_bool;
|
|||
use clippy_utils::macros::span_is_local;
|
||||
use clippy_utils::source::is_present_in_source;
|
||||
use clippy_utils::str_utils::{camel_case_split, count_match_end, count_match_start, to_camel_case, to_snake_case};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir::{EnumDef, FieldDef, Item, ItemKind, OwnerId, Variant, VariantData};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
|
@ -147,6 +148,7 @@ pub struct ItemNameRepetitions {
|
|||
struct_threshold: u64,
|
||||
avoid_breaking_exported_api: bool,
|
||||
allow_private_module_inception: bool,
|
||||
allowed_prefixes: FxHashSet<String>,
|
||||
}
|
||||
|
||||
impl ItemNameRepetitions {
|
||||
|
|
@ -156,6 +158,7 @@ impl ItemNameRepetitions {
|
|||
struct_threshold: u64,
|
||||
avoid_breaking_exported_api: bool,
|
||||
allow_private_module_inception: bool,
|
||||
allowed_prefixes: &[String],
|
||||
) -> Self {
|
||||
Self {
|
||||
modules: Vec::new(),
|
||||
|
|
@ -163,8 +166,13 @@ impl ItemNameRepetitions {
|
|||
struct_threshold,
|
||||
avoid_breaking_exported_api,
|
||||
allow_private_module_inception,
|
||||
allowed_prefixes: allowed_prefixes.iter().map(|s| to_camel_case(s)).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_allowed_prefix(&self, prefix: &str) -> bool {
|
||||
self.allowed_prefixes.contains(prefix)
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(ItemNameRepetitions => [
|
||||
|
|
@ -423,7 +431,9 @@ impl LateLintPass<'_> for ItemNameRepetitions {
|
|||
_ => (),
|
||||
}
|
||||
}
|
||||
if rmatching.char_count == nchars {
|
||||
if rmatching.char_count == nchars
|
||||
&& !self.is_allowed_prefix(&item_camel[..item_camel.len() - rmatching.byte_count])
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
MODULE_NAME_REPETITIONS,
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ declare_clippy_lint! {
|
|||
/// `std::<float>::EPSILON`, etc.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// All of these have been superceded by the associated constants on their respective types,
|
||||
/// All of these have been superseded by the associated constants on their respective types,
|
||||
/// such as `i128::MAX`. These legacy items may be deprecated in a future version of rust.
|
||||
///
|
||||
/// ### Example
|
||||
|
|
|
|||
|
|
@ -594,6 +594,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
|||
pub_underscore_fields_behavior,
|
||||
ref allowed_duplicate_crates,
|
||||
allow_comparison_to_zero,
|
||||
ref allowed_prefixes,
|
||||
|
||||
blacklisted_names: _,
|
||||
cyclomatic_complexity_threshold: _,
|
||||
|
|
@ -864,6 +865,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
|||
struct_field_name_threshold,
|
||||
avoid_breaking_exported_api,
|
||||
allow_private_module_inception,
|
||||
allowed_prefixes,
|
||||
))
|
||||
});
|
||||
store.register_early_pass(|| Box::new(tabs_in_doc_comments::TabsInDocComments));
|
||||
|
|
|
|||
|
|
@ -291,10 +291,7 @@ fn elision_suggestions(
|
|||
}) => {
|
||||
// expand `&'a T` to `&'a T`
|
||||
// ^^ ^^^
|
||||
let span = cx
|
||||
.sess()
|
||||
.source_map()
|
||||
.span_extend_while_whitespace(usage.ident.span);
|
||||
let span = cx.sess().source_map().span_extend_while_whitespace(usage.ident.span);
|
||||
|
||||
(span, String::new())
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
use clippy_utils::sugg::Sugg;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::{Arm, Expr, ExprKind, HirId, LangItem, MatchSource, Pat, PatKind, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::ty::GenericArgKind;
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::sym;
|
||||
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::higher::IfLetOrMatch;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::{in_constant, is_default_equivalent, peel_blocks, span_contains_comment};
|
||||
|
||||
|
|
@ -105,19 +106,39 @@ fn get_some_and_none_bodies<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
fn handle_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
|
||||
let ExprKind::Match(match_expr, [arm1, arm2], MatchSource::Normal | MatchSource::ForLoopDesugar) = expr.kind else {
|
||||
return false;
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
fn handle<'tcx>(cx: &LateContext<'tcx>, if_let_or_match: IfLetOrMatch<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
// Get expr_name ("if let" or "match" depending on kind of expression), the condition, the body for
|
||||
// the some arm, the body for the none arm and the binding id of the some arm
|
||||
let (expr_name, condition, body_some, body_none, binding_id) = match if_let_or_match {
|
||||
IfLetOrMatch::Match(condition, [arm1, arm2], MatchSource::Normal | MatchSource::ForLoopDesugar)
|
||||
// Make sure there are no guards to keep things simple
|
||||
if arm1.guard.is_none()
|
||||
&& arm2.guard.is_none()
|
||||
// Get the some and none bodies and the binding id of the some arm
|
||||
&& let Some(((body_some, binding_id), body_none)) = get_some_and_none_bodies(cx, arm1, arm2) =>
|
||||
{
|
||||
("match", condition, body_some, body_none, binding_id)
|
||||
},
|
||||
IfLetOrMatch::IfLet(condition, pat, if_expr, Some(else_expr), _)
|
||||
if let Some(binding_id) = get_some(cx, pat) =>
|
||||
{
|
||||
("if let", condition, if_expr, else_expr, binding_id)
|
||||
},
|
||||
_ => {
|
||||
// All other cases (match with number of arms != 2, if let without else, etc.)
|
||||
return;
|
||||
},
|
||||
};
|
||||
// We don't want conditions on the arms to simplify things.
|
||||
if arm1.guard.is_none()
|
||||
&& arm2.guard.is_none()
|
||||
// We check that the returned type implements the `Default` trait.
|
||||
&& let match_ty = cx.typeck_results().expr_ty(expr)
|
||||
&& let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default)
|
||||
&& implements_trait(cx, match_ty, default_trait_id, &[])
|
||||
// We now get the bodies for both the `Some` and `None` arms.
|
||||
&& let Some(((body_some, binding_id), body_none)) = get_some_and_none_bodies(cx, arm1, arm2)
|
||||
|
||||
// We check if the return type of the expression implements Default.
|
||||
let expr_type = cx.typeck_results().expr_ty(expr);
|
||||
if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default)
|
||||
&& implements_trait(cx, expr_type, default_trait_id, &[])
|
||||
// We check if the initial condition implements Default.
|
||||
&& let Some(condition_ty) = cx.typeck_results().expr_ty(condition).walk().nth(1)
|
||||
&& let GenericArgKind::Type(condition_ty) = condition_ty.unpack()
|
||||
&& implements_trait(cx, condition_ty, default_trait_id, &[])
|
||||
// We check that the `Some(x) => x` doesn't do anything apart "returning" the value in `Some`.
|
||||
&& let ExprKind::Path(QPath::Resolved(_, path)) = peel_blocks(body_some).kind
|
||||
&& let Res::Local(local_id) = path.res
|
||||
|
|
@ -125,8 +146,9 @@ fn handle_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
|
|||
// We now check the `None` arm is calling a method equivalent to `Default::default`.
|
||||
&& let body_none = peel_blocks(body_none)
|
||||
&& is_default_equivalent(cx, body_none)
|
||||
&& let Some(receiver) = Sugg::hir_opt(cx, match_expr).map(Sugg::maybe_par)
|
||||
&& let Some(receiver) = Sugg::hir_opt(cx, condition).map(Sugg::maybe_par)
|
||||
{
|
||||
// Machine applicable only if there are no comments present
|
||||
let applicability = if span_contains_comment(cx.sess().source_map(), expr.span) {
|
||||
Applicability::MaybeIncorrect
|
||||
} else {
|
||||
|
|
@ -136,48 +158,12 @@ fn handle_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
|
|||
cx,
|
||||
MANUAL_UNWRAP_OR_DEFAULT,
|
||||
expr.span,
|
||||
"match can be simplified with `.unwrap_or_default()`",
|
||||
format!("{expr_name} can be simplified with `.unwrap_or_default()`"),
|
||||
"replace it with",
|
||||
format!("{receiver}.unwrap_or_default()"),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn handle_if_let<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if let ExprKind::If(cond, if_block, Some(else_expr)) = expr.kind
|
||||
&& let ExprKind::Let(let_) = cond.kind
|
||||
&& let ExprKind::Block(_, _) = else_expr.kind
|
||||
// We check that the returned type implements the `Default` trait.
|
||||
&& let match_ty = cx.typeck_results().expr_ty(expr)
|
||||
&& let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default)
|
||||
&& implements_trait(cx, match_ty, default_trait_id, &[])
|
||||
&& let Some(binding_id) = get_some(cx, let_.pat)
|
||||
// We check that the `Some(x) => x` doesn't do anything apart "returning" the value in `Some`.
|
||||
&& let ExprKind::Path(QPath::Resolved(_, path)) = peel_blocks(if_block).kind
|
||||
&& let Res::Local(local_id) = path.res
|
||||
&& local_id == binding_id
|
||||
// We now check the `None` arm is calling a method equivalent to `Default::default`.
|
||||
&& let body_else = peel_blocks(else_expr)
|
||||
&& is_default_equivalent(cx, body_else)
|
||||
&& let Some(if_let_expr_snippet) = snippet_opt(cx, let_.init.span)
|
||||
{
|
||||
let applicability = if span_contains_comment(cx.sess().source_map(), expr.span) {
|
||||
Applicability::MaybeIncorrect
|
||||
} else {
|
||||
Applicability::MachineApplicable
|
||||
};
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_UNWRAP_OR_DEFAULT,
|
||||
expr.span,
|
||||
"if let can be simplified with `.unwrap_or_default()`",
|
||||
"replace it with",
|
||||
format!("{if_let_expr_snippet}.unwrap_or_default()"),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for ManualUnwrapOrDefault {
|
||||
|
|
@ -185,8 +171,9 @@ impl<'tcx> LateLintPass<'tcx> for ManualUnwrapOrDefault {
|
|||
if expr.span.from_expansion() || in_constant(cx, expr.hir_id) {
|
||||
return;
|
||||
}
|
||||
if !handle_match(cx, expr) {
|
||||
handle_if_let(cx, expr);
|
||||
// Call handle only if the expression is `if let` or `match`
|
||||
if let Some(if_let_or_match) = IfLetOrMatch::parse(cx, expr) {
|
||||
handle(cx, if_let_or_match, expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3938,7 +3938,6 @@ declare_clippy_lint! {
|
|||
/// This lint cannot detect if the split is intentionally restricted to a single type of newline (`"\n"` or
|
||||
/// `"\r\n"`), for example during the parsing of a specific file format in which precisely one newline type is
|
||||
/// valid.
|
||||
/// ```
|
||||
#[clippy::version = "1.77.0"]
|
||||
pub STR_SPLIT_AT_NEWLINE,
|
||||
pedantic,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
|
|||
use clippy_utils::source::{snippet, snippet_with_applicability};
|
||||
use clippy_utils::sugg::deref_closure_args;
|
||||
use clippy_utils::ty::is_type_lang_item;
|
||||
use clippy_utils::{is_trait_method, strip_pat_refs};
|
||||
use clippy_utils::{get_parent_expr, is_trait_method, strip_pat_refs};
|
||||
use hir::ExprKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::PatKind;
|
||||
|
|
@ -35,7 +36,7 @@ pub(super) fn check<'tcx>(
|
|||
// suggest `any(|..| *..)` instead of `any(|..| **..)` for `find(|..| **..).is_some()`
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let any_search_snippet = if search_method == "find"
|
||||
&& let hir::ExprKind::Closure(&hir::Closure { body, .. }) = search_arg.kind
|
||||
&& let ExprKind::Closure(&hir::Closure { body, .. }) = search_arg.kind
|
||||
&& let closure_body = cx.tcx.hir().body(body)
|
||||
&& let Some(closure_arg) = closure_body.params.first()
|
||||
{
|
||||
|
|
@ -72,16 +73,24 @@ pub(super) fn check<'tcx>(
|
|||
);
|
||||
} else {
|
||||
let iter = snippet(cx, search_recv.span, "..");
|
||||
let sugg = if is_receiver_of_method_call(cx, expr) {
|
||||
format!(
|
||||
"(!{iter}.any({}))",
|
||||
any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"!{iter}.any({})",
|
||||
any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
|
||||
)
|
||||
};
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SEARCH_IS_SOME,
|
||||
expr.span,
|
||||
msg,
|
||||
"consider using",
|
||||
format!(
|
||||
"!{iter}.any({})",
|
||||
any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
|
||||
),
|
||||
sugg,
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
|
|
@ -127,13 +136,18 @@ pub(super) fn check<'tcx>(
|
|||
let string = snippet(cx, search_recv.span, "..");
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let find_arg = snippet_with_applicability(cx, search_arg.span, "..", &mut applicability);
|
||||
let sugg = if is_receiver_of_method_call(cx, expr) {
|
||||
format!("(!{string}.contains({find_arg}))")
|
||||
} else {
|
||||
format!("!{string}.contains({find_arg})")
|
||||
};
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SEARCH_IS_SOME,
|
||||
expr.span,
|
||||
msg,
|
||||
"consider using",
|
||||
format!("!{string}.contains({find_arg})"),
|
||||
sugg,
|
||||
applicability,
|
||||
);
|
||||
},
|
||||
|
|
@ -142,3 +156,13 @@ pub(super) fn check<'tcx>(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
|
||||
if let Some(parent_expr) = get_parent_expr(cx, expr)
|
||||
&& let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
|
||||
&& receiver.hir_id == expr.hir_id
|
||||
{
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ use std::collections::VecDeque;
|
|||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for borrow operations (`&`) that used as a generic argument to a
|
||||
/// Checks for borrow operations (`&`) that are used as a generic argument to a
|
||||
/// function when the borrowed value could be used.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult, GlobalId};
|
|||
use rustc_middle::ty::adjustment::Adjust;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{sym, DUMMY_SP, InnerSpan, Span};
|
||||
use rustc_span::{sym, InnerSpan, Span, DUMMY_SP};
|
||||
use rustc_target::abi::VariantIdx;
|
||||
|
||||
// FIXME: this is a correctness problem but there's no suitable
|
||||
|
|
@ -297,12 +297,7 @@ impl NonCopyConst {
|
|||
fn is_value_unfrozen_expr<'tcx>(&self, cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool {
|
||||
let args = cx.typeck_results().node_args(hir_id);
|
||||
|
||||
let result = Self::const_eval_resolve(
|
||||
cx.tcx,
|
||||
cx.param_env,
|
||||
ty::UnevaluatedConst::new(def_id, args),
|
||||
DUMMY_SP,
|
||||
);
|
||||
let result = Self::const_eval_resolve(cx.tcx, cx.param_env, ty::UnevaluatedConst::new(def_id, args), DUMMY_SP);
|
||||
self.is_value_unfrozen_raw(cx, result, ty)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -300,11 +300,8 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes {
|
|||
e.span,
|
||||
"calling `as_bytes()` on `include_str!(..)`",
|
||||
"consider using `include_bytes!(..)` instead",
|
||||
snippet_with_applicability(cx, receiver.span.source_callsite(), r#""foo""#, &mut applicability).replacen(
|
||||
"include_str",
|
||||
"include_bytes",
|
||||
1,
|
||||
),
|
||||
snippet_with_applicability(cx, receiver.span.source_callsite(), r#""foo""#, &mut applicability)
|
||||
.replacen("include_str", "include_bytes", 1),
|
||||
applicability,
|
||||
);
|
||||
} else if lit_content.as_str().is_ascii()
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ pub(super) fn check<'tcx>(
|
|||
};
|
||||
|
||||
// FIXME: This can be simplified once `NonZero<T>` is stable.
|
||||
let coercable_types = [
|
||||
let coercible_types = [
|
||||
("NonZeroU8", tcx.types.u8),
|
||||
("NonZeroU16", tcx.types.u16),
|
||||
("NonZeroU32", tcx.types.u32),
|
||||
|
|
@ -44,7 +44,7 @@ pub(super) fn check<'tcx>(
|
|||
|
||||
let int_type = substs.type_at(0);
|
||||
|
||||
let Some(nonzero_alias) = coercable_types.iter().find_map(|(nonzero_alias, t)| {
|
||||
let Some(nonzero_alias) = coercible_types.iter().find_map(|(nonzero_alias, t)| {
|
||||
if *t == int_type && *t == from_ty {
|
||||
Some(nonzero_alias)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
use rustc_hir_typeck::cast::check_cast;
|
||||
use super::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use rustc_ast::ExprPrecedence;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, Node};
|
||||
use rustc_hir_typeck::cast::check_cast;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::cast::CastKind;
|
||||
use rustc_middle::ty::Ty;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue