Skip to content

Commit d1b087f

Browse files
committed
Merge commit 'dc5423ad448877e33cca28db2f1445c9c4473c75' into clippyup
1 parent e110231 commit d1b087f

File tree

91 files changed

+3350
-331
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

91 files changed

+3350
-331
lines changed

.github/workflows/clippy.yml

-7
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,3 @@ jobs:
7474
run: bash .github/driver.sh
7575
env:
7676
OS: ${{ runner.os }}
77-
78-
- name: Test cargo dev new lint
79-
run: |
80-
cargo dev new_lint --name new_early_pass --pass early
81-
cargo dev new_lint --name new_late_pass --pass late
82-
cargo check
83-
git reset --hard HEAD

.github/workflows/clippy_bors.yml

-7
Original file line numberDiff line numberDiff line change
@@ -143,13 +143,6 @@ jobs:
143143
env:
144144
OS: ${{ runner.os }}
145145

146-
- name: Test cargo dev new lint
147-
run: |
148-
cargo dev new_lint --name new_early_pass --pass early
149-
cargo dev new_lint --name new_late_pass --pass late
150-
cargo check
151-
git reset --hard HEAD
152-
153146
integration_build:
154147
needs: changelog
155148
runs-on: ubuntu-latest

.github/workflows/clippy_dev.yml

+7
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@ jobs:
3636
- name: Test fmt
3737
run: cargo dev fmt --check
3838

39+
- name: Test cargo dev new lint
40+
run: |
41+
cargo dev new_lint --name new_early_pass --pass early
42+
cargo dev new_lint --name new_late_pass --pass late
43+
cargo check
44+
git reset --hard HEAD
45+
3946
# These jobs doesn't actually test anything, but they're only used to tell
4047
# bors the build completed, as there is no practical way to detect when a
4148
# workflow is successful listening to webhooks only.

CHANGELOG.md

+7
Original file line numberDiff line numberDiff line change
@@ -3042,6 +3042,7 @@ Released 2018-09-13
30423042
<!-- lint disable no-unused-definitions -->
30433043
<!-- begin autogenerated links to lint list -->
30443044
[`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons
3045+
[`allow_attributes_without_reason`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes_without_reason
30453046
[`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
30463047
[`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
30473048
[`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions
@@ -3076,6 +3077,7 @@ Released 2018-09-13
30763077
[`cast_ptr_alignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ptr_alignment
30773078
[`cast_ref_to_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ref_to_mut
30783079
[`cast_sign_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_sign_loss
3080+
[`cast_slice_different_sizes`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_different_sizes
30793081
[`char_lit_as_u8`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_lit_as_u8
30803082
[`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp
30813083
[`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp
@@ -3225,6 +3227,7 @@ Released 2018-09-13
32253227
[`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero
32263228
[`iter_overeager_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_overeager_cloned
32273229
[`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next
3230+
[`iter_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_with_drain
32283231
[`iterator_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iterator_step_by_zero
32293232
[`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits
32303233
[`large_const_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays
@@ -3297,6 +3300,7 @@ Released 2018-09-13
32973300
[`missing_inline_in_public_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_inline_in_public_items
32983301
[`missing_panics_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc
32993302
[`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
3303+
[`missing_spin_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_spin_loop
33003304
[`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes
33013305
[`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals
33023306
[`mod_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#mod_module_files
@@ -3327,6 +3331,7 @@ Released 2018-09-13
33273331
[`needless_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each
33283332
[`needless_late_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_late_init
33293333
[`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes
3334+
[`needless_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match
33303335
[`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref
33313336
[`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value
33323337
[`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark
@@ -3351,6 +3356,7 @@ Released 2018-09-13
33513356
[`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref
33523357
[`octal_escapes`]: https://rust-lang.github.io/rust-clippy/master/index.html#octal_escapes
33533358
[`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect
3359+
[`only_used_in_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#only_used_in_recursion
33543360
[`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref
33553361
[`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref
33563362
[`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap
@@ -3498,6 +3504,7 @@ Released 2018-09-13
34983504
[`unit_return_expecting_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_return_expecting_ord
34993505
[`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast
35003506
[`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map
3507+
[`unnecessary_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_find_map
35013508
[`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold
35023509
[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
35033510
[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ syn = { version = "1.0", features = ["full"] }
5050
futures = "0.3"
5151
parking_lot = "0.11.2"
5252
tokio = { version = "1", features = ["io-util"] }
53-
num_cpus = "1.13"
53+
rustc-semver = "1.1"
5454

5555
[build-dependencies]
5656
rustc_tools_util = { version = "0.2", path = "rustc_tools_util" }

clippy_lints/src/attrs.rs

+59-1
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,38 @@ declare_clippy_lint! {
255255
"usage of `cfg(operating_system)` instead of `cfg(target_os = \"operating_system\")`"
256256
}
257257

258+
declare_clippy_lint! {
259+
/// ### What it does
260+
/// Checks for attributes that allow lints without a reason.
261+
///
262+
/// (This requires the `lint_reasons` feature)
263+
///
264+
/// ### Why is this bad?
265+
/// Allowing a lint should always have a reason. This reason should be documented to
266+
/// ensure that others understand the reasoning
267+
///
268+
/// ### Example
269+
/// Bad:
270+
/// ```rust
271+
/// #![feature(lint_reasons)]
272+
///
273+
/// #![allow(clippy::some_lint)]
274+
/// ```
275+
///
276+
/// Good:
277+
/// ```rust
278+
/// #![feature(lint_reasons)]
279+
///
280+
/// #![allow(clippy::some_lint, reason = "False positive rust-lang/rust-clippy#1002020")]
281+
/// ```
282+
#[clippy::version = "1.61.0"]
283+
pub ALLOW_ATTRIBUTES_WITHOUT_REASON,
284+
restriction,
285+
"ensures that all `allow` and `expect` attributes have a reason"
286+
}
287+
258288
declare_lint_pass!(Attributes => [
289+
ALLOW_ATTRIBUTES_WITHOUT_REASON,
259290
INLINE_ALWAYS,
260291
DEPRECATED_SEMVER,
261292
USELESS_ATTRIBUTE,
@@ -269,6 +300,9 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
269300
if is_lint_level(ident.name) {
270301
check_clippy_lint_names(cx, ident.name, items);
271302
}
303+
if matches!(ident.name, sym::allow | sym::expect) {
304+
check_lint_reason(cx, ident.name, items, attr);
305+
}
272306
if items.is_empty() || !attr.has_name(sym::deprecated) {
273307
return;
274308
}
@@ -404,6 +438,30 @@ fn check_clippy_lint_names(cx: &LateContext<'_>, name: Symbol, items: &[NestedMe
404438
}
405439
}
406440

441+
fn check_lint_reason(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem], attr: &'_ Attribute) {
442+
// Check for the feature
443+
if !cx.tcx.sess.features_untracked().lint_reasons {
444+
return;
445+
}
446+
447+
// Check if the reason is present
448+
if let Some(item) = items.last().and_then(NestedMetaItem::meta_item)
449+
&& let MetaItemKind::NameValue(_) = &item.kind
450+
&& item.path == sym::reason
451+
{
452+
return;
453+
}
454+
455+
span_lint_and_help(
456+
cx,
457+
ALLOW_ATTRIBUTES_WITHOUT_REASON,
458+
attr.span,
459+
&format!("`{}` attribute without specifying a reason", name.as_str()),
460+
None,
461+
"try adding a reason at the end with `, reason = \"..\"`",
462+
);
463+
}
464+
407465
fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
408466
if let ItemKind::Fn(_, _, eid) = item.kind {
409467
is_relevant_expr(cx, cx.tcx.typeck_body(eid), &cx.tcx.hir().body(eid).value)
@@ -659,5 +717,5 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) {
659717
}
660718

661719
fn is_lint_level(symbol: Symbol) -> bool {
662-
matches!(symbol, sym::allow | sym::warn | sym::deny | sym::forbid)
720+
matches!(symbol, sym::allow | sym::expect | sym::warn | sym::deny | sym::forbid)
663721
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
use clippy_utils::{diagnostics::span_lint_and_then, meets_msrv, msrvs, source::snippet_opt};
2+
use if_chain::if_chain;
3+
use rustc_ast::Mutability;
4+
use rustc_hir::{Expr, ExprKind, Node};
5+
use rustc_lint::LateContext;
6+
use rustc_middle::ty::{self, layout::LayoutOf, Ty, TypeAndMut};
7+
use rustc_semver::RustcVersion;
8+
9+
use super::CAST_SLICE_DIFFERENT_SIZES;
10+
11+
fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
12+
let map = cx.tcx.hir();
13+
if_chain! {
14+
if let Some(parent_id) = map.find_parent_node(expr.hir_id);
15+
if let Some(parent) = map.find(parent_id);
16+
then {
17+
let expr = match parent {
18+
Node::Block(block) => {
19+
if let Some(parent_expr) = block.expr {
20+
parent_expr
21+
} else {
22+
return false;
23+
}
24+
},
25+
Node::Expr(expr) => expr,
26+
_ => return false,
27+
};
28+
29+
matches!(expr.kind, ExprKind::Cast(..))
30+
} else {
31+
false
32+
}
33+
}
34+
}
35+
36+
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Option<RustcVersion>) {
37+
// suggestion is invalid if `ptr::slice_from_raw_parts` does not exist
38+
if !meets_msrv(msrv.as_ref(), &msrvs::PTR_SLICE_RAW_PARTS) {
39+
return;
40+
}
41+
42+
// if this cast is the child of another cast expression then don't emit something for it, the full
43+
// chain will be analyzed
44+
if is_child_of_cast(cx, expr) {
45+
return;
46+
}
47+
48+
if let Some((from_slice_ty, to_slice_ty)) = expr_cast_chain_tys(cx, expr) {
49+
if let (Ok(from_layout), Ok(to_layout)) = (cx.layout_of(from_slice_ty.ty), cx.layout_of(to_slice_ty.ty)) {
50+
let from_size = from_layout.size.bytes();
51+
let to_size = to_layout.size.bytes();
52+
if from_size != to_size && from_size != 0 && to_size != 0 {
53+
span_lint_and_then(
54+
cx,
55+
CAST_SLICE_DIFFERENT_SIZES,
56+
expr.span,
57+
&format!(
58+
"casting between raw pointers to `[{}]` (element size {}) and `[{}]` (element size {}) does not adjust the count",
59+
from_slice_ty, from_size, to_slice_ty, to_size,
60+
),
61+
|diag| {
62+
let cast_expr = match expr.kind {
63+
ExprKind::Cast(cast_expr, ..) => cast_expr,
64+
_ => unreachable!("expr should be a cast as checked by expr_cast_chain_tys"),
65+
};
66+
let ptr_snippet = snippet_opt(cx, cast_expr.span).unwrap();
67+
68+
let (mutbl_fn_str, mutbl_ptr_str) = match to_slice_ty.mutbl {
69+
Mutability::Mut => ("_mut", "mut"),
70+
Mutability::Not => ("", "const"),
71+
};
72+
let sugg = format!(
73+
"core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {to_slice_ty}, ..)"
74+
);
75+
76+
diag.span_suggestion(
77+
expr.span,
78+
&format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"),
79+
sugg,
80+
rustc_errors::Applicability::HasPlaceholders,
81+
);
82+
},
83+
);
84+
}
85+
}
86+
}
87+
}
88+
89+
/// Returns the type T of the pointed to *const [T] or *mut [T] and the mutability of the slice if
90+
/// the type is one of those slices
91+
fn get_raw_slice_ty_mut(ty: Ty<'_>) -> Option<TypeAndMut<'_>> {
92+
match ty.kind() {
93+
ty::RawPtr(TypeAndMut { ty: slice_ty, mutbl }) => match slice_ty.kind() {
94+
ty::Slice(ty) => Some(TypeAndMut { ty: *ty, mutbl: *mutbl }),
95+
_ => None,
96+
},
97+
_ => None,
98+
}
99+
}
100+
101+
/// Returns the pair (original ptr T, final ptr U) if the expression is composed of casts
102+
/// Returns None if the expr is not a Cast
103+
fn expr_cast_chain_tys<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<(TypeAndMut<'tcx>, TypeAndMut<'tcx>)> {
104+
if let ExprKind::Cast(cast_expr, _cast_to_hir_ty) = expr.peel_blocks().kind {
105+
let cast_to = cx.typeck_results().expr_ty(expr);
106+
let to_slice_ty = get_raw_slice_ty_mut(cast_to)?;
107+
if let Some((inner_from_ty, _inner_to_ty)) = expr_cast_chain_tys(cx, cast_expr) {
108+
Some((inner_from_ty, to_slice_ty))
109+
} else {
110+
let cast_from = cx.typeck_results().expr_ty(cast_expr);
111+
let from_slice_ty = get_raw_slice_ty_mut(cast_from)?;
112+
Some((from_slice_ty, to_slice_ty))
113+
}
114+
} else {
115+
None
116+
}
117+
}

clippy_lints/src/casts/mod.rs

+48
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ mod cast_precision_loss;
55
mod cast_ptr_alignment;
66
mod cast_ref_to_mut;
77
mod cast_sign_loss;
8+
mod cast_slice_different_sizes;
89
mod char_lit_as_u8;
910
mod fn_to_numeric_cast;
1011
mod fn_to_numeric_cast_any;
@@ -409,6 +410,50 @@ declare_clippy_lint! {
409410
"casts from an enum type to an integral type which will truncate the value"
410411
}
411412

413+
declare_clippy_lint! {
414+
/// Checks for `as` casts between raw pointers to slices with differently sized elements.
415+
///
416+
/// ### Why is this bad?
417+
/// The produced raw pointer to a slice does not update its length metadata. The produced
418+
/// pointer will point to a different number of bytes than the original pointer because the
419+
/// length metadata of a raw slice pointer is in elements rather than bytes.
420+
/// Producing a slice reference from the raw pointer will either create a slice with
421+
/// less data (which can be surprising) or create a slice with more data and cause Undefined Behavior.
422+
///
423+
/// ### Example
424+
/// // Missing data
425+
/// ```rust
426+
/// let a = [1_i32, 2, 3, 4];
427+
/// let p = &a as *const [i32] as *const [u8];
428+
/// unsafe {
429+
/// println!("{:?}", &*p);
430+
/// }
431+
/// ```
432+
/// // Undefined Behavior (note: also potential alignment issues)
433+
/// ```rust
434+
/// let a = [1_u8, 2, 3, 4];
435+
/// let p = &a as *const [u8] as *const [u32];
436+
/// unsafe {
437+
/// println!("{:?}", &*p);
438+
/// }
439+
/// ```
440+
/// Instead use `ptr::slice_from_raw_parts` to construct a slice from a data pointer and the correct length
441+
/// ```rust
442+
/// let a = [1_i32, 2, 3, 4];
443+
/// let old_ptr = &a as *const [i32];
444+
/// // The data pointer is cast to a pointer to the target `u8` not `[u8]`
445+
/// // The length comes from the known length of 4 i32s times the 4 bytes per i32
446+
/// let new_ptr = core::ptr::slice_from_raw_parts(old_ptr as *const u8, 16);
447+
/// unsafe {
448+
/// println!("{:?}", &*new_ptr);
449+
/// }
450+
/// ```
451+
#[clippy::version = "1.60.0"]
452+
pub CAST_SLICE_DIFFERENT_SIZES,
453+
correctness,
454+
"casting using `as` between raw pointers to slices of types with different sizes"
455+
}
456+
412457
pub struct Casts {
413458
msrv: Option<RustcVersion>,
414459
}
@@ -428,6 +473,7 @@ impl_lint_pass!(Casts => [
428473
CAST_LOSSLESS,
429474
CAST_REF_TO_MUT,
430475
CAST_PTR_ALIGNMENT,
476+
CAST_SLICE_DIFFERENT_SIZES,
431477
UNNECESSARY_CAST,
432478
FN_TO_NUMERIC_CAST_ANY,
433479
FN_TO_NUMERIC_CAST,
@@ -478,6 +524,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
478524
cast_ref_to_mut::check(cx, expr);
479525
cast_ptr_alignment::check(cx, expr);
480526
char_lit_as_u8::check(cx, expr);
527+
ptr_as_ptr::check(cx, expr, &self.msrv);
528+
cast_slice_different_sizes::check(cx, expr, &self.msrv);
481529
}
482530

483531
extract_msrv_attr!(LateContext);

0 commit comments

Comments
 (0)