Merge commit '4f3ab69ea0' into clippyup
This commit is contained in:
parent
24444945ec
commit
4ccafea92d
97 changed files with 2558 additions and 359 deletions
|
|
@ -54,7 +54,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Msrv
|
|||
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
&format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"),
|
||||
format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"),
|
||||
sugg,
|
||||
rustc_errors::Applicability::HasPlaceholders,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -161,6 +161,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::float_literal::LOSSY_FLOAT_LITERAL_INFO,
|
||||
crate::floating_point_arithmetic::IMPRECISE_FLOPS_INFO,
|
||||
crate::floating_point_arithmetic::SUBOPTIMAL_FLOPS_INFO,
|
||||
crate::fn_null_check::FN_NULL_CHECK_INFO,
|
||||
crate::format::USELESS_FORMAT_INFO,
|
||||
crate::format_args::FORMAT_IN_FORMAT_ARGS_INFO,
|
||||
crate::format_args::TO_STRING_IN_FORMAT_ARGS_INFO,
|
||||
|
|
@ -494,6 +495,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE_INFO,
|
||||
crate::pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF_INFO,
|
||||
crate::pattern_type_mismatch::PATTERN_TYPE_MISMATCH_INFO,
|
||||
crate::permissions_set_readonly_false::PERMISSIONS_SET_READONLY_FALSE_INFO,
|
||||
crate::precedence::PRECEDENCE_INFO,
|
||||
crate::ptr::CMP_NULL_INFO,
|
||||
crate::ptr::INVALID_NULL_PTR_USAGE_INFO,
|
||||
|
|
@ -535,6 +537,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES_INFO,
|
||||
crate::single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS_INFO,
|
||||
crate::size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT_INFO,
|
||||
crate::size_of_ref::SIZE_OF_REF_INFO,
|
||||
crate::slow_vector_initialization::SLOW_VECTOR_INITIALIZATION_INFO,
|
||||
crate::std_instead_of_core::ALLOC_INSTEAD_OF_CORE_INFO,
|
||||
crate::std_instead_of_core::STD_INSTEAD_OF_ALLOC_INFO,
|
||||
|
|
@ -568,6 +571,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::transmute::TRANSMUTE_INT_TO_BOOL_INFO,
|
||||
crate::transmute::TRANSMUTE_INT_TO_CHAR_INFO,
|
||||
crate::transmute::TRANSMUTE_INT_TO_FLOAT_INFO,
|
||||
crate::transmute::TRANSMUTE_NULL_TO_FN_INFO,
|
||||
crate::transmute::TRANSMUTE_NUM_TO_BYTES_INFO,
|
||||
crate::transmute::TRANSMUTE_PTR_TO_PTR_INFO,
|
||||
crate::transmute::TRANSMUTE_PTR_TO_REF_INFO,
|
||||
|
|
|
|||
|
|
@ -1282,10 +1282,10 @@ fn referent_used_exactly_once<'tcx>(
|
|||
possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir)));
|
||||
}
|
||||
let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1;
|
||||
// If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is
|
||||
// that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of
|
||||
// itself. See the comment in that method for an explanation as to why.
|
||||
possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location)
|
||||
// If `place.local` were not included here, the `copyable_iterator::warn` test would fail. The
|
||||
// reason is that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible
|
||||
// borrower of itself. See the comment in that method for an explanation as to why.
|
||||
possible_borrower.at_most_borrowers(cx, &[local, place.local], place.local, location)
|
||||
&& used_exactly_once(mir, place.local).unwrap_or(false)
|
||||
} else {
|
||||
false
|
||||
|
|
|
|||
|
|
@ -324,7 +324,7 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args:
|
|||
let maybe_neg_sugg = |expr, hir_id| {
|
||||
let sugg = Sugg::hir(cx, expr, "..");
|
||||
if matches!(op, BinOpKind::Sub) && hir_id == rhs.hir_id {
|
||||
format!("-{sugg}")
|
||||
format!("-{}", sugg.maybe_par())
|
||||
} else {
|
||||
sugg.to_string()
|
||||
}
|
||||
|
|
|
|||
106
clippy_lints/src/fn_null_check.rs
Normal file
106
clippy_lints/src/fn_null_check.rs
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::{is_integer_literal, is_path_diagnostic_item};
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for comparing a function pointer to null.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Function pointers are assumed to not be null.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// let fn_ptr: fn() = /* somehow obtained nullable function pointer */
|
||||
///
|
||||
/// if (fn_ptr as *const ()).is_null() { ... }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// let fn_ptr: Option<fn()> = /* somehow obtained nullable function pointer */
|
||||
///
|
||||
/// if fn_ptr.is_none() { ... }
|
||||
/// ```
|
||||
#[clippy::version = "1.67.0"]
|
||||
pub FN_NULL_CHECK,
|
||||
correctness,
|
||||
"`fn()` type assumed to be nullable"
|
||||
}
|
||||
declare_lint_pass!(FnNullCheck => [FN_NULL_CHECK]);
|
||||
|
||||
fn lint_expr(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
FN_NULL_CHECK,
|
||||
expr.span,
|
||||
"function pointer assumed to be nullable, even though it isn't",
|
||||
None,
|
||||
"try wrapping your function pointer type in `Option<T>` instead, and using `is_none` to check for null pointer value",
|
||||
);
|
||||
}
|
||||
|
||||
fn is_fn_ptr_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
if let ExprKind::Cast(cast_expr, cast_ty) = expr.kind
|
||||
&& let TyKind::Ptr(_) = cast_ty.kind
|
||||
{
|
||||
cx.typeck_results().expr_ty_adjusted(cast_expr).is_fn()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for FnNullCheck {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
match expr.kind {
|
||||
// Catching:
|
||||
// (fn_ptr as *<const/mut> <ty>).is_null()
|
||||
ExprKind::MethodCall(method_name, receiver, _, _)
|
||||
if method_name.ident.as_str() == "is_null" && is_fn_ptr_cast(cx, receiver) =>
|
||||
{
|
||||
lint_expr(cx, expr);
|
||||
},
|
||||
|
||||
ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Eq) => {
|
||||
let to_check: &Expr<'_>;
|
||||
if is_fn_ptr_cast(cx, left) {
|
||||
to_check = right;
|
||||
} else if is_fn_ptr_cast(cx, right) {
|
||||
to_check = left;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
match to_check.kind {
|
||||
// Catching:
|
||||
// (fn_ptr as *<const/mut> <ty>) == (0 as <ty>)
|
||||
ExprKind::Cast(cast_expr, _) if is_integer_literal(cast_expr, 0) => {
|
||||
lint_expr(cx, expr);
|
||||
},
|
||||
|
||||
// Catching:
|
||||
// (fn_ptr as *<const/mut> <ty>) == std::ptr::null()
|
||||
ExprKind::Call(func, []) if is_path_diagnostic_item(cx, func, sym::ptr_null) => {
|
||||
lint_expr(cx, expr);
|
||||
},
|
||||
|
||||
// Catching:
|
||||
// (fn_ptr as *<const/mut> <ty>) == <const that evaluates to null_ptr>
|
||||
_ if matches!(
|
||||
constant(cx, cx.typeck_results(), to_check),
|
||||
Some((Constant::RawPtr(0), _))
|
||||
) =>
|
||||
{
|
||||
lint_expr(cx, expr);
|
||||
},
|
||||
|
||||
_ => {},
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -382,7 +382,7 @@ fn check_format_in_format_args(
|
|||
call_site,
|
||||
&format!("`format!` in `{name}!` args"),
|
||||
|diag| {
|
||||
diag.help(&format!(
|
||||
diag.help(format!(
|
||||
"combine the `format!(..)` arguments with the outer `{name}!(..)` call"
|
||||
));
|
||||
diag.help("or consider changing `format!` to `format_args!`");
|
||||
|
|
|
|||
|
|
@ -63,23 +63,40 @@ declare_clippy_lint! {
|
|||
/// arguments but are not marked `unsafe`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The function should probably be marked `unsafe`, since
|
||||
/// for an arbitrary raw pointer, there is no way of telling for sure if it is
|
||||
/// valid.
|
||||
/// The function should almost definitely be marked `unsafe`, since for an
|
||||
/// arbitrary raw pointer, there is no way of telling for sure if it is valid.
|
||||
///
|
||||
/// In general, this lint should **never be disabled** unless it is definitely a
|
||||
/// false positive (please submit an issue if so) since it breaks Rust's
|
||||
/// soundness guarantees, directly exposing API users to potentially dangerous
|
||||
/// program behavior. This is also true for internal APIs, as it is easy to leak
|
||||
/// unsoundness.
|
||||
///
|
||||
/// ### Context
|
||||
/// In Rust, an `unsafe {...}` block is used to indicate that the code in that
|
||||
/// section has been verified in some way that the compiler can not. For a
|
||||
/// function that accepts a raw pointer then accesses the pointer's data, this is
|
||||
/// generally impossible as the incoming pointer could point anywhere, valid or
|
||||
/// not. So, the signature should be marked `unsafe fn`: this indicates that the
|
||||
/// function's caller must provide some verification that the arguments it sends
|
||||
/// are valid (and then call the function within an `unsafe` block).
|
||||
///
|
||||
/// ### Known problems
|
||||
/// * It does not check functions recursively so if the pointer is passed to a
|
||||
/// private non-`unsafe` function which does the dereferencing, the lint won't
|
||||
/// trigger.
|
||||
/// trigger (false negative).
|
||||
/// * It only checks for arguments whose type are raw pointers, not raw pointers
|
||||
/// got from an argument in some other way (`fn foo(bar: &[*const u8])` or
|
||||
/// `some_argument.get_raw_ptr()`).
|
||||
/// `some_argument.get_raw_ptr()`) (false negative).
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// pub fn foo(x: *const u8) {
|
||||
/// println!("{}", unsafe { *x });
|
||||
/// }
|
||||
///
|
||||
/// // this call "looks" safe but will segfault or worse!
|
||||
/// // foo(invalid_ptr);
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
|
|
@ -87,6 +104,12 @@ declare_clippy_lint! {
|
|||
/// pub unsafe fn foo(x: *const u8) {
|
||||
/// println!("{}", unsafe { *x });
|
||||
/// }
|
||||
///
|
||||
/// // this would cause a compiler error for calling without `unsafe`
|
||||
/// // foo(invalid_ptr);
|
||||
///
|
||||
/// // sound call if the caller knows the pointer is valid
|
||||
/// unsafe { foo(valid_ptr); }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub NOT_UNSAFE_PTR_ARG_DEREF,
|
||||
|
|
|
|||
|
|
@ -34,12 +34,12 @@ declare_clippy_lint! {
|
|||
}
|
||||
|
||||
pub struct LargeConstArrays {
|
||||
maximum_allowed_size: u64,
|
||||
maximum_allowed_size: u128,
|
||||
}
|
||||
|
||||
impl LargeConstArrays {
|
||||
#[must_use]
|
||||
pub fn new(maximum_allowed_size: u64) -> Self {
|
||||
pub fn new(maximum_allowed_size: u128) -> Self {
|
||||
Self { maximum_allowed_size }
|
||||
}
|
||||
}
|
||||
|
|
@ -56,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays {
|
|||
if let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind();
|
||||
if let Ok(element_count) = element_count.try_to_machine_usize(cx.tcx);
|
||||
if let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes());
|
||||
if self.maximum_allowed_size < element_count * element_size;
|
||||
if self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size);
|
||||
|
||||
then {
|
||||
let hi_pos = item.ident.span.lo() - BytePos::from_usize(1);
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
|
|||
);
|
||||
diag.span_label(
|
||||
def.variants[variants_size[1].ind].span,
|
||||
&if variants_size[1].fields_size.is_empty() {
|
||||
if variants_size[1].fields_size.is_empty() {
|
||||
"the second-largest variant carries no data at all".to_owned()
|
||||
} else {
|
||||
format!(
|
||||
|
|
|
|||
|
|
@ -24,12 +24,12 @@ declare_clippy_lint! {
|
|||
}
|
||||
|
||||
pub struct LargeStackArrays {
|
||||
maximum_allowed_size: u64,
|
||||
maximum_allowed_size: u128,
|
||||
}
|
||||
|
||||
impl LargeStackArrays {
|
||||
#[must_use]
|
||||
pub fn new(maximum_allowed_size: u64) -> Self {
|
||||
pub fn new(maximum_allowed_size: u128) -> Self {
|
||||
Self { maximum_allowed_size }
|
||||
}
|
||||
}
|
||||
|
|
@ -45,7 +45,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
|
|||
&& let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes())
|
||||
&& !cx.tcx.hir().parent_iter(expr.hir_id)
|
||||
.any(|(_, node)| matches!(node, Node::Item(Item { kind: ItemKind::Static(..), .. })))
|
||||
&& self.maximum_allowed_size < element_count * element_size {
|
||||
&& self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size) {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
LARGE_STACK_ARRAYS,
|
||||
|
|
|
|||
|
|
@ -361,7 +361,7 @@ fn check_for_is_empty<'tcx>(
|
|||
db.span_note(span, "`is_empty` defined here");
|
||||
}
|
||||
if let Some(self_kind) = self_kind {
|
||||
db.note(&output.expected_sig(self_kind));
|
||||
db.note(output.expected_sig(self_kind));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,6 +125,7 @@ mod explicit_write;
|
|||
mod fallible_impl_from;
|
||||
mod float_literal;
|
||||
mod floating_point_arithmetic;
|
||||
mod fn_null_check;
|
||||
mod format;
|
||||
mod format_args;
|
||||
mod format_impl;
|
||||
|
|
@ -234,6 +235,7 @@ mod partialeq_ne_impl;
|
|||
mod partialeq_to_none;
|
||||
mod pass_by_ref_or_value;
|
||||
mod pattern_type_mismatch;
|
||||
mod permissions_set_readonly_false;
|
||||
mod precedence;
|
||||
mod ptr;
|
||||
mod ptr_offset_with_cast;
|
||||
|
|
@ -263,6 +265,7 @@ mod shadow;
|
|||
mod single_char_lifetime_names;
|
||||
mod single_component_path_imports;
|
||||
mod size_of_in_element_count;
|
||||
mod size_of_ref;
|
||||
mod slow_vector_initialization;
|
||||
mod std_instead_of_core;
|
||||
mod strings;
|
||||
|
|
@ -334,7 +337,7 @@ pub fn read_conf(sess: &Session, path: &io::Result<Option<PathBuf>>) -> Conf {
|
|||
Ok(Some(path)) => path,
|
||||
Ok(None) => return Conf::default(),
|
||||
Err(error) => {
|
||||
sess.struct_err(&format!("error finding Clippy's configuration file: {error}"))
|
||||
sess.struct_err(format!("error finding Clippy's configuration file: {error}"))
|
||||
.emit();
|
||||
return Conf::default();
|
||||
},
|
||||
|
|
@ -902,6 +905,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow));
|
||||
store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv())));
|
||||
store.register_late_pass(|_| Box::new(semicolon_block::SemicolonBlock));
|
||||
store.register_late_pass(|_| Box::new(fn_null_check::FnNullCheck));
|
||||
store.register_late_pass(|_| Box::new(permissions_set_readonly_false::PermissionsSetReadonlyFalse));
|
||||
store.register_late_pass(|_| Box::new(size_of_ref::SizeOfRef));
|
||||
// add lints here, do not remove this comment, it's used in `new_lint`
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ pub(super) fn check<'tcx>(
|
|||
applicability,
|
||||
);
|
||||
|
||||
diag.note(&format!(
|
||||
diag.note(format!(
|
||||
"`{name}` is of type `{int_name}`, making it ineligible for `Iterator::enumerate`"
|
||||
));
|
||||
},
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
|
|||
let help = format!("make the function `async` and {ret_sugg}");
|
||||
diag.span_suggestion(
|
||||
header_span,
|
||||
&help,
|
||||
help,
|
||||
format!("async {}{ret_snip}", &header_snip[..ret_pos]),
|
||||
Applicability::MachineApplicable
|
||||
);
|
||||
|
|
|
|||
|
|
@ -35,6 +35,9 @@ declare_clippy_lint! {
|
|||
/// Some may consider panicking in these situations to be desirable, but it also may
|
||||
/// introduce panicking where there wasn't any before.
|
||||
///
|
||||
/// See also [the discussion in the
|
||||
/// PR](https://github.com/rust-lang/rust-clippy/pull/9484#issuecomment-1278922613).
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```rust
|
||||
/// # let (input, min, max) = (0, -2, 1);
|
||||
|
|
@ -78,7 +81,7 @@ declare_clippy_lint! {
|
|||
/// ```
|
||||
#[clippy::version = "1.66.0"]
|
||||
pub MANUAL_CLAMP,
|
||||
complexity,
|
||||
nursery,
|
||||
"using a clamp pattern instead of the clamp function"
|
||||
}
|
||||
impl_lint_pass!(ManualClamp => [MANUAL_CLAMP]);
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
|
|||
|
||||
let test_span = expr.span.until(then.span);
|
||||
span_lint_and_then(cx, MANUAL_STRIP, strippings[0], &format!("stripping a {kind_word} manually"), |diag| {
|
||||
diag.span_note(test_span, &format!("the {kind_word} was tested here"));
|
||||
diag.span_note(test_span, format!("the {kind_word} was tested here"));
|
||||
multispan_sugg(
|
||||
diag,
|
||||
&format!("try using the `strip_{kind_word}` method"),
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use clippy_utils::ty::is_type_diagnostic_item;
|
|||
use clippy_utils::visitors::contains_unsafe_block;
|
||||
use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id};
|
||||
|
||||
use rustc_hir::LangItem::OptionSome;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::{sym, SyntaxContext};
|
||||
|
|
@ -25,15 +25,13 @@ fn get_cond_expr<'tcx>(
|
|||
if let Some(block_expr) = peels_blocks_incl_unsafe_opt(expr);
|
||||
if let ExprKind::If(cond, then_expr, Some(else_expr)) = block_expr.kind;
|
||||
if let PatKind::Binding(_,target, ..) = pat.kind;
|
||||
if let (then_visitor, else_visitor)
|
||||
= (is_some_expr(cx, target, ctxt, then_expr),
|
||||
is_some_expr(cx, target, ctxt, else_expr));
|
||||
if then_visitor != else_visitor; // check that one expr resolves to `Some(x)`, the other to `None`
|
||||
if is_some_expr(cx, target, ctxt, then_expr) && is_none_expr(cx, else_expr)
|
||||
|| is_none_expr(cx, then_expr) && is_some_expr(cx, target, ctxt, else_expr); // check that one expr resolves to `Some(x)`, the other to `None`
|
||||
then {
|
||||
return Some(SomeExpr {
|
||||
expr: peels_blocks_incl_unsafe(cond.peel_drop_temps()),
|
||||
needs_unsafe_block: contains_unsafe_block(cx, expr),
|
||||
needs_negated: !then_visitor // if the `then_expr` resolves to `None`, need to negate the cond
|
||||
needs_negated: is_none_expr(cx, then_expr) // if the `then_expr` resolves to `None`, need to negate the cond
|
||||
})
|
||||
}
|
||||
};
|
||||
|
|
@ -74,6 +72,13 @@ fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr:
|
|||
false
|
||||
}
|
||||
|
||||
fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) {
|
||||
return is_res_lang_ctor(cx, path_res(cx, inner_expr), OptionNone);
|
||||
};
|
||||
false
|
||||
}
|
||||
|
||||
// given the closure: `|<pattern>| <expr>`
|
||||
// returns `|&<pattern>| <expr>`
|
||||
fn add_ampersand_if_copy(body_str: String, has_copy_trait: bool) -> String {
|
||||
|
|
|
|||
|
|
@ -31,19 +31,11 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
|
|||
};
|
||||
|
||||
// Do we need to add ';' to suggestion ?
|
||||
match match_body.kind {
|
||||
ExprKind::Block(block, _) => {
|
||||
// macro + expr_ty(body) == ()
|
||||
if block.span.from_expansion() && cx.typeck_results().expr_ty(match_body).is_unit() {
|
||||
snippet_body.push(';');
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
// expr_ty(body) == ()
|
||||
if cx.typeck_results().expr_ty(match_body).is_unit() {
|
||||
snippet_body.push(';');
|
||||
}
|
||||
},
|
||||
if let ExprKind::Block(block, _) = match_body.kind {
|
||||
// macro + expr_ty(body) == ()
|
||||
if block.span.from_expansion() && cx.typeck_results().expr_ty(match_body).is_unit() {
|
||||
snippet_body.push(';');
|
||||
}
|
||||
}
|
||||
|
||||
let mut applicability = Applicability::MaybeIncorrect;
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
|
|||
let mut has_non_wild = false;
|
||||
for arm in arms {
|
||||
match peel_hir_pat_refs(arm.pat).0.kind {
|
||||
PatKind::Wild => wildcard_span = Some(arm.pat.span),
|
||||
PatKind::Wild if arm.guard.is_none() => wildcard_span = Some(arm.pat.span),
|
||||
PatKind::Binding(_, _, ident, None) => {
|
||||
wildcard_span = Some(arm.pat.span);
|
||||
wildcard_ident = Some(ident);
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ pub fn check(
|
|||
expr.span,
|
||||
&format!("calling `to_string` on `{arg_ty}`"),
|
||||
|diag| {
|
||||
diag.help(&format!(
|
||||
diag.help(format!(
|
||||
"`{self_ty}` implements `ToString` through a slower blanket impl, but `{deref_self_ty}` has a fast specialization of `ToString`"
|
||||
));
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
|
|||
application = Applicability::Unspecified;
|
||||
diag.span_help(
|
||||
pat.span,
|
||||
&format!("for this change `{}` has to be mutable", snippet(cx, pat.span, "..")),
|
||||
format!("for this change `{}` has to be mutable", snippet(cx, pat.span, "..")),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ pub(super) fn check<'tcx>(
|
|||
suggestion.push((map_arg_span.with_hi(map_arg_span.lo()), format!("{unwrap_snippet}, ")));
|
||||
}
|
||||
|
||||
diag.multipart_suggestion(&format!("use `{suggest}` instead"), suggestion, applicability);
|
||||
diag.multipart_suggestion(format!("use `{suggest}` instead"), suggestion, applicability);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ fn check_manual_split_once_indirect(
|
|||
};
|
||||
diag.span_suggestion_verbose(
|
||||
local.span,
|
||||
&format!("try `{r}split_once`"),
|
||||
format!("try `{r}split_once`"),
|
||||
format!("let ({lhs}, {rhs}) = {self_snip}.{r}split_once({pat_snip}){unwrap};"),
|
||||
app,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ pub(super) fn check<'tcx>(
|
|||
span_lint_and_then(cx, UNNECESSARY_LAZY_EVALUATIONS, expr.span, msg, |diag| {
|
||||
diag.span_suggestion(
|
||||
span,
|
||||
&format!("use `{simplify_using}(..)` instead"),
|
||||
format!("use `{simplify_using}(..)` instead"),
|
||||
format!("{simplify_using}({})", snippet(cx, body_expr.span, "..")),
|
||||
applicability,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -284,7 +284,7 @@ fn check<'tcx>(
|
|||
|
||||
diag.span_suggestion(
|
||||
assign.lhs_span,
|
||||
&format!("declare `{binding_name}` here"),
|
||||
format!("declare `{binding_name}` here"),
|
||||
let_snippet,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
|
|
@ -304,7 +304,7 @@ fn check<'tcx>(
|
|||
|
||||
diag.span_suggestion_verbose(
|
||||
usage.stmt.span.shrink_to_lo(),
|
||||
&format!("declare `{binding_name}` here"),
|
||||
format!("declare `{binding_name}` here"),
|
||||
format!("{let_snippet} = "),
|
||||
applicability,
|
||||
);
|
||||
|
|
@ -335,7 +335,7 @@ fn check<'tcx>(
|
|||
|
||||
diag.span_suggestion_verbose(
|
||||
usage.stmt.span.shrink_to_lo(),
|
||||
&format!("declare `{binding_name}` here"),
|
||||
format!("declare `{binding_name}` here"),
|
||||
format!("{let_snippet} = "),
|
||||
applicability,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ fn check_lit(cx: &EarlyContext<'_>, lit: &Lit, span: Span, is_string: bool) {
|
|||
if is_string { "string" } else { "byte string" }
|
||||
),
|
||||
|diag| {
|
||||
diag.help(&format!(
|
||||
diag.help(format!(
|
||||
"octal escapes are not supported, `\\0` is always a null {}",
|
||||
if is_string { "character" } else { "byte" }
|
||||
));
|
||||
|
|
@ -139,7 +139,7 @@ fn check_lit(cx: &EarlyContext<'_>, lit: &Lit, span: Span, is_string: bool) {
|
|||
// suggestion 2: unambiguous null byte
|
||||
diag.span_suggestion(
|
||||
span,
|
||||
&format!(
|
||||
format!(
|
||||
"if the null {} is intended, disambiguate using",
|
||||
if is_string { "character" } else { "byte" }
|
||||
),
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ fn lint_misrefactored_assign_op(
|
|||
let long = format!("{snip_a} = {}", sugg::make_binop(op.into(), a, r));
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
&format!(
|
||||
format!(
|
||||
"did you mean `{snip_a} = {snip_a} {} {snip_r}` or `{long}`? Consider replacing it with",
|
||||
op.as_str()
|
||||
),
|
||||
|
|
|
|||
52
clippy_lints/src/permissions_set_readonly_false.rs
Normal file
52
clippy_lints/src/permissions_set_readonly_false.rs
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::paths;
|
||||
use clippy_utils::ty::match_type;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for calls to `std::fs::Permissions.set_readonly` with argument `false`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// On Unix platforms this results in the file being world writable,
|
||||
/// equivalent to `chmod a+w <file>`.
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// use std::fs::File;
|
||||
/// let f = File::create("foo.txt").unwrap();
|
||||
/// let metadata = f.metadata().unwrap();
|
||||
/// let mut permissions = metadata.permissions();
|
||||
/// permissions.set_readonly(false);
|
||||
/// ```
|
||||
#[clippy::version = "1.66.0"]
|
||||
pub PERMISSIONS_SET_READONLY_FALSE,
|
||||
suspicious,
|
||||
"Checks for calls to `std::fs::Permissions.set_readonly` with argument `false`"
|
||||
}
|
||||
declare_lint_pass!(PermissionsSetReadonlyFalse => [PERMISSIONS_SET_READONLY_FALSE]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for PermissionsSetReadonlyFalse {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if let ExprKind::MethodCall(path, receiver, [arg], _) = &expr.kind
|
||||
&& match_type(cx, cx.typeck_results().expr_ty(receiver), &paths::PERMISSIONS)
|
||||
&& path.ident.name == sym!(set_readonly)
|
||||
&& let ExprKind::Lit(lit) = &arg.kind
|
||||
&& LitKind::Bool(false) == lit.node
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
PERMISSIONS_SET_READONLY_FALSE,
|
||||
expr.span,
|
||||
"call to `set_readonly` with argument `false`",
|
||||
|diag| {
|
||||
diag.note("on Unix platforms this results in the file being world writable");
|
||||
diag.help("you can set the desired permissions using `PermissionsExt`. For more information, see\n\
|
||||
https://doc.rust-lang.org/std/os/unix/fs/trait.PermissionsExt.html");
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -131,7 +131,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
|
|||
// `res = clone(arg)` can be turned into `res = move arg;`
|
||||
// if `arg` is the only borrow of `cloned` at this point.
|
||||
|
||||
if cannot_move_out || !possible_borrower.only_borrowers(&[arg], cloned, loc) {
|
||||
if cannot_move_out || !possible_borrower.at_most_borrowers(cx, &[arg], cloned, loc) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -178,7 +178,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
|
|||
// StorageDead(pred_arg);
|
||||
// res = to_path_buf(cloned);
|
||||
// ```
|
||||
if cannot_move_out || !possible_borrower.only_borrowers(&[arg, cloned], local, loc) {
|
||||
if cannot_move_out || !possible_borrower.at_most_borrowers(cx, &[arg, cloned], local, loc) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use core::ops::ControlFlow;
|
|||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, PatKind, StmtKind};
|
||||
use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, LangItem, MatchSource, PatKind, QPath, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::subst::GenericArgKind;
|
||||
|
|
@ -207,6 +207,12 @@ fn check_final_expr<'tcx>(
|
|||
match &peeled_drop_expr.kind {
|
||||
// simple return is always "bad"
|
||||
ExprKind::Ret(ref inner) => {
|
||||
// if desugar of `do yeet`, don't lint
|
||||
if let Some(inner_expr) = inner
|
||||
&& let ExprKind::Call(path_expr, _) = inner_expr.kind
|
||||
&& let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, _, _)) = path_expr.kind {
|
||||
return;
|
||||
}
|
||||
if cx.tcx.hir().attrs(expr.hir_id).is_empty() {
|
||||
let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner));
|
||||
if !borrows {
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
|
|||
|diag| {
|
||||
diag.span_note(
|
||||
trait_method_span,
|
||||
&format!("existing `{method_name}` defined here"),
|
||||
format!("existing `{method_name}` defined here"),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
@ -151,7 +151,7 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
|
|||
// iterate on trait_spans?
|
||||
diag.span_note(
|
||||
trait_spans[0],
|
||||
&format!("existing `{method_name}` defined here"),
|
||||
format!("existing `{method_name}` defined here"),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
|||
73
clippy_lints/src/size_of_ref.rs
Normal file
73
clippy_lints/src/size_of_ref.rs
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
use clippy_utils::{diagnostics::span_lint_and_help, path_def_id, ty::peel_mid_ty_refs};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
///
|
||||
/// Checks for calls to `std::mem::size_of_val()` where the argument is
|
||||
/// a reference to a reference.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// Calling `size_of_val()` with a reference to a reference as the argument
|
||||
/// yields the size of the reference-type, not the size of the value behind
|
||||
/// the reference.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// struct Foo {
|
||||
/// buffer: [u8],
|
||||
/// }
|
||||
///
|
||||
/// impl Foo {
|
||||
/// fn size(&self) -> usize {
|
||||
/// // Note that `&self` as an argument is a `&&Foo`: Because `self`
|
||||
/// // is already a reference, `&self` is a double-reference.
|
||||
/// // The return value of `size_of_val()` therefor is the
|
||||
/// // size of the reference-type, not the size of `self`.
|
||||
/// std::mem::size_of_val(&self)
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// struct Foo {
|
||||
/// buffer: [u8],
|
||||
/// }
|
||||
///
|
||||
/// impl Foo {
|
||||
/// fn size(&self) -> usize {
|
||||
/// // Correct
|
||||
/// std::mem::size_of_val(self)
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.67.0"]
|
||||
pub SIZE_OF_REF,
|
||||
suspicious,
|
||||
"Argument to `std::mem::size_of_val()` is a double-reference, which is almost certainly unintended"
|
||||
}
|
||||
declare_lint_pass!(SizeOfRef => [SIZE_OF_REF]);
|
||||
|
||||
impl LateLintPass<'_> for SizeOfRef {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
|
||||
if let ExprKind::Call(path, [arg]) = expr.kind
|
||||
&& let Some(def_id) = path_def_id(cx, path)
|
||||
&& cx.tcx.is_diagnostic_item(sym::mem_size_of_val, def_id)
|
||||
&& let arg_ty = cx.typeck_results().expr_ty(arg)
|
||||
&& peel_mid_ty_refs(arg_ty).1 > 1
|
||||
{
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
SIZE_OF_REF,
|
||||
expr.span,
|
||||
"argument to `std::mem::size_of_val()` is a reference to a reference",
|
||||
None,
|
||||
"dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the size of the reference-type",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -132,7 +132,7 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa
|
|||
applicability,
|
||||
);
|
||||
if !is_xor_based {
|
||||
diag.note(&format!("or maybe you should use `{sugg}::mem::replace`?"));
|
||||
diag.note(format!("or maybe you should use `{sugg}::mem::replace`?"));
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
@ -214,7 +214,7 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) {
|
|||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
diag.note(
|
||||
&format!("or maybe you should use `{sugg}::mem::replace`?")
|
||||
format!("or maybe you should use `{sugg}::mem::replace`?")
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ mod transmute_float_to_int;
|
|||
mod transmute_int_to_bool;
|
||||
mod transmute_int_to_char;
|
||||
mod transmute_int_to_float;
|
||||
mod transmute_null_to_fn;
|
||||
mod transmute_num_to_bytes;
|
||||
mod transmute_ptr_to_ptr;
|
||||
mod transmute_ptr_to_ref;
|
||||
|
|
@ -409,6 +410,34 @@ declare_clippy_lint! {
|
|||
"transmutes from a null pointer to a reference, which is undefined behavior"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for null function pointer creation through transmute.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Creating a null function pointer is undefined behavior.
|
||||
///
|
||||
/// More info: https://doc.rust-lang.org/nomicon/ffi.html#the-nullable-pointer-optimization
|
||||
///
|
||||
/// ### Known problems
|
||||
/// Not all cases can be detected at the moment of this writing.
|
||||
/// For example, variables which hold a null pointer and are then fed to a `transmute`
|
||||
/// call, aren't detectable yet.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let null_fn: fn() = unsafe { std::mem::transmute( std::ptr::null::<()>() ) };
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let null_fn: Option<fn()> = None;
|
||||
/// ```
|
||||
#[clippy::version = "1.67.0"]
|
||||
pub TRANSMUTE_NULL_TO_FN,
|
||||
correctness,
|
||||
"transmute results in a null function pointer, which is undefined behavior"
|
||||
}
|
||||
|
||||
pub struct Transmute {
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
|
@ -428,6 +457,7 @@ impl_lint_pass!(Transmute => [
|
|||
TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
|
||||
TRANSMUTE_UNDEFINED_REPR,
|
||||
TRANSMUTING_NULL,
|
||||
TRANSMUTE_NULL_TO_FN,
|
||||
]);
|
||||
impl Transmute {
|
||||
#[must_use]
|
||||
|
|
@ -461,6 +491,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
|
|||
let linted = wrong_transmute::check(cx, e, from_ty, to_ty)
|
||||
| crosspointer_transmute::check(cx, e, from_ty, to_ty)
|
||||
| transmuting_null::check(cx, e, arg, to_ty)
|
||||
| transmute_null_to_fn::check(cx, e, arg, to_ty)
|
||||
| transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, &self.msrv)
|
||||
| transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context)
|
||||
| transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context)
|
||||
|
|
|
|||
64
clippy_lints/src/transmute/transmute_null_to_fn.rs
Normal file
64
clippy_lints/src/transmute/transmute_null_to_fn.rs
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::{is_integer_literal, is_path_diagnostic_item};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
use super::TRANSMUTE_NULL_TO_FN;
|
||||
|
||||
fn lint_expr(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
TRANSMUTE_NULL_TO_FN,
|
||||
expr.span,
|
||||
"transmuting a known null pointer into a function pointer",
|
||||
|diag| {
|
||||
diag.span_label(expr.span, "this transmute results in undefined behavior");
|
||||
diag.help(
|
||||
"try wrapping your function pointer type in `Option<T>` instead, and using `None` as a null pointer value"
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>, to_ty: Ty<'tcx>) -> bool {
|
||||
if !to_ty.is_fn() {
|
||||
return false;
|
||||
}
|
||||
|
||||
match arg.kind {
|
||||
// Catching:
|
||||
// transmute over constants that resolve to `null`.
|
||||
ExprKind::Path(ref _qpath)
|
||||
if matches!(constant(cx, cx.typeck_results(), arg), Some((Constant::RawPtr(0), _))) =>
|
||||
{
|
||||
lint_expr(cx, expr);
|
||||
true
|
||||
},
|
||||
|
||||
// Catching:
|
||||
// `std::mem::transmute(0 as *const i32)`
|
||||
ExprKind::Cast(inner_expr, _cast_ty) if is_integer_literal(inner_expr, 0) => {
|
||||
lint_expr(cx, expr);
|
||||
true
|
||||
},
|
||||
|
||||
// Catching:
|
||||
// `std::mem::transmute(std::ptr::null::<i32>())`
|
||||
ExprKind::Call(func1, []) if is_path_diagnostic_item(cx, func1, sym::ptr_null) => {
|
||||
lint_expr(cx, expr);
|
||||
true
|
||||
},
|
||||
|
||||
_ => {
|
||||
// FIXME:
|
||||
// Also catch transmutations of variables which are known nulls.
|
||||
// To do this, MIR const propagation seems to be the better tool.
|
||||
// Whenever MIR const prop routines are more developed, this will
|
||||
// become available. As of this writing (25/03/19) it is not yet.
|
||||
false
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -77,7 +77,7 @@ pub(super) fn check<'tcx>(
|
|||
&format!("transmute from `{from_ty_orig}` which has an undefined layout"),
|
||||
|diag| {
|
||||
if from_ty_orig.peel_refs() != from_ty.peel_refs() {
|
||||
diag.note(&format!("the contained type `{from_ty}` has an undefined layout"));
|
||||
diag.note(format!("the contained type `{from_ty}` has an undefined layout"));
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
@ -91,7 +91,7 @@ pub(super) fn check<'tcx>(
|
|||
&format!("transmute to `{to_ty_orig}` which has an undefined layout"),
|
||||
|diag| {
|
||||
if to_ty_orig.peel_refs() != to_ty.peel_refs() {
|
||||
diag.note(&format!("the contained type `{to_ty}` has an undefined layout"));
|
||||
diag.note(format!("the contained type `{to_ty}` has an undefined layout"));
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
@ -119,16 +119,16 @@ pub(super) fn check<'tcx>(
|
|||
),
|
||||
|diag| {
|
||||
if let Some(same_adt_did) = same_adt_did {
|
||||
diag.note(&format!(
|
||||
diag.note(format!(
|
||||
"two instances of the same generic type (`{}`) may have different layouts",
|
||||
cx.tcx.item_name(same_adt_did)
|
||||
));
|
||||
} else {
|
||||
if from_ty_orig.peel_refs() != from_ty {
|
||||
diag.note(&format!("the contained type `{from_ty}` has an undefined layout"));
|
||||
diag.note(format!("the contained type `{from_ty}` has an undefined layout"));
|
||||
}
|
||||
if to_ty_orig.peel_refs() != to_ty {
|
||||
diag.note(&format!("the contained type `{to_ty}` has an undefined layout"));
|
||||
diag.note(format!("the contained type `{to_ty}` has an undefined layout"));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -146,7 +146,7 @@ pub(super) fn check<'tcx>(
|
|||
&format!("transmute from `{from_ty_orig}` which has an undefined layout"),
|
||||
|diag| {
|
||||
if from_ty_orig.peel_refs() != from_ty {
|
||||
diag.note(&format!("the contained type `{from_ty}` has an undefined layout"));
|
||||
diag.note(format!("the contained type `{from_ty}` has an undefined layout"));
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
@ -163,7 +163,7 @@ pub(super) fn check<'tcx>(
|
|||
&format!("transmute into `{to_ty_orig}` which has an undefined layout"),
|
||||
|diag| {
|
||||
if to_ty_orig.peel_refs() != to_ty {
|
||||
diag.note(&format!("the contained type `{to_ty}` has an undefined layout"));
|
||||
diag.note(format!("the contained type `{to_ty}` has an undefined layout"));
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ pub(super) fn check<'tcx>(
|
|||
&format!("transmute from `{from_ty}` to `{to_ty}` which could be expressed as a pointer cast instead"),
|
||||
|diag| {
|
||||
if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) {
|
||||
let sugg = arg.as_ty(&to_ty.to_string()).to_string();
|
||||
let sugg = arg.as_ty(to_ty.to_string()).to_string();
|
||||
diag.span_suggestion(e.span, "try", sugg, Applicability::MachineApplicable);
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -18,8 +18,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t
|
|||
// Catching transmute over constants that resolve to `null`.
|
||||
let mut const_eval_context = constant_context(cx, cx.typeck_results());
|
||||
if let ExprKind::Path(ref _qpath) = arg.kind &&
|
||||
let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg) &&
|
||||
x == 0
|
||||
let Some(Constant::RawPtr(0)) = const_eval_context.expr(arg)
|
||||
{
|
||||
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ pub(super) fn check<'tcx>(
|
|||
"transmute from an integer to a pointer",
|
||||
|diag| {
|
||||
if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) {
|
||||
diag.span_suggestion(e.span, "try", arg.as_ty(&to_ty.to_string()), Applicability::Unspecified);
|
||||
diag.span_suggestion(e.span, "try", arg.as_ty(to_ty.to_string()), Applicability::Unspecified);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
|
|||
&format!("usage of `{outer_sym}<{generic_snippet}>`"),
|
||||
|diag| {
|
||||
diag.span_suggestion(hir_ty.span, "try", format!("{generic_snippet}"), applicability);
|
||||
diag.note(&format!(
|
||||
diag.note(format!(
|
||||
"`{generic_snippet}` is already a pointer, `{outer_sym}<{generic_snippet}>` allocates a pointer on the heap"
|
||||
));
|
||||
},
|
||||
|
|
@ -78,7 +78,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
|
|||
format!("{outer_sym}<{generic_snippet}>"),
|
||||
applicability,
|
||||
);
|
||||
diag.note(&format!(
|
||||
diag.note(format!(
|
||||
"`{inner_sym}<{generic_snippet}>` is already on the heap, `{outer_sym}<{inner_sym}<{generic_snippet}>>` makes an extra allocation"
|
||||
));
|
||||
},
|
||||
|
|
@ -91,10 +91,10 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
|
|||
hir_ty.span,
|
||||
&format!("usage of `{outer_sym}<{inner_sym}<{generic_snippet}>>`"),
|
||||
|diag| {
|
||||
diag.note(&format!(
|
||||
diag.note(format!(
|
||||
"`{inner_sym}<{generic_snippet}>` is already on the heap, `{outer_sym}<{inner_sym}<{generic_snippet}>>` makes an extra allocation"
|
||||
));
|
||||
diag.help(&format!(
|
||||
diag.help(format!(
|
||||
"consider using just `{outer_sym}<{generic_snippet}>` or `{inner_sym}<{generic_snippet}>`"
|
||||
));
|
||||
},
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp
|
|||
|
||||
if arg_snippets_without_empty_blocks.is_empty() {
|
||||
db.multipart_suggestion(
|
||||
&format!("use {singular}unit literal{plural} instead"),
|
||||
format!("use {singular}unit literal{plural} instead"),
|
||||
args_to_recover
|
||||
.iter()
|
||||
.map(|arg| (arg.span, "()".to_string()))
|
||||
|
|
@ -142,7 +142,7 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp
|
|||
let it_or_them = if plural { "them" } else { "it" };
|
||||
db.span_suggestion(
|
||||
expr.span,
|
||||
&format!(
|
||||
format!(
|
||||
"{or}move the expression{empty_or_s} in front of the call and replace {it_or_them} with the unit literal `()`"
|
||||
),
|
||||
sugg,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
|
||||
use clippy_utils::source::{snippet, snippet_with_macro_callsite};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts};
|
||||
use clippy_utils::{get_parent_expr, is_trait_method, match_def_path, paths};
|
||||
use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts};
|
||||
use clippy_utils::{get_parent_expr, is_trait_method, match_def_path, path_to_local, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, HirId, MatchSource};
|
||||
use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, MatchSource, Node, PatKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
|
@ -81,16 +81,24 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
|
|||
}
|
||||
}
|
||||
if is_trait_method(cx, e, sym::IntoIterator) && name.ident.name == sym::into_iter {
|
||||
if let Some(parent_expr) = get_parent_expr(cx, e) {
|
||||
if let ExprKind::MethodCall(parent_name, ..) = parent_expr.kind {
|
||||
if parent_name.ident.name != sym::into_iter {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if get_parent_expr(cx, e).is_some() &&
|
||||
let Some(id) = path_to_local(recv) &&
|
||||
let Node::Pat(pat) = cx.tcx.hir().get(id) &&
|
||||
let PatKind::Binding(ann, ..) = pat.kind &&
|
||||
ann != BindingAnnotation::MUT
|
||||
{
|
||||
// Do not remove .into_iter() applied to a non-mutable local variable used in
|
||||
// a larger expression context as it would differ in mutability.
|
||||
return;
|
||||
}
|
||||
|
||||
let a = cx.typeck_results().expr_ty(e);
|
||||
let b = cx.typeck_results().expr_ty(recv);
|
||||
if same_type_and_consts(a, b) {
|
||||
|
||||
// If the types are identical then .into_iter() can be removed, unless the type
|
||||
// implements Copy, in which case .into_iter() returns a copy of the receiver and
|
||||
// cannot be safely omitted.
|
||||
if same_type_and_consts(a, b) && !is_copy(cx, b) {
|
||||
let sugg = snippet(cx, recv.span, "<expr>").into_owned();
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
|
|
|||
|
|
@ -333,7 +333,7 @@ define_Conf! {
|
|||
/// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS.
|
||||
///
|
||||
/// The maximum allowed size for arrays on the stack
|
||||
(array_size_threshold: u64 = 512_000),
|
||||
(array_size_threshold: u128 = 512_000),
|
||||
/// Lint: VEC_BOX.
|
||||
///
|
||||
/// The size of the boxed type in bytes, where boxing in a `Vec` is allowed
|
||||
|
|
|
|||
|
|
@ -558,8 +558,8 @@ impl fmt::Display for ClippyConfiguration {
|
|||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
|
||||
writeln!(
|
||||
f,
|
||||
"* `{}`: `{}`: {} (defaults to `{}`)",
|
||||
self.name, self.config_type, self.doc, self.default
|
||||
"* `{}`: `{}`(defaults to `{}`): {}",
|
||||
self.name, self.config_type, self.default, self.doc
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -377,7 +377,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c
|
|||
// print!("\n"), write!(f, "\n")
|
||||
|
||||
diag.multipart_suggestion(
|
||||
&format!("use `{name}ln!` instead"),
|
||||
format!("use `{name}ln!` instead"),
|
||||
vec![(name_span, format!("{name}ln")), (format_string_span, String::new())],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
|
|
@ -388,7 +388,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c
|
|||
let newline_span = format_string_span.with_lo(hi - BytePos(3)).with_hi(hi - BytePos(1));
|
||||
|
||||
diag.multipart_suggestion(
|
||||
&format!("use `{name}ln!` instead"),
|
||||
format!("use `{name}ln!` instead"),
|
||||
vec![(name_span, format!("{name}ln")), (newline_span, String::new())],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue