Skip to content

Commit 35a559f

Browse files
committedNov 23, 2019
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`]
2 parents 60e8413 + 7cc8fa2 commit 35a559f

19 files changed

+786
-4
lines changed
 

‎CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1059,6 +1059,7 @@ Released 2018-09-13
10591059
[`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits
10601060
[`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups
10611061
[`large_enum_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant
1062+
[`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays
10621063
[`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty
10631064
[`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero
10641065
[`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return
@@ -1176,6 +1177,7 @@ Released 2018-09-13
11761177
[`result_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unwrap_or_else
11771178
[`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used
11781179
[`reverse_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#reverse_range_loop
1180+
[`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition
11791181
[`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
11801182
[`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse
11811183
[`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse
@@ -1201,6 +1203,7 @@ Released 2018-09-13
12011203
[`suspicious_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_map
12021204
[`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl
12031205
[`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting
1206+
[`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments
12041207
[`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment
12051208
[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr
12061209
[`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
12731276
[`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal
12741277
[`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr
12751278
[`zero_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space
1279+
[`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset
12761280
<!-- end autogenerated links to lint list -->

‎README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
88

9-
[There are 333 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
9+
[There are 337 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
1010

1111
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
1212

‎clippy_lints/src/copies.rs

+77-1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,53 @@ declare_clippy_lint! {
4040
"consecutive `ifs` with the same condition"
4141
}
4242

43+
declare_clippy_lint! {
44+
/// **What it does:** Checks for consecutive `if`s with the same function call.
45+
///
46+
/// **Why is this bad?** This is probably a copy & paste error.
47+
/// Despite the fact that function can have side effects and `if` works as
48+
/// intended, such an approach is implicit and can be considered a "code smell".
49+
///
50+
/// **Known problems:** Hopefully none.
51+
///
52+
/// **Example:**
53+
/// ```ignore
54+
/// if foo() == bar {
55+
/// …
56+
/// } else if foo() == bar {
57+
/// …
58+
/// }
59+
/// ```
60+
///
61+
/// This probably should be:
62+
/// ```ignore
63+
/// if foo() == bar {
64+
/// …
65+
/// } else if foo() == baz {
66+
/// …
67+
/// }
68+
/// ```
69+
///
70+
/// or if the original code was not a typo and called function mutates a state,
71+
/// consider move the mutation out of the `if` condition to avoid similarity to
72+
/// a copy & paste error:
73+
///
74+
/// ```ignore
75+
/// let first = foo();
76+
/// if first == bar {
77+
/// …
78+
/// } else {
79+
/// let second = foo();
80+
/// if second == bar {
81+
/// …
82+
/// }
83+
/// }
84+
/// ```
85+
pub SAME_FUNCTIONS_IN_IF_CONDITION,
86+
pedantic,
87+
"consecutive `ifs` with the same function call"
88+
}
89+
4390
declare_clippy_lint! {
4491
/// **What it does:** Checks for `if/else` with the same body as the *then* part
4592
/// and the *else* part.
@@ -102,7 +149,7 @@ declare_clippy_lint! {
102149
"`match` with identical arm bodies"
103150
}
104151

105-
declare_lint_pass!(CopyAndPaste => [IFS_SAME_COND, IF_SAME_THEN_ELSE, MATCH_SAME_ARMS]);
152+
declare_lint_pass!(CopyAndPaste => [IFS_SAME_COND, SAME_FUNCTIONS_IN_IF_CONDITION, IF_SAME_THEN_ELSE, MATCH_SAME_ARMS]);
106153

107154
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CopyAndPaste {
108155
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
@@ -119,6 +166,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CopyAndPaste {
119166
let (conds, blocks) = if_sequence(expr);
120167
lint_same_then_else(cx, &blocks);
121168
lint_same_cond(cx, &conds);
169+
lint_same_fns_in_if_cond(cx, &conds);
122170
lint_match_arms(cx, expr);
123171
}
124172
}
@@ -163,6 +211,34 @@ fn lint_same_cond(cx: &LateContext<'_, '_>, conds: &[&Expr]) {
163211
}
164212
}
165213

214+
/// Implementation of `SAME_FUNCTIONS_IN_IF_CONDITION`.
215+
fn lint_same_fns_in_if_cond(cx: &LateContext<'_, '_>, conds: &[&Expr]) {
216+
let hash: &dyn Fn(&&Expr) -> u64 = &|expr| -> u64 {
217+
let mut h = SpanlessHash::new(cx, cx.tables);
218+
h.hash_expr(expr);
219+
h.finish()
220+
};
221+
222+
let eq: &dyn Fn(&&Expr, &&Expr) -> bool = &|&lhs, &rhs| -> bool {
223+
// Do not spawn warning if `IFS_SAME_COND` already produced it.
224+
if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, rhs) {
225+
return false;
226+
}
227+
SpanlessEq::new(cx).eq_expr(lhs, rhs)
228+
};
229+
230+
for (i, j) in search_same(conds, hash, eq) {
231+
span_note_and_lint(
232+
cx,
233+
SAME_FUNCTIONS_IN_IF_CONDITION,
234+
j.span,
235+
"this `if` has the same function call as a previous if",
236+
i.span,
237+
"same as this",
238+
);
239+
}
240+
}
241+
166242
/// Implementation of `MATCH_SAME_ARMS`.
167243
fn lint_match_arms<'tcx>(cx: &LateContext<'_, 'tcx>, expr: &Expr) {
168244
fn same_bindings<'tcx>(
+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
use rustc::hir::*;
2+
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
3+
use rustc::mir::interpret::ConstValue;
4+
use rustc::ty::{self, ConstKind};
5+
use rustc::{declare_tool_lint, impl_lint_pass};
6+
7+
use if_chain::if_chain;
8+
9+
use crate::rustc_target::abi::LayoutOf;
10+
use crate::utils::{snippet, span_help_and_lint};
11+
12+
declare_clippy_lint! {
13+
/// **What it does:** Checks for local arrays that may be too large.
14+
///
15+
/// **Why is this bad?** Large local arrays may cause stack overflow.
16+
///
17+
/// **Known problems:** None.
18+
///
19+
/// **Example:**
20+
/// ```rust,ignore
21+
/// let a = [0u32; 1_000_000];
22+
/// ```
23+
pub LARGE_STACK_ARRAYS,
24+
pedantic,
25+
"allocating large arrays on stack may cause stack overflow"
26+
}
27+
28+
pub struct LargeStackArrays {
29+
maximum_allowed_size: u64,
30+
}
31+
32+
impl LargeStackArrays {
33+
#[must_use]
34+
pub fn new(maximum_allowed_size: u64) -> Self {
35+
Self { maximum_allowed_size }
36+
}
37+
}
38+
39+
impl_lint_pass!(LargeStackArrays => [LARGE_STACK_ARRAYS]);
40+
41+
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LargeStackArrays {
42+
fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr) {
43+
if_chain! {
44+
if let ExprKind::Repeat(_, _) = expr.kind;
45+
if let ty::Array(element_type, cst) = cx.tables.expr_ty(expr).kind;
46+
if let ConstKind::Value(val) = cst.val;
47+
if let ConstValue::Scalar(element_count) = val;
48+
if let Ok(element_count) = element_count.to_machine_usize(&cx.tcx);
49+
if let Ok(element_size) = cx.layout_of(element_type).map(|l| l.size.bytes());
50+
if self.maximum_allowed_size < element_count * element_size;
51+
then {
52+
span_help_and_lint(
53+
cx,
54+
LARGE_STACK_ARRAYS,
55+
expr.span,
56+
&format!(
57+
"allocating a local array larger than {} bytes",
58+
self.maximum_allowed_size
59+
),
60+
&format!(
61+
"consider allocating on the heap with vec!{}.into_boxed_slice()",
62+
snippet(cx, expr.span, "[...]")
63+
),
64+
);
65+
}
66+
}
67+
}
68+
}

‎clippy_lints/src/lib.rs

+15
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ pub mod int_plus_one;
212212
pub mod integer_division;
213213
pub mod items_after_statements;
214214
pub mod large_enum_variant;
215+
pub mod large_stack_arrays;
215216
pub mod len_zero;
216217
pub mod let_if_seq;
217218
pub mod lifetimes;
@@ -274,6 +275,7 @@ pub mod slow_vector_initialization;
274275
pub mod strings;
275276
pub mod suspicious_trait_impl;
276277
pub mod swap;
278+
pub mod tabs_in_doc_comments;
277279
pub mod temporary_assignment;
278280
pub mod to_digit_is_some;
279281
pub mod trait_bounds;
@@ -472,6 +474,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
472474
&copies::IFS_SAME_COND,
473475
&copies::IF_SAME_THEN_ELSE,
474476
&copies::MATCH_SAME_ARMS,
477+
&copies::SAME_FUNCTIONS_IN_IF_CONDITION,
475478
&copy_iterator::COPY_ITERATOR,
476479
&dbg_macro::DBG_MACRO,
477480
&default_trait_access::DEFAULT_TRAIT_ACCESS,
@@ -538,6 +541,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
538541
&integer_division::INTEGER_DIVISION,
539542
&items_after_statements::ITEMS_AFTER_STATEMENTS,
540543
&large_enum_variant::LARGE_ENUM_VARIANT,
544+
&large_stack_arrays::LARGE_STACK_ARRAYS,
541545
&len_zero::LEN_WITHOUT_IS_EMPTY,
542546
&len_zero::LEN_ZERO,
543547
&let_if_seq::USELESS_LET_IF_SEQ,
@@ -624,6 +628,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
624628
&methods::USELESS_ASREF,
625629
&methods::WRONG_PUB_SELF_CONVENTION,
626630
&methods::WRONG_SELF_CONVENTION,
631+
&methods::ZST_OFFSET,
627632
&minmax::MIN_MAX,
628633
&misc::CMP_NAN,
629634
&misc::CMP_OWNED,
@@ -716,6 +721,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
716721
&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL,
717722
&swap::ALMOST_SWAPPED,
718723
&swap::MANUAL_SWAP,
724+
&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS,
719725
&temporary_assignment::TEMPORARY_ASSIGNMENT,
720726
&to_digit_is_some::TO_DIGIT_IS_SOME,
721727
&trait_bounds::TYPE_REPETITION_IN_BOUNDS,
@@ -946,10 +952,13 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
946952
store.register_early_pass(|| box utils::internal_lints::ClippyLintsInternal);
947953
let enum_variant_name_threshold = conf.enum_variant_name_threshold;
948954
store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold));
955+
store.register_early_pass(|| box tabs_in_doc_comments::TabsInDocComments);
949956
store.register_late_pass(|| box unused_self::UnusedSelf);
950957
store.register_late_pass(|| box mutable_debug_assertion::DebugAssertWithMutCall);
951958
store.register_late_pass(|| box exit::Exit);
952959
store.register_late_pass(|| box to_digit_is_some::ToDigitIsSome);
960+
let array_size_threshold = conf.array_size_threshold;
961+
store.register_late_pass(move || box large_stack_arrays::LargeStackArrays::new(array_size_threshold));
953962

954963
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
955964
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
@@ -989,6 +998,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
989998
LintId::of(&attrs::INLINE_ALWAYS),
990999
LintId::of(&checked_conversions::CHECKED_CONVERSIONS),
9911000
LintId::of(&copies::MATCH_SAME_ARMS),
1001+
LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION),
9921002
LintId::of(&copy_iterator::COPY_ITERATOR),
9931003
LintId::of(&default_trait_access::DEFAULT_TRAIT_ACCESS),
9941004
LintId::of(&derive::EXPL_IMPL_CLONE_ON_COPY),
@@ -1003,6 +1013,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
10031013
LintId::of(&if_not_else::IF_NOT_ELSE),
10041014
LintId::of(&infinite_iter::MAYBE_INFINITE_ITER),
10051015
LintId::of(&items_after_statements::ITEMS_AFTER_STATEMENTS),
1016+
LintId::of(&large_stack_arrays::LARGE_STACK_ARRAYS),
10061017
LintId::of(&literal_representation::LARGE_DIGIT_GROUPS),
10071018
LintId::of(&loops::EXPLICIT_INTO_ITER_LOOP),
10081019
LintId::of(&loops::EXPLICIT_ITER_LOOP),
@@ -1176,6 +1187,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
11761187
LintId::of(&methods::UNNECESSARY_FOLD),
11771188
LintId::of(&methods::USELESS_ASREF),
11781189
LintId::of(&methods::WRONG_SELF_CONVENTION),
1190+
LintId::of(&methods::ZST_OFFSET),
11791191
LintId::of(&minmax::MIN_MAX),
11801192
LintId::of(&misc::CMP_NAN),
11811193
LintId::of(&misc::CMP_OWNED),
@@ -1243,6 +1255,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
12431255
LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
12441256
LintId::of(&swap::ALMOST_SWAPPED),
12451257
LintId::of(&swap::MANUAL_SWAP),
1258+
LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
12461259
LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT),
12471260
LintId::of(&to_digit_is_some::TO_DIGIT_IS_SOME),
12481261
LintId::of(&transmute::CROSSPOINTER_TRANSMUTE),
@@ -1370,6 +1383,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
13701383
LintId::of(&returns::NEEDLESS_RETURN),
13711384
LintId::of(&returns::UNUSED_UNIT),
13721385
LintId::of(&strings::STRING_LIT_AS_BYTES),
1386+
LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
13731387
LintId::of(&to_digit_is_some::TO_DIGIT_IS_SOME),
13741388
LintId::of(&try_err::TRY_ERR),
13751389
LintId::of(&types::FN_TO_NUMERIC_CAST),
@@ -1497,6 +1511,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
14971511
LintId::of(&methods::CLONE_DOUBLE_REF),
14981512
LintId::of(&methods::TEMPORARY_CSTRING_AS_PTR),
14991513
LintId::of(&methods::UNINIT_ASSUMED_INIT),
1514+
LintId::of(&methods::ZST_OFFSET),
15001515
LintId::of(&minmax::MIN_MAX),
15011516
LintId::of(&misc::CMP_NAN),
15021517
LintId::of(&misc::FLOAT_CMP),

‎clippy_lints/src/methods/mod.rs

+33
Original file line numberDiff line numberDiff line change
@@ -1065,6 +1065,23 @@ declare_clippy_lint! {
10651065
"`.chcked_add/sub(x).unwrap_or(MAX/MIN)`"
10661066
}
10671067

1068+
declare_clippy_lint! {
1069+
/// **What it does:** Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to
1070+
/// zero-sized types
1071+
///
1072+
/// **Why is this bad?** This is a no-op, and likely unintended
1073+
///
1074+
/// **Known problems:** None
1075+
///
1076+
/// **Example:**
1077+
/// ```ignore
1078+
/// unsafe { (&() as *const ()).offest(1) };
1079+
/// ```
1080+
pub ZST_OFFSET,
1081+
correctness,
1082+
"Check for offset calculations on raw pointers to zero-sized types"
1083+
}
1084+
10681085
declare_lint_pass!(Methods => [
10691086
OPTION_UNWRAP_USED,
10701087
RESULT_UNWRAP_USED,
@@ -1109,6 +1126,7 @@ declare_lint_pass!(Methods => [
11091126
SUSPICIOUS_MAP,
11101127
UNINIT_ASSUMED_INIT,
11111128
MANUAL_SATURATING_ARITHMETIC,
1129+
ZST_OFFSET,
11121130
]);
11131131

11141132
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
@@ -1167,6 +1185,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
11671185
| ["unwrap_or", arith @ "checked_mul"] => {
11681186
manual_saturating_arithmetic::lint(cx, expr, &arg_lists, &arith["checked_".len()..])
11691187
},
1188+
["add"] | ["offset"] | ["sub"] | ["wrapping_offset"] | ["wrapping_add"] | ["wrapping_sub"] => {
1189+
check_pointer_offset(cx, expr, arg_lists[0])
1190+
},
11701191
_ => {},
11711192
}
11721193

@@ -3063,3 +3084,15 @@ fn contains_return(expr: &hir::Expr) -> bool {
30633084
visitor.visit_expr(expr);
30643085
visitor.found
30653086
}
3087+
3088+
fn check_pointer_offset(cx: &LateContext<'_, '_>, expr: &hir::Expr, args: &[hir::Expr]) {
3089+
if_chain! {
3090+
if args.len() == 2;
3091+
if let ty::RawPtr(ty::TypeAndMut { ref ty, .. }) = cx.tables.expr_ty(&args[0]).kind;
3092+
if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty));
3093+
if layout.is_zst();
3094+
then {
3095+
span_lint(cx, ZST_OFFSET, expr.span, "offset calculation on zero-sized value");
3096+
}
3097+
}
3098+
}
+219
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
use crate::utils::span_lint_and_sugg;
2+
use rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass};
3+
use rustc::{declare_lint_pass, declare_tool_lint};
4+
use rustc_errors::Applicability;
5+
use std::convert::TryFrom;
6+
use syntax::ast;
7+
use syntax::source_map::{BytePos, Span};
8+
9+
declare_clippy_lint! {
10+
/// **What it does:** Checks doc comments for usage of tab characters.
11+
///
12+
/// **Why is this bad?** The rust style-guide promotes spaces instead of tabs for indentation.
13+
/// To keep a consistent view on the source, also doc comments should not have tabs.
14+
/// Also, explaining ascii-diagrams containing tabs can get displayed incorrectly when the
15+
/// display settings of the author and reader differ.
16+
///
17+
/// **Known problems:** None.
18+
///
19+
/// **Example:**
20+
/// ```rust
21+
/// ///
22+
/// /// Struct to hold two strings:
23+
/// /// - first one
24+
/// /// - second one
25+
/// pub struct DoubleString {
26+
/// ///
27+
/// /// - First String:
28+
/// /// - needs to be inside here
29+
/// first_string: String,
30+
/// ///
31+
/// /// - Second String:
32+
/// /// - needs to be inside here
33+
/// second_string: String,
34+
///}
35+
/// ```
36+
///
37+
/// Will be converted to:
38+
/// ```rust
39+
/// ///
40+
/// /// Struct to hold two strings:
41+
/// /// - first one
42+
/// /// - second one
43+
/// pub struct DoubleString {
44+
/// ///
45+
/// /// - First String:
46+
/// /// - needs to be inside here
47+
/// first_string: String,
48+
/// ///
49+
/// /// - Second String:
50+
/// /// - needs to be inside here
51+
/// second_string: String,
52+
///}
53+
/// ```
54+
pub TABS_IN_DOC_COMMENTS,
55+
style,
56+
"using tabs in doc comments is not recommended"
57+
}
58+
59+
declare_lint_pass!(TabsInDocComments => [TABS_IN_DOC_COMMENTS]);
60+
61+
impl TabsInDocComments {
62+
fn warn_if_tabs_in_doc(cx: &EarlyContext<'_>, attr: &ast::Attribute) {
63+
if let ast::AttrKind::DocComment(comment) = attr.kind {
64+
let comment = comment.as_str();
65+
66+
for (lo, hi) in get_chunks_of_tabs(&comment) {
67+
let new_span = Span::new(
68+
attr.span.lo() + BytePos(lo),
69+
attr.span.lo() + BytePos(hi),
70+
attr.span.ctxt(),
71+
);
72+
span_lint_and_sugg(
73+
cx,
74+
TABS_IN_DOC_COMMENTS,
75+
new_span,
76+
"using tabs in doc comments is not recommended",
77+
"consider using four spaces per tab",
78+
" ".repeat((hi - lo) as usize),
79+
Applicability::MaybeIncorrect,
80+
);
81+
}
82+
}
83+
}
84+
}
85+
86+
impl EarlyLintPass for TabsInDocComments {
87+
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attribute: &ast::Attribute) {
88+
Self::warn_if_tabs_in_doc(cx, &attribute);
89+
}
90+
}
91+
92+
///
93+
/// scans the string for groups of tabs and returns the start(inclusive) and end positions
94+
/// (exclusive) of all groups
95+
/// e.g. "sd\tasd\t\taa" will be converted to [(2, 3), (6, 8)] as
96+
/// 012 3456 7 89
97+
/// ^-^ ^---^
98+
fn get_chunks_of_tabs(the_str: &str) -> Vec<(u32, u32)> {
99+
let line_length_way_to_long = "doc comment longer than 2^32 chars";
100+
let mut spans: Vec<(u32, u32)> = vec![];
101+
let mut current_start: u32 = 0;
102+
103+
// tracker to decide if the last group of tabs is not closed by a non-tab character
104+
let mut is_active = false;
105+
106+
let chars_array: Vec<_> = the_str.chars().collect();
107+
108+
if chars_array == vec!['\t'] {
109+
return vec![(0, 1)];
110+
}
111+
112+
for (index, arr) in chars_array.windows(2).enumerate() {
113+
let index = u32::try_from(index).expect(line_length_way_to_long);
114+
match arr {
115+
['\t', '\t'] => {
116+
// either string starts with double tab, then we have to set it active,
117+
// otherwise is_active is true anyway
118+
is_active = true;
119+
},
120+
[_, '\t'] => {
121+
// as ['\t', '\t'] is excluded, this has to be a start of a tab group,
122+
// set indices accordingly
123+
is_active = true;
124+
current_start = index + 1;
125+
},
126+
['\t', _] => {
127+
// this now has to be an end of the group, hence we have to push a new tuple
128+
is_active = false;
129+
spans.push((current_start, index + 1));
130+
},
131+
_ => {},
132+
}
133+
}
134+
135+
// only possible when tabs are at the end, insert last group
136+
if is_active {
137+
spans.push((
138+
current_start,
139+
u32::try_from(the_str.chars().count()).expect(line_length_way_to_long),
140+
));
141+
}
142+
143+
spans
144+
}
145+
146+
#[cfg(test)]
147+
mod tests_for_get_chunks_of_tabs {
148+
use super::get_chunks_of_tabs;
149+
150+
#[test]
151+
fn test_empty_string() {
152+
let res = get_chunks_of_tabs("");
153+
154+
assert_eq!(res, vec![]);
155+
}
156+
157+
#[test]
158+
fn test_simple() {
159+
let res = get_chunks_of_tabs("sd\t\t\taa");
160+
161+
assert_eq!(res, vec![(2, 5)]);
162+
}
163+
164+
#[test]
165+
fn test_only_t() {
166+
let res = get_chunks_of_tabs("\t\t");
167+
168+
assert_eq!(res, vec![(0, 2)]);
169+
}
170+
171+
#[test]
172+
fn test_only_one_t() {
173+
let res = get_chunks_of_tabs("\t");
174+
175+
assert_eq!(res, vec![(0, 1)]);
176+
}
177+
178+
#[test]
179+
fn test_double() {
180+
let res = get_chunks_of_tabs("sd\tasd\t\taa");
181+
182+
assert_eq!(res, vec![(2, 3), (6, 8)]);
183+
}
184+
185+
#[test]
186+
fn test_start() {
187+
let res = get_chunks_of_tabs("\t\taa");
188+
189+
assert_eq!(res, vec![(0, 2)]);
190+
}
191+
192+
#[test]
193+
fn test_end() {
194+
let res = get_chunks_of_tabs("aa\t\t");
195+
196+
assert_eq!(res, vec![(2, 4)]);
197+
}
198+
199+
#[test]
200+
fn test_start_single() {
201+
let res = get_chunks_of_tabs("\taa");
202+
203+
assert_eq!(res, vec![(0, 1)]);
204+
}
205+
206+
#[test]
207+
fn test_end_single() {
208+
let res = get_chunks_of_tabs("aa\t");
209+
210+
assert_eq!(res, vec![(2, 3)]);
211+
}
212+
213+
#[test]
214+
fn test_no_tabs() {
215+
let res = get_chunks_of_tabs("dsfs");
216+
217+
assert_eq!(res, vec![]);
218+
}
219+
}

‎clippy_lints/src/utils/conf.rs

+2
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ define_Conf! {
151151
(trivial_copy_size_limit, "trivial_copy_size_limit", None => Option<u64>),
152152
/// Lint: TOO_MANY_LINES. The maximum number of lines a function or method can have
153153
(too_many_lines_threshold, "too_many_lines_threshold", 100 => u64),
154+
/// Lint: LARGE_STACK_ARRAYS. The maximum allowed size for arrays on the stack
155+
(array_size_threshold, "array_size_threshold", 512_000 => u64),
154156
}
155157

156158
impl Default for Conf {

‎src/lintlist/mod.rs

+29-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pub use lint::Lint;
66
pub use lint::LINT_LEVELS;
77

88
// begin lint list, do not remove this comment, it’s used in `update_lints`
9-
pub const ALL_LINTS: [Lint; 333] = [
9+
pub const ALL_LINTS: [Lint; 337] = [
1010
Lint {
1111
name: "absurd_extreme_comparisons",
1212
group: "correctness",
@@ -903,6 +903,13 @@ pub const ALL_LINTS: [Lint; 333] = [
903903
deprecation: None,
904904
module: "large_enum_variant",
905905
},
906+
Lint {
907+
name: "large_stack_arrays",
908+
group: "pedantic",
909+
desc: "allocating large arrays on stack may cause stack overflow",
910+
deprecation: None,
911+
module: "large_stack_arrays",
912+
},
906913
Lint {
907914
name: "len_without_is_empty",
908915
group: "style",
@@ -1708,6 +1715,13 @@ pub const ALL_LINTS: [Lint; 333] = [
17081715
deprecation: None,
17091716
module: "loops",
17101717
},
1718+
Lint {
1719+
name: "same_functions_in_if_condition",
1720+
group: "pedantic",
1721+
desc: "consecutive `ifs` with the same function call",
1722+
deprecation: None,
1723+
module: "copies",
1724+
},
17111725
Lint {
17121726
name: "search_is_some",
17131727
group: "complexity",
@@ -1862,6 +1876,13 @@ pub const ALL_LINTS: [Lint; 333] = [
18621876
deprecation: None,
18631877
module: "formatting",
18641878
},
1879+
Lint {
1880+
name: "tabs_in_doc_comments",
1881+
group: "style",
1882+
desc: "using tabs in doc comments is not recommended",
1883+
deprecation: None,
1884+
module: "tabs_in_doc_comments",
1885+
},
18651886
Lint {
18661887
name: "temporary_assignment",
18671888
group: "complexity",
@@ -2338,5 +2359,12 @@ pub const ALL_LINTS: [Lint; 333] = [
23382359
deprecation: None,
23392360
module: "unicode",
23402361
},
2362+
Lint {
2363+
name: "zst_offset",
2364+
group: "correctness",
2365+
desc: "Check for offset calculations on raw pointers to zero-sized types",
2366+
deprecation: None,
2367+
module: "methods",
2368+
},
23412369
];
23422370
// end lint list, do not remove this comment, it’s used in `update_lints`
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
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`, `third-party` at line 5 column 1
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
22

33
error: aborting due to previous error
44

‎tests/ui/large_stack_arrays.rs

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#![warn(clippy::large_stack_arrays)]
2+
#![allow(clippy::large_enum_variant)]
3+
4+
#[derive(Clone, Copy)]
5+
struct S {
6+
pub data: [u64; 32],
7+
}
8+
9+
#[derive(Clone, Copy)]
10+
enum E {
11+
S(S),
12+
T(u32),
13+
}
14+
15+
fn main() {
16+
let bad = (
17+
[0u32; 20_000_000],
18+
[S { data: [0; 32] }; 5000],
19+
[Some(""); 20_000_000],
20+
[E::T(0); 5000],
21+
);
22+
23+
let good = (
24+
[0u32; 1000],
25+
[S { data: [0; 32] }; 1000],
26+
[Some(""); 1000],
27+
[E::T(0); 1000],
28+
[(); 20_000_000],
29+
);
30+
}

‎tests/ui/large_stack_arrays.stderr

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
error: allocating a local array larger than 512000 bytes
2+
--> $DIR/large_stack_arrays.rs:17:9
3+
|
4+
LL | [0u32; 20_000_000],
5+
| ^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `-D clippy::large-stack-arrays` implied by `-D warnings`
8+
= help: consider allocating on the heap with vec![0u32; 20_000_000].into_boxed_slice()
9+
10+
error: allocating a local array larger than 512000 bytes
11+
--> $DIR/large_stack_arrays.rs:18:9
12+
|
13+
LL | [S { data: [0; 32] }; 5000],
14+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
15+
|
16+
= help: consider allocating on the heap with vec![S { data: [0; 32] }; 5000].into_boxed_slice()
17+
18+
error: allocating a local array larger than 512000 bytes
19+
--> $DIR/large_stack_arrays.rs:19:9
20+
|
21+
LL | [Some(""); 20_000_000],
22+
| ^^^^^^^^^^^^^^^^^^^^^^
23+
|
24+
= help: consider allocating on the heap with vec![Some(""); 20_000_000].into_boxed_slice()
25+
26+
error: allocating a local array larger than 512000 bytes
27+
--> $DIR/large_stack_arrays.rs:20:9
28+
|
29+
LL | [E::T(0); 5000],
30+
| ^^^^^^^^^^^^^^^
31+
|
32+
= help: consider allocating on the heap with vec![E::T(0); 5000].into_boxed_slice()
33+
34+
error: aborting due to 4 previous errors
35+
+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#![warn(clippy::same_functions_in_if_condition)]
2+
#![allow(clippy::ifs_same_cond)] // This warning is different from `ifs_same_cond`.
3+
#![allow(clippy::if_same_then_else, clippy::comparison_chain)] // all empty blocks
4+
5+
fn function() -> bool {
6+
true
7+
}
8+
9+
fn fn_arg(_arg: u8) -> bool {
10+
true
11+
}
12+
13+
struct Struct;
14+
15+
impl Struct {
16+
fn method(&self) -> bool {
17+
true
18+
}
19+
fn method_arg(&self, _arg: u8) -> bool {
20+
true
21+
}
22+
}
23+
24+
fn ifs_same_cond_fn() {
25+
let a = 0;
26+
let obj = Struct;
27+
28+
if function() {
29+
} else if function() {
30+
//~ ERROR ifs same condition
31+
}
32+
33+
if fn_arg(a) {
34+
} else if fn_arg(a) {
35+
//~ ERROR ifs same condition
36+
}
37+
38+
if obj.method() {
39+
} else if obj.method() {
40+
//~ ERROR ifs same condition
41+
}
42+
43+
if obj.method_arg(a) {
44+
} else if obj.method_arg(a) {
45+
//~ ERROR ifs same condition
46+
}
47+
48+
let mut v = vec![1];
49+
if v.pop() == None {
50+
//~ ERROR ifs same condition
51+
} else if v.pop() == None {
52+
}
53+
54+
if v.len() == 42 {
55+
//~ ERROR ifs same condition
56+
} else if v.len() == 42 {
57+
}
58+
59+
if v.len() == 1 {
60+
// ok, different conditions
61+
} else if v.len() == 2 {
62+
}
63+
64+
if fn_arg(0) {
65+
// ok, different arguments.
66+
} else if fn_arg(1) {
67+
}
68+
69+
if obj.method_arg(0) {
70+
// ok, different arguments.
71+
} else if obj.method_arg(1) {
72+
}
73+
74+
if a == 1 {
75+
// ok, warning is on `ifs_same_cond` behalf.
76+
} else if a == 1 {
77+
}
78+
}
79+
80+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
error: this `if` has the same function call as a previous if
2+
--> $DIR/same_functions_in_if_condition.rs:29:15
3+
|
4+
LL | } else if function() {
5+
| ^^^^^^^^^^
6+
|
7+
= note: `-D clippy::same-functions-in-if-condition` implied by `-D warnings`
8+
note: same as this
9+
--> $DIR/same_functions_in_if_condition.rs:28:8
10+
|
11+
LL | if function() {
12+
| ^^^^^^^^^^
13+
14+
error: this `if` has the same function call as a previous if
15+
--> $DIR/same_functions_in_if_condition.rs:34:15
16+
|
17+
LL | } else if fn_arg(a) {
18+
| ^^^^^^^^^
19+
|
20+
note: same as this
21+
--> $DIR/same_functions_in_if_condition.rs:33:8
22+
|
23+
LL | if fn_arg(a) {
24+
| ^^^^^^^^^
25+
26+
error: this `if` has the same function call as a previous if
27+
--> $DIR/same_functions_in_if_condition.rs:39:15
28+
|
29+
LL | } else if obj.method() {
30+
| ^^^^^^^^^^^^
31+
|
32+
note: same as this
33+
--> $DIR/same_functions_in_if_condition.rs:38:8
34+
|
35+
LL | if obj.method() {
36+
| ^^^^^^^^^^^^
37+
38+
error: this `if` has the same function call as a previous if
39+
--> $DIR/same_functions_in_if_condition.rs:44:15
40+
|
41+
LL | } else if obj.method_arg(a) {
42+
| ^^^^^^^^^^^^^^^^^
43+
|
44+
note: same as this
45+
--> $DIR/same_functions_in_if_condition.rs:43:8
46+
|
47+
LL | if obj.method_arg(a) {
48+
| ^^^^^^^^^^^^^^^^^
49+
50+
error: this `if` has the same function call as a previous if
51+
--> $DIR/same_functions_in_if_condition.rs:51:15
52+
|
53+
LL | } else if v.pop() == None {
54+
| ^^^^^^^^^^^^^^^
55+
|
56+
note: same as this
57+
--> $DIR/same_functions_in_if_condition.rs:49:8
58+
|
59+
LL | if v.pop() == None {
60+
| ^^^^^^^^^^^^^^^
61+
62+
error: this `if` has the same function call as a previous if
63+
--> $DIR/same_functions_in_if_condition.rs:56:15
64+
|
65+
LL | } else if v.len() == 42 {
66+
| ^^^^^^^^^^^^^
67+
|
68+
note: same as this
69+
--> $DIR/same_functions_in_if_condition.rs:54:8
70+
|
71+
LL | if v.len() == 42 {
72+
| ^^^^^^^^^^^^^
73+
74+
error: aborting due to 6 previous errors
75+

‎tests/ui/tabs_in_doc_comments.fixed

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// run-rustfix
2+
3+
#![warn(clippy::tabs_in_doc_comments)]
4+
#[allow(dead_code)]
5+
6+
///
7+
/// Struct to hold two strings:
8+
/// - first one
9+
/// - second one
10+
pub struct DoubleString {
11+
///
12+
/// - First String:
13+
/// - needs to be inside here
14+
first_string: String,
15+
///
16+
/// - Second String:
17+
/// - needs to be inside here
18+
second_string: String,
19+
}
20+
21+
/// This is main
22+
fn main() {}

‎tests/ui/tabs_in_doc_comments.rs

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// run-rustfix
2+
3+
#![warn(clippy::tabs_in_doc_comments)]
4+
#[allow(dead_code)]
5+
6+
///
7+
/// Struct to hold two strings:
8+
/// - first one
9+
/// - second one
10+
pub struct DoubleString {
11+
///
12+
/// - First String:
13+
/// - needs to be inside here
14+
first_string: String,
15+
///
16+
/// - Second String:
17+
/// - needs to be inside here
18+
second_string: String,
19+
}
20+
21+
/// This is main
22+
fn main() {}

‎tests/ui/tabs_in_doc_comments.stderr

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
error: using tabs in doc comments is not recommended
2+
--> $DIR/tabs_in_doc_comments.rs:12:9
3+
|
4+
LL | /// - First String:
5+
| ^^^^ help: consider using four spaces per tab
6+
|
7+
= note: `-D clippy::tabs-in-doc-comments` implied by `-D warnings`
8+
9+
error: using tabs in doc comments is not recommended
10+
--> $DIR/tabs_in_doc_comments.rs:13:9
11+
|
12+
LL | /// - needs to be inside here
13+
| ^^^^^^^^ help: consider using four spaces per tab
14+
15+
error: using tabs in doc comments is not recommended
16+
--> $DIR/tabs_in_doc_comments.rs:16:9
17+
|
18+
LL | /// - Second String:
19+
| ^^^^ help: consider using four spaces per tab
20+
21+
error: using tabs in doc comments is not recommended
22+
--> $DIR/tabs_in_doc_comments.rs:17:9
23+
|
24+
LL | /// - needs to be inside here
25+
| ^^^^^^^^ help: consider using four spaces per tab
26+
27+
error: using tabs in doc comments is not recommended
28+
--> $DIR/tabs_in_doc_comments.rs:8:5
29+
|
30+
LL | /// - first one
31+
| ^^^^ help: consider using four spaces per tab
32+
33+
error: using tabs in doc comments is not recommended
34+
--> $DIR/tabs_in_doc_comments.rs:8:13
35+
|
36+
LL | /// - first one
37+
| ^^^^^^^^ help: consider using four spaces per tab
38+
39+
error: using tabs in doc comments is not recommended
40+
--> $DIR/tabs_in_doc_comments.rs:9:5
41+
|
42+
LL | /// - second one
43+
| ^^^^ help: consider using four spaces per tab
44+
45+
error: using tabs in doc comments is not recommended
46+
--> $DIR/tabs_in_doc_comments.rs:9:14
47+
|
48+
LL | /// - second one
49+
| ^^^^ help: consider using four spaces per tab
50+
51+
error: aborting due to 8 previous errors
52+

‎tests/ui/zero_offset.rs

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
fn main() {
2+
unsafe {
3+
let x = &() as *const ();
4+
x.offset(0);
5+
x.wrapping_add(0);
6+
x.sub(0);
7+
x.wrapping_sub(0);
8+
9+
let y = &1 as *const u8;
10+
y.offset(0);
11+
}
12+
}

‎tests/ui/zero_offset.stderr

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

0 commit comments

Comments
 (0)
Please sign in to comment.