mirror of
https://github.com/rust-lang/rust.git
synced 2026-01-25 07:48:44 +00:00
Auto merge of #144800 - Mark-Simulacrum:beta-next, r=Mark-Simulacrum
[beta] backports Backport: * add unsupported_calling_conventions to lint list rust-lang/rust#144772 * thread name in stack overflow message rust-lang/rust#144500 * rustdoc: don't treat methods under const impls or traits as const rust-lang/rust#143381 * [beta] Clippy beta backport rust-lang/rust#144742 r? `@Mark-Simulacrum`
This commit is contained in:
@@ -122,6 +122,7 @@ declare_lint_pass! {
|
||||
UNSAFE_OP_IN_UNSAFE_FN,
|
||||
UNSTABLE_NAME_COLLISIONS,
|
||||
UNSTABLE_SYNTAX_PRE_EXPANSION,
|
||||
UNSUPPORTED_CALLING_CONVENTIONS,
|
||||
UNSUPPORTED_FN_PTR_CALLING_CONVENTIONS,
|
||||
UNUSED_ASSIGNMENTS,
|
||||
UNUSED_ASSOCIATED_TYPE_BOUNDS,
|
||||
|
||||
@@ -58,7 +58,11 @@ impl Thread {
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
|
||||
pub unsafe fn new(
|
||||
stack: usize,
|
||||
_name: Option<&str>,
|
||||
p: Box<dyn FnOnce()>,
|
||||
) -> io::Result<Thread> {
|
||||
unsafe {
|
||||
Thread::new_with_coreid(stack, p, -1 /* = no specific core */)
|
||||
}
|
||||
|
||||
@@ -86,7 +86,11 @@ impl Thread {
|
||||
/// # Safety
|
||||
///
|
||||
/// See `thread::Builder::spawn_unchecked` for safety requirements.
|
||||
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
|
||||
pub unsafe fn new(
|
||||
stack: usize,
|
||||
_name: Option<&str>,
|
||||
p: Box<dyn FnOnce()>,
|
||||
) -> io::Result<Thread> {
|
||||
let inner = Box::new(ThreadInner {
|
||||
start: UnsafeCell::new(ManuallyDrop::new(p)),
|
||||
lifecycle: AtomicUsize::new(LIFECYCLE_INIT),
|
||||
|
||||
@@ -96,7 +96,11 @@ pub mod wait_notify {
|
||||
|
||||
impl Thread {
|
||||
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
|
||||
pub unsafe fn new(_stack: usize, p: Box<dyn FnOnce() + Send>) -> io::Result<Thread> {
|
||||
pub unsafe fn new(
|
||||
_stack: usize,
|
||||
_name: Option<&str>,
|
||||
p: Box<dyn FnOnce() + Send>,
|
||||
) -> io::Result<Thread> {
|
||||
let mut queue_lock = task_queue::lock();
|
||||
unsafe { usercalls::launch_thread()? };
|
||||
let (task, handle) = task_queue::Task::new(p);
|
||||
|
||||
@@ -22,7 +22,11 @@ unsafe extern "C" {
|
||||
|
||||
impl Thread {
|
||||
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
|
||||
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
|
||||
pub unsafe fn new(
|
||||
stack: usize,
|
||||
_name: Option<&str>,
|
||||
p: Box<dyn FnOnce()>,
|
||||
) -> io::Result<Thread> {
|
||||
let p = Box::into_raw(Box::new(p));
|
||||
let mut native: libc::pthread_t = unsafe { mem::zeroed() };
|
||||
let mut attr: libc::pthread_attr_t = unsafe { mem::zeroed() };
|
||||
|
||||
@@ -11,7 +11,11 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 64 * 1024;
|
||||
|
||||
impl Thread {
|
||||
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
|
||||
pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) -> io::Result<Thread> {
|
||||
pub unsafe fn new(
|
||||
_stack: usize,
|
||||
_name: Option<&str>,
|
||||
_p: Box<dyn FnOnce()>,
|
||||
) -> io::Result<Thread> {
|
||||
unsupported()
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ pub struct Handler {
|
||||
}
|
||||
|
||||
impl Handler {
|
||||
pub unsafe fn new() -> Handler {
|
||||
make_handler(false)
|
||||
pub unsafe fn new(thread_name: Option<Box<str>>) -> Handler {
|
||||
make_handler(false, thread_name)
|
||||
}
|
||||
|
||||
fn null() -> Handler {
|
||||
@@ -72,7 +72,6 @@ mod imp {
|
||||
use crate::sync::OnceLock;
|
||||
use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr, AtomicUsize, Ordering};
|
||||
use crate::sys::pal::unix::os;
|
||||
use crate::thread::with_current_name;
|
||||
use crate::{io, mem, panic, ptr};
|
||||
|
||||
// Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages
|
||||
@@ -158,13 +157,12 @@ mod imp {
|
||||
if !NEED_ALTSTACK.load(Ordering::Relaxed) {
|
||||
// haven't set up our sigaltstack yet
|
||||
NEED_ALTSTACK.store(true, Ordering::Release);
|
||||
let handler = unsafe { make_handler(true) };
|
||||
let handler = unsafe { make_handler(true, None) };
|
||||
MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed);
|
||||
mem::forget(handler);
|
||||
|
||||
if let Some(guard_page_range) = guard_page_range.take() {
|
||||
let thread_name = with_current_name(|name| name.map(Box::from));
|
||||
set_current_info(guard_page_range, thread_name);
|
||||
set_current_info(guard_page_range, Some(Box::from("main")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,14 +228,13 @@ mod imp {
|
||||
/// # Safety
|
||||
/// Mutates the alternate signal stack
|
||||
#[forbid(unsafe_op_in_unsafe_fn)]
|
||||
pub unsafe fn make_handler(main_thread: bool) -> Handler {
|
||||
pub unsafe fn make_handler(main_thread: bool, thread_name: Option<Box<str>>) -> Handler {
|
||||
if !NEED_ALTSTACK.load(Ordering::Acquire) {
|
||||
return Handler::null();
|
||||
}
|
||||
|
||||
if !main_thread {
|
||||
if let Some(guard_page_range) = unsafe { current_guard() } {
|
||||
let thread_name = with_current_name(|name| name.map(Box::from));
|
||||
set_current_info(guard_page_range, thread_name);
|
||||
}
|
||||
}
|
||||
@@ -634,7 +631,10 @@ mod imp {
|
||||
|
||||
pub unsafe fn cleanup() {}
|
||||
|
||||
pub unsafe fn make_handler(_main_thread: bool) -> super::Handler {
|
||||
pub unsafe fn make_handler(
|
||||
_main_thread: bool,
|
||||
_thread_name: Option<Box<str>>,
|
||||
) -> super::Handler {
|
||||
super::Handler::null()
|
||||
}
|
||||
|
||||
@@ -717,7 +717,10 @@ mod imp {
|
||||
|
||||
pub unsafe fn cleanup() {}
|
||||
|
||||
pub unsafe fn make_handler(main_thread: bool) -> super::Handler {
|
||||
pub unsafe fn make_handler(
|
||||
main_thread: bool,
|
||||
_thread_name: Option<Box<str>>,
|
||||
) -> super::Handler {
|
||||
if !main_thread {
|
||||
reserve_stack();
|
||||
}
|
||||
|
||||
@@ -22,6 +22,11 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 256 * 1024;
|
||||
#[cfg(any(target_os = "espidf", target_os = "nuttx"))]
|
||||
pub const DEFAULT_MIN_STACK_SIZE: usize = 0; // 0 indicates that the stack size configured in the ESP-IDF/NuttX menuconfig system should be used
|
||||
|
||||
struct ThreadData {
|
||||
name: Option<Box<str>>,
|
||||
f: Box<dyn FnOnce()>,
|
||||
}
|
||||
|
||||
pub struct Thread {
|
||||
id: libc::pthread_t,
|
||||
}
|
||||
@@ -34,8 +39,12 @@ unsafe impl Sync for Thread {}
|
||||
impl Thread {
|
||||
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
|
||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
||||
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
|
||||
let p = Box::into_raw(Box::new(p));
|
||||
pub unsafe fn new(
|
||||
stack: usize,
|
||||
name: Option<&str>,
|
||||
f: Box<dyn FnOnce()>,
|
||||
) -> io::Result<Thread> {
|
||||
let data = Box::into_raw(Box::new(ThreadData { name: name.map(Box::from), f }));
|
||||
let mut native: libc::pthread_t = mem::zeroed();
|
||||
let mut attr: mem::MaybeUninit<libc::pthread_attr_t> = mem::MaybeUninit::uninit();
|
||||
assert_eq!(libc::pthread_attr_init(attr.as_mut_ptr()), 0);
|
||||
@@ -73,7 +82,7 @@ impl Thread {
|
||||
};
|
||||
}
|
||||
|
||||
let ret = libc::pthread_create(&mut native, attr.as_ptr(), thread_start, p as *mut _);
|
||||
let ret = libc::pthread_create(&mut native, attr.as_ptr(), thread_start, data as *mut _);
|
||||
// Note: if the thread creation fails and this assert fails, then p will
|
||||
// be leaked. However, an alternative design could cause double-free
|
||||
// which is clearly worse.
|
||||
@@ -82,19 +91,20 @@ impl Thread {
|
||||
return if ret != 0 {
|
||||
// The thread failed to start and as a result p was not consumed. Therefore, it is
|
||||
// safe to reconstruct the box so that it gets deallocated.
|
||||
drop(Box::from_raw(p));
|
||||
drop(Box::from_raw(data));
|
||||
Err(io::Error::from_raw_os_error(ret))
|
||||
} else {
|
||||
Ok(Thread { id: native })
|
||||
};
|
||||
|
||||
extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void {
|
||||
extern "C" fn thread_start(data: *mut libc::c_void) -> *mut libc::c_void {
|
||||
unsafe {
|
||||
let data = Box::from_raw(data as *mut ThreadData);
|
||||
// Next, set up our stack overflow handler which may get triggered if we run
|
||||
// out of stack.
|
||||
let _handler = stack_overflow::Handler::new();
|
||||
let _handler = stack_overflow::Handler::new(data.name);
|
||||
// Finally, let's run some code.
|
||||
Box::from_raw(main as *mut Box<dyn FnOnce()>)();
|
||||
(data.f)();
|
||||
}
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
@@ -10,7 +10,11 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 64 * 1024;
|
||||
|
||||
impl Thread {
|
||||
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
|
||||
pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) -> io::Result<Thread> {
|
||||
pub unsafe fn new(
|
||||
_stack: usize,
|
||||
_name: Option<&str>,
|
||||
_p: Box<dyn FnOnce()>,
|
||||
) -> io::Result<Thread> {
|
||||
unsupported()
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ impl Thread {
|
||||
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(target_feature = "atomics")] {
|
||||
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
|
||||
pub unsafe fn new(stack: usize, _name: Option<&str>, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
|
||||
let p = Box::into_raw(Box::new(p));
|
||||
let mut native: libc::pthread_t = unsafe { mem::zeroed() };
|
||||
let mut attr: libc::pthread_attr_t = unsafe { mem::zeroed() };
|
||||
@@ -120,7 +120,7 @@ impl Thread {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) -> io::Result<Thread> {
|
||||
pub unsafe fn new(_stack: usize, _name: Option<&str>, _p: Box<dyn FnOnce()>) -> io::Result<Thread> {
|
||||
crate::sys::unsupported()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,11 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024;
|
||||
|
||||
impl Thread {
|
||||
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
|
||||
pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) -> io::Result<Thread> {
|
||||
pub unsafe fn new(
|
||||
_stack: usize,
|
||||
_name: Option<&str>,
|
||||
_p: Box<dyn FnOnce()>,
|
||||
) -> io::Result<Thread> {
|
||||
unsupported()
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,11 @@ pub struct Thread {
|
||||
impl Thread {
|
||||
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
|
||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
||||
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
|
||||
pub unsafe fn new(
|
||||
stack: usize,
|
||||
_name: Option<&str>,
|
||||
p: Box<dyn FnOnce()>,
|
||||
) -> io::Result<Thread> {
|
||||
let p = Box::into_raw(Box::new(p));
|
||||
|
||||
// CreateThread rounds up values for the stack size to the nearest page size (at least 4kb).
|
||||
|
||||
@@ -20,7 +20,11 @@ pub const GUARD_PAGE_SIZE: usize = 4096;
|
||||
|
||||
impl Thread {
|
||||
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
|
||||
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
|
||||
pub unsafe fn new(
|
||||
stack: usize,
|
||||
_name: Option<&str>,
|
||||
p: Box<dyn FnOnce()>,
|
||||
) -> io::Result<Thread> {
|
||||
let p = Box::into_raw(Box::new(p));
|
||||
let mut stack_size = crate::cmp::max(stack, MIN_STACK_SIZE);
|
||||
|
||||
|
||||
@@ -595,7 +595,7 @@ impl Builder {
|
||||
// Similarly, the `sys` implementation must guarantee that no references to the closure
|
||||
// exist after the thread has terminated, which is signaled by `Thread::join`
|
||||
// returning.
|
||||
native: unsafe { imp::Thread::new(stack_size, main)? },
|
||||
native: unsafe { imp::Thread::new(stack_size, my_thread.name(), main)? },
|
||||
thread: my_thread,
|
||||
packet: my_packet,
|
||||
})
|
||||
|
||||
@@ -647,7 +647,20 @@ impl Item {
|
||||
) -> hir::FnHeader {
|
||||
let sig = tcx.fn_sig(def_id).skip_binder();
|
||||
let constness = if tcx.is_const_fn(def_id) {
|
||||
hir::Constness::Const
|
||||
// rustc's `is_const_fn` returns `true` for associated functions that have an `impl const` parent
|
||||
// or that have a `#[const_trait]` parent. Do not display those as `const` in rustdoc because we
|
||||
// won't be printing correct syntax plus the syntax is unstable.
|
||||
match tcx.opt_associated_item(def_id) {
|
||||
Some(ty::AssocItem {
|
||||
container: ty::AssocItemContainer::Impl,
|
||||
trait_item_def_id: Some(_),
|
||||
..
|
||||
})
|
||||
| Some(ty::AssocItem { container: ty::AssocItemContainer::Trait, .. }) => {
|
||||
hir::Constness::NotConst
|
||||
}
|
||||
None | Some(_) => hir::Constness::Const,
|
||||
}
|
||||
} else {
|
||||
hir::Constness::NotConst
|
||||
};
|
||||
|
||||
@@ -163,7 +163,7 @@ declare_clippy_lint! {
|
||||
/// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`.
|
||||
#[clippy::version = "1.66.0"]
|
||||
pub UNINLINED_FORMAT_ARGS,
|
||||
style,
|
||||
pedantic,
|
||||
"using non-inlined variables in `format!` calls"
|
||||
}
|
||||
|
||||
|
||||
@@ -1,22 +1,18 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::get_parent_expr;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::{snippet, snippet_opt};
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::{BytePos, Span, sym};
|
||||
use rustc_span::{Span, sym};
|
||||
|
||||
use super::MANUAL_IS_VARIANT_AND;
|
||||
|
||||
pub(super) fn check(
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &Expr<'_>,
|
||||
map_recv: &Expr<'_>,
|
||||
map_arg: &Expr<'_>,
|
||||
expr: &'tcx rustc_hir::Expr<'_>,
|
||||
map_recv: &'tcx rustc_hir::Expr<'_>,
|
||||
map_arg: &'tcx rustc_hir::Expr<'_>,
|
||||
map_span: Span,
|
||||
msrv: Msrv,
|
||||
) {
|
||||
@@ -61,57 +57,3 @@ pub(super) fn check(
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
||||
fn emit_lint(cx: &LateContext<'_>, op: BinOpKind, parent: &Expr<'_>, method_span: Span, is_option: bool) {
|
||||
if let Some(before_map_snippet) = snippet_opt(cx, parent.span.with_hi(method_span.lo()))
|
||||
&& let Some(after_map_snippet) = snippet_opt(cx, method_span.with_lo(method_span.lo() + BytePos(3)))
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_IS_VARIANT_AND,
|
||||
parent.span,
|
||||
format!(
|
||||
"called `.map() {}= {}()`",
|
||||
if op == BinOpKind::Eq { '=' } else { '!' },
|
||||
if is_option { "Some" } else { "Ok" },
|
||||
),
|
||||
"use",
|
||||
if is_option && op == BinOpKind::Ne {
|
||||
format!("{before_map_snippet}is_none_or{after_map_snippet}",)
|
||||
} else {
|
||||
format!(
|
||||
"{}{before_map_snippet}{}{after_map_snippet}",
|
||||
if op == BinOpKind::Eq { "" } else { "!" },
|
||||
if is_option { "is_some_and" } else { "is_ok_and" },
|
||||
)
|
||||
},
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn check_map(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let Some(parent_expr) = get_parent_expr(cx, expr)
|
||||
&& let ExprKind::Binary(op, left, right) = parent_expr.kind
|
||||
&& matches!(op.node, BinOpKind::Eq | BinOpKind::Ne)
|
||||
&& op.span.eq_ctxt(expr.span)
|
||||
{
|
||||
// Check `left` and `right` expression in any order, and for `Option` and `Result`
|
||||
for (expr1, expr2) in [(left, right), (right, left)] {
|
||||
for item in [sym::Option, sym::Result] {
|
||||
if let ExprKind::Call(call, ..) = expr1.kind
|
||||
&& let ExprKind::Path(QPath::Resolved(_, path)) = call.kind
|
||||
&& let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), _) = path.res
|
||||
&& let ty = cx.typeck_results().expr_ty(expr1)
|
||||
&& let ty::Adt(adt, args) = ty.kind()
|
||||
&& cx.tcx.is_diagnostic_item(item, adt.did())
|
||||
&& args.type_at(0).is_bool()
|
||||
&& let ExprKind::MethodCall(_, recv, _, span) = expr2.kind
|
||||
&& is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), item)
|
||||
{
|
||||
return emit_lint(cx, op.node, parent_expr, span, item == sym::Option);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5242,7 +5242,6 @@ impl Methods {
|
||||
unused_enumerate_index::check(cx, expr, recv, m_arg);
|
||||
map_clone::check(cx, expr, recv, m_arg, self.msrv);
|
||||
map_with_unused_argument_over_ranges::check(cx, expr, recv, m_arg, self.msrv, span);
|
||||
manual_is_variant_and::check_map(cx, expr);
|
||||
match method_call(recv) {
|
||||
Some((map_name @ (sym::iter | sym::into_iter), recv2, _, _, _)) => {
|
||||
iter_kv_map::check(cx, map_name, expr, recv2, m_arg, self.msrv);
|
||||
|
||||
@@ -9,7 +9,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::{indent_of, reindent_multiline, snippet_with_applicability};
|
||||
use clippy_utils::ty::get_type_diagnostic_name;
|
||||
use clippy_utils::visitors::for_each_unconsumed_temporary;
|
||||
use clippy_utils::{get_parent_expr, peel_blocks};
|
||||
use clippy_utils::{is_expr_final_block_expr, peel_blocks};
|
||||
|
||||
use super::RETURN_AND_THEN;
|
||||
|
||||
@@ -21,7 +21,7 @@ pub(super) fn check<'tcx>(
|
||||
recv: &'tcx hir::Expr<'tcx>,
|
||||
arg: &'tcx hir::Expr<'_>,
|
||||
) {
|
||||
if cx.tcx.hir_get_fn_id_for_return_block(expr.hir_id).is_none() {
|
||||
if !is_expr_final_block_expr(cx.tcx, expr) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -55,24 +55,12 @@ pub(super) fn check<'tcx>(
|
||||
None => &body_snip,
|
||||
};
|
||||
|
||||
// If suggestion is going to get inserted as part of a `return` expression, it must be blockified.
|
||||
let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) {
|
||||
let base_indent = indent_of(cx, parent_expr.span);
|
||||
let inner_indent = base_indent.map(|i| i + 4);
|
||||
format!(
|
||||
"{}\n{}\n{}",
|
||||
reindent_multiline(&format!("{{\nlet {arg_snip} = {recv_snip}?;"), true, inner_indent),
|
||||
reindent_multiline(inner, false, inner_indent),
|
||||
reindent_multiline("}", false, base_indent),
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"let {} = {}?;\n{}",
|
||||
arg_snip,
|
||||
recv_snip,
|
||||
reindent_multiline(inner, false, indent_of(cx, expr.span))
|
||||
)
|
||||
};
|
||||
let sugg = format!(
|
||||
"let {} = {}?;\n{}",
|
||||
arg_snip,
|
||||
recv_snip,
|
||||
reindent_multiline(inner, false, indent_of(cx, expr.span))
|
||||
);
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
||||
@@ -4,6 +4,7 @@ use rustc_ast::BorrowKind;
|
||||
use rustc_errors::{Applicability, Diag};
|
||||
use rustc_hir::{Expr, ExprKind, Node, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::adjustment::Adjust;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::SWAP_WITH_TEMPORARY;
|
||||
@@ -11,12 +12,12 @@ use super::SWAP_WITH_TEMPORARY;
|
||||
const MSG_TEMPORARY: &str = "this expression returns a temporary value";
|
||||
const MSG_TEMPORARY_REFMUT: &str = "this is a mutable reference to a temporary value";
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args: &[Expr<'_>]) {
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, func: &Expr<'_>, args: &'tcx [Expr<'_>]) {
|
||||
if let ExprKind::Path(QPath::Resolved(_, func_path)) = func.kind
|
||||
&& let Some(func_def_id) = func_path.res.opt_def_id()
|
||||
&& cx.tcx.is_diagnostic_item(sym::mem_swap, func_def_id)
|
||||
{
|
||||
match (ArgKind::new(&args[0]), ArgKind::new(&args[1])) {
|
||||
match (ArgKind::new(cx, &args[0]), ArgKind::new(cx, &args[1])) {
|
||||
(ArgKind::RefMutToTemp(left_temp), ArgKind::RefMutToTemp(right_temp)) => {
|
||||
emit_lint_useless(cx, expr, &args[0], &args[1], left_temp, right_temp);
|
||||
},
|
||||
@@ -28,10 +29,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args
|
||||
}
|
||||
|
||||
enum ArgKind<'tcx> {
|
||||
// Mutable reference to a place, coming from a macro
|
||||
RefMutToPlaceAsMacro(&'tcx Expr<'tcx>),
|
||||
// Place behind a mutable reference
|
||||
RefMutToPlace(&'tcx Expr<'tcx>),
|
||||
// Mutable reference to a place, coming from a macro, and number of dereferences to use
|
||||
RefMutToPlaceAsMacro(&'tcx Expr<'tcx>, usize),
|
||||
// Place behind a mutable reference, and number of dereferences to use
|
||||
RefMutToPlace(&'tcx Expr<'tcx>, usize),
|
||||
// Temporary value behind a mutable reference
|
||||
RefMutToTemp(&'tcx Expr<'tcx>),
|
||||
// Any other case
|
||||
@@ -39,13 +40,29 @@ enum ArgKind<'tcx> {
|
||||
}
|
||||
|
||||
impl<'tcx> ArgKind<'tcx> {
|
||||
fn new(arg: &'tcx Expr<'tcx>) -> Self {
|
||||
if let ExprKind::AddrOf(BorrowKind::Ref, _, target) = arg.kind {
|
||||
if target.is_syntactic_place_expr() {
|
||||
/// Build a new `ArgKind` from `arg`. There must be no false positive when returning a
|
||||
/// `ArgKind::RefMutToTemp` variant, as this may cause a spurious lint to be emitted.
|
||||
fn new(cx: &LateContext<'tcx>, arg: &'tcx Expr<'tcx>) -> Self {
|
||||
if let ExprKind::AddrOf(BorrowKind::Ref, _, target) = arg.kind
|
||||
&& let adjustments = cx.typeck_results().expr_adjustments(arg)
|
||||
&& adjustments
|
||||
.first()
|
||||
.is_some_and(|adj| matches!(adj.kind, Adjust::Deref(None)))
|
||||
&& adjustments
|
||||
.last()
|
||||
.is_some_and(|adj| matches!(adj.kind, Adjust::Borrow(_)))
|
||||
{
|
||||
let extra_derefs = adjustments[1..adjustments.len() - 1]
|
||||
.iter()
|
||||
.filter(|adj| matches!(adj.kind, Adjust::Deref(_)))
|
||||
.count();
|
||||
// If a deref is used, `arg` might be a place expression. For example, a mutex guard
|
||||
// would dereference into the mutex content which is probably not temporary.
|
||||
if target.is_syntactic_place_expr() || extra_derefs > 0 {
|
||||
if arg.span.from_expansion() {
|
||||
ArgKind::RefMutToPlaceAsMacro(arg)
|
||||
ArgKind::RefMutToPlaceAsMacro(arg, extra_derefs)
|
||||
} else {
|
||||
ArgKind::RefMutToPlace(target)
|
||||
ArgKind::RefMutToPlace(target, extra_derefs)
|
||||
}
|
||||
} else {
|
||||
ArgKind::RefMutToTemp(target)
|
||||
@@ -106,10 +123,15 @@ fn emit_lint_assign(cx: &LateContext<'_>, expr: &Expr<'_>, target: &ArgKind<'_>,
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let ctxt = expr.span.ctxt();
|
||||
let assign_target = match target {
|
||||
ArgKind::Expr(target) | ArgKind::RefMutToPlaceAsMacro(target) => {
|
||||
Sugg::hir_with_context(cx, target, ctxt, "_", &mut applicability).deref()
|
||||
},
|
||||
ArgKind::RefMutToPlace(target) => Sugg::hir_with_context(cx, target, ctxt, "_", &mut applicability),
|
||||
ArgKind::Expr(target) => Sugg::hir_with_context(cx, target, ctxt, "_", &mut applicability).deref(),
|
||||
ArgKind::RefMutToPlaceAsMacro(arg, derefs) => (0..*derefs).fold(
|
||||
Sugg::hir_with_context(cx, arg, ctxt, "_", &mut applicability).deref(),
|
||||
|sugg, _| sugg.deref(),
|
||||
),
|
||||
ArgKind::RefMutToPlace(target, derefs) => (0..*derefs).fold(
|
||||
Sugg::hir_with_context(cx, target, ctxt, "_", &mut applicability),
|
||||
|sugg, _| sugg.deref(),
|
||||
),
|
||||
ArgKind::RefMutToTemp(_) => unreachable!(),
|
||||
};
|
||||
let assign_source = Sugg::hir_with_context(cx, temp, ctxt, "_", &mut applicability);
|
||||
|
||||
@@ -4,44 +4,6 @@
|
||||
#[macro_use]
|
||||
extern crate option_helpers;
|
||||
|
||||
struct Foo<T>(T);
|
||||
|
||||
impl<T> Foo<T> {
|
||||
fn map<F: FnMut(T) -> bool>(self, mut f: F) -> Option<bool> {
|
||||
Some(f(self.0))
|
||||
}
|
||||
}
|
||||
|
||||
fn foo() -> Option<bool> {
|
||||
Some(true)
|
||||
}
|
||||
|
||||
macro_rules! some_true {
|
||||
() => {
|
||||
Some(true)
|
||||
};
|
||||
}
|
||||
macro_rules! some_false {
|
||||
() => {
|
||||
Some(false)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! mac {
|
||||
(some $e:expr) => {
|
||||
Some($e)
|
||||
};
|
||||
(some_map $e:expr) => {
|
||||
Some($e).map(|x| x % 2 == 0)
|
||||
};
|
||||
(map $e:expr) => {
|
||||
$e.map(|x| x % 2 == 0)
|
||||
};
|
||||
(eq $a:expr, $b:expr) => {
|
||||
$a == $b
|
||||
};
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn option_methods() {
|
||||
let opt = Some(1);
|
||||
@@ -59,15 +21,6 @@ fn option_methods() {
|
||||
let _ = opt
|
||||
.is_some_and(|x| x > 1);
|
||||
|
||||
let _ = Some(2).is_some_and(|x| x % 2 == 0);
|
||||
//~^ manual_is_variant_and
|
||||
let _ = Some(2).is_none_or(|x| x % 2 == 0);
|
||||
//~^ manual_is_variant_and
|
||||
let _ = Some(2).is_some_and(|x| x % 2 == 0);
|
||||
//~^ manual_is_variant_and
|
||||
let _ = Some(2).is_none_or(|x| x % 2 == 0);
|
||||
//~^ manual_is_variant_and
|
||||
|
||||
// won't fix because the return type of the closure is not `bool`
|
||||
let _ = opt.map(|x| x + 1).unwrap_or_default();
|
||||
|
||||
@@ -75,14 +28,6 @@ fn option_methods() {
|
||||
let _ = opt2.is_some_and(char::is_alphanumeric); // should lint
|
||||
//~^ manual_is_variant_and
|
||||
let _ = opt_map!(opt2, |x| x == 'a').unwrap_or_default(); // should not lint
|
||||
|
||||
// Should not lint.
|
||||
let _ = Foo::<u32>(0).map(|x| x % 2 == 0) == Some(true);
|
||||
let _ = Some(2).map(|x| x % 2 == 0) != foo();
|
||||
let _ = mac!(eq Some(2).map(|x| x % 2 == 0), Some(true));
|
||||
let _ = mac!(some 2).map(|x| x % 2 == 0) == Some(true);
|
||||
let _ = mac!(some_map 2) == Some(true);
|
||||
let _ = mac!(map Some(2)) == Some(true);
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
@@ -96,13 +41,6 @@ fn result_methods() {
|
||||
});
|
||||
let _ = res.is_ok_and(|x| x > 1);
|
||||
|
||||
let _ = Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0);
|
||||
//~^ manual_is_variant_and
|
||||
let _ = !Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0);
|
||||
//~^ manual_is_variant_and
|
||||
let _ = !Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0);
|
||||
//~^ manual_is_variant_and
|
||||
|
||||
// won't fix because the return type of the closure is not `bool`
|
||||
let _ = res.map(|x| x + 1).unwrap_or_default();
|
||||
|
||||
|
||||
@@ -4,44 +4,6 @@
|
||||
#[macro_use]
|
||||
extern crate option_helpers;
|
||||
|
||||
struct Foo<T>(T);
|
||||
|
||||
impl<T> Foo<T> {
|
||||
fn map<F: FnMut(T) -> bool>(self, mut f: F) -> Option<bool> {
|
||||
Some(f(self.0))
|
||||
}
|
||||
}
|
||||
|
||||
fn foo() -> Option<bool> {
|
||||
Some(true)
|
||||
}
|
||||
|
||||
macro_rules! some_true {
|
||||
() => {
|
||||
Some(true)
|
||||
};
|
||||
}
|
||||
macro_rules! some_false {
|
||||
() => {
|
||||
Some(false)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! mac {
|
||||
(some $e:expr) => {
|
||||
Some($e)
|
||||
};
|
||||
(some_map $e:expr) => {
|
||||
Some($e).map(|x| x % 2 == 0)
|
||||
};
|
||||
(map $e:expr) => {
|
||||
$e.map(|x| x % 2 == 0)
|
||||
};
|
||||
(eq $a:expr, $b:expr) => {
|
||||
$a == $b
|
||||
};
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn option_methods() {
|
||||
let opt = Some(1);
|
||||
@@ -65,15 +27,6 @@ fn option_methods() {
|
||||
//~^ manual_is_variant_and
|
||||
.unwrap_or_default();
|
||||
|
||||
let _ = Some(2).map(|x| x % 2 == 0) == Some(true);
|
||||
//~^ manual_is_variant_and
|
||||
let _ = Some(2).map(|x| x % 2 == 0) != Some(true);
|
||||
//~^ manual_is_variant_and
|
||||
let _ = Some(2).map(|x| x % 2 == 0) == some_true!();
|
||||
//~^ manual_is_variant_and
|
||||
let _ = Some(2).map(|x| x % 2 == 0) != some_false!();
|
||||
//~^ manual_is_variant_and
|
||||
|
||||
// won't fix because the return type of the closure is not `bool`
|
||||
let _ = opt.map(|x| x + 1).unwrap_or_default();
|
||||
|
||||
@@ -81,14 +34,6 @@ fn option_methods() {
|
||||
let _ = opt2.map(char::is_alphanumeric).unwrap_or_default(); // should lint
|
||||
//~^ manual_is_variant_and
|
||||
let _ = opt_map!(opt2, |x| x == 'a').unwrap_or_default(); // should not lint
|
||||
|
||||
// Should not lint.
|
||||
let _ = Foo::<u32>(0).map(|x| x % 2 == 0) == Some(true);
|
||||
let _ = Some(2).map(|x| x % 2 == 0) != foo();
|
||||
let _ = mac!(eq Some(2).map(|x| x % 2 == 0), Some(true));
|
||||
let _ = mac!(some 2).map(|x| x % 2 == 0) == Some(true);
|
||||
let _ = mac!(some_map 2) == Some(true);
|
||||
let _ = mac!(map Some(2)) == Some(true);
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
@@ -105,13 +50,6 @@ fn result_methods() {
|
||||
//~^ manual_is_variant_and
|
||||
.unwrap_or_default();
|
||||
|
||||
let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) == Ok(true);
|
||||
//~^ manual_is_variant_and
|
||||
let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) != Ok(true);
|
||||
//~^ manual_is_variant_and
|
||||
let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) != Ok(true);
|
||||
//~^ manual_is_variant_and
|
||||
|
||||
// won't fix because the return type of the closure is not `bool`
|
||||
let _ = res.map(|x| x + 1).unwrap_or_default();
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
error: called `map(<f>).unwrap_or_default()` on an `Option` value
|
||||
--> tests/ui/manual_is_variant_and.rs:51:17
|
||||
--> tests/ui/manual_is_variant_and.rs:13:17
|
||||
|
|
||||
LL | let _ = opt.map(|x| x > 1)
|
||||
| _________________^
|
||||
@@ -11,7 +11,7 @@ LL | | .unwrap_or_default();
|
||||
= help: to override `-D warnings` add `#[allow(clippy::manual_is_variant_and)]`
|
||||
|
||||
error: called `map(<f>).unwrap_or_default()` on an `Option` value
|
||||
--> tests/ui/manual_is_variant_and.rs:56:17
|
||||
--> tests/ui/manual_is_variant_and.rs:18:17
|
||||
|
|
||||
LL | let _ = opt.map(|x| {
|
||||
| _________________^
|
||||
@@ -30,13 +30,13 @@ LL ~ });
|
||||
|
|
||||
|
||||
error: called `map(<f>).unwrap_or_default()` on an `Option` value
|
||||
--> tests/ui/manual_is_variant_and.rs:61:17
|
||||
--> tests/ui/manual_is_variant_and.rs:23:17
|
||||
|
|
||||
LL | let _ = opt.map(|x| x > 1).unwrap_or_default();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `is_some_and(|x| x > 1)`
|
||||
|
||||
error: called `map(<f>).unwrap_or_default()` on an `Option` value
|
||||
--> tests/ui/manual_is_variant_and.rs:64:10
|
||||
--> tests/ui/manual_is_variant_and.rs:26:10
|
||||
|
|
||||
LL | .map(|x| x > 1)
|
||||
| __________^
|
||||
@@ -44,38 +44,14 @@ LL | |
|
||||
LL | | .unwrap_or_default();
|
||||
| |____________________________^ help: use: `is_some_and(|x| x > 1)`
|
||||
|
||||
error: called `.map() == Some()`
|
||||
--> tests/ui/manual_is_variant_and.rs:68:13
|
||||
|
|
||||
LL | let _ = Some(2).map(|x| x % 2 == 0) == Some(true);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Some(2).is_some_and(|x| x % 2 == 0)`
|
||||
|
||||
error: called `.map() != Some()`
|
||||
--> tests/ui/manual_is_variant_and.rs:70:13
|
||||
|
|
||||
LL | let _ = Some(2).map(|x| x % 2 == 0) != Some(true);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Some(2).is_none_or(|x| x % 2 == 0)`
|
||||
|
||||
error: called `.map() == Some()`
|
||||
--> tests/ui/manual_is_variant_and.rs:72:13
|
||||
|
|
||||
LL | let _ = Some(2).map(|x| x % 2 == 0) == some_true!();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Some(2).is_some_and(|x| x % 2 == 0)`
|
||||
|
||||
error: called `.map() != Some()`
|
||||
--> tests/ui/manual_is_variant_and.rs:74:13
|
||||
|
|
||||
LL | let _ = Some(2).map(|x| x % 2 == 0) != some_false!();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Some(2).is_none_or(|x| x % 2 == 0)`
|
||||
|
||||
error: called `map(<f>).unwrap_or_default()` on an `Option` value
|
||||
--> tests/ui/manual_is_variant_and.rs:81:18
|
||||
--> tests/ui/manual_is_variant_and.rs:34:18
|
||||
|
|
||||
LL | let _ = opt2.map(char::is_alphanumeric).unwrap_or_default(); // should lint
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `is_some_and(char::is_alphanumeric)`
|
||||
|
||||
error: called `map(<f>).unwrap_or_default()` on a `Result` value
|
||||
--> tests/ui/manual_is_variant_and.rs:99:17
|
||||
--> tests/ui/manual_is_variant_and.rs:44:17
|
||||
|
|
||||
LL | let _ = res.map(|x| {
|
||||
| _________________^
|
||||
@@ -94,7 +70,7 @@ LL ~ });
|
||||
|
|
||||
|
||||
error: called `map(<f>).unwrap_or_default()` on a `Result` value
|
||||
--> tests/ui/manual_is_variant_and.rs:104:17
|
||||
--> tests/ui/manual_is_variant_and.rs:49:17
|
||||
|
|
||||
LL | let _ = res.map(|x| x > 1)
|
||||
| _________________^
|
||||
@@ -102,29 +78,11 @@ LL | |
|
||||
LL | | .unwrap_or_default();
|
||||
| |____________________________^ help: use: `is_ok_and(|x| x > 1)`
|
||||
|
||||
error: called `.map() == Ok()`
|
||||
--> tests/ui/manual_is_variant_and.rs:108:13
|
||||
|
|
||||
LL | let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) == Ok(true);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0)`
|
||||
|
||||
error: called `.map() != Ok()`
|
||||
--> tests/ui/manual_is_variant_and.rs:110:13
|
||||
|
|
||||
LL | let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) != Ok(true);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0)`
|
||||
|
||||
error: called `.map() != Ok()`
|
||||
--> tests/ui/manual_is_variant_and.rs:112:13
|
||||
|
|
||||
LL | let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) != Ok(true);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0)`
|
||||
|
||||
error: called `map(<f>).unwrap_or_default()` on a `Result` value
|
||||
--> tests/ui/manual_is_variant_and.rs:119:18
|
||||
--> tests/ui/manual_is_variant_and.rs:57:18
|
||||
|
|
||||
LL | let _ = res2.map(char::is_alphanumeric).unwrap_or_default(); // should lint
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `is_ok_and(char::is_alphanumeric)`
|
||||
|
||||
error: aborting due to 15 previous errors
|
||||
error: aborting due to 8 previous errors
|
||||
|
||||
|
||||
@@ -67,60 +67,8 @@ fn main() {
|
||||
.first() // creates temporary reference
|
||||
.and_then(|x| test_opt_block(Some(*x)))
|
||||
}
|
||||
|
||||
fn in_closure() -> bool {
|
||||
let _ = || {
|
||||
let x = Some("")?;
|
||||
if x.len() > 2 { Some(3) } else { None }
|
||||
//~^ return_and_then
|
||||
};
|
||||
true
|
||||
}
|
||||
|
||||
fn with_return(shortcut: bool) -> Option<i32> {
|
||||
if shortcut {
|
||||
return {
|
||||
let x = Some("")?;
|
||||
if x.len() > 2 { Some(3) } else { None }
|
||||
};
|
||||
//~^ return_and_then
|
||||
};
|
||||
None
|
||||
}
|
||||
|
||||
fn with_return_multiline(shortcut: bool) -> Option<i32> {
|
||||
if shortcut {
|
||||
return {
|
||||
let mut x = Some("")?;
|
||||
let x = format!("{x}.");
|
||||
if x.len() > 2 { Some(3) } else { None }
|
||||
};
|
||||
//~^^^^ return_and_then
|
||||
};
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_option(n: i32) -> Option<i32> {
|
||||
Some(n)
|
||||
}
|
||||
|
||||
mod issue14781 {
|
||||
fn foo(_: &str, _: (u32, u32)) -> Result<(u32, u32), ()> {
|
||||
Ok((1, 1))
|
||||
}
|
||||
|
||||
fn bug(_: Option<&str>) -> Result<(), ()> {
|
||||
let year: Option<&str> = None;
|
||||
let month: Option<&str> = None;
|
||||
let day: Option<&str> = None;
|
||||
|
||||
let _day = if let (Some(year), Some(month)) = (year, month) {
|
||||
day.and_then(|day| foo(day, (1, 31)).ok())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,55 +63,8 @@ fn main() {
|
||||
.first() // creates temporary reference
|
||||
.and_then(|x| test_opt_block(Some(*x)))
|
||||
}
|
||||
|
||||
fn in_closure() -> bool {
|
||||
let _ = || {
|
||||
Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None })
|
||||
//~^ return_and_then
|
||||
};
|
||||
true
|
||||
}
|
||||
|
||||
fn with_return(shortcut: bool) -> Option<i32> {
|
||||
if shortcut {
|
||||
return Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None });
|
||||
//~^ return_and_then
|
||||
};
|
||||
None
|
||||
}
|
||||
|
||||
fn with_return_multiline(shortcut: bool) -> Option<i32> {
|
||||
if shortcut {
|
||||
return Some("").and_then(|mut x| {
|
||||
let x = format!("{x}.");
|
||||
if x.len() > 2 { Some(3) } else { None }
|
||||
});
|
||||
//~^^^^ return_and_then
|
||||
};
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_option(n: i32) -> Option<i32> {
|
||||
Some(n)
|
||||
}
|
||||
|
||||
mod issue14781 {
|
||||
fn foo(_: &str, _: (u32, u32)) -> Result<(u32, u32), ()> {
|
||||
Ok((1, 1))
|
||||
}
|
||||
|
||||
fn bug(_: Option<&str>) -> Result<(), ()> {
|
||||
let year: Option<&str> = None;
|
||||
let month: Option<&str> = None;
|
||||
let day: Option<&str> = None;
|
||||
|
||||
let _day = if let (Some(year), Some(month)) = (year, month) {
|
||||
day.and_then(|day| foo(day, (1, 31)).ok())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,50 +101,5 @@ LL + })?;
|
||||
LL + if x.len() > 2 { Some(3) } else { None }
|
||||
|
|
||||
|
||||
error: use the `?` operator instead of an `and_then` call
|
||||
--> tests/ui/return_and_then.rs:69:13
|
||||
|
|
||||
LL | Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None })
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: try
|
||||
|
|
||||
LL ~ let x = Some("")?;
|
||||
LL + if x.len() > 2 { Some(3) } else { None }
|
||||
|
|
||||
|
||||
error: use the `?` operator instead of an `and_then` call
|
||||
--> tests/ui/return_and_then.rs:77:20
|
||||
|
|
||||
LL | return Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None });
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: try
|
||||
|
|
||||
LL ~ return {
|
||||
LL + let x = Some("")?;
|
||||
LL + if x.len() > 2 { Some(3) } else { None }
|
||||
LL ~ };
|
||||
|
|
||||
|
||||
error: use the `?` operator instead of an `and_then` call
|
||||
--> tests/ui/return_and_then.rs:85:20
|
||||
|
|
||||
LL | return Some("").and_then(|mut x| {
|
||||
| ____________________^
|
||||
LL | | let x = format!("{x}.");
|
||||
LL | | if x.len() > 2 { Some(3) } else { None }
|
||||
LL | | });
|
||||
| |______________^
|
||||
|
|
||||
help: try
|
||||
|
|
||||
LL ~ return {
|
||||
LL + let mut x = Some("")?;
|
||||
LL + let x = format!("{x}.");
|
||||
LL + if x.len() > 2 { Some(3) } else { None }
|
||||
LL ~ };
|
||||
|
|
||||
|
||||
error: aborting due to 10 previous errors
|
||||
error: aborting due to 7 previous errors
|
||||
|
||||
|
||||
@@ -72,3 +72,49 @@ fn dont_lint_those(s: &mut S, v: &mut [String], w: Option<&mut String>) {
|
||||
swap(&mut s.t, v.get_mut(0).unwrap());
|
||||
swap(w.unwrap(), &mut s.t);
|
||||
}
|
||||
|
||||
fn issue15166() {
|
||||
use std::sync::Mutex;
|
||||
|
||||
struct A {
|
||||
thing: Mutex<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl A {
|
||||
fn a(&self) {
|
||||
let mut new_vec = vec![42];
|
||||
// Do not lint here, as neither `new_vec` nor the result of `.lock().unwrap()` are temporaries
|
||||
swap(&mut new_vec, &mut self.thing.lock().unwrap());
|
||||
for v in new_vec {
|
||||
// Do something with v
|
||||
}
|
||||
// Here `vec![42]` is temporary though, and a proper dereference will have to be used in the fix
|
||||
*self.thing.lock().unwrap() = vec![42];
|
||||
//~^ ERROR: swapping with a temporary value is inefficient
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn multiple_deref() {
|
||||
let mut v1 = &mut &mut &mut vec![42];
|
||||
***v1 = vec![];
|
||||
//~^ ERROR: swapping with a temporary value is inefficient
|
||||
|
||||
struct Wrapper<T: ?Sized>(T);
|
||||
impl<T: ?Sized> std::ops::Deref for Wrapper<T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
impl<T: ?Sized> std::ops::DerefMut for Wrapper<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
use std::sync::Mutex;
|
||||
let mut v1 = Mutex::new(Wrapper(Wrapper(vec![42])));
|
||||
*(*(*v1.lock().unwrap())) = vec![];
|
||||
//~^ ERROR: swapping with a temporary value is inefficient
|
||||
}
|
||||
|
||||
@@ -72,3 +72,49 @@ fn dont_lint_those(s: &mut S, v: &mut [String], w: Option<&mut String>) {
|
||||
swap(&mut s.t, v.get_mut(0).unwrap());
|
||||
swap(w.unwrap(), &mut s.t);
|
||||
}
|
||||
|
||||
fn issue15166() {
|
||||
use std::sync::Mutex;
|
||||
|
||||
struct A {
|
||||
thing: Mutex<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl A {
|
||||
fn a(&self) {
|
||||
let mut new_vec = vec![42];
|
||||
// Do not lint here, as neither `new_vec` nor the result of `.lock().unwrap()` are temporaries
|
||||
swap(&mut new_vec, &mut self.thing.lock().unwrap());
|
||||
for v in new_vec {
|
||||
// Do something with v
|
||||
}
|
||||
// Here `vec![42]` is temporary though, and a proper dereference will have to be used in the fix
|
||||
swap(&mut vec![42], &mut self.thing.lock().unwrap());
|
||||
//~^ ERROR: swapping with a temporary value is inefficient
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn multiple_deref() {
|
||||
let mut v1 = &mut &mut &mut vec![42];
|
||||
swap(&mut ***v1, &mut vec![]);
|
||||
//~^ ERROR: swapping with a temporary value is inefficient
|
||||
|
||||
struct Wrapper<T: ?Sized>(T);
|
||||
impl<T: ?Sized> std::ops::Deref for Wrapper<T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
impl<T: ?Sized> std::ops::DerefMut for Wrapper<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
use std::sync::Mutex;
|
||||
let mut v1 = Mutex::new(Wrapper(Wrapper(vec![42])));
|
||||
swap(&mut vec![], &mut v1.lock().unwrap());
|
||||
//~^ ERROR: swapping with a temporary value is inefficient
|
||||
}
|
||||
|
||||
@@ -96,5 +96,41 @@ note: this expression returns a temporary value
|
||||
LL | swap(mac!(refmut y), &mut func());
|
||||
| ^^^^^^
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
error: swapping with a temporary value is inefficient
|
||||
--> tests/ui/swap_with_temporary.rs:92:13
|
||||
|
|
||||
LL | swap(&mut vec![42], &mut self.thing.lock().unwrap());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `*self.thing.lock().unwrap() = vec![42]`
|
||||
|
|
||||
note: this expression returns a temporary value
|
||||
--> tests/ui/swap_with_temporary.rs:92:23
|
||||
|
|
||||
LL | swap(&mut vec![42], &mut self.thing.lock().unwrap());
|
||||
| ^^^^^^^^
|
||||
|
||||
error: swapping with a temporary value is inefficient
|
||||
--> tests/ui/swap_with_temporary.rs:100:5
|
||||
|
|
||||
LL | swap(&mut ***v1, &mut vec![]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `***v1 = vec![]`
|
||||
|
|
||||
note: this expression returns a temporary value
|
||||
--> tests/ui/swap_with_temporary.rs:100:27
|
||||
|
|
||||
LL | swap(&mut ***v1, &mut vec![]);
|
||||
| ^^^^^^
|
||||
|
||||
error: swapping with a temporary value is inefficient
|
||||
--> tests/ui/swap_with_temporary.rs:118:5
|
||||
|
|
||||
LL | swap(&mut vec![], &mut v1.lock().unwrap());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `*(*(*v1.lock().unwrap())) = vec![]`
|
||||
|
|
||||
note: this expression returns a temporary value
|
||||
--> tests/ui/swap_with_temporary.rs:118:15
|
||||
|
|
||||
LL | swap(&mut vec![], &mut v1.lock().unwrap());
|
||||
| ^^^^^^
|
||||
|
||||
error: aborting due to 11 previous errors
|
||||
|
||||
|
||||
36
tests/rustdoc/constant/const-trait-and-impl-methods.rs
Normal file
36
tests/rustdoc/constant/const-trait-and-impl-methods.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
// check that we don't render `#[const_trait]` methods as `const` - even for
|
||||
// const `trait`s and `impl`s.
|
||||
#![crate_name = "foo"]
|
||||
#![feature(const_trait_impl)]
|
||||
|
||||
//@ has foo/trait.Tr.html
|
||||
//@ has - '//*[@id="tymethod.required"]' 'fn required()'
|
||||
//@ !has - '//*[@id="tymethod.required"]' 'const'
|
||||
//@ has - '//*[@id="method.defaulted"]' 'fn defaulted()'
|
||||
//@ !has - '//*[@id="method.defaulted"]' 'const'
|
||||
#[const_trait]
|
||||
pub trait Tr {
|
||||
fn required();
|
||||
fn defaulted() {}
|
||||
}
|
||||
|
||||
pub struct ConstImpl {}
|
||||
pub struct NonConstImpl {}
|
||||
|
||||
//@ has foo/struct.ConstImpl.html
|
||||
//@ has - '//*[@id="method.required"]' 'fn required()'
|
||||
//@ !has - '//*[@id="method.required"]' 'const'
|
||||
//@ has - '//*[@id="method.defaulted"]' 'fn defaulted()'
|
||||
//@ !has - '//*[@id="method.defaulted"]' 'const'
|
||||
impl const Tr for ConstImpl {
|
||||
fn required() {}
|
||||
}
|
||||
|
||||
//@ has foo/struct.NonConstImpl.html
|
||||
//@ has - '//*[@id="method.required"]' 'fn required()'
|
||||
//@ !has - '//*[@id="method.required"]' 'const'
|
||||
//@ has - '//*[@id="method.defaulted"]' 'fn defaulted()'
|
||||
//@ !has - '//*[@id="method.defaulted"]' 'const'
|
||||
impl Tr for NonConstImpl {
|
||||
fn required() {}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ extern crate libc;
|
||||
use std::env;
|
||||
use std::hint::black_box;
|
||||
use std::process::Command;
|
||||
use std::thread;
|
||||
use std::thread::Builder;
|
||||
|
||||
fn silent_recurse() {
|
||||
let buf = [0u8; 1000];
|
||||
@@ -56,9 +56,9 @@ fn main() {
|
||||
} else if args.len() > 1 && args[1] == "loud" {
|
||||
loud_recurse();
|
||||
} else if args.len() > 1 && args[1] == "silent-thread" {
|
||||
thread::spawn(silent_recurse).join();
|
||||
Builder::new().name("ferris".to_string()).spawn(silent_recurse).unwrap().join();
|
||||
} else if args.len() > 1 && args[1] == "loud-thread" {
|
||||
thread::spawn(loud_recurse).join();
|
||||
Builder::new().name("ferris".to_string()).spawn(loud_recurse).unwrap().join();
|
||||
} else {
|
||||
let mut modes = vec![
|
||||
"silent-thread",
|
||||
@@ -82,6 +82,12 @@ fn main() {
|
||||
let error = String::from_utf8_lossy(&silent.stderr);
|
||||
assert!(error.contains("has overflowed its stack"),
|
||||
"missing overflow message: {}", error);
|
||||
|
||||
if mode.contains("thread") {
|
||||
assert!(error.contains("ferris"), "missing thread name: {}", error);
|
||||
} else {
|
||||
assert!(error.contains("main"), "missing thread name: {}", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user