Auto merge of #4839 - flip1995:rollup-new-lints, r=flip1995

Rollup of 4 Pull requests with new lints

Rollup of pull requests

- #4816 (New lint: zst_offset)
- #4814 (New lint: Implement ifs_same_cond_fn)
- #4807 (Add `large_stack_arrays` lint)
- #4806 (Issue/4623)

changelog: add [`zst_offset`] lint
changelog: New lint: [`ifs_same_cond_fn`]
cahngelog: Add new lint [large_stack_arrays]
changelog: added lint [`tabs_in_doc_comments`]
This commit is contained in:
bors 2019-11-23 17:15:11 +00:00
commit 35a559fdd1
19 changed files with 786 additions and 4 deletions

View file

@ -1059,6 +1059,7 @@ Released 2018-09-13
[`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits [`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits
[`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups [`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups
[`large_enum_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant [`large_enum_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant
[`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays
[`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty [`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty
[`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero [`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero
[`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return [`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return
@ -1176,6 +1177,7 @@ Released 2018-09-13
[`result_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unwrap_or_else [`result_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unwrap_or_else
[`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used [`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used
[`reverse_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#reverse_range_loop [`reverse_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#reverse_range_loop
[`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition
[`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some [`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
[`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse [`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse
[`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse [`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse
@ -1201,6 +1203,7 @@ Released 2018-09-13
[`suspicious_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_map [`suspicious_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_map
[`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl [`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl
[`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting [`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting
[`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments
[`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment
[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr [`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr
[`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some [`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some
@ -1273,4 +1276,5 @@ Released 2018-09-13
[`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal [`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal
[`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr [`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr
[`zero_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space [`zero_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space
[`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset
<!-- end autogenerated links to lint list --> <!-- end autogenerated links to lint list -->

View file

@ -6,7 +6,7 @@
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
[There are 333 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) [There are 337 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you: We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:

View file

@ -40,6 +40,53 @@ declare_clippy_lint! {
"consecutive `ifs` with the same condition" "consecutive `ifs` with the same condition"
} }
declare_clippy_lint! {
/// **What it does:** Checks for consecutive `if`s with the same function call.
///
/// **Why is this bad?** This is probably a copy & paste error.
/// Despite the fact that function can have side effects and `if` works as
/// intended, such an approach is implicit and can be considered a "code smell".
///
/// **Known problems:** Hopefully none.
///
/// **Example:**
/// ```ignore
/// if foo() == bar {
/// …
/// } else if foo() == bar {
/// …
/// }
/// ```
///
/// This probably should be:
/// ```ignore
/// if foo() == bar {
/// …
/// } else if foo() == baz {
/// …
/// }
/// ```
///
/// or if the original code was not a typo and called function mutates a state,
/// consider move the mutation out of the `if` condition to avoid similarity to
/// a copy & paste error:
///
/// ```ignore
/// let first = foo();
/// if first == bar {
/// …
/// } else {
/// let second = foo();
/// if second == bar {
/// …
/// }
/// }
/// ```
pub SAME_FUNCTIONS_IN_IF_CONDITION,
pedantic,
"consecutive `ifs` with the same function call"
}
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for `if/else` with the same body as the *then* part /// **What it does:** Checks for `if/else` with the same body as the *then* part
/// and the *else* part. /// and the *else* part.
@ -102,7 +149,7 @@ declare_clippy_lint! {
"`match` with identical arm bodies" "`match` with identical arm bodies"
} }
declare_lint_pass!(CopyAndPaste => [IFS_SAME_COND, IF_SAME_THEN_ELSE, MATCH_SAME_ARMS]); declare_lint_pass!(CopyAndPaste => [IFS_SAME_COND, SAME_FUNCTIONS_IN_IF_CONDITION, IF_SAME_THEN_ELSE, MATCH_SAME_ARMS]);
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CopyAndPaste { impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CopyAndPaste {
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) { fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
@ -119,6 +166,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CopyAndPaste {
let (conds, blocks) = if_sequence(expr); let (conds, blocks) = if_sequence(expr);
lint_same_then_else(cx, &blocks); lint_same_then_else(cx, &blocks);
lint_same_cond(cx, &conds); lint_same_cond(cx, &conds);
lint_same_fns_in_if_cond(cx, &conds);
lint_match_arms(cx, expr); lint_match_arms(cx, expr);
} }
} }
@ -163,6 +211,34 @@ fn lint_same_cond(cx: &LateContext<'_, '_>, conds: &[&Expr]) {
} }
} }
/// Implementation of `SAME_FUNCTIONS_IN_IF_CONDITION`.
fn lint_same_fns_in_if_cond(cx: &LateContext<'_, '_>, conds: &[&Expr]) {
let hash: &dyn Fn(&&Expr) -> u64 = &|expr| -> u64 {
let mut h = SpanlessHash::new(cx, cx.tables);
h.hash_expr(expr);
h.finish()
};
let eq: &dyn Fn(&&Expr, &&Expr) -> bool = &|&lhs, &rhs| -> bool {
// Do not spawn warning if `IFS_SAME_COND` already produced it.
if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, rhs) {
return false;
}
SpanlessEq::new(cx).eq_expr(lhs, rhs)
};
for (i, j) in search_same(conds, hash, eq) {
span_note_and_lint(
cx,
SAME_FUNCTIONS_IN_IF_CONDITION,
j.span,
"this `if` has the same function call as a previous if",
i.span,
"same as this",
);
}
}
/// Implementation of `MATCH_SAME_ARMS`. /// Implementation of `MATCH_SAME_ARMS`.
fn lint_match_arms<'tcx>(cx: &LateContext<'_, 'tcx>, expr: &Expr) { fn lint_match_arms<'tcx>(cx: &LateContext<'_, 'tcx>, expr: &Expr) {
fn same_bindings<'tcx>( fn same_bindings<'tcx>(

View file

@ -0,0 +1,68 @@
use rustc::hir::*;
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::mir::interpret::ConstValue;
use rustc::ty::{self, ConstKind};
use rustc::{declare_tool_lint, impl_lint_pass};
use if_chain::if_chain;
use crate::rustc_target::abi::LayoutOf;
use crate::utils::{snippet, span_help_and_lint};
declare_clippy_lint! {
/// **What it does:** Checks for local arrays that may be too large.
///
/// **Why is this bad?** Large local arrays may cause stack overflow.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust,ignore
/// let a = [0u32; 1_000_000];
/// ```
pub LARGE_STACK_ARRAYS,
pedantic,
"allocating large arrays on stack may cause stack overflow"
}
pub struct LargeStackArrays {
maximum_allowed_size: u64,
}
impl LargeStackArrays {
#[must_use]
pub fn new(maximum_allowed_size: u64) -> Self {
Self { maximum_allowed_size }
}
}
impl_lint_pass!(LargeStackArrays => [LARGE_STACK_ARRAYS]);
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LargeStackArrays {
fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr) {
if_chain! {
if let ExprKind::Repeat(_, _) = expr.kind;
if let ty::Array(element_type, cst) = cx.tables.expr_ty(expr).kind;
if let ConstKind::Value(val) = cst.val;
if let ConstValue::Scalar(element_count) = val;
if let Ok(element_count) = element_count.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;
then {
span_help_and_lint(
cx,
LARGE_STACK_ARRAYS,
expr.span,
&format!(
"allocating a local array larger than {} bytes",
self.maximum_allowed_size
),
&format!(
"consider allocating on the heap with vec!{}.into_boxed_slice()",
snippet(cx, expr.span, "[...]")
),
);
}
}
}
}

View file

@ -212,6 +212,7 @@ pub mod int_plus_one;
pub mod integer_division; pub mod integer_division;
pub mod items_after_statements; pub mod items_after_statements;
pub mod large_enum_variant; pub mod large_enum_variant;
pub mod large_stack_arrays;
pub mod len_zero; pub mod len_zero;
pub mod let_if_seq; pub mod let_if_seq;
pub mod lifetimes; pub mod lifetimes;
@ -274,6 +275,7 @@ pub mod slow_vector_initialization;
pub mod strings; pub mod strings;
pub mod suspicious_trait_impl; pub mod suspicious_trait_impl;
pub mod swap; pub mod swap;
pub mod tabs_in_doc_comments;
pub mod temporary_assignment; pub mod temporary_assignment;
pub mod to_digit_is_some; pub mod to_digit_is_some;
pub mod trait_bounds; pub mod trait_bounds;
@ -472,6 +474,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
&copies::IFS_SAME_COND, &copies::IFS_SAME_COND,
&copies::IF_SAME_THEN_ELSE, &copies::IF_SAME_THEN_ELSE,
&copies::MATCH_SAME_ARMS, &copies::MATCH_SAME_ARMS,
&copies::SAME_FUNCTIONS_IN_IF_CONDITION,
&copy_iterator::COPY_ITERATOR, &copy_iterator::COPY_ITERATOR,
&dbg_macro::DBG_MACRO, &dbg_macro::DBG_MACRO,
&default_trait_access::DEFAULT_TRAIT_ACCESS, &default_trait_access::DEFAULT_TRAIT_ACCESS,
@ -538,6 +541,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
&integer_division::INTEGER_DIVISION, &integer_division::INTEGER_DIVISION,
&items_after_statements::ITEMS_AFTER_STATEMENTS, &items_after_statements::ITEMS_AFTER_STATEMENTS,
&large_enum_variant::LARGE_ENUM_VARIANT, &large_enum_variant::LARGE_ENUM_VARIANT,
&large_stack_arrays::LARGE_STACK_ARRAYS,
&len_zero::LEN_WITHOUT_IS_EMPTY, &len_zero::LEN_WITHOUT_IS_EMPTY,
&len_zero::LEN_ZERO, &len_zero::LEN_ZERO,
&let_if_seq::USELESS_LET_IF_SEQ, &let_if_seq::USELESS_LET_IF_SEQ,
@ -624,6 +628,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
&methods::USELESS_ASREF, &methods::USELESS_ASREF,
&methods::WRONG_PUB_SELF_CONVENTION, &methods::WRONG_PUB_SELF_CONVENTION,
&methods::WRONG_SELF_CONVENTION, &methods::WRONG_SELF_CONVENTION,
&methods::ZST_OFFSET,
&minmax::MIN_MAX, &minmax::MIN_MAX,
&misc::CMP_NAN, &misc::CMP_NAN,
&misc::CMP_OWNED, &misc::CMP_OWNED,
@ -716,6 +721,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL, &suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL,
&swap::ALMOST_SWAPPED, &swap::ALMOST_SWAPPED,
&swap::MANUAL_SWAP, &swap::MANUAL_SWAP,
&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS,
&temporary_assignment::TEMPORARY_ASSIGNMENT, &temporary_assignment::TEMPORARY_ASSIGNMENT,
&to_digit_is_some::TO_DIGIT_IS_SOME, &to_digit_is_some::TO_DIGIT_IS_SOME,
&trait_bounds::TYPE_REPETITION_IN_BOUNDS, &trait_bounds::TYPE_REPETITION_IN_BOUNDS,
@ -946,10 +952,13 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
store.register_early_pass(|| box utils::internal_lints::ClippyLintsInternal); store.register_early_pass(|| box utils::internal_lints::ClippyLintsInternal);
let enum_variant_name_threshold = conf.enum_variant_name_threshold; let enum_variant_name_threshold = conf.enum_variant_name_threshold;
store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold)); store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold));
store.register_early_pass(|| box tabs_in_doc_comments::TabsInDocComments);
store.register_late_pass(|| box unused_self::UnusedSelf); store.register_late_pass(|| box unused_self::UnusedSelf);
store.register_late_pass(|| box mutable_debug_assertion::DebugAssertWithMutCall); store.register_late_pass(|| box mutable_debug_assertion::DebugAssertWithMutCall);
store.register_late_pass(|| box exit::Exit); store.register_late_pass(|| box exit::Exit);
store.register_late_pass(|| box to_digit_is_some::ToDigitIsSome); store.register_late_pass(|| box to_digit_is_some::ToDigitIsSome);
let array_size_threshold = conf.array_size_threshold;
store.register_late_pass(move || box large_stack_arrays::LargeStackArrays::new(array_size_threshold));
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
LintId::of(&arithmetic::FLOAT_ARITHMETIC), LintId::of(&arithmetic::FLOAT_ARITHMETIC),
@ -989,6 +998,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
LintId::of(&attrs::INLINE_ALWAYS), LintId::of(&attrs::INLINE_ALWAYS),
LintId::of(&checked_conversions::CHECKED_CONVERSIONS), LintId::of(&checked_conversions::CHECKED_CONVERSIONS),
LintId::of(&copies::MATCH_SAME_ARMS), LintId::of(&copies::MATCH_SAME_ARMS),
LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION),
LintId::of(&copy_iterator::COPY_ITERATOR), LintId::of(&copy_iterator::COPY_ITERATOR),
LintId::of(&default_trait_access::DEFAULT_TRAIT_ACCESS), LintId::of(&default_trait_access::DEFAULT_TRAIT_ACCESS),
LintId::of(&derive::EXPL_IMPL_CLONE_ON_COPY), LintId::of(&derive::EXPL_IMPL_CLONE_ON_COPY),
@ -1003,6 +1013,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
LintId::of(&if_not_else::IF_NOT_ELSE), LintId::of(&if_not_else::IF_NOT_ELSE),
LintId::of(&infinite_iter::MAYBE_INFINITE_ITER), LintId::of(&infinite_iter::MAYBE_INFINITE_ITER),
LintId::of(&items_after_statements::ITEMS_AFTER_STATEMENTS), LintId::of(&items_after_statements::ITEMS_AFTER_STATEMENTS),
LintId::of(&large_stack_arrays::LARGE_STACK_ARRAYS),
LintId::of(&literal_representation::LARGE_DIGIT_GROUPS), LintId::of(&literal_representation::LARGE_DIGIT_GROUPS),
LintId::of(&loops::EXPLICIT_INTO_ITER_LOOP), LintId::of(&loops::EXPLICIT_INTO_ITER_LOOP),
LintId::of(&loops::EXPLICIT_ITER_LOOP), LintId::of(&loops::EXPLICIT_ITER_LOOP),
@ -1176,6 +1187,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
LintId::of(&methods::UNNECESSARY_FOLD), LintId::of(&methods::UNNECESSARY_FOLD),
LintId::of(&methods::USELESS_ASREF), LintId::of(&methods::USELESS_ASREF),
LintId::of(&methods::WRONG_SELF_CONVENTION), LintId::of(&methods::WRONG_SELF_CONVENTION),
LintId::of(&methods::ZST_OFFSET),
LintId::of(&minmax::MIN_MAX), LintId::of(&minmax::MIN_MAX),
LintId::of(&misc::CMP_NAN), LintId::of(&misc::CMP_NAN),
LintId::of(&misc::CMP_OWNED), LintId::of(&misc::CMP_OWNED),
@ -1243,6 +1255,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
LintId::of(&swap::ALMOST_SWAPPED), LintId::of(&swap::ALMOST_SWAPPED),
LintId::of(&swap::MANUAL_SWAP), LintId::of(&swap::MANUAL_SWAP),
LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT), LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT),
LintId::of(&to_digit_is_some::TO_DIGIT_IS_SOME), LintId::of(&to_digit_is_some::TO_DIGIT_IS_SOME),
LintId::of(&transmute::CROSSPOINTER_TRANSMUTE), LintId::of(&transmute::CROSSPOINTER_TRANSMUTE),
@ -1370,6 +1383,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::NEEDLESS_RETURN),
LintId::of(&returns::UNUSED_UNIT), LintId::of(&returns::UNUSED_UNIT),
LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&strings::STRING_LIT_AS_BYTES),
LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
LintId::of(&to_digit_is_some::TO_DIGIT_IS_SOME), LintId::of(&to_digit_is_some::TO_DIGIT_IS_SOME),
LintId::of(&try_err::TRY_ERR), LintId::of(&try_err::TRY_ERR),
LintId::of(&types::FN_TO_NUMERIC_CAST), LintId::of(&types::FN_TO_NUMERIC_CAST),
@ -1497,6 +1511,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
LintId::of(&methods::CLONE_DOUBLE_REF), LintId::of(&methods::CLONE_DOUBLE_REF),
LintId::of(&methods::TEMPORARY_CSTRING_AS_PTR), LintId::of(&methods::TEMPORARY_CSTRING_AS_PTR),
LintId::of(&methods::UNINIT_ASSUMED_INIT), LintId::of(&methods::UNINIT_ASSUMED_INIT),
LintId::of(&methods::ZST_OFFSET),
LintId::of(&minmax::MIN_MAX), LintId::of(&minmax::MIN_MAX),
LintId::of(&misc::CMP_NAN), LintId::of(&misc::CMP_NAN),
LintId::of(&misc::FLOAT_CMP), LintId::of(&misc::FLOAT_CMP),

View file

@ -1065,6 +1065,23 @@ declare_clippy_lint! {
"`.chcked_add/sub(x).unwrap_or(MAX/MIN)`" "`.chcked_add/sub(x).unwrap_or(MAX/MIN)`"
} }
declare_clippy_lint! {
/// **What it does:** Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to
/// zero-sized types
///
/// **Why is this bad?** This is a no-op, and likely unintended
///
/// **Known problems:** None
///
/// **Example:**
/// ```ignore
/// unsafe { (&() as *const ()).offest(1) };
/// ```
pub ZST_OFFSET,
correctness,
"Check for offset calculations on raw pointers to zero-sized types"
}
declare_lint_pass!(Methods => [ declare_lint_pass!(Methods => [
OPTION_UNWRAP_USED, OPTION_UNWRAP_USED,
RESULT_UNWRAP_USED, RESULT_UNWRAP_USED,
@ -1109,6 +1126,7 @@ declare_lint_pass!(Methods => [
SUSPICIOUS_MAP, SUSPICIOUS_MAP,
UNINIT_ASSUMED_INIT, UNINIT_ASSUMED_INIT,
MANUAL_SATURATING_ARITHMETIC, MANUAL_SATURATING_ARITHMETIC,
ZST_OFFSET,
]); ]);
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods { impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
@ -1167,6 +1185,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
| ["unwrap_or", arith @ "checked_mul"] => { | ["unwrap_or", arith @ "checked_mul"] => {
manual_saturating_arithmetic::lint(cx, expr, &arg_lists, &arith["checked_".len()..]) manual_saturating_arithmetic::lint(cx, expr, &arg_lists, &arith["checked_".len()..])
}, },
["add"] | ["offset"] | ["sub"] | ["wrapping_offset"] | ["wrapping_add"] | ["wrapping_sub"] => {
check_pointer_offset(cx, expr, arg_lists[0])
},
_ => {}, _ => {},
} }
@ -3063,3 +3084,15 @@ fn contains_return(expr: &hir::Expr) -> bool {
visitor.visit_expr(expr); visitor.visit_expr(expr);
visitor.found visitor.found
} }
fn check_pointer_offset(cx: &LateContext<'_, '_>, expr: &hir::Expr, args: &[hir::Expr]) {
if_chain! {
if args.len() == 2;
if let ty::RawPtr(ty::TypeAndMut { ref ty, .. }) = cx.tables.expr_ty(&args[0]).kind;
if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty));
if layout.is_zst();
then {
span_lint(cx, ZST_OFFSET, expr.span, "offset calculation on zero-sized value");
}
}
}

View file

@ -0,0 +1,219 @@
use crate::utils::span_lint_and_sugg;
use rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass};
use rustc::{declare_lint_pass, declare_tool_lint};
use rustc_errors::Applicability;
use std::convert::TryFrom;
use syntax::ast;
use syntax::source_map::{BytePos, Span};
declare_clippy_lint! {
/// **What it does:** Checks doc comments for usage of tab characters.
///
/// **Why is this bad?** The rust style-guide promotes spaces instead of tabs for indentation.
/// To keep a consistent view on the source, also doc comments should not have tabs.
/// Also, explaining ascii-diagrams containing tabs can get displayed incorrectly when the
/// display settings of the author and reader differ.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// ///
/// /// Struct to hold two strings:
/// /// - first one
/// /// - second one
/// pub struct DoubleString {
/// ///
/// /// - First String:
/// /// - needs to be inside here
/// first_string: String,
/// ///
/// /// - Second String:
/// /// - needs to be inside here
/// second_string: String,
///}
/// ```
///
/// Will be converted to:
/// ```rust
/// ///
/// /// Struct to hold two strings:
/// /// - first one
/// /// - second one
/// pub struct DoubleString {
/// ///
/// /// - First String:
/// /// - needs to be inside here
/// first_string: String,
/// ///
/// /// - Second String:
/// /// - needs to be inside here
/// second_string: String,
///}
/// ```
pub TABS_IN_DOC_COMMENTS,
style,
"using tabs in doc comments is not recommended"
}
declare_lint_pass!(TabsInDocComments => [TABS_IN_DOC_COMMENTS]);
impl TabsInDocComments {
fn warn_if_tabs_in_doc(cx: &EarlyContext<'_>, attr: &ast::Attribute) {
if let ast::AttrKind::DocComment(comment) = attr.kind {
let comment = comment.as_str();
for (lo, hi) in get_chunks_of_tabs(&comment) {
let new_span = Span::new(
attr.span.lo() + BytePos(lo),
attr.span.lo() + BytePos(hi),
attr.span.ctxt(),
);
span_lint_and_sugg(
cx,
TABS_IN_DOC_COMMENTS,
new_span,
"using tabs in doc comments is not recommended",
"consider using four spaces per tab",
" ".repeat((hi - lo) as usize),
Applicability::MaybeIncorrect,
);
}
}
}
}
impl EarlyLintPass for TabsInDocComments {
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attribute: &ast::Attribute) {
Self::warn_if_tabs_in_doc(cx, &attribute);
}
}
///
/// scans the string for groups of tabs and returns the start(inclusive) and end positions
/// (exclusive) of all groups
/// e.g. "sd\tasd\t\taa" will be converted to [(2, 3), (6, 8)] as
/// 012 3456 7 89
/// ^-^ ^---^
fn get_chunks_of_tabs(the_str: &str) -> Vec<(u32, u32)> {
let line_length_way_to_long = "doc comment longer than 2^32 chars";
let mut spans: Vec<(u32, u32)> = vec![];
let mut current_start: u32 = 0;
// tracker to decide if the last group of tabs is not closed by a non-tab character
let mut is_active = false;
let chars_array: Vec<_> = the_str.chars().collect();
if chars_array == vec!['\t'] {
return vec![(0, 1)];
}
for (index, arr) in chars_array.windows(2).enumerate() {
let index = u32::try_from(index).expect(line_length_way_to_long);
match arr {
['\t', '\t'] => {
// either string starts with double tab, then we have to set it active,
// otherwise is_active is true anyway
is_active = true;
},
[_, '\t'] => {
// as ['\t', '\t'] is excluded, this has to be a start of a tab group,
// set indices accordingly
is_active = true;
current_start = index + 1;
},
['\t', _] => {
// this now has to be an end of the group, hence we have to push a new tuple
is_active = false;
spans.push((current_start, index + 1));
},
_ => {},
}
}
// only possible when tabs are at the end, insert last group
if is_active {
spans.push((
current_start,
u32::try_from(the_str.chars().count()).expect(line_length_way_to_long),
));
}
spans
}
#[cfg(test)]
mod tests_for_get_chunks_of_tabs {
use super::get_chunks_of_tabs;
#[test]
fn test_empty_string() {
let res = get_chunks_of_tabs("");
assert_eq!(res, vec![]);
}
#[test]
fn test_simple() {
let res = get_chunks_of_tabs("sd\t\t\taa");
assert_eq!(res, vec![(2, 5)]);
}
#[test]
fn test_only_t() {
let res = get_chunks_of_tabs("\t\t");
assert_eq!(res, vec![(0, 2)]);
}
#[test]
fn test_only_one_t() {
let res = get_chunks_of_tabs("\t");
assert_eq!(res, vec![(0, 1)]);
}
#[test]
fn test_double() {
let res = get_chunks_of_tabs("sd\tasd\t\taa");
assert_eq!(res, vec![(2, 3), (6, 8)]);
}
#[test]
fn test_start() {
let res = get_chunks_of_tabs("\t\taa");
assert_eq!(res, vec![(0, 2)]);
}
#[test]
fn test_end() {
let res = get_chunks_of_tabs("aa\t\t");
assert_eq!(res, vec![(2, 4)]);
}
#[test]
fn test_start_single() {
let res = get_chunks_of_tabs("\taa");
assert_eq!(res, vec![(0, 1)]);
}
#[test]
fn test_end_single() {
let res = get_chunks_of_tabs("aa\t");
assert_eq!(res, vec![(2, 3)]);
}
#[test]
fn test_no_tabs() {
let res = get_chunks_of_tabs("dsfs");
assert_eq!(res, vec![]);
}
}

View file

@ -151,6 +151,8 @@ define_Conf! {
(trivial_copy_size_limit, "trivial_copy_size_limit", None => Option<u64>), (trivial_copy_size_limit, "trivial_copy_size_limit", None => Option<u64>),
/// Lint: TOO_MANY_LINES. The maximum number of lines a function or method can have /// Lint: TOO_MANY_LINES. The maximum number of lines a function or method can have
(too_many_lines_threshold, "too_many_lines_threshold", 100 => u64), (too_many_lines_threshold, "too_many_lines_threshold", 100 => u64),
/// Lint: LARGE_STACK_ARRAYS. The maximum allowed size for arrays on the stack
(array_size_threshold, "array_size_threshold", 512_000 => u64),
} }
impl Default for Conf { impl Default for Conf {

View file

@ -6,7 +6,7 @@ pub use lint::Lint;
pub use lint::LINT_LEVELS; pub use lint::LINT_LEVELS;
// begin lint list, do not remove this comment, its used in `update_lints` // begin lint list, do not remove this comment, its used in `update_lints`
pub const ALL_LINTS: [Lint; 333] = [ pub const ALL_LINTS: [Lint; 337] = [
Lint { Lint {
name: "absurd_extreme_comparisons", name: "absurd_extreme_comparisons",
group: "correctness", group: "correctness",
@ -903,6 +903,13 @@ pub const ALL_LINTS: [Lint; 333] = [
deprecation: None, deprecation: None,
module: "large_enum_variant", module: "large_enum_variant",
}, },
Lint {
name: "large_stack_arrays",
group: "pedantic",
desc: "allocating large arrays on stack may cause stack overflow",
deprecation: None,
module: "large_stack_arrays",
},
Lint { Lint {
name: "len_without_is_empty", name: "len_without_is_empty",
group: "style", group: "style",
@ -1708,6 +1715,13 @@ pub const ALL_LINTS: [Lint; 333] = [
deprecation: None, deprecation: None,
module: "loops", module: "loops",
}, },
Lint {
name: "same_functions_in_if_condition",
group: "pedantic",
desc: "consecutive `ifs` with the same function call",
deprecation: None,
module: "copies",
},
Lint { Lint {
name: "search_is_some", name: "search_is_some",
group: "complexity", group: "complexity",
@ -1862,6 +1876,13 @@ pub const ALL_LINTS: [Lint; 333] = [
deprecation: None, deprecation: None,
module: "formatting", module: "formatting",
}, },
Lint {
name: "tabs_in_doc_comments",
group: "style",
desc: "using tabs in doc comments is not recommended",
deprecation: None,
module: "tabs_in_doc_comments",
},
Lint { Lint {
name: "temporary_assignment", name: "temporary_assignment",
group: "complexity", group: "complexity",
@ -2338,5 +2359,12 @@ pub const ALL_LINTS: [Lint; 333] = [
deprecation: None, deprecation: None,
module: "unicode", module: "unicode",
}, },
Lint {
name: "zst_offset",
group: "correctness",
desc: "Check for offset calculations on raw pointers to zero-sized types",
deprecation: None,
module: "methods",
},
]; ];
// end lint list, do not remove this comment, its used in `update_lints` // end lint list, do not remove this comment, its used in `update_lints`

View file

@ -1,4 +1,4 @@
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `third-party` at line 5 column 1 error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `third-party` at line 5 column 1
error: aborting due to previous error error: aborting due to previous error

View file

@ -0,0 +1,30 @@
#![warn(clippy::large_stack_arrays)]
#![allow(clippy::large_enum_variant)]
#[derive(Clone, Copy)]
struct S {
pub data: [u64; 32],
}
#[derive(Clone, Copy)]
enum E {
S(S),
T(u32),
}
fn main() {
let bad = (
[0u32; 20_000_000],
[S { data: [0; 32] }; 5000],
[Some(""); 20_000_000],
[E::T(0); 5000],
);
let good = (
[0u32; 1000],
[S { data: [0; 32] }; 1000],
[Some(""); 1000],
[E::T(0); 1000],
[(); 20_000_000],
);
}

View file

@ -0,0 +1,35 @@
error: allocating a local array larger than 512000 bytes
--> $DIR/large_stack_arrays.rs:17:9
|
LL | [0u32; 20_000_000],
| ^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::large-stack-arrays` implied by `-D warnings`
= help: consider allocating on the heap with vec![0u32; 20_000_000].into_boxed_slice()
error: allocating a local array larger than 512000 bytes
--> $DIR/large_stack_arrays.rs:18:9
|
LL | [S { data: [0; 32] }; 5000],
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider allocating on the heap with vec![S { data: [0; 32] }; 5000].into_boxed_slice()
error: allocating a local array larger than 512000 bytes
--> $DIR/large_stack_arrays.rs:19:9
|
LL | [Some(""); 20_000_000],
| ^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider allocating on the heap with vec![Some(""); 20_000_000].into_boxed_slice()
error: allocating a local array larger than 512000 bytes
--> $DIR/large_stack_arrays.rs:20:9
|
LL | [E::T(0); 5000],
| ^^^^^^^^^^^^^^^
|
= help: consider allocating on the heap with vec![E::T(0); 5000].into_boxed_slice()
error: aborting due to 4 previous errors

View file

@ -0,0 +1,80 @@
#![warn(clippy::same_functions_in_if_condition)]
#![allow(clippy::ifs_same_cond)] // This warning is different from `ifs_same_cond`.
#![allow(clippy::if_same_then_else, clippy::comparison_chain)] // all empty blocks
fn function() -> bool {
true
}
fn fn_arg(_arg: u8) -> bool {
true
}
struct Struct;
impl Struct {
fn method(&self) -> bool {
true
}
fn method_arg(&self, _arg: u8) -> bool {
true
}
}
fn ifs_same_cond_fn() {
let a = 0;
let obj = Struct;
if function() {
} else if function() {
//~ ERROR ifs same condition
}
if fn_arg(a) {
} else if fn_arg(a) {
//~ ERROR ifs same condition
}
if obj.method() {
} else if obj.method() {
//~ ERROR ifs same condition
}
if obj.method_arg(a) {
} else if obj.method_arg(a) {
//~ ERROR ifs same condition
}
let mut v = vec![1];
if v.pop() == None {
//~ ERROR ifs same condition
} else if v.pop() == None {
}
if v.len() == 42 {
//~ ERROR ifs same condition
} else if v.len() == 42 {
}
if v.len() == 1 {
// ok, different conditions
} else if v.len() == 2 {
}
if fn_arg(0) {
// ok, different arguments.
} else if fn_arg(1) {
}
if obj.method_arg(0) {
// ok, different arguments.
} else if obj.method_arg(1) {
}
if a == 1 {
// ok, warning is on `ifs_same_cond` behalf.
} else if a == 1 {
}
}
fn main() {}

View file

@ -0,0 +1,75 @@
error: this `if` has the same function call as a previous if
--> $DIR/same_functions_in_if_condition.rs:29:15
|
LL | } else if function() {
| ^^^^^^^^^^
|
= note: `-D clippy::same-functions-in-if-condition` implied by `-D warnings`
note: same as this
--> $DIR/same_functions_in_if_condition.rs:28:8
|
LL | if function() {
| ^^^^^^^^^^
error: this `if` has the same function call as a previous if
--> $DIR/same_functions_in_if_condition.rs:34:15
|
LL | } else if fn_arg(a) {
| ^^^^^^^^^
|
note: same as this
--> $DIR/same_functions_in_if_condition.rs:33:8
|
LL | if fn_arg(a) {
| ^^^^^^^^^
error: this `if` has the same function call as a previous if
--> $DIR/same_functions_in_if_condition.rs:39:15
|
LL | } else if obj.method() {
| ^^^^^^^^^^^^
|
note: same as this
--> $DIR/same_functions_in_if_condition.rs:38:8
|
LL | if obj.method() {
| ^^^^^^^^^^^^
error: this `if` has the same function call as a previous if
--> $DIR/same_functions_in_if_condition.rs:44:15
|
LL | } else if obj.method_arg(a) {
| ^^^^^^^^^^^^^^^^^
|
note: same as this
--> $DIR/same_functions_in_if_condition.rs:43:8
|
LL | if obj.method_arg(a) {
| ^^^^^^^^^^^^^^^^^
error: this `if` has the same function call as a previous if
--> $DIR/same_functions_in_if_condition.rs:51:15
|
LL | } else if v.pop() == None {
| ^^^^^^^^^^^^^^^
|
note: same as this
--> $DIR/same_functions_in_if_condition.rs:49:8
|
LL | if v.pop() == None {
| ^^^^^^^^^^^^^^^
error: this `if` has the same function call as a previous if
--> $DIR/same_functions_in_if_condition.rs:56:15
|
LL | } else if v.len() == 42 {
| ^^^^^^^^^^^^^
|
note: same as this
--> $DIR/same_functions_in_if_condition.rs:54:8
|
LL | if v.len() == 42 {
| ^^^^^^^^^^^^^
error: aborting due to 6 previous errors

View file

@ -0,0 +1,22 @@
// run-rustfix
#![warn(clippy::tabs_in_doc_comments)]
#[allow(dead_code)]
///
/// Struct to hold two strings:
/// - first one
/// - second one
pub struct DoubleString {
///
/// - First String:
/// - needs to be inside here
first_string: String,
///
/// - Second String:
/// - needs to be inside here
second_string: String,
}
/// This is main
fn main() {}

View file

@ -0,0 +1,22 @@
// run-rustfix
#![warn(clippy::tabs_in_doc_comments)]
#[allow(dead_code)]
///
/// Struct to hold two strings:
/// - first one
/// - second one
pub struct DoubleString {
///
/// - First String:
/// - needs to be inside here
first_string: String,
///
/// - Second String:
/// - needs to be inside here
second_string: String,
}
/// This is main
fn main() {}

View file

@ -0,0 +1,52 @@
error: using tabs in doc comments is not recommended
--> $DIR/tabs_in_doc_comments.rs:12:9
|
LL | /// - First String:
| ^^^^ help: consider using four spaces per tab
|
= note: `-D clippy::tabs-in-doc-comments` implied by `-D warnings`
error: using tabs in doc comments is not recommended
--> $DIR/tabs_in_doc_comments.rs:13:9
|
LL | /// - needs to be inside here
| ^^^^^^^^ help: consider using four spaces per tab
error: using tabs in doc comments is not recommended
--> $DIR/tabs_in_doc_comments.rs:16:9
|
LL | /// - Second String:
| ^^^^ help: consider using four spaces per tab
error: using tabs in doc comments is not recommended
--> $DIR/tabs_in_doc_comments.rs:17:9
|
LL | /// - needs to be inside here
| ^^^^^^^^ help: consider using four spaces per tab
error: using tabs in doc comments is not recommended
--> $DIR/tabs_in_doc_comments.rs:8:5
|
LL | /// - first one
| ^^^^ help: consider using four spaces per tab
error: using tabs in doc comments is not recommended
--> $DIR/tabs_in_doc_comments.rs:8:13
|
LL | /// - first one
| ^^^^^^^^ help: consider using four spaces per tab
error: using tabs in doc comments is not recommended
--> $DIR/tabs_in_doc_comments.rs:9:5
|
LL | /// - second one
| ^^^^ help: consider using four spaces per tab
error: using tabs in doc comments is not recommended
--> $DIR/tabs_in_doc_comments.rs:9:14
|
LL | /// - second one
| ^^^^ help: consider using four spaces per tab
error: aborting due to 8 previous errors

12
tests/ui/zero_offset.rs Normal file
View file

@ -0,0 +1,12 @@
fn main() {
unsafe {
let x = &() as *const ();
x.offset(0);
x.wrapping_add(0);
x.sub(0);
x.wrapping_sub(0);
let y = &1 as *const u8;
y.offset(0);
}
}

View file

@ -0,0 +1,9 @@
error[E0606]: casting `&i32` as `*const u8` is invalid
--> $DIR/zero_offset.rs:9:17
|
LL | let y = &1 as *const u8;
| ^^^^^^^^^^^^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0606`.