Skip to content

Commit

Permalink
Auto merge of rust-lang#114937 - flip1995:clippy_beta_backport, r=Mar…
Browse files Browse the repository at this point in the history
…k-Simulacrum

[beta] Clippy backports for ICE fixes

This backports PRs to beta, that fix ICEs, some lint grouping and FP fixes. Namely:

- rust-lang/rust-clippy#11191
- rust-lang/rust-clippy#11172
- rust-lang/rust-clippy#11130
- rust-lang/rust-clippy#11106
- rust-lang/rust-clippy#11104
- rust-lang/rust-clippy#11077
- rust-lang/rust-clippy#11070 (This PR is not synced to the Rust repo yet, but I will open a separate PR to get it into `master`, before beta is branched: rust-lang#114938)
- rust-lang/rust-clippy#11069

Kind of a big backport, but most of it is tests.

r? `@Mark-Simulacrum`

cc `@Manishearth`
  • Loading branch information
bors committed Aug 17, 2023
2 parents ab197f4 + 48fca12 commit 8c60295
Show file tree
Hide file tree
Showing 23 changed files with 528 additions and 212 deletions.
73 changes: 40 additions & 33 deletions src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::last_path_segment;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
use if_chain::if_chain;

use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_lint::LateLintPass;
use rustc_middle::ty;
use rustc_middle::ty::print::with_forced_trimmed_paths;
use rustc_middle::ty::GenericArgKind;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::sym;

Expand All @@ -15,58 +15,65 @@ declare_clippy_lint! {
/// This lint warns when you use `Arc` with a type that does not implement `Send` or `Sync`.
///
/// ### Why is this bad?
/// Wrapping a type in Arc doesn't add thread safety to the underlying data, so data races
/// could occur when touching the underlying data.
/// `Arc<T>` is only `Send`/`Sync` when `T` is [both `Send` and `Sync`](https://doc.rust-lang.org/std/sync/struct.Arc.html#impl-Send-for-Arc%3CT%3E),
/// either `T` should be made `Send + Sync` or an `Rc` should be used instead of an `Arc`
///
/// ### Example
/// ```rust
/// # use std::cell::RefCell;
/// # use std::sync::Arc;
///
/// fn main() {
/// // This is safe, as `i32` implements `Send` and `Sync`.
/// // This is fine, as `i32` implements `Send` and `Sync`.
/// let a = Arc::new(42);
///
/// // This is not safe, as `RefCell` does not implement `Sync`.
/// // `RefCell` is `!Sync`, so either the `Arc` should be replaced with an `Rc`
/// // or the `RefCell` replaced with something like a `RwLock`
/// let b = Arc::new(RefCell::new(42));
/// }
/// ```
#[clippy::version = "1.72.0"]
pub ARC_WITH_NON_SEND_SYNC,
correctness,
suspicious,
"using `Arc` with a type that does not implement `Send` or `Sync`"
}
declare_lint_pass!(ArcWithNonSendSync => [ARC_WITH_NON_SEND_SYNC]);

impl LateLintPass<'_> for ArcWithNonSendSync {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
let ty = cx.typeck_results().expr_ty(expr);
if_chain! {
if is_type_diagnostic_item(cx, ty, sym::Arc);
if let ExprKind::Call(func, [arg]) = expr.kind;
if let ExprKind::Path(func_path) = func.kind;
if last_path_segment(&func_path).ident.name == sym::new;
if let arg_ty = cx.typeck_results().expr_ty(arg);
if !matches!(arg_ty.kind(), ty::Param(_));
if !cx.tcx
.lang_items()
.sync_trait()
.map_or(false, |id| implements_trait(cx, arg_ty, id, &[])) ||
!cx.tcx
.get_diagnostic_item(sym::Send)
.map_or(false, |id| implements_trait(cx, arg_ty, id, &[]));
if is_type_diagnostic_item(cx, ty, sym::Arc)
&& let ExprKind::Call(func, [arg]) = expr.kind
&& let ExprKind::Path(func_path) = func.kind
&& last_path_segment(&func_path).ident.name == sym::new
&& let arg_ty = cx.typeck_results().expr_ty(arg)
// make sure that the type is not and does not contain any type parameters
&& arg_ty.walk().all(|arg| {
!matches!(arg.unpack(), GenericArgKind::Type(ty) if matches!(ty.kind(), ty::Param(_)))
})
&& let Some(send) = cx.tcx.get_diagnostic_item(sym::Send)
&& let Some(sync) = cx.tcx.lang_items().sync_trait()
&& let [is_send, is_sync] = [send, sync].map(|id| implements_trait(cx, arg_ty, id, &[]))
&& !(is_send && is_sync)
{
span_lint_and_then(
cx,
ARC_WITH_NON_SEND_SYNC,
expr.span,
"usage of an `Arc` that is not `Send` or `Sync`",
|diag| with_forced_trimmed_paths!({
if !is_send {
diag.note(format!("the trait `Send` is not implemented for `{arg_ty}`"));
}
if !is_sync {
diag.note(format!("the trait `Sync` is not implemented for `{arg_ty}`"));
}

diag.note(format!("required for `{ty}` to implement `Send` and `Sync`"));

then {
span_lint_and_help(
cx,
ARC_WITH_NON_SEND_SYNC,
expr.span,
"usage of `Arc<T>` where `T` is not `Send` or `Sync`",
None,
"consider using `Rc<T>` instead or wrapping `T` in a std::sync type like \
`Mutex<T>`",
);
}
diag.help("consider using an `Rc` instead or wrapping the inner type with a `Mutex`");
}
));
}
}
}
4 changes: 2 additions & 2 deletions src/tools/clippy/clippy_lints/src/dereference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1296,8 +1296,8 @@ fn referent_used_exactly_once<'tcx>(
possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
reference: &Expr<'tcx>,
) -> bool {
let mir = enclosing_mir(cx.tcx, reference.hir_id);
if let Some(local) = expr_local(cx.tcx, reference)
if let Some(mir) = enclosing_mir(cx.tcx, reference.hir_id)
&& let Some(local) = expr_local(cx.tcx, reference)
&& let [location] = *local_assignments(mir, local).as_slice()
&& let Some(statement) = mir.basic_blocks[location.block].statements.get(location.statement_index)
&& let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use clippy_utils::{diagnostics::span_lint_and_then, is_res_lang_ctor, last_path_
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_middle::ty;
use rustc_middle::ty::print::with_forced_trimmed_paths;

use super::UNNECESSARY_LITERAL_UNWRAP;

Expand All @@ -21,6 +23,7 @@ fn get_ty_from_args<'a>(args: Option<&'a [hir::GenericArg<'a>]>, index: usize) -
}
}

#[expect(clippy::too_many_lines)]
pub(super) fn check(
cx: &LateContext<'_>,
expr: &hir::Expr<'_>,
Expand Down Expand Up @@ -62,6 +65,34 @@ pub(super) fn check(
(expr.span.with_hi(args[0].span.lo()), "panic!(".to_string()),
(expr.span.with_lo(args[0].span.hi()), ")".to_string()),
]),
("None", "unwrap_or_default", _) => {
let ty = cx.typeck_results().expr_ty(expr);
let default_ty_string = if let ty::Adt(def, ..) = ty.kind() {
with_forced_trimmed_paths!(format!("{}", cx.tcx.def_path_str(def.did())))
} else {
"Default".to_string()
};
Some(vec![(expr.span, format!("{default_ty_string}::default()"))])
},
("None", "unwrap_or", _) => Some(vec![
(expr.span.with_hi(args[0].span.lo()), String::new()),
(expr.span.with_lo(args[0].span.hi()), String::new()),
]),
("None", "unwrap_or_else", _) => match args[0].kind {
hir::ExprKind::Closure(hir::Closure {
fn_decl:
hir::FnDecl {
output: hir::FnRetTy::DefaultReturn(span) | hir::FnRetTy::Return(hir::Ty { span, .. }),
..
},
..
}) => Some(vec![
(expr.span.with_hi(span.hi()), String::new()),
(expr.span.with_lo(args[0].span.hi()), String::new()),
]),
_ => None,
},
_ if call_args.is_empty() => None,
(_, _, Some(_)) => None,
("Ok", "unwrap_err", None) | ("Err", "unwrap", None) => Some(vec![
(
Expand Down
11 changes: 5 additions & 6 deletions src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,11 +207,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug {
if let ItemKind::Impl(Impl { of_trait: Some(trait_ref), self_ty, items, .. }) = item.kind
&& let Res::Def(DefKind::Trait, trait_def_id) = trait_ref.path.res
&& let TyKind::Path(QPath::Resolved(_, self_path)) = &self_ty.kind
// don't trigger if self is a generic parameter, e.g. `impl<T> Debug for T`
// this can only happen in core itself, where the trait is defined,
// but it caused ICEs in the past:
// https://github.com/rust-lang/rust-clippy/issues/10887
&& !matches!(self_path.res, Res::Def(DefKind::TyParam, _))
// make sure that the self type is either a struct, an enum or a union
// this prevents ICEs such as when self is a type parameter or a primitive type
// (see #10887, #11063)
&& let Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, self_path_did) = self_path.res
&& cx.match_def_path(trait_def_id, &[sym::core, sym::fmt, sym::Debug])
// don't trigger if this impl was derived
&& !cx.tcx.has_attr(item.owner_id, sym::automatically_derived)
Expand All @@ -222,7 +221,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug {
&& let body = cx.tcx.hir().body(*body_id)
&& let ExprKind::Block(block, _) = body.value.kind
// inspect `self`
&& let self_ty = cx.tcx.type_of(self_path.res.def_id()).skip_binder().peel_refs()
&& let self_ty = cx.tcx.type_of(self_path_did).skip_binder().peel_refs()
&& let Some(self_adt) = self_ty.ty_adt_def()
&& let Some(self_def_id) = self_adt.did().as_local()
&& let Some(Node::Item(self_item)) = cx.tcx.hir().find_by_def_id(self_def_id)
Expand Down
11 changes: 7 additions & 4 deletions src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use clippy_utils::diagnostics::span_lint;
use clippy_utils::is_lint_allowed;
use rustc_ast::LitKind;
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::Ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
Expand Down Expand Up @@ -45,8 +47,8 @@ fn is_same_type<'tcx>(cx: &LateContext<'tcx>, ty_resolved_path: hir::def::Res, f
return primty.name() == func_return_type_sym;
}

// type annotation is any other non generic type
if let hir::def::Res::Def(_, defid) = ty_resolved_path
// type annotation is a non generic type
if let hir::def::Res::Def(DefKind::Struct | DefKind::Union | DefKind::Enum, defid) = ty_resolved_path
&& let Some(annotation_ty) = cx.tcx.type_of(defid).no_bound_vars()
{
return annotation_ty == func_return_type;
Expand Down Expand Up @@ -130,8 +132,9 @@ fn extract_primty(ty_kind: &hir::TyKind<'_>) -> Option<hir::PrimTy> {

impl LateLintPass<'_> for RedundantTypeAnnotations {
fn check_local<'tcx>(&mut self, cx: &LateContext<'tcx>, local: &'tcx rustc_hir::Local<'tcx>) {
// type annotation part
if !local.span.from_expansion()
if !is_lint_allowed(cx, REDUNDANT_TYPE_ANNOTATIONS, local.hir_id)
// type annotation part
&& !local.span.from_expansion()
&& let Some(ty) = &local.ty

// initialization part
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ declare_clippy_lint! {
/// ```
#[clippy::version = "1.72.0"]
pub TUPLE_ARRAY_CONVERSIONS,
complexity,
nursery,
"checks for tuple<=>array conversions that are not done with `.into()`"
}
impl_lint_pass!(TupleArrayConversions => [TUPLE_ARRAY_CONVERSIONS]);
Expand Down
18 changes: 15 additions & 3 deletions src/tools/clippy/clippy_lints/src/useless_conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts};
use clippy_utils::{get_parent_expr, is_trait_method, match_def_path, path_to_local, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, MatchSource, Node, PatKind};
use rustc_lint::{LateContext, LateLintPass};
Expand Down Expand Up @@ -40,6 +41,7 @@ declare_clippy_lint! {
#[derive(Default)]
pub struct UselessConversion {
try_desugar_arm: Vec<HirId>,
expn_depth: u32,
}

impl_lint_pass!(UselessConversion => [USELESS_CONVERSION]);
Expand Down Expand Up @@ -106,6 +108,7 @@ fn into_iter_deep_call<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -
impl<'tcx> LateLintPass<'tcx> for UselessConversion {
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
if e.span.from_expansion() {
self.expn_depth += 1;
return;
}

Expand Down Expand Up @@ -151,9 +154,14 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
{
if let Some(parent) = get_parent_expr(cx, e) {
let parent_fn = match parent.kind {
ExprKind::Call(recv, args) if let ExprKind::Path(ref qpath) = recv.kind => {
cx.qpath_res(qpath, recv.hir_id).opt_def_id()
.map(|did| (did, args, MethodOrFunction::Function))
ExprKind::Call(recv, args)
if let ExprKind::Path(ref qpath) = recv.kind
&& let Some(did) = cx.qpath_res(qpath, recv.hir_id).opt_def_id()
// make sure that the path indeed points to a fn-like item, so that
// `fn_sig` does not ICE. (see #11065)
&& cx.tcx.opt_def_kind(did).is_some_and(DefKind::is_fn_like) =>
{
Some((did, args, MethodOrFunction::Function))
}
ExprKind::MethodCall(.., args, _) => {
cx.typeck_results().type_dependent_def_id(parent.hir_id)
Expand All @@ -169,6 +177,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
&& let Some(&into_iter_param) = sig.inputs().get(kind.param_pos(arg_pos))
&& let ty::Param(param) = into_iter_param.kind()
&& let Some(span) = into_iter_bound(cx, parent_fn_did, into_iter_did, param.index)
&& self.expn_depth == 0
{
// Get the "innermost" `.into_iter()` call, e.g. given this expression:
// `foo.into_iter().into_iter()`
Expand Down Expand Up @@ -304,5 +313,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
if Some(&e.hir_id) == self.try_desugar_arm.last() {
self.try_desugar_arm.pop();
}
if e.span.from_expansion() {
self.expn_depth -= 1;
}
}
}
23 changes: 14 additions & 9 deletions src/tools/clippy/clippy_utils/src/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,21 +101,26 @@ pub fn used_exactly_once(mir: &rustc_middle::mir::Body<'_>, local: rustc_middle:

/// Returns the `mir::Body` containing the node associated with `hir_id`.
#[allow(clippy::module_name_repetitions)]
pub fn enclosing_mir(tcx: TyCtxt<'_>, hir_id: HirId) -> &Body<'_> {
pub fn enclosing_mir(tcx: TyCtxt<'_>, hir_id: HirId) -> Option<&Body<'_>> {
let body_owner_local_def_id = tcx.hir().enclosing_body_owner(hir_id);
tcx.optimized_mir(body_owner_local_def_id.to_def_id())
if tcx.hir().body_owner_kind(body_owner_local_def_id).is_fn_or_closure() {
Some(tcx.optimized_mir(body_owner_local_def_id.to_def_id()))
} else {
None
}
}

/// Tries to determine the `Local` corresponding to `expr`, if any.
/// This function is expensive and should be used sparingly.
pub fn expr_local(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> Option<Local> {
let mir = enclosing_mir(tcx, expr.hir_id);
mir.local_decls.iter_enumerated().find_map(|(local, local_decl)| {
if local_decl.source_info.span == expr.span {
Some(local)
} else {
None
}
enclosing_mir(tcx, expr.hir_id).and_then(|mir| {
mir.local_decls.iter_enumerated().find_map(|(local, local_decl)| {
if local_decl.source_info.span == expr.span {
Some(local)
} else {
None
}
})
})
}

Expand Down
15 changes: 11 additions & 4 deletions src/tools/clippy/tests/ui/arc_with_non_send_sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,18 @@ fn foo<T>(x: T) {
// Should not lint - purposefully ignoring generic args.
let a = Arc::new(x);
}
fn issue11076<T>() {
let a: Arc<Vec<T>> = Arc::new(Vec::new());
}

fn main() {
// This is safe, as `i32` implements `Send` and `Sync`.
let a = Arc::new(42);
let _ = Arc::new(42);

// This is not safe, as `RefCell` does not implement `Sync`.
let b = Arc::new(RefCell::new(42));
// !Sync
let _ = Arc::new(RefCell::new(42));
let mutex = Mutex::new(1);
// !Send
let _ = Arc::new(mutex.lock().unwrap());
// !Send + !Sync
let _ = Arc::new(&42 as *const i32);
}
33 changes: 28 additions & 5 deletions src/tools/clippy/tests/ui/arc_with_non_send_sync.stderr
Original file line number Diff line number Diff line change
@@ -1,11 +1,34 @@
error: usage of `Arc<T>` where `T` is not `Send` or `Sync`
--> $DIR/arc_with_non_send_sync.rs:16:13
error: usage of an `Arc` that is not `Send` or `Sync`
--> $DIR/arc_with_non_send_sync.rs:18:13
|
LL | let b = Arc::new(RefCell::new(42));
LL | let _ = Arc::new(RefCell::new(42));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider using `Rc<T>` instead or wrapping `T` in a std::sync type like `Mutex<T>`
= note: the trait `Sync` is not implemented for `RefCell<i32>`
= note: required for `Arc<RefCell<i32>>` to implement `Send` and `Sync`
= help: consider using an `Rc` instead or wrapping the inner type with a `Mutex`
= note: `-D clippy::arc-with-non-send-sync` implied by `-D warnings`

error: aborting due to previous error
error: usage of an `Arc` that is not `Send` or `Sync`
--> $DIR/arc_with_non_send_sync.rs:21:13
|
LL | let _ = Arc::new(mutex.lock().unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: the trait `Send` is not implemented for `MutexGuard<'_, i32>`
= note: required for `Arc<MutexGuard<'_, i32>>` to implement `Send` and `Sync`
= help: consider using an `Rc` instead or wrapping the inner type with a `Mutex`

error: usage of an `Arc` that is not `Send` or `Sync`
--> $DIR/arc_with_non_send_sync.rs:23:13
|
LL | let _ = Arc::new(&42 as *const i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: the trait `Send` is not implemented for `*const i32`
= note: the trait `Sync` is not implemented for `*const i32`
= note: required for `Arc<*const i32>` to implement `Send` and `Sync`
= help: consider using an `Rc` instead or wrapping the inner type with a `Mutex`

error: aborting due to 3 previous errors

0 comments on commit 8c60295

Please sign in to comment.